/services/java/com/android/server/am/UsageStatsService.java

https://github.com/aizuzi/platform_frameworks_base · Java · 1180 lines · 972 code · 103 blank · 105 comment · 223 complexity · 034ce4ccc24dfc9c0133847b51740c8a MD5 · raw file

  1. /*
  2. * Copyright (C) 2006-2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.server.am;
  17. import android.app.AppGlobals;
  18. import android.content.ComponentName;
  19. import android.content.Context;
  20. import android.content.pm.IPackageManager;
  21. import android.content.pm.PackageInfo;
  22. import android.content.pm.PackageManager;
  23. import android.os.Binder;
  24. import android.os.IBinder;
  25. import android.os.FileUtils;
  26. import android.os.Parcel;
  27. import android.os.Process;
  28. import android.os.RemoteException;
  29. import android.os.ServiceManager;
  30. import android.os.SystemClock;
  31. import android.util.ArrayMap;
  32. import android.util.AtomicFile;
  33. import android.util.Slog;
  34. import android.util.Xml;
  35. import com.android.internal.app.IUsageStats;
  36. import com.android.internal.content.PackageMonitor;
  37. import com.android.internal.os.PkgUsageStats;
  38. import com.android.internal.util.FastXmlSerializer;
  39. import org.xmlpull.v1.XmlPullParser;
  40. import org.xmlpull.v1.XmlPullParserException;
  41. import org.xmlpull.v1.XmlSerializer;
  42. import java.io.File;
  43. import java.io.FileDescriptor;
  44. import java.io.FileInputStream;
  45. import java.io.FileNotFoundException;
  46. import java.io.FileOutputStream;
  47. import java.io.IOException;
  48. import java.io.PrintWriter;
  49. import java.util.ArrayList;
  50. import java.util.Calendar;
  51. import java.util.Collections;
  52. import java.util.HashMap;
  53. import java.util.HashSet;
  54. import java.util.List;
  55. import java.util.Map;
  56. import java.util.Set;
  57. import java.util.TimeZone;
  58. import java.util.concurrent.atomic.AtomicBoolean;
  59. import java.util.concurrent.atomic.AtomicInteger;
  60. import java.util.concurrent.atomic.AtomicLong;
  61. /**
  62. * This service collects the statistics associated with usage
  63. * of various components, like when a particular package is launched or
  64. * paused and aggregates events like number of time a component is launched
  65. * total duration of a component launch.
  66. */
  67. public final class UsageStatsService extends IUsageStats.Stub {
  68. public static final String SERVICE_NAME = "usagestats";
  69. private static final boolean localLOGV = false;
  70. private static final boolean REPORT_UNEXPECTED = false;
  71. private static final String TAG = "UsageStats";
  72. // Current on-disk Parcel version
  73. private static final int VERSION = 1008;
  74. private static final int CHECKIN_VERSION = 4;
  75. private static final String FILE_PREFIX = "usage-";
  76. private static final String FILE_HISTORY = FILE_PREFIX + "history.xml";
  77. private static final int FILE_WRITE_INTERVAL = (localLOGV) ? 0 : 30*60*1000; // 30m in ms
  78. private static final int MAX_NUM_FILES = 5;
  79. private static final int NUM_LAUNCH_TIME_BINS = 10;
  80. private static final int[] LAUNCH_TIME_BINS = {
  81. 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000
  82. };
  83. static IUsageStats sService;
  84. private Context mContext;
  85. // structure used to maintain statistics since the last checkin.
  86. final private ArrayMap<String, PkgUsageStatsExtended> mStats
  87. = new ArrayMap<String, PkgUsageStatsExtended>();
  88. // Maintains the last time any component was resumed, for all time.
  89. final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes
  90. = new ArrayMap<String, ArrayMap<String, Long>>();
  91. // To remove last-resume time stats when a pacakge is removed.
  92. private PackageMonitor mPackageMonitor;
  93. // Lock to update package stats. Methods suffixed by SLOCK should invoked with
  94. // this lock held
  95. final Object mStatsLock = new Object();
  96. // Lock to write to file. Methods suffixed by FLOCK should invoked with
  97. // this lock held.
  98. final Object mFileLock = new Object();
  99. // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
  100. private String mLastResumedPkg;
  101. private String mLastResumedComp;
  102. private boolean mIsResumed;
  103. private File mFile;
  104. private AtomicFile mHistoryFile;
  105. private String mFileLeaf;
  106. private File mDir;
  107. private final Calendar mCal // guarded by itself
  108. = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
  109. private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
  110. private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
  111. private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
  112. static class TimeStats {
  113. int mCount;
  114. final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS];
  115. TimeStats() {
  116. }
  117. void incCount() {
  118. mCount++;
  119. }
  120. void add(int val) {
  121. final int[] bins = LAUNCH_TIME_BINS;
  122. for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
  123. if (val < bins[i]) {
  124. mTimes[i]++;
  125. return;
  126. }
  127. }
  128. mTimes[NUM_LAUNCH_TIME_BINS-1]++;
  129. }
  130. TimeStats(Parcel in) {
  131. mCount = in.readInt();
  132. final int[] localTimes = mTimes;
  133. for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
  134. localTimes[i] = in.readInt();
  135. }
  136. }
  137. void writeToParcel(Parcel out) {
  138. out.writeInt(mCount);
  139. final int[] localTimes = mTimes;
  140. for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
  141. out.writeInt(localTimes[i]);
  142. }
  143. }
  144. }
  145. static class PkgUsageStatsExtended {
  146. final ArrayMap<String, TimeStats> mLaunchTimes
  147. = new ArrayMap<String, TimeStats>();
  148. final ArrayMap<String, TimeStats> mFullyDrawnTimes
  149. = new ArrayMap<String, TimeStats>();
  150. int mLaunchCount;
  151. long mUsageTime;
  152. long mPausedTime;
  153. long mResumedTime;
  154. PkgUsageStatsExtended() {
  155. mLaunchCount = 0;
  156. mUsageTime = 0;
  157. }
  158. PkgUsageStatsExtended(Parcel in) {
  159. mLaunchCount = in.readInt();
  160. mUsageTime = in.readLong();
  161. if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
  162. + ", Usage time:" + mUsageTime);
  163. final int numLaunchTimeStats = in.readInt();
  164. if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
  165. mLaunchTimes.ensureCapacity(numLaunchTimeStats);
  166. for (int i=0; i<numLaunchTimeStats; i++) {
  167. String comp = in.readString();
  168. if (localLOGV) Slog.v(TAG, "Component: " + comp);
  169. TimeStats times = new TimeStats(in);
  170. mLaunchTimes.put(comp, times);
  171. }
  172. final int numFullyDrawnTimeStats = in.readInt();
  173. if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats);
  174. mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats);
  175. for (int i=0; i<numFullyDrawnTimeStats; i++) {
  176. String comp = in.readString();
  177. if (localLOGV) Slog.v(TAG, "Component: " + comp);
  178. TimeStats times = new TimeStats(in);
  179. mFullyDrawnTimes.put(comp, times);
  180. }
  181. }
  182. void updateResume(String comp, boolean launched) {
  183. if (launched) {
  184. mLaunchCount++;
  185. }
  186. mResumedTime = SystemClock.elapsedRealtime();
  187. }
  188. void updatePause() {
  189. mPausedTime = SystemClock.elapsedRealtime();
  190. mUsageTime += (mPausedTime - mResumedTime);
  191. }
  192. void addLaunchCount(String comp) {
  193. TimeStats times = mLaunchTimes.get(comp);
  194. if (times == null) {
  195. times = new TimeStats();
  196. mLaunchTimes.put(comp, times);
  197. }
  198. times.incCount();
  199. }
  200. void addLaunchTime(String comp, int millis) {
  201. TimeStats times = mLaunchTimes.get(comp);
  202. if (times == null) {
  203. times = new TimeStats();
  204. mLaunchTimes.put(comp, times);
  205. }
  206. times.add(millis);
  207. }
  208. void addFullyDrawnTime(String comp, int millis) {
  209. TimeStats times = mFullyDrawnTimes.get(comp);
  210. if (times == null) {
  211. times = new TimeStats();
  212. mFullyDrawnTimes.put(comp, times);
  213. }
  214. times.add(millis);
  215. }
  216. void writeToParcel(Parcel out) {
  217. out.writeInt(mLaunchCount);
  218. out.writeLong(mUsageTime);
  219. final int numLaunchTimeStats = mLaunchTimes.size();
  220. out.writeInt(numLaunchTimeStats);
  221. for (int i=0; i<numLaunchTimeStats; i++) {
  222. out.writeString(mLaunchTimes.keyAt(i));
  223. mLaunchTimes.valueAt(i).writeToParcel(out);
  224. }
  225. final int numFullyDrawnTimeStats = mFullyDrawnTimes.size();
  226. out.writeInt(numFullyDrawnTimeStats);
  227. for (int i=0; i<numFullyDrawnTimeStats; i++) {
  228. out.writeString(mFullyDrawnTimes.keyAt(i));
  229. mFullyDrawnTimes.valueAt(i).writeToParcel(out);
  230. }
  231. }
  232. void clear() {
  233. mLaunchTimes.clear();
  234. mFullyDrawnTimes.clear();
  235. mLaunchCount = 0;
  236. mUsageTime = 0;
  237. }
  238. }
  239. UsageStatsService(String dir) {
  240. if (localLOGV) Slog.v(TAG, "UsageStatsService: " + dir);
  241. mDir = new File(dir);
  242. mDir.mkdir();
  243. // Remove any old /data/system/usagestats.* files from previous versions.
  244. File parentDir = mDir.getParentFile();
  245. String files[] = parentDir.list();
  246. if (files != null) {
  247. String prefix = mDir.getName() + ".";
  248. for (String file : files) {
  249. if (file.startsWith(prefix)) {
  250. Slog.i(TAG, "Deleting old usage file: " + file);
  251. (new File(parentDir, file)).delete();
  252. }
  253. }
  254. }
  255. // Update current stats which are binned by date
  256. mFileLeaf = getCurrentDateStr(FILE_PREFIX);
  257. mFile = new File(mDir, mFileLeaf);
  258. mHistoryFile = new AtomicFile(new File(mDir, FILE_HISTORY));
  259. readStatsFromFile();
  260. readHistoryStatsFromFile();
  261. mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
  262. // mCal was set by getCurrentDateStr(), want to use that same time.
  263. mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
  264. }
  265. /*
  266. * Utility method to convert date into string.
  267. */
  268. private String getCurrentDateStr(String prefix) {
  269. StringBuilder sb = new StringBuilder();
  270. if (prefix != null) {
  271. sb.append(prefix);
  272. }
  273. synchronized (mCal) {
  274. mCal.setTimeInMillis(System.currentTimeMillis());
  275. sb.append(mCal.get(Calendar.YEAR));
  276. int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
  277. if (mm < 10) {
  278. sb.append("0");
  279. }
  280. sb.append(mm);
  281. int dd = mCal.get(Calendar.DAY_OF_MONTH);
  282. if (dd < 10) {
  283. sb.append("0");
  284. }
  285. sb.append(dd);
  286. }
  287. return sb.toString();
  288. }
  289. private Parcel getParcelForFile(File file) throws IOException {
  290. FileInputStream stream = new FileInputStream(file);
  291. try {
  292. byte[] raw = readFully(stream);
  293. Parcel in = Parcel.obtain();
  294. in.unmarshall(raw, 0, raw.length);
  295. in.setDataPosition(0);
  296. return in;
  297. } finally {
  298. stream.close();
  299. }
  300. }
  301. private void readStatsFromFile() {
  302. File newFile = mFile;
  303. synchronized (mFileLock) {
  304. try {
  305. if (newFile.exists()) {
  306. readStatsFLOCK(newFile);
  307. } else {
  308. // Check for file limit before creating a new file
  309. checkFileLimitFLOCK();
  310. newFile.createNewFile();
  311. }
  312. } catch (IOException e) {
  313. Slog.w(TAG,"Error : " + e + " reading data from file:" + newFile);
  314. }
  315. }
  316. }
  317. private void readStatsFLOCK(File file) throws IOException {
  318. Parcel in = getParcelForFile(file);
  319. int vers = in.readInt();
  320. if (vers != VERSION) { // vers will be 0 if the parcel file was empty
  321. Slog.w(TAG, "Usage stats version of " + file + " changed from " + vers + " to "
  322. + VERSION + "; dropping");
  323. return;
  324. }
  325. int N = in.readInt();
  326. while (N > 0) {
  327. N--;
  328. String pkgName = in.readString();
  329. if (pkgName == null) {
  330. break;
  331. }
  332. if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName);
  333. PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
  334. synchronized (mStatsLock) {
  335. mStats.put(pkgName, pus);
  336. }
  337. }
  338. }
  339. private void readHistoryStatsFromFile() {
  340. synchronized (mFileLock) {
  341. if (mHistoryFile.getBaseFile().exists()) {
  342. readHistoryStatsFLOCK();
  343. }
  344. }
  345. }
  346. private void readHistoryStatsFLOCK() {
  347. FileInputStream fis = null;
  348. try {
  349. fis = mHistoryFile.openRead();
  350. XmlPullParser parser = Xml.newPullParser();
  351. parser.setInput(fis, null);
  352. int eventType = parser.getEventType();
  353. while (eventType != XmlPullParser.START_TAG &&
  354. eventType != XmlPullParser.END_DOCUMENT) {
  355. eventType = parser.next();
  356. }
  357. if (eventType == XmlPullParser.END_DOCUMENT) {
  358. return;
  359. }
  360. String tagName = parser.getName();
  361. if ("usage-history".equals(tagName)) {
  362. String pkg = null;
  363. do {
  364. eventType = parser.next();
  365. if (eventType == XmlPullParser.START_TAG) {
  366. tagName = parser.getName();
  367. int depth = parser.getDepth();
  368. if ("pkg".equals(tagName) && depth == 2) {
  369. pkg = parser.getAttributeValue(null, "name");
  370. } else if ("comp".equals(tagName) && depth == 3 && pkg != null) {
  371. String comp = parser.getAttributeValue(null, "name");
  372. String lastResumeTimeStr = parser.getAttributeValue(null, "lrt");
  373. if (comp != null && lastResumeTimeStr != null) {
  374. try {
  375. long lastResumeTime = Long.parseLong(lastResumeTimeStr);
  376. synchronized (mStatsLock) {
  377. ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
  378. if (lrt == null) {
  379. lrt = new ArrayMap<String, Long>();
  380. mLastResumeTimes.put(pkg, lrt);
  381. }
  382. lrt.put(comp, lastResumeTime);
  383. }
  384. } catch (NumberFormatException e) {
  385. }
  386. }
  387. }
  388. } else if (eventType == XmlPullParser.END_TAG) {
  389. if ("pkg".equals(parser.getName())) {
  390. pkg = null;
  391. }
  392. }
  393. } while (eventType != XmlPullParser.END_DOCUMENT);
  394. }
  395. } catch (XmlPullParserException e) {
  396. Slog.w(TAG,"Error reading history stats: " + e);
  397. } catch (IOException e) {
  398. Slog.w(TAG,"Error reading history stats: " + e);
  399. } finally {
  400. if (fis != null) {
  401. try {
  402. fis.close();
  403. } catch (IOException e) {
  404. }
  405. }
  406. }
  407. }
  408. private ArrayList<String> getUsageStatsFileListFLOCK() {
  409. // Check if there are too many files in the system and delete older files
  410. String fList[] = mDir.list();
  411. if (fList == null) {
  412. return null;
  413. }
  414. ArrayList<String> fileList = new ArrayList<String>();
  415. for (String file : fList) {
  416. if (!file.startsWith(FILE_PREFIX)) {
  417. continue;
  418. }
  419. if (file.endsWith(".bak")) {
  420. (new File(mDir, file)).delete();
  421. continue;
  422. }
  423. fileList.add(file);
  424. }
  425. return fileList;
  426. }
  427. private void checkFileLimitFLOCK() {
  428. // Get all usage stats output files
  429. ArrayList<String> fileList = getUsageStatsFileListFLOCK();
  430. if (fileList == null) {
  431. // Empty /data/system/usagestats/ so we don't have anything to delete
  432. return;
  433. }
  434. int count = fileList.size();
  435. if (count <= MAX_NUM_FILES) {
  436. return;
  437. }
  438. // Sort files
  439. Collections.sort(fileList);
  440. count -= MAX_NUM_FILES;
  441. // Delete older files
  442. for (int i = 0; i < count; i++) {
  443. String fileName = fileList.get(i);
  444. File file = new File(mDir, fileName);
  445. Slog.i(TAG, "Deleting usage file : " + fileName);
  446. file.delete();
  447. }
  448. }
  449. /**
  450. * Conditionally start up a disk write if it's been awhile, or the
  451. * day has rolled over.
  452. *
  453. * This is called indirectly from user-facing actions (when
  454. * 'force' is false) so it tries to be quick, without writing to
  455. * disk directly or acquiring heavy locks.
  456. *
  457. * @params force do an unconditional, synchronous stats flush
  458. * to disk on the current thread.
  459. * @params forceWriteHistoryStats Force writing of historical stats.
  460. */
  461. private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
  462. int curDay;
  463. synchronized (mCal) {
  464. mCal.setTimeInMillis(System.currentTimeMillis());
  465. curDay = mCal.get(Calendar.DAY_OF_YEAR);
  466. }
  467. final boolean dayChanged = curDay != mLastWriteDay.get();
  468. // Determine if the day changed... note that this will be wrong
  469. // if the year has changed but we are in the same day of year...
  470. // we can probably live with this.
  471. final long currElapsedTime = SystemClock.elapsedRealtime();
  472. // Fast common path, without taking the often-contentious
  473. // mFileLock.
  474. if (!force) {
  475. if (!dayChanged &&
  476. (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
  477. // wait till the next update
  478. return;
  479. }
  480. if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
  481. new Thread("UsageStatsService_DiskWriter") {
  482. public void run() {
  483. try {
  484. if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
  485. writeStatsToFile(true, false);
  486. } finally {
  487. mUnforcedDiskWriteRunning.set(false);
  488. if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
  489. }
  490. }
  491. }.start();
  492. }
  493. return;
  494. }
  495. synchronized (mFileLock) {
  496. // Get the most recent file
  497. mFileLeaf = getCurrentDateStr(FILE_PREFIX);
  498. // Copy current file to back up
  499. File backupFile = null;
  500. if (mFile != null && mFile.exists()) {
  501. backupFile = new File(mFile.getPath() + ".bak");
  502. if (!backupFile.exists()) {
  503. if (!mFile.renameTo(backupFile)) {
  504. Slog.w(TAG, "Failed to persist new stats");
  505. return;
  506. }
  507. } else {
  508. mFile.delete();
  509. }
  510. }
  511. try {
  512. // Write mStats to file
  513. writeStatsFLOCK(mFile);
  514. mLastWriteElapsedTime.set(currElapsedTime);
  515. if (dayChanged) {
  516. mLastWriteDay.set(curDay);
  517. // clear stats
  518. synchronized (mStats) {
  519. mStats.clear();
  520. }
  521. mFile = new File(mDir, mFileLeaf);
  522. checkFileLimitFLOCK();
  523. }
  524. if (dayChanged || forceWriteHistoryStats) {
  525. // Write history stats daily or when forced (due to shutdown) or when debugging.
  526. writeHistoryStatsFLOCK();
  527. }
  528. // Delete the backup file
  529. if (backupFile != null) {
  530. backupFile.delete();
  531. }
  532. } catch (IOException e) {
  533. Slog.w(TAG, "Failed writing stats to file:" + mFile);
  534. if (backupFile != null) {
  535. mFile.delete();
  536. backupFile.renameTo(mFile);
  537. }
  538. }
  539. }
  540. if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
  541. }
  542. private void writeStatsFLOCK(File file) throws IOException {
  543. FileOutputStream stream = new FileOutputStream(file);
  544. try {
  545. Parcel out = Parcel.obtain();
  546. writeStatsToParcelFLOCK(out);
  547. stream.write(out.marshall());
  548. out.recycle();
  549. stream.flush();
  550. } finally {
  551. FileUtils.sync(stream);
  552. stream.close();
  553. }
  554. }
  555. private void writeStatsToParcelFLOCK(Parcel out) {
  556. synchronized (mStatsLock) {
  557. out.writeInt(VERSION);
  558. Set<String> keys = mStats.keySet();
  559. out.writeInt(keys.size());
  560. for (String key : keys) {
  561. PkgUsageStatsExtended pus = mStats.get(key);
  562. out.writeString(key);
  563. pus.writeToParcel(out);
  564. }
  565. }
  566. }
  567. /** Filter out stats for any packages which aren't present anymore. */
  568. private void filterHistoryStats() {
  569. synchronized (mStatsLock) {
  570. IPackageManager pm = AppGlobals.getPackageManager();
  571. for (int i=0; i<mLastResumeTimes.size(); i++) {
  572. String pkg = mLastResumeTimes.keyAt(i);
  573. try {
  574. if (pm.getPackageUid(pkg, 0) < 0) {
  575. mLastResumeTimes.removeAt(i);
  576. i--;
  577. }
  578. } catch (RemoteException e) {
  579. }
  580. }
  581. }
  582. }
  583. private void writeHistoryStatsFLOCK() {
  584. FileOutputStream fos = null;
  585. try {
  586. fos = mHistoryFile.startWrite();
  587. XmlSerializer out = new FastXmlSerializer();
  588. out.setOutput(fos, "utf-8");
  589. out.startDocument(null, true);
  590. out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
  591. out.startTag(null, "usage-history");
  592. synchronized (mStatsLock) {
  593. for (int i=0; i<mLastResumeTimes.size(); i++) {
  594. out.startTag(null, "pkg");
  595. out.attribute(null, "name", mLastResumeTimes.keyAt(i));
  596. ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
  597. for (int j=0; j<comp.size(); j++) {
  598. out.startTag(null, "comp");
  599. out.attribute(null, "name", comp.keyAt(j));
  600. out.attribute(null, "lrt", comp.valueAt(j).toString());
  601. out.endTag(null, "comp");
  602. }
  603. out.endTag(null, "pkg");
  604. }
  605. }
  606. out.endTag(null, "usage-history");
  607. out.endDocument();
  608. mHistoryFile.finishWrite(fos);
  609. } catch (IOException e) {
  610. Slog.w(TAG,"Error writing history stats" + e);
  611. if (fos != null) {
  612. mHistoryFile.failWrite(fos);
  613. }
  614. }
  615. }
  616. public void publish(Context context) {
  617. mContext = context;
  618. ServiceManager.addService(SERVICE_NAME, asBinder());
  619. }
  620. /**
  621. * Start watching packages to remove stats when a package is uninstalled.
  622. * May only be called when the package manager is ready.
  623. */
  624. public void monitorPackages() {
  625. mPackageMonitor = new PackageMonitor() {
  626. @Override
  627. public void onPackageRemovedAllUsers(String packageName, int uid) {
  628. synchronized (mStatsLock) {
  629. mLastResumeTimes.remove(packageName);
  630. }
  631. }
  632. };
  633. mPackageMonitor.register(mContext, null, true);
  634. filterHistoryStats();
  635. }
  636. public void shutdown() {
  637. if (mPackageMonitor != null) {
  638. mPackageMonitor.unregister();
  639. }
  640. Slog.i(TAG, "Writing usage stats before shutdown...");
  641. writeStatsToFile(true, true);
  642. }
  643. public static IUsageStats getService() {
  644. if (sService != null) {
  645. return sService;
  646. }
  647. IBinder b = ServiceManager.getService(SERVICE_NAME);
  648. sService = asInterface(b);
  649. return sService;
  650. }
  651. @Override
  652. public void noteResumeComponent(ComponentName componentName) {
  653. enforceCallingPermission();
  654. String pkgName;
  655. synchronized (mStatsLock) {
  656. if ((componentName == null) ||
  657. ((pkgName = componentName.getPackageName()) == null)) {
  658. return;
  659. }
  660. final boolean samePackage = pkgName.equals(mLastResumedPkg);
  661. if (mIsResumed) {
  662. if (mLastResumedPkg != null) {
  663. // We last resumed some other package... just pause it now
  664. // to recover.
  665. if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
  666. + " while already resumed in " + mLastResumedPkg);
  667. PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
  668. if (pus != null) {
  669. pus.updatePause();
  670. }
  671. }
  672. }
  673. final boolean sameComp = samePackage
  674. && componentName.getClassName().equals(mLastResumedComp);
  675. mIsResumed = true;
  676. mLastResumedPkg = pkgName;
  677. mLastResumedComp = componentName.getClassName();
  678. if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
  679. PkgUsageStatsExtended pus = mStats.get(pkgName);
  680. if (pus == null) {
  681. pus = new PkgUsageStatsExtended();
  682. mStats.put(pkgName, pus);
  683. }
  684. pus.updateResume(mLastResumedComp, !samePackage);
  685. if (!sameComp) {
  686. pus.addLaunchCount(mLastResumedComp);
  687. }
  688. ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
  689. if (componentResumeTimes == null) {
  690. componentResumeTimes = new ArrayMap<String, Long>();
  691. mLastResumeTimes.put(pkgName, componentResumeTimes);
  692. }
  693. componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
  694. }
  695. }
  696. @Override
  697. public void notePauseComponent(ComponentName componentName) {
  698. enforceCallingPermission();
  699. synchronized (mStatsLock) {
  700. String pkgName;
  701. if ((componentName == null) ||
  702. ((pkgName = componentName.getPackageName()) == null)) {
  703. return;
  704. }
  705. if (!mIsResumed) {
  706. if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
  707. + pkgName + " to be paused");
  708. return;
  709. }
  710. mIsResumed = false;
  711. if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
  712. PkgUsageStatsExtended pus = mStats.get(pkgName);
  713. if (pus == null) {
  714. // Weird some error here
  715. Slog.i(TAG, "No package stats for pkg:"+pkgName);
  716. return;
  717. }
  718. pus.updatePause();
  719. }
  720. // Persist current data to file if needed.
  721. writeStatsToFile(false, false);
  722. }
  723. @Override
  724. public void noteLaunchTime(ComponentName componentName, int millis) {
  725. enforceCallingPermission();
  726. String pkgName;
  727. if ((componentName == null) ||
  728. ((pkgName = componentName.getPackageName()) == null)) {
  729. return;
  730. }
  731. // Persist current data to file if needed.
  732. writeStatsToFile(false, false);
  733. synchronized (mStatsLock) {
  734. PkgUsageStatsExtended pus = mStats.get(pkgName);
  735. if (pus != null) {
  736. pus.addLaunchTime(componentName.getClassName(), millis);
  737. }
  738. }
  739. }
  740. public void noteFullyDrawnTime(ComponentName componentName, int millis) {
  741. enforceCallingPermission();
  742. String pkgName;
  743. if ((componentName == null) ||
  744. ((pkgName = componentName.getPackageName()) == null)) {
  745. return;
  746. }
  747. // Persist current data to file if needed.
  748. writeStatsToFile(false, false);
  749. synchronized (mStatsLock) {
  750. PkgUsageStatsExtended pus = mStats.get(pkgName);
  751. if (pus != null) {
  752. pus.addFullyDrawnTime(componentName.getClassName(), millis);
  753. }
  754. }
  755. }
  756. public void enforceCallingPermission() {
  757. if (Binder.getCallingPid() == Process.myPid()) {
  758. return;
  759. }
  760. mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
  761. Binder.getCallingPid(), Binder.getCallingUid(), null);
  762. }
  763. @Override
  764. public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
  765. mContext.enforceCallingOrSelfPermission(
  766. android.Manifest.permission.PACKAGE_USAGE_STATS, null);
  767. String pkgName;
  768. if ((componentName == null) ||
  769. ((pkgName = componentName.getPackageName()) == null)) {
  770. return null;
  771. }
  772. synchronized (mStatsLock) {
  773. PkgUsageStatsExtended pus = mStats.get(pkgName);
  774. Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
  775. if (pus == null && lastResumeTimes == null) {
  776. return null;
  777. }
  778. int launchCount = pus != null ? pus.mLaunchCount : 0;
  779. long usageTime = pus != null ? pus.mUsageTime : 0;
  780. return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
  781. }
  782. }
  783. @Override
  784. public PkgUsageStats[] getAllPkgUsageStats() {
  785. mContext.enforceCallingOrSelfPermission(
  786. android.Manifest.permission.PACKAGE_USAGE_STATS, null);
  787. synchronized (mStatsLock) {
  788. int size = mLastResumeTimes.size();
  789. if (size <= 0) {
  790. return null;
  791. }
  792. PkgUsageStats retArr[] = new PkgUsageStats[size];
  793. for (int i=0; i<size; i++) {
  794. String pkg = mLastResumeTimes.keyAt(i);
  795. long usageTime = 0;
  796. int launchCount = 0;
  797. PkgUsageStatsExtended pus = mStats.get(pkg);
  798. if (pus != null) {
  799. usageTime = pus.mUsageTime;
  800. launchCount = pus.mLaunchCount;
  801. }
  802. retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
  803. mLastResumeTimes.valueAt(i));
  804. }
  805. return retArr;
  806. }
  807. }
  808. static byte[] readFully(FileInputStream stream) throws IOException {
  809. int pos = 0;
  810. int avail = stream.available();
  811. byte[] data = new byte[avail];
  812. while (true) {
  813. int amt = stream.read(data, pos, data.length-pos);
  814. if (amt <= 0) {
  815. return data;
  816. }
  817. pos += amt;
  818. avail = stream.available();
  819. if (avail > data.length-pos) {
  820. byte[] newData = new byte[pos+avail];
  821. System.arraycopy(data, 0, newData, 0, pos);
  822. data = newData;
  823. }
  824. }
  825. }
  826. private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
  827. boolean deleteAfterPrint, HashSet<String> packages) {
  828. List<String> fileList = getUsageStatsFileListFLOCK();
  829. if (fileList == null) {
  830. return;
  831. }
  832. Collections.sort(fileList);
  833. for (String file : fileList) {
  834. if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
  835. // In this mode we don't print the current day's stats, since
  836. // they are incomplete.
  837. continue;
  838. }
  839. File dFile = new File(mDir, file);
  840. String dateStr = file.substring(FILE_PREFIX.length());
  841. if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
  842. // If the remainder does not start with a number, it is not a date,
  843. // so we should ignore it for purposes here.
  844. continue;
  845. }
  846. try {
  847. Parcel in = getParcelForFile(dFile);
  848. collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
  849. packages);
  850. if (deleteAfterPrint) {
  851. // Delete old file after collecting info only for checkin requests
  852. dFile.delete();
  853. }
  854. } catch (IOException e) {
  855. Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
  856. }
  857. }
  858. }
  859. private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
  860. String date, boolean isCompactOutput, HashSet<String> packages) {
  861. StringBuilder sb = new StringBuilder(512);
  862. if (isCompactOutput) {
  863. sb.append("D:");
  864. sb.append(CHECKIN_VERSION);
  865. sb.append(',');
  866. } else {
  867. sb.append("Date: ");
  868. }
  869. sb.append(date);
  870. int vers = in.readInt();
  871. if (vers != VERSION) {
  872. sb.append(" (old data version)");
  873. pw.println(sb.toString());
  874. return;
  875. }
  876. pw.println(sb.toString());
  877. int N = in.readInt();
  878. while (N > 0) {
  879. N--;
  880. String pkgName = in.readString();
  881. if (pkgName == null) {
  882. break;
  883. }
  884. sb.setLength(0);
  885. PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
  886. if (packages != null && !packages.contains(pkgName)) {
  887. // This package has not been requested -- don't print
  888. // anything for it.
  889. } else if (isCompactOutput) {
  890. sb.append("P:");
  891. sb.append(pkgName);
  892. sb.append(',');
  893. sb.append(pus.mLaunchCount);
  894. sb.append(',');
  895. sb.append(pus.mUsageTime);
  896. sb.append('\n');
  897. final int NLT = pus.mLaunchTimes.size();
  898. for (int i=0; i<NLT; i++) {
  899. sb.append("A:");
  900. String activity = pus.mLaunchTimes.keyAt(i);
  901. sb.append(activity);
  902. TimeStats times = pus.mLaunchTimes.valueAt(i);
  903. sb.append(',');
  904. sb.append(times.mCount);
  905. for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
  906. sb.append(",");
  907. sb.append(times.mTimes[j]);
  908. }
  909. sb.append('\n');
  910. }
  911. final int NFDT = pus.mFullyDrawnTimes.size();
  912. for (int i=0; i<NFDT; i++) {
  913. sb.append("A:");
  914. String activity = pus.mFullyDrawnTimes.keyAt(i);
  915. sb.append(activity);
  916. TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
  917. for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
  918. sb.append(",");
  919. sb.append(times.mTimes[j]);
  920. }
  921. sb.append('\n');
  922. }
  923. } else {
  924. sb.append(" ");
  925. sb.append(pkgName);
  926. sb.append(": ");
  927. sb.append(pus.mLaunchCount);
  928. sb.append(" times, ");
  929. sb.append(pus.mUsageTime);
  930. sb.append(" ms");
  931. sb.append('\n');
  932. final int NLT = pus.mLaunchTimes.size();
  933. for (int i=0; i<NLT; i++) {
  934. sb.append(" ");
  935. sb.append(pus.mLaunchTimes.keyAt(i));
  936. TimeStats times = pus.mLaunchTimes.valueAt(i);
  937. sb.append(": ");
  938. sb.append(times.mCount);
  939. sb.append(" starts");
  940. int lastBin = 0;
  941. for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
  942. if (times.mTimes[j] != 0) {
  943. sb.append(", ");
  944. sb.append(lastBin);
  945. sb.append('-');
  946. sb.append(LAUNCH_TIME_BINS[j]);
  947. sb.append("ms=");
  948. sb.append(times.mTimes[j]);
  949. }
  950. lastBin = LAUNCH_TIME_BINS[j];
  951. }
  952. if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) {
  953. sb.append(", ");
  954. sb.append(">=");
  955. sb.append(lastBin);
  956. sb.append("ms=");
  957. sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]);
  958. }
  959. sb.append('\n');
  960. }
  961. final int NFDT = pus.mFullyDrawnTimes.size();
  962. for (int i=0; i<NFDT; i++) {
  963. sb.append(" ");
  964. sb.append(pus.mFullyDrawnTimes.keyAt(i));
  965. TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
  966. sb.append(": fully drawn ");
  967. boolean needComma = false;
  968. int lastBin = 0;
  969. for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
  970. if (times.mTimes[j] != 0) {
  971. if (needComma) {
  972. sb.append(", ");
  973. } else {
  974. needComma = true;
  975. }
  976. sb.append(lastBin);
  977. sb.append('-');
  978. sb.append(LAUNCH_TIME_BINS[j]);
  979. sb.append("ms=");
  980. sb.append(times.mTimes[j]);
  981. }
  982. lastBin = LAUNCH_TIME_BINS[j];
  983. }
  984. if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) {
  985. if (needComma) {
  986. sb.append(", ");
  987. }
  988. sb.append(">=");
  989. sb.append(lastBin);
  990. sb.append("ms=");
  991. sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]);
  992. }
  993. sb.append('\n');
  994. }
  995. }
  996. pw.write(sb.toString());
  997. }
  998. }
  999. /**
  1000. * Searches array of arguments for the specified string
  1001. * @param args array of argument strings
  1002. * @param value value to search for
  1003. * @return true if the value is contained in the array
  1004. */
  1005. private static boolean scanArgs(String[] args, String value) {
  1006. if (args != null) {
  1007. for (String arg : args) {
  1008. if (value.equals(arg)) {
  1009. return true;
  1010. }
  1011. }
  1012. }
  1013. return false;
  1014. }
  1015. /**
  1016. * Searches array of arguments for the specified string's data
  1017. * @param args array of argument strings
  1018. * @param value value to search for
  1019. * @return the string of data after the arg, or null if there is none
  1020. */
  1021. private static String scanArgsData(String[] args, String value) {
  1022. if (args != null) {
  1023. final int N = args.length;
  1024. for (int i=0; i<N; i++) {
  1025. if (value.equals(args[i])) {
  1026. i++;
  1027. return i < N ? args[i] : null;
  1028. }
  1029. }
  1030. }
  1031. return null;
  1032. }
  1033. /*
  1034. * The data persisted to file is parsed and the stats are computed.
  1035. */
  1036. @Override
  1037. protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
  1038. if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
  1039. != PackageManager.PERMISSION_GRANTED) {
  1040. pw.println("Permission Denial: can't dump UsageStats from from pid="
  1041. + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
  1042. + " without permission " + android.Manifest.permission.DUMP);
  1043. return;
  1044. }
  1045. final boolean isCheckinRequest = scanArgs(args, "--checkin");
  1046. final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
  1047. final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
  1048. final String rawPackages = scanArgsData(args, "--packages");
  1049. // Make sure the current stats are written to the file. This
  1050. // doesn't need to be done if we are deleting files after printing,
  1051. // since in that case we won't print the current stats.
  1052. if (!deleteAfterPrint) {
  1053. writeStatsToFile(true, false);
  1054. }
  1055. HashSet<String> packages = null;
  1056. if (rawPackages != null) {
  1057. if (!"*".equals(rawPackages)) {
  1058. // A * is a wildcard to show all packages.
  1059. String[] names = rawPackages.split(",");
  1060. if (names.length != 0) {
  1061. packages = new HashSet<String>();
  1062. }
  1063. for (String n : names) {
  1064. packages.add(n);
  1065. }
  1066. }
  1067. } else if (isCheckinRequest) {
  1068. // If checkin doesn't specify any packages, then we simply won't
  1069. // show anything.
  1070. Slog.w(TAG, "Checkin without packages");
  1071. return;
  1072. }
  1073. synchronized (mFileLock) {
  1074. collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
  1075. }
  1076. }
  1077. }