blob: 09cb344531c361355839eca81898dc480342d5d8 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package com.android.server.am;
18
Dianne Hackborn904a8572013-06-28 18:12:31 -070019import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.ComponentName;
21import android.content.Context;
Dianne Hackborn904a8572013-06-28 18:12:31 -070022import android.content.pm.IPackageManager;
Mark Brophyc6350272011-08-05 16:16:39 +010023import android.content.pm.PackageInfo;
Kenny Root3abd75b2011-09-29 11:00:41 -070024import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.os.Binder;
26import android.os.IBinder;
Dianne Hackborn8bdf5932010-10-15 12:54:40 -070027import android.os.FileUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Parcel;
29import android.os.Process;
Dianne Hackborn904a8572013-06-28 18:12:31 -070030import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.ServiceManager;
32import android.os.SystemClock;
Dianne Hackborn904a8572013-06-28 18:12:31 -070033import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070034import android.util.AtomicFile;
Joe Onorato8a9b2202010-02-26 18:56:32 -080035import android.util.Slog;
Mark Brophyc6350272011-08-05 16:16:39 +010036import android.util.Xml;
37
38import com.android.internal.app.IUsageStats;
39import com.android.internal.content.PackageMonitor;
Mark Brophyc6350272011-08-05 16:16:39 +010040import com.android.internal.os.PkgUsageStats;
41import com.android.internal.util.FastXmlSerializer;
42
43import org.xmlpull.v1.XmlPullParser;
44import org.xmlpull.v1.XmlPullParserException;
45import org.xmlpull.v1.XmlSerializer;
46
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.io.File;
48import java.io.FileDescriptor;
49import java.io.FileInputStream;
50import java.io.FileNotFoundException;
51import java.io.FileOutputStream;
52import java.io.IOException;
53import java.io.PrintWriter;
54import java.util.ArrayList;
55import java.util.Calendar;
56import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import java.util.HashMap;
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -070058import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import java.util.List;
60import java.util.Map;
61import java.util.Set;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070062import java.util.TimeZone;
Brad Fitzpatrick389a9162010-08-03 15:41:05 -070063import java.util.concurrent.atomic.AtomicBoolean;
64import java.util.concurrent.atomic.AtomicInteger;
65import java.util.concurrent.atomic.AtomicLong;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67/**
68 * This service collects the statistics associated with usage
69 * of various components, like when a particular package is launched or
70 * paused and aggregates events like number of time a component is launched
71 * total duration of a component launch.
72 */
73public final class UsageStatsService extends IUsageStats.Stub {
74 public static final String SERVICE_NAME = "usagestats";
75 private static final boolean localLOGV = false;
Dianne Hackborncef65ee2010-09-30 18:27:22 -070076 private static final boolean REPORT_UNEXPECTED = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 private static final String TAG = "UsageStats";
Dianne Hackborn6447ca32009-04-07 19:50:08 -070078
79 // Current on-disk Parcel version
Dianne Hackborn2286cdc2013-07-01 19:10:06 -070080 private static final int VERSION = 1008;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070081
Dianne Hackborn760ec4a2009-06-17 20:05:26 -070082 private static final int CHECKIN_VERSION = 4;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070083
84 private static final String FILE_PREFIX = "usage-";
Mark Brophyc6350272011-08-05 16:16:39 +010085
86 private static final String FILE_HISTORY = FILE_PREFIX + "history.xml";
87
Dianne Hackborn6447ca32009-04-07 19:50:08 -070088 private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms
89
90 private static final int MAX_NUM_FILES = 5;
91
Dianne Hackbornf210d6b2009-04-13 18:42:49 -070092 private static final int NUM_LAUNCH_TIME_BINS = 10;
93 private static final int[] LAUNCH_TIME_BINS = {
94 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000
95 };
Dianne Hackborn6447ca32009-04-07 19:50:08 -070096
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 static IUsageStats sService;
98 private Context mContext;
99 // structure used to maintain statistics since the last checkin.
Dianne Hackborn904a8572013-06-28 18:12:31 -0700100 final private ArrayMap<String, PkgUsageStatsExtended> mStats;
Mark Brophyc6350272011-08-05 16:16:39 +0100101
102 // Maintains the last time any component was resumed, for all time.
Dianne Hackborn904a8572013-06-28 18:12:31 -0700103 final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes;
Mark Brophyc6350272011-08-05 16:16:39 +0100104
105 // To remove last-resume time stats when a pacakge is removed.
106 private PackageMonitor mPackageMonitor;
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 // Lock to update package stats. Methods suffixed by SLOCK should invoked with
109 // this lock held
110 final Object mStatsLock;
111 // Lock to write to file. Methods suffixed by FLOCK should invoked with
112 // this lock held.
113 final Object mFileLock;
114 // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700115 private String mLastResumedPkg;
116 private String mLastResumedComp;
117 private boolean mIsResumed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 private File mFile;
Mark Brophyc6350272011-08-05 16:16:39 +0100119 private AtomicFile mHistoryFile;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700120 private String mFileLeaf;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700121 private File mDir;
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700122
123 private Calendar mCal; // guarded by itself
124
125 private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
126 private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
127 private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700128
129 static class TimeStats {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700130 int count;
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700131 int[] times = new int[NUM_LAUNCH_TIME_BINS];
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700132
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700133 TimeStats() {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700134 }
135
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700136 void incCount() {
137 count++;
138 }
139
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700140 void add(int val) {
141 final int[] bins = LAUNCH_TIME_BINS;
142 for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
143 if (val < bins[i]) {
144 times[i]++;
145 return;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700146 }
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700147 }
148 times[NUM_LAUNCH_TIME_BINS-1]++;
149 }
150
151 TimeStats(Parcel in) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700152 count = in.readInt();
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700153 final int[] localTimes = times;
154 for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
155 localTimes[i] = in.readInt();
156 }
157 }
158
159 void writeToParcel(Parcel out) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700160 out.writeInt(count);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700161 final int[] localTimes = times;
162 for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
163 out.writeInt(localTimes[i]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700164 }
165 }
166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167
168 private class PkgUsageStatsExtended {
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700169 final ArrayMap<String, TimeStats> mLaunchTimes
170 = new ArrayMap<String, TimeStats>();
171 final ArrayMap<String, TimeStats> mFullyDrawnTimes
172 = new ArrayMap<String, TimeStats>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 int mLaunchCount;
174 long mUsageTime;
175 long mPausedTime;
176 long mResumedTime;
177
178 PkgUsageStatsExtended() {
179 mLaunchCount = 0;
180 mUsageTime = 0;
181 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700182
183 PkgUsageStatsExtended(Parcel in) {
184 mLaunchCount = in.readInt();
185 mUsageTime = in.readLong();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800186 if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700187 + ", Usage time:" + mUsageTime);
188
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700189 final int numLaunchTimeStats = in.readInt();
190 if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
191 mLaunchTimes.ensureCapacity(numLaunchTimeStats);
192 for (int i=0; i<numLaunchTimeStats; i++) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700193 String comp = in.readString();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800194 if (localLOGV) Slog.v(TAG, "Component: " + comp);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700195 TimeStats times = new TimeStats(in);
196 mLaunchTimes.put(comp, times);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700197 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700198
199 final int numFullyDrawnTimeStats = in.readInt();
200 if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats);
201 mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats);
202 for (int i=0; i<numFullyDrawnTimeStats; i++) {
203 String comp = in.readString();
204 if (localLOGV) Slog.v(TAG, "Component: " + comp);
205 TimeStats times = new TimeStats(in);
206 mFullyDrawnTimes.put(comp, times);
207 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700208 }
Mark Brophy9fc03302011-07-01 16:56:24 +0100209
210 void updateResume(String comp, boolean launched) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700211 if (launched) {
212 mLaunchCount ++;
213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 mResumedTime = SystemClock.elapsedRealtime();
215 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 void updatePause() {
218 mPausedTime = SystemClock.elapsedRealtime();
219 mUsageTime += (mPausedTime - mResumedTime);
220 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700221
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700222 void addLaunchCount(String comp) {
223 TimeStats times = mLaunchTimes.get(comp);
224 if (times == null) {
225 times = new TimeStats();
226 mLaunchTimes.put(comp, times);
227 }
228 times.incCount();
229 }
230
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700231 void addLaunchTime(String comp, int millis) {
232 TimeStats times = mLaunchTimes.get(comp);
233 if (times == null) {
234 times = new TimeStats();
235 mLaunchTimes.put(comp, times);
236 }
237 times.add(millis);
238 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700239
240 void addFullyDrawnTime(String comp, int millis) {
241 TimeStats times = mFullyDrawnTimes.get(comp);
242 if (times == null) {
243 times = new TimeStats();
244 mFullyDrawnTimes.put(comp, times);
245 }
246 times.add(millis);
247 }
248
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700249 void writeToParcel(Parcel out) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700250 out.writeInt(mLaunchCount);
251 out.writeLong(mUsageTime);
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700252 final int numLaunchTimeStats = mLaunchTimes.size();
253 out.writeInt(numLaunchTimeStats);
254 for (int i=0; i<numLaunchTimeStats; i++) {
255 out.writeString(mLaunchTimes.keyAt(i));
256 mLaunchTimes.valueAt(i).writeToParcel(out);
257 }
258 final int numFullyDrawnTimeStats = mFullyDrawnTimes.size();
259 out.writeInt(numFullyDrawnTimeStats);
260 for (int i=0; i<numFullyDrawnTimeStats; i++) {
261 out.writeString(mFullyDrawnTimes.keyAt(i));
262 mFullyDrawnTimes.valueAt(i).writeToParcel(out);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700263 }
264 }
265
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 void clear() {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700267 mLaunchTimes.clear();
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700268 mFullyDrawnTimes.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 mLaunchCount = 0;
270 mUsageTime = 0;
271 }
272 }
273
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700274 UsageStatsService(String dir) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700275 mStats = new ArrayMap<String, PkgUsageStatsExtended>();
276 mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 mStatsLock = new Object();
278 mFileLock = new Object();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700279 mDir = new File(dir);
280 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
281
282 mDir.mkdir();
283
284 // Remove any old usage files from previous versions.
285 File parentDir = mDir.getParentFile();
286 String fList[] = parentDir.list();
287 if (fList != null) {
288 String prefix = mDir.getName() + ".";
289 int i = fList.length;
290 while (i > 0) {
291 i--;
292 if (fList[i].startsWith(prefix)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800293 Slog.i(TAG, "Deleting old usage file: " + fList[i]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700294 (new File(parentDir, fList[i])).delete();
295 }
296 }
297 }
298
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 // Update current stats which are binned by date
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700300 mFileLeaf = getCurrentDateStr(FILE_PREFIX);
301 mFile = new File(mDir, mFileLeaf);
Mark Brophyc6350272011-08-05 16:16:39 +0100302 mHistoryFile = new AtomicFile(new File(mDir, FILE_HISTORY));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 readStatsFromFile();
Mark Brophyc6350272011-08-05 16:16:39 +0100304 readHistoryStatsFromFile();
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700305 mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700306 // mCal was set by getCurrentDateStr(), want to use that same time.
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700307 mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 }
309
310 /*
311 * Utility method to convert date into string.
312 */
313 private String getCurrentDateStr(String prefix) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 StringBuilder sb = new StringBuilder();
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700315 synchronized (mCal) {
316 mCal.setTimeInMillis(System.currentTimeMillis());
317 if (prefix != null) {
318 sb.append(prefix);
319 }
320 sb.append(mCal.get(Calendar.YEAR));
321 int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
322 if (mm < 10) {
323 sb.append("0");
324 }
325 sb.append(mm);
326 int dd = mCal.get(Calendar.DAY_OF_MONTH);
327 if (dd < 10) {
328 sb.append("0");
329 }
330 sb.append(dd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 return sb.toString();
333 }
334
335 private Parcel getParcelForFile(File file) throws IOException {
336 FileInputStream stream = new FileInputStream(file);
337 byte[] raw = readFully(stream);
338 Parcel in = Parcel.obtain();
339 in.unmarshall(raw, 0, raw.length);
340 in.setDataPosition(0);
341 stream.close();
342 return in;
343 }
344
345 private void readStatsFromFile() {
346 File newFile = mFile;
347 synchronized (mFileLock) {
348 try {
349 if (newFile.exists()) {
350 readStatsFLOCK(newFile);
351 } else {
352 // Check for file limit before creating a new file
353 checkFileLimitFLOCK();
354 newFile.createNewFile();
355 }
356 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800357 Slog.w(TAG,"Error : " + e + " reading data from file:" + newFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
359 }
360 }
361
362 private void readStatsFLOCK(File file) throws IOException {
363 Parcel in = getParcelForFile(file);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700364 int vers = in.readInt();
365 if (vers != VERSION) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800366 Slog.w(TAG, "Usage stats version changed; dropping");
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700367 return;
368 }
369 int N = in.readInt();
370 while (N > 0) {
371 N--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 String pkgName = in.readString();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700373 if (pkgName == null) {
374 break;
375 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800376 if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700377 PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 synchronized (mStatsLock) {
379 mStats.put(pkgName, pus);
380 }
381 }
382 }
383
Mark Brophyc6350272011-08-05 16:16:39 +0100384 private void readHistoryStatsFromFile() {
385 synchronized (mFileLock) {
386 if (mHistoryFile.getBaseFile().exists()) {
387 readHistoryStatsFLOCK(mHistoryFile);
388 }
389 }
390 }
391
392 private void readHistoryStatsFLOCK(AtomicFile file) {
393 FileInputStream fis = null;
394 try {
395 fis = mHistoryFile.openRead();
396 XmlPullParser parser = Xml.newPullParser();
397 parser.setInput(fis, null);
398 int eventType = parser.getEventType();
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000399 while (eventType != XmlPullParser.START_TAG &&
400 eventType != XmlPullParser.END_DOCUMENT) {
Mark Brophyc6350272011-08-05 16:16:39 +0100401 eventType = parser.next();
402 }
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000403 if (eventType == XmlPullParser.END_DOCUMENT) {
404 return;
405 }
406
Mark Brophyc6350272011-08-05 16:16:39 +0100407 String tagName = parser.getName();
408 if ("usage-history".equals(tagName)) {
409 String pkg = null;
410 do {
411 eventType = parser.next();
412 if (eventType == XmlPullParser.START_TAG) {
413 tagName = parser.getName();
414 int depth = parser.getDepth();
415 if ("pkg".equals(tagName) && depth == 2) {
416 pkg = parser.getAttributeValue(null, "name");
417 } else if ("comp".equals(tagName) && depth == 3 && pkg != null) {
418 String comp = parser.getAttributeValue(null, "name");
419 String lastResumeTimeStr = parser.getAttributeValue(null, "lrt");
420 if (comp != null && lastResumeTimeStr != null) {
421 try {
422 long lastResumeTime = Long.parseLong(lastResumeTimeStr);
423 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700424 ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
Mark Brophyc6350272011-08-05 16:16:39 +0100425 if (lrt == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700426 lrt = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100427 mLastResumeTimes.put(pkg, lrt);
428 }
429 lrt.put(comp, lastResumeTime);
430 }
431 } catch (NumberFormatException e) {
432 }
433 }
434 }
435 } else if (eventType == XmlPullParser.END_TAG) {
436 if ("pkg".equals(parser.getName())) {
437 pkg = null;
438 }
439 }
440 } while (eventType != XmlPullParser.END_DOCUMENT);
441 }
442 } catch (XmlPullParserException e) {
443 Slog.w(TAG,"Error reading history stats: " + e);
444 } catch (IOException e) {
445 Slog.w(TAG,"Error reading history stats: " + e);
446 } finally {
447 if (fis != null) {
448 try {
449 fis.close();
450 } catch (IOException e) {
451 }
452 }
453 }
454 }
455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 private ArrayList<String> getUsageStatsFileListFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 // Check if there are too many files in the system and delete older files
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700458 String fList[] = mDir.list();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 if (fList == null) {
460 return null;
461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 ArrayList<String> fileList = new ArrayList<String>();
463 for (String file : fList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700464 if (!file.startsWith(FILE_PREFIX)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 continue;
466 }
467 if (file.endsWith(".bak")) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700468 (new File(mDir, file)).delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 continue;
470 }
471 fileList.add(file);
472 }
473 return fileList;
474 }
475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 private void checkFileLimitFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 // Get all usage stats output files
478 ArrayList<String> fileList = getUsageStatsFileListFLOCK();
479 if (fileList == null) {
480 // Strange but we dont have to delete any thing
481 return;
482 }
483 int count = fileList.size();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700484 if (count <= MAX_NUM_FILES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 return;
486 }
487 // Sort files
488 Collections.sort(fileList);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700489 count -= MAX_NUM_FILES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 // Delete older files
491 for (int i = 0; i < count; i++) {
492 String fileName = fileList.get(i);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700493 File file = new File(mDir, fileName);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800494 Slog.i(TAG, "Deleting usage file : " + fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 file.delete();
496 }
497 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700498
499 /**
500 * Conditionally start up a disk write if it's been awhile, or the
501 * day has rolled over.
502 *
503 * This is called indirectly from user-facing actions (when
504 * 'force' is false) so it tries to be quick, without writing to
505 * disk directly or acquiring heavy locks.
506 *
507 * @params force do an unconditional, synchronous stats flush
508 * to disk on the current thread.
Mark Brophyc6350272011-08-05 16:16:39 +0100509 * @params forceWriteHistoryStats Force writing of historical stats.
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700510 */
Mark Brophyc6350272011-08-05 16:16:39 +0100511 private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700512 int curDay;
513 synchronized (mCal) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700514 mCal.setTimeInMillis(System.currentTimeMillis());
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700515 curDay = mCal.get(Calendar.DAY_OF_YEAR);
516 }
517 final boolean dayChanged = curDay != mLastWriteDay.get();
518
519 // Determine if the day changed... note that this will be wrong
520 // if the year has changed but we are in the same day of year...
521 // we can probably live with this.
522 final long currElapsedTime = SystemClock.elapsedRealtime();
523
524 // Fast common path, without taking the often-contentious
525 // mFileLock.
526 if (!force) {
527 if (!dayChanged &&
528 (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
529 // wait till the next update
530 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700532 if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
533 new Thread("UsageStatsService_DiskWriter") {
534 public void run() {
535 try {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700536 if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
Mark Brophyc6350272011-08-05 16:16:39 +0100537 writeStatsToFile(true, false);
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700538 } finally {
539 mUnforcedDiskWriteRunning.set(false);
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700540 if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700541 }
542 }
543 }.start();
544 }
545 return;
546 }
547
548 synchronized (mFileLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 // Get the most recent file
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700550 mFileLeaf = getCurrentDateStr(FILE_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 // Copy current file to back up
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800552 File backupFile = null;
553 if (mFile != null && mFile.exists()) {
554 backupFile = new File(mFile.getPath() + ".bak");
Dianne Hackborn1afd1c92010-03-18 22:47:17 -0700555 if (!backupFile.exists()) {
556 if (!mFile.renameTo(backupFile)) {
557 Slog.w(TAG, "Failed to persist new stats");
558 return;
559 }
560 } else {
561 mFile.delete();
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800562 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700563 }
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 // Write mStats to file
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700567 writeStatsFLOCK(mFile);
568 mLastWriteElapsedTime.set(currElapsedTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 if (dayChanged) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700570 mLastWriteDay.set(curDay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 // clear stats
572 synchronized (mStats) {
573 mStats.clear();
574 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700575 mFile = new File(mDir, mFileLeaf);
576 checkFileLimitFLOCK();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 }
Mark Brophyc6350272011-08-05 16:16:39 +0100578
579 if (dayChanged || forceWriteHistoryStats) {
580 // Write history stats daily, or when forced (due to shutdown).
581 writeHistoryStatsFLOCK(mHistoryFile);
582 }
583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 // Delete the backup file
585 if (backupFile != null) {
586 backupFile.delete();
587 }
588 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800589 Slog.w(TAG, "Failed writing stats to file:" + mFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 if (backupFile != null) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700591 mFile.delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 backupFile.renameTo(mFile);
593 }
594 }
595 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700596 if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
598
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700599 private void writeStatsFLOCK(File file) throws IOException {
600 FileOutputStream stream = new FileOutputStream(file);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700601 try {
602 Parcel out = Parcel.obtain();
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700603 writeStatsToParcelFLOCK(out);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700604 stream.write(out.marshall());
605 out.recycle();
606 stream.flush();
607 } finally {
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700608 FileUtils.sync(stream);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700609 stream.close();
610 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
612
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700613 private void writeStatsToParcelFLOCK(Parcel out) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 synchronized (mStatsLock) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700615 out.writeInt(VERSION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 Set<String> keys = mStats.keySet();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700617 out.writeInt(keys.size());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 for (String key : keys) {
619 PkgUsageStatsExtended pus = mStats.get(key);
620 out.writeString(key);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700621 pus.writeToParcel(out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 }
623 }
624 }
625
Mark Brophyc6350272011-08-05 16:16:39 +0100626 /** Filter out stats for any packages which aren't present anymore. */
627 private void filterHistoryStats() {
628 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700629 IPackageManager pm = AppGlobals.getPackageManager();
630 for (int i=0; i<mLastResumeTimes.size(); i++) {
631 String pkg = mLastResumeTimes.keyAt(i);
632 try {
633 if (pm.getPackageUid(pkg, 0) < 0) {
634 mLastResumeTimes.removeAt(i);
635 i--;
636 }
637 } catch (RemoteException e) {
Mark Brophyc6350272011-08-05 16:16:39 +0100638 }
639 }
640 }
641 }
642
643 private void writeHistoryStatsFLOCK(AtomicFile historyFile) {
644 FileOutputStream fos = null;
645 try {
646 fos = historyFile.startWrite();
647 XmlSerializer out = new FastXmlSerializer();
648 out.setOutput(fos, "utf-8");
649 out.startDocument(null, true);
650 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
651 out.startTag(null, "usage-history");
652 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700653 for (int i=0; i<mLastResumeTimes.size(); i++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100654 out.startTag(null, "pkg");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700655 out.attribute(null, "name", mLastResumeTimes.keyAt(i));
656 ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
657 for (int j=0; j<comp.size(); j++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100658 out.startTag(null, "comp");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700659 out.attribute(null, "name", comp.keyAt(j));
660 out.attribute(null, "lrt", comp.valueAt(j).toString());
Mark Brophyc6350272011-08-05 16:16:39 +0100661 out.endTag(null, "comp");
662 }
663 out.endTag(null, "pkg");
664 }
665 }
666 out.endTag(null, "usage-history");
667 out.endDocument();
668
669 historyFile.finishWrite(fos);
670 } catch (IOException e) {
671 Slog.w(TAG,"Error writing history stats" + e);
672 if (fos != null) {
673 historyFile.failWrite(fos);
674 }
675 }
676 }
677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 public void publish(Context context) {
679 mContext = context;
680 ServiceManager.addService(SERVICE_NAME, asBinder());
681 }
Mark Brophyc6350272011-08-05 16:16:39 +0100682
683 /**
684 * Start watching packages to remove stats when a package is uninstalled.
685 * May only be called when the package manager is ready.
686 */
687 public void monitorPackages() {
688 mPackageMonitor = new PackageMonitor() {
689 @Override
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700690 public void onPackageRemovedAllUsers(String packageName, int uid) {
Mark Brophyc6350272011-08-05 16:16:39 +0100691 synchronized (mStatsLock) {
692 mLastResumeTimes.remove(packageName);
693 }
694 }
695 };
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700696 mPackageMonitor.register(mContext, null, true);
Mark Brophyc6350272011-08-05 16:16:39 +0100697 filterHistoryStats();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700698 }
Mark Brophyc6350272011-08-05 16:16:39 +0100699
700 public void shutdown() {
701 if (mPackageMonitor != null) {
702 mPackageMonitor.unregister();
703 }
704 Slog.i(TAG, "Writing usage stats before shutdown...");
705 writeStatsToFile(true, true);
706 }
707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 public static IUsageStats getService() {
709 if (sService != null) {
710 return sService;
711 }
712 IBinder b = ServiceManager.getService(SERVICE_NAME);
713 sService = asInterface(b);
714 return sService;
715 }
716
717 public void noteResumeComponent(ComponentName componentName) {
718 enforceCallingPermission();
719 String pkgName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700721 if ((componentName == null) ||
722 ((pkgName = componentName.getPackageName()) == null)) {
723 return;
724 }
725
726 final boolean samePackage = pkgName.equals(mLastResumedPkg);
727 if (mIsResumed) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700728 if (mLastResumedPkg != null) {
729 // We last resumed some other package... just pause it now
730 // to recover.
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700731 if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700732 + " while already resumed in " + mLastResumedPkg);
733 PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
734 if (pus != null) {
735 pus.updatePause();
736 }
737 }
738 }
739
740 final boolean sameComp = samePackage
741 && componentName.getClassName().equals(mLastResumedComp);
742
743 mIsResumed = true;
744 mLastResumedPkg = pkgName;
745 mLastResumedComp = componentName.getClassName();
746
Joe Onorato8a9b2202010-02-26 18:56:32 -0800747 if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 PkgUsageStatsExtended pus = mStats.get(pkgName);
749 if (pus == null) {
750 pus = new PkgUsageStatsExtended();
751 mStats.put(pkgName, pus);
752 }
Mark Brophy9fc03302011-07-01 16:56:24 +0100753 pus.updateResume(mLastResumedComp, !samePackage);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700754 if (!sameComp) {
755 pus.addLaunchCount(mLastResumedComp);
756 }
Mark Brophyc6350272011-08-05 16:16:39 +0100757
Dianne Hackborn904a8572013-06-28 18:12:31 -0700758 ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100759 if (componentResumeTimes == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700760 componentResumeTimes = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100761 mLastResumeTimes.put(pkgName, componentResumeTimes);
762 }
763 componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 }
766
767 public void notePauseComponent(ComponentName componentName) {
768 enforceCallingPermission();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700771 String pkgName;
772 if ((componentName == null) ||
773 ((pkgName = componentName.getPackageName()) == null)) {
774 return;
775 }
776 if (!mIsResumed) {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700777 if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700778 + pkgName + " to be paused");
779 return;
780 }
781 mIsResumed = false;
782
Joe Onorato8a9b2202010-02-26 18:56:32 -0800783 if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 PkgUsageStatsExtended pus = mStats.get(pkgName);
786 if (pus == null) {
787 // Weird some error here
Joe Onorato8a9b2202010-02-26 18:56:32 -0800788 Slog.i(TAG, "No package stats for pkg:"+pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 return;
790 }
791 pus.updatePause();
792 }
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700793
794 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100795 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700796 }
797
798 public void noteLaunchTime(ComponentName componentName, int millis) {
799 enforceCallingPermission();
800 String pkgName;
801 if ((componentName == null) ||
802 ((pkgName = componentName.getPackageName()) == null)) {
803 return;
804 }
805
806 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100807 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700808
809 synchronized (mStatsLock) {
810 PkgUsageStatsExtended pus = mStats.get(pkgName);
811 if (pus != null) {
812 pus.addLaunchTime(componentName.getClassName(), millis);
813 }
814 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 }
816
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700817 public void noteFullyDrawnTime(ComponentName componentName, int millis) {
818 enforceCallingPermission();
819 String pkgName;
820 if ((componentName == null) ||
821 ((pkgName = componentName.getPackageName()) == null)) {
822 return;
823 }
824
825 // Persist current data to file if needed.
826 writeStatsToFile(false, false);
827
828 synchronized (mStatsLock) {
829 PkgUsageStatsExtended pus = mStats.get(pkgName);
830 if (pus != null) {
831 pus.addFullyDrawnTime(componentName.getClassName(), millis);
832 }
833 }
834 }
835
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 public void enforceCallingPermission() {
837 if (Binder.getCallingPid() == Process.myPid()) {
838 return;
839 }
840 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
841 Binder.getCallingPid(), Binder.getCallingUid(), null);
842 }
843
844 public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
845 mContext.enforceCallingOrSelfPermission(
846 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
847 String pkgName;
848 if ((componentName == null) ||
849 ((pkgName = componentName.getPackageName()) == null)) {
850 return null;
851 }
852 synchronized (mStatsLock) {
853 PkgUsageStatsExtended pus = mStats.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100854 Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
855 if (pus == null && lastResumeTimes == null) {
856 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 }
Mark Brophyc6350272011-08-05 16:16:39 +0100858 int launchCount = pus != null ? pus.mLaunchCount : 0;
859 long usageTime = pus != null ? pus.mUsageTime : 0;
860 return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 }
862 }
863
864 public PkgUsageStats[] getAllPkgUsageStats() {
865 mContext.enforceCallingOrSelfPermission(
866 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
867 synchronized (mStatsLock) {
Mark Brophyc6350272011-08-05 16:16:39 +0100868 int size = mLastResumeTimes.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 if (size <= 0) {
870 return null;
871 }
872 PkgUsageStats retArr[] = new PkgUsageStats[size];
Dianne Hackborn904a8572013-06-28 18:12:31 -0700873 for (int i=0; i<size; i++) {
874 String pkg = mLastResumeTimes.keyAt(i);
Mark Brophyc6350272011-08-05 16:16:39 +0100875 long usageTime = 0;
876 int launchCount = 0;
877
878 PkgUsageStatsExtended pus = mStats.get(pkg);
879 if (pus != null) {
880 usageTime = pus.mUsageTime;
881 launchCount = pus.mLaunchCount;
882 }
Dianne Hackborn904a8572013-06-28 18:12:31 -0700883 retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
884 mLastResumeTimes.valueAt(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 }
886 return retArr;
887 }
888 }
889
890 static byte[] readFully(FileInputStream stream) throws java.io.IOException {
891 int pos = 0;
892 int avail = stream.available();
893 byte[] data = new byte[avail];
894 while (true) {
895 int amt = stream.read(data, pos, data.length-pos);
896 if (amt <= 0) {
897 return data;
898 }
899 pos += amt;
900 avail = stream.available();
901 if (avail > data.length-pos) {
902 byte[] newData = new byte[pos+avail];
903 System.arraycopy(data, 0, newData, 0, pos);
904 data = newData;
905 }
906 }
907 }
908
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700909 private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700910 boolean deleteAfterPrint, HashSet<String> packages) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 List<String> fileList = getUsageStatsFileListFLOCK();
912 if (fileList == null) {
913 return;
914 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 Collections.sort(fileList);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 for (String file : fileList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700917 if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
918 // In this mode we don't print the current day's stats, since
919 // they are incomplete.
920 continue;
921 }
922 File dFile = new File(mDir, file);
923 String dateStr = file.substring(FILE_PREFIX.length());
Dianne Hackborn904a8572013-06-28 18:12:31 -0700924 if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
925 // If the remainder does not start with a number, it is not a date,
926 // so we should ignore it for purposes here.
927 continue;
928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 try {
930 Parcel in = getParcelForFile(dFile);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700931 collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
932 packages);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700933 if (deleteAfterPrint) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 // Delete old file after collecting info only for checkin requests
935 dFile.delete();
936 }
937 } catch (FileNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800938 Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 return;
940 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800941 Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 }
943 }
944 }
945
946 private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700947 String date, boolean isCompactOutput, HashSet<String> packages) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700948 StringBuilder sb = new StringBuilder(512);
949 if (isCompactOutput) {
950 sb.append("D:");
951 sb.append(CHECKIN_VERSION);
952 sb.append(',');
953 } else {
954 sb.append("Date: ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700956
957 sb.append(date);
958
959 int vers = in.readInt();
960 if (vers != VERSION) {
961 sb.append(" (old data version)");
962 pw.println(sb.toString());
963 return;
964 }
965
966 pw.println(sb.toString());
967 int N = in.readInt();
968
969 while (N > 0) {
970 N--;
971 String pkgName = in.readString();
972 if (pkgName == null) {
973 break;
974 }
975 sb.setLength(0);
976 PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700977 if (packages != null && !packages.contains(pkgName)) {
978 // This package has not been requested -- don't print
979 // anything for it.
980 } else if (isCompactOutput) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700981 sb.append("P:");
982 sb.append(pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700983 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700984 sb.append(pus.mLaunchCount);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700985 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700986 sb.append(pus.mUsageTime);
987 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700988 final int NLT = pus.mLaunchTimes.size();
989 for (int i=0; i<NLT; i++) {
990 sb.append("A:");
991 String activity = pus.mLaunchTimes.keyAt(i);
992 sb.append(activity);
993 TimeStats times = pus.mLaunchTimes.valueAt(i);
994 sb.append(',');
995 sb.append(times.count);
996 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
997 sb.append(",");
998 sb.append(times.times[j]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700999 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001000 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001001 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001002 final int NFDT = pus.mFullyDrawnTimes.size();
1003 for (int i=0; i<NFDT; i++) {
1004 sb.append("A:");
1005 String activity = pus.mFullyDrawnTimes.keyAt(i);
1006 sb.append(activity);
1007 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1008 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
1009 sb.append(",");
1010 sb.append(times.times[j]);
1011 }
1012 sb.append('\n');
1013 }
1014
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001015 } else {
1016 sb.append(" ");
1017 sb.append(pkgName);
1018 sb.append(": ");
1019 sb.append(pus.mLaunchCount);
1020 sb.append(" times, ");
1021 sb.append(pus.mUsageTime);
1022 sb.append(" ms");
1023 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001024 final int NLT = pus.mLaunchTimes.size();
1025 for (int i=0; i<NLT; i++) {
1026 sb.append(" ");
1027 sb.append(pus.mLaunchTimes.keyAt(i));
1028 TimeStats times = pus.mLaunchTimes.valueAt(i);
1029 sb.append(": ");
1030 sb.append(times.count);
1031 sb.append(" starts");
1032 int lastBin = 0;
1033 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1034 if (times.times[j] != 0) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001035 sb.append(", ");
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001036 sb.append(lastBin);
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001037 sb.append('-');
1038 sb.append(LAUNCH_TIME_BINS[j]);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001039 sb.append("ms=");
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001040 sb.append(times.times[j]);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001041 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001042 lastBin = LAUNCH_TIME_BINS[j];
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001043 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001044 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1045 sb.append(", ");
1046 sb.append(">=");
1047 sb.append(lastBin);
1048 sb.append("ms=");
1049 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1050 }
1051 sb.append('\n');
1052 }
1053 final int NFDT = pus.mFullyDrawnTimes.size();
1054 for (int i=0; i<NFDT; i++) {
1055 sb.append(" ");
1056 sb.append(pus.mFullyDrawnTimes.keyAt(i));
1057 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1058 sb.append(": fully drawn ");
1059 boolean needComma = false;
1060 int lastBin = 0;
1061 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1062 if (times.times[j] != 0) {
1063 if (needComma) {
1064 sb.append(", ");
1065 } else {
1066 needComma = true;
1067 }
1068 sb.append(lastBin);
1069 sb.append('-');
1070 sb.append(LAUNCH_TIME_BINS[j]);
1071 sb.append("ms=");
1072 sb.append(times.times[j]);
1073 }
1074 lastBin = LAUNCH_TIME_BINS[j];
1075 }
1076 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1077 if (needComma) {
1078 sb.append(", ");
1079 }
1080 sb.append(">=");
1081 sb.append(lastBin);
1082 sb.append("ms=");
1083 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1084 }
1085 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001086 }
1087 }
1088
1089 pw.write(sb.toString());
1090 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
1092
1093 /**
1094 * Searches array of arguments for the specified string
1095 * @param args array of argument strings
1096 * @param value value to search for
1097 * @return true if the value is contained in the array
1098 */
1099 private static boolean scanArgs(String[] args, String value) {
1100 if (args != null) {
1101 for (String arg : args) {
1102 if (value.equals(arg)) {
1103 return true;
1104 }
1105 }
1106 }
1107 return false;
1108 }
1109
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001110 /**
1111 * Searches array of arguments for the specified string's data
1112 * @param args array of argument strings
1113 * @param value value to search for
1114 * @return the string of data after the arg, or null if there is none
1115 */
1116 private static String scanArgsData(String[] args, String value) {
1117 if (args != null) {
1118 final int N = args.length;
1119 for (int i=0; i<N; i++) {
1120 if (value.equals(args[i])) {
1121 i++;
1122 return i < N ? args[i] : null;
1123 }
1124 }
1125 }
1126 return null;
1127 }
1128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 @Override
1130 /*
1131 * The data persisted to file is parsed and the stats are computed.
1132 */
1133 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07001134 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
1135 != PackageManager.PERMISSION_GRANTED) {
1136 pw.println("Permission Denial: can't dump UsageStats from from pid="
1137 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1138 + " without permission " + android.Manifest.permission.DUMP);
1139 return;
1140 }
1141
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001142 final boolean isCheckinRequest = scanArgs(args, "--checkin");
1143 final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
1144 final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001145 final String rawPackages = scanArgsData(args, "--packages");
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001146
1147 // Make sure the current stats are written to the file. This
1148 // doesn't need to be done if we are deleting files after printing,
1149 // since it that case we won't print the current stats.
1150 if (!deleteAfterPrint) {
Mark Brophyc6350272011-08-05 16:16:39 +01001151 writeStatsToFile(true, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001152 }
1153
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001154 HashSet<String> packages = null;
1155 if (rawPackages != null) {
1156 if (!"*".equals(rawPackages)) {
1157 // A * is a wildcard to show all packages.
1158 String[] names = rawPackages.split(",");
1159 for (String n : names) {
1160 if (packages == null) {
1161 packages = new HashSet<String>();
1162 }
1163 packages.add(n);
1164 }
1165 }
1166 } else if (isCheckinRequest) {
1167 // If checkin doesn't specify any packages, then we simply won't
1168 // show anything.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001169 Slog.w(TAG, "Checkin without packages");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001170 return;
1171 }
1172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 synchronized (mFileLock) {
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001174 collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 }
1176 }
1177
1178}