1 /*
2  * Copyright (C) 2013 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 
17 package com.android.server.am;
18 
19 import android.os.Binder;
20 import android.os.Parcel;
21 import android.os.ParcelFileDescriptor;
22 import android.os.RemoteException;
23 import android.os.SystemClock;
24 import android.os.SystemProperties;
25 import android.service.procstats.ProcessStatsServiceDumpProto;
26 import android.text.format.DateFormat;
27 import android.util.ArrayMap;
28 import android.util.AtomicFile;
29 import android.util.Log;
30 import android.util.LongSparseArray;
31 import android.util.Slog;
32 import android.util.SparseArray;
33 import android.util.TimeUtils;
34 import android.util.proto.ProtoOutputStream;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.app.procstats.DumpUtils;
38 import com.android.internal.app.procstats.IProcessStats;
39 import com.android.internal.app.procstats.ProcessState;
40 import com.android.internal.app.procstats.ProcessStats;
41 import com.android.internal.app.procstats.ServiceState;
42 import com.android.internal.os.BackgroundThread;
43 
44 import java.io.File;
45 import java.io.FileDescriptor;
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.PrintWriter;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.concurrent.locks.ReentrantLock;
56 
57 public final class ProcessStatsService extends IProcessStats.Stub {
58     static final String TAG = "ProcessStatsService";
59     static final boolean DEBUG = false;
60 
61     // Most data is kept in a sparse data structure: an integer array which integer
62     // holds the type of the entry, and the identifier for a long array that data
63     // exists in and the offset into the array to find it.  The constants below
64     // define the encoding of that data in an integer.
65 
66     static final int MAX_HISTORIC_STATES = 8;   // Maximum number of historic states we will keep.
67     static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
68     static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
69     static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
70     static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
71 
72     final ActivityManagerService mAm;
73     final File mBaseDir;
74     ProcessStats mProcessStats;
75     AtomicFile mFile;
76     boolean mCommitPending;
77     boolean mShuttingDown;
78     int mLastMemOnlyState = -1;
79     boolean mMemFactorLowered;
80 
81     final ReentrantLock mWriteLock = new ReentrantLock();
82     final Object mPendingWriteLock = new Object();
83     AtomicFile mPendingWriteFile;
84     Parcel mPendingWrite;
85     boolean mPendingWriteCommitted;
86     long mLastWriteTime;
87 
88     /** For CTS to inject the screen state. */
89     @GuardedBy("mAm")
90     Boolean mInjectedScreenState;
91 
ProcessStatsService(ActivityManagerService am, File file)92     public ProcessStatsService(ActivityManagerService am, File file) {
93         mAm = am;
94         mBaseDir = file;
95         mBaseDir.mkdirs();
96         mProcessStats = new ProcessStats(true);
97         updateFile();
98         SystemProperties.addChangeCallback(new Runnable() {
99             @Override public void run() {
100                 synchronized (mAm) {
101                     if (mProcessStats.evaluateSystemProperties(false)) {
102                         mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
103                         writeStateLocked(true, true);
104                         mProcessStats.evaluateSystemProperties(true);
105                     }
106                 }
107             }
108         });
109     }
110 
111     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)112     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
113             throws RemoteException {
114         try {
115             return super.onTransact(code, data, reply, flags);
116         } catch (RuntimeException e) {
117             if (!(e instanceof SecurityException)) {
118                 Slog.wtf(TAG, "Process Stats Crash", e);
119             }
120             throw e;
121         }
122     }
123 
124     @GuardedBy("mAm")
updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder, String packageName, int uid, long versionCode, String processName)125     public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
126             String packageName, int uid, long versionCode, String processName) {
127         holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
128         holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
129     }
130 
131     @GuardedBy("mAm")
getProcessStateLocked(String packageName, int uid, long versionCode, String processName)132     public ProcessState getProcessStateLocked(String packageName,
133             int uid, long versionCode, String processName) {
134         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
135     }
136 
137     @GuardedBy("mAm")
getServiceStateLocked(String packageName, int uid, long versionCode, String processName, String className)138     public ServiceState getServiceStateLocked(String packageName, int uid,
139             long versionCode, String processName, String className) {
140         return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
141                 className);
142     }
143 
isMemFactorLowered()144     public boolean isMemFactorLowered() {
145         return mMemFactorLowered;
146     }
147 
148     @GuardedBy("mAm")
setMemFactorLocked(int memFactor, boolean screenOn, long now)149     public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
150         mMemFactorLowered = memFactor < mLastMemOnlyState;
151         mLastMemOnlyState = memFactor;
152         if (mInjectedScreenState != null) {
153             screenOn = mInjectedScreenState;
154         }
155         if (screenOn) {
156             memFactor += ProcessStats.ADJ_SCREEN_ON;
157         }
158         if (memFactor != mProcessStats.mMemFactor) {
159             if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) {
160                 mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor]
161                         += now - mProcessStats.mStartTime;
162             }
163             mProcessStats.mMemFactor = memFactor;
164             mProcessStats.mStartTime = now;
165             final ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pmap
166                     = mProcessStats.mPackages.getMap();
167             for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) {
168                 final SparseArray<LongSparseArray<ProcessStats.PackageState>> uids =
169                         pmap.valueAt(ipkg);
170                 for (int iuid=uids.size()-1; iuid>=0; iuid--) {
171                     final LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
172                     for (int iver=vers.size()-1; iver>=0; iver--) {
173                         final ProcessStats.PackageState pkg = vers.valueAt(iver);
174                         final ArrayMap<String, ServiceState> services = pkg.mServices;
175                         for (int isvc=services.size()-1; isvc>=0; isvc--) {
176                             final ServiceState service = services.valueAt(isvc);
177                             service.setMemFactor(memFactor, now);
178                         }
179                     }
180                 }
181             }
182             return true;
183         }
184         return false;
185     }
186 
187     @GuardedBy("mAm")
getMemFactorLocked()188     public int getMemFactorLocked() {
189         return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
190     }
191 
192     @GuardedBy("mAm")
addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, long nativeMem)193     public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
194             long nativeMem) {
195         mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
196     }
197 
198     @GuardedBy("mAm")
updateTrackingAssociationsLocked(int curSeq, long now)199     public void updateTrackingAssociationsLocked(int curSeq, long now) {
200         mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
201     }
202 
203     @GuardedBy("mAm")
shouldWriteNowLocked(long now)204     public boolean shouldWriteNowLocked(long now) {
205         if (now > (mLastWriteTime+WRITE_PERIOD)) {
206             if (SystemClock.elapsedRealtime()
207                     > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
208                     SystemClock.uptimeMillis()
209                     > (mProcessStats.mTimePeriodStartUptime+ProcessStats.COMMIT_UPTIME_PERIOD)) {
210                 mCommitPending = true;
211             }
212             return true;
213         }
214         return false;
215     }
216 
217     @GuardedBy("mAm")
shutdownLocked()218     public void shutdownLocked() {
219         Slog.w(TAG, "Writing process stats before shutdown...");
220         mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
221         writeStateSyncLocked();
222         mShuttingDown = true;
223     }
224 
225     @GuardedBy("mAm")
writeStateAsyncLocked()226     public void writeStateAsyncLocked() {
227         writeStateLocked(false);
228     }
229 
230     @GuardedBy("mAm")
writeStateSyncLocked()231     public void writeStateSyncLocked() {
232         writeStateLocked(true);
233     }
234 
235     @GuardedBy("mAm")
writeStateLocked(boolean sync)236     private void writeStateLocked(boolean sync) {
237         if (mShuttingDown) {
238             return;
239         }
240         boolean commitPending = mCommitPending;
241         mCommitPending = false;
242         writeStateLocked(sync, commitPending);
243     }
244 
245     @GuardedBy("mAm")
writeStateLocked(boolean sync, final boolean commit)246     public void writeStateLocked(boolean sync, final boolean commit) {
247         final long totalTime;
248         synchronized (mPendingWriteLock) {
249             final long now = SystemClock.uptimeMillis();
250             if (mPendingWrite == null || !mPendingWriteCommitted) {
251                 mPendingWrite = Parcel.obtain();
252                 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
253                 mProcessStats.mTimePeriodEndUptime = now;
254                 if (commit) {
255                     mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
256                 }
257                 mProcessStats.writeToParcel(mPendingWrite, 0);
258                 mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
259                 mPendingWriteCommitted = commit;
260             }
261             if (commit) {
262                 mProcessStats.resetSafely();
263                 updateFile();
264                 mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
265             }
266             mLastWriteTime = SystemClock.uptimeMillis();
267             totalTime = SystemClock.uptimeMillis() - now;
268             if (DEBUG) Slog.d(TAG, "Prepared write state in " + now + "ms");
269             if (!sync) {
270                 BackgroundThread.getHandler().post(new Runnable() {
271                     @Override public void run() {
272                         performWriteState(totalTime);
273                     }
274                 });
275                 return;
276             }
277         }
278 
279         performWriteState(totalTime);
280     }
281 
updateFile()282     private void updateFile() {
283         mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
284                 + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
285         mLastWriteTime = SystemClock.uptimeMillis();
286     }
287 
performWriteState(long initialTime)288     void performWriteState(long initialTime) {
289         if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
290         Parcel data;
291         AtomicFile file;
292         synchronized (mPendingWriteLock) {
293             data = mPendingWrite;
294             file = mPendingWriteFile;
295             mPendingWriteCommitted = false;
296             if (data == null) {
297                 return;
298             }
299             mPendingWrite = null;
300             mPendingWriteFile = null;
301             mWriteLock.lock();
302         }
303 
304         final long startTime = SystemClock.uptimeMillis();
305         FileOutputStream stream = null;
306         try {
307             stream = file.startWrite();
308             stream.write(data.marshall());
309             stream.flush();
310             file.finishWrite(stream);
311             com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
312                     "procstats", SystemClock.uptimeMillis() - startTime + initialTime);
313             if (DEBUG) Slog.d(TAG, "Write completed successfully!");
314         } catch (IOException e) {
315             Slog.w(TAG, "Error writing process statistics", e);
316             file.failWrite(stream);
317         } finally {
318             data.recycle();
319             trimHistoricStatesWriteLocked();
320             mWriteLock.unlock();
321         }
322     }
323 
324     @GuardedBy("mAm")
readLocked(ProcessStats stats, AtomicFile file)325     boolean readLocked(ProcessStats stats, AtomicFile file) {
326         try {
327             FileInputStream stream = file.openRead();
328             stats.read(stream);
329             stream.close();
330             if (stats.mReadError != null) {
331                 Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError);
332                 if (DEBUG) {
333                     ArrayMap<String, SparseArray<ProcessState>> procMap = stats.mProcesses.getMap();
334                     final int NPROC = procMap.size();
335                     for (int ip=0; ip<NPROC; ip++) {
336                         Slog.w(TAG, "Process: " + procMap.keyAt(ip));
337                         SparseArray<ProcessState> uids = procMap.valueAt(ip);
338                         final int NUID = uids.size();
339                         for (int iu=0; iu<NUID; iu++) {
340                             Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
341                         }
342                     }
343                     ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pkgMap
344                             = stats.mPackages.getMap();
345                     final int NPKG = pkgMap.size();
346                     for (int ip=0; ip<NPKG; ip++) {
347                         Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
348                         SparseArray<LongSparseArray<ProcessStats.PackageState>> uids
349                                 = pkgMap.valueAt(ip);
350                         final int NUID = uids.size();
351                         for (int iu=0; iu<NUID; iu++) {
352                             Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
353                             LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
354                             final int NVERS = vers.size();
355                             for (int iv=0; iv<NVERS; iv++) {
356                                 Slog.w(TAG, "    Vers: " + vers.keyAt(iv));
357                                 ProcessStats.PackageState pkgState = vers.valueAt(iv);
358                                 final int NPROCS = pkgState.mProcesses.size();
359                                 for (int iproc=0; iproc<NPROCS; iproc++) {
360                                     Slog.w(TAG, "      Process " + pkgState.mProcesses.keyAt(iproc)
361                                             + ": " + pkgState.mProcesses.valueAt(iproc));
362                                 }
363                                 final int NSRVS = pkgState.mServices.size();
364                                 for (int isvc=0; isvc<NSRVS; isvc++) {
365                                     Slog.w(TAG, "      Service " + pkgState.mServices.keyAt(isvc)
366                                             + ": " + pkgState.mServices.valueAt(isvc));
367 
368                                 }
369                                 final int NASCS = pkgState.mAssociations.size();
370                                 for (int iasc=0; iasc<NASCS; iasc++) {
371                                     Slog.w(TAG, "      Association "
372                                             + pkgState.mServices.keyAt(iasc)
373                                             + ": " + pkgState.mAssociations.valueAt(iasc));
374 
375                                 }
376                             }
377                         }
378                     }
379                 }
380                 return false;
381             }
382         } catch (Throwable e) {
383             stats.mReadError = "caught exception: " + e;
384             Slog.e(TAG, "Error reading process statistics", e);
385             return false;
386         }
387         return true;
388     }
389 
getCommittedFiles(int minNum, boolean inclCurrent, boolean inclCheckedIn)390     private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
391             boolean inclCheckedIn) {
392         File[] files = mBaseDir.listFiles();
393         if (files == null || files.length <= minNum) {
394             return null;
395         }
396         ArrayList<String> filesArray = new ArrayList<String>(files.length);
397         String currentFile = mFile.getBaseFile().getPath();
398         if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
399         for (int i=0; i<files.length; i++) {
400             File file = files[i];
401             String fileStr = file.getPath();
402             if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
403             if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
404                 if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
405                 continue;
406             }
407             if (!inclCurrent && fileStr.equals(currentFile)) {
408                 if (DEBUG) Slog.d(TAG, "Skipping: current stats");
409                 continue;
410             }
411             filesArray.add(fileStr);
412         }
413         Collections.sort(filesArray);
414         return filesArray;
415     }
416 
417     @GuardedBy("mAm")
trimHistoricStatesWriteLocked()418     public void trimHistoricStatesWriteLocked() {
419         ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
420         if (filesArray == null) {
421             return;
422         }
423         while (filesArray.size() > MAX_HISTORIC_STATES) {
424             String file = filesArray.remove(0);
425             Slog.i(TAG, "Pruning old procstats: " + file);
426             (new File(file)).delete();
427         }
428     }
429 
430     @GuardedBy("mAm")
dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage)431     boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
432             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
433             boolean sepProcStates, int[] procStates, long now, String reqPackage) {
434         ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked(
435                 screenStates, memStates, procStates, procStates, now, reqPackage, false);
436         if (procs.size() > 0) {
437             if (header != null) {
438                 pw.println(header);
439             }
440             DumpUtils.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
441                     sepMemStates, memStates, sepProcStates, procStates, now);
442             return true;
443         }
444         return false;
445     }
446 
parseStateList(String[] states, int mult, String arg, boolean[] outSep, String[] outError)447     static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
448             String[] outError) {
449         ArrayList<Integer> res = new ArrayList<Integer>();
450         int lastPos = 0;
451         for (int i=0; i<=arg.length(); i++) {
452             char c = i < arg.length() ? arg.charAt(i) : 0;
453             if (c != ',' && c != '+' && c != ' ' && c != 0) {
454                 continue;
455             }
456             boolean isSep = c == ',';
457             if (lastPos == 0) {
458                 // We now know the type of op.
459                 outSep[0] = isSep;
460             } else if (c != 0 && outSep[0] != isSep) {
461                 outError[0] = "inconsistent separators (can't mix ',' with '+')";
462                 return null;
463             }
464             if (lastPos < (i-1)) {
465                 String str = arg.substring(lastPos, i);
466                 for (int j=0; j<states.length; j++) {
467                     if (str.equals(states[j])) {
468                         res.add(j);
469                         str = null;
470                         break;
471                     }
472                 }
473                 if (str != null) {
474                     outError[0] = "invalid word \"" + str + "\"";
475                     return null;
476                 }
477             }
478             lastPos = i + 1;
479         }
480 
481         int[] finalRes = new int[res.size()];
482         for (int i=0; i<res.size(); i++) {
483             finalRes[i] = res.get(i) * mult;
484         }
485         return finalRes;
486     }
487 
488     static int parseSectionOptions(String optionsStr) {
489         final String sep = ",";
490         String[] sectionsStr = optionsStr.split(sep);
491         if (sectionsStr.length == 0) {
492             return ProcessStats.REPORT_ALL;
493         }
494         int res = 0;
495         List<String> optionStrList = Arrays.asList(ProcessStats.OPTIONS_STR);
496         for (String sectionStr : sectionsStr) {
497             int optionIndex = optionStrList.indexOf(sectionStr);
498             if (optionIndex != -1) {
499                 res |= ProcessStats.OPTIONS[optionIndex];
500             }
501         }
502         return res;
503     }
504 
505     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
506         mAm.mContext.enforceCallingOrSelfPermission(
507                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
508         Parcel current = Parcel.obtain();
509         synchronized (mAm) {
510             long now = SystemClock.uptimeMillis();
511             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
512             mProcessStats.mTimePeriodEndUptime = now;
513             mProcessStats.writeToParcel(current, now, 0);
514         }
515         mWriteLock.lock();
516         try {
517             if (historic != null) {
518                 ArrayList<String> files = getCommittedFiles(0, false, true);
519                 if (files != null) {
520                     for (int i=files.size()-1; i>=0; i--) {
521                         try {
522                             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
523                                     new File(files.get(i)), ParcelFileDescriptor.MODE_READ_ONLY);
524                             historic.add(pfd);
525                         } catch (IOException e) {
526                             Slog.w(TAG, "Failure opening procstat file " + files.get(i), e);
527                         }
528                     }
529                 }
530             }
531         } finally {
532             mWriteLock.unlock();
533         }
534         return current.marshall();
535     }
536 
537     /**
538      * Get stats committed after highWaterMarkMs
539      * @param highWaterMarkMs Report stats committed after this time.
540      * @param section Integer mask to indicage which sections to include in the stats.
541      * @param doAggregate Whether to aggregate the stats or keep them separated.
542      * @return List of proto binary of individual commit files or one that is merged from them.
543      */
544     @Override
545     public long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate,
546             List<ParcelFileDescriptor> committedStats) {
547         mAm.mContext.enforceCallingOrSelfPermission(
548                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
549 
550         ProcessStats mergedStats = new ProcessStats(false);
551         long newHighWaterMark = highWaterMarkMs;
552         mWriteLock.lock();
553         try {
554             ArrayList<String> files = getCommittedFiles(0, false, true);
555             if (files != null) {
556                 String highWaterMarkStr =
557                         DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
558                 ProcessStats stats = new ProcessStats(false);
559                 for (int i = files.size() - 1; i >= 0; i--) {
560                     String fileName = files.get(i);
561                     try {
562                         String startTimeStr = fileName.substring(
563                                 fileName.lastIndexOf(STATE_FILE_PREFIX)
564                                         + STATE_FILE_PREFIX.length(),
565                                 fileName.lastIndexOf(STATE_FILE_SUFFIX));
566                         if (startTimeStr.compareToIgnoreCase(highWaterMarkStr) > 0) {
567                             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
568                                     new File(fileName),
569                                     ParcelFileDescriptor.MODE_READ_ONLY);
570                             InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
571                             stats.reset();
572                             stats.read(is);
573                             is.close();
574                             if (stats.mTimePeriodStartClock > newHighWaterMark) {
575                                 newHighWaterMark = stats.mTimePeriodStartClock;
576                             }
577                             if (doAggregate) {
578                                 mergedStats.add(stats);
579                             } else {
580                                 committedStats.add(protoToParcelFileDescriptor(stats, section));
581                             }
582                             if (stats.mReadError != null) {
583                                 Log.w(TAG, "Failure reading process stats: " + stats.mReadError);
584                                 continue;
585                             }
586                         }
587                     } catch (IOException e) {
588                         Slog.w(TAG, "Failure opening procstat file " + fileName, e);
589                     } catch (IndexOutOfBoundsException e) {
590                         Slog.w(TAG, "Failure to read and parse commit file " + fileName, e);
591                     }
592                 }
593                 if (doAggregate) {
594                     committedStats.add(protoToParcelFileDescriptor(mergedStats, section));
595                 }
596                 return newHighWaterMark;
597             }
598         } catch (IOException e) {
599             Slog.w(TAG, "Failure opening procstat file", e);
600         } finally {
mWriteLock.unlock()601             mWriteLock.unlock();
602         }
603         return newHighWaterMark;
604     }
605 
606     private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
607             throws IOException {
608         final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
609         Thread thr = new Thread("ProcessStats pipe output") {
610             public void run() {
611                 try {
612                     FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
613                     final ProtoOutputStream proto = new ProtoOutputStream(fout);
614                     stats.writeToProto(proto, stats.mTimePeriodEndRealtime, section);
615                     proto.flush();
616                     fout.close();
617                 } catch (IOException e) {
618                     Slog.w(TAG, "Failure writing pipe", e);
619                 }
620             }
621         };
622         thr.start();
623         return fds[0];
624     }
625 
626     public ParcelFileDescriptor getStatsOverTime(long minTime) {
627         mAm.mContext.enforceCallingOrSelfPermission(
628                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
629         Parcel current = Parcel.obtain();
630         long curTime;
631         synchronized (mAm) {
632             long now = SystemClock.uptimeMillis();
633             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
634             mProcessStats.mTimePeriodEndUptime = now;
635             mProcessStats.writeToParcel(current, now, 0);
636             curTime = mProcessStats.mTimePeriodEndRealtime
637                     - mProcessStats.mTimePeriodStartRealtime;
638         }
639         mWriteLock.lock();
640         try {
641             if (curTime < minTime) {
642                 // Need to add in older stats to reach desired time.
643                 ArrayList<String> files = getCommittedFiles(0, false, true);
644                 if (files != null && files.size() > 0) {
645                     current.setDataPosition(0);
646                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
647                     current.recycle();
648                     int i = files.size()-1;
649                     while (i >= 0 && (stats.mTimePeriodEndRealtime
650                             - stats.mTimePeriodStartRealtime) < minTime) {
651                         AtomicFile file = new AtomicFile(new File(files.get(i)));
652                         i--;
653                         ProcessStats moreStats = new ProcessStats(false);
654                         readLocked(moreStats, file);
655                         if (moreStats.mReadError == null) {
656                             stats.add(moreStats);
657                             StringBuilder sb = new StringBuilder();
658                             sb.append("Added stats: ");
659                             sb.append(moreStats.mTimePeriodStartClockStr);
660                             sb.append(", over ");
661                             TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime
662                                     - moreStats.mTimePeriodStartRealtime, sb);
663                             Slog.i(TAG, sb.toString());
664                         } else {
665                             Slog.w(TAG, "Failure reading " + files.get(i+1) + "; "
666                                     + moreStats.mReadError);
667                             continue;
668                         }
669                     }
670                     current = Parcel.obtain();
671                     stats.writeToParcel(current, 0);
672                 }
673             }
674             final byte[] outData = current.marshall();
675             current.recycle();
676             final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
677             Thread thr = new Thread("ProcessStats pipe output") {
678                 public void run() {
679                     FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
680                     try {
681                         fout.write(outData);
682                         fout.close();
683                     } catch (IOException e) {
684                         Slog.w(TAG, "Failure writing pipe", e);
685                     }
686                 }
687             };
688             thr.start();
689             return fds[0];
690         } catch (IOException e) {
691             Slog.w(TAG, "Failed building output pipe", e);
692         } finally {
693             mWriteLock.unlock();
694         }
695         return null;
696     }
697 
698     public int getCurrentMemoryState() {
699         synchronized (mAm) {
700             return mLastMemOnlyState;
701         }
702     }
703 
704     private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
705             String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
706             boolean dumpAll, boolean activeOnly, int section) {
707         ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
708                 - (ProcessStats.COMMIT_PERIOD/2));
709         if (pfd == null) {
710             pw.println("Unable to build stats!");
711             return;
712         }
713         ProcessStats stats = new ProcessStats(false);
714         InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
715         stats.read(stream);
716         if (stats.mReadError != null) {
717             pw.print("Failure reading: "); pw.println(stats.mReadError);
718             return;
719         }
720         if (isCompact) {
721             stats.dumpCheckinLocked(pw, reqPackage, section);
722         } else {
723             if (dumpDetails || dumpFullDetails) {
724                 stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
725                         activeOnly, section);
726             } else {
727                 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
728             }
729         }
730     }
731 
732     static private void dumpHelp(PrintWriter pw) {
733         pw.println("Process stats (procstats) dump options:");
734         pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
735         pw.println("    [--details] [--full-details] [--current] [--hours N] [--last N]");
736         pw.println("    [--max N] --active] [--commit] [--reset] [--clear] [--write] [-h]");
737         pw.println("    [--start-testing] [--stop-testing] ");
738         pw.println("    [--pretend-screen-on] [--pretend-screen-off] [--stop-pretend-screen]");
739         pw.println("    [<package.name>]");
740         pw.println("  --checkin: perform a checkin: print and delete old committed states.");
741         pw.println("  -c: print only state in checkin format.");
742         pw.println("  --csv: output data suitable for putting in a spreadsheet.");
743         pw.println("  --csv-screen: on, off.");
744         pw.println("  --csv-mem: norm, mod, low, crit.");
745         pw.println("  --csv-proc: pers, top, fore, vis, precept, backup,");
746         pw.println("    service, home, prev, cached");
747         pw.println("  --details: dump per-package details, not just summary.");
748         pw.println("  --full-details: dump all timing and active state details.");
749         pw.println("  --current: only dump current state.");
750         pw.println("  --hours: aggregate over about N last hours.");
751         pw.println("  --last: only show the last committed stats at index N (starting at 1).");
752         pw.println("  --max: for -a, max num of historical batches to print.");
753         pw.println("  --active: only show currently active processes/services.");
754         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
755         pw.println("  --section: proc|pkg-proc|pkg-svc|pkg-asc|pkg-all|all ");
756         pw.println("    options can be combined to select desired stats");
757         pw.println("  --reset: reset current stats, without committing.");
758         pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
759         pw.println("  --write: write current in-memory stats to disk.");
760         pw.println("  --read: replace current stats with last-written stats.");
761         pw.println("  --start-testing: clear all stats and starting high frequency pss sampling.");
762         pw.println("  --stop-testing: stop high frequency pss sampling.");
763         pw.println("  --pretend-screen-on: pretend screen is on.");
764         pw.println("  --pretend-screen-off: pretend screen is off.");
765         pw.println("  --stop-pretend-screen: forget \"pretend screen\" and use the real state.");
766         pw.println("  -a: print everything.");
767         pw.println("  -h: print this help text.");
768         pw.println("  <package.name>: optional name of package to filter output by.");
769     }
770 
771     @Override
772     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
773         if (!com.android.internal.util.DumpUtils.checkDumpAndUsageStatsPermission(mAm.mContext,
774                 TAG, pw)) return;
775 
776         long ident = Binder.clearCallingIdentity();
777         try {
778             if (args.length > 0 && "--proto".equals(args[0])) {
779                 dumpProto(fd);
780             } else {
781                 dumpInner(pw, args);
782             }
783         } finally {
784             Binder.restoreCallingIdentity(ident);
785         }
786     }
787 
788     private void dumpInner(PrintWriter pw, String[] args) {
789         final long now = SystemClock.uptimeMillis();
790 
791         boolean isCheckin = false;
792         boolean isCompact = false;
793         boolean isCsv = false;
794         boolean currentOnly = false;
795         boolean dumpDetails = false;
796         boolean dumpFullDetails = false;
797         boolean dumpAll = false;
798         boolean quit = false;
799         int aggregateHours = 0;
800         int lastIndex = 0;
801         int maxNum = 2;
802         boolean activeOnly = false;
803         String reqPackage = null;
804         boolean csvSepScreenStats = false;
805         int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
806         boolean csvSepMemStats = false;
807         int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
808         boolean csvSepProcStats = true;
809         int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
810         int section = ProcessStats.REPORT_ALL;
811         if (args != null) {
812             for (int i=0; i<args.length; i++) {
813                 String arg = args[i];
814                 if ("--checkin".equals(arg)) {
815                     isCheckin = true;
816                 } else if ("-c".equals(arg)) {
817                     isCompact = true;
818                 } else if ("--csv".equals(arg)) {
819                     isCsv = true;
820                 } else if ("--csv-screen".equals(arg)) {
821                     i++;
822                     if (i >= args.length) {
823                         pw.println("Error: argument required for --csv-screen");
824                         dumpHelp(pw);
825                         return;
826                     }
827                     boolean[] sep = new boolean[1];
828                     String[] error = new String[1];
829                     csvScreenStats = parseStateList(DumpUtils.ADJ_SCREEN_NAMES_CSV,
830                             ProcessStats.ADJ_SCREEN_MOD, args[i], sep, error);
831                     if (csvScreenStats == null) {
832                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
833                         dumpHelp(pw);
834                         return;
835                     }
836                     csvSepScreenStats = sep[0];
837                 } else if ("--csv-mem".equals(arg)) {
838                     i++;
839                     if (i >= args.length) {
840                         pw.println("Error: argument required for --csv-mem");
841                         dumpHelp(pw);
842                         return;
843                     }
844                     boolean[] sep = new boolean[1];
845                     String[] error = new String[1];
846                     csvMemStats = parseStateList(DumpUtils.ADJ_MEM_NAMES_CSV, 1, args[i],
847                             sep, error);
848                     if (csvMemStats == null) {
849                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
850                         dumpHelp(pw);
851                         return;
852                     }
853                     csvSepMemStats = sep[0];
854                 } else if ("--csv-proc".equals(arg)) {
855                     i++;
856                     if (i >= args.length) {
857                         pw.println("Error: argument required for --csv-proc");
858                         dumpHelp(pw);
859                         return;
860                     }
861                     boolean[] sep = new boolean[1];
862                     String[] error = new String[1];
863                     csvProcStats = parseStateList(DumpUtils.STATE_NAMES_CSV, 1, args[i],
864                             sep, error);
865                     if (csvProcStats == null) {
866                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
867                         dumpHelp(pw);
868                         return;
869                     }
870                     csvSepProcStats = sep[0];
871                 } else if ("--details".equals(arg)) {
872                     dumpDetails = true;
873                 } else if ("--full-details".equals(arg)) {
874                     dumpFullDetails = true;
875                 } else if ("--hours".equals(arg)) {
876                     i++;
877                     if (i >= args.length) {
878                         pw.println("Error: argument required for --hours");
879                         dumpHelp(pw);
880                         return;
881                     }
882                     try {
883                         aggregateHours = Integer.parseInt(args[i]);
884                     } catch (NumberFormatException e) {
885                         pw.println("Error: --hours argument not an int -- " + args[i]);
886                         dumpHelp(pw);
887                         return;
888                     }
889                 } else if ("--last".equals(arg)) {
890                     i++;
891                     if (i >= args.length) {
892                         pw.println("Error: argument required for --last");
893                         dumpHelp(pw);
894                         return;
895                     }
896                     try {
897                         lastIndex = Integer.parseInt(args[i]);
898                     } catch (NumberFormatException e) {
899                         pw.println("Error: --last argument not an int -- " + args[i]);
900                         dumpHelp(pw);
901                         return;
902                     }
903                 } else if ("--max".equals(arg)) {
904                     i++;
905                     if (i >= args.length) {
906                         pw.println("Error: argument required for --max");
907                         dumpHelp(pw);
908                         return;
909                     }
910                     try {
911                         maxNum = Integer.parseInt(args[i]);
912                     } catch (NumberFormatException e) {
913                         pw.println("Error: --max argument not an int -- " + args[i]);
914                         dumpHelp(pw);
915                         return;
916                     }
917                 } else if ("--active".equals(arg)) {
918                     activeOnly = true;
919                     currentOnly = true;
920                 } else if ("--current".equals(arg)) {
921                     currentOnly = true;
922                 } else if ("--commit".equals(arg)) {
923                     synchronized (mAm) {
924                         mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
925                         writeStateLocked(true, true);
926                         pw.println("Process stats committed.");
927                         quit = true;
928                     }
929                 } else if ("--section".equals(arg)) {
930                     i++;
931                     if (i >= args.length) {
932                         pw.println("Error: argument required for --section");
933                         dumpHelp(pw);
934                         return;
935                     }
936                     section = parseSectionOptions(args[i]);
937                 } else if ("--clear".equals(arg)) {
938                     synchronized (mAm) {
939                         mProcessStats.resetSafely();
940                         mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
941                         ArrayList<String> files = getCommittedFiles(0, true, true);
942                         if (files != null) {
943                             for (int fi=0; fi<files.size(); fi++) {
944                                 (new File(files.get(fi))).delete();
945                             }
946                         }
947                         pw.println("All process stats cleared.");
948                         quit = true;
949                     }
950                 } else if ("--write".equals(arg)) {
951                     synchronized (mAm) {
952                         writeStateSyncLocked();
953                         pw.println("Process stats written.");
954                         quit = true;
955                     }
956                 } else if ("--read".equals(arg)) {
957                     synchronized (mAm) {
958                         readLocked(mProcessStats, mFile);
959                         pw.println("Process stats read.");
960                         quit = true;
961                     }
962                 } else if ("--start-testing".equals(arg)) {
963                     synchronized (mAm) {
964                         mAm.setTestPssMode(true);
965                         pw.println("Started high frequency sampling.");
966                         quit = true;
967                     }
968                 } else if ("--stop-testing".equals(arg)) {
969                     synchronized (mAm) {
970                         mAm.setTestPssMode(false);
971                         pw.println("Stopped high frequency sampling.");
972                         quit = true;
973                     }
974                 } else if ("--pretend-screen-on".equals(arg)) {
975                     synchronized (mAm) {
976                         mInjectedScreenState = true;
977                     }
978                     quit = true;
979                 } else if ("--pretend-screen-off".equals(arg)) {
980                     synchronized (mAm) {
981                         mInjectedScreenState = false;
982                     }
983                     quit = true;
984                 } else if ("--stop-pretend-screen".equals(arg)) {
985                     synchronized (mAm) {
986                         mInjectedScreenState = null;
987                     }
988                     quit = true;
989                 } else if ("-h".equals(arg)) {
990                     dumpHelp(pw);
991                     return;
992                 } else if ("-a".equals(arg)) {
993                     dumpDetails = true;
994                     dumpAll = true;
995                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
996                     pw.println("Unknown option: " + arg);
997                     dumpHelp(pw);
998                     return;
999                 } else {
1000                     // Not an option, last argument must be a package name.
1001                     reqPackage = arg;
1002                     // Include all details, since we know we are only going to
1003                     // be dumping a smaller set of data.  In fact only the details
1004                     // contain per-package data, so this is needed to be able
1005                     // to dump anything at all when filtering by package.
1006                     dumpDetails = true;
1007                 }
1008             }
1009         }
1010 
1011         if (quit) {
1012             return;
1013         }
1014 
1015         if (isCsv) {
1016             pw.print("Processes running summed over");
1017             if (!csvSepScreenStats) {
1018                 for (int i=0; i<csvScreenStats.length; i++) {
1019                     pw.print(" ");
1020                     DumpUtils.printScreenLabelCsv(pw, csvScreenStats[i]);
1021                 }
1022             }
1023             if (!csvSepMemStats) {
1024                 for (int i=0; i<csvMemStats.length; i++) {
1025                     pw.print(" ");
1026                     DumpUtils.printMemLabelCsv(pw, csvMemStats[i]);
1027                 }
1028             }
1029             if (!csvSepProcStats) {
1030                 for (int i=0; i<csvProcStats.length; i++) {
1031                     pw.print(" ");
1032                     pw.print(DumpUtils.STATE_NAMES_CSV[csvProcStats[i]]);
1033                 }
1034             }
1035             pw.println();
1036             synchronized (mAm) {
1037                 dumpFilteredProcessesCsvLocked(pw, null,
1038                         csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
1039                         csvSepProcStats, csvProcStats, now, reqPackage);
1040                 /*
1041                 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
1042                         false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
1043                         true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
1044                         true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
1045                                 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
1046                                 STATE_PREVIOUS, STATE_CACHED},
1047                         now, reqPackage);
1048                 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
1049                         false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
1050                         false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
1051                                 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
1052                         true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
1053                                 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
1054                                 STATE_PREVIOUS, STATE_CACHED},
1055                         now, reqPackage);
1056                 */
1057             }
1058             return;
1059         } else if (aggregateHours != 0) {
1060             pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
1061             dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
1062                     dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
1063             return;
1064         } else if (lastIndex > 0) {
1065             pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
1066             ArrayList<String> files = getCommittedFiles(0, false, true);
1067             if (lastIndex >= files.size()) {
1068                 pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
1069                 return;
1070             }
1071             AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
1072             ProcessStats processStats = new ProcessStats(false);
1073             readLocked(processStats, file);
1074             if (processStats.mReadError != null) {
1075                 if (isCheckin || isCompact) pw.print("err,");
1076                 pw.print("Failure reading "); pw.print(files.get(lastIndex));
1077                 pw.print("; "); pw.println(processStats.mReadError);
1078                 return;
1079             }
1080             String fileStr = file.getBaseFile().getPath();
1081             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
1082             if (isCheckin || isCompact) {
1083                 // Don't really need to lock because we uniquely own this object.
1084                 processStats.dumpCheckinLocked(pw, reqPackage, section);
1085             } else {
1086                 pw.print("COMMITTED STATS FROM ");
1087                 pw.print(processStats.mTimePeriodStartClockStr);
1088                 if (checkedIn) pw.print(" (checked in)");
1089                 pw.println(":");
1090                 if (dumpDetails || dumpFullDetails) {
1091                     processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
1092                             dumpAll, activeOnly, section);
1093                     if (dumpAll) {
1094                         pw.print("  mFile="); pw.println(mFile.getBaseFile());
1095                     }
1096                 } else {
1097                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
1098                 }
1099             }
1100             return;
1101         }
1102 
1103         boolean sepNeeded = false;
1104         if (dumpAll || isCheckin) {
1105             mWriteLock.lock();
1106             try {
1107                 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
1108                 if (files != null) {
1109                     int start = isCheckin ? 0 : (files.size() - maxNum);
1110                     if (start < 0) {
1111                         start = 0;
1112                     }
1113                     for (int i=start; i<files.size(); i++) {
1114                         if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
1115                         try {
1116                             AtomicFile file = new AtomicFile(new File(files.get(i)));
1117                             ProcessStats processStats = new ProcessStats(false);
1118                             readLocked(processStats, file);
1119                             if (processStats.mReadError != null) {
1120                                 if (isCheckin || isCompact) pw.print("err,");
1121                                 pw.print("Failure reading "); pw.print(files.get(i));
1122                                 pw.print("; "); pw.println(processStats.mReadError);
1123                                 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
1124                                 (new File(files.get(i))).delete();
1125                                 continue;
1126                             }
1127                             String fileStr = file.getBaseFile().getPath();
1128                             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
1129                             if (isCheckin || isCompact) {
1130                                 // Don't really need to lock because we uniquely own this object.
1131                                 processStats.dumpCheckinLocked(pw, reqPackage, section);
1132                             } else {
1133                                 if (sepNeeded) {
1134                                     pw.println();
1135                                 } else {
1136                                     sepNeeded = true;
1137                                 }
1138                                 pw.print("COMMITTED STATS FROM ");
1139                                 pw.print(processStats.mTimePeriodStartClockStr);
1140                                 if (checkedIn) pw.print(" (checked in)");
1141                                 pw.println(":");
1142                                 // Don't really need to lock because we uniquely own this object.
1143                                 // Always dump summary here, dumping all details is just too
1144                                 // much crud.
1145                                 if (dumpFullDetails) {
1146                                     processStats.dumpLocked(pw, reqPackage, now, false, false,
1147                                             false, activeOnly, section);
1148                                 } else {
1149                                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
1150                                 }
1151                             }
1152                             if (isCheckin) {
1153                                 // Rename file suffix to mark that it has checked in.
1154                                 file.getBaseFile().renameTo(new File(
1155                                         fileStr + STATE_FILE_CHECKIN_SUFFIX));
1156                             }
1157                         } catch (Throwable e) {
1158                             pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
1159                             e.printStackTrace(pw);
1160                         }
1161                     }
1162                 }
1163             } finally {
1164                 mWriteLock.unlock();
1165             }
1166         }
1167         if (!isCheckin) {
1168             synchronized (mAm) {
1169                 if (isCompact) {
1170                     mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
1171                 } else {
1172                     if (sepNeeded) {
1173                         pw.println();
1174                     }
1175                     pw.println("CURRENT STATS:");
1176                     if (dumpDetails || dumpFullDetails) {
1177                         mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
1178                                 dumpAll, activeOnly, section);
1179                         if (dumpAll) {
1180                             pw.print("  mFile="); pw.println(mFile.getBaseFile());
1181                         }
1182                     } else {
1183                         mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
1184                     }
1185                     sepNeeded = true;
1186                 }
1187             }
1188             if (!currentOnly) {
1189                 if (sepNeeded) {
1190                     pw.println();
1191                 }
1192                 pw.println("AGGREGATED OVER LAST 24 HOURS:");
1193                 dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
1194                         dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
1195                 pw.println();
1196                 pw.println("AGGREGATED OVER LAST 3 HOURS:");
1197                 dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
1198                         dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
1199             }
1200         }
1201     }
1202 
1203     private void dumpAggregatedStats(ProtoOutputStream proto, long fieldId, int aggregateHours, long now) {
1204         ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
1205                 - (ProcessStats.COMMIT_PERIOD/2));
1206         if (pfd == null) {
1207             return;
1208         }
1209         ProcessStats stats = new ProcessStats(false);
1210         InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
1211         stats.read(stream);
1212         if (stats.mReadError != null) {
1213             return;
1214         }
1215         final long token = proto.start(fieldId);
1216         stats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
1217         proto.end(token);
1218     }
1219 
1220     private void dumpProto(FileDescriptor fd) {
1221         final ProtoOutputStream proto = new ProtoOutputStream(fd);
1222 
1223         // dump current procstats
1224         long now;
1225         synchronized (mAm) {
1226             now = SystemClock.uptimeMillis();
1227             final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
1228             mProcessStats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
1229             proto.end(token);
1230         }
1231 
1232         // aggregated over last 3 hours procstats
1233         dumpAggregatedStats(proto, ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS, 3, now);
1234 
1235         // aggregated over last 24 hours procstats
1236         dumpAggregatedStats(proto, ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS, 24, now);
1237 
1238         proto.flush();
1239     }
1240 }
1241