diff options
Diffstat (limited to 'services/java/com/android/server/am/UsageStatsService.java')
-rw-r--r-- | services/java/com/android/server/am/UsageStatsService.java | 1173 |
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); - } - } - -} |