summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/am/UsageStatsService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/am/UsageStatsService.java')
-rw-r--r--services/java/com/android/server/am/UsageStatsService.java1173
1 files changed, 0 insertions, 1173 deletions
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
deleted file mode 100644
index e96d8b1..0000000
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ /dev/null
@@ -1,1173 +0,0 @@
-/*
- * Copyright (C) 2006-2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.FileUtils;
-import android.os.Parcel;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.app.IUsageStats;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.PkgUsageStats;
-import com.android.internal.util.FastXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * This service collects the statistics associated with usage
- * of various components, like when a particular package is launched or
- * paused and aggregates events like number of time a component is launched
- * total duration of a component launch.
- */
-public final class UsageStatsService extends IUsageStats.Stub {
- public static final String SERVICE_NAME = "usagestats";
- private static final boolean localLOGV = false;
- private static final boolean REPORT_UNEXPECTED = false;
- private static final String TAG = "UsageStats";
-
- // Current on-disk Parcel version
- private static final int VERSION = 1008;
-
- private static final int CHECKIN_VERSION = 4;
-
- private static final String FILE_PREFIX = "usage-";
-
- private static final String FILE_HISTORY = FILE_PREFIX + "history.xml";
-
- private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms
-
- private static final int MAX_NUM_FILES = 5;
-
- private static final int NUM_LAUNCH_TIME_BINS = 10;
- private static final int[] LAUNCH_TIME_BINS = {
- 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000
- };
-
- static IUsageStats sService;
- private Context mContext;
- // structure used to maintain statistics since the last checkin.
- final private ArrayMap<String, PkgUsageStatsExtended> mStats;
-
- // Maintains the last time any component was resumed, for all time.
- final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes;
-
- // To remove last-resume time stats when a pacakge is removed.
- private PackageMonitor mPackageMonitor;
-
- // Lock to update package stats. Methods suffixed by SLOCK should invoked with
- // this lock held
- final Object mStatsLock;
- // Lock to write to file. Methods suffixed by FLOCK should invoked with
- // this lock held.
- final Object mFileLock;
- // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
- private String mLastResumedPkg;
- private String mLastResumedComp;
- private boolean mIsResumed;
- private File mFile;
- private AtomicFile mHistoryFile;
- private String mFileLeaf;
- private File mDir;
-
- private Calendar mCal; // guarded by itself
-
- private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
- private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
- private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
-
- static class TimeStats {
- int count;
- int[] times = new int[NUM_LAUNCH_TIME_BINS];
-
- TimeStats() {
- }
-
- void incCount() {
- count++;
- }
-
- void add(int val) {
- final int[] bins = LAUNCH_TIME_BINS;
- for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
- if (val < bins[i]) {
- times[i]++;
- return;
- }
- }
- times[NUM_LAUNCH_TIME_BINS-1]++;
- }
-
- TimeStats(Parcel in) {
- count = in.readInt();
- final int[] localTimes = times;
- for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
- localTimes[i] = in.readInt();
- }
- }
-
- void writeToParcel(Parcel out) {
- out.writeInt(count);
- final int[] localTimes = times;
- for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
- out.writeInt(localTimes[i]);
- }
- }
- }
-
- private class PkgUsageStatsExtended {
- final ArrayMap<String, TimeStats> mLaunchTimes
- = new ArrayMap<String, TimeStats>();
- final ArrayMap<String, TimeStats> mFullyDrawnTimes
- = new ArrayMap<String, TimeStats>();
- int mLaunchCount;
- long mUsageTime;
- long mPausedTime;
- long mResumedTime;
-
- PkgUsageStatsExtended() {
- mLaunchCount = 0;
- mUsageTime = 0;
- }
-
- PkgUsageStatsExtended(Parcel in) {
- mLaunchCount = in.readInt();
- mUsageTime = in.readLong();
- if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
- + ", Usage time:" + mUsageTime);
-
- final int numLaunchTimeStats = in.readInt();
- if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
- mLaunchTimes.ensureCapacity(numLaunchTimeStats);
- for (int i=0; i<numLaunchTimeStats; i++) {
- String comp = in.readString();
- if (localLOGV) Slog.v(TAG, "Component: " + comp);
- TimeStats times = new TimeStats(in);
- mLaunchTimes.put(comp, times);
- }
-
- final int numFullyDrawnTimeStats = in.readInt();
- if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats);
- mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats);
- for (int i=0; i<numFullyDrawnTimeStats; i++) {
- String comp = in.readString();
- if (localLOGV) Slog.v(TAG, "Component: " + comp);
- TimeStats times = new TimeStats(in);
- mFullyDrawnTimes.put(comp, times);
- }
- }
-
- void updateResume(String comp, boolean launched) {
- if (launched) {
- mLaunchCount ++;
- }
- mResumedTime = SystemClock.elapsedRealtime();
- }
-
- void updatePause() {
- mPausedTime = SystemClock.elapsedRealtime();
- mUsageTime += (mPausedTime - mResumedTime);
- }
-
- void addLaunchCount(String comp) {
- TimeStats times = mLaunchTimes.get(comp);
- if (times == null) {
- times = new TimeStats();
- mLaunchTimes.put(comp, times);
- }
- times.incCount();
- }
-
- void addLaunchTime(String comp, int millis) {
- TimeStats times = mLaunchTimes.get(comp);
- if (times == null) {
- times = new TimeStats();
- mLaunchTimes.put(comp, times);
- }
- times.add(millis);
- }
-
- void addFullyDrawnTime(String comp, int millis) {
- TimeStats times = mFullyDrawnTimes.get(comp);
- if (times == null) {
- times = new TimeStats();
- mFullyDrawnTimes.put(comp, times);
- }
- times.add(millis);
- }
-
- void writeToParcel(Parcel out) {
- out.writeInt(mLaunchCount);
- out.writeLong(mUsageTime);
- final int numLaunchTimeStats = mLaunchTimes.size();
- out.writeInt(numLaunchTimeStats);
- for (int i=0; i<numLaunchTimeStats; i++) {
- out.writeString(mLaunchTimes.keyAt(i));
- mLaunchTimes.valueAt(i).writeToParcel(out);
- }
- final int numFullyDrawnTimeStats = mFullyDrawnTimes.size();
- out.writeInt(numFullyDrawnTimeStats);
- for (int i=0; i<numFullyDrawnTimeStats; i++) {
- out.writeString(mFullyDrawnTimes.keyAt(i));
- mFullyDrawnTimes.valueAt(i).writeToParcel(out);
- }
- }
-
- void clear() {
- mLaunchTimes.clear();
- mFullyDrawnTimes.clear();
- mLaunchCount = 0;
- mUsageTime = 0;
- }
- }
-
- UsageStatsService(String dir) {
- mStats = new ArrayMap<String, PkgUsageStatsExtended>();
- mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>();
- mStatsLock = new Object();
- mFileLock = new Object();
- mDir = new File(dir);
- mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
-
- mDir.mkdir();
-
- // Remove any old usage files from previous versions.
- File parentDir = mDir.getParentFile();
- String fList[] = parentDir.list();
- if (fList != null) {
- String prefix = mDir.getName() + ".";
- int i = fList.length;
- while (i > 0) {
- i--;
- if (fList[i].startsWith(prefix)) {
- Slog.i(TAG, "Deleting old usage file: " + fList[i]);
- (new File(parentDir, fList[i])).delete();
- }
- }
- }
-
- // Update current stats which are binned by date
- mFileLeaf = getCurrentDateStr(FILE_PREFIX);
- mFile = new File(mDir, mFileLeaf);
- mHistoryFile = new AtomicFile(new File(mDir, FILE_HISTORY));
- readStatsFromFile();
- readHistoryStatsFromFile();
- mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
- // mCal was set by getCurrentDateStr(), want to use that same time.
- mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
- }
-
- /*
- * Utility method to convert date into string.
- */
- private String getCurrentDateStr(String prefix) {
- StringBuilder sb = new StringBuilder();
- synchronized (mCal) {
- mCal.setTimeInMillis(System.currentTimeMillis());
- if (prefix != null) {
- sb.append(prefix);
- }
- sb.append(mCal.get(Calendar.YEAR));
- int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
- if (mm < 10) {
- sb.append("0");
- }
- sb.append(mm);
- int dd = mCal.get(Calendar.DAY_OF_MONTH);
- if (dd < 10) {
- sb.append("0");
- }
- sb.append(dd);
- }
- return sb.toString();
- }
-
- private Parcel getParcelForFile(File file) throws IOException {
- FileInputStream stream = new FileInputStream(file);
- byte[] raw = readFully(stream);
- Parcel in = Parcel.obtain();
- in.unmarshall(raw, 0, raw.length);
- in.setDataPosition(0);
- stream.close();
- return in;
- }
-
- private void readStatsFromFile() {
- File newFile = mFile;
- synchronized (mFileLock) {
- try {
- if (newFile.exists()) {
- readStatsFLOCK(newFile);
- } else {
- // Check for file limit before creating a new file
- checkFileLimitFLOCK();
- newFile.createNewFile();
- }
- } catch (IOException e) {
- Slog.w(TAG,"Error : " + e + " reading data from file:" + newFile);
- }
- }
- }
-
- private void readStatsFLOCK(File file) throws IOException {
- Parcel in = getParcelForFile(file);
- int vers = in.readInt();
- if (vers != VERSION) {
- Slog.w(TAG, "Usage stats version changed; dropping");
- return;
- }
- int N = in.readInt();
- while (N > 0) {
- N--;
- String pkgName = in.readString();
- if (pkgName == null) {
- break;
- }
- if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName);
- PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
- synchronized (mStatsLock) {
- mStats.put(pkgName, pus);
- }
- }
- }
-
- private void readHistoryStatsFromFile() {
- synchronized (mFileLock) {
- if (mHistoryFile.getBaseFile().exists()) {
- readHistoryStatsFLOCK(mHistoryFile);
- }
- }
- }
-
- private void readHistoryStatsFLOCK(AtomicFile file) {
- FileInputStream fis = null;
- try {
- fis = mHistoryFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int eventType = parser.getEventType();
- while (eventType != XmlPullParser.START_TAG) {
- eventType = parser.next();
- }
- String tagName = parser.getName();
- if ("usage-history".equals(tagName)) {
- String pkg = null;
- do {
- eventType = parser.next();
- if (eventType == XmlPullParser.START_TAG) {
- tagName = parser.getName();
- int depth = parser.getDepth();
- if ("pkg".equals(tagName) && depth == 2) {
- pkg = parser.getAttributeValue(null, "name");
- } else if ("comp".equals(tagName) && depth == 3 && pkg != null) {
- String comp = parser.getAttributeValue(null, "name");
- String lastResumeTimeStr = parser.getAttributeValue(null, "lrt");
- if (comp != null && lastResumeTimeStr != null) {
- try {
- long lastResumeTime = Long.parseLong(lastResumeTimeStr);
- synchronized (mStatsLock) {
- ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
- if (lrt == null) {
- lrt = new ArrayMap<String, Long>();
- mLastResumeTimes.put(pkg, lrt);
- }
- lrt.put(comp, lastResumeTime);
- }
- } catch (NumberFormatException e) {
- }
- }
- }
- } else if (eventType == XmlPullParser.END_TAG) {
- if ("pkg".equals(parser.getName())) {
- pkg = null;
- }
- }
- } while (eventType != XmlPullParser.END_DOCUMENT);
- }
- } catch (XmlPullParserException e) {
- Slog.w(TAG,"Error reading history stats: " + e);
- } catch (IOException e) {
- Slog.w(TAG,"Error reading history stats: " + e);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- private ArrayList<String> getUsageStatsFileListFLOCK() {
- // Check if there are too many files in the system and delete older files
- String fList[] = mDir.list();
- if (fList == null) {
- return null;
- }
- ArrayList<String> fileList = new ArrayList<String>();
- for (String file : fList) {
- if (!file.startsWith(FILE_PREFIX)) {
- continue;
- }
- if (file.endsWith(".bak")) {
- (new File(mDir, file)).delete();
- continue;
- }
- fileList.add(file);
- }
- return fileList;
- }
-
- private void checkFileLimitFLOCK() {
- // Get all usage stats output files
- ArrayList<String> fileList = getUsageStatsFileListFLOCK();
- if (fileList == null) {
- // Strange but we dont have to delete any thing
- return;
- }
- int count = fileList.size();
- if (count <= MAX_NUM_FILES) {
- return;
- }
- // Sort files
- Collections.sort(fileList);
- count -= MAX_NUM_FILES;
- // Delete older files
- for (int i = 0; i < count; i++) {
- String fileName = fileList.get(i);
- File file = new File(mDir, fileName);
- Slog.i(TAG, "Deleting usage file : " + fileName);
- file.delete();
- }
- }
-
- /**
- * Conditionally start up a disk write if it's been awhile, or the
- * day has rolled over.
- *
- * This is called indirectly from user-facing actions (when
- * 'force' is false) so it tries to be quick, without writing to
- * disk directly or acquiring heavy locks.
- *
- * @params force do an unconditional, synchronous stats flush
- * to disk on the current thread.
- * @params forceWriteHistoryStats Force writing of historical stats.
- */
- private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
- int curDay;
- synchronized (mCal) {
- mCal.setTimeInMillis(System.currentTimeMillis());
- curDay = mCal.get(Calendar.DAY_OF_YEAR);
- }
- final boolean dayChanged = curDay != mLastWriteDay.get();
-
- // Determine if the day changed... note that this will be wrong
- // if the year has changed but we are in the same day of year...
- // we can probably live with this.
- final long currElapsedTime = SystemClock.elapsedRealtime();
-
- // Fast common path, without taking the often-contentious
- // mFileLock.
- if (!force) {
- if (!dayChanged &&
- (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
- // wait till the next update
- return;
- }
- if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
- new Thread("UsageStatsService_DiskWriter") {
- public void run() {
- try {
- if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
- writeStatsToFile(true, false);
- } finally {
- mUnforcedDiskWriteRunning.set(false);
- if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
- }
- }
- }.start();
- }
- return;
- }
-
- synchronized (mFileLock) {
- // Get the most recent file
- mFileLeaf = getCurrentDateStr(FILE_PREFIX);
- // Copy current file to back up
- File backupFile = null;
- if (mFile != null && mFile.exists()) {
- backupFile = new File(mFile.getPath() + ".bak");
- if (!backupFile.exists()) {
- if (!mFile.renameTo(backupFile)) {
- Slog.w(TAG, "Failed to persist new stats");
- return;
- }
- } else {
- mFile.delete();
- }
- }
-
- try {
- // Write mStats to file
- writeStatsFLOCK(mFile);
- mLastWriteElapsedTime.set(currElapsedTime);
- if (dayChanged) {
- mLastWriteDay.set(curDay);
- // clear stats
- synchronized (mStats) {
- mStats.clear();
- }
- mFile = new File(mDir, mFileLeaf);
- checkFileLimitFLOCK();
- }
-
- if (dayChanged || forceWriteHistoryStats) {
- // Write history stats daily, or when forced (due to shutdown).
- writeHistoryStatsFLOCK(mHistoryFile);
- }
-
- // Delete the backup file
- if (backupFile != null) {
- backupFile.delete();
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failed writing stats to file:" + mFile);
- if (backupFile != null) {
- mFile.delete();
- backupFile.renameTo(mFile);
- }
- }
- }
- if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
- }
-
- private void writeStatsFLOCK(File file) throws IOException {
- FileOutputStream stream = new FileOutputStream(file);
- try {
- Parcel out = Parcel.obtain();
- writeStatsToParcelFLOCK(out);
- stream.write(out.marshall());
- out.recycle();
- stream.flush();
- } finally {
- FileUtils.sync(stream);
- stream.close();
- }
- }
-
- private void writeStatsToParcelFLOCK(Parcel out) {
- synchronized (mStatsLock) {
- out.writeInt(VERSION);
- Set<String> keys = mStats.keySet();
- out.writeInt(keys.size());
- for (String key : keys) {
- PkgUsageStatsExtended pus = mStats.get(key);
- out.writeString(key);
- pus.writeToParcel(out);
- }
- }
- }
-
- /** Filter out stats for any packages which aren't present anymore. */
- private void filterHistoryStats() {
- synchronized (mStatsLock) {
- IPackageManager pm = AppGlobals.getPackageManager();
- for (int i=0; i<mLastResumeTimes.size(); i++) {
- String pkg = mLastResumeTimes.keyAt(i);
- try {
- if (pm.getPackageUid(pkg, 0) < 0) {
- mLastResumeTimes.removeAt(i);
- i--;
- }
- } catch (RemoteException e) {
- }
- }
- }
- }
-
- private void writeHistoryStatsFLOCK(AtomicFile historyFile) {
- FileOutputStream fos = null;
- try {
- fos = historyFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, "utf-8");
- out.startDocument(null, true);
- out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- out.startTag(null, "usage-history");
- synchronized (mStatsLock) {
- for (int i=0; i<mLastResumeTimes.size(); i++) {
- out.startTag(null, "pkg");
- out.attribute(null, "name", mLastResumeTimes.keyAt(i));
- ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
- for (int j=0; j<comp.size(); j++) {
- out.startTag(null, "comp");
- out.attribute(null, "name", comp.keyAt(j));
- out.attribute(null, "lrt", comp.valueAt(j).toString());
- out.endTag(null, "comp");
- }
- out.endTag(null, "pkg");
- }
- }
- out.endTag(null, "usage-history");
- out.endDocument();
-
- historyFile.finishWrite(fos);
- } catch (IOException e) {
- Slog.w(TAG,"Error writing history stats" + e);
- if (fos != null) {
- historyFile.failWrite(fos);
- }
- }
- }
-
- public void publish(Context context) {
- mContext = context;
- ServiceManager.addService(SERVICE_NAME, asBinder());
- }
-
- /**
- * Start watching packages to remove stats when a package is uninstalled.
- * May only be called when the package manager is ready.
- */
- public void monitorPackages() {
- mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageRemovedAllUsers(String packageName, int uid) {
- synchronized (mStatsLock) {
- mLastResumeTimes.remove(packageName);
- }
- }
- };
- mPackageMonitor.register(mContext, null, true);
- filterHistoryStats();
- }
-
- public void shutdown() {
- if (mPackageMonitor != null) {
- mPackageMonitor.unregister();
- }
- Slog.i(TAG, "Writing usage stats before shutdown...");
- writeStatsToFile(true, true);
- }
-
- public static IUsageStats getService() {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService(SERVICE_NAME);
- sService = asInterface(b);
- return sService;
- }
-
- public void noteResumeComponent(ComponentName componentName) {
- enforceCallingPermission();
- String pkgName;
- synchronized (mStatsLock) {
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
-
- final boolean samePackage = pkgName.equals(mLastResumedPkg);
- if (mIsResumed) {
- if (mLastResumedPkg != null) {
- // We last resumed some other package... just pause it now
- // to recover.
- if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
- + " while already resumed in " + mLastResumedPkg);
- PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
- if (pus != null) {
- pus.updatePause();
- }
- }
- }
-
- final boolean sameComp = samePackage
- && componentName.getClassName().equals(mLastResumedComp);
-
- mIsResumed = true;
- mLastResumedPkg = pkgName;
- mLastResumedComp = componentName.getClassName();
-
- if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- if (pus == null) {
- pus = new PkgUsageStatsExtended();
- mStats.put(pkgName, pus);
- }
- pus.updateResume(mLastResumedComp, !samePackage);
- if (!sameComp) {
- pus.addLaunchCount(mLastResumedComp);
- }
-
- ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
- if (componentResumeTimes == null) {
- componentResumeTimes = new ArrayMap<String, Long>();
- mLastResumeTimes.put(pkgName, componentResumeTimes);
- }
- componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
- }
- }
-
- public void notePauseComponent(ComponentName componentName) {
- enforceCallingPermission();
-
- synchronized (mStatsLock) {
- String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
- if (!mIsResumed) {
- if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
- + pkgName + " to be paused");
- return;
- }
- mIsResumed = false;
-
- if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
-
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- if (pus == null) {
- // Weird some error here
- Slog.i(TAG, "No package stats for pkg:"+pkgName);
- return;
- }
- pus.updatePause();
- }
-
- // Persist current data to file if needed.
- writeStatsToFile(false, false);
- }
-
- public void noteLaunchTime(ComponentName componentName, int millis) {
- enforceCallingPermission();
- String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
-
- // Persist current data to file if needed.
- writeStatsToFile(false, false);
-
- synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- if (pus != null) {
- pus.addLaunchTime(componentName.getClassName(), millis);
- }
- }
- }
-
- public void noteFullyDrawnTime(ComponentName componentName, int millis) {
- enforceCallingPermission();
- String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
-
- // Persist current data to file if needed.
- writeStatsToFile(false, false);
-
- synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- if (pus != null) {
- pus.addFullyDrawnTime(componentName.getClassName(), millis);
- }
- }
- }
-
- public void enforceCallingPermission() {
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
-
- public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
- String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return null;
- }
- synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
- if (pus == null && lastResumeTimes == null) {
- return null;
- }
- int launchCount = pus != null ? pus.mLaunchCount : 0;
- long usageTime = pus != null ? pus.mUsageTime : 0;
- return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
- }
- }
-
- public PkgUsageStats[] getAllPkgUsageStats() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
- synchronized (mStatsLock) {
- int size = mLastResumeTimes.size();
- if (size <= 0) {
- return null;
- }
- PkgUsageStats retArr[] = new PkgUsageStats[size];
- for (int i=0; i<size; i++) {
- String pkg = mLastResumeTimes.keyAt(i);
- long usageTime = 0;
- int launchCount = 0;
-
- PkgUsageStatsExtended pus = mStats.get(pkg);
- if (pus != null) {
- usageTime = pus.mUsageTime;
- launchCount = pus.mLaunchCount;
- }
- retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
- mLastResumeTimes.valueAt(i));
- }
- return retArr;
- }
- }
-
- static byte[] readFully(FileInputStream stream) throws java.io.IOException {
- int pos = 0;
- int avail = stream.available();
- byte[] data = new byte[avail];
- while (true) {
- int amt = stream.read(data, pos, data.length-pos);
- if (amt <= 0) {
- return data;
- }
- pos += amt;
- avail = stream.available();
- if (avail > data.length-pos) {
- byte[] newData = new byte[pos+avail];
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- }
-
- private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
- boolean deleteAfterPrint, HashSet<String> packages) {
- List<String> fileList = getUsageStatsFileListFLOCK();
- if (fileList == null) {
- return;
- }
- Collections.sort(fileList);
- for (String file : fileList) {
- if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
- // In this mode we don't print the current day's stats, since
- // they are incomplete.
- continue;
- }
- File dFile = new File(mDir, file);
- String dateStr = file.substring(FILE_PREFIX.length());
- if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
- // If the remainder does not start with a number, it is not a date,
- // so we should ignore it for purposes here.
- continue;
- }
- try {
- Parcel in = getParcelForFile(dFile);
- collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
- packages);
- if (deleteAfterPrint) {
- // Delete old file after collecting info only for checkin requests
- dFile.delete();
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file);
- return;
- } catch (IOException e) {
- Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
- }
- }
- }
-
- private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
- String date, boolean isCompactOutput, HashSet<String> packages) {
- StringBuilder sb = new StringBuilder(512);
- if (isCompactOutput) {
- sb.append("D:");
- sb.append(CHECKIN_VERSION);
- sb.append(',');
- } else {
- sb.append("Date: ");
- }
-
- sb.append(date);
-
- int vers = in.readInt();
- if (vers != VERSION) {
- sb.append(" (old data version)");
- pw.println(sb.toString());
- return;
- }
-
- pw.println(sb.toString());
- int N = in.readInt();
-
- while (N > 0) {
- N--;
- String pkgName = in.readString();
- if (pkgName == null) {
- break;
- }
- sb.setLength(0);
- PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
- if (packages != null && !packages.contains(pkgName)) {
- // This package has not been requested -- don't print
- // anything for it.
- } else if (isCompactOutput) {
- sb.append("P:");
- sb.append(pkgName);
- sb.append(',');
- sb.append(pus.mLaunchCount);
- sb.append(',');
- sb.append(pus.mUsageTime);
- sb.append('\n');
- final int NLT = pus.mLaunchTimes.size();
- for (int i=0; i<NLT; i++) {
- sb.append("A:");
- String activity = pus.mLaunchTimes.keyAt(i);
- sb.append(activity);
- TimeStats times = pus.mLaunchTimes.valueAt(i);
- sb.append(',');
- sb.append(times.count);
- for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
- sb.append(",");
- sb.append(times.times[j]);
- }
- sb.append('\n');
- }
- final int NFDT = pus.mFullyDrawnTimes.size();
- for (int i=0; i<NFDT; i++) {
- sb.append("A:");
- String activity = pus.mFullyDrawnTimes.keyAt(i);
- sb.append(activity);
- TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
- for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
- sb.append(",");
- sb.append(times.times[j]);
- }
- sb.append('\n');
- }
-
- } else {
- sb.append(" ");
- sb.append(pkgName);
- sb.append(": ");
- sb.append(pus.mLaunchCount);
- sb.append(" times, ");
- sb.append(pus.mUsageTime);
- sb.append(" ms");
- sb.append('\n');
- final int NLT = pus.mLaunchTimes.size();
- for (int i=0; i<NLT; i++) {
- sb.append(" ");
- sb.append(pus.mLaunchTimes.keyAt(i));
- TimeStats times = pus.mLaunchTimes.valueAt(i);
- sb.append(": ");
- sb.append(times.count);
- sb.append(" starts");
- int lastBin = 0;
- for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
- if (times.times[j] != 0) {
- sb.append(", ");
- sb.append(lastBin);
- sb.append('-');
- sb.append(LAUNCH_TIME_BINS[j]);
- sb.append("ms=");
- sb.append(times.times[j]);
- }
- lastBin = LAUNCH_TIME_BINS[j];
- }
- if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
- sb.append(", ");
- sb.append(">=");
- sb.append(lastBin);
- sb.append("ms=");
- sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
- }
- sb.append('\n');
- }
- final int NFDT = pus.mFullyDrawnTimes.size();
- for (int i=0; i<NFDT; i++) {
- sb.append(" ");
- sb.append(pus.mFullyDrawnTimes.keyAt(i));
- TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
- sb.append(": fully drawn ");
- boolean needComma = false;
- int lastBin = 0;
- for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
- if (times.times[j] != 0) {
- if (needComma) {
- sb.append(", ");
- } else {
- needComma = true;
- }
- sb.append(lastBin);
- sb.append('-');
- sb.append(LAUNCH_TIME_BINS[j]);
- sb.append("ms=");
- sb.append(times.times[j]);
- }
- lastBin = LAUNCH_TIME_BINS[j];
- }
- if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
- if (needComma) {
- sb.append(", ");
- }
- sb.append(">=");
- sb.append(lastBin);
- sb.append("ms=");
- sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
- }
- sb.append('\n');
- }
- }
-
- pw.write(sb.toString());
- }
- }
-
- /**
- * Searches array of arguments for the specified string
- * @param args array of argument strings
- * @param value value to search for
- * @return true if the value is contained in the array
- */
- private static boolean scanArgs(String[] args, String value) {
- if (args != null) {
- for (String arg : args) {
- if (value.equals(arg)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Searches array of arguments for the specified string's data
- * @param args array of argument strings
- * @param value value to search for
- * @return the string of data after the arg, or null if there is none
- */
- private static String scanArgsData(String[] args, String value) {
- if (args != null) {
- final int N = args.length;
- for (int i=0; i<N; i++) {
- if (value.equals(args[i])) {
- i++;
- return i < N ? args[i] : null;
- }
- }
- }
- return null;
- }
-
- @Override
- /*
- * The data persisted to file is parsed and the stats are computed.
- */
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump UsageStats from from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " without permission " + android.Manifest.permission.DUMP);
- return;
- }
-
- final boolean isCheckinRequest = scanArgs(args, "--checkin");
- final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
- final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
- final String rawPackages = scanArgsData(args, "--packages");
-
- // Make sure the current stats are written to the file. This
- // doesn't need to be done if we are deleting files after printing,
- // since it that case we won't print the current stats.
- if (!deleteAfterPrint) {
- writeStatsToFile(true, false);
- }
-
- HashSet<String> packages = null;
- if (rawPackages != null) {
- if (!"*".equals(rawPackages)) {
- // A * is a wildcard to show all packages.
- String[] names = rawPackages.split(",");
- for (String n : names) {
- if (packages == null) {
- packages = new HashSet<String>();
- }
- packages.add(n);
- }
- }
- } else if (isCheckinRequest) {
- // If checkin doesn't specify any packages, then we simply won't
- // show anything.
- Slog.w(TAG, "Checkin without packages");
- return;
- }
-
- synchronized (mFileLock) {
- collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
- }
- }
-
-}