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