blob: e96d8b1ce91c8e59e29d5c19650f3eb820ada0eb [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();
399 while (eventType != XmlPullParser.START_TAG) {
400 eventType = parser.next();
401 }
402 String tagName = parser.getName();
403 if ("usage-history".equals(tagName)) {
404 String pkg = null;
405 do {
406 eventType = parser.next();
407 if (eventType == XmlPullParser.START_TAG) {
408 tagName = parser.getName();
409 int depth = parser.getDepth();
410 if ("pkg".equals(tagName) && depth == 2) {
411 pkg = parser.getAttributeValue(null, "name");
412 } else if ("comp".equals(tagName) && depth == 3 && pkg != null) {
413 String comp = parser.getAttributeValue(null, "name");
414 String lastResumeTimeStr = parser.getAttributeValue(null, "lrt");
415 if (comp != null && lastResumeTimeStr != null) {
416 try {
417 long lastResumeTime = Long.parseLong(lastResumeTimeStr);
418 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700419 ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
Mark Brophyc6350272011-08-05 16:16:39 +0100420 if (lrt == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700421 lrt = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100422 mLastResumeTimes.put(pkg, lrt);
423 }
424 lrt.put(comp, lastResumeTime);
425 }
426 } catch (NumberFormatException e) {
427 }
428 }
429 }
430 } else if (eventType == XmlPullParser.END_TAG) {
431 if ("pkg".equals(parser.getName())) {
432 pkg = null;
433 }
434 }
435 } while (eventType != XmlPullParser.END_DOCUMENT);
436 }
437 } catch (XmlPullParserException e) {
438 Slog.w(TAG,"Error reading history stats: " + e);
439 } catch (IOException e) {
440 Slog.w(TAG,"Error reading history stats: " + e);
441 } finally {
442 if (fis != null) {
443 try {
444 fis.close();
445 } catch (IOException e) {
446 }
447 }
448 }
449 }
450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 private ArrayList<String> getUsageStatsFileListFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 // Check if there are too many files in the system and delete older files
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700453 String fList[] = mDir.list();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 if (fList == null) {
455 return null;
456 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 ArrayList<String> fileList = new ArrayList<String>();
458 for (String file : fList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700459 if (!file.startsWith(FILE_PREFIX)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 continue;
461 }
462 if (file.endsWith(".bak")) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700463 (new File(mDir, file)).delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 continue;
465 }
466 fileList.add(file);
467 }
468 return fileList;
469 }
470
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 private void checkFileLimitFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 // Get all usage stats output files
473 ArrayList<String> fileList = getUsageStatsFileListFLOCK();
474 if (fileList == null) {
475 // Strange but we dont have to delete any thing
476 return;
477 }
478 int count = fileList.size();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700479 if (count <= MAX_NUM_FILES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 return;
481 }
482 // Sort files
483 Collections.sort(fileList);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700484 count -= MAX_NUM_FILES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 // Delete older files
486 for (int i = 0; i < count; i++) {
487 String fileName = fileList.get(i);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700488 File file = new File(mDir, fileName);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800489 Slog.i(TAG, "Deleting usage file : " + fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 file.delete();
491 }
492 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700493
494 /**
495 * Conditionally start up a disk write if it's been awhile, or the
496 * day has rolled over.
497 *
498 * This is called indirectly from user-facing actions (when
499 * 'force' is false) so it tries to be quick, without writing to
500 * disk directly or acquiring heavy locks.
501 *
502 * @params force do an unconditional, synchronous stats flush
503 * to disk on the current thread.
Mark Brophyc6350272011-08-05 16:16:39 +0100504 * @params forceWriteHistoryStats Force writing of historical stats.
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700505 */
Mark Brophyc6350272011-08-05 16:16:39 +0100506 private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700507 int curDay;
508 synchronized (mCal) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700509 mCal.setTimeInMillis(System.currentTimeMillis());
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700510 curDay = mCal.get(Calendar.DAY_OF_YEAR);
511 }
512 final boolean dayChanged = curDay != mLastWriteDay.get();
513
514 // Determine if the day changed... note that this will be wrong
515 // if the year has changed but we are in the same day of year...
516 // we can probably live with this.
517 final long currElapsedTime = SystemClock.elapsedRealtime();
518
519 // Fast common path, without taking the often-contentious
520 // mFileLock.
521 if (!force) {
522 if (!dayChanged &&
523 (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
524 // wait till the next update
525 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700527 if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
528 new Thread("UsageStatsService_DiskWriter") {
529 public void run() {
530 try {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700531 if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
Mark Brophyc6350272011-08-05 16:16:39 +0100532 writeStatsToFile(true, false);
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700533 } finally {
534 mUnforcedDiskWriteRunning.set(false);
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700535 if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700536 }
537 }
538 }.start();
539 }
540 return;
541 }
542
543 synchronized (mFileLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 // Get the most recent file
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700545 mFileLeaf = getCurrentDateStr(FILE_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 // Copy current file to back up
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800547 File backupFile = null;
548 if (mFile != null && mFile.exists()) {
549 backupFile = new File(mFile.getPath() + ".bak");
Dianne Hackborn1afd1c92010-03-18 22:47:17 -0700550 if (!backupFile.exists()) {
551 if (!mFile.renameTo(backupFile)) {
552 Slog.w(TAG, "Failed to persist new stats");
553 return;
554 }
555 } else {
556 mFile.delete();
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800557 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700558 }
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 // Write mStats to file
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700562 writeStatsFLOCK(mFile);
563 mLastWriteElapsedTime.set(currElapsedTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 if (dayChanged) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700565 mLastWriteDay.set(curDay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 // clear stats
567 synchronized (mStats) {
568 mStats.clear();
569 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700570 mFile = new File(mDir, mFileLeaf);
571 checkFileLimitFLOCK();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 }
Mark Brophyc6350272011-08-05 16:16:39 +0100573
574 if (dayChanged || forceWriteHistoryStats) {
575 // Write history stats daily, or when forced (due to shutdown).
576 writeHistoryStatsFLOCK(mHistoryFile);
577 }
578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 // Delete the backup file
580 if (backupFile != null) {
581 backupFile.delete();
582 }
583 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800584 Slog.w(TAG, "Failed writing stats to file:" + mFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 if (backupFile != null) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700586 mFile.delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 backupFile.renameTo(mFile);
588 }
589 }
590 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700591 if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 }
593
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700594 private void writeStatsFLOCK(File file) throws IOException {
595 FileOutputStream stream = new FileOutputStream(file);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700596 try {
597 Parcel out = Parcel.obtain();
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700598 writeStatsToParcelFLOCK(out);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700599 stream.write(out.marshall());
600 out.recycle();
601 stream.flush();
602 } finally {
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700603 FileUtils.sync(stream);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700604 stream.close();
605 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 }
607
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700608 private void writeStatsToParcelFLOCK(Parcel out) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 synchronized (mStatsLock) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700610 out.writeInt(VERSION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 Set<String> keys = mStats.keySet();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700612 out.writeInt(keys.size());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 for (String key : keys) {
614 PkgUsageStatsExtended pus = mStats.get(key);
615 out.writeString(key);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700616 pus.writeToParcel(out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
618 }
619 }
620
Mark Brophyc6350272011-08-05 16:16:39 +0100621 /** Filter out stats for any packages which aren't present anymore. */
622 private void filterHistoryStats() {
623 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700624 IPackageManager pm = AppGlobals.getPackageManager();
625 for (int i=0; i<mLastResumeTimes.size(); i++) {
626 String pkg = mLastResumeTimes.keyAt(i);
627 try {
628 if (pm.getPackageUid(pkg, 0) < 0) {
629 mLastResumeTimes.removeAt(i);
630 i--;
631 }
632 } catch (RemoteException e) {
Mark Brophyc6350272011-08-05 16:16:39 +0100633 }
634 }
635 }
636 }
637
638 private void writeHistoryStatsFLOCK(AtomicFile historyFile) {
639 FileOutputStream fos = null;
640 try {
641 fos = historyFile.startWrite();
642 XmlSerializer out = new FastXmlSerializer();
643 out.setOutput(fos, "utf-8");
644 out.startDocument(null, true);
645 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
646 out.startTag(null, "usage-history");
647 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700648 for (int i=0; i<mLastResumeTimes.size(); i++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100649 out.startTag(null, "pkg");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700650 out.attribute(null, "name", mLastResumeTimes.keyAt(i));
651 ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
652 for (int j=0; j<comp.size(); j++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100653 out.startTag(null, "comp");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700654 out.attribute(null, "name", comp.keyAt(j));
655 out.attribute(null, "lrt", comp.valueAt(j).toString());
Mark Brophyc6350272011-08-05 16:16:39 +0100656 out.endTag(null, "comp");
657 }
658 out.endTag(null, "pkg");
659 }
660 }
661 out.endTag(null, "usage-history");
662 out.endDocument();
663
664 historyFile.finishWrite(fos);
665 } catch (IOException e) {
666 Slog.w(TAG,"Error writing history stats" + e);
667 if (fos != null) {
668 historyFile.failWrite(fos);
669 }
670 }
671 }
672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 public void publish(Context context) {
674 mContext = context;
675 ServiceManager.addService(SERVICE_NAME, asBinder());
676 }
Mark Brophyc6350272011-08-05 16:16:39 +0100677
678 /**
679 * Start watching packages to remove stats when a package is uninstalled.
680 * May only be called when the package manager is ready.
681 */
682 public void monitorPackages() {
683 mPackageMonitor = new PackageMonitor() {
684 @Override
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700685 public void onPackageRemovedAllUsers(String packageName, int uid) {
Mark Brophyc6350272011-08-05 16:16:39 +0100686 synchronized (mStatsLock) {
687 mLastResumeTimes.remove(packageName);
688 }
689 }
690 };
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700691 mPackageMonitor.register(mContext, null, true);
Mark Brophyc6350272011-08-05 16:16:39 +0100692 filterHistoryStats();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700693 }
Mark Brophyc6350272011-08-05 16:16:39 +0100694
695 public void shutdown() {
696 if (mPackageMonitor != null) {
697 mPackageMonitor.unregister();
698 }
699 Slog.i(TAG, "Writing usage stats before shutdown...");
700 writeStatsToFile(true, true);
701 }
702
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 public static IUsageStats getService() {
704 if (sService != null) {
705 return sService;
706 }
707 IBinder b = ServiceManager.getService(SERVICE_NAME);
708 sService = asInterface(b);
709 return sService;
710 }
711
712 public void noteResumeComponent(ComponentName componentName) {
713 enforceCallingPermission();
714 String pkgName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700716 if ((componentName == null) ||
717 ((pkgName = componentName.getPackageName()) == null)) {
718 return;
719 }
720
721 final boolean samePackage = pkgName.equals(mLastResumedPkg);
722 if (mIsResumed) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700723 if (mLastResumedPkg != null) {
724 // We last resumed some other package... just pause it now
725 // to recover.
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700726 if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700727 + " while already resumed in " + mLastResumedPkg);
728 PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
729 if (pus != null) {
730 pus.updatePause();
731 }
732 }
733 }
734
735 final boolean sameComp = samePackage
736 && componentName.getClassName().equals(mLastResumedComp);
737
738 mIsResumed = true;
739 mLastResumedPkg = pkgName;
740 mLastResumedComp = componentName.getClassName();
741
Joe Onorato8a9b2202010-02-26 18:56:32 -0800742 if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 PkgUsageStatsExtended pus = mStats.get(pkgName);
744 if (pus == null) {
745 pus = new PkgUsageStatsExtended();
746 mStats.put(pkgName, pus);
747 }
Mark Brophy9fc03302011-07-01 16:56:24 +0100748 pus.updateResume(mLastResumedComp, !samePackage);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700749 if (!sameComp) {
750 pus.addLaunchCount(mLastResumedComp);
751 }
Mark Brophyc6350272011-08-05 16:16:39 +0100752
Dianne Hackborn904a8572013-06-28 18:12:31 -0700753 ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100754 if (componentResumeTimes == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700755 componentResumeTimes = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100756 mLastResumeTimes.put(pkgName, componentResumeTimes);
757 }
758 componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761
762 public void notePauseComponent(ComponentName componentName) {
763 enforceCallingPermission();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700764
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700766 String pkgName;
767 if ((componentName == null) ||
768 ((pkgName = componentName.getPackageName()) == null)) {
769 return;
770 }
771 if (!mIsResumed) {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700772 if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700773 + pkgName + " to be paused");
774 return;
775 }
776 mIsResumed = false;
777
Joe Onorato8a9b2202010-02-26 18:56:32 -0800778 if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700779
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 PkgUsageStatsExtended pus = mStats.get(pkgName);
781 if (pus == null) {
782 // Weird some error here
Joe Onorato8a9b2202010-02-26 18:56:32 -0800783 Slog.i(TAG, "No package stats for pkg:"+pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 return;
785 }
786 pus.updatePause();
787 }
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700788
789 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100790 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700791 }
792
793 public void noteLaunchTime(ComponentName componentName, int millis) {
794 enforceCallingPermission();
795 String pkgName;
796 if ((componentName == null) ||
797 ((pkgName = componentName.getPackageName()) == null)) {
798 return;
799 }
800
801 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100802 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700803
804 synchronized (mStatsLock) {
805 PkgUsageStatsExtended pus = mStats.get(pkgName);
806 if (pus != null) {
807 pus.addLaunchTime(componentName.getClassName(), millis);
808 }
809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
811
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700812 public void noteFullyDrawnTime(ComponentName componentName, int millis) {
813 enforceCallingPermission();
814 String pkgName;
815 if ((componentName == null) ||
816 ((pkgName = componentName.getPackageName()) == null)) {
817 return;
818 }
819
820 // Persist current data to file if needed.
821 writeStatsToFile(false, false);
822
823 synchronized (mStatsLock) {
824 PkgUsageStatsExtended pus = mStats.get(pkgName);
825 if (pus != null) {
826 pus.addFullyDrawnTime(componentName.getClassName(), millis);
827 }
828 }
829 }
830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 public void enforceCallingPermission() {
832 if (Binder.getCallingPid() == Process.myPid()) {
833 return;
834 }
835 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
836 Binder.getCallingPid(), Binder.getCallingUid(), null);
837 }
838
839 public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
840 mContext.enforceCallingOrSelfPermission(
841 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
842 String pkgName;
843 if ((componentName == null) ||
844 ((pkgName = componentName.getPackageName()) == null)) {
845 return null;
846 }
847 synchronized (mStatsLock) {
848 PkgUsageStatsExtended pus = mStats.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100849 Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
850 if (pus == null && lastResumeTimes == null) {
851 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
Mark Brophyc6350272011-08-05 16:16:39 +0100853 int launchCount = pus != null ? pus.mLaunchCount : 0;
854 long usageTime = pus != null ? pus.mUsageTime : 0;
855 return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
857 }
858
859 public PkgUsageStats[] getAllPkgUsageStats() {
860 mContext.enforceCallingOrSelfPermission(
861 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
862 synchronized (mStatsLock) {
Mark Brophyc6350272011-08-05 16:16:39 +0100863 int size = mLastResumeTimes.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 if (size <= 0) {
865 return null;
866 }
867 PkgUsageStats retArr[] = new PkgUsageStats[size];
Dianne Hackborn904a8572013-06-28 18:12:31 -0700868 for (int i=0; i<size; i++) {
869 String pkg = mLastResumeTimes.keyAt(i);
Mark Brophyc6350272011-08-05 16:16:39 +0100870 long usageTime = 0;
871 int launchCount = 0;
872
873 PkgUsageStatsExtended pus = mStats.get(pkg);
874 if (pus != null) {
875 usageTime = pus.mUsageTime;
876 launchCount = pus.mLaunchCount;
877 }
Dianne Hackborn904a8572013-06-28 18:12:31 -0700878 retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
879 mLastResumeTimes.valueAt(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 }
881 return retArr;
882 }
883 }
884
885 static byte[] readFully(FileInputStream stream) throws java.io.IOException {
886 int pos = 0;
887 int avail = stream.available();
888 byte[] data = new byte[avail];
889 while (true) {
890 int amt = stream.read(data, pos, data.length-pos);
891 if (amt <= 0) {
892 return data;
893 }
894 pos += amt;
895 avail = stream.available();
896 if (avail > data.length-pos) {
897 byte[] newData = new byte[pos+avail];
898 System.arraycopy(data, 0, newData, 0, pos);
899 data = newData;
900 }
901 }
902 }
903
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700904 private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700905 boolean deleteAfterPrint, HashSet<String> packages) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 List<String> fileList = getUsageStatsFileListFLOCK();
907 if (fileList == null) {
908 return;
909 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 Collections.sort(fileList);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 for (String file : fileList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700912 if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
913 // In this mode we don't print the current day's stats, since
914 // they are incomplete.
915 continue;
916 }
917 File dFile = new File(mDir, file);
918 String dateStr = file.substring(FILE_PREFIX.length());
Dianne Hackborn904a8572013-06-28 18:12:31 -0700919 if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
920 // If the remainder does not start with a number, it is not a date,
921 // so we should ignore it for purposes here.
922 continue;
923 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 try {
925 Parcel in = getParcelForFile(dFile);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700926 collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
927 packages);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700928 if (deleteAfterPrint) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 // Delete old file after collecting info only for checkin requests
930 dFile.delete();
931 }
932 } catch (FileNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800933 Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 return;
935 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800936 Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 }
938 }
939 }
940
941 private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700942 String date, boolean isCompactOutput, HashSet<String> packages) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700943 StringBuilder sb = new StringBuilder(512);
944 if (isCompactOutput) {
945 sb.append("D:");
946 sb.append(CHECKIN_VERSION);
947 sb.append(',');
948 } else {
949 sb.append("Date: ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700951
952 sb.append(date);
953
954 int vers = in.readInt();
955 if (vers != VERSION) {
956 sb.append(" (old data version)");
957 pw.println(sb.toString());
958 return;
959 }
960
961 pw.println(sb.toString());
962 int N = in.readInt();
963
964 while (N > 0) {
965 N--;
966 String pkgName = in.readString();
967 if (pkgName == null) {
968 break;
969 }
970 sb.setLength(0);
971 PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700972 if (packages != null && !packages.contains(pkgName)) {
973 // This package has not been requested -- don't print
974 // anything for it.
975 } else if (isCompactOutput) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700976 sb.append("P:");
977 sb.append(pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700978 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700979 sb.append(pus.mLaunchCount);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700980 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700981 sb.append(pus.mUsageTime);
982 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700983 final int NLT = pus.mLaunchTimes.size();
984 for (int i=0; i<NLT; i++) {
985 sb.append("A:");
986 String activity = pus.mLaunchTimes.keyAt(i);
987 sb.append(activity);
988 TimeStats times = pus.mLaunchTimes.valueAt(i);
989 sb.append(',');
990 sb.append(times.count);
991 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
992 sb.append(",");
993 sb.append(times.times[j]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700994 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700995 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700996 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700997 final int NFDT = pus.mFullyDrawnTimes.size();
998 for (int i=0; i<NFDT; i++) {
999 sb.append("A:");
1000 String activity = pus.mFullyDrawnTimes.keyAt(i);
1001 sb.append(activity);
1002 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1003 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
1004 sb.append(",");
1005 sb.append(times.times[j]);
1006 }
1007 sb.append('\n');
1008 }
1009
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001010 } else {
1011 sb.append(" ");
1012 sb.append(pkgName);
1013 sb.append(": ");
1014 sb.append(pus.mLaunchCount);
1015 sb.append(" times, ");
1016 sb.append(pus.mUsageTime);
1017 sb.append(" ms");
1018 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001019 final int NLT = pus.mLaunchTimes.size();
1020 for (int i=0; i<NLT; i++) {
1021 sb.append(" ");
1022 sb.append(pus.mLaunchTimes.keyAt(i));
1023 TimeStats times = pus.mLaunchTimes.valueAt(i);
1024 sb.append(": ");
1025 sb.append(times.count);
1026 sb.append(" starts");
1027 int lastBin = 0;
1028 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1029 if (times.times[j] != 0) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001030 sb.append(", ");
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001031 sb.append(lastBin);
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001032 sb.append('-');
1033 sb.append(LAUNCH_TIME_BINS[j]);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001034 sb.append("ms=");
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001035 sb.append(times.times[j]);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001036 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001037 lastBin = LAUNCH_TIME_BINS[j];
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001038 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001039 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1040 sb.append(", ");
1041 sb.append(">=");
1042 sb.append(lastBin);
1043 sb.append("ms=");
1044 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1045 }
1046 sb.append('\n');
1047 }
1048 final int NFDT = pus.mFullyDrawnTimes.size();
1049 for (int i=0; i<NFDT; i++) {
1050 sb.append(" ");
1051 sb.append(pus.mFullyDrawnTimes.keyAt(i));
1052 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1053 sb.append(": fully drawn ");
1054 boolean needComma = false;
1055 int lastBin = 0;
1056 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1057 if (times.times[j] != 0) {
1058 if (needComma) {
1059 sb.append(", ");
1060 } else {
1061 needComma = true;
1062 }
1063 sb.append(lastBin);
1064 sb.append('-');
1065 sb.append(LAUNCH_TIME_BINS[j]);
1066 sb.append("ms=");
1067 sb.append(times.times[j]);
1068 }
1069 lastBin = LAUNCH_TIME_BINS[j];
1070 }
1071 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1072 if (needComma) {
1073 sb.append(", ");
1074 }
1075 sb.append(">=");
1076 sb.append(lastBin);
1077 sb.append("ms=");
1078 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1079 }
1080 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001081 }
1082 }
1083
1084 pw.write(sb.toString());
1085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087
1088 /**
1089 * Searches array of arguments for the specified string
1090 * @param args array of argument strings
1091 * @param value value to search for
1092 * @return true if the value is contained in the array
1093 */
1094 private static boolean scanArgs(String[] args, String value) {
1095 if (args != null) {
1096 for (String arg : args) {
1097 if (value.equals(arg)) {
1098 return true;
1099 }
1100 }
1101 }
1102 return false;
1103 }
1104
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001105 /**
1106 * Searches array of arguments for the specified string's data
1107 * @param args array of argument strings
1108 * @param value value to search for
1109 * @return the string of data after the arg, or null if there is none
1110 */
1111 private static String scanArgsData(String[] args, String value) {
1112 if (args != null) {
1113 final int N = args.length;
1114 for (int i=0; i<N; i++) {
1115 if (value.equals(args[i])) {
1116 i++;
1117 return i < N ? args[i] : null;
1118 }
1119 }
1120 }
1121 return null;
1122 }
1123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 @Override
1125 /*
1126 * The data persisted to file is parsed and the stats are computed.
1127 */
1128 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07001129 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
1130 != PackageManager.PERMISSION_GRANTED) {
1131 pw.println("Permission Denial: can't dump UsageStats from from pid="
1132 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1133 + " without permission " + android.Manifest.permission.DUMP);
1134 return;
1135 }
1136
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001137 final boolean isCheckinRequest = scanArgs(args, "--checkin");
1138 final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
1139 final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001140 final String rawPackages = scanArgsData(args, "--packages");
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001141
1142 // Make sure the current stats are written to the file. This
1143 // doesn't need to be done if we are deleting files after printing,
1144 // since it that case we won't print the current stats.
1145 if (!deleteAfterPrint) {
Mark Brophyc6350272011-08-05 16:16:39 +01001146 writeStatsToFile(true, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001147 }
1148
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001149 HashSet<String> packages = null;
1150 if (rawPackages != null) {
1151 if (!"*".equals(rawPackages)) {
1152 // A * is a wildcard to show all packages.
1153 String[] names = rawPackages.split(",");
1154 for (String n : names) {
1155 if (packages == null) {
1156 packages = new HashSet<String>();
1157 }
1158 packages.add(n);
1159 }
1160 }
1161 } else if (isCheckinRequest) {
1162 // If checkin doesn't specify any packages, then we simply won't
1163 // show anything.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001164 Slog.w(TAG, "Checkin without packages");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001165 return;
1166 }
1167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 synchronized (mFileLock) {
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001169 collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 }
1171 }
1172
1173}