Get ProcessStats callable from other processes.

Also fix a bug where, when parceling the stats, we were
computing the final duration values too late.  We need to
do that before we write the long table.

Change-Id: Idb6c1ed95417448c56973fe5866bfb3570e525f4
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4e6c3dc..c65f17e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,14 +16,13 @@
 
 package android.app;
 
-import android.R;
 import android.os.BatteryStats;
 import android.os.IBinder;
 import com.android.internal.app.IUsageStats;
+import com.android.internal.app.ProcessStats;
 import com.android.internal.os.PkgUsageStats;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.MemInfoReader;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,9 +34,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -52,7 +49,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -2253,7 +2249,7 @@
         PrintWriter pw = new FastPrintWriter(fout);
         dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName });
         pw.println();
-        dumpService(pw, fd, "procstats", new String[] { packageName });
+        dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName });
         pw.println();
         dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
         pw.println();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 12646bd..38ffb96 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
     private static final String BATTERY_LEVEL_DATA = "lv";
     private static final String WIFI_DATA = "wfl";
     private static final String MISC_DATA = "m";
+    private static final String HISTORY_DATA = "h";
     private static final String SCREEN_BRIGHTNESS_DATA = "br";
     private static final String SIGNAL_STRENGTH_TIME_DATA = "sgt";
     private static final String SIGNAL_SCANNING_TIME_DATA = "sst";
@@ -2390,7 +2391,7 @@
             while (getNextHistoryLocked(rec)) {
                 pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                 pw.print(0); pw.print(',');
-                pw.print("h"); pw.print(',');
+                pw.print(HISTORY_DATA); pw.print(',');
                 hprinter.printNextItemCheckin(pw, rec, now);
                 pw.println();
             }
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
new file mode 100644
index 0000000..6ff0304
--- /dev/null
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+public class ProcessMap<E> {
+    final ArrayMap<String, SparseArray<E>> mMap
+            = new ArrayMap<String, SparseArray<E>>();
+    
+    public E get(String name, int uid) {
+        SparseArray<E> uids = mMap.get(name);
+        if (uids == null) return null;
+        return uids.get(uid);
+    }
+    
+    public E put(String name, int uid, E value) {
+        SparseArray<E> uids = mMap.get(name);
+        if (uids == null) {
+            uids = new SparseArray<E>(2);
+            mMap.put(name, uids);
+        }
+        uids.put(uid, value);
+        return value;
+    }
+    
+    public void remove(String name, int uid) {
+        SparseArray<E> uids = mMap.get(name);
+        if (uids != null) {
+            uids.remove(uid);
+            if (uids.size() == 0) {
+                mMap.remove(name);
+            }
+        }
+    }
+    
+    public ArrayMap<String, SparseArray<E>> getMap() {
+        return mMap;
+    }
+}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
new file mode 100644
index 0000000..39c23cf
--- /dev/null
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -0,0 +1,2481 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.webkit.WebViewFactory;
+import com.android.internal.util.ArrayUtils;
+import dalvik.system.VMRuntime;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+
+public final class ProcessStats implements Parcelable {
+    static final String TAG = "ProcessStats";
+    static final boolean DEBUG = false;
+
+    public static final String SERVICE_NAME = "procstats";
+
+    public static final int STATE_NOTHING = -1;
+    public static final int STATE_PERSISTENT = 0;
+    public static final int STATE_TOP = 1;
+    public static final int STATE_IMPORTANT_FOREGROUND = 2;
+    public static final int STATE_IMPORTANT_BACKGROUND = 3;
+    public static final int STATE_BACKUP = 4;
+    public static final int STATE_HEAVY_WEIGHT = 5;
+    public static final int STATE_SERVICE = 6;
+    public static final int STATE_SERVICE_RESTARTING = 7;
+    public static final int STATE_RECEIVER = 8;
+    public static final int STATE_HOME = 9;
+    public static final int STATE_LAST_ACTIVITY = 10;
+    public static final int STATE_CACHED_ACTIVITY = 11;
+    public static final int STATE_CACHED_ACTIVITY_CLIENT = 12;
+    public static final int STATE_CACHED_EMPTY = 13;
+    public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
+
+    public static final int PSS_SAMPLE_COUNT = 0;
+    public static final int PSS_MINIMUM = 1;
+    public static final int PSS_AVERAGE = 2;
+    public static final int PSS_MAXIMUM = 3;
+    public static final int PSS_USS_MINIMUM = 4;
+    public static final int PSS_USS_AVERAGE = 5;
+    public static final int PSS_USS_MAXIMUM = 6;
+    public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
+
+    public static final int ADJ_NOTHING = -1;
+    public static final int ADJ_MEM_FACTOR_NORMAL = 0;
+    public static final int ADJ_MEM_FACTOR_MODERATE = 1;
+    public static final int ADJ_MEM_FACTOR_LOW = 2;
+    public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
+    public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
+    public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
+    public static final int ADJ_SCREEN_OFF = 0;
+    public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
+    public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
+
+    public static final int FLAG_COMPLETE = 1<<0;
+    public static final int FLAG_SHUTDOWN = 1<<1;
+    public static final int FLAG_SYSPROPS = 1<<2;
+
+    public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL,
+            ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+
+    public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+
+    public static final int[] NON_CACHED_PROC_STATES = new int[] {
+            STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+            STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
+            STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+    };
+
+    public static final int[] BACKGROUND_PROC_STATES = new int[] {
+            STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+            STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+    };
+
+    // Map from process states to the states we track.
+    static final int[] PROCESS_STATE_TO_STATE = new int[] {
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+            STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
+            STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+            STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+            STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
+            STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+            STATE_SERVICE,                  // ActivityManager.PROCESS_STATE_SERVICE
+            STATE_RECEIVER,                 // ActivityManager.PROCESS_STATE_RECEIVER
+            STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
+            STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+            STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+            STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+            STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
+            STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+            STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
+            STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
+            STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
+    };
+
+    static final String[] STATE_NAMES = new String[] {
+            "Persistent", "Top       ", "Imp Fg    ", "Imp Bg    ",
+            "Backup    ", "Heavy Wght", "Service   ", "Service Rs",
+            "Receiver  ", "Home      ",
+            "Last Act  ", "Cch Act   ", "Cch CliAct", "Cch Empty "
+    };
+
+    public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
+            "off", "on"
+    };
+
+    public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
+            "norm", "mod",  "low", "crit"
+    };
+
+    public static final String[] STATE_NAMES_CSV = new String[] {
+            "pers", "top", "impfg", "impbg", "backup", "heavy",
+            "service", "service-rs", "receiver", "home", "lastact",
+            "cch-activity", "cch-aclient", "cch-empty"
+    };
+
+    static final String[] ADJ_SCREEN_TAGS = new String[] {
+            "0", "1"
+    };
+
+    static final String[] ADJ_MEM_TAGS = new String[] {
+            "n", "m",  "l", "c"
+    };
+
+    static final String[] STATE_TAGS = new String[] {
+            "p", "t", "f", "b", "u", "w",
+            "s", "x", "r", "h", "l", "a", "c", "e"
+    };
+
+    static final String CSV_SEP = "\t";
+
+    // Current version of the parcel format.
+    private static final int PARCEL_VERSION = 9;
+    // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
+    private static final int MAGIC = 0x50535453;
+
+    // Where the "type"/"state" part of the data appears in an offset integer.
+    static int OFFSET_TYPE_SHIFT = 0;
+    static int OFFSET_TYPE_MASK = 0xff;
+    // Where the "which array" part of the data appears in an offset integer.
+    static int OFFSET_ARRAY_SHIFT = 8;
+    static int OFFSET_ARRAY_MASK = 0xff;
+    // Where the "index into array" part of the data appears in an offset integer.
+    static int OFFSET_INDEX_SHIFT = 16;
+    static int OFFSET_INDEX_MASK = 0xffff;
+
+    public String mReadError;
+    public String mTimePeriodStartClockStr;
+    public int mFlags;
+
+    public final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
+    public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+
+    public final long[] mMemFactorDurations = new long[ADJ_COUNT];
+    public int mMemFactor = STATE_NOTHING;
+    public long mStartTime;
+
+    public long mTimePeriodStartClock;
+    public long mTimePeriodStartRealtime;
+    public long mTimePeriodEndRealtime;
+    String mRuntime;
+    String mWebView;
+    boolean mRunning;
+
+    static final int LONGS_SIZE = 4096;
+    final ArrayList<long[]> mLongs = new ArrayList<long[]>();
+    int mNextLong;
+
+    int[] mAddLongTable;
+    int mAddLongTableSize;
+
+    public ProcessStats(boolean running) {
+        mRunning = running;
+        reset();
+    }
+
+    public ProcessStats(Parcel in) {
+        reset();
+        readFromParcel(in);
+    }
+
+    public static final Parcelable.Creator<ProcessStats> CREATOR
+            = new Parcelable.Creator<ProcessStats>() {
+        public ProcessStats createFromParcel(Parcel in) {
+            return new ProcessStats(in);
+        }
+
+        public ProcessStats[] newArray(int size) {
+            return new ProcessStats[size];
+        }
+    };
+
+    private static void printScreenLabel(PrintWriter pw, int offset) {
+        switch (offset) {
+            case ADJ_NOTHING:
+                pw.print("             ");
+                break;
+            case ADJ_SCREEN_OFF:
+                pw.print("Screen Off / ");
+                break;
+            case ADJ_SCREEN_ON:
+                pw.print("Screen On  / ");
+                break;
+            default:
+                pw.print("?????????? / ");
+                break;
+        }
+    }
+
+    public static void printScreenLabelCsv(PrintWriter pw, int offset) {
+        switch (offset) {
+            case ADJ_NOTHING:
+                break;
+            case ADJ_SCREEN_OFF:
+                pw.print(ADJ_SCREEN_NAMES_CSV[0]);
+                break;
+            case ADJ_SCREEN_ON:
+                pw.print(ADJ_SCREEN_NAMES_CSV[1]);
+                break;
+            default:
+                pw.print("???");
+                break;
+        }
+    }
+
+    private static void printMemLabel(PrintWriter pw, int offset) {
+        switch (offset) {
+            case ADJ_NOTHING:
+                pw.print("       ");
+                break;
+            case ADJ_MEM_FACTOR_NORMAL:
+                pw.print("Norm / ");
+                break;
+            case ADJ_MEM_FACTOR_MODERATE:
+                pw.print("Mod  / ");
+                break;
+            case ADJ_MEM_FACTOR_LOW:
+                pw.print("Low  / ");
+                break;
+            case ADJ_MEM_FACTOR_CRITICAL:
+                pw.print("Crit / ");
+                break;
+            default:
+                pw.print("???? / ");
+                break;
+        }
+    }
+
+    public static void printMemLabelCsv(PrintWriter pw, int offset) {
+        if (offset >= ADJ_MEM_FACTOR_NORMAL) {
+            if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
+                pw.print(ADJ_MEM_NAMES_CSV[offset]);
+            } else {
+                pw.print("???");
+            }
+        }
+    }
+
+    public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
+            int curState, long curStartTime, long now) {
+        long totalTime = 0;
+        int printedScreen = -1;
+        for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+            int printedMem = -1;
+            for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+                int state = imem+iscreen;
+                long time = durations[state];
+                String running = "";
+                if (curState == state) {
+                    time += now - curStartTime;
+                    if (pw != null) {
+                        running = " (running)";
+                    }
+                }
+                if (time != 0) {
+                    if (pw != null) {
+                        pw.print(prefix);
+                        printScreenLabel(pw, printedScreen != iscreen
+                                ? iscreen : STATE_NOTHING);
+                        printedScreen = iscreen;
+                        printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                        printedMem = imem;
+                        TimeUtils.formatDuration(time, pw); pw.println(running);
+                    }
+                    totalTime += time;
+                }
+            }
+        }
+        if (totalTime != 0 && pw != null) {
+            pw.print(prefix);
+            printScreenLabel(pw, STATE_NOTHING);
+            pw.print("TOTAL: ");
+            TimeUtils.formatDuration(totalTime, pw);
+            pw.println();
+        }
+        return totalTime;
+    }
+
+    static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
+            int curState, long curStartTime, long now) {
+        for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+            for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+                int state = imem+iscreen;
+                long time = durations[state];
+                if (curState == state) {
+                    time += now - curStartTime;
+                }
+                if (time != 0) {
+                    printAdjTagAndValue(pw, state, time);
+                }
+            }
+        }
+    }
+
+    static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName,
+            int uid, String serviceName, ServiceState svc, int serviceType, int opCount,
+            int curState, long curStartTime, long now) {
+        if (opCount <= 0) {
+            return;
+        }
+        pw.print(label);
+        pw.print(",");
+        pw.print(packageName);
+        pw.print(",");
+        pw.print(uid);
+        pw.print(",");
+        pw.print(serviceName);
+        pw.print(",");
+        pw.print(opCount);
+        boolean didCurState = false;
+        for (int i=0; i<svc.mDurationsTableSize; i++) {
+            int off = svc.mDurationsTable[i];
+            int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+            int memFactor = type / ServiceState.SERVICE_COUNT;
+            type %= ServiceState.SERVICE_COUNT;
+            if (type != serviceType) {
+                continue;
+            }
+            long time = svc.mStats.getLong(off, 0);
+            if (curState == memFactor) {
+                didCurState = true;
+                time += now - curStartTime;
+            }
+            printAdjTagAndValue(pw, memFactor, time);
+        }
+        if (!didCurState && curState != STATE_NOTHING) {
+            printAdjTagAndValue(pw, curState, now - curStartTime);
+        }
+        pw.println();
+    }
+
+    public static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
+        data.totalTime = 0;
+        data.numPss = data.minPss = data.avgPss = data.maxPss =
+                data.minUss = data.avgUss = data.maxUss = 0;
+        for (int is=0; is<data.screenStates.length; is++) {
+            for (int im=0; im<data.memStates.length; im++) {
+                for (int ip=0; ip<data.procStates.length; ip++) {
+                    int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
+                            + data.procStates[ip];
+                    data.totalTime += proc.getDuration(bucket, now);
+                    long samples = proc.getPssSampleCount(bucket);
+                    if (samples > 0) {
+                        long minPss = proc.getPssMinimum(bucket);
+                        long avgPss = proc.getPssAverage(bucket);
+                        long maxPss = proc.getPssMaximum(bucket);
+                        long minUss = proc.getPssUssMinimum(bucket);
+                        long avgUss = proc.getPssUssAverage(bucket);
+                        long maxUss = proc.getPssUssMaximum(bucket);
+                        if (data.numPss == 0) {
+                            data.minPss = minPss;
+                            data.avgPss = avgPss;
+                            data.maxPss = maxPss;
+                            data.minUss = minUss;
+                            data.avgUss = avgUss;
+                            data.maxUss = maxUss;
+                        } else {
+                            if (minPss < data.minPss) {
+                                data.minPss = minPss;
+                            }
+                            data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
+                                    + (avgPss*(double)samples)) / (data.numPss+samples) );
+                            if (maxPss > data.maxPss) {
+                                data.maxPss = maxPss;
+                            }
+                            if (minUss < data.minUss) {
+                                data.minUss = minUss;
+                            }
+                            data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
+                                    + (avgUss*(double)samples)) / (data.numPss+samples) );
+                            if (maxUss > data.maxUss) {
+                                data.maxUss = maxUss;
+                            }
+                        }
+                        data.numPss += samples;
+                    }
+                }
+            }
+        }
+    }
+
+    static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
+                int[] procStates, long now) {
+        long totalTime = 0;
+        /*
+        for (int i=0; i<proc.mDurationsTableSize; i++) {
+            int val = proc.mDurationsTable[i];
+            totalTime += proc.mState.getLong(val, 0);
+            if ((val&0xff) == proc.mCurState) {
+                totalTime += now - proc.mStartTime;
+            }
+        }
+        */
+        for (int is=0; is<screenStates.length; is++) {
+            for (int im=0; im<memStates.length; im++) {
+                for (int ip=0; ip<procStates.length; ip++) {
+                    int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
+                            + procStates[ip];
+                    totalTime += proc.getDuration(bucket, now);
+                }
+            }
+        }
+        proc.mTmpTotalTime = totalTime;
+        return totalTime;
+    }
+
+    static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
+            int[] screenStates, int[] memStates, int[] procStates, long now) {
+        long totalTime = 0;
+        int printedScreen = -1;
+        for (int is=0; is<screenStates.length; is++) {
+            int printedMem = -1;
+            for (int im=0; im<memStates.length; im++) {
+                for (int ip=0; ip<procStates.length; ip++) {
+                    final int iscreen = screenStates[is];
+                    final int imem = memStates[im];
+                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+                    long time = proc.getDuration(bucket, now);
+                    String running = "";
+                    if (proc.mCurState == bucket) {
+                        running = " (running)";
+                    }
+                    if (time != 0) {
+                        pw.print(prefix);
+                        if (screenStates.length > 1) {
+                            printScreenLabel(pw, printedScreen != iscreen
+                                    ? iscreen : STATE_NOTHING);
+                            printedScreen = iscreen;
+                        }
+                        if (memStates.length > 1) {
+                            printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                            printedMem = imem;
+                        }
+                        pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
+                        TimeUtils.formatDuration(time, pw); pw.println(running);
+                        totalTime += time;
+                    }
+                }
+            }
+        }
+        if (totalTime != 0) {
+            pw.print(prefix);
+            if (screenStates.length > 1) {
+                printScreenLabel(pw, STATE_NOTHING);
+            }
+            if (memStates.length > 1) {
+                printMemLabel(pw, STATE_NOTHING);
+            }
+            pw.print("TOTAL     : ");
+            TimeUtils.formatDuration(totalTime, pw);
+            pw.println();
+        }
+    }
+
+    static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
+            int[] memStates, int[] procStates) {
+        boolean printedHeader = false;
+        int printedScreen = -1;
+        for (int is=0; is<screenStates.length; is++) {
+            int printedMem = -1;
+            for (int im=0; im<memStates.length; im++) {
+                for (int ip=0; ip<procStates.length; ip++) {
+                    final int iscreen = screenStates[is];
+                    final int imem = memStates[im];
+                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+                    long count = proc.getPssSampleCount(bucket);
+                    if (count > 0) {
+                        if (!printedHeader) {
+                            pw.print(prefix);
+                            pw.print("PSS/USS (");
+                            pw.print(proc.mPssTableSize);
+                            pw.println(" entries):");
+                            printedHeader = true;
+                        }
+                        pw.print(prefix);
+                        pw.print("  ");
+                        if (screenStates.length > 1) {
+                            printScreenLabel(pw, printedScreen != iscreen
+                                    ? iscreen : STATE_NOTHING);
+                            printedScreen = iscreen;
+                        }
+                        if (memStates.length > 1) {
+                            printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                            printedMem = imem;
+                        }
+                        pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
+                        pw.print(count);
+                        pw.print(" samples ");
+                        printSizeValue(pw, proc.getPssMinimum(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
+                        pw.print(" / ");
+                        printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024);
+                        pw.println();
+                    }
+                }
+            }
+        }
+        if (proc.mNumExcessiveWake != 0) {
+            pw.print(prefix); pw.print("Killed for excessive wake locks: ");
+                    pw.print(proc.mNumExcessiveWake); pw.println(" times");
+        }
+        if (proc.mNumExcessiveCpu != 0) {
+            pw.print(prefix); pw.print("Killed for excessive CPU use: ");
+                    pw.print(proc.mNumExcessiveCpu); pw.println(" times");
+        }
+    }
+
+    static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
+            int[] memStates, int[] procStates) {
+        final int NS = screenStates != null ? screenStates.length : 1;
+        final int NM = memStates != null ? memStates.length : 1;
+        final int NP = procStates != null ? procStates.length : 1;
+        for (int is=0; is<NS; is++) {
+            for (int im=0; im<NM; im++) {
+                for (int ip=0; ip<NP; ip++) {
+                    pw.print(sep);
+                    boolean printed = false;
+                    if (screenStates != null && screenStates.length > 1) {
+                        printScreenLabelCsv(pw, screenStates[is]);
+                        printed = true;
+                    }
+                    if (memStates != null && memStates.length > 1) {
+                        if (printed) {
+                            pw.print("-");
+                        }
+                        printMemLabelCsv(pw, memStates[im]);
+                        printed = true;
+                    }
+                    if (procStates != null && procStates.length > 1) {
+                        if (printed) {
+                            pw.print("-");
+                        }
+                        pw.print(STATE_NAMES_CSV[procStates[ip]]);
+                    }
+                }
+            }
+        }
+    }
+
+    static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
+            boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+            boolean sepProcStates, int[] procStates, long now) {
+        final int NSS = sepScreenStates ? screenStates.length : 1;
+        final int NMS = sepMemStates ? memStates.length : 1;
+        final int NPS = sepProcStates ? procStates.length : 1;
+        for (int iss=0; iss<NSS; iss++) {
+            for (int ims=0; ims<NMS; ims++) {
+                for (int ips=0; ips<NPS; ips++) {
+                    final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
+                    final int vsmem = sepMemStates ? memStates[ims] : 0;
+                    final int vsproc = sepProcStates ? procStates[ips] : 0;
+                    final int NSA = sepScreenStates ? 1 : screenStates.length;
+                    final int NMA = sepMemStates ? 1 : memStates.length;
+                    final int NPA = sepProcStates ? 1 : procStates.length;
+                    long totalTime = 0;
+                    for (int isa=0; isa<NSA; isa++) {
+                        for (int ima=0; ima<NMA; ima++) {
+                            for (int ipa=0; ipa<NPA; ipa++) {
+                                final int vascreen = sepScreenStates ? 0 : screenStates[isa];
+                                final int vamem = sepMemStates ? 0 : memStates[ima];
+                                final int vaproc = sepProcStates ? 0 : procStates[ipa];
+                                final int bucket = ((vsscreen + vascreen + vsmem + vamem)
+                                        * STATE_COUNT) + vsproc + vaproc;
+                                totalTime += proc.getDuration(bucket, now);
+                            }
+                        }
+                    }
+                    pw.print(CSV_SEP);
+                    pw.print(totalTime);
+                }
+            }
+        }
+    }
+
+    static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+            int[] screenStates, int[] memStates, int[] procStates, long now) {
+        String innerPrefix = prefix + "  ";
+        for (int i=procs.size()-1; i>=0; i--) {
+            ProcessState proc = procs.get(i);
+            pw.print(prefix);
+            pw.print(proc.mName);
+            pw.print(" / ");
+            UserHandle.formatUid(pw, proc.mUid);
+            pw.print(" (");
+            pw.print(proc.mDurationsTableSize);
+            pw.print(" entries)");
+            pw.println(":");
+            dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
+            if (proc.mPssTableSize > 0) {
+                dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates);
+            }
+        }
+    }
+
+    static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix,
+            String label, int[] screenStates, int[] memStates, int[] procStates,
+            long now, long totalTime, boolean full) {
+        ProcessDataCollection totals = new ProcessDataCollection(screenStates,
+                memStates, procStates);
+        computeProcessData(proc, totals, now);
+        if (totals.totalTime != 0 || totals.numPss != 0) {
+            if (prefix != null) {
+                pw.print(prefix);
+            }
+            if (label != null) {
+                pw.print(label);
+            }
+            totals.print(pw, totalTime, full);
+            if (prefix != null) {
+                pw.println();
+            }
+        }
+    }
+
+    static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
+            ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
+            long now, long totalTime) {
+        for (int i=procs.size()-1; i>=0; i--) {
+            ProcessState proc = procs.get(i);
+            pw.print(prefix);
+            pw.print("* ");
+            pw.print(proc.mName);
+            pw.print(" / ");
+            UserHandle.formatUid(pw, proc.mUid);
+            pw.println(":");
+            dumpProcessSummaryDetails(pw, proc, prefix, "         TOTAL: ", screenStates, memStates,
+                    procStates, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "    Persistent: ", screenStates, memStates,
+                    new int[] { STATE_PERSISTENT }, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "           Top: ", screenStates, memStates,
+                    new int[] {STATE_TOP}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Fg: ", screenStates, memStates,
+                    new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Bg: ", screenStates, memStates,
+                    new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Backup: ", screenStates, memStates,
+                    new int[] {STATE_BACKUP}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "     Heavy Wgt: ", screenStates, memStates,
+                    new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "       Service: ", screenStates, memStates,
+                    new int[] {STATE_SERVICE}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "    Service Rs: ", screenStates, memStates,
+                    new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      Receiver: ", screenStates, memStates,
+                    new int[] {STATE_RECEIVER}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "          Home: ", screenStates, memStates,
+                    new int[] {STATE_HOME}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "    (Last Act): ", screenStates, memStates,
+                    new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      (Cached): ", screenStates, memStates,
+                    new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
+                            STATE_CACHED_EMPTY}, now, totalTime, true);
+        }
+    }
+
+    static void printPercent(PrintWriter pw, double fraction) {
+        fraction *= 100;
+        if (fraction < 1) {
+            pw.print(String.format("%.2f", fraction));
+        } else if (fraction < 10) {
+            pw.print(String.format("%.1f", fraction));
+        } else {
+            pw.print(String.format("%.0f", fraction));
+        }
+        pw.print("%");
+    }
+
+    static void printSizeValue(PrintWriter pw, long number) {
+        float result = number;
+        String suffix = "";
+        if (result > 900) {
+            suffix = "KB";
+            result = result / 1024;
+        }
+        if (result > 900) {
+            suffix = "MB";
+            result = result / 1024;
+        }
+        if (result > 900) {
+            suffix = "GB";
+            result = result / 1024;
+        }
+        if (result > 900) {
+            suffix = "TB";
+            result = result / 1024;
+        }
+        if (result > 900) {
+            suffix = "PB";
+            result = result / 1024;
+        }
+        String value;
+        if (result < 1) {
+            value = String.format("%.2f", result);
+        } else if (result < 10) {
+            value = String.format("%.1f", result);
+        } else if (result < 100) {
+            value = String.format("%.0f", result);
+        } else {
+            value = String.format("%.0f", result);
+        }
+        pw.print(value);
+        pw.print(suffix);
+    }
+
+    public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
+            boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+            boolean sepProcStates, int[] procStates, long now) {
+        pw.print("process");
+        pw.print(CSV_SEP);
+        pw.print("uid");
+        dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
+                sepMemStates ? memStates : null,
+                sepProcStates ? procStates : null);
+        pw.println();
+        for (int i=procs.size()-1; i>=0; i--) {
+            ProcessState proc = procs.get(i);
+            pw.print(proc.mName);
+            pw.print(CSV_SEP);
+            UserHandle.formatUid(pw, proc.mUid);
+            dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates,
+                    sepMemStates, memStates, sepProcStates, procStates, now);
+            pw.println();
+        }
+    }
+
+    static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
+        int index = value/mod;
+        if (index >= 0 && index < array.length) {
+            pw.print(array[index]);
+        } else {
+            pw.print('?');
+        }
+        return value - index*mod;
+    }
+
+    static void printProcStateTag(PrintWriter pw, int state) {
+        state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD*STATE_COUNT);
+        state = printArrayEntry(pw, ADJ_MEM_TAGS,  state, STATE_COUNT);
+        printArrayEntry(pw, STATE_TAGS,  state, 1);
+    }
+
+    static void printAdjTag(PrintWriter pw, int state) {
+        state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD);
+        printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
+    }
+
+    static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
+        pw.print(',');
+        printProcStateTag(pw, state);
+        pw.print(':');
+        pw.print(value);
+    }
+
+    static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
+        pw.print(',');
+        printAdjTag(pw, state);
+        pw.print(':');
+        pw.print(value);
+    }
+
+    static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
+        boolean didCurState = false;
+        for (int i=0; i<proc.mDurationsTableSize; i++) {
+            int off = proc.mDurationsTable[i];
+            int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+            long time = proc.mStats.getLong(off, 0);
+            if (proc.mCurState == type) {
+                didCurState = true;
+                time += now - proc.mStartTime;
+            }
+            printProcStateTagAndValue(pw, type, time);
+        }
+        if (!didCurState && proc.mCurState != STATE_NOTHING) {
+            printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime);
+        }
+    }
+
+    static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) {
+        for (int i=0; i<proc.mPssTableSize; i++) {
+            int off = proc.mPssTable[i];
+            int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+            long count = proc.mStats.getLong(off, PSS_SAMPLE_COUNT);
+            long min = proc.mStats.getLong(off, PSS_MINIMUM);
+            long avg = proc.mStats.getLong(off, PSS_AVERAGE);
+            long max = proc.mStats.getLong(off, PSS_MAXIMUM);
+            long umin = proc.mStats.getLong(off, PSS_USS_MINIMUM);
+            long uavg = proc.mStats.getLong(off, PSS_USS_AVERAGE);
+            long umax = proc.mStats.getLong(off, PSS_USS_MAXIMUM);
+            pw.print(',');
+            printProcStateTag(pw, type);
+            pw.print(':');
+            pw.print(count);
+            pw.print(':');
+            pw.print(min);
+            pw.print(':');
+            pw.print(avg);
+            pw.print(':');
+            pw.print(max);
+            pw.print(':');
+            pw.print(umin);
+            pw.print(':');
+            pw.print(uavg);
+            pw.print(':');
+            pw.print(umax);
+        }
+    }
+
+    public void reset() {
+        if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr);
+        resetCommon();
+        mPackages.getMap().clear();
+        mProcesses.getMap().clear();
+        mMemFactor = STATE_NOTHING;
+        mStartTime = 0;
+        if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
+    }
+
+    public void resetSafely() {
+        if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr);
+        resetCommon();
+        long now = SystemClock.uptimeMillis();
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int ip=procMap.size()-1; ip>=0; ip--) {
+            SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            for (int iu=uids.size()-1; iu>=0; iu--) {
+                uids.valueAt(iu).resetSafely(now);
+            }
+        }
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        for (int ip=pkgMap.size()-1; ip>=0; ip--) {
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            for (int iu=uids.size()-1; iu>=0; iu--) {
+                PackageState pkgState = uids.valueAt(iu);
+                for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
+                    pkgState.mProcesses.valueAt(iproc).resetSafely(now);
+                }
+                for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
+                    ServiceState ss = pkgState.mServices.valueAt(isvc);
+                    if (ss.isActive()) {
+                        pkgState.mServices.valueAt(isvc).resetSafely(now);
+                    } else {
+                        pkgState.mServices.removeAt(isvc);
+                    }
+                }
+            }
+        }
+        mStartTime = SystemClock.uptimeMillis();
+        if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
+    }
+
+    private void resetCommon() {
+        mTimePeriodStartClock = System.currentTimeMillis();
+        buildTimePeriodStartClockStr();
+        mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+        mLongs.clear();
+        mLongs.add(new long[LONGS_SIZE]);
+        mNextLong = 0;
+        Arrays.fill(mMemFactorDurations, 0);
+        mStartTime = 0;
+        mReadError = null;
+        mFlags = 0;
+        evaluateSystemProperties(true);
+    }
+
+    public boolean evaluateSystemProperties(boolean update) {
+        boolean changed = false;
+        String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib",
+                VMRuntime.getRuntime().vmLibrary());
+        if (!Objects.equals(runtime, mRuntime)) {
+            changed = true;
+            if (update) {
+                mRuntime = runtime;
+            }
+        }
+        String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
+        if (!Objects.equals(webview, mWebView)) {
+            changed = true;
+            if (update) {
+                mWebView = webview;
+            }
+        }
+        return changed;
+    }
+
+    private void buildTimePeriodStartClockStr() {
+        mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                mTimePeriodStartClock).toString();
+    }
+
+    static final int[] BAD_TABLE = new int[0];
+
+    private int[] readTableFromParcel(Parcel in, String name, String what) {
+        final int size = in.readInt();
+        if (size < 0) {
+            Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
+            return BAD_TABLE;
+        }
+        if (size == 0) {
+            return null;
+        }
+        final int[] table = new int[size];
+        for (int i=0; i<size; i++) {
+            table[i] = in.readInt();
+            if (DEBUG) Slog.i(TAG, "Reading in " + name + " table #" + i + ": "
+                    + ProcessStats.printLongOffset(table[i]));
+            if (!validateLongOffset(table[i])) {
+                Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
+                        + ProcessStats.printLongOffset(table[i]));
+                return null;
+            }
+        }
+        return table;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        long now = SystemClock.uptimeMillis();
+        out.writeInt(MAGIC);
+        out.writeInt(PARCEL_VERSION);
+        out.writeInt(STATE_COUNT);
+        out.writeInt(ADJ_COUNT);
+        out.writeInt(PSS_COUNT);
+        out.writeInt(LONGS_SIZE);
+
+        // First commit all running times.
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        final int NPROC = procMap.size();
+        for (int ip=0; ip<NPROC; ip++) {
+            SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            final int NUID = uids.size();
+            for (int iu=0; iu<NUID; iu++) {
+                uids.valueAt(iu).commitStateTime(now);
+            }
+        }
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        final int NPKG = pkgMap.size();
+        for (int ip=0; ip<NPKG; ip++) {
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            final int NUID = uids.size();
+            for (int iu=0; iu<NUID; iu++) {
+                PackageState pkgState = uids.valueAt(iu);
+                final int NPROCS = pkgState.mProcesses.size();
+                for (int iproc=0; iproc<NPROCS; iproc++) {
+                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                    if (proc.mCommonProcess != proc) {
+                        proc.commitStateTime(now);
+                    }
+                }
+                final int NSRVS = pkgState.mServices.size();
+                for (int isvc=0; isvc<NSRVS; isvc++) {
+                    pkgState.mServices.valueAt(isvc).commitStateTime(now);
+                }
+            }
+        }
+
+        out.writeLong(mTimePeriodStartClock);
+        out.writeLong(mTimePeriodStartRealtime);
+        out.writeLong(mTimePeriodEndRealtime);
+        out.writeString(mRuntime);
+        out.writeString(mWebView);
+        out.writeInt(mFlags);
+
+        out.writeInt(mLongs.size());
+        out.writeInt(mNextLong);
+        for (int i=0; i<(mLongs.size()-1); i++) {
+            out.writeLongArray(mLongs.get(i));
+        }
+        long[] lastLongs = mLongs.get(mLongs.size() - 1);
+        for (int i=0; i<mNextLong; i++) {
+            out.writeLong(lastLongs[i]);
+            if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]);
+        }
+
+        if (mMemFactor != STATE_NOTHING) {
+            mMemFactorDurations[mMemFactor] += now - mStartTime;
+            mStartTime = now;
+        }
+        out.writeLongArray(mMemFactorDurations);
+
+        out.writeInt(NPROC);
+        for (int ip=0; ip<NPROC; ip++) {
+            out.writeString(procMap.keyAt(ip));
+            SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            final int NUID = uids.size();
+            out.writeInt(NUID);
+            for (int iu=0; iu<NUID; iu++) {
+                out.writeInt(uids.keyAt(iu));
+                ProcessState proc = uids.valueAt(iu);
+                out.writeString(proc.mPackage);
+                proc.writeToParcel(out, now);
+            }
+        }
+        out.writeInt(NPKG);
+        for (int ip=0; ip<NPKG; ip++) {
+            out.writeString(pkgMap.keyAt(ip));
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            final int NUID = uids.size();
+            out.writeInt(NUID);
+            for (int iu=0; iu<NUID; iu++) {
+                out.writeInt(uids.keyAt(iu));
+                PackageState pkgState = uids.valueAt(iu);
+                final int NPROCS = pkgState.mProcesses.size();
+                out.writeInt(NPROCS);
+                for (int iproc=0; iproc<NPROCS; iproc++) {
+                    out.writeString(pkgState.mProcesses.keyAt(iproc));
+                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                    if (proc.mCommonProcess == proc) {
+                        // This is the same as the common process we wrote above.
+                        out.writeInt(0);
+                    } else {
+                        // There is separate data for this package's process.
+                        out.writeInt(1);
+                        proc.writeToParcel(out, now);
+                    }
+                }
+                final int NSRVS = pkgState.mServices.size();
+                out.writeInt(NSRVS);
+                for (int isvc=0; isvc<NSRVS; isvc++) {
+                    out.writeString(pkgState.mServices.keyAt(isvc));
+                    ServiceState svc = pkgState.mServices.valueAt(isvc);
+                    svc.writeToParcel(out, now);
+                }
+            }
+        }
+    }
+
+    private boolean readCheckedInt(Parcel in, int val, String what) {
+        int got;
+        if ((got=in.readInt()) != val) {
+            mReadError = "bad " + what + ": " + got;
+            return false;
+        }
+        return true;
+    }
+
+    public void readFromParcel(Parcel in) {
+        final boolean hadData = mPackages.getMap().size() > 0
+                || mProcesses.getMap().size() > 0;
+        if (hadData) {
+            resetSafely();
+        }
+
+        if (!readCheckedInt(in, MAGIC, "magic number")) {
+            return;
+        }
+        int version = in.readInt();
+        if (version != PARCEL_VERSION && version != 6) {
+            mReadError = "bad version: " + version;
+            return;
+        }
+        if (!readCheckedInt(in, STATE_COUNT, "state count")) {
+            return;
+        }
+        if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
+            return;
+        }
+        if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
+            return;
+        }
+        if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
+            return;
+        }
+
+        mTimePeriodStartClock = in.readLong();
+        buildTimePeriodStartClockStr();
+        mTimePeriodStartRealtime = in.readLong();
+        mTimePeriodEndRealtime = in.readLong();
+        if (version ==  PARCEL_VERSION) {
+            mRuntime = in.readString();
+            mWebView = in.readString();
+        }
+        mFlags = in.readInt();
+
+        final int NLONGS = in.readInt();
+        final int NEXTLONG = in.readInt();
+        mLongs.clear();
+        for (int i=0; i<(NLONGS-1); i++) {
+            while (i >= mLongs.size()) {
+                mLongs.add(new long[LONGS_SIZE]);
+            }
+            in.readLongArray(mLongs.get(i));
+        }
+        long[] longs = new long[LONGS_SIZE];
+        mNextLong = NEXTLONG;
+        for (int i=0; i<NEXTLONG; i++) {
+            longs[i] = in.readLong();
+            if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]);
+        }
+        mLongs.add(longs);
+
+        in.readLongArray(mMemFactorDurations);
+
+        int NPROC = in.readInt();
+        if (NPROC < 0) {
+            mReadError = "bad process count: " + NPROC;
+            return;
+        }
+        while (NPROC > 0) {
+            NPROC--;
+            String procName = in.readString();
+            if (procName == null) {
+                mReadError = "bad process name";
+                return;
+            }
+            int NUID = in.readInt();
+            if (NUID < 0) {
+                mReadError = "bad uid count: " + NUID;
+                return;
+            }
+            while (NUID > 0) {
+                NUID--;
+                int uid = in.readInt();
+                if (uid < 0) {
+                    mReadError = "bad uid: " + uid;
+                    return;
+                }
+                String pkgName = in.readString();
+                if (pkgName == null) {
+                    mReadError = "bad process package name";
+                    return;
+                }
+                ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
+                if (proc != null) {
+                    if (!proc.readFromParcel(in, false)) {
+                        return;
+                    }
+                } else {
+                    proc = new ProcessState(this, pkgName, uid, procName);
+                    if (!proc.readFromParcel(in, true)) {
+                        return;
+                    }
+                }
+                if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc);
+                mProcesses.put(procName, uid, proc);
+            }
+        }
+
+        if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
+
+        int NPKG = in.readInt();
+        if (NPKG < 0) {
+            mReadError = "bad package count: " + NPKG;
+            return;
+        }
+        while (NPKG > 0) {
+            NPKG--;
+            String pkgName = in.readString();
+            if (pkgName == null) {
+                mReadError = "bad package name";
+                return;
+            }
+            int NUID = in.readInt();
+            if (NUID < 0) {
+                mReadError = "bad uid count: " + NUID;
+                return;
+            }
+            while (NUID > 0) {
+                NUID--;
+                int uid = in.readInt();
+                if (uid < 0) {
+                    mReadError = "bad uid: " + uid;
+                    return;
+                }
+                PackageState pkgState = new PackageState(uid);
+                mPackages.put(pkgName, uid, pkgState);
+                int NPROCS = in.readInt();
+                if (NPROCS < 0) {
+                    mReadError = "bad package process count: " + NPROCS;
+                    return;
+                }
+                while (NPROCS > 0) {
+                    NPROCS--;
+                    String procName = in.readString();
+                    if (procName == null) {
+                        mReadError = "bad package process name";
+                        return;
+                    }
+                    int hasProc = in.readInt();
+                    if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid
+                            + " process " + procName + " hasProc=" + hasProc);
+                    ProcessState commonProc = mProcesses.get(procName, uid);
+                    if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid
+                            + ": " + commonProc);
+                    if (commonProc == null) {
+                        mReadError = "no common proc: " + procName;
+                        return;
+                    }
+                    if (hasProc != 0) {
+                        // The process for this package is unique to the package; we
+                        // need to load it.  We don't need to do anything about it if
+                        // it is not unique because if someone later looks for it
+                        // they will find and use it from the global procs.
+                        ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
+                        if (proc != null) {
+                            if (!proc.readFromParcel(in, false)) {
+                                return;
+                            }
+                        } else {
+                            proc = new ProcessState(commonProc, pkgName, uid, procName, 0);
+                            if (!proc.readFromParcel(in, true)) {
+                                return;
+                            }
+                        }
+                        if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+                                + procName + " " + uid + " " + proc);
+                        pkgState.mProcesses.put(procName, proc);
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+                                + procName + " " + uid + " " + commonProc);
+                        pkgState.mProcesses.put(procName, commonProc);
+                    }
+                }
+                int NSRVS = in.readInt();
+                if (NSRVS < 0) {
+                    mReadError = "bad package service count: " + NSRVS;
+                    return;
+                }
+                while (NSRVS > 0) {
+                    NSRVS--;
+                    String serviceName = in.readString();
+                    if (serviceName == null) {
+                        mReadError = "bad package service name";
+                        return;
+                    }
+                    ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
+                    if (serv == null) {
+                        serv = new ServiceState(this, pkgName, null);
+                    }
+                    if (!serv.readFromParcel(in)) {
+                        return;
+                    }
+                    if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: "
+                            + serviceName + " " + uid + " " + serv);
+                    pkgState.mServices.put(serviceName, serv);
+                }
+            }
+        }
+
+        if (DEBUG) Slog.d(TAG, "Successfully read procstats!");
+    }
+
+    int addLongData(int index, int type, int num) {
+        int tableLen = mAddLongTable != null ? mAddLongTable.length : 0;
+        if (mAddLongTableSize >= tableLen) {
+            int newSize = ArrayUtils.idealIntArraySize(tableLen + 1);
+            int[] newTable = new int[newSize];
+            if (tableLen > 0) {
+                System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen);
+            }
+            mAddLongTable = newTable;
+        }
+        if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) {
+            System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1,
+                    mAddLongTableSize - index);
+        }
+        int off = allocLongData(num);
+        mAddLongTable[index] = type | off;
+        mAddLongTableSize++;
+        return off;
+    }
+
+    int allocLongData(int num) {
+        int whichLongs = mLongs.size()-1;
+        long[] longs = mLongs.get(whichLongs);
+        if (mNextLong + num > longs.length) {
+            longs = new long[LONGS_SIZE];
+            mLongs.add(longs);
+            whichLongs++;
+            mNextLong = 0;
+        }
+        int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT);
+        mNextLong += num;
+        return off;
+    }
+
+    boolean validateLongOffset(int off) {
+        int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK;
+        if (arr >= mLongs.size()) {
+            return false;
+        }
+        int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+        if (idx >= LONGS_SIZE) {
+            return false;
+        }
+        if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off)
+                + ": " + getLong(off, 0));
+        return true;
+    }
+
+    static String printLongOffset(int off) {
+        StringBuilder sb = new StringBuilder(16);
+        sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+        sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK);
+        sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK);
+        return sb.toString();
+    }
+
+    void setLong(int off, int index, long value) {
+        long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+        longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value;
+    }
+
+    long getLong(int off, int index) {
+        long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+        return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)];
+    }
+
+    static int binarySearch(int[] array, int size, int value) {
+        int lo = 0;
+        int hi = size - 1;
+
+        while (lo <= hi) {
+            int mid = (lo + hi) >>> 1;
+            int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK;
+
+            if (midVal < value) {
+                lo = mid + 1;
+            } else if (midVal > value) {
+                hi = mid - 1;
+            } else {
+                return mid;  // value found
+            }
+        }
+        return ~lo;  // value not present
+    }
+
+    public PackageState getPackageStateLocked(String packageName, int uid) {
+        PackageState as = mPackages.get(packageName, uid);
+        if (as != null) {
+            return as;
+        }
+        as = new PackageState(uid);
+        mPackages.put(packageName, uid, as);
+        return as;
+    }
+
+    public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
+        final PackageState pkgState = getPackageStateLocked(packageName, uid);
+        ProcessState ps = pkgState.mProcesses.get(processName);
+        if (ps != null) {
+            return ps;
+        }
+        ProcessState commonProc = mProcesses.get(processName, uid);
+        if (commonProc == null) {
+            commonProc = new ProcessState(this, packageName, uid, processName);
+            mProcesses.put(processName, uid, commonProc);
+        }
+        if (!commonProc.mMultiPackage) {
+            if (packageName.equals(commonProc.mPackage)) {
+                // This common process is not in use by multiple packages, and
+                // is for the calling package, so we can just use it directly.
+                ps = commonProc;
+            } else {
+                // This common process has not been in use by multiple packages,
+                // but it was created for a different package than the caller.
+                // We need to convert it to a multi-package process.
+                commonProc.mMultiPackage = true;
+                // The original package it was created for now needs to point
+                // to its own copy.
+                long now = SystemClock.uptimeMillis();
+                pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
+                        commonProc.mPackage, now));
+                ps = new ProcessState(commonProc, packageName, uid, processName, now);
+            }
+        } else {
+            // The common process is for multiple packages, we need to create a
+            // separate object for the per-package data.
+            ps = new ProcessState(commonProc, packageName, uid, processName,
+                    SystemClock.uptimeMillis());
+        }
+        pkgState.mProcesses.put(processName, ps);
+        return ps;
+    }
+
+    public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) {
+        long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
+                mStartTime, now);
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        boolean printedHeader = false;
+        for (int ip=0; ip<pkgMap.size(); ip++) {
+            String pkgName = pkgMap.keyAt(ip);
+            if (reqPackage != null && !reqPackage.equals(pkgName)) {
+                continue;
+            }
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            for (int iu=0; iu<uids.size(); iu++) {
+                int uid = uids.keyAt(iu);
+                PackageState pkgState = uids.valueAt(iu);
+                final int NPROCS = pkgState.mProcesses.size();
+                final int NSRVS = pkgState.mServices.size();
+                if (NPROCS > 0 || NSRVS > 0) {
+                    if (!printedHeader) {
+                        pw.println("Per-Package Process Stats:");
+                        printedHeader = true;
+                    }
+                    pw.print("  * "); pw.print(pkgName); pw.print(" / ");
+                            UserHandle.formatUid(pw, uid); pw.println(":");
+                }
+                if (dumpAll) {
+                    for (int iproc=0; iproc<NPROCS; iproc++) {
+                        ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                        pw.print("      Process ");
+                        pw.print(pkgState.mProcesses.keyAt(iproc));
+                        pw.print(" (");
+                        pw.print(proc.mDurationsTableSize);
+                        pw.print(" entries)");
+                        pw.println(":");
+                        dumpProcessState(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                                ALL_PROC_STATES, now);
+                        dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                                ALL_PROC_STATES);
+                        if (dumpAll) {
+                            pw.print("        mNumStartedServices=");
+                                    pw.println(proc.mNumStartedServices);
+                        }
+                    }
+                } else {
+                    ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
+                    for (int iproc=0; iproc<NPROCS; iproc++) {
+                        procs.add(pkgState.mProcesses.valueAt(iproc));
+                    }
+                    dumpProcessSummaryLocked(pw, "      ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                            NON_CACHED_PROC_STATES, now, totalTime);
+                }
+                for (int isvc=0; isvc<NSRVS; isvc++) {
+                    if (dumpAll) {
+                        pw.print("      Service ");
+                    } else {
+                        pw.print("      * ");
+                    }
+                    pw.print(pkgState.mServices.keyAt(isvc));
+                    pw.println(":");
+                    ServiceState svc = pkgState.mServices.valueAt(isvc);
+                    dumpServiceStats(pw, "        ", "          ", "    ", "Started", svc,
+                            svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
+                            svc.mStartedStartTime, now, totalTime, dumpAll);
+                    dumpServiceStats(pw, "        ", "          ", "      ", "Bound", svc,
+                            svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState,
+                            svc.mBoundStartTime, now, totalTime, dumpAll);
+                    dumpServiceStats(pw, "        ", "          ", "  ", "Executing", svc,
+                            svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
+                            svc.mExecStartTime, now, totalTime, dumpAll);
+                }
+            }
+        }
+
+        if (reqPackage == null) {
+            ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+            printedHeader = false;
+            for (int ip=0; ip<procMap.size(); ip++) {
+                String procName = procMap.keyAt(ip);
+                SparseArray<ProcessState> uids = procMap.valueAt(ip);
+                for (int iu=0; iu<uids.size(); iu++) {
+                    int uid = uids.keyAt(iu);
+                    ProcessState proc = uids.valueAt(iu);
+                    if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
+                            && proc.mPssTableSize == 0) {
+                        continue;
+                    }
+                    if (!printedHeader) {
+                        pw.println("Process Stats:");
+                        printedHeader = true;
+                    }
+                    pw.print("  * "); pw.print(procName); pw.print(" / ");
+                            UserHandle.formatUid(pw, uid);
+                            pw.print(" ("); pw.print(proc.mDurationsTableSize);
+                            pw.print(" entries)"); pw.println(":");
+                    dumpProcessState(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                            ALL_PROC_STATES, now);
+                    dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                            ALL_PROC_STATES);
+                }
+            }
+
+            pw.println();
+            pw.println("Summary:");
+            dumpSummaryLocked(pw, reqPackage, now);
+        } else {
+            pw.println();
+            dumpTotalsLocked(pw, now);
+        }
+
+        if (dumpAll) {
+            pw.println();
+            pw.println("Internal state:");
+            pw.print("  Num long arrays: "); pw.println(mLongs.size());
+            pw.print("  Next long entry: "); pw.println(mNextLong);
+            pw.print("  mRunning="); pw.println(mRunning);
+        }
+    }
+
+    static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service,
+            int serviceType, int curState, long curStartTime, long now) {
+        long totalTime = 0;
+        int printedScreen = -1;
+        for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+            int printedMem = -1;
+            for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+                int state = imem+iscreen;
+                long time = service.getDuration(serviceType, curState, curStartTime,
+                        state, now);
+                String running = "";
+                if (curState == state && pw != null) {
+                    running = " (running)";
+                }
+                if (time != 0) {
+                    if (pw != null) {
+                        pw.print(prefix);
+                        printScreenLabel(pw, printedScreen != iscreen
+                                ? iscreen : STATE_NOTHING);
+                        printedScreen = iscreen;
+                        printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                        printedMem = imem;
+                        TimeUtils.formatDuration(time, pw); pw.println(running);
+                    }
+                    totalTime += time;
+                }
+            }
+        }
+        if (totalTime != 0 && pw != null) {
+            pw.print(prefix);
+            printScreenLabel(pw, STATE_NOTHING);
+            pw.print("TOTAL: ");
+            TimeUtils.formatDuration(totalTime, pw);
+            pw.println();
+        }
+        return totalTime;
+    }
+
+    void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner,
+            String headerPrefix, String header, ServiceState service,
+            int count, int serviceType, int state, long startTime, long now, long totalTime,
+            boolean dumpAll) {
+        if (count != 0) {
+            if (dumpAll) {
+                pw.print(prefix); pw.print(header);
+                pw.print(" op count "); pw.print(count); pw.println(":");
+                dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime,
+                        now);
+            } else {
+                long myTime = dumpSingleServiceTime(null, null, service, serviceType, state,
+                        startTime, now);
+                pw.print(prefix); pw.print(headerPrefix); pw.print(header);
+                pw.print(" count "); pw.print(count);
+                pw.print(" / time ");
+                printPercent(pw, (double)myTime/(double)totalTime);
+                pw.println();
+            }
+        }
+    }
+
+    public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) {
+        long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
+                mStartTime, now);
+        dumpFilteredSummaryLocked(pw, null, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                NON_CACHED_PROC_STATES, now, totalTime, reqPackage);
+        pw.println();
+        dumpTotalsLocked(pw, now);
+    }
+
+    void dumpTotalsLocked(PrintWriter pw, long now) {
+        pw.println("Run time Stats:");
+        dumpSingleTime(pw, "  ", mMemFactorDurations, mMemFactor, mStartTime, now);
+        pw.println();
+        pw.print("          Start time: ");
+        pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+        pw.println();
+        pw.print("  Total elapsed time: ");
+        TimeUtils.formatDuration(
+                (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+                        - mTimePeriodStartRealtime, pw);
+        boolean partial = true;
+        if ((mFlags&FLAG_SHUTDOWN) != 0) {
+            pw.print(" (shutdown)");
+            partial = false;
+        }
+        if ((mFlags&FLAG_SYSPROPS) != 0) {
+            pw.print(" (sysprops)");
+            partial = false;
+        }
+        if ((mFlags&FLAG_COMPLETE) != 0) {
+            pw.print(" (complete)");
+            partial = false;
+        }
+        if (partial) {
+            pw.print(" (partial)");
+        }
+        pw.print(' ');
+        pw.print(mRuntime);
+        pw.print(' ');
+        pw.print(mWebView);
+        pw.println();
+    }
+
+    void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
+            int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime,
+            String reqPackage) {
+        ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+                procStates, now, reqPackage);
+        if (procs.size() > 0) {
+            if (header != null) {
+                pw.println();
+                pw.println(header);
+            }
+            dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates,
+                    now, totalTime);
+        }
+    }
+
+    public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
+            int[] procStates, long now, String reqPackage) {
+        ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        for (int ip=0; ip<pkgMap.size(); ip++) {
+            if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
+                continue;
+            }
+            SparseArray<PackageState> procs = pkgMap.valueAt(ip);
+            for (int iu=0; iu<procs.size(); iu++) {
+                PackageState state = procs.valueAt(iu);
+                for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
+                    ProcessState proc = state.mProcesses.valueAt(iproc);
+                    foundProcs.add(proc.mCommonProcess);
+                }
+            }
+        }
+        ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
+        for (int i=0; i<foundProcs.size(); i++) {
+            ProcessState proc = foundProcs.valueAt(i);
+            if (computeProcessTimeLocked(proc, screenStates, memStates,
+                    procStates, now) > 0) {
+                outProcs.add(proc);
+            }
+        }
+        Collections.sort(outProcs, new Comparator<ProcessState>() {
+            @Override
+            public int compare(ProcessState lhs, ProcessState rhs) {
+                if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
+                    return -1;
+                } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
+                    return 1;
+                }
+                return 0;
+            }
+        });
+        return outProcs;
+    }
+
+    String collapseString(String pkgName, String itemName) {
+        if (itemName.startsWith(pkgName)) {
+            final int ITEMLEN = itemName.length();
+            final int PKGLEN = pkgName.length();
+            if (ITEMLEN == PKGLEN) {
+                return "";
+            } else if (ITEMLEN >= PKGLEN) {
+                if (itemName.charAt(PKGLEN) == '.') {
+                    return itemName.substring(PKGLEN);
+                }
+            }
+        }
+        return itemName;
+    }
+
+    public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
+        final long now = SystemClock.uptimeMillis();
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        pw.println("vers,3");
+        pw.print("period,"); pw.print(mTimePeriodStartClockStr);
+        pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
+        pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+        boolean partial = true;
+        if ((mFlags&FLAG_SHUTDOWN) != 0) {
+            pw.print(",shutdown");
+            partial = false;
+        }
+        if ((mFlags&FLAG_SYSPROPS) != 0) {
+            pw.print(",sysprops");
+            partial = false;
+        }
+        if ((mFlags&FLAG_COMPLETE) != 0) {
+            pw.print(",complete");
+            partial = false;
+        }
+        if (partial) {
+            pw.print(",partial");
+        }
+        pw.println();
+        pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
+        for (int ip=0; ip<pkgMap.size(); ip++) {
+            String pkgName = pkgMap.keyAt(ip);
+            if (reqPackage != null && !reqPackage.equals(pkgName)) {
+                continue;
+            }
+            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+            for (int iu=0; iu<uids.size(); iu++) {
+                int uid = uids.keyAt(iu);
+                PackageState pkgState = uids.valueAt(iu);
+                final int NPROCS = pkgState.mProcesses.size();
+                final int NSRVS = pkgState.mServices.size();
+                for (int iproc=0; iproc<NPROCS; iproc++) {
+                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                    pw.print("pkgproc,");
+                    pw.print(pkgName);
+                    pw.print(",");
+                    pw.print(uid);
+                    pw.print(",");
+                    pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+                    dumpAllProcessStateCheckin(pw, proc, now);
+                    pw.println();
+                    if (proc.mPssTableSize > 0) {
+                        pw.print("pkgpss,");
+                        pw.print(pkgName);
+                        pw.print(",");
+                        pw.print(uid);
+                        pw.print(",");
+                        pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+                        dumpAllProcessPssCheckin(pw, proc);
+                        pw.println();
+                    }
+                    if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
+                        pw.print("pkgkills,");
+                        pw.print(pkgName);
+                        pw.print(",");
+                        pw.print(uid);
+                        pw.print(",");
+                        pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
+                        pw.print(",");
+                        pw.print(proc.mNumExcessiveWake);
+                        pw.print(",");
+                        pw.print(proc.mNumExcessiveCpu);
+                        pw.println();
+                    }
+                }
+                for (int isvc=0; isvc<NSRVS; isvc++) {
+                    String serviceName = collapseString(pkgName,
+                            pkgState.mServices.keyAt(isvc));
+                    ServiceState svc = pkgState.mServices.valueAt(isvc);
+                    dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
+                            svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
+                            svc.mStartedState, svc.mStartedStartTime, now);
+                    dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
+                            svc, ServiceState.SERVICE_BOUND, svc.mBoundCount,
+                            svc.mBoundState, svc.mBoundStartTime, now);
+                    dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
+                            svc, ServiceState.SERVICE_EXEC, svc.mExecCount,
+                            svc.mExecState, svc.mExecStartTime, now);
+                }
+            }
+        }
+
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int ip=0; ip<procMap.size(); ip++) {
+            String procName = procMap.keyAt(ip);
+            SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            for (int iu=0; iu<uids.size(); iu++) {
+                int uid = uids.keyAt(iu);
+                ProcessState procState = uids.valueAt(iu);
+                if (procState.mDurationsTableSize > 0) {
+                    pw.print("proc,");
+                    pw.print(procName);
+                    pw.print(",");
+                    pw.print(uid);
+                    dumpAllProcessStateCheckin(pw, procState, now);
+                    pw.println();
+                }
+                if (procState.mPssTableSize > 0) {
+                    pw.print("pss,");
+                    pw.print(procName);
+                    pw.print(",");
+                    pw.print(uid);
+                    dumpAllProcessPssCheckin(pw, procState);
+                    pw.println();
+                }
+                if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) {
+                    pw.print("kills,");
+                    pw.print(procName);
+                    pw.print(",");
+                    pw.print(uid);
+                    pw.print(",");
+                    pw.print(procState.mNumExcessiveWake);
+                    pw.print(",");
+                    pw.print(procState.mNumExcessiveCpu);
+                    pw.println();
+                }
+            }
+        }
+        pw.print("total");
+        dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
+                mStartTime, now);
+        pw.println();
+    }
+
+    public static final class ProcessState {
+        public final ProcessStats mStats;
+        public final ProcessState mCommonProcess;
+        public final String mPackage;
+        public final int mUid;
+        public final String mName;
+
+        int[] mDurationsTable;
+        int mDurationsTableSize;
+
+        //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
+        int mCurState = STATE_NOTHING;
+        long mStartTime;
+
+        int mLastPssState = STATE_NOTHING;
+        long mLastPssTime;
+        int[] mPssTable;
+        int mPssTableSize;
+
+        int mNumStartedServices;
+
+        int mNumExcessiveWake;
+        int mNumExcessiveCpu;
+
+        boolean mMultiPackage;
+
+        public long mTmpTotalTime;
+
+        /**
+         * Create a new top-level process state, for the initial case where there is only
+         * a single package running in a process.  The initial state is not running.
+         */
+        public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
+            mStats = processStats;
+            mCommonProcess = this;
+            mPackage = pkg;
+            mUid = uid;
+            mName = name;
+        }
+
+        /**
+         * Create a new per-package process state for an existing top-level process
+         * state.  The current running state of the top-level process is also copied,
+         * marked as started running at 'now'.
+         */
+        public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
+                long now) {
+            mStats = commonProcess.mStats;
+            mCommonProcess = commonProcess;
+            mPackage = pkg;
+            mUid = uid;
+            mName = name;
+            mCurState = commonProcess.mCurState;
+            mStartTime = now;
+        }
+
+        ProcessState clone(String pkg, long now) {
+            ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
+            if (mDurationsTable != null) {
+                mStats.mAddLongTable = new int[mDurationsTable.length];
+                mStats.mAddLongTableSize = 0;
+                for (int i=0; i<mDurationsTableSize; i++) {
+                    int origEnt = mDurationsTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mStats.addLongData(i, type, 1);
+                    mStats.mAddLongTable[i] = newOff | type;
+                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
+                }
+                pnew.mDurationsTable = mStats.mAddLongTable;
+                pnew.mDurationsTableSize = mStats.mAddLongTableSize;
+            }
+            if (mPssTable != null) {
+                mStats.mAddLongTable = new int[mPssTable.length];
+                mStats.mAddLongTableSize = 0;
+                for (int i=0; i<mPssTableSize; i++) {
+                    int origEnt = mPssTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mStats.addLongData(i, type, PSS_COUNT);
+                    mStats.mAddLongTable[i] = newOff | type;
+                    for (int j=0; j<PSS_COUNT; j++) {
+                        mStats.setLong(newOff, j, mStats.getLong(origEnt, j));
+                    }
+                }
+                pnew.mPssTable = mStats.mAddLongTable;
+                pnew.mPssTableSize = mStats.mAddLongTableSize;
+            }
+            pnew.mNumExcessiveWake = mNumExcessiveWake;
+            pnew.mNumExcessiveCpu = mNumExcessiveCpu;
+            pnew.mNumStartedServices = mNumStartedServices;
+            return pnew;
+        }
+
+        void resetSafely(long now) {
+            mDurationsTable = null;
+            mDurationsTableSize = 0;
+            mStartTime = now;
+            mLastPssState = STATE_NOTHING;
+            mLastPssTime = 0;
+            mPssTable = null;
+            mPssTableSize = 0;
+            mNumExcessiveWake = 0;
+            mNumExcessiveCpu = 0;
+        }
+
+        void writeToParcel(Parcel out, long now) {
+            out.writeInt(mMultiPackage ? 1 : 0);
+            out.writeInt(mDurationsTableSize);
+            for (int i=0; i<mDurationsTableSize; i++) {
+                if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
+                        + printLongOffset(mDurationsTable[i]));
+                out.writeInt(mDurationsTable[i]);
+            }
+            out.writeInt(mPssTableSize);
+            for (int i=0; i<mPssTableSize; i++) {
+                if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
+                        + printLongOffset(mPssTable[i]));
+                out.writeInt(mPssTable[i]);
+            }
+            out.writeInt(mNumExcessiveWake);
+            out.writeInt(mNumExcessiveCpu);
+        }
+
+        boolean readFromParcel(Parcel in, boolean fully) {
+            boolean multiPackage = in.readInt() != 0;
+            if (fully) {
+                mMultiPackage = multiPackage;
+            }
+            if (DEBUG) Slog.d(TAG, "Reading durations table...");
+            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
+            if (mDurationsTable == BAD_TABLE) {
+                return false;
+            }
+            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+            if (DEBUG) Slog.d(TAG, "Reading pss table...");
+            mPssTable = mStats.readTableFromParcel(in, mName, "pss");
+            if (mPssTable == BAD_TABLE) {
+                return false;
+            }
+            mPssTableSize = mPssTable != null ? mPssTable.length : 0;
+            mNumExcessiveWake = in.readInt();
+            mNumExcessiveCpu = in.readInt();
+            return true;
+        }
+
+        /**
+         * Update the current state of the given list of processes.
+         *
+         * @param state Current ActivityManager.PROCESS_STATE_*
+         * @param memFactor Current mem factor constant.
+         * @param now Current time.
+         * @param pkgList Processes to update.
+         */
+        public void setState(int state, int memFactor, long now,
+                ArrayMap<String, ProcessState> pkgList) {
+            if (state < 0) {
+                state = mNumStartedServices > 0
+                        ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
+            } else {
+                state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
+            }
+
+            // First update the common process.
+            mCommonProcess.setState(state, now);
+
+            // If the common process is not multi-package, there is nothing else to do.
+            if (!mCommonProcess.mMultiPackage) {
+                return;
+            }
+
+            if (pkgList != null) {
+                for (int ip=pkgList.size()-1; ip>=0; ip--) {
+                    pullFixedProc(pkgList, ip).setState(state, now);
+                }
+            }
+        }
+
+        void setState(int state, long now) {
+            if (mCurState != state) {
+                //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
+                commitStateTime(now);
+                mCurState = state;
+            }
+        }
+
+        void commitStateTime(long now) {
+            if (mCurState != STATE_NOTHING) {
+                long dur = now - mStartTime;
+                if (dur > 0) {
+                    int idx = binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
+                    int off;
+                    if (idx >= 0) {
+                        off = mDurationsTable[idx];
+                    } else {
+                        mStats.mAddLongTable = mDurationsTable;
+                        mStats.mAddLongTableSize = mDurationsTableSize;
+                        off = mStats.addLongData(~idx, mCurState, 1);
+                        mDurationsTable = mStats.mAddLongTable;
+                        mDurationsTableSize = mStats.mAddLongTableSize;
+                    }
+                    long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                    longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
+                }
+            }
+            mStartTime = now;
+        }
+
+        void incStartedServices(int memFactor, long now) {
+            if (mCommonProcess != this) {
+                mCommonProcess.incStartedServices(memFactor, now);
+            }
+            mNumStartedServices++;
+            if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
+                setState(STATE_NOTHING, memFactor, now, null);
+            }
+        }
+
+        void decStartedServices(int memFactor, long now) {
+            if (mCommonProcess != this) {
+                mCommonProcess.decStartedServices(memFactor, now);
+            }
+            mNumStartedServices--;
+            if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) {
+                setState(STATE_NOTHING, memFactor, now, null);
+            } else if (mNumStartedServices < 0) {
+                throw new IllegalStateException("Proc started services underrun: pkg="
+                        + mPackage + " uid=" + mUid + " name=" + mName);
+            }
+        }
+
+        public void addPss(long pss, long uss, boolean always) {
+            if (!always) {
+                if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+                        < (mLastPssTime+(30*1000))) {
+                    return;
+                }
+            }
+            mLastPssState = mCurState;
+            mLastPssTime = SystemClock.uptimeMillis();
+            if (mCurState != STATE_NOTHING) {
+                int idx = binarySearch(mPssTable, mPssTableSize, mCurState);
+                int off;
+                if (idx >= 0) {
+                    off = mPssTable[idx];
+                } else {
+                    mStats.mAddLongTable = mPssTable;
+                    mStats.mAddLongTableSize = mPssTableSize;
+                    off = mStats.addLongData(~idx, mCurState, PSS_COUNT);
+                    mPssTable = mStats.mAddLongTable;
+                    mPssTableSize = mStats.mAddLongTableSize;
+                }
+                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+                long count = longs[idx+PSS_SAMPLE_COUNT];
+                if (count == 0) {
+                    longs[idx+PSS_SAMPLE_COUNT] = 1;
+                    longs[idx+PSS_MINIMUM] = pss;
+                    longs[idx+PSS_AVERAGE] = pss;
+                    longs[idx+PSS_MAXIMUM] = pss;
+                    longs[idx+PSS_USS_MINIMUM] = uss;
+                    longs[idx+PSS_USS_AVERAGE] = uss;
+                    longs[idx+PSS_USS_MAXIMUM] = uss;
+                } else {
+                    longs[idx+PSS_SAMPLE_COUNT] = count+1;
+                    if (longs[idx+PSS_MINIMUM] > pss) {
+                        longs[idx+PSS_MINIMUM] = pss;
+                    }
+                    longs[idx+PSS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) );
+                    if (longs[idx+PSS_MAXIMUM] < pss) {
+                        longs[idx+PSS_MAXIMUM] = pss;
+                    }
+                    if (longs[idx+PSS_USS_MINIMUM] > uss) {
+                        longs[idx+PSS_USS_MINIMUM] = uss;
+                    }
+                    longs[idx+PSS_USS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) );
+                    if (longs[idx+PSS_USS_MAXIMUM] < uss) {
+                        longs[idx+PSS_USS_MAXIMUM] = uss;
+                    }
+                }
+            }
+        }
+
+        public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) {
+            mCommonProcess.mNumExcessiveWake++;
+            if (!mCommonProcess.mMultiPackage) {
+                return;
+            }
+
+            for (int ip=pkgList.size()-1; ip>=0; ip--) {
+                pullFixedProc(pkgList, ip).mNumExcessiveWake++;
+            }
+        }
+
+        public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) {
+            mCommonProcess.mNumExcessiveCpu++;
+            if (!mCommonProcess.mMultiPackage) {
+                return;
+            }
+
+            for (int ip=pkgList.size()-1; ip>=0; ip--) {
+                pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
+            }
+        }
+
+        ProcessState pullFixedProc(String pkgName) {
+            if (mMultiPackage) {
+                // The array map is still pointing to a common process state
+                // that is now shared across packages.  Update it to point to
+                // the new per-package state.
+                ProcessState proc = mStats.mPackages.get(pkgName,
+                        mUid).mProcesses.get(mName);
+                if (proc == null) {
+                    throw new IllegalStateException("Didn't create per-package process");
+                }
+                return proc;
+            }
+            return this;
+        }
+
+        private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList,
+                int index) {
+            ProcessState proc = pkgList.valueAt(index);
+            if (proc.mMultiPackage) {
+                // The array map is still pointing to a common process state
+                // that is now shared across packages.  Update it to point to
+                // the new per-package state.
+                proc = mStats.mPackages.get(pkgList.keyAt(index),
+                        proc.mUid).mProcesses.get(proc.mName);
+                if (proc == null) {
+                    throw new IllegalStateException("Didn't create per-package process");
+                }
+                pkgList.setValueAt(index, proc);
+            }
+            return proc;
+        }
+
+        long getDuration(int state, long now) {
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            if (mCurState == state) {
+                time += now - mStartTime;
+            }
+            return time;
+        }
+
+        long getPssSampleCount(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
+        }
+
+        long getPssMinimum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
+        }
+
+        long getPssAverage(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
+        }
+
+        long getPssMaximum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
+        }
+
+        long getPssUssMinimum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
+        }
+
+        long getPssUssAverage(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
+        }
+
+        long getPssUssMaximum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
+        }
+    }
+
+    public static final class ServiceState {
+        final ProcessStats mStats;
+        final String mPackage;
+        ProcessState mProc;
+
+        int mActive = 1;
+
+        static final int SERVICE_STARTED = 0;
+        static final int SERVICE_BOUND = 1;
+        static final int SERVICE_EXEC = 2;
+        static final int SERVICE_COUNT = 3;
+
+        int[] mDurationsTable;
+        int mDurationsTableSize;
+
+        int mStartedCount;
+        public int mStartedState = STATE_NOTHING;
+        long mStartedStartTime;
+
+        int mBoundCount;
+        public int mBoundState = STATE_NOTHING;
+        long mBoundStartTime;
+
+        int mExecCount;
+        public int mExecState = STATE_NOTHING;
+        long mExecStartTime;
+
+        public ServiceState(ProcessStats processStats, String pkg, ProcessState proc) {
+            mStats = processStats;
+            mPackage = pkg;
+            mProc = proc;
+        }
+
+        public void makeActive() {
+            mActive++;
+        }
+
+        public void makeInactive() {
+            /*
+            RuntimeException here = new RuntimeException("here");
+            here.fillInStackTrace();
+            Slog.i(TAG, "Making " + this + " inactive", here);
+            */
+            mActive--;
+        }
+
+        public boolean isActive() {
+            return mActive > 0;
+        }
+
+        void resetSafely(long now) {
+            mDurationsTable = null;
+            mDurationsTableSize = 0;
+            mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
+            mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
+            mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
+            mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+        }
+
+        void writeToParcel(Parcel out, long now) {
+            out.writeInt(mDurationsTableSize);
+            for (int i=0; i<mDurationsTableSize; i++) {
+                if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": "
+                        + printLongOffset(mDurationsTable[i]));
+                out.writeInt(mDurationsTable[i]);
+            }
+            out.writeInt(mStartedCount);
+            out.writeInt(mBoundCount);
+            out.writeInt(mExecCount);
+        }
+
+        boolean readFromParcel(Parcel in) {
+            if (DEBUG) Slog.d(TAG, "Reading durations table...");
+            mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service");
+            if (mDurationsTable == BAD_TABLE) {
+                return false;
+            }
+            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+            mStartedCount = in.readInt();
+            mBoundCount = in.readInt();
+            mExecCount = in.readInt();
+            return true;
+        }
+
+        void addStateTime(int opType, int memFactor, long time) {
+            if (time > 0) {
+                int state = opType + (memFactor*SERVICE_COUNT);
+                int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+                int off;
+                if (idx >= 0) {
+                    off = mDurationsTable[idx];
+                } else {
+                    mStats.mAddLongTable = mDurationsTable;
+                    mStats.mAddLongTableSize = mDurationsTableSize;
+                    off = mStats.addLongData(~idx, state, 1);
+                    mDurationsTable = mStats.mAddLongTable;
+                    mDurationsTableSize = mStats.mAddLongTableSize;
+                }
+                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
+            }
+        }
+
+        void commitStateTime(long now) {
+            if (mStartedState != STATE_NOTHING) {
+                addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
+                mStartedStartTime = now;
+            }
+            if (mBoundState != STATE_NOTHING) {
+                addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
+                mBoundStartTime = now;
+            }
+            if (mExecState != STATE_NOTHING) {
+                addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
+                mExecStartTime = now;
+            }
+        }
+
+        public void setStarted(boolean started, int memFactor, long now) {
+            if (mActive <= 0) {
+                throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
+            }
+            int state = started ? memFactor : STATE_NOTHING;
+            if (mStartedState != state) {
+                if (mStartedState != STATE_NOTHING) {
+                    addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
+                } else if (started) {
+                    mStartedCount++;
+                }
+                mStartedState = state;
+                mStartedStartTime = now;
+                if (mProc != null) {
+                    mProc = mProc.pullFixedProc(mPackage);
+                    if (started) {
+                        mProc.incStartedServices(memFactor, now);
+                    } else {
+                        mProc.decStartedServices(memFactor, now);
+                    }
+                }
+            }
+        }
+
+        public void setBound(boolean bound, int memFactor, long now) {
+            if (mActive <= 0) {
+                throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
+            }
+            int state = bound ? memFactor : STATE_NOTHING;
+            if (mBoundState != state) {
+                if (mBoundState != STATE_NOTHING) {
+                    addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
+                } else if (bound) {
+                    mBoundCount++;
+                }
+                mBoundState = state;
+                mBoundStartTime = now;
+            }
+        }
+
+        public void setExecuting(boolean executing, int memFactor, long now) {
+            if (mActive <= 0) {
+                throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
+            }
+            int state = executing ? memFactor : STATE_NOTHING;
+            if (mExecState != state) {
+                if (mExecState != STATE_NOTHING) {
+                    addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
+                } else if (executing) {
+                    mExecCount++;
+                }
+                mExecState = state;
+                mExecStartTime = now;
+            }
+        }
+
+        private long getDuration(int opType, int curState, long startTime, int memFactor,
+                long now) {
+            int state = opType + (memFactor*SERVICE_COUNT);
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            if (curState == memFactor) {
+                time += now - startTime;
+            }
+            return time;
+        }
+    }
+
+    public static final class PackageState {
+        public final ArrayMap<String, ProcessState> mProcesses
+                = new ArrayMap<String, ProcessState>();
+        public final ArrayMap<String, ServiceState> mServices
+                = new ArrayMap<String, ServiceState>();
+        final int mUid;
+
+        public PackageState(int uid) {
+            mUid = uid;
+        }
+    }
+
+    public static final class ProcessDataCollection {
+        final int[] screenStates;
+        final int[] memStates;
+        final int[] procStates;
+
+        public long totalTime;
+        public long numPss;
+        public long minPss;
+        public long avgPss;
+        public long maxPss;
+        public long minUss;
+        public long avgUss;
+        public long maxUss;
+
+        public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
+            screenStates = _screenStates;
+            memStates = _memStates;
+            procStates = _procStates;
+        }
+
+        void print(PrintWriter pw, long overallTime, boolean full) {
+            printPercent(pw, (double) totalTime / (double) overallTime);
+            if (numPss > 0) {
+                pw.print(" (");
+                printSizeValue(pw, minPss * 1024);
+                pw.print("-");
+                printSizeValue(pw, avgPss * 1024);
+                pw.print("-");
+                printSizeValue(pw, maxPss * 1024);
+                pw.print("/");
+                printSizeValue(pw, minUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, avgUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, maxUss * 1024);
+                if (full) {
+                    pw.print(" over ");
+                    pw.print(numPss);
+                }
+                pw.print(")");
+            }
+        }
+    }
+}