blob: 587f9492bf39b6718ed4a154e0e32314db083414 [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;
Kenny Root3abd75b2011-09-29 11:00:41 -070023import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.os.Binder;
25import android.os.IBinder;
Dianne Hackborn8bdf5932010-10-15 12:54:40 -070026import android.os.FileUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.Parcel;
28import android.os.Process;
Dianne Hackborn904a8572013-06-28 18:12:31 -070029import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.ServiceManager;
31import android.os.SystemClock;
Dianne Hackborn904a8572013-06-28 18:12:31 -070032import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070033import android.util.AtomicFile;
Joe Onorato8a9b2202010-02-26 18:56:32 -080034import android.util.Slog;
Mark Brophyc6350272011-08-05 16:16:39 +010035import android.util.Xml;
36
37import com.android.internal.app.IUsageStats;
38import com.android.internal.content.PackageMonitor;
Mark Brophyc6350272011-08-05 16:16:39 +010039import com.android.internal.os.PkgUsageStats;
40import com.android.internal.util.FastXmlSerializer;
41
42import org.xmlpull.v1.XmlPullParser;
43import org.xmlpull.v1.XmlPullParserException;
44import org.xmlpull.v1.XmlSerializer;
45
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileNotFoundException;
50import java.io.FileOutputStream;
51import java.io.IOException;
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.Calendar;
55import java.util.Collections;
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -070056import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import java.util.List;
58import java.util.Map;
59import java.util.Set;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070060import java.util.TimeZone;
Brad Fitzpatrick389a9162010-08-03 15:41:05 -070061import java.util.concurrent.atomic.AtomicBoolean;
62import java.util.concurrent.atomic.AtomicInteger;
63import java.util.concurrent.atomic.AtomicLong;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65/**
66 * This service collects the statistics associated with usage
67 * of various components, like when a particular package is launched or
68 * paused and aggregates events like number of time a component is launched
69 * total duration of a component launch.
70 */
71public final class UsageStatsService extends IUsageStats.Stub {
72 public static final String SERVICE_NAME = "usagestats";
73 private static final boolean localLOGV = false;
Dianne Hackborncef65ee2010-09-30 18:27:22 -070074 private static final boolean REPORT_UNEXPECTED = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private static final String TAG = "UsageStats";
Dianne Hackborn6447ca32009-04-07 19:50:08 -070076
77 // Current on-disk Parcel version
Dianne Hackborn2286cdc2013-07-01 19:10:06 -070078 private static final int VERSION = 1008;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070079
Dianne Hackborn760ec4a2009-06-17 20:05:26 -070080 private static final int CHECKIN_VERSION = 4;
Dianne Hackborn6447ca32009-04-07 19:50:08 -070081
82 private static final String FILE_PREFIX = "usage-";
Mark Brophyc6350272011-08-05 16:16:39 +010083
84 private static final String FILE_HISTORY = FILE_PREFIX + "history.xml";
85
Dianne Hackborn6447ca32009-04-07 19:50:08 -070086 private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms
87
88 private static final int MAX_NUM_FILES = 5;
89
Dianne Hackbornf210d6b2009-04-13 18:42:49 -070090 private static final int NUM_LAUNCH_TIME_BINS = 10;
91 private static final int[] LAUNCH_TIME_BINS = {
92 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000
93 };
Dianne Hackborn6447ca32009-04-07 19:50:08 -070094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 static IUsageStats sService;
96 private Context mContext;
97 // structure used to maintain statistics since the last checkin.
Dianne Hackborn904a8572013-06-28 18:12:31 -070098 final private ArrayMap<String, PkgUsageStatsExtended> mStats;
Mark Brophyc6350272011-08-05 16:16:39 +010099
100 // Maintains the last time any component was resumed, for all time.
Dianne Hackborn904a8572013-06-28 18:12:31 -0700101 final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes;
Mark Brophyc6350272011-08-05 16:16:39 +0100102
103 // To remove last-resume time stats when a pacakge is removed.
104 private PackageMonitor mPackageMonitor;
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 // Lock to update package stats. Methods suffixed by SLOCK should invoked with
107 // this lock held
108 final Object mStatsLock;
109 // Lock to write to file. Methods suffixed by FLOCK should invoked with
110 // this lock held.
111 final Object mFileLock;
112 // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700113 private String mLastResumedPkg;
114 private String mLastResumedComp;
115 private boolean mIsResumed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 private File mFile;
Mark Brophyc6350272011-08-05 16:16:39 +0100117 private AtomicFile mHistoryFile;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700118 private String mFileLeaf;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700119 private File mDir;
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700120
121 private Calendar mCal; // guarded by itself
122
123 private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
124 private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
125 private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700126
127 static class TimeStats {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700128 int count;
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700129 int[] times = new int[NUM_LAUNCH_TIME_BINS];
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700130
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700131 TimeStats() {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700132 }
133
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700134 void incCount() {
135 count++;
136 }
137
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700138 void add(int val) {
139 final int[] bins = LAUNCH_TIME_BINS;
140 for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
141 if (val < bins[i]) {
142 times[i]++;
143 return;
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700144 }
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700145 }
146 times[NUM_LAUNCH_TIME_BINS-1]++;
147 }
148
149 TimeStats(Parcel in) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700150 count = in.readInt();
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700151 final int[] localTimes = times;
152 for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
153 localTimes[i] = in.readInt();
154 }
155 }
156
157 void writeToParcel(Parcel out) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700158 out.writeInt(count);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700159 final int[] localTimes = times;
160 for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
161 out.writeInt(localTimes[i]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700162 }
163 }
164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165
166 private class PkgUsageStatsExtended {
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700167 final ArrayMap<String, TimeStats> mLaunchTimes
168 = new ArrayMap<String, TimeStats>();
169 final ArrayMap<String, TimeStats> mFullyDrawnTimes
170 = new ArrayMap<String, TimeStats>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 int mLaunchCount;
172 long mUsageTime;
173 long mPausedTime;
174 long mResumedTime;
175
176 PkgUsageStatsExtended() {
177 mLaunchCount = 0;
178 mUsageTime = 0;
179 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700180
181 PkgUsageStatsExtended(Parcel in) {
182 mLaunchCount = in.readInt();
183 mUsageTime = in.readLong();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800184 if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700185 + ", Usage time:" + mUsageTime);
186
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700187 final int numLaunchTimeStats = in.readInt();
188 if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
189 mLaunchTimes.ensureCapacity(numLaunchTimeStats);
190 for (int i=0; i<numLaunchTimeStats; i++) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700191 String comp = in.readString();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800192 if (localLOGV) Slog.v(TAG, "Component: " + comp);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700193 TimeStats times = new TimeStats(in);
194 mLaunchTimes.put(comp, times);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700195 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700196
197 final int numFullyDrawnTimeStats = in.readInt();
198 if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats);
199 mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats);
200 for (int i=0; i<numFullyDrawnTimeStats; i++) {
201 String comp = in.readString();
202 if (localLOGV) Slog.v(TAG, "Component: " + comp);
203 TimeStats times = new TimeStats(in);
204 mFullyDrawnTimes.put(comp, times);
205 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700206 }
Mark Brophy9fc03302011-07-01 16:56:24 +0100207
208 void updateResume(String comp, boolean launched) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700209 if (launched) {
210 mLaunchCount ++;
211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 mResumedTime = SystemClock.elapsedRealtime();
213 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 void updatePause() {
216 mPausedTime = SystemClock.elapsedRealtime();
217 mUsageTime += (mPausedTime - mResumedTime);
218 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700219
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700220 void addLaunchCount(String comp) {
221 TimeStats times = mLaunchTimes.get(comp);
222 if (times == null) {
223 times = new TimeStats();
224 mLaunchTimes.put(comp, times);
225 }
226 times.incCount();
227 }
228
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700229 void addLaunchTime(String comp, int millis) {
230 TimeStats times = mLaunchTimes.get(comp);
231 if (times == null) {
232 times = new TimeStats();
233 mLaunchTimes.put(comp, times);
234 }
235 times.add(millis);
236 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700237
238 void addFullyDrawnTime(String comp, int millis) {
239 TimeStats times = mFullyDrawnTimes.get(comp);
240 if (times == null) {
241 times = new TimeStats();
242 mFullyDrawnTimes.put(comp, times);
243 }
244 times.add(millis);
245 }
246
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700247 void writeToParcel(Parcel out) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700248 out.writeInt(mLaunchCount);
249 out.writeLong(mUsageTime);
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700250 final int numLaunchTimeStats = mLaunchTimes.size();
251 out.writeInt(numLaunchTimeStats);
252 for (int i=0; i<numLaunchTimeStats; i++) {
253 out.writeString(mLaunchTimes.keyAt(i));
254 mLaunchTimes.valueAt(i).writeToParcel(out);
255 }
256 final int numFullyDrawnTimeStats = mFullyDrawnTimes.size();
257 out.writeInt(numFullyDrawnTimeStats);
258 for (int i=0; i<numFullyDrawnTimeStats; i++) {
259 out.writeString(mFullyDrawnTimes.keyAt(i));
260 mFullyDrawnTimes.valueAt(i).writeToParcel(out);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700261 }
262 }
263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 void clear() {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700265 mLaunchTimes.clear();
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700266 mFullyDrawnTimes.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 mLaunchCount = 0;
268 mUsageTime = 0;
269 }
270 }
271
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700272 UsageStatsService(String dir) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700273 mStats = new ArrayMap<String, PkgUsageStatsExtended>();
274 mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 mStatsLock = new Object();
276 mFileLock = new Object();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700277 mDir = new File(dir);
278 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
279
280 mDir.mkdir();
281
282 // Remove any old usage files from previous versions.
283 File parentDir = mDir.getParentFile();
284 String fList[] = parentDir.list();
285 if (fList != null) {
286 String prefix = mDir.getName() + ".";
287 int i = fList.length;
288 while (i > 0) {
289 i--;
290 if (fList[i].startsWith(prefix)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800291 Slog.i(TAG, "Deleting old usage file: " + fList[i]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700292 (new File(parentDir, fList[i])).delete();
293 }
294 }
295 }
296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 // Update current stats which are binned by date
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700298 mFileLeaf = getCurrentDateStr(FILE_PREFIX);
299 mFile = new File(mDir, mFileLeaf);
Mark Brophyc6350272011-08-05 16:16:39 +0100300 mHistoryFile = new AtomicFile(new File(mDir, FILE_HISTORY));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 readStatsFromFile();
Mark Brophyc6350272011-08-05 16:16:39 +0100302 readHistoryStatsFromFile();
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700303 mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700304 // mCal was set by getCurrentDateStr(), want to use that same time.
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700305 mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
307
308 /*
309 * Utility method to convert date into string.
310 */
311 private String getCurrentDateStr(String prefix) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 StringBuilder sb = new StringBuilder();
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700313 synchronized (mCal) {
314 mCal.setTimeInMillis(System.currentTimeMillis());
315 if (prefix != null) {
316 sb.append(prefix);
317 }
318 sb.append(mCal.get(Calendar.YEAR));
319 int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
320 if (mm < 10) {
321 sb.append("0");
322 }
323 sb.append(mm);
324 int dd = mCal.get(Calendar.DAY_OF_MONTH);
325 if (dd < 10) {
326 sb.append("0");
327 }
328 sb.append(dd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 return sb.toString();
331 }
332
333 private Parcel getParcelForFile(File file) throws IOException {
334 FileInputStream stream = new FileInputStream(file);
335 byte[] raw = readFully(stream);
336 Parcel in = Parcel.obtain();
337 in.unmarshall(raw, 0, raw.length);
338 in.setDataPosition(0);
339 stream.close();
340 return in;
341 }
342
343 private void readStatsFromFile() {
344 File newFile = mFile;
345 synchronized (mFileLock) {
346 try {
347 if (newFile.exists()) {
348 readStatsFLOCK(newFile);
349 } else {
350 // Check for file limit before creating a new file
351 checkFileLimitFLOCK();
352 newFile.createNewFile();
353 }
354 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800355 Slog.w(TAG,"Error : " + e + " reading data from file:" + newFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 }
357 }
358 }
359
360 private void readStatsFLOCK(File file) throws IOException {
361 Parcel in = getParcelForFile(file);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700362 int vers = in.readInt();
363 if (vers != VERSION) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800364 Slog.w(TAG, "Usage stats version changed; dropping");
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700365 return;
366 }
367 int N = in.readInt();
368 while (N > 0) {
369 N--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 String pkgName = in.readString();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700371 if (pkgName == null) {
372 break;
373 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800374 if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700375 PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 synchronized (mStatsLock) {
377 mStats.put(pkgName, pus);
378 }
379 }
380 }
381
Mark Brophyc6350272011-08-05 16:16:39 +0100382 private void readHistoryStatsFromFile() {
383 synchronized (mFileLock) {
384 if (mHistoryFile.getBaseFile().exists()) {
385 readHistoryStatsFLOCK(mHistoryFile);
386 }
387 }
388 }
389
390 private void readHistoryStatsFLOCK(AtomicFile file) {
391 FileInputStream fis = null;
392 try {
393 fis = mHistoryFile.openRead();
394 XmlPullParser parser = Xml.newPullParser();
395 parser.setInput(fis, null);
396 int eventType = parser.getEventType();
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000397 while (eventType != XmlPullParser.START_TAG &&
398 eventType != XmlPullParser.END_DOCUMENT) {
Mark Brophyc6350272011-08-05 16:16:39 +0100399 eventType = parser.next();
400 }
Narayan Kamath2ac3cb72014-01-06 11:31:35 +0000401 if (eventType == XmlPullParser.END_DOCUMENT) {
402 return;
403 }
404
Mark Brophyc6350272011-08-05 16:16:39 +0100405 String tagName = parser.getName();
406 if ("usage-history".equals(tagName)) {
407 String pkg = null;
408 do {
409 eventType = parser.next();
410 if (eventType == XmlPullParser.START_TAG) {
411 tagName = parser.getName();
412 int depth = parser.getDepth();
413 if ("pkg".equals(tagName) && depth == 2) {
414 pkg = parser.getAttributeValue(null, "name");
415 } else if ("comp".equals(tagName) && depth == 3 && pkg != null) {
416 String comp = parser.getAttributeValue(null, "name");
417 String lastResumeTimeStr = parser.getAttributeValue(null, "lrt");
418 if (comp != null && lastResumeTimeStr != null) {
419 try {
420 long lastResumeTime = Long.parseLong(lastResumeTimeStr);
421 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700422 ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
Mark Brophyc6350272011-08-05 16:16:39 +0100423 if (lrt == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700424 lrt = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100425 mLastResumeTimes.put(pkg, lrt);
426 }
427 lrt.put(comp, lastResumeTime);
428 }
429 } catch (NumberFormatException e) {
430 }
431 }
432 }
433 } else if (eventType == XmlPullParser.END_TAG) {
434 if ("pkg".equals(parser.getName())) {
435 pkg = null;
436 }
437 }
438 } while (eventType != XmlPullParser.END_DOCUMENT);
439 }
440 } catch (XmlPullParserException e) {
441 Slog.w(TAG,"Error reading history stats: " + e);
442 } catch (IOException e) {
443 Slog.w(TAG,"Error reading history stats: " + e);
444 } finally {
445 if (fis != null) {
446 try {
447 fis.close();
448 } catch (IOException e) {
449 }
450 }
451 }
452 }
453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 private ArrayList<String> getUsageStatsFileListFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 // Check if there are too many files in the system and delete older files
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700456 String fList[] = mDir.list();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 if (fList == null) {
458 return null;
459 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 ArrayList<String> fileList = new ArrayList<String>();
461 for (String file : fList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700462 if (!file.startsWith(FILE_PREFIX)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 continue;
464 }
465 if (file.endsWith(".bak")) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700466 (new File(mDir, file)).delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 continue;
468 }
469 fileList.add(file);
470 }
471 return fileList;
472 }
473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 private void checkFileLimitFLOCK() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 // Get all usage stats output files
476 ArrayList<String> fileList = getUsageStatsFileListFLOCK();
477 if (fileList == null) {
478 // Strange but we dont have to delete any thing
479 return;
480 }
481 int count = fileList.size();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700482 if (count <= MAX_NUM_FILES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 return;
484 }
485 // Sort files
486 Collections.sort(fileList);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700487 count -= MAX_NUM_FILES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 // Delete older files
489 for (int i = 0; i < count; i++) {
490 String fileName = fileList.get(i);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700491 File file = new File(mDir, fileName);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800492 Slog.i(TAG, "Deleting usage file : " + fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 file.delete();
494 }
495 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700496
497 /**
498 * Conditionally start up a disk write if it's been awhile, or the
499 * day has rolled over.
500 *
501 * This is called indirectly from user-facing actions (when
502 * 'force' is false) so it tries to be quick, without writing to
503 * disk directly or acquiring heavy locks.
504 *
505 * @params force do an unconditional, synchronous stats flush
506 * to disk on the current thread.
Mark Brophyc6350272011-08-05 16:16:39 +0100507 * @params forceWriteHistoryStats Force writing of historical stats.
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700508 */
Mark Brophyc6350272011-08-05 16:16:39 +0100509 private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700510 int curDay;
511 synchronized (mCal) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700512 mCal.setTimeInMillis(System.currentTimeMillis());
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700513 curDay = mCal.get(Calendar.DAY_OF_YEAR);
514 }
515 final boolean dayChanged = curDay != mLastWriteDay.get();
516
517 // Determine if the day changed... note that this will be wrong
518 // if the year has changed but we are in the same day of year...
519 // we can probably live with this.
520 final long currElapsedTime = SystemClock.elapsedRealtime();
521
522 // Fast common path, without taking the often-contentious
523 // mFileLock.
524 if (!force) {
525 if (!dayChanged &&
526 (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
527 // wait till the next update
528 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700530 if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
531 new Thread("UsageStatsService_DiskWriter") {
532 public void run() {
533 try {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700534 if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
Mark Brophyc6350272011-08-05 16:16:39 +0100535 writeStatsToFile(true, false);
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700536 } finally {
537 mUnforcedDiskWriteRunning.set(false);
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700538 if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700539 }
540 }
541 }.start();
542 }
543 return;
544 }
545
546 synchronized (mFileLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 // Get the most recent file
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700548 mFileLeaf = getCurrentDateStr(FILE_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 // Copy current file to back up
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800550 File backupFile = null;
551 if (mFile != null && mFile.exists()) {
552 backupFile = new File(mFile.getPath() + ".bak");
Dianne Hackborn1afd1c92010-03-18 22:47:17 -0700553 if (!backupFile.exists()) {
554 if (!mFile.renameTo(backupFile)) {
555 Slog.w(TAG, "Failed to persist new stats");
556 return;
557 }
558 } else {
559 mFile.delete();
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800560 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700561 }
Suchi Amalapurapube1c4222009-12-04 12:31:13 -0800562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 // Write mStats to file
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700565 writeStatsFLOCK(mFile);
566 mLastWriteElapsedTime.set(currElapsedTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 if (dayChanged) {
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700568 mLastWriteDay.set(curDay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 // clear stats
570 synchronized (mStats) {
571 mStats.clear();
572 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700573 mFile = new File(mDir, mFileLeaf);
574 checkFileLimitFLOCK();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 }
Mark Brophyc6350272011-08-05 16:16:39 +0100576
577 if (dayChanged || forceWriteHistoryStats) {
578 // Write history stats daily, or when forced (due to shutdown).
579 writeHistoryStatsFLOCK(mHistoryFile);
580 }
581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 // Delete the backup file
583 if (backupFile != null) {
584 backupFile.delete();
585 }
586 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800587 Slog.w(TAG, "Failed writing stats to file:" + mFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 if (backupFile != null) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700589 mFile.delete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 backupFile.renameTo(mFile);
591 }
592 }
593 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700594 if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
596
Brad Fitzpatrick389a9162010-08-03 15:41:05 -0700597 private void writeStatsFLOCK(File file) throws IOException {
598 FileOutputStream stream = new FileOutputStream(file);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700599 try {
600 Parcel out = Parcel.obtain();
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700601 writeStatsToParcelFLOCK(out);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700602 stream.write(out.marshall());
603 out.recycle();
604 stream.flush();
605 } finally {
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700606 FileUtils.sync(stream);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700607 stream.close();
608 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
610
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700611 private void writeStatsToParcelFLOCK(Parcel out) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 synchronized (mStatsLock) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700613 out.writeInt(VERSION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 Set<String> keys = mStats.keySet();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700615 out.writeInt(keys.size());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 for (String key : keys) {
617 PkgUsageStatsExtended pus = mStats.get(key);
618 out.writeString(key);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -0700619 pus.writeToParcel(out);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 }
621 }
622 }
623
Mark Brophyc6350272011-08-05 16:16:39 +0100624 /** Filter out stats for any packages which aren't present anymore. */
625 private void filterHistoryStats() {
626 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700627 IPackageManager pm = AppGlobals.getPackageManager();
628 for (int i=0; i<mLastResumeTimes.size(); i++) {
629 String pkg = mLastResumeTimes.keyAt(i);
630 try {
631 if (pm.getPackageUid(pkg, 0) < 0) {
632 mLastResumeTimes.removeAt(i);
633 i--;
634 }
635 } catch (RemoteException e) {
Mark Brophyc6350272011-08-05 16:16:39 +0100636 }
637 }
638 }
639 }
640
641 private void writeHistoryStatsFLOCK(AtomicFile historyFile) {
642 FileOutputStream fos = null;
643 try {
644 fos = historyFile.startWrite();
645 XmlSerializer out = new FastXmlSerializer();
646 out.setOutput(fos, "utf-8");
647 out.startDocument(null, true);
648 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
649 out.startTag(null, "usage-history");
650 synchronized (mStatsLock) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700651 for (int i=0; i<mLastResumeTimes.size(); i++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100652 out.startTag(null, "pkg");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700653 out.attribute(null, "name", mLastResumeTimes.keyAt(i));
654 ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
655 for (int j=0; j<comp.size(); j++) {
Mark Brophyc6350272011-08-05 16:16:39 +0100656 out.startTag(null, "comp");
Dianne Hackborn904a8572013-06-28 18:12:31 -0700657 out.attribute(null, "name", comp.keyAt(j));
658 out.attribute(null, "lrt", comp.valueAt(j).toString());
Mark Brophyc6350272011-08-05 16:16:39 +0100659 out.endTag(null, "comp");
660 }
661 out.endTag(null, "pkg");
662 }
663 }
664 out.endTag(null, "usage-history");
665 out.endDocument();
666
667 historyFile.finishWrite(fos);
668 } catch (IOException e) {
669 Slog.w(TAG,"Error writing history stats" + e);
670 if (fos != null) {
671 historyFile.failWrite(fos);
672 }
673 }
674 }
675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 public void publish(Context context) {
677 mContext = context;
678 ServiceManager.addService(SERVICE_NAME, asBinder());
679 }
Mark Brophyc6350272011-08-05 16:16:39 +0100680
681 /**
682 * Start watching packages to remove stats when a package is uninstalled.
683 * May only be called when the package manager is ready.
684 */
685 public void monitorPackages() {
686 mPackageMonitor = new PackageMonitor() {
687 @Override
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700688 public void onPackageRemovedAllUsers(String packageName, int uid) {
Mark Brophyc6350272011-08-05 16:16:39 +0100689 synchronized (mStatsLock) {
690 mLastResumeTimes.remove(packageName);
691 }
692 }
693 };
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700694 mPackageMonitor.register(mContext, null, true);
Mark Brophyc6350272011-08-05 16:16:39 +0100695 filterHistoryStats();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700696 }
Mark Brophyc6350272011-08-05 16:16:39 +0100697
698 public void shutdown() {
699 if (mPackageMonitor != null) {
700 mPackageMonitor.unregister();
701 }
702 Slog.i(TAG, "Writing usage stats before shutdown...");
703 writeStatsToFile(true, true);
704 }
705
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 public static IUsageStats getService() {
707 if (sService != null) {
708 return sService;
709 }
710 IBinder b = ServiceManager.getService(SERVICE_NAME);
711 sService = asInterface(b);
712 return sService;
713 }
714
715 public void noteResumeComponent(ComponentName componentName) {
716 enforceCallingPermission();
717 String pkgName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700719 if ((componentName == null) ||
720 ((pkgName = componentName.getPackageName()) == null)) {
721 return;
722 }
723
724 final boolean samePackage = pkgName.equals(mLastResumedPkg);
725 if (mIsResumed) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700726 if (mLastResumedPkg != null) {
727 // We last resumed some other package... just pause it now
728 // to recover.
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700729 if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700730 + " while already resumed in " + mLastResumedPkg);
731 PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
732 if (pus != null) {
733 pus.updatePause();
734 }
735 }
736 }
737
738 final boolean sameComp = samePackage
739 && componentName.getClassName().equals(mLastResumedComp);
740
741 mIsResumed = true;
742 mLastResumedPkg = pkgName;
743 mLastResumedComp = componentName.getClassName();
744
Joe Onorato8a9b2202010-02-26 18:56:32 -0800745 if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 PkgUsageStatsExtended pus = mStats.get(pkgName);
747 if (pus == null) {
748 pus = new PkgUsageStatsExtended();
749 mStats.put(pkgName, pus);
750 }
Mark Brophy9fc03302011-07-01 16:56:24 +0100751 pus.updateResume(mLastResumedComp, !samePackage);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700752 if (!sameComp) {
753 pus.addLaunchCount(mLastResumedComp);
754 }
Mark Brophyc6350272011-08-05 16:16:39 +0100755
Dianne Hackborn904a8572013-06-28 18:12:31 -0700756 ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100757 if (componentResumeTimes == null) {
Dianne Hackborn904a8572013-06-28 18:12:31 -0700758 componentResumeTimes = new ArrayMap<String, Long>();
Mark Brophyc6350272011-08-05 16:16:39 +0100759 mLastResumeTimes.put(pkgName, componentResumeTimes);
760 }
761 componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
764
765 public void notePauseComponent(ComponentName componentName) {
766 enforceCallingPermission();
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700767
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 synchronized (mStatsLock) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700769 String pkgName;
770 if ((componentName == null) ||
771 ((pkgName = componentName.getPackageName()) == null)) {
772 return;
773 }
774 if (!mIsResumed) {
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700775 if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700776 + pkgName + " to be paused");
777 return;
778 }
779 mIsResumed = false;
780
Joe Onorato8a9b2202010-02-26 18:56:32 -0800781 if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700782
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 PkgUsageStatsExtended pus = mStats.get(pkgName);
784 if (pus == null) {
785 // Weird some error here
Joe Onorato8a9b2202010-02-26 18:56:32 -0800786 Slog.i(TAG, "No package stats for pkg:"+pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 return;
788 }
789 pus.updatePause();
790 }
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700791
792 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100793 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700794 }
795
796 public void noteLaunchTime(ComponentName componentName, int millis) {
797 enforceCallingPermission();
798 String pkgName;
799 if ((componentName == null) ||
800 ((pkgName = componentName.getPackageName()) == null)) {
801 return;
802 }
803
804 // Persist current data to file if needed.
Mark Brophyc6350272011-08-05 16:16:39 +0100805 writeStatsToFile(false, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700806
807 synchronized (mStatsLock) {
808 PkgUsageStatsExtended pus = mStats.get(pkgName);
809 if (pus != null) {
810 pus.addLaunchTime(componentName.getClassName(), millis);
811 }
812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
814
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700815 public void noteFullyDrawnTime(ComponentName componentName, int millis) {
816 enforceCallingPermission();
817 String pkgName;
818 if ((componentName == null) ||
819 ((pkgName = componentName.getPackageName()) == null)) {
820 return;
821 }
822
823 // Persist current data to file if needed.
824 writeStatsToFile(false, false);
825
826 synchronized (mStatsLock) {
827 PkgUsageStatsExtended pus = mStats.get(pkgName);
828 if (pus != null) {
829 pus.addFullyDrawnTime(componentName.getClassName(), millis);
830 }
831 }
832 }
833
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 public void enforceCallingPermission() {
835 if (Binder.getCallingPid() == Process.myPid()) {
836 return;
837 }
838 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
839 Binder.getCallingPid(), Binder.getCallingUid(), null);
840 }
841
842 public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
843 mContext.enforceCallingOrSelfPermission(
844 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
845 String pkgName;
846 if ((componentName == null) ||
847 ((pkgName = componentName.getPackageName()) == null)) {
848 return null;
849 }
850 synchronized (mStatsLock) {
851 PkgUsageStatsExtended pus = mStats.get(pkgName);
Mark Brophyc6350272011-08-05 16:16:39 +0100852 Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
853 if (pus == null && lastResumeTimes == null) {
854 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
Mark Brophyc6350272011-08-05 16:16:39 +0100856 int launchCount = pus != null ? pus.mLaunchCount : 0;
857 long usageTime = pus != null ? pus.mUsageTime : 0;
858 return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
860 }
861
862 public PkgUsageStats[] getAllPkgUsageStats() {
863 mContext.enforceCallingOrSelfPermission(
864 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
865 synchronized (mStatsLock) {
Mark Brophyc6350272011-08-05 16:16:39 +0100866 int size = mLastResumeTimes.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 if (size <= 0) {
868 return null;
869 }
870 PkgUsageStats retArr[] = new PkgUsageStats[size];
Dianne Hackborn904a8572013-06-28 18:12:31 -0700871 for (int i=0; i<size; i++) {
872 String pkg = mLastResumeTimes.keyAt(i);
Mark Brophyc6350272011-08-05 16:16:39 +0100873 long usageTime = 0;
874 int launchCount = 0;
875
876 PkgUsageStatsExtended pus = mStats.get(pkg);
877 if (pus != null) {
878 usageTime = pus.mUsageTime;
879 launchCount = pus.mLaunchCount;
880 }
Dianne Hackborn904a8572013-06-28 18:12:31 -0700881 retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
882 mLastResumeTimes.valueAt(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884 return retArr;
885 }
886 }
887
888 static byte[] readFully(FileInputStream stream) throws java.io.IOException {
889 int pos = 0;
890 int avail = stream.available();
891 byte[] data = new byte[avail];
892 while (true) {
893 int amt = stream.read(data, pos, data.length-pos);
894 if (amt <= 0) {
895 return data;
896 }
897 pos += amt;
898 avail = stream.available();
899 if (avail > data.length-pos) {
900 byte[] newData = new byte[pos+avail];
901 System.arraycopy(data, 0, newData, 0, pos);
902 data = newData;
903 }
904 }
905 }
906
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700907 private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700908 boolean deleteAfterPrint, HashSet<String> packages) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 List<String> fileList = getUsageStatsFileListFLOCK();
910 if (fileList == null) {
911 return;
912 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 Collections.sort(fileList);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 for (String file : fileList) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700915 if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
916 // In this mode we don't print the current day's stats, since
917 // they are incomplete.
918 continue;
919 }
920 File dFile = new File(mDir, file);
921 String dateStr = file.substring(FILE_PREFIX.length());
Dianne Hackborn904a8572013-06-28 18:12:31 -0700922 if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
923 // If the remainder does not start with a number, it is not a date,
924 // so we should ignore it for purposes here.
925 continue;
926 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 try {
928 Parcel in = getParcelForFile(dFile);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700929 collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,
930 packages);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700931 if (deleteAfterPrint) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 // Delete old file after collecting info only for checkin requests
933 dFile.delete();
934 }
935 } catch (FileNotFoundException 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 return;
938 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800939 Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941 }
942 }
943
944 private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700945 String date, boolean isCompactOutput, HashSet<String> packages) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700946 StringBuilder sb = new StringBuilder(512);
947 if (isCompactOutput) {
948 sb.append("D:");
949 sb.append(CHECKIN_VERSION);
950 sb.append(',');
951 } else {
952 sb.append("Date: ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 }
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700954
955 sb.append(date);
956
957 int vers = in.readInt();
958 if (vers != VERSION) {
959 sb.append(" (old data version)");
960 pw.println(sb.toString());
961 return;
962 }
963
964 pw.println(sb.toString());
965 int N = in.readInt();
966
967 while (N > 0) {
968 N--;
969 String pkgName = in.readString();
970 if (pkgName == null) {
971 break;
972 }
973 sb.setLength(0);
974 PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -0700975 if (packages != null && !packages.contains(pkgName)) {
976 // This package has not been requested -- don't print
977 // anything for it.
978 } else if (isCompactOutput) {
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700979 sb.append("P:");
980 sb.append(pkgName);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700981 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700982 sb.append(pus.mLaunchCount);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -0700983 sb.append(',');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700984 sb.append(pus.mUsageTime);
985 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700986 final int NLT = pus.mLaunchTimes.size();
987 for (int i=0; i<NLT; i++) {
988 sb.append("A:");
989 String activity = pus.mLaunchTimes.keyAt(i);
990 sb.append(activity);
991 TimeStats times = pus.mLaunchTimes.valueAt(i);
992 sb.append(',');
993 sb.append(times.count);
994 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
995 sb.append(",");
996 sb.append(times.times[j]);
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700997 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -0700998 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -0700999 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001000 final int NFDT = pus.mFullyDrawnTimes.size();
1001 for (int i=0; i<NFDT; i++) {
1002 sb.append("A:");
1003 String activity = pus.mFullyDrawnTimes.keyAt(i);
1004 sb.append(activity);
1005 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1006 for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) {
1007 sb.append(",");
1008 sb.append(times.times[j]);
1009 }
1010 sb.append('\n');
1011 }
1012
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001013 } else {
1014 sb.append(" ");
1015 sb.append(pkgName);
1016 sb.append(": ");
1017 sb.append(pus.mLaunchCount);
1018 sb.append(" times, ");
1019 sb.append(pus.mUsageTime);
1020 sb.append(" ms");
1021 sb.append('\n');
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001022 final int NLT = pus.mLaunchTimes.size();
1023 for (int i=0; i<NLT; i++) {
1024 sb.append(" ");
1025 sb.append(pus.mLaunchTimes.keyAt(i));
1026 TimeStats times = pus.mLaunchTimes.valueAt(i);
1027 sb.append(": ");
1028 sb.append(times.count);
1029 sb.append(" starts");
1030 int lastBin = 0;
1031 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1032 if (times.times[j] != 0) {
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001033 sb.append(", ");
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001034 sb.append(lastBin);
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001035 sb.append('-');
1036 sb.append(LAUNCH_TIME_BINS[j]);
Dianne Hackborn760ec4a2009-06-17 20:05:26 -07001037 sb.append("ms=");
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001038 sb.append(times.times[j]);
Dianne Hackbornf210d6b2009-04-13 18:42:49 -07001039 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001040 lastBin = LAUNCH_TIME_BINS[j];
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001041 }
Dianne Hackborn2286cdc2013-07-01 19:10:06 -07001042 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1043 sb.append(", ");
1044 sb.append(">=");
1045 sb.append(lastBin);
1046 sb.append("ms=");
1047 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1048 }
1049 sb.append('\n');
1050 }
1051 final int NFDT = pus.mFullyDrawnTimes.size();
1052 for (int i=0; i<NFDT; i++) {
1053 sb.append(" ");
1054 sb.append(pus.mFullyDrawnTimes.keyAt(i));
1055 TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
1056 sb.append(": fully drawn ");
1057 boolean needComma = false;
1058 int lastBin = 0;
1059 for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) {
1060 if (times.times[j] != 0) {
1061 if (needComma) {
1062 sb.append(", ");
1063 } else {
1064 needComma = true;
1065 }
1066 sb.append(lastBin);
1067 sb.append('-');
1068 sb.append(LAUNCH_TIME_BINS[j]);
1069 sb.append("ms=");
1070 sb.append(times.times[j]);
1071 }
1072 lastBin = LAUNCH_TIME_BINS[j];
1073 }
1074 if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
1075 if (needComma) {
1076 sb.append(", ");
1077 }
1078 sb.append(">=");
1079 sb.append(lastBin);
1080 sb.append("ms=");
1081 sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
1082 }
1083 sb.append('\n');
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001084 }
1085 }
1086
1087 pw.write(sb.toString());
1088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
1090
1091 /**
1092 * Searches array of arguments for the specified string
1093 * @param args array of argument strings
1094 * @param value value to search for
1095 * @return true if the value is contained in the array
1096 */
1097 private static boolean scanArgs(String[] args, String value) {
1098 if (args != null) {
1099 for (String arg : args) {
1100 if (value.equals(arg)) {
1101 return true;
1102 }
1103 }
1104 }
1105 return false;
1106 }
1107
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001108 /**
1109 * Searches array of arguments for the specified string's data
1110 * @param args array of argument strings
1111 * @param value value to search for
1112 * @return the string of data after the arg, or null if there is none
1113 */
1114 private static String scanArgsData(String[] args, String value) {
1115 if (args != null) {
1116 final int N = args.length;
1117 for (int i=0; i<N; i++) {
1118 if (value.equals(args[i])) {
1119 i++;
1120 return i < N ? args[i] : null;
1121 }
1122 }
1123 }
1124 return null;
1125 }
1126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 @Override
1128 /*
1129 * The data persisted to file is parsed and the stats are computed.
1130 */
1131 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07001132 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
1133 != PackageManager.PERMISSION_GRANTED) {
1134 pw.println("Permission Denial: can't dump UsageStats from from pid="
1135 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1136 + " without permission " + android.Manifest.permission.DUMP);
1137 return;
1138 }
1139
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001140 final boolean isCheckinRequest = scanArgs(args, "--checkin");
1141 final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
1142 final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001143 final String rawPackages = scanArgsData(args, "--packages");
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001144
1145 // Make sure the current stats are written to the file. This
1146 // doesn't need to be done if we are deleting files after printing,
1147 // since it that case we won't print the current stats.
1148 if (!deleteAfterPrint) {
Mark Brophyc6350272011-08-05 16:16:39 +01001149 writeStatsToFile(true, false);
Dianne Hackborn6447ca32009-04-07 19:50:08 -07001150 }
1151
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001152 HashSet<String> packages = null;
1153 if (rawPackages != null) {
1154 if (!"*".equals(rawPackages)) {
1155 // A * is a wildcard to show all packages.
1156 String[] names = rawPackages.split(",");
1157 for (String n : names) {
1158 if (packages == null) {
1159 packages = new HashSet<String>();
1160 }
1161 packages.add(n);
1162 }
1163 }
1164 } else if (isCheckinRequest) {
1165 // If checkin doesn't specify any packages, then we simply won't
1166 // show anything.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001167 Slog.w(TAG, "Checkin without packages");
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001168 return;
1169 }
1170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 synchronized (mFileLock) {
Dianne Hackborn9fdbf6a2009-07-19 14:18:51 -07001172 collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 }
1174 }
1175
1176}