Merge "Use more styles for preferences to allow single pane 10""
diff --git a/api/current.txt b/api/current.txt
index 9df2a16..7f333b7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14819,7 +14819,6 @@
     method public static synchronized android.nfc.cardemulation.CardEmulationManager getInstance(android.nfc.NfcAdapter);
     method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
-    method public boolean setDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.ACTION_CHANGE_DEFAULT";
     field public static final java.lang.String CATEGORY_OTHER = "other";
     field public static final java.lang.String CATEGORY_PAYMENT = "payment";
@@ -27257,13 +27256,15 @@
     field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
     field public static final deprecated int STATUS_BAR_HIDDEN = 1; // 0x1
     field public static final deprecated int STATUS_BAR_VISIBLE = 0; // 0x0
-    field public static final int SYSTEM_UI_FLAG_ALLOW_OVERLAY = 2048; // 0x800
+    field public static final int SYSTEM_UI_FLAG_ALLOW_TRANSIENT = 2048; // 0x800
     field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
     field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
+    field public static final int SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION = 8192; // 0x2000
+    field public static final int SYSTEM_UI_FLAG_TRANSPARENT_STATUS = 4096; // 0x1000
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
     field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 7369c0b..b8a5ba7 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -28,5 +28,6 @@
     boolean isDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
     boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
     boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
+    boolean setDefaultForNextTap(int userHandle, in ComponentName service);
     List<ApduServiceInfo> getServices(int userHandle, in String category);
 }
diff --git a/core/java/android/nfc/cardemulation/CardEmulationManager.java b/core/java/android/nfc/cardemulation/CardEmulationManager.java
index abfeaf9..673b33a 100644
--- a/core/java/android/nfc/cardemulation/CardEmulationManager.java
+++ b/core/java/android/nfc/cardemulation/CardEmulationManager.java
@@ -185,7 +185,7 @@
     }
 
     /**
-     * @return
+     * @hide
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
         try {
@@ -210,6 +210,27 @@
     /**
      * @hide
      */
+    public boolean setDefaultForNextTap(ComponentName service) {
+        try {
+            return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+    /**
+     * @hide
+     */
     public List<ApduServiceInfo> getServices(String category) {
         try {
             return sService.getServices(UserHandle.myUserId(), category);
@@ -233,4 +254,4 @@
         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
         sService = adapter.getCardEmulationService();
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7b91418..4627c88 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -296,7 +296,21 @@
                 disabled = sDumpDisabled;
             }
             if (disabled == null) {
-                dump(fd, pw, args);
+                try {
+                    dump(fd, pw, args);
+                } catch (SecurityException e) {
+                    pw.println();
+                    pw.println("Security exception: " + e.getMessage());
+                    throw e;
+                } catch (Throwable e) {
+                    // Unlike usual calls, in this case if an exception gets thrown
+                    // back to us we want to print it back in to the dump data, since
+                    // that is where the caller expects all interesting information to
+                    // go.
+                    pw.println();
+                    pw.println("Exception occurred while dumping:");
+                    e.printStackTrace(pw);
+                }
             } else {
                 pw.println(sDumpDisabled);
             }
@@ -443,7 +457,6 @@
         data.writeStringArray(args);
         try {
             transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
-            reply.readException();
         } finally {
             data.recycle();
             reply.recycle();
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 9e8cfad..c067661 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -288,6 +288,7 @@
         private void doFinish() {
             mDocumentAdapter = null;
             mHandler = null;
+            mLayoutOrWriteCancellation = null;
         }
 
         private final class MyHandler extends Handler {
@@ -312,10 +313,10 @@
 
                     case MSG_LAYOUT: {
                         SomeArgs args = (SomeArgs) message.obj;
-                        final PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
-                        final PrintAttributes newAttributes = (PrintAttributes) args.arg2;
-                        final ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
-                        final Bundle metadata = (Bundle) args.arg4;
+                        PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
+                        PrintAttributes newAttributes = (PrintAttributes) args.arg2;
+                        ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
+                        Bundle metadata = (Bundle) args.arg4;
                         final int sequence = args.argi1;
                         args.recycle();
 
@@ -324,49 +325,15 @@
                             mLayoutOrWriteCancellation = cancellation;
                         }
 
-                        mDocumentAdapter.onLayout(oldAttributes, newAttributes,
-                                cancellation, new LayoutResultCallback() {
-                            @Override
-                            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
-                                if (info == null) {
-                                    throw new IllegalArgumentException("info cannot be null");
-                                }
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                try {
-                                    callback.onLayoutFinished(info, changed, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
-                                }
-                            }
-
-                            @Override
-                            public void onLayoutFailed(CharSequence error) {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                try {
-                                    callback.onLayoutFailed(error, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
-                                }
-                            }
-
-                            @Override
-                            public void onLayoutCancelled() {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                            }
-                        }, metadata);
+                        mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
+                                new MyLayoutResultCallback(callback, sequence), metadata);
                     } break;
 
                     case MSG_WRITE: {
                         SomeArgs args = (SomeArgs) message.obj;
-                        final PageRange[] pages = (PageRange[]) args.arg1;
-                        final FileDescriptor fd = (FileDescriptor) args.arg2;
-                        final IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
+                        PageRange[] pages = (PageRange[]) args.arg1;
+                        FileDescriptor fd = (FileDescriptor) args.arg2;
+                        IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
                         final int sequence = args.argi1;
                         args.recycle();
 
@@ -376,52 +343,7 @@
                         }
 
                         mDocumentAdapter.onWrite(pages, fd, cancellation,
-                                new WriteResultCallback() {
-                            @Override
-                            public void onWriteFinished(PageRange[] pages) {
-                                if (pages == null) {
-                                    throw new IllegalArgumentException("pages cannot be null");
-                                }
-                                if (pages.length == 0) {
-                                    throw new IllegalArgumentException("pages cannot be empty");
-                                }
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Close before notifying the other end. We want
-                                // to be ready by the time we announce it.
-                                IoUtils.closeQuietly(fd);
-                                try {
-                                    callback.onWriteFinished(pages, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onWriteFinished", re);
-                                }
-                            }
-
-                            @Override
-                            public void onWriteFailed(CharSequence error) {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Close before notifying the other end. We want
-                                // to be ready by the time we announce it.
-                                IoUtils.closeQuietly(fd);
-                                try {
-                                    callback.onWriteFailed(error, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onWriteFailed", re);
-                                }
-                            }
-
-                            @Override
-                            public void onWriteCancelled() {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Just close the fd for now.
-                                IoUtils.closeQuietly(fd);
-                            }
-                        });
+                                new MyWriteResultCallback(callback, fd, sequence));
                     } break;
 
                     case MSG_FINISH: {
@@ -436,5 +358,128 @@
                 }
             }
         }
+
+        private final class MyLayoutResultCallback extends LayoutResultCallback {
+            private ILayoutResultCallback mCallback;
+            private final int mSequence;
+
+            public MyLayoutResultCallback(ILayoutResultCallback callback,
+                    int sequence) {
+                mCallback = callback;
+                mSequence = sequence;
+            }
+
+            @Override
+            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+                final ILayoutResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (info == null) {
+                    throw new IllegalArgumentException("info cannot be null");
+                }
+                if (callback != null) {
+                    try {
+                        callback.onLayoutFinished(info, changed, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onLayoutFailed(CharSequence error) {
+                final ILayoutResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (callback != null) {
+                    try {
+                        callback.onLayoutFailed(error, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onLayoutCancelled() {
+                synchronized (mLock) {
+                    clearLocked();
+                }
+            }
+
+            private void clearLocked() {
+                mLayoutOrWriteCancellation = null;
+                mCallback = null;
+            }
+        }
+
+        private final class MyWriteResultCallback extends WriteResultCallback {
+            private FileDescriptor mFd;
+            private int mSequence;
+            private IWriteResultCallback mCallback;
+
+            public MyWriteResultCallback(IWriteResultCallback callback,
+                    FileDescriptor fd, int sequence) {
+                mFd = fd;
+                mSequence = sequence;
+                mCallback = callback;
+            }
+
+            @Override
+            public void onWriteFinished(PageRange[] pages) {
+                final IWriteResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (pages == null) {
+                    throw new IllegalArgumentException("pages cannot be null");
+                }
+                if (pages.length == 0) {
+                    throw new IllegalArgumentException("pages cannot be empty");
+                }
+                if (callback != null) {
+                    try {
+                        callback.onWriteFinished(pages, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onWriteFinished", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onWriteFailed(CharSequence error) {
+                final IWriteResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (callback != null) {
+                    try {
+                        callback.onWriteFailed(error, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onWriteFailed", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onWriteCancelled() {
+                synchronized (mLock) {
+                    clearLocked();
+                }
+            }
+
+            private void clearLocked() {
+                mLayoutOrWriteCancellation = null;
+                IoUtils.closeQuietly(mFd);
+                mCallback = null;
+                mFd = null;
+            }
+        }
     }
 }
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 52ad76d..b2c9f8c 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -322,7 +322,7 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
         if (mSurface != null) {
-            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
+            mSurface.setDefaultBufferSize(getWidth(), getHeight());
             updateLayer();
             if (mListener != null) {
                 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
@@ -362,7 +362,7 @@
                 // Create a new SurfaceTexture for the layer.
                 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
             }
-            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
+            mSurface.setDefaultBufferSize(getWidth(), getHeight());
             nCreateNativeWindow(mSurface);
 
             mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
@@ -399,7 +399,7 @@
             mMatrixChanged = true;
 
             mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
-            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
+            mSurface.setDefaultBufferSize(getWidth(), getHeight());
         }
 
         applyUpdate();
@@ -816,9 +816,6 @@
     private native void nCreateNativeWindow(SurfaceTexture surface);
     private native void nDestroyNativeWindow();
 
-    private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
-            int width, int height);
-
     private static native boolean nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
     private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7624b56..20938f51 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2379,9 +2379,27 @@
      * when hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the
      * navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} instead of having the system
      * clear these flags upon interaction.  The system may compensate by temporarily overlaying
-     * transparent system ui while also delivering the event.
+     * transparent system bars while also delivering the event.
      */
-    public static final int SYSTEM_UI_FLAG_ALLOW_OVERLAY = 0x00000800;
+    public static final int SYSTEM_UI_FLAG_ALLOW_TRANSIENT = 0x00000800;
+
+    /**
+     * Flag for {@link #setSystemUiVisibility(int)}: View would like the status bar to have
+     * transparency.
+     *
+     * <p>The transparency request may be denied if the bar is in another mode with a specific
+     * style, like {@link #SYSTEM_UI_FLAG_ALLOW_TRANSIENT transient mode}.
+     */
+    public static final int SYSTEM_UI_FLAG_TRANSPARENT_STATUS = 0x00001000;
+
+    /**
+     * Flag for {@link #setSystemUiVisibility(int)}: View would like the navigation bar to have
+     * transparency.
+     *
+     * <p>The transparency request may be denied if the bar is in another mode with a specific
+     * style, like {@link #SYSTEM_UI_FLAG_ALLOW_TRANSIENT transient mode}.
+     */
+    public static final int SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION = 0x00002000;
 
     /**
      * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
@@ -2508,11 +2526,9 @@
      * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
      * out of the public fields to keep the undefined bits out of the developer's way.
      *
-     * Flag to specify that the status bar should temporarily overlay underlying content
-     * that is otherwise assuming the status bar is hidden.  The status bar may
-     * have some degree of transparency while in this temporary overlay mode.
+     * Flag to specify that the status bar is displayed in transient mode.
      */
-    public static final int STATUS_BAR_OVERLAY = 0x04000000;
+    public static final int STATUS_BAR_TRANSIENT = 0x04000000;
 
     /**
      * @hide
@@ -2520,11 +2536,9 @@
      * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
      * out of the public fields to keep the undefined bits out of the developer's way.
      *
-     * Flag to specify that the navigation bar should temporarily overlay underlying content
-     * that is otherwise assuming the navigation bar is hidden.  The navigation bar mayu
-     * have some degree of transparency while in this temporary overlay mode.
+     * Flag to specify that the navigation bar is displayed in transient mode.
      */
-    public static final int NAVIGATION_BAR_OVERLAY = 0x08000000;
+    public static final int NAVIGATION_BAR_TRANSIENT = 0x08000000;
 
     /**
      * @hide
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
similarity index 98%
rename from core/java/com/android/internal/os/ProcessStats.java
rename to core/java/com/android/internal/os/ProcessCpuTracker.java
index 874bc0e..c092807 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -34,11 +34,11 @@
 import java.util.Comparator;
 import java.util.StringTokenizer;
 
-public class ProcessStats {
+public class ProcessCpuTracker {
     private static final String TAG = "ProcessStats";
     private static final boolean DEBUG = false;
     private static final boolean localLOGV = DEBUG || false;
-    
+
     private static final int[] PROCESS_STATS_FORMAT = new int[] {
         PROC_SPACE_TERM,
         PROC_SPACE_TERM|PROC_PARENS,
@@ -61,7 +61,7 @@
     static final int PROCESS_STAT_MAJOR_FAULTS = 1;
     static final int PROCESS_STAT_UTIME = 2;
     static final int PROCESS_STAT_STIME = 3;
-    
+
     /** Stores user time and system time in 100ths of a second. */
     private final long[] mProcessStatsData = new long[4];
     /** Stores user time and system time in 100ths of a second. */
@@ -123,14 +123,14 @@
     private final float[] mLoadAverageData = new float[3];
 
     private final boolean mIncludeThreads;
-    
+
     private float mLoad1 = 0;
     private float mLoad5 = 0;
     private float mLoad15 = 0;
-    
+
     private long mCurrentSampleTime;
     private long mLastSampleTime;
-    
+
     private long mCurrentSampleRealTime;
     private long mLastSampleRealTime;
 
@@ -149,7 +149,7 @@
 
     private int[] mCurPids;
     private int[] mCurThreadPids;
-    
+
     private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
     private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
     private boolean mWorkingProcsSorted;
@@ -202,12 +202,12 @@
         public long base_majfaults;
         public int rel_minfaults;
         public int rel_majfaults;
-        
+
         public boolean active;
         public boolean working;
         public boolean added;
         public boolean removed;
-        
+
         Stats(int _pid, int parentPid, boolean includeThreads) {
             pid = _pid;
             if (parentPid < 0) {
@@ -256,30 +256,30 @@
     };
 
 
-    public ProcessStats(boolean includeThreads) {
+    public ProcessCpuTracker(boolean includeThreads) {
         mIncludeThreads = includeThreads;
     }
-    
+
     public void onLoadChanged(float load1, float load5, float load15) {
     }
-    
+
     public int onMeasureProcessName(String name) {
         return 0;
     }
-    
+
     public void init() {
         if (DEBUG) Slog.v(TAG, "Init: " + this);
         mFirst = true;
         update();
     }
-    
+
     public void update() {
         if (DEBUG) Slog.v(TAG, "Update: " + this);
         mLastSampleTime = mCurrentSampleTime;
         mCurrentSampleTime = SystemClock.uptimeMillis();
         mLastSampleRealTime = mCurrentSampleRealTime;
         mCurrentSampleRealTime = SystemClock.elapsedRealtime();
-        
+
         final long[] sysCpu = mSystemCpuData;
         if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
                 null, sysCpu, null)) {
@@ -339,11 +339,11 @@
 
         mWorkingProcsSorted = false;
         mFirst = false;
-    }    
-    
+    }
+
     private int[] collectStats(String statsFile, int parentPid, boolean first,
             int[] curPids, ArrayList<Stats> allProcs) {
-        
+
         int[] pids = Process.getPids(statsFile, curPids);
         int NP = (pids == null) ? 0 : pids.length;
         int NS = allProcs.size();
@@ -355,7 +355,7 @@
                 break;
             }
             Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
-            
+
             if (st != null && st.pid == pid) {
                 // Update an existing process...
                 st.added = false;
@@ -373,7 +373,7 @@
                             PROCESS_STATS_FORMAT, null, procStats, null)) {
                         continue;
                     }
-                    
+
                     final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
                     final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
                     final long utime = procStats[PROCESS_STAT_UTIME];
@@ -423,7 +423,7 @@
 
                 continue;
             }
-            
+
             if (st == null || st.pid > pid) {
                 // We have a new process!
                 st = new Stats(pid, parentPid, mIncludeThreads);
@@ -477,7 +477,7 @@
                 if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
                         + " utime=" + st.base_utime + " stime=" + st.base_stime
                         + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
-                
+
                 st.rel_utime = 0;
                 st.rel_stime = 0;
                 st.rel_minfaults = 0;
@@ -488,7 +488,7 @@
                 }
                 continue;
             }
-                
+
             // This process has gone away!
             st.rel_utime = 0;
             st.rel_stime = 0;
@@ -520,7 +520,7 @@
             NS--;
             if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
         }
-        
+
         return pids;
     }
 
@@ -607,27 +607,27 @@
     final public int getLastUserTime() {
         return mRelUserTime;
     }
-    
+
     final public int getLastSystemTime() {
         return mRelSystemTime;
     }
-    
+
     final public int getLastIoWaitTime() {
         return mRelIoWaitTime;
     }
-    
+
     final public int getLastIrqTime() {
         return mRelIrqTime;
     }
-    
+
     final public int getLastSoftIrqTime() {
         return mRelSoftIrqTime;
     }
-    
+
     final public int getLastIdleTime() {
         return mRelIdleTime;
     }
-    
+
     final public float getTotalCpuPercent() {
         int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
         if (denom <= 0) {
@@ -635,7 +635,7 @@
         }
         return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom;
     }
-    
+
     final void buildWorkingProcs() {
         if (!mWorkingProcsSorted) {
             mWorkingProcs.clear();
@@ -678,7 +678,7 @@
     final public Stats getWorkingStats(int index) {
         return mWorkingProcs.get(index);
     }
-    
+
     final public String printCurrentLoad() {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 128);
@@ -694,10 +694,10 @@
 
     final public String printCurrentState(long now) {
         buildWorkingProcs();
-        
+
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
-        
+
         pw.print("CPU usage from ");
         if (now > mLastSampleTime) {
             pw.print(now-mLastSampleTime);
@@ -720,10 +720,10 @@
             pw.print("% awake");
         }
         pw.println(":");
-        
+
         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
-        
+
         if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
                 + (mCurrentSampleTime-mLastSampleTime));
 
@@ -744,14 +744,14 @@
                 }
             }
         }
-        
+
         printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
                 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
 
         pw.flush();
         return sw.toString();
     }
-    
+
     private void printRatio(PrintWriter pw, long numerator, long denominator) {
         long thousands = (numerator*1000)/denominator;
         long hundreds = thousands/10;
@@ -812,7 +812,7 @@
         }
         pw.println();
     }
-    
+
     private String readFile(String file, char endChar) {
         // Permit disk reads here, as /proc/meminfo isn't really "on
         // disk" and should be fast.  TODO: make BlockGuard ignore
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 31b2cad..2c482ea 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -40,6 +40,7 @@
 
 struct fields_t {
     jfieldID  surfaceTexture;
+    jfieldID  bufferQueue;
     jfieldID  frameAvailableListener;
     jmethodID postEvent;
 };
@@ -61,6 +62,20 @@
     env->SetIntField(thiz, fields.surfaceTexture, (int)surfaceTexture.get());
 }
 
+static void SurfaceTexture_setBufferQueue(JNIEnv* env, jobject thiz,
+        const sp<BufferQueue>& bq)
+{
+    BufferQueue* const p =
+        (BufferQueue*)env->GetIntField(thiz, fields.bufferQueue);
+    if (bq.get()) {
+        bq->incStrong((void*)SurfaceTexture_setBufferQueue);
+    }
+    if (p) {
+        p->decStrong((void*)SurfaceTexture_setBufferQueue);
+    }
+    env->SetIntField(thiz, fields.bufferQueue, (int)bq.get());
+}
+
 static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env,
         jobject thiz, sp<GLConsumer::FrameAvailableListener> listener)
 {
@@ -76,23 +91,22 @@
     env->SetIntField(thiz, fields.frameAvailableListener, (int)listener.get());
 }
 
-sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env,
-        jobject thiz)
-{
+sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
     return (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture);
 }
 
-sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(
-        JNIEnv* env, jobject thiz)
-{
+sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
+    return (BufferQueue*)env->GetIntField(thiz, fields.bufferQueue);
+}
+
+sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) {
     sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
-    sp<Surface> surfaceTextureClient(surfaceTexture != NULL ?
-            new Surface(surfaceTexture->getBufferQueue()) : NULL);
+    sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
+    sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
     return surfaceTextureClient;
 }
 
-bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz)
-{
+bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
     jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
     return env->IsInstanceOf(thiz, surfaceTextureClass);
 }
@@ -175,6 +189,12 @@
 
 // ----------------------------------------------------------------------------
 
+
+#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID "mBufferQueue"
+#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
+                                         "mFrameAvailableListener"
+
 static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
 {
     fields.surfaceTexture = env->GetFieldID(clazz,
@@ -183,6 +203,12 @@
         ALOGE("can't find android/graphics/SurfaceTexture.%s",
                 ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
     }
+    fields.bufferQueue = env->GetFieldID(clazz,
+            ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID, "I");
+    if (fields.bufferQueue == NULL) {
+        ALOGE("can't find android/graphics/SurfaceTexture.%s",
+                ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID);
+    }
     fields.frameAvailableListener = env->GetFieldID(clazz,
             ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID, "I");
     if (fields.frameAvailableListener == NULL) {
@@ -214,6 +240,7 @@
         return;
     }
     SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
+    SurfaceTexture_setBufferQueue(env, thiz, bq);
 
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
@@ -234,11 +261,11 @@
     surfaceTexture->setFrameAvailableListener(0);
     SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
     SurfaceTexture_setSurfaceTexture(env, thiz, 0);
+    SurfaceTexture_setBufferQueue(env, thiz, 0);
 }
 
 static void SurfaceTexture_setDefaultBufferSize(
-        JNIEnv* env, jobject thiz, jint width, jint height)
-{
+        JNIEnv* env, jobject thiz, jint width, jint height) {
     sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
     surfaceTexture->setDefaultBufferSize(width, height);
 }
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index dec4cd4..0018dd2 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -565,14 +565,10 @@
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
-    sp<BufferQueue> bufferQueue = NULL;
+    sp<IGraphicBufferProducer> producer = NULL;
     if (jSurfaceTexture != NULL) {
-        sp<GLConsumer> surfaceTexture =
-            SurfaceTexture_getSurfaceTexture(env, jSurfaceTexture);
-        if (surfaceTexture != NULL) {
-            bufferQueue = surfaceTexture->getBufferQueue();
-        }
-        else {
+        producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
+        if (producer == NULL) {
             jniThrowException(env, "java/lang/IllegalArgumentException",
                     "SurfaceTexture already released in setPreviewTexture");
             return;
@@ -580,7 +576,7 @@
 
     }
 
-    if (camera->setPreviewTexture(bufferQueue) != NO_ERROR) {
+    if (camera->setPreviewTexture(producer) != NO_ERROR) {
         jniThrowException(env, "java/io/IOException",
                 "setPreviewTexture failed");
     }
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 48babb3..1fe4b08 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -604,7 +604,7 @@
     jint _remaining;
     EGLint *attrib_list = (EGLint *) 0;
     android::sp<ANativeWindow> window;
-    android::sp<android::GLConsumer> glConsumer;
+    android::sp<android::IGraphicBufferProducer> producer;
 
     if (!attrib_list_ref) {
         _exception = 1;
@@ -625,12 +625,12 @@
         _exceptionMessage = "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface";
         goto exit;
     }
-    glConsumer = android::SurfaceTexture_getSurfaceTexture(_env, win);
+    producer = android::SurfaceTexture_getProducer(_env, win);
 
-    if (glConsumer == NULL)
+    if (producer == NULL)
         goto not_valid_surface;
 
-    window = new android::Surface(glConsumer->getBufferQueue());
+    window = new android::Surface(producer);
 
     if (window == NULL)
         goto not_valid_surface;
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 1007e7d..304514b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -135,15 +135,14 @@
 
 static jint nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz,
         jobject surfaceTextureObj) {
-    sp<GLConsumer> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj));
-    if (st == NULL) {
+    sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surfaceTextureObj));
+    if (producer == NULL) {
         jniThrowException(env, "java/lang/IllegalArgumentException",
                 "SurfaceTexture has already been released");
         return 0;
     }
 
-    sp<IGraphicBufferProducer> bq = st->getBufferQueue();
-    sp<Surface> surface(new Surface(bq, true));
+    sp<Surface> surface(new Surface(producer, true));
     if (surface == NULL) {
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index d515696..8dc2fc6 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -69,13 +69,6 @@
 // Native layer
 // ----------------------------------------------------------------------------
 
-static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject,
-    jobject surface, jint width, jint height) {
-
-    sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface));
-    glConsumer->setDefaultBufferSize(width, height);
-}
-
 static inline SkBitmap::Config convertPixelFormat(int32_t format) {
     switch (format) {
         case WINDOW_FORMAT_RGBA_8888:
@@ -106,8 +99,8 @@
 static void android_view_TextureView_createNativeWindow(JNIEnv* env, jobject textureView,
         jobject surface) {
 
-    sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface));
-    sp<ANativeWindow> window = new Surface(glConsumer->getBufferQueue());
+    sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surface));
+    sp<ANativeWindow> window = new Surface(producer);
 
     window->incStrong((void*)android_view_TextureView_createNativeWindow);
     SET_INT(textureView, gTextureViewClassInfo.nativeWindow, jint(window.get()));
@@ -208,9 +201,6 @@
 const char* const kClassPathName = "android/view/TextureView";
 
 static JNINativeMethod gMethods[] = {
-    {   "nSetDefaultBufferSize", "(Landroid/graphics/SurfaceTexture;II)V",
-            (void*) android_view_TextureView_setDefaultBufferSize },
-
     {   "nCreateNativeWindow", "(Landroid/graphics/SurfaceTexture;)V",
             (void*) android_view_TextureView_createNativeWindow },
     {   "nDestroyNativeWindow", "()V",
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index b0c73a2..a3ce2a5 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -352,9 +352,8 @@
         return 0;
     }
     
-    sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(_env, native_window));
-
-    window = new Surface(glConsumer->getBufferQueue());
+    sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(_env, native_window));
+    window = new Surface(producer);
     if (window == NULL)
         goto not_valid_surface;
 
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 91deb87..e8d6f16 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -69,6 +69,7 @@
      * These fields are used by native code, do not access or modify.
      */
     private int mSurfaceTexture;
+    private int mBufferQueue;
     private int mFrameAvailableListener;
 
     /**
diff --git a/include/android_runtime/android_graphics_SurfaceTexture.h b/include/android_runtime/android_graphics_SurfaceTexture.h
index 77ccd2a..c534d4b 100644
--- a/include/android_runtime/android_graphics_SurfaceTexture.h
+++ b/include/android_runtime/android_graphics_SurfaceTexture.h
@@ -24,14 +24,17 @@
 namespace android {
 
 class GLConsumer;
+class IGraphicBufferProducer;
 
-extern sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(
-        JNIEnv* env, jobject thiz);
+extern sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz);
 extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz);
 
 /* Gets the underlying GLConsumer from a SurfaceTexture Java object. */
 extern sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
 
+/* gets the producer end of the SurfaceTexture */
+extern sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz);
+
 } // namespace android
 
 #endif // _ANDROID_GRAPHICS_SURFACETEXTURE_H
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index b36f1400..60a84a6 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -2141,7 +2141,7 @@
                 new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
     }
 
-    protected void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
+    private void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
         if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
                 + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
         synchronized(mRCStack) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 0646f98..0b429f6 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 #include <utils/misc.h>
 #include <utils/List.h>
+#include <utils/String8.h>
 
 #include <cstdio>
 
@@ -47,22 +48,20 @@
     IMAGE_READER_MAX_NUM_PLANES = 3,
 };
 
-struct fields_t {
-    // For ImageReader class
-    jfieldID  imageReaderContext;
-    jmethodID postEvent;
-    // For SurfaceImage class
-    jfieldID  buffer;
-    jfieldID  timeStamp;
-};
+static struct {
+    jfieldID mNativeContext;
+    jmethodID postEventFromNative;
+} gImageReaderClassInfo;
 
-struct classInfo_t {
+static struct {
+    jfieldID mLockedBuffer;
+    jfieldID mTimestamp;
+} gSurfaceImageClassInfo;
+
+static struct {
     jclass clazz;
     jmethodID ctor;
-};
-
-static fields_t    fields;
-static classInfo_t surfPlaneClassInfo;
+} gSurfacePlaneClassInfo;
 
 // ----------------------------------------------------------------------------
 
@@ -79,9 +78,11 @@
 
     void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer);
 
+    void setCpuConsumer(const sp<CpuConsumer>& consumer) { mConsumer = consumer; }
     CpuConsumer* getCpuConsumer() { return mConsumer.get(); }
 
-    void setCpuConsumer(sp<CpuConsumer> consumer) { mConsumer = consumer; }
+    void setBufferQueue(const sp<BufferQueue>& bq) { mBufferQueue = bq; }
+    BufferQueue* getBufferQueue() { return mBufferQueue.get(); }
 
     void setBufferFormat(int format) { mFormat = format; }
     int getBufferFormat() { return mFormat; }
@@ -98,6 +99,7 @@
 
     List<CpuConsumer::LockedBuffer*> mBuffers;
     sp<CpuConsumer> mConsumer;
+    sp<BufferQueue> mBufferQueue;
     jobject mWeakThiz;
     jclass mClazz;
     int mFormat;
@@ -183,7 +185,7 @@
     bool needsDetach = false;
     JNIEnv* env = getJNIEnv(&needsDetach);
     if (env != NULL) {
-        env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+        env->CallStaticVoidMethod(mClazz, gImageReaderClassInfo.postEventFromNative, mWeakThiz);
     } else {
         ALOGW("onFrameAvailable event will not posted");
     }
@@ -200,7 +202,7 @@
 {
     JNIImageReaderContext *ctx;
     ctx = reinterpret_cast<JNIImageReaderContext *>
-              (env->GetLongField(thiz, fields.imageReaderContext));
+              (env->GetLongField(thiz, gImageReaderClassInfo.mNativeContext));
     return ctx;
 }
 
@@ -215,6 +217,17 @@
     return ctx->getCpuConsumer();
 }
 
+static BufferQueue* ImageReader_getBufferQueue(JNIEnv* env, jobject thiz)
+{
+    ALOGV("%s:", __FUNCTION__);
+    JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
+    if (ctx == NULL) {
+        jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
+        return NULL;
+    }
+    return ctx->getBufferQueue();
+}
+
 static void ImageReader_setNativeContext(JNIEnv* env,
         jobject thiz, sp<JNIImageReaderContext> ctx)
 {
@@ -226,18 +239,20 @@
     if (p) {
         p->decStrong((void*)ImageReader_setNativeContext);
     }
-    env->SetLongField(thiz, fields.imageReaderContext, reinterpret_cast<jlong>(ctx.get()));
+    env->SetLongField(thiz, gImageReaderClassInfo.mNativeContext,
+            reinterpret_cast<jlong>(ctx.get()));
 }
 
 static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image)
 {
-    return reinterpret_cast<CpuConsumer::LockedBuffer*>(env->GetLongField(image, fields.buffer));
+    return reinterpret_cast<CpuConsumer::LockedBuffer*>(
+            env->GetLongField(image, gSurfaceImageClassInfo.mLockedBuffer));
 }
 
 static void Image_setBuffer(JNIEnv* env, jobject thiz,
         const CpuConsumer::LockedBuffer* buffer)
 {
-    env->SetLongField(thiz, fields.buffer, reinterpret_cast<jlong>(buffer));
+    env->SetLongField(thiz, gSurfaceImageClassInfo.mLockedBuffer, reinterpret_cast<jlong>(buffer));
 }
 
 // Some formats like JPEG defined with different values between android.graphics.ImageFormat and
@@ -549,33 +564,37 @@
     jclass imageClazz = env->FindClass("android/media/ImageReader$SurfaceImage");
     LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
                         "can't find android/graphics/ImageReader$SurfaceImage");
-    fields.buffer = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J");
-    LOG_ALWAYS_FATAL_IF(fields.buffer == NULL,
+    gSurfaceImageClassInfo.mLockedBuffer = env->GetFieldID(
+            imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J");
+    LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mLockedBuffer == NULL,
                         "can't find android/graphics/ImageReader.%s",
                         ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID);
 
-    fields.timeStamp = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J");
-    LOG_ALWAYS_FATAL_IF(fields.timeStamp == NULL,
+    gSurfaceImageClassInfo.mTimestamp = env->GetFieldID(
+            imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J");
+    LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mTimestamp == NULL,
                         "can't find android/graphics/ImageReader.%s",
                         ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
 
-    fields.imageReaderContext = env->GetFieldID(clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J");
-    LOG_ALWAYS_FATAL_IF(fields.imageReaderContext == NULL,
+    gImageReaderClassInfo.mNativeContext = env->GetFieldID(
+            clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J");
+    LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.mNativeContext == NULL,
                         "can't find android/graphics/ImageReader.%s",
                           ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID);
 
-    fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
-                                              "(Ljava/lang/Object;)V");
-    LOG_ALWAYS_FATAL_IF(fields.postEvent == NULL,
+    gImageReaderClassInfo.postEventFromNative = env->GetStaticMethodID(
+            clazz, "postEventFromNative", "(Ljava/lang/Object;)V");
+    LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.postEventFromNative == NULL,
                         "can't find android/graphics/ImageReader.postEventFromNative");
 
     jclass planeClazz = env->FindClass("android/media/ImageReader$SurfaceImage$SurfacePlane");
     LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class");
     // FindClass only gives a local reference of jclass object.
-    surfPlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
-    surfPlaneClassInfo.ctor = env->GetMethodID(surfPlaneClassInfo.clazz, "<init>",
-                                               "(Landroid/media/ImageReader$SurfaceImage;III)V");
-    LOG_ALWAYS_FATAL_IF(surfPlaneClassInfo.ctor == NULL, "Can not find SurfacePlane constructor");
+    gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
+    gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>",
+            "(Landroid/media/ImageReader$SurfaceImage;III)V");
+    LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
+            "Can not find SurfacePlane constructor");
 }
 
 static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz,
@@ -604,6 +623,7 @@
     }
     sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
     ctx->setCpuConsumer(consumer);
+    ctx->setBufferQueue(bq);
     consumer->setFrameAvailableListener(ctx);
     ImageReader_setNativeContext(env, thiz, ctx);
     ctx->setBufferFormat(nativeFormat);
@@ -710,8 +730,8 @@
 
     int imageReaderWidth = ctx->getBufferWidth();
     int imageReaderHeight = ctx->getBufferHeight();
-    if ((imageReaderWidth != outputWidth) ||
-        (imageReaderHeight != outputHeight)) {
+    if (imageReaderWidth != outputWidth
+            || imageReaderHeight != outputHeight) {
         // Spew warning for now, since MediaCodec decoder has a bug to setup the right crop
         // TODO: make it throw exception once the decoder bug is fixed.
         ALOGW("Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
@@ -726,14 +746,18 @@
         // Throw exception
         ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
               buffer->format, ctx->getBufferFormat());
+        String8 msg;
+        msg.appendFormat("The producer output buffer format 0x%x doesn't "
+                "match the ImageReader's configured buffer format 0x%x.",
+                buffer->format, ctx->getBufferFormat());
         jniThrowException(env, "java/lang/UnsupportedOperationException",
-                          "The producer output buffer configuration doesn't match the ImageReader"
-                          "configured");
+                msg.string());
         return false;
     }
     // Set SurfaceImage instance member variables
     Image_setBuffer(env, image, buffer);
-    env->SetLongField(image, fields.timeStamp, static_cast<jlong>(buffer->timestamp));
+    env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
+            static_cast<jlong>(buffer->timestamp));
 
     return true;
 }
@@ -742,15 +766,14 @@
 {
     ALOGV("%s: ", __FUNCTION__);
 
-    CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz);
-    if (consumer == NULL) {
+    BufferQueue* bq = ImageReader_getBufferQueue(env, thiz);
+    if (bq == NULL) {
         jniThrowRuntimeException(env, "CpuConsumer is uninitialized");
         return NULL;
     }
 
     // Wrap the IGBP in a Java-language Surface.
-    return android_view_Surface_createFromIGraphicBufferProducer(env,
-                                                                 consumer->getProducerInterface());
+    return android_view_Surface_createFromIGraphicBufferProducer(env, bq);
 }
 
 static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx)
@@ -767,8 +790,8 @@
     rowStride = Image_imageGetRowStride(env, buffer, idx);
     pixelStride = Image_imageGetPixelStride(env, buffer, idx);
 
-    jobject surfPlaneObj = env->NewObject(surfPlaneClassInfo.clazz, surfPlaneClassInfo.ctor,
-                                          thiz, idx, rowStride, pixelStride);
+    jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz,
+            gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride);
 
     return surfPlaneObj;
 }
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
index 63fd16e..fdecda3 100644
--- a/media/mca/filterfw/native/core/gl_env.cpp
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -162,8 +162,7 @@
   // Create dummy surface using a GLConsumer
   sp<BufferQueue> bq = new BufferQueue();
   surfaceTexture_ = new GLConsumer(bq, 0);
-  window_ = new Surface(static_cast<sp<IGraphicBufferProducer> >(
-          surfaceTexture_->getBufferQueue()));
+  window_ = new Surface(static_cast<sp<IGraphicBufferProducer> >(bq));
 
   surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
   if (CheckEGLError("eglCreateWindowSurface")) return false;
diff --git a/packages/PrintSpooler/res/layout/generating_print_job_dialog.xml b/packages/PrintSpooler/res/layout/generating_print_job_dialog.xml
new file mode 100644
index 0000000..360f8434
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/generating_print_job_dialog.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/progress"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="16dip"
+    android:layout_gravity="center_horizontal"
+    style="?android:attr/progressBarStyleLarge">
+</ProgressBar>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity.xml b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
index a4105ea..1a8b0f1 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
@@ -14,274 +14,276 @@
      limitations under the License.
 -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:scrollbars="vertical">
+    android:orientation="vertical">
 
-    <GridLayout
-        android:layout_width="wrap_content"
+    <ScrollView
+        android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:columnCount="2">
+        android:scrollbars="vertical">
 
-        <!-- Destination -->
-
-        <Spinner
-            android:id="@+id/destination_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="0"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <!-- Copies -->
-
-        <view
-            class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
-            android:id="@+id/copies_edittext"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="2"
-            android:layout_column="0"
-            android:layout_gravity="bottom"
-            android:inputType="numberDecimal"
-            android:selectAllOnFocus="true"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </view>
-
-        <TextView
+        <GridLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="1"
-            android:layout_column="0"
-            android:layout_gravity="left|bottom"
-            android:text="@string/label_copies"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/copies_edittext">
-        </TextView>
+            android:orientation="vertical"
+            android:columnCount="2">
 
-        <!-- Paper size -->
+            <!-- Destination -->
 
-        <Spinner
-            android:id="@+id/paper_size_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="2"
-            android:layout_column="1"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
+            <Spinner
+                android:id="@+id/destination_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="0"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_row="1"
-            android:layout_column="1"
-            android:text="@string/label_paper_size"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/paper_size_spinner">
-        </TextView>
+            <!-- Copies -->
 
-        <!-- Color -->
+            <view
+                class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+                android:id="@+id/copies_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="0"
+                android:layout_gravity="bottom"
+                android:inputType="numberDecimal"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </view>
 
-        <Spinner
-            android:id="@+id/color_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="4"
-            android:layout_column="0"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="1"
+                android:layout_column="0"
+                android:layout_gravity="left|bottom"
+                android:text="@string/label_copies"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/copies_edittext">
+            </TextView>
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="3"
-            android:layout_column="0"
-            android:text="@string/label_color"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/color_spinner">
-        </TextView>
+            <!-- Paper size -->
 
-        <!-- Orientation -->
+            <Spinner
+                android:id="@+id/paper_size_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="1"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/orientation_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="4"
-            android:layout_column="1"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_row="1"
+                android:layout_column="1"
+                android:text="@string/label_paper_size"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/paper_size_spinner">
+            </TextView>
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="3"
-            android:layout_column="1"
-            android:text="@string/label_orientation"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/orientation_spinner">
-        </TextView>
+            <!-- Color -->
 
-        <!-- Pages -->
+            <Spinner
+                android:id="@+id/color_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="0"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/range_options_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="6"
-            android:layout_column="0"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="3"
+                android:layout_column="0"
+                android:text="@string/label_color"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/color_spinner">
+            </TextView>
 
-        <view
-            class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
-            android:id="@+id/page_range_edittext"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="6"
-            android:layout_column="1"
-            android:layout_gravity="bottom"
-            android:selectAllOnFocus="true"
-            android:minWidth="150dip"
-            android:hint="@string/pages_range_example"
-            android:inputType="textNoSuggestions"
-            android:visibility="gone"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </view>
+            <!-- Orientation -->
 
-        <TextView
-            android:id="@+id/page_range_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="5"
-            android:layout_column="0"
-            android:text="@string/label_pages"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/range_options_spinner">
-        </TextView>
+            <Spinner
+                android:id="@+id/orientation_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="1"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
 
-        <!-- Print pereview  -->
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="3"
+                android:layout_column="1"
+                android:text="@string/label_orientation"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/orientation_spinner">
+            </TextView>
 
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="7"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
+            <!-- Pages -->
 
-        <Button
-            android:id="@+id/print_preview_button"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="8"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:text="@string/print_preview"
-            android:gravity="left|center_vertical"
-            android:background="?android:attr/selectableItemBackground"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Button>
+            <Spinner
+                android:id="@+id/range_options_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="6"
+                android:layout_column="0"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
 
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="32dip"
-            android:layout_row="9"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
+            <view
+                class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+                android:id="@+id/page_range_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="6"
+                android:layout_column="1"
+                android:layout_gravity="bottom"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip"
+                android:hint="@string/pages_range_example"
+                android:inputType="textNoSuggestions"
+                android:visibility="gone"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </view>
 
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_row="10"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
+            <TextView
+                android:id="@+id/page_range_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="5"
+                android:layout_column="0"
+                android:text="@string/label_pages"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/range_options_spinner">
+            </TextView>
 
-        <Button
-            android:id="@+id/print_button"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_row="11"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:padding="0dip"
-            android:text="@string/print_button"
-            android:background="?android:attr/selectableItemBackground"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Button>
+            <!-- Print pereview  -->
 
-    </GridLayout>
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="7"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
 
-</ScrollView>
+            <Button
+                android:id="@+id/print_preview_button"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="8"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:text="@string/print_preview"
+                android:gravity="left|center_vertical"
+                android:background="?android:attr/selectableItemBackground"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Button>
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="32dip"
+                android:layout_row="9"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_row="10"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
+
+        </GridLayout>
+
+    </ScrollView>
+
+    <Button
+        android:id="@+id/print_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="fill_horizontal"
+        android:text="@string/print_button"
+        style="?android:attr/buttonBarButtonStyle">
+    </Button>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index f400f21..fbddf43 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -55,10 +55,10 @@
     <!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] -->
     <string name="page_count_unknown">unknown</string>
 
-    <!-- Notifications -->
+    <!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
+    <string name="generating_print_job">Generating print job</string>
 
-    <!-- Template for the notificaiton label for a queued print job. [CHAR LIMIT=25] -->
-    <string name="queued_notification_title_template">Queued <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
+    <!-- Notifications -->
 
     <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
     <string name="printing_notification_title_template">Printing <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
@@ -75,6 +75,9 @@
     <!-- Label for the notification button for restrating a filed print job. [CHAR LIMIT=25] -->
     <string name="restart">Restart</string>
 
+    <!-- Message that there is no connection to a printer. [CHAR LIMIT=40] -->
+    <string name="no_connection_to_printer">No connection to printer</string>
+
     <!-- Arrays -->
 
     <!-- Color mode labels. -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
index 14a96c9..8b49c0e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
@@ -65,10 +65,6 @@
         }
         switch (printJob.getState()) {
             case PrintJobInfo.STATE_QUEUED: {
-                createQueuingNotificaiton(printJob);
-            } break;
-
-            case PrintJobInfo.STATE_STARTED: {
                 createPrintingNotificaiton(printJob);
             } break;
 
@@ -83,22 +79,6 @@
         }
     }
 
-    private void createQueuingNotificaiton(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                // TODO: Use appropriate icon when assets are ready
-                .setSmallIcon(android.R.drawable.ic_secure)
-                .setContentTitle(mContext.getString(R.string.queued_notification_title_template,
-                        printJob.getLabel()))
-                // TODO: Use appropriate icon when assets are ready
-                .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
-                        createCancelIntent(printJob))
-                .setContentText(printJob.getPrinterId().getPrinterName())
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true);
-        mNotificationManager.notify(printJob.getId(), builder.build());
-    }
-
     private void createPrintingNotificaiton(PrintJobInfo printJob) {
         Notification.Builder builder = new Notification.Builder(mContext)
                 // TODO: Use appropriate icon when assets are ready
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 15c2b2f..00a76b8 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -17,7 +17,11 @@
 package com.android.printspooler;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -66,6 +70,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -137,6 +142,8 @@
 
     private IBinder mIPrintDocumentAdapter;
 
+    private Dialog mGeneratingPrintJobDialog;
+
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
@@ -167,17 +174,14 @@
         mController = new PrintController(new RemotePrintDocumentAdapter(
                 IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
                 mSpooler.generateFileForPrintJob(mPrintJobId)));
-    }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
         try {
             mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
         } catch (RemoteException re) {
             finish();
             return;
         }
+
         mController.initialize();
         mEditor.initialize();
         mPrinterDiscoveryObserver = new PrinterDiscoveryObserver(mEditor, getMainLooper());
@@ -185,27 +189,29 @@
     }
 
     @Override
-    protected void onPause() {
+    protected void onDestroy() {
+        // We can safely do the work in here since at this point
+        // the system is bound to our (spooler) process which
+        // guarantees that this process will not be killed.
         mSpooler.stopPrinterDiscovery();
         mPrinterDiscoveryObserver.destroy();
         mPrinterDiscoveryObserver = null;
-        if (mController.isCancelled() || mController.isFailed()) {
+        if (mController.hasStarted()) {
+            mController.finish();
+        }
+        if (mEditor.isPrintConfirmed() && mController.isFinished()) {
+            mSpooler.setPrintJobState(mPrintJobId,
+                    PrintJobInfo.STATE_QUEUED, null);
+        } else {
             mSpooler.setPrintJobState(mPrintJobId,
                     PrintJobInfo.STATE_CANCELED, null);
-        } else if (mController.hasStarted()) {
-            mController.finish();
-            if (mEditor.isPrintConfirmed()) {
-                if (mController.isFinished()) {
-                    mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_QUEUED, null);
-                } else {
-                    mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_CANCELED, null);
-                }
-            }
         }
         mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
-        super.onPause();
+        if (mGeneratingPrintJobDialog != null) {
+            mGeneratingPrintJobDialog.dismiss();
+            mGeneratingPrintJobDialog = null;
+        }
+        super.onDestroy();
     }
 
     public boolean onTouchEvent(MotionEvent event) {
@@ -243,56 +249,54 @@
         return !mOldPrintAttributes.equals(mCurrPrintAttributes);
     }
 
+    private void showGeneratingPrintJobUi() {
+        getWindow().getDecorView().setVisibility(View.GONE);
+
+        DialogFragment fragment = new DialogFragment() {
+            @Override
+            public Dialog onCreateDialog(Bundle savedInstanceState) {
+                return new AlertDialog.Builder(PrintJobConfigActivity.this)
+                    .setTitle(getString(R.string.generating_print_job))
+                    .setView(PrintJobConfigActivity.this.getLayoutInflater().inflate(
+                            R.layout.generating_print_job_dialog, null))
+                    .setCancelable(false)
+                    .setPositiveButton(getString(R.string.cancel),
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    mEditor.cancel();
+                                    PrintJobConfigActivity.this.finish();
+                                }
+                            })
+                    .create();
+            }
+        };
+        fragment.show(getFragmentManager(), getString(R.string.generating_print_job));
+    }
+
     private class PrintController {
         private final AtomicInteger mRequestCounter = new AtomicInteger();
 
         private final RemotePrintDocumentAdapter mRemotePrintAdapter;
 
-        private final Handler mHandler;
+        private final Bundle mMetadata;
+
+        private final ControllerHandler mHandler;
+
+        private final LayoutResultCallback mLayoutResultCallback;
+
+        private final WriteResultCallback mWriteResultCallback;
 
         private int mControllerState = CONTROLLER_STATE_INITIALIZED;
 
         private PageRange[] mRequestedPages;
 
-        private Bundle mMetadata = new Bundle();
-
-        private final ILayoutResultCallback mILayoutResultCallback =
-                new ILayoutResultCallback.Stub() {
-            @Override
-            public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FINISHED, changed ? 1 : 0,
-                            0, info).sendToTarget();
-                }
-            }
-
-            @Override
-            public void onLayoutFailed(CharSequence error, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FAILED, error).sendToTarget();
-                }
-            }
-        };
-
-        private IWriteResultCallback mIWriteResultCallback = new IWriteResultCallback.Stub() {
-            @Override
-            public void onWriteFinished(PageRange[] pages, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FINISHED, pages).sendToTarget();
-                }
-            }
-
-            @Override
-            public void onWriteFailed(CharSequence error, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FAILED, error).sendToTarget();
-                }
-            }
-        };
-
         public PrintController(RemotePrintDocumentAdapter adapter) {
             mRemotePrintAdapter = adapter;
-            mHandler = new MyHandler(Looper.getMainLooper());
+            mMetadata = new Bundle();
+            mHandler = new ControllerHandler(getMainLooper());
+            mLayoutResultCallback = new LayoutResultCallback(mHandler);
+            mWriteResultCallback = new WriteResultCallback(mHandler);
         }
 
         public void initialize() {
@@ -311,10 +315,6 @@
             return (mControllerState == CONTROLLER_STATE_FINISHED);
         }
 
-        public boolean isFailed() {
-            return (mControllerState == CONTROLLER_STATE_FAILED);
-        }
-
         public boolean hasStarted() {
             return mControllerState >= CONTROLLER_STATE_STARTED;
         }
@@ -338,7 +338,7 @@
                 // If the attributes changes, then we do not do a layout but may
                 // have to ask the app to write some pages. Hence, pretend layout
                 // completed and nothing changed, so we handle writing as usual.
-                handleOnLayoutFinished(mDocument.info, false);
+                handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
             } else {
                 mSpooler.setPrintJobAttributesNoPersistence(mPrintJobId, mCurrPrintAttributes);
 
@@ -348,7 +348,7 @@
                 mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;
 
                 mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
-                        mILayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
+                        mLayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
 
                 mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
             }
@@ -359,7 +359,12 @@
             mRemotePrintAdapter.finish();
         }
 
-        private void handleOnLayoutFinished(PrintDocumentInfo info, boolean layoutChanged) {
+        private void handleOnLayoutFinished(PrintDocumentInfo info,
+                boolean layoutChanged, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
+
             if (isCancelled()) {
                 if (mEditor.isDone()) {
                     PrintJobConfigActivity.this.finish();
@@ -421,18 +426,25 @@
 
             // Request a write of the pages of interest.
             mControllerState = CONTROLLER_STATE_WRITE_STARTED;
-            mRemotePrintAdapter.write(mRequestedPages, mIWriteResultCallback,
+            mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback,
                     mRequestCounter.incrementAndGet());
         }
 
-        private void handleOnLayoutFailed(CharSequence error) {
+        private void handleOnLayoutFailed(CharSequence error, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
             mControllerState = CONTROLLER_STATE_FAILED;
             // TODO: We need some UI for announcing an error.
             Log.e(LOG_TAG, "Error during layout: " + error);
             PrintJobConfigActivity.this.finish();
         }
 
-        private void handleOnWriteFinished(PageRange[] pages) {
+        private void handleOnWriteFinished(PageRange[] pages, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
+
             if (isCancelled()) {
                 if (mEditor.isDone()) {
                     PrintJobConfigActivity.this.finish();
@@ -490,19 +502,22 @@
             }
         }
 
-        private void handleOnWriteFailed(CharSequence error) {
+        private void handleOnWriteFailed(CharSequence error, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
             mControllerState = CONTROLLER_STATE_FAILED;
             Log.e(LOG_TAG, "Error during write: " + error);
             PrintJobConfigActivity.this.finish();
         }
 
-        private final class MyHandler extends Handler {
+        private final class ControllerHandler extends Handler {
             public static final int MSG_ON_LAYOUT_FINISHED = 1;
             public static final int MSG_ON_LAYOUT_FAILED = 2;
             public static final int MSG_ON_WRITE_FINISHED = 3;
             public static final int MSG_ON_WRITE_FAILED = 4;
 
-            public MyHandler(Looper looper) {
+            public ControllerHandler(Looper looper) {
                 super(looper, null, false);
             }
 
@@ -512,28 +527,84 @@
                     case MSG_ON_LAYOUT_FINISHED: {
                         PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
                         final boolean changed = (message.arg1 == 1);
-                        mController.handleOnLayoutFinished(info, changed);
+                        final int sequence = message.arg2;
+                        handleOnLayoutFinished(info, changed, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        mController.handleOnLayoutFailed(error);
+                        final int sequence = message.arg1;
+                        handleOnLayoutFailed(error, sequence);
                     } break;
 
                     case MSG_ON_WRITE_FINISHED: {
                         PageRange[] pages = (PageRange[]) message.obj;
-                        mController.handleOnWriteFinished(pages);
+                        final int sequence = message.arg1;
+                        handleOnWriteFinished(pages, sequence);
                     } break;
 
                     case MSG_ON_WRITE_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        mController.handleOnWriteFailed(error);
+                        final int sequence = message.arg1;
+                        handleOnWriteFailed(error, sequence);
                     } break;
                 }
             }
         }
     }
 
+    private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
+        private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
+
+        public LayoutResultCallback(PrintController.ControllerHandler handler) {
+            mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
+        }
+
+        @Override
+        public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FINISHED,
+                        changed ? 1 : 0, sequence, info).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onLayoutFailed(CharSequence error, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FAILED,
+                        sequence, 0, error).sendToTarget();
+            }
+        }
+    }
+
+    private static final class WriteResultCallback extends IWriteResultCallback.Stub {
+        private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
+
+        public WriteResultCallback(PrintController.ControllerHandler handler) {
+            mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
+        }
+
+        @Override
+        public void onWriteFinished(PageRange[] pages, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FINISHED,
+                        sequence, 0, pages).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onWriteFailed(CharSequence error, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FAILED,
+                    sequence, 0, error).sendToTarget();
+            }
+        }
+    }
+
     private final class Editor {
         private final EditText mCopiesEditText;
 
@@ -837,6 +908,7 @@
                     mEditor.confirmPrint();
                     updateUi();
                     mController.update();
+                    showGeneratingPrintJobUi();
                 }
             });
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 0bc20a3..00e05bb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -177,17 +177,15 @@
             // Update the notification.
             mNotificationController.onPrintJobStateChanged(printJob);
 
-            //TODO: Figure out what the right policy for read print jobs is.
-
             switch (printJob.getState()) {
-                case PrintJobInfo.STATE_QUEUED: {
-                    // Notify that we have a queued job.
-                    mService.onPrintJobQueued(new PrintJobInfo(printJob));
-                } break;
-
+                case PrintJobInfo.STATE_QUEUED:
                 case PrintJobInfo.STATE_STARTED: {
-                    // We really want to restart this print job.
-                    setPrintJobState(printJob.getId(), PrintJobInfo.STATE_QUEUED, null);
+                    // We have a print job that was queued or started in the past
+                    // but the device battery died or a crash occurred. In this case
+                    // we assume the print job failed and let the user decide whether
+                    // to restart the job or just
+                    setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
+                            mService.getString(R.string.no_connection_to_printer));
                 } break;
             }
         }
diff --git a/packages/SystemUI/res/values-land/refs.xml b/packages/SystemUI/res/values-land/refs.xml
index f5e79b9..62fb77d 100644
--- a/packages/SystemUI/res/values-land/refs.xml
+++ b/packages/SystemUI/res/values-land/refs.xml
@@ -16,5 +16,5 @@
 */
 -->
 <resources>
-    <item type="string" name="hideybar_confirmation_message">@string/hideybar_confirmation_message_long</item>
+    <item type="string" name="hiding_navigation_confirmation_message">@string/hiding_navigation_confirmation_message_long</item>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/refs.xml b/packages/SystemUI/res/values-sw600dp-port/refs.xml
index f5e79b9..62fb77d 100644
--- a/packages/SystemUI/res/values-sw600dp-port/refs.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/refs.xml
@@ -16,5 +16,5 @@
 */
 -->
 <resources>
-    <item type="string" name="hideybar_confirmation_message">@string/hideybar_confirmation_message_long</item>
+    <item type="string" name="hiding_navigation_confirmation_message">@string/hiding_navigation_confirmation_message_long</item>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b99d20c..0073e60 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -502,8 +502,8 @@
     <string name="status_bar_help_text">Access them anytime by swiping down.\nSwipe down again for system controls.</string>
 
     <!-- Toast bar message when hiding the navigation bar -->
-    <string name="hideybar_confirmation_message">Swipe edge of screen to reveal bar</string>
+    <string name="hiding_navigation_confirmation_message">Swipe edge of screen to reveal bar</string>
 
     <!-- Longer version of toast bar message when hiding the navigation bar (if room) -->
-    <string name="hideybar_confirmation_message_long">Swipe from edge of screen to reveal system bar</string>
+    <string name="hiding_navigation_confirmation_message_long">Swipe from edge of screen to reveal system bar</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
index efbee5b..610e42b 100644
--- a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
+++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
@@ -29,18 +29,18 @@
 import android.view.View;
 import android.view.WindowManager;
 
-import com.android.internal.os.ProcessStats;
+import com.android.internal.os.ProcessCpuTracker;
 
 public class LoadAverageService extends Service {
     private View mView;
 
-    private static final class Stats extends ProcessStats {
+    private static final class CpuTracker extends ProcessCpuTracker {
         String mLoadText;
         int mLoadWidth;
 
         private final Paint mPaint;
 
-        Stats(Paint paint) {
+        CpuTracker(Paint paint) {
             super(false);
             mPaint = paint;
         }
@@ -70,7 +70,7 @@
             }
         };
 
-        private final Stats mStats;
+        private final CpuTracker mStats;
 
         private Paint mLoadPaint;
         private Paint mAddedPaint;
@@ -150,7 +150,7 @@
             float descent = mLoadPaint.descent();
             mFH = (int)(descent - mAscent + .5f);
 
-            mStats = new Stats(mLoadPaint);
+            mStats = new CpuTracker(mLoadPaint);
             mStats.init();
             updateDisplay();
         }
@@ -179,7 +179,7 @@
             final int W = mNeededWidth;
             final int RIGHT = getWidth()-1;
 
-            final Stats stats = mStats;
+            final CpuTracker stats = mStats;
             final int userTime = stats.getLastUserTime();
             final int systemTime = stats.getLastSystemTime();
             final int iowaitTime = stats.getLastIoWaitTime();
@@ -226,7 +226,7 @@
 
             int N = stats.countWorkingStats();
             for (int i=0; i<N; i++) {
-                Stats.Stats st = stats.getWorkingStats(i);
+                CpuTracker.Stats st = stats.getWorkingStats(i);
                 y += mFH;
                 top += mFH;
                 bottom += mFH;
@@ -259,12 +259,12 @@
         }
 
         void updateDisplay() {
-            final Stats stats = mStats;
+            final CpuTracker stats = mStats;
             final int NW = stats.countWorkingStats();
 
             int maxWidth = stats.mLoadWidth;
             for (int i=0; i<NW; i++) {
-                Stats.Stats st = stats.getWorkingStats(i);
+                CpuTracker.Stats st = stats.getWorkingStats(i);
                 if (st.nameWidth > maxWidth) {
                     maxWidth = st.nameWidth;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d1ccde1..3a81454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -127,11 +127,15 @@
     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
     private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
 
-    private static final int STATUS_OR_NAV_OVERLAY =
-            View.STATUS_BAR_OVERLAY | View.NAVIGATION_BAR_OVERLAY;
+    private static final int STATUS_OR_NAV_TRANSIENT =
+            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
     private static final float TRANSPARENT_ALPHA = 0.7f;
 
+    private static final int BAR_MODE_NORMAL = 0;
+    private static final int BAR_MODE_TRANSIENT = 1;
+    private static final int BAR_MODE_TRANSPARENT = 2;
+
     // fling gesture tuning parameters, scaled to display density
     private float mSelfExpandVelocityPx; // classic value: 2000px/s
     private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -306,33 +310,33 @@
         }
     };
 
-    private Toast mHideybarConfirmation;
-    private boolean mHideybarConfirmationDismissed;
+    private Toast mHidingNavigationConfirmation;
+    private boolean mHidingNavigationConfirmationDismissed;
 
-    private final View.OnTouchListener mDismissHideybarConfirmationOnTouchOutside =
+    private final View.OnTouchListener mDismissHidingNavigationConfirmationOnTouchOutside =
             new View.OnTouchListener() {
                 @Override
                 public boolean onTouch(View v, MotionEvent event) {
                     if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
-                        dismissHideybarConfirmation();
+                        dismissHidingNavigationConfirmation();
                     }
                     return false;
                 }
             };
 
-    private final Runnable mHideybarConfirmationAction = new Runnable() {
+    private final Runnable mHidingNavigationConfirmationAction = new Runnable() {
         @Override
         public void run() {
-            if (mHideybarConfirmation != null) {
+            if (mHidingNavigationConfirmation != null) {
                 final boolean isGloballyConfirmed = Prefs.read(mContext)
-                        .getBoolean(Prefs.HIDEYBAR_CONFIRMED, false);
+                        .getBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, false);
                 if (!isGloballyConfirmed) {
                     // user pressed button, consider this a confirmation
                     Prefs.edit(mContext)
-                            .putBoolean(Prefs.HIDEYBAR_CONFIRMED, true)
+                            .putBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, true)
                             .apply();
                 }
-                dismissHideybarConfirmation();
+                dismissHidingNavigationConfirmation();
             }
         }
     };
@@ -342,7 +346,7 @@
     private final Runnable mAutohide = new Runnable() {
         @Override
         public void run() {
-            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_OVERLAY;
+            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
             if (mSystemUiVisibility != requested) {
                 notifyUiVisibilityChanged(requested);
             }
@@ -1892,8 +1896,9 @@
         if (diff != 0) {
             mSystemUiVisibility = newVal;
 
-            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
-                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
+            // update low profile
+            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
                 if (lightsOut) {
                     animateCollapsePanels();
                     if (mTicking) {
@@ -1908,70 +1913,93 @@
                 setStatusBarLowProfile(lightsOut);
             }
 
-            boolean sbOverlayChanged = 0 != (diff & View.STATUS_BAR_OVERLAY);
-            boolean nbOverlayChanged = 0 != (diff & View.NAVIGATION_BAR_OVERLAY);
-            if (sbOverlayChanged || nbOverlayChanged) {
-                boolean sbOverlay = 0 != (vis & View.STATUS_BAR_OVERLAY);
-                boolean nbOverlay = 0 != (vis & View.NAVIGATION_BAR_OVERLAY);
-                if (sbOverlayChanged) {
-                    setTransparent(mStatusBarView, sbOverlay);
-                }
-                if (nbOverlayChanged) {
-                    setTransparent(mNavigationBarView, nbOverlay);
-                }
-                if (sbOverlayChanged && sbOverlay || nbOverlayChanged && nbOverlay) {
+            // update status bar mode
+            int sbMode = updateBarMode(oldVal, newVal, mStatusBarView,
+                    View.STATUS_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS);
+
+            // update navigation bar mode
+            int nbMode = updateBarMode(oldVal, newVal, mNavigationBarView,
+                    View.NAVIGATION_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION);
+
+            if (sbMode != -1 || nbMode != -1) {
+                // update transient bar autohide
+                if (sbMode == BAR_MODE_TRANSIENT || nbMode == BAR_MODE_TRANSIENT) {
                     scheduleAutohide();
                 } else {
                     cancelAutohide();
                 }
             }
+
+            // update hiding navigation confirmation
             if (mNavigationBarView != null) {
-                int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
-                boolean oldVisible =  (oldVal & flags) == flags;
-                boolean newVisible = (newVal & flags) == flags;
-                if (!oldVisible && newVisible) {
-                    mHideybarConfirmationDismissed = false;
+                final int hidingNav =
+                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT;
+                boolean oldHidingNav = (oldVal & hidingNav) != 0;
+                boolean newHidingNav = (newVal & hidingNav) != 0;
+                if (!oldHidingNav && newHidingNav) {
+                    mHidingNavigationConfirmationDismissed = false;
                 }
-                setHideybarConfirmationVisible(newVisible);
+                setHidingNavigationConfirmationVisible(newHidingNav);
             }
+
+            // send updated sysui visibility to window manager
             notifyUiVisibilityChanged(mSystemUiVisibility);
         }
     }
 
-    private void dismissHideybarConfirmation() {
-        if (mHideybarConfirmation != null) {
-            mHideybarConfirmationDismissed = true;
-            mHideybarConfirmation.cancel();
-            mHideybarConfirmation = null;
+    private int updateBarMode(int oldVis, int newVis, View view,
+            int transientFlag, int transparentFlag) {
+        final int oldMode = barMode(oldVis, transientFlag, transparentFlag);
+        final int newMode = barMode(newVis, transientFlag, transparentFlag);
+        if (oldMode == newMode) {
+            return -1; // no mode change
+        }
+        setTransparent(view, newMode != BAR_MODE_NORMAL);
+        return newMode;
+    }
+
+    private int barMode(int vis, int transientFlag, int transparentFlag) {
+        return (vis & transientFlag) != 0 ? BAR_MODE_TRANSIENT
+                : (vis & transparentFlag) != 0 ? BAR_MODE_TRANSPARENT
+                : BAR_MODE_NORMAL;
+    }
+
+    private void dismissHidingNavigationConfirmation() {
+        if (mHidingNavigationConfirmation != null) {
+            mHidingNavigationConfirmationDismissed = true;
+            mHidingNavigationConfirmation.cancel();
+            mHidingNavigationConfirmation = null;
         }
     }
 
-    private void setHideybarConfirmationVisible(boolean visible) {
-        if (DEBUG) Log.d(TAG, "setHideybarConfirmationVisible " + visible);
-        if (visible && mHideybarConfirmation == null && !mHideybarConfirmationDismissed) {
+    private void setHidingNavigationConfirmationVisible(boolean visible) {
+        if (DEBUG) Log.d(TAG, "setHidingNavigationConfirmationVisible " + visible);
+        if (visible &&
+                mHidingNavigationConfirmation == null && !mHidingNavigationConfirmationDismissed) {
             // create the confirmation toast bar
-            int msg = R.string.hideybar_confirmation_message;
-            mHideybarConfirmation = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE)
-                    .setAction(com.android.internal.R.string.ok, mHideybarConfirmationAction);
-            View v = mHideybarConfirmation.getView();
+            int msg = R.string.hiding_navigation_confirmation_message;
+            mHidingNavigationConfirmation = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE)
+                    .setAction(com.android.internal.R.string.ok,
+                            mHidingNavigationConfirmationAction);
+            View v = mHidingNavigationConfirmation.getView();
             v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
             boolean isGloballyConfirmed = Prefs.read(mContext)
-                    .getBoolean(Prefs.HIDEYBAR_CONFIRMED, false);
+                    .getBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, false);
             if (isGloballyConfirmed) {
                 // dismiss on outside touch if globally confirmed
-                v.setOnTouchListener(mDismissHideybarConfirmationOnTouchOutside);
+                v.setOnTouchListener(mDismissHidingNavigationConfirmationOnTouchOutside);
             }
             // position at the bottom like normal toasts, but use top gravity
             // to avoid jumping around when showing/hiding the nav bar
             v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
             int offsetY = mContext.getResources().getDimensionPixelSize(
                     com.android.internal.R.dimen.toast_y_offset);
-            mHideybarConfirmation.setGravity(Gravity.TOP,
+            mHidingNavigationConfirmation.setGravity(Gravity.TOP,
                     0, mCurrentDisplaySize.y - v.getMeasuredHeight() / 2 - offsetY);
             // show the confirmation
-            mHideybarConfirmation.show();
+            mHidingNavigationConfirmation.show();
         } else if (!visible) {
-            dismissHideybarConfirmation();
+            dismissHidingNavigationConfirmation();
         }
     }
 
@@ -1985,7 +2013,7 @@
     @Override
     public void suspendAutohide() {
         mHandler.removeCallbacks(mAutohide);
-        mAutohideSuspended = 0 != (mSystemUiVisibility & STATUS_OR_NAV_OVERLAY);
+        mAutohideSuspended = 0 != (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT);
     }
 
     private void cancelAutohide() {
@@ -1999,7 +2027,7 @@
     }
 
     private void checkUserAutohide(View v, MotionEvent event) {
-        if ((mSystemUiVisibility & STATUS_OR_NAV_OVERLAY) != 0  // an overlay bar is revealed
+        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
                 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
                 && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
                 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
index d03f6ce..3d51f20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
@@ -25,7 +25,7 @@
     public static final String SHOWN_COMPAT_MODE_HELP = "shown_compat_mode_help";
     public static final String SHOWN_QUICK_SETTINGS_HELP = "shown_quick_settings_help";
 
-    public static final String HIDEYBAR_CONFIRMED = "hideybar_confirmed";
+    public static final String HIDING_NAVIGATION_CONFIRMED = "hiding_navigation_confirmed";
 
     public static SharedPreferences read(Context context) {
         return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 2dfe6af..f83b017 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -164,7 +164,10 @@
      * of the screen to change.
      */
     static final int SYSTEM_UI_CHANGING_LAYOUT =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
+              View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+            | View.SYSTEM_UI_FLAG_FULLSCREEN
+            | View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS
+            | View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION;
 
     /**
      * Keyguard stuff
@@ -526,7 +529,7 @@
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
-                    OverlayTesting.ENABLED_SETTING), false, this,
+                    ImmersiveModeTesting.ENABLED_SETTING), false, this,
                     UserHandle.USER_ALL);
             updateSettings();
         }
@@ -550,11 +553,11 @@
     }
     MyOrientationListener mOrientationListener;
 
-    private static final int HIDEYBAR_NONE = 0;
-    private static final int HIDEYBAR_SHOWING = 1;
-    private static final int HIDEYBAR_HIDING = 2;
-    private int mStatusHideybar;
-    private int mNavigationHideybar;
+    private static final int TRANSIENT_BAR_NONE = 0;
+    private static final int TRANSIENT_BAR_SHOWING = 1;
+    private static final int TRANSIENT_BAR_HIDING = 2;
+    private int mStatusTransientBar;
+    private int mNavigationTransientBar;
 
     private SystemGesturesPointerEventListener mSystemGestures;
 
@@ -917,25 +920,25 @@
                     @Override
                     public void onSwipeFromTop() {
                         if (mStatusBar != null) {
-                            requestHideybars(mStatusBar);
+                            requestTransientBars(mStatusBar);
                         }
                     }
                     @Override
                     public void onSwipeFromBottom() {
                         if (mNavigationBar != null && mNavigationBarOnBottom) {
-                            requestHideybars(mNavigationBar);
+                            requestTransientBars(mNavigationBar);
                         }
                     }
                     @Override
                     public void onSwipeFromRight() {
                         if (mNavigationBar != null && !mNavigationBarOnBottom) {
-                            requestHideybars(mNavigationBar);
+                            requestTransientBars(mNavigationBar);
                         }
                     }
                     @Override
                     public void onDebug() {
-                        if (OverlayTesting.enabled) {
-                            OverlayTesting.toggleForceOverlay(mFocusedWindow, mContext);
+                        if (ImmersiveModeTesting.enabled) {
+                            ImmersiveModeTesting.toggleForceImmersiveMode(mFocusedWindow, mContext);
                         }
                     }
                 });
@@ -1152,8 +1155,8 @@
                 mHasSoftInput = hasSoftInput;
                 updateRotation = true;
             }
-            OverlayTesting.enabled = Settings.System.getIntForUser(resolver,
-                    OverlayTesting.ENABLED_SETTING, 0, UserHandle.USER_CURRENT) != 0;
+            ImmersiveModeTesting.enabled = Settings.System.getIntForUser(resolver,
+                    ImmersiveModeTesting.ENABLED_SETTING, 0, UserHandle.USER_CURRENT) != 0;
         }
         if (updateRotation) {
             updateRotation(true);
@@ -2541,14 +2544,14 @@
 
     @Override
     public int adjustSystemUiVisibilityLw(int visibility) {
-        if (mStatusBar != null && mStatusHideybar == HIDEYBAR_SHOWING &&
-                0 == (visibility & View.STATUS_BAR_OVERLAY)) {
-            mStatusHideybar = HIDEYBAR_HIDING;
+        if (mStatusBar != null && mStatusTransientBar == TRANSIENT_BAR_SHOWING &&
+                0 == (visibility & View.STATUS_BAR_TRANSIENT)) {
+            mStatusTransientBar = TRANSIENT_BAR_HIDING;
             setBarShowingLw(mStatusBar, false);
         }
-        if (mNavigationBar != null && mNavigationHideybar == HIDEYBAR_SHOWING &&
-                0 == (visibility & View.NAVIGATION_BAR_OVERLAY)) {
-            mNavigationHideybar = HIDEYBAR_HIDING;
+        if (mNavigationBar != null && mNavigationTransientBar == TRANSIENT_BAR_SHOWING &&
+                0 == (visibility & View.NAVIGATION_BAR_TRANSIENT)) {
+            mNavigationTransientBar = TRANSIENT_BAR_HIDING;
             setBarShowingLw(mNavigationBar, false);
         }
         // Reset any bits in mForceClearingStatusBarVisibility that
@@ -2678,14 +2681,17 @@
         if (isDefaultDisplay) {
             // For purposes of putting out fake window up to steal focus, we will
             // drive nav being hidden only by whether it is requested.
-            boolean navVisible = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
-            boolean overlayAllowed = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_ALLOW_OVERLAY) != 0;
+            final int sysui = mLastSystemUiFlags;
+            boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+            boolean navTransparent = (sysui & View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION) != 0;
+            boolean transientAllowed = (sysui & View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT) != 0;
+            navTransparent &= !transientAllowed;  // transient trumps transparent
 
             // When the navigation bar isn't visible, we put up a fake
             // input window to catch all touch events.  This way we can
             // detect when the user presses anywhere to bring back the nav
             // bar and ensure the application doesn't see the event.
-            if (navVisible || overlayAllowed) {
+            if (navVisible || transientAllowed) {
                 if (mHideNavFakeWindow != null) {
                     mHideNavFakeWindow.dismiss();
                     mHideNavFakeWindow = null;
@@ -2704,7 +2710,7 @@
 
             boolean updateSysUiVisibility = false;
             if (mNavigationBar != null) {
-                boolean navBarHideyShowing = mNavigationHideybar == HIDEYBAR_SHOWING;
+                boolean transientNavBarShowing = mNavigationTransientBar == TRANSIENT_BAR_SHOWING;
                 // Force the navigation bar to its appropriate place and
                 // size.  We need to do this directly, instead of relying on
                 // it to bubble up from the nav bar, because this needs to
@@ -2716,7 +2722,7 @@
                             - mNavigationBarHeightForRotation[displayRotation];
                     mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
                     mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
-                    if (navBarHideyShowing) {
+                    if (transientNavBarShowing || navTransparent) {
                         setBarShowingLw(mNavigationBar, true);
                     } else if (navVisible) {
                         setBarShowingLw(mNavigationBar, true);
@@ -2727,8 +2733,8 @@
                         // We currently want to hide the navigation UI.
                         setBarShowingLw(mNavigationBar, false);
                     }
-                    if (navVisible && !mNavigationBar.isAnimatingLw()) {
-                        // If the nav bar is currently requested to be visible,
+                    if (navVisible && !navTransparent && !mNavigationBar.isAnimatingLw()) {
+                        // If the opaque nav bar is currently requested to be visible,
                         // and not in the process of animating on or off, then
                         // we can tell the app that it is covered by it.
                         mSystemBottom = mTmpNavigationFrame.top;
@@ -2739,7 +2745,7 @@
                             - mNavigationBarWidthForRotation[displayRotation];
                     mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
                     mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
-                    if (navBarHideyShowing) {
+                    if (transientNavBarShowing || navTransparent) {
                         setBarShowingLw(mNavigationBar, true);
                     } else if (navVisible) {
                         setBarShowingLw(mNavigationBar, true);
@@ -2750,7 +2756,7 @@
                         // We currently want to hide the navigation UI.
                         setBarShowingLw(mNavigationBar, false);
                     }
-                    if (navVisible && !mNavigationBar.isAnimatingLw()) {
+                    if (navVisible && !navTransparent && !mNavigationBar.isAnimatingLw()) {
                         // If the nav bar is currently requested to be visible,
                         // and not in the process of animating on or off, then
                         // we can tell the app that it is covered by it.
@@ -2768,9 +2774,9 @@
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                         mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
-                if (mNavigationHideybar == HIDEYBAR_HIDING && !mNavigationBar.isVisibleLw()) {
+                if (mNavigationTransientBar == TRANSIENT_BAR_HIDING && !mNavigationBar.isVisibleLw()) {
                     // Finished animating out, clean up and reset alpha
-                    mNavigationHideybar = HIDEYBAR_NONE;
+                    mNavigationTransientBar = TRANSIENT_BAR_NONE;
                     updateSysUiVisibility = true;
                 }
             }
@@ -2798,11 +2804,12 @@
                 // For layout, the status bar is always at the top with our fixed height.
                 mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
 
-                boolean statusBarOverlay = (mLastSystemUiFlags & View.STATUS_BAR_OVERLAY) != 0;
+                boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+                boolean statusBarTransparent = (sysui & View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS) != 0;
 
                 // If the status bar is hidden, we don't want to cause
                 // windows behind it to scroll.
-                if (mStatusBar.isVisibleLw() && !statusBarOverlay) {
+                if (mStatusBar.isVisibleLw() && !statusBarTransient && !statusBarTransparent) {
                     // Status bar may go away, so the screen area it occupies
                     // is available to apps but just covering them when the
                     // status bar is visible.
@@ -2820,16 +2827,17 @@
                             mContentLeft, mContentTop, mContentRight, mContentBottom,
                             mCurLeft, mCurTop, mCurRight, mCurBottom));
                 }
-                if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw() && !statusBarOverlay) {
-                    // If the status bar is currently requested to be visible,
+                if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
+                        && !statusBarTransient && !statusBarTransparent) {
+                    // If the opaque status bar is currently requested to be visible,
                     // and not in the process of animating on or off, then
                     // we can tell the app that it is covered by it.
                     mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
                 }
 
-                if (mStatusHideybar == HIDEYBAR_HIDING && !mStatusBar.isVisibleLw()) {
+                if (mStatusTransientBar == TRANSIENT_BAR_HIDING && !mStatusBar.isVisibleLw()) {
                     // Finished animating out, clean up and reset alpha
-                    mStatusHideybar = HIDEYBAR_NONE;
+                    mStatusTransientBar = TRANSIENT_BAR_NONE;
                     updateSysUiVisibility = true;
                 }
             }
@@ -3411,7 +3419,7 @@
                 // and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window
                 // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
                 // case though.
-                if (mStatusHideybar == HIDEYBAR_SHOWING) {
+                if (mStatusTransientBar == TRANSIENT_BAR_SHOWING) {
                     if (setBarShowingLw(mStatusBar, true)) {
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
                     }
@@ -4135,32 +4143,32 @@
         }
     };
 
-    private void requestHideybars(WindowState swipeTarget) {
+    private void requestTransientBars(WindowState swipeTarget) {
         synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
-            boolean sb = checkShowHideybar("status", mStatusHideybar, mStatusBar);
-            boolean nb = checkShowHideybar("navigation", mNavigationHideybar, mNavigationBar);
+            boolean sb = checkShowTransientBar("status", mStatusTransientBar, mStatusBar);
+            boolean nb = checkShowTransientBar("nav", mNavigationTransientBar, mNavigationBar);
             if (sb || nb) {
-                WindowState hideyTarget = sb ? mStatusBar : mNavigationBar;
-                if (sb ^ nb && hideyTarget != swipeTarget) {
-                    if (DEBUG) Slog.d(TAG, "Not showing hideybar, wrong swipe target");
+                WindowState barTarget = sb ? mStatusBar : mNavigationBar;
+                if (sb ^ nb && barTarget != swipeTarget) {
+                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
                     return;
                 }
-                mStatusHideybar = sb ? HIDEYBAR_SHOWING : mStatusHideybar;
-                mNavigationHideybar = nb ? HIDEYBAR_SHOWING : mNavigationHideybar;
+                mStatusTransientBar = sb ? TRANSIENT_BAR_SHOWING : mStatusTransientBar;
+                mNavigationTransientBar = nb ? TRANSIENT_BAR_SHOWING : mNavigationTransientBar;
                 updateSystemUiVisibilityLw();
             }
         }
     }
 
-    private boolean checkShowHideybar(String tag, int hideybar, WindowState win) {
-        if (hideybar == HIDEYBAR_SHOWING) {
-            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, already shown");
+    private boolean checkShowTransientBar(String tag, int transientBar, WindowState win) {
+        if (transientBar == TRANSIENT_BAR_SHOWING) {
+            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " transient bar, already shown");
             return false;
         } else if (win == null) {
-            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, bar doesn't exist");
+            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " transient bar, bar doesn't exist");
             return false;
         } else if (win.isDisplayedLw()) {
-            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " hideybar, bar already visible");
+            if (DEBUG) Slog.d(TAG, "Not showing " + tag + " transient bar, bar already visible");
             return false;
         } else {
             return true;
@@ -5009,7 +5017,7 @@
         if (mForcingShowNavBar && mFocusedWindow.getSurfaceLayer() < mForcingShowNavBarLayer) {
             tmpVisibility &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
         }
-        final int visibility = updateHideybarsLw(tmpVisibility);
+        final int visibility = updateTransientBarsLw(tmpVisibility);
         final int diff = visibility ^ mLastSystemUiFlags;
         final boolean needsMenu = mFocusedWindow.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
         if (diff == 0 && mLastFocusNeedsMenu == needsMenu
@@ -5037,35 +5045,34 @@
         return diff;
     }
 
-    private int updateHideybarsLw(int tmpVisibility) {
-        if (OverlayTesting.enabled) {
-            tmpVisibility = OverlayTesting.applyForced(mFocusedWindow, tmpVisibility);
+    private int updateTransientBarsLw(int vis) {
+        if (ImmersiveModeTesting.enabled) {
+            vis = ImmersiveModeTesting.applyForced(mFocusedWindow, vis);
         }
-        boolean statusBarHasFocus =
-                mFocusedWindow.getAttrs().type == TYPE_STATUS_BAR;
+        boolean statusBarHasFocus = mFocusedWindow.getAttrs().type == TYPE_STATUS_BAR;
         if (statusBarHasFocus) {
             // prevent status bar interaction from clearing certain flags
             int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                     | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-                    | View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
-            tmpVisibility = (tmpVisibility & ~flags) | (mLastSystemUiFlags & flags);
+                    | View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT;
+            vis = (vis & ~flags) | (mLastSystemUiFlags & flags);
         }
-        boolean overlayAllowed = (tmpVisibility & View.SYSTEM_UI_FLAG_ALLOW_OVERLAY) != 0;
-        if (mStatusHideybar == HIDEYBAR_SHOWING) {
-            // status hideybar requested
+        boolean transientAllowed = (vis & View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT) != 0;
+        if (mStatusTransientBar == TRANSIENT_BAR_SHOWING) {
+            // status transient bar requested
             boolean hideStatusBarWM =
                     (mFocusedWindow.getAttrs().flags
                             & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
             boolean hideStatusBarSysui =
-                    (tmpVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+                    (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
 
-            boolean statusHideyAllowed =
+            boolean transientStatusBarAllowed =
                     hideStatusBarWM
-                    || (hideStatusBarSysui && overlayAllowed)
+                    || (hideStatusBarSysui && transientAllowed)
                     || statusBarHasFocus;
 
-            if (mStatusBar == null || !statusHideyAllowed) {
-                mStatusHideybar = HIDEYBAR_NONE;
+            if (mStatusBar == null || !transientStatusBarAllowed) {
+                mStatusTransientBar = TRANSIENT_BAR_NONE;
                 if (mStatusBar != null && hideStatusBarSysui) {
                     // clear the clearable flags instead
                     int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
@@ -5075,32 +5082,32 @@
                     }
                 }
             } else {
-                // show status hideybar
-                tmpVisibility |= View.STATUS_BAR_OVERLAY;
-                if ((mLastSystemUiFlags & View.STATUS_BAR_OVERLAY) == 0) {
-                    tmpVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                // show status transient bar
+                vis |= View.STATUS_BAR_TRANSIENT;
+                if ((mLastSystemUiFlags & View.STATUS_BAR_TRANSIENT) == 0) {
+                    vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
                     setBarShowingLw(mStatusBar, true);
                 }
             }
         }
-        if (mNavigationHideybar == HIDEYBAR_SHOWING) {
-            // navigation hideybar requested
+        if (mNavigationTransientBar == TRANSIENT_BAR_SHOWING) {
+            // navigation transient bar requested
             boolean hideNavigationBarSysui =
-                    (tmpVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-            boolean navigationHideyAllowed =
-                    hideNavigationBarSysui && overlayAllowed && mNavigationBar != null;
-            if (!navigationHideyAllowed) {
-                mNavigationHideybar = HIDEYBAR_NONE;
+                    (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+            boolean transientNavigationBarAllowed =
+                    mNavigationBar != null && hideNavigationBarSysui && transientAllowed;
+            if (!transientNavigationBarAllowed) {
+                mNavigationTransientBar = TRANSIENT_BAR_NONE;
             } else {
-                // show navigation hideybar
-                tmpVisibility |= View.NAVIGATION_BAR_OVERLAY;
-                if ((mLastSystemUiFlags & View.NAVIGATION_BAR_OVERLAY) == 0) {
-                    tmpVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                // show navigation transient bar
+                vis |= View.NAVIGATION_BAR_TRANSIENT;
+                if ((mLastSystemUiFlags & View.NAVIGATION_BAR_TRANSIENT) == 0) {
+                    vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
                     setBarShowingLw(mNavigationBar, true);
                 }
             }
         }
-        return tmpVisibility;
+        return vis;
     }
 
     private boolean setBarShowingLw(WindowState win, final boolean show) {
@@ -5129,9 +5136,10 @@
         return show ? win.showLw(true) : win.hideLw(true);
     }
 
-    // TODO temporary helper that allows testing overlay bars on existing apps
-    private static final class OverlayTesting {
-        static String ENABLED_SETTING = "overlay_testing_enabled";
+    // Temporary helper that allows testing immersive mode on existing apps
+    // TODO remove
+    private static final class ImmersiveModeTesting {
+        static String ENABLED_SETTING = "immersive_mode_testing_enabled";
         static boolean enabled = false;
         private static final HashSet<String> sForced = new HashSet<String>();
 
@@ -5153,21 +5161,21 @@
             if (sForced.contains(parseActivity(focused))) {
                 vis |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_FULLSCREEN |
-                       View.SYSTEM_UI_FLAG_ALLOW_OVERLAY;
+                       View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT;
             }
             return vis;
         }
 
-        public static void toggleForceOverlay(WindowState focused, Context context) {
+        public static void toggleForceImmersiveMode(WindowState focused, Context context) {
             String activity = parseActivity(focused);
             if (activity != null) {
                 String action;
                 if (sForced.contains(activity)) {
                     sForced.remove(activity);
-                    action = "Force overlay disabled";
+                    action = "Force immersive mode disabled";
                 } else {
                     sForced.add(activity);
-                    action = "Force overlay enabled";
+                    action = "Force immersive mode enabled";
                 }
                 android.widget.Toast.makeText(context,
                         action + " for " + activity, android.widget.Toast.LENGTH_SHORT).show();
diff --git a/services/java/com/android/internal/app/ProcessStats.java b/services/java/com/android/internal/app/ProcessStats.java
new file mode 100644
index 0000000..e916b56
--- /dev/null
+++ b/services/java/com/android/internal/app/ProcessStats.java
@@ -0,0 +1,2437 @@
+/*
+ * 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.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 com.android.server.ProcessMap;
+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;
+
+final public class ProcessStats {
+    static final String TAG = "ProcessStats";
+    static final boolean DEBUG = false;
+    
+    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;
+
+    static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
+            ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+    static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+    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, STATE_HOME
+    };
+
+    // 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();
+    }
+
+    static private 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;
+        }
+    }
+
+    static public 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;
+        }
+    }
+
+    static private 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;
+        }
+    }
+
+    static public 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("???");
+            }
+        }
+    }
+
+    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.mProcessStats.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();
+    }
+
+    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.mProcessStats.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.mProcessStats.getLong(off, PSS_SAMPLE_COUNT);
+            long min = proc.mProcessStats.getLong(off, PSS_MINIMUM);
+            long avg = proc.mProcessStats.getLong(off, PSS_AVERAGE);
+            long max = proc.mProcessStats.getLong(off, PSS_MAXIMUM);
+            long umin = proc.mProcessStats.getLong(off, PSS_USS_MINIMUM);
+            long uavg = proc.mProcessStats.getLong(off, PSS_USS_AVERAGE);
+            long umax = proc.mProcessStats.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;
+    }
+
+    public void writeToParcel(Parcel out) {
+        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);
+
+        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);
+
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        final int NPROC = procMap.size();
+        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);
+            }
+        }
+        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+        final int NPKG = pkgMap.size();
+        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) {
+                    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;
+    }
+
+    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 {
+        final ProcessStats mProcessStats;
+        final ProcessState mCommonProcess;
+        final String mPackage;
+        final int mUid;
+        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;
+
+        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) {
+            mProcessStats = 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) {
+            mProcessStats = commonProcess.mProcessStats;
+            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) {
+                mProcessStats.mAddLongTable = new int[mDurationsTable.length];
+                mProcessStats.mAddLongTableSize = 0;
+                for (int i=0; i<mDurationsTableSize; i++) {
+                    int origEnt = mDurationsTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mProcessStats.addLongData(i, type, 1);
+                    mProcessStats.mAddLongTable[i] = newOff | type;
+                    mProcessStats.setLong(newOff, 0, mProcessStats.getLong(origEnt, 0));
+                }
+                pnew.mDurationsTable = mProcessStats.mAddLongTable;
+                pnew.mDurationsTableSize = mProcessStats.mAddLongTableSize;
+            }
+            if (mPssTable != null) {
+                mProcessStats.mAddLongTable = new int[mPssTable.length];
+                mProcessStats.mAddLongTableSize = 0;
+                for (int i=0; i<mPssTableSize; i++) {
+                    int origEnt = mPssTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mProcessStats.addLongData(i, type, PSS_COUNT);
+                    mProcessStats.mAddLongTable[i] = newOff | type;
+                    for (int j=0; j<PSS_COUNT; j++) {
+                        mProcessStats.setLong(newOff, j, mProcessStats.getLong(origEnt, j));
+                    }
+                }
+                pnew.mPssTable = mProcessStats.mAddLongTable;
+                pnew.mPssTableSize = mProcessStats.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) {
+            commitStateTime(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 = mProcessStats.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 = mProcessStats.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 {
+                        mProcessStats.mAddLongTable = mDurationsTable;
+                        mProcessStats.mAddLongTableSize = mDurationsTableSize;
+                        off = mProcessStats.addLongData(~idx, mCurState, 1);
+                        mDurationsTable = mProcessStats.mAddLongTable;
+                        mDurationsTableSize = mProcessStats.mAddLongTableSize;
+                    }
+                    long[] longs = mProcessStats.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 {
+                    mProcessStats.mAddLongTable = mPssTable;
+                    mProcessStats.mAddLongTableSize = mPssTableSize;
+                    off = mProcessStats.addLongData(~idx, mCurState, PSS_COUNT);
+                    mPssTable = mProcessStats.mAddLongTable;
+                    mPssTableSize = mProcessStats.mAddLongTableSize;
+                }
+                long[] longs = mProcessStats.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 = mProcessStats.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 = mProcessStats.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 ? mProcessStats.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 ? mProcessStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
+        }
+
+        long getPssMinimum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
+        }
+
+        long getPssAverage(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
+        }
+
+        long getPssMaximum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
+        }
+
+        long getPssUssMinimum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
+        }
+
+        long getPssUssAverage(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
+        }
+
+        long getPssUssMaximum(int state) {
+            int idx = binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
+        }
+    }
+
+    public static final class ServiceState {
+        final ProcessStats mProcessStats;
+        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) {
+            mProcessStats = 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) {
+            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;
+            }
+            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 = mProcessStats.readTableFromParcel(in, mPackage, "service");
+            if (mDurationsTable == BAD_TABLE) {
+                return false;
+            }
+            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 {
+                    mProcessStats.mAddLongTable = mDurationsTable;
+                    mProcessStats.mAddLongTableSize = mDurationsTableSize;
+                    off = mProcessStats.addLongData(~idx, state, 1);
+                    mDurationsTable = mProcessStats.mAddLongTable;
+                    mDurationsTableSize = mProcessStats.mAddLongTableSize;
+                }
+                long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
+            }
+        }
+
+        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;
+            }
+        }
+
+        long getStartDuration(int opType, int memFactor, long now) {
+            switch (opType) {
+                case SERVICE_STARTED:
+                    return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now);
+                case SERVICE_BOUND:
+                    return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now);
+                case SERVICE_EXEC:
+                    return getDuration(opType, mExecState, mExecStartTime, memFactor, now);
+                default:
+                    throw new IllegalArgumentException("Bad opType: " + opType);
+            }
+        }
+
+
+        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 ? mProcessStats.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;
+        }
+    }
+
+    static final class ProcessDataCollection {
+        final int[] screenStates;
+        final int[] memStates;
+        final int[] procStates;
+
+        long totalTime;
+        long numPss;
+        long minPss;
+        long avgPss;
+        long maxPss;
+        long minUss;
+        long avgUss;
+        long maxUss;
+
+        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(")");
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index f3aabe2..d61a48d 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -26,6 +26,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -251,9 +252,9 @@
         }
         r.lastActivity = SystemClock.uptimeMillis();
         r.startRequested = true;
-        ProcessTracker.ServiceState stracker = r.getTracker();
+        ProcessStats.ServiceState stracker = r.getTracker();
         if (stracker != null) {
-            stracker.setStarted(true, mAm.mProcessTracker.getMemFactorLocked(), r.lastActivity);
+            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
         }
         r.callStart = false;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
@@ -274,7 +275,7 @@
         }
         service.startRequested = false;
         if (service.tracker != null) {
-            service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+            service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                     SystemClock.uptimeMillis());
         }
         service.callStart = false;
@@ -374,7 +375,7 @@
             }
             r.startRequested = false;
             if (r.tracker != null) {
-                r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+                r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                         SystemClock.uptimeMillis());
             }
             r.callStart = false;
@@ -516,9 +517,9 @@
                 s.lastActivity = SystemClock.uptimeMillis();
                 if (!s.hasAutoCreateConnections()) {
                     // This is the first binding, let the tracker know.
-                    ProcessTracker.ServiceState stracker = s.getTracker();
+                    ProcessStats.ServiceState stracker = s.getTracker();
                     if (stracker != null) {
-                        stracker.setBound(true, mAm.mProcessTracker.getMemFactorLocked(),
+                        stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
                                 s.lastActivity);
                     }
                 }
@@ -844,9 +845,9 @@
         long now = SystemClock.uptimeMillis();
         if (r.executeNesting == 0) {
             r.executeFg = fg;
-            ProcessTracker.ServiceState stracker = r.getTracker();
+            ProcessStats.ServiceState stracker = r.getTracker();
             if (stracker != null) {
-                stracker.setExecuting(true, mAm.mProcessTracker.getMemFactorLocked(), now);
+                stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
             }
             if (r.app != null) {
                 if (r.app.executingServices.size() == 0) {
@@ -1079,7 +1080,7 @@
                 Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
             if (app != null && app.thread != null) {
                 try {
-                    app.addPackage(r.appInfo.packageName, mAm.mProcessTracker);
+                    app.addPackage(r.appInfo.packageName, mAm.mProcessStats);
                     realStartServiceLocked(r, app, execInFg);
                     return null;
                 } catch (RemoteException e) {
@@ -1364,7 +1365,7 @@
            ((ServiceRestarter)r.restarter).setService(null);
         }
 
-        int memFactor = mAm.mProcessTracker.getMemFactorLocked();
+        int memFactor = mAm.mProcessStats.getMemFactorLocked();
         long now = SystemClock.uptimeMillis();
         if (r.tracker != null) {
             r.tracker.setStarted(false, memFactor, now);
@@ -1435,7 +1436,7 @@
                 boolean hasAutoCreate = s.hasAutoCreateConnections();
                 if (!hasAutoCreate) {
                     if (s.tracker != null) {
-                        s.tracker.setBound(false, mAm.mProcessTracker.getMemFactorLocked(),
+                        s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
                                 SystemClock.uptimeMillis());
                     }
                 }
@@ -1541,7 +1542,7 @@
             }
             r.executeFg = false;
             if (r.tracker != null) {
-                r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+                r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
                         SystemClock.uptimeMillis());
                 if (inStopping) {
                     r.tracker.makeInactive();
@@ -1566,7 +1567,7 @@
 
                     mPendingServices.remove(i);
                     i--;
-                    proc.addPackage(sr.appInfo.packageName, mAm.mProcessTracker);
+                    proc.addPackage(sr.appInfo.packageName, mAm.mProcessStats);
                     realStartServiceLocked(sr, proc, sr.createdFromFg);
                     didSomething = true;
                 }
@@ -1737,7 +1738,7 @@
             sr.isolatedProc = null;
             sr.executeNesting = 0;
             if (sr.tracker != null) {
-                sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+                sr.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
                         SystemClock.uptimeMillis());
             }
             if (mStoppingServices.remove(sr)) {
@@ -1772,7 +1773,7 @@
                     if (sr.pendingStarts.size() == 0) {
                         sr.startRequested = false;
                         if (sr.tracker != null) {
-                            sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+                            sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                                     SystemClock.uptimeMillis());
                         }
                         if (!sr.hasAutoCreateConnections()) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cb1747f..1d26c2c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,10 +30,11 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.ProcessStats;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.os.ProcessStats;
+import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -419,7 +420,7 @@
      * Tracking long-term execution of processes to look for abuse and other
      * bad app behavior.
      */
-    final ProcessTracker mProcessTracker;
+    final ProcessStatsService mProcessStats;
 
     /**
      * The currently running isolated processes.
@@ -891,19 +892,19 @@
             = new ArrayList<ProcessChangeItem>();
 
     /**
-     * Runtime statistics collection thread.  This object's lock is used to
+     * Runtime CPU use collection thread.  This object's lock is used to
      * protect all related state.
      */
-    final Thread mProcessStatsThread;
+    final Thread mProcessCpuThread;
 
     /**
      * Used to collect process stats when showing not responding dialog.
-     * Protected by mProcessStatsThread.
+     * Protected by mProcessCpuThread.
      */
-    final ProcessStats mProcessStats = new ProcessStats(
+    final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
             MONITOR_THREAD_CPU_USAGE);
     final AtomicLong mLastCpuTime = new AtomicLong(0);
-    final AtomicBoolean mProcessStatsMutexFree = new AtomicBoolean(true);
+    final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
 
     long mLastWriteTime = 0;
 
@@ -1745,9 +1746,9 @@
                 return;
             }
 
-            synchronized (mActivityManagerService.mProcessStatsThread) {
-                pw.print(mActivityManagerService.mProcessStats.printCurrentLoad());
-                pw.print(mActivityManagerService.mProcessStats.printCurrentState(
+            synchronized (mActivityManagerService.mProcessCpuThread) {
+                pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
+                pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
                         SystemClock.uptimeMillis()));
             }
         }
@@ -1769,7 +1770,7 @@
                 return;
             }
 
-            mActivityManagerService.mProcessTracker.dump(fd, pw, args);
+            mActivityManagerService.mProcessStats.dump(fd, pw, args);
         }
     }
 
@@ -1795,7 +1796,7 @@
                 : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
 
-        mProcessTracker = new ProcessTracker(this, new File(systemDir, "procstats"));
+        mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
 
         mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString());
         mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));
@@ -1816,14 +1817,14 @@
         mConfiguration.setLocale(Locale.getDefault());
 
         mConfigurationSeq = mConfiguration.seq = 1;
-        mProcessStats.init();
+        mProcessCpuTracker.init();
 
         mCompatModePackages = new CompatModePackages(this, systemDir);
 
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
 
-        mProcessStatsThread = new Thread("ProcessStats") {
+        mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
                 while (true) {
@@ -1839,7 +1840,7 @@
                                     nextCpuDelay = nextWriteDelay;
                                 }
                                 if (nextCpuDelay > 0) {
-                                    mProcessStatsMutexFree.set(true);
+                                    mProcessCpuMutexFree.set(true);
                                     this.wait(nextCpuDelay);
                                 }
                             }
@@ -1852,7 +1853,7 @@
                 }
             }
         };
-        mProcessStatsThread.start();
+        mProcessCpuThread.start();
     }
 
     @Override
@@ -1902,16 +1903,16 @@
         if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
             return;
         }
-        if (mProcessStatsMutexFree.compareAndSet(true, false)) {
-            synchronized (mProcessStatsThread) {
-                mProcessStatsThread.notify();
+        if (mProcessCpuMutexFree.compareAndSet(true, false)) {
+            synchronized (mProcessCpuThread) {
+                mProcessCpuThread.notify();
             }
         }
     }
 
     void updateCpuStatsNow() {
-        synchronized (mProcessStatsThread) {
-            mProcessStatsMutexFree.set(false);
+        synchronized (mProcessCpuThread) {
+            mProcessCpuMutexFree.set(false);
             final long now = SystemClock.uptimeMillis();
             boolean haveNewCpuStats = false;
 
@@ -1919,19 +1920,19 @@
                     mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) {
                 mLastCpuTime.set(now);
                 haveNewCpuStats = true;
-                mProcessStats.update();
-                //Slog.i(TAG, mProcessStats.printCurrentState());
+                mProcessCpuTracker.update();
+                //Slog.i(TAG, mProcessCpu.printCurrentState());
                 //Slog.i(TAG, "Total CPU usage: "
-                //        + mProcessStats.getTotalCpuPercent() + "%");
+                //        + mProcessCpu.getTotalCpuPercent() + "%");
 
                 // Slog the cpu usage if the property is set.
                 if ("true".equals(SystemProperties.get("events.cpu"))) {
-                    int user = mProcessStats.getLastUserTime();
-                    int system = mProcessStats.getLastSystemTime();
-                    int iowait = mProcessStats.getLastIoWaitTime();
-                    int irq = mProcessStats.getLastIrqTime();
-                    int softIrq = mProcessStats.getLastSoftIrqTime();
-                    int idle = mProcessStats.getLastIdleTime();
+                    int user = mProcessCpuTracker.getLastUserTime();
+                    int system = mProcessCpuTracker.getLastSystemTime();
+                    int iowait = mProcessCpuTracker.getLastIoWaitTime();
+                    int irq = mProcessCpuTracker.getLastIrqTime();
+                    int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
+                    int idle = mProcessCpuTracker.getLastIdleTime();
 
                     int total = user + system + iowait + irq + softIrq + idle;
                     if (total == 0) total = 1;
@@ -1946,7 +1947,7 @@
                 }
             }
 
-            long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes();
+            long[] cpuSpeedTimes = mProcessCpuTracker.getLastCpuSpeedTimes();
             final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
@@ -1955,9 +1956,9 @@
                             int perc = bstats.startAddingCpuLocked();
                             int totalUTime = 0;
                             int totalSTime = 0;
-                            final int N = mProcessStats.countStats();
+                            final int N = mProcessCpuTracker.countStats();
                             for (int i=0; i<N; i++) {
-                                ProcessStats.Stats st = mProcessStats.getStats(i);
+                                ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
                                 if (!st.working) {
                                     continue;
                                 }
@@ -2233,7 +2234,7 @@
                 // come up (we have a pid but not yet its thread), so keep it.
                 if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
                 // If this is a new package in the process, add the package to the list
-                app.addPackage(info.packageName, mProcessTracker);
+                app.addPackage(info.packageName, mProcessStats);
                 return app;
             }
 
@@ -2288,7 +2289,7 @@
             }
         } else {
             // If this is a new package in the process, add the package to the list
-            app.addPackage(info.packageName, mProcessTracker);
+            app.addPackage(info.packageName, mProcessStats);
         }
 
         // If the system is not ready yet, then hold off on starting this
@@ -3368,7 +3369,7 @@
      * @return file containing stack traces, or null if no dump file is configured
      */
     public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids,
-            ProcessStats processStats, SparseArray<Boolean> lastPids, String[] nativeProcs) {
+            ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
         String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
         if (tracesPath == null || tracesPath.length() == 0) {
             return null;
@@ -3393,12 +3394,12 @@
             return null;
         }
 
-        dumpStackTraces(tracesPath, firstPids, processStats, lastPids, nativeProcs);
+        dumpStackTraces(tracesPath, firstPids, processCpuTracker, lastPids, nativeProcs);
         return tracesFile;
     }
 
     private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids,
-            ProcessStats processStats, SparseArray<Boolean> lastPids, String[] nativeProcs) {
+            ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
         // Use a FileObserver to detect when traces finish writing.
         // The order of traces is considered important to maintain for legibility.
         FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
@@ -3425,23 +3426,23 @@
             }
 
             // Next measure CPU usage.
-            if (processStats != null) {
-                processStats.init();
+            if (processCpuTracker != null) {
+                processCpuTracker.init();
                 System.gc();
-                processStats.update();
+                processCpuTracker.update();
                 try {
-                    synchronized (processStats) {
-                        processStats.wait(500); // measure over 1/2 second.
+                    synchronized (processCpuTracker) {
+                        processCpuTracker.wait(500); // measure over 1/2 second.
                     }
                 } catch (InterruptedException e) {
                 }
-                processStats.update();
+                processCpuTracker.update();
 
                 // We'll take the stack crawls of just the top apps using CPU.
-                final int N = processStats.countWorkingStats();
+                final int N = processCpuTracker.countWorkingStats();
                 int numProcs = 0;
                 for (int i=0; i<N && numProcs<5; i++) {
-                    ProcessStats.Stats stats = processStats.getWorkingStats(i);
+                    ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
                     if (lastPids.indexOfKey(stats.pid) >= 0) {
                         numProcs++;
                         try {
@@ -3629,21 +3630,21 @@
             info.append("Parent: ").append(parent.shortComponentName).append("\n");
         }
 
-        final ProcessStats processStats = new ProcessStats(true);
+        final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
 
-        File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids, null);
+        File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, null);
 
         String cpuInfo = null;
         if (MONITOR_CPU_USAGE) {
             updateCpuStatsNow();
-            synchronized (mProcessStatsThread) {
-                cpuInfo = mProcessStats.printCurrentState(anrTime);
+            synchronized (mProcessCpuThread) {
+                cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
             }
-            info.append(processStats.printCurrentLoad());
+            info.append(processCpuTracker.printCurrentLoad());
             info.append(cpuInfo);
         }
 
-        info.append(processStats.printCurrentState(anrTime));
+        info.append(processCpuTracker.printCurrentState(anrTime));
 
         Slog.e(TAG, info.toString());
         if (tracesFile == null) {
@@ -4474,7 +4475,7 @@
             thread.asBinder().linkToDeath(adr, 0);
             app.deathRecipient = adr;
         } catch (RemoteException e) {
-            app.resetPackageList(mProcessTracker);
+            app.resetPackageList(mProcessStats);
             startProcessLocked(app, "link fail", processName);
             return false;
         }
@@ -4566,7 +4567,7 @@
             // an infinite loop of restarting processes...
             Slog.w(TAG, "Exception thrown during bind!", e);
 
-            app.resetPackageList(mProcessTracker);
+            app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
             startProcessLocked(app, "bind fail", processName);
             return false;
@@ -6782,7 +6783,7 @@
                 if (DEBUG_MU)
                     Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
                 app.pubProviders.put(cpi.name, cpr);
-                app.addPackage(cpi.applicationInfo.packageName, mProcessTracker);
+                app.addPackage(cpi.applicationInfo.packageName, mProcessStats);
                 ensurePackageDexOpt(cpi.applicationInfo.packageName);
             }
         }
@@ -7520,7 +7521,7 @@
             ps = stats.getProcessStatsLocked(info.uid, proc);
         }
         return new ProcessRecord(ps, thread, info, proc, uid,
-                mProcessTracker.getProcessStateLocked(info.packageName, info.uid, proc));
+                mProcessStats.getProcessStateLocked(info.packageName, info.uid, proc));
     }
 
     final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
@@ -7660,7 +7661,7 @@
         mUsageStatsService.shutdown();
         mBatteryStatsService.shutdown();
         synchronized (this) {
-            mProcessTracker.shutdownLocked();
+            mProcessStats.shutdownLocked();
         }
 
         return timedout;
@@ -11665,7 +11666,7 @@
         app.crashing = false;
         app.notResponding = false;
         
-        app.resetPackageList(mProcessTracker);
+        app.resetPackageList(mProcessStats);
         app.unlinkDeathRecipient();
         app.thread = null;
         app.forcingToForeground = null;
@@ -14540,7 +14541,7 @@
                     + " to " + app.curProcState);
             app.setProcState = app.curProcState;
             if (!doingAll) {
-                setProcessTrackerState(app, mProcessTracker.getMemFactorLocked(), now);
+                setProcessTrackerState(app, mProcessStats.getMemFactorLocked(), now);
             } else {
                 app.procStateChanged = true;
             }
@@ -14818,17 +14819,17 @@
             int memFactor;
             if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
-                memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL;
+                memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
             } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
-                memFactor = ProcessTracker.ADJ_MEM_FACTOR_LOW;
+                memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
             } else {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
-                memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE;
+                memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
             }
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
-            final int trackerMemFactor = mProcessTracker.getMemFactorLocked();
+            allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now);
+            final int trackerMemFactor = mProcessStats.getMemFactorLocked();
             for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
@@ -14916,9 +14917,9 @@
                 }
             }
         } else {
-            allChanged = mProcessTracker.setMemFactorLocked(
-                    ProcessTracker.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now);
-            final int trackerMemFactor = mProcessTracker.getMemFactorLocked();
+            allChanged = mProcessStats.setMemFactorLocked(
+                    ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now);
+            final int trackerMemFactor = mProcessStats.getMemFactorLocked();
             for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
@@ -14951,14 +14952,14 @@
         }
 
         if (allChanged) {
-            requestPssAllProcsLocked(now, false, mProcessTracker.isMemFactorLowered());
+            requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
         }
 
-        if (mProcessTracker.shouldWriteNowLocked(now)) {
+        if (mProcessStats.shouldWriteNowLocked(now)) {
             mHandler.post(new Runnable() {
                 @Override public void run() {
                     synchronized (ActivityManagerService.this) {
-                        mProcessTracker.writeStateAsyncLocked();
+                        mProcessStats.writeStateAsyncLocked();
                     }
                 }
             });
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index cc1be98..a0bbfad 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -177,6 +177,13 @@
     ActivityRecord mLastPausedActivity = null;
 
     /**
+     * Activities that specify No History must be removed once the user navigates away from them.
+     * If the device goes to sleep with such an activity in the paused state then we save it here
+     * and finish it later if another activity replaces it on wakeup.
+     */
+    ActivityRecord mLastNoHistoryActivity = null;
+
+    /**
      * Current activity that is resumed, or null if there is none.
      */
     ActivityRecord mResumedActivity = null;
@@ -710,6 +717,8 @@
         mResumedActivity = null;
         mPausingActivity = prev;
         mLastPausedActivity = prev;
+        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
         prev.state = ActivityState.PAUSING;
         prev.task.touchActiveTime();
         clearLaunchTime(prev);
@@ -732,10 +741,12 @@
                 Slog.w(TAG, "Exception thrown during pause", e);
                 mPausingActivity = null;
                 mLastPausedActivity = null;
+                mLastNoHistoryActivity = null;
             }
         } else {
             mPausingActivity = null;
             mLastPausedActivity = null;
+            mLastNoHistoryActivity = null;
         }
 
         // If we are not going to sleep, we want to ensure the device is
@@ -893,8 +904,8 @@
             if (prev.app != null && prev.cpuTimeAtResume > 0
                     && mService.mBatteryStatsService.isOnBattery()) {
                 long diff;
-                synchronized (mService.mProcessStatsThread) {
-                    diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+                synchronized (mService.mProcessCpuThread) {
+                    diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
                             - prev.cpuTimeAtResume;
                 }
                 if (diff > 0) {
@@ -902,7 +913,7 @@
                     synchronized (bsi) {
                         BatteryStatsImpl.Uid.Proc ps =
                                 bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
-                                prev.info.packageName);
+                                        prev.info.packageName);
                         if (ps != null) {
                             ps.addForegroundTimeLocked(diff);
                         }
@@ -935,8 +946,8 @@
         // TODO: To be more accurate, the mark should be before the onCreate,
         //       not after the onResume. But for subsequent starts, onResume is fine.
         if (next.app != null) {
-            synchronized (mService.mProcessStatsThread) {
-                next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid);
+            synchronized (mService.mProcessCpuThread) {
+                next.cpuTimeAtResume = mService.mProcessCpuTracker.getCpuTimeForPid(next.app.pid);
             }
         } else {
             next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
@@ -1333,16 +1344,13 @@
         // If the most recent activity was noHistory but was only stopped rather
         // than stopped+finished because the device went to sleep, we need to make
         // sure to finish it as we're making a new activity topmost.
-        final ActivityRecord last = mLastPausedActivity;
-        if (mService.mSleeping && last != null && !last.finishing) {
-            if ((last.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                    || (last.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
-                if (DEBUG_STATES) {
-                    Slog.d(TAG, "no-history finish of " + last + " on new resume");
-                }
-                requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
-                        "no-history", false);
-            }
+        if (mService.mSleeping && mLastNoHistoryActivity != null &&
+                !mLastNoHistoryActivity.finishing) {
+            if (DEBUG_STATES) Slog.d(TAG, "no-history finish of " + mLastNoHistoryActivity +
+                    " on new resume");
+            requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
+                    null, "no-history", false);
+            mLastNoHistoryActivity = null;
         }
 
         if (prev != null && prev != next) {
@@ -2949,7 +2957,6 @@
 
         mWindowManager.moveTaskToTop(tr.taskId);
 
-        mLastPausedActivity = null;
         mStackSupervisor.resumeTopActivitiesLocked();
         EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
 
@@ -3368,6 +3375,7 @@
         }
         if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
             mLastPausedActivity = null;
+            mLastNoHistoryActivity = null;
         }
         final ActivityRecord top = topRunningActivityLocked(null);
         final boolean launchHomeTaskNext =
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 1681c2d..467c0b3 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1027,7 +1027,7 @@
 
         if (app != null && app.thread != null) {
             try {
-                app.addPackage(r.info.packageName, mService.mProcessTracker);
+                app.addPackage(r.info.packageName, mService.mProcessStats);
                 realStartActivityLocked(r, app, andResume, checkConfig);
                 return;
             } catch (RemoteException e) {
@@ -1398,6 +1398,7 @@
                         r.task = intentActivity.task;
                     }
                     targetStack = intentActivity.task.stack;
+                    targetStack.mLastPausedActivity = null;
                     moveHomeStack(targetStack.isHomeStack());
                     if (intentActivity.task.intent == null) {
                         // This task was started because of movement of
@@ -1575,6 +1576,7 @@
                                     top.task);
                             // For paranoia, make sure we have correctly
                             // resumed the top activity.
+                            topStack.mLastPausedActivity = null;
                             if (doResume) {
                                 setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
                                 resumeTopActivitiesLocked();
@@ -1653,6 +1655,7 @@
                     top.deliverNewIntentLocked(callingUid, r.intent);
                     // For paranoia, make sure we have correctly
                     // resumed the top activity.
+                    targetStack.mLastPausedActivity = null;
                     if (doResume) {
                         setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
                         targetStack.resumeTopActivityLocked(null);
@@ -1675,6 +1678,7 @@
                     ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
                     top.updateOptionsLocked(options);
                     top.deliverNewIntentLocked(callingUid, r.intent);
+                    targetStack.mLastPausedActivity = null;
                     if (doResume) {
                         setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
                         targetStack.resumeTopActivityLocked(null);
@@ -1714,6 +1718,7 @@
         }
         ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
         setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack);
+        targetStack.mLastPausedActivity = null;
         targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
         mService.setFocusedActivityLocked(r);
         return ActivityManager.START_SUCCESS;
@@ -2390,7 +2395,10 @@
                         "    mLastPausedActivity: ");
                 if (pr) {
                     printed = true;
+                    needSep = true;
                 }
+                printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+                        needSep, "    mLastNoHistoryActivity: ");
             }
             needSep = printed;
         }
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index c0140e4..cb4b8ff 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -797,7 +797,7 @@
                     info.activityInfo.applicationInfo.uid);
             if (app != null && app.thread != null) {
                 try {
-                    app.addPackage(info.activityInfo.packageName, mService.mProcessTracker);
+                    app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
                     processCurBroadcastLocked(r, app);
                     return;
                 } catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 2b73b8a..f1a030e 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.util.ArraySet;
+import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
 
 import android.app.ActivityManager;
@@ -51,10 +52,10 @@
     final int uid;              // uid of process; may be different from 'info' if isolated
     final int userId;           // user of process.
     final String processName;   // name of the process
-    final ProcessTracker.ProcessState baseProcessTracker;
+    final ProcessStats.ProcessState baseProcessTracker;
     // List of packages running in the process
-    final ArrayMap<String, ProcessTracker.ProcessState> pkgList
-            = new ArrayMap<String, ProcessTracker.ProcessState>();
+    final ArrayMap<String, ProcessStats.ProcessState> pkgList
+            = new ArrayMap<String, ProcessStats.ProcessState>();
     IApplicationThread thread;  // the actual proc...  may be null only if
                                 // 'persistent' is true (in which case we
                                 // are in the process of launching the app)
@@ -350,7 +351,7 @@
     
     ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
             ApplicationInfo _info, String _processName, int _uid,
-            ProcessTracker.ProcessState tracker) {
+            ProcessStats.ProcessState tracker) {
         batteryStats = _batteryStats;
         info = _info;
         isolated = _info.uid != _uid;
@@ -487,7 +488,7 @@
     /*
      *  Return true if package has been added false if not
      */
-    public boolean addPackage(String pkg, ProcessTracker tracker) {
+    public boolean addPackage(String pkg, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
             pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName));
             return true;
@@ -513,9 +514,9 @@
     /*
      *  Delete all packages from list except the package indicated in info
      */
-    public void resetPackageList(ProcessTracker tracker) {
+    public void resetPackageList(ProcessStatsService tracker) {
         long now = SystemClock.uptimeMillis();
-        baseProcessTracker.setState(ProcessTracker.STATE_NOTHING,
+        baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
                 tracker.getMemFactorLocked(), now, pkgList);
         if (pkgList.size() != 1) {
             pkgList.clear();
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
new file mode 100644
index 0000000..ee6f7ec
--- /dev/null
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -0,0 +1,714 @@
+/*
+ * 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.server.am;
+
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.app.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.locks.ReentrantLock;
+
+public final class ProcessStatsService {
+    static final String TAG = "ProcessStatsService";
+    static final boolean DEBUG = false;
+
+    // Most data is kept in a sparse data structure: an integer array which integer
+    // holds the type of the entry, and the identifier for a long array that data
+    // exists in and the offset into the array to find it.  The constants below
+    // define the encoding of that data in an integer.
+
+    static final int MAX_HISTORIC_STATES = 4;   // Maximum number of historic states we will keep.
+    static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
+    static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
+    static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
+    static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
+    static long COMMIT_PERIOD = 24*60*60*1000;  // Commit current stats every day.
+
+    final Object mLock;
+    final File mBaseDir;
+    ProcessStats mProcessStats;
+    AtomicFile mFile;
+    boolean mCommitPending;
+    boolean mShuttingDown;
+    int mLastMemOnlyState = -1;
+    boolean mMemFactorLowered;
+
+    final ReentrantLock mWriteLock = new ReentrantLock();
+    final Object mPendingWriteLock = new Object();
+    AtomicFile mPendingWriteFile;
+    Parcel mPendingWrite;
+    boolean mPendingWriteCommitted;
+    long mLastWriteTime;
+
+    public ProcessStatsService(Object lock, File file) {
+        mLock = lock;
+        mBaseDir = file;
+        mBaseDir.mkdirs();
+        mProcessStats = new ProcessStats(true);
+        updateFile();
+        SystemProperties.addChangeCallback(new Runnable() {
+            @Override public void run() {
+                synchronized (mLock) {
+                    if (mProcessStats.evaluateSystemProperties(false)) {
+                        mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
+                        writeStateLocked(true, true);
+                        mProcessStats.evaluateSystemProperties(true);
+                    }
+                }
+            }
+        });
+    }
+
+    public ProcessStats.ProcessState getProcessStateLocked(String packageName,
+            int uid, String processName) {
+        return mProcessStats.getProcessStateLocked(packageName, uid, processName);
+    }
+
+    public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid,
+            String processName, String className) {
+        final ProcessStats.PackageState as = mProcessStats.getPackageStateLocked(packageName, uid);
+        ProcessStats.ServiceState ss = as.mServices.get(className);
+        if (ss != null) {
+            ss.makeActive();
+            return ss;
+        }
+        final ProcessStats.ProcessState ps = mProcessStats.getProcessStateLocked(packageName,
+                uid, processName);
+        ss = new ProcessStats.ServiceState(mProcessStats, packageName, ps);
+        as.mServices.put(className, ss);
+        return ss;
+    }
+
+    public boolean isMemFactorLowered() {
+        return mMemFactorLowered;
+    }
+
+    public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
+        mMemFactorLowered = memFactor < mLastMemOnlyState;
+        mLastMemOnlyState = memFactor;
+        if (screenOn) {
+            memFactor += ProcessStats.ADJ_SCREEN_ON;
+        }
+        if (memFactor != mProcessStats.mMemFactor) {
+            if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) {
+                mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor]
+                        += now - mProcessStats.mStartTime;
+            }
+            mProcessStats.mMemFactor = memFactor;
+            mProcessStats.mStartTime = now;
+            ArrayMap<String, SparseArray<ProcessStats.PackageState>> pmap
+                    = mProcessStats.mPackages.getMap();
+            for (int i=0; i<pmap.size(); i++) {
+                SparseArray<ProcessStats.PackageState> uids = pmap.valueAt(i);
+                for (int j=0; j<uids.size(); j++) {
+                    ProcessStats.PackageState pkg = uids.valueAt(j);
+                    ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
+                    for (int k=0; k<services.size(); k++) {
+                        ProcessStats.ServiceState service = services.valueAt(k);
+                        if (service.isActive()) {
+                            if (service.mStartedState != ProcessStats.STATE_NOTHING) {
+                                service.setStarted(true, memFactor, now);
+                            }
+                            if (service.mBoundState != ProcessStats.STATE_NOTHING) {
+                                service.setBound(true, memFactor, now);
+                            }
+                            if (service.mExecState != ProcessStats.STATE_NOTHING) {
+                                service.setExecuting(true, memFactor, now);
+                            }
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public int getMemFactorLocked() {
+        return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
+    }
+
+    public boolean shouldWriteNowLocked(long now) {
+        if (now > (mLastWriteTime+WRITE_PERIOD)) {
+            if (SystemClock.elapsedRealtime()
+                    > (mProcessStats.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
+                mCommitPending = true;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void shutdownLocked() {
+        Slog.w(TAG, "Writing process stats before shutdown...");
+        mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
+        writeStateSyncLocked();
+        mShuttingDown = true;
+    }
+
+    public void writeStateAsyncLocked() {
+        writeStateLocked(false);
+    }
+
+    public void writeStateSyncLocked() {
+        writeStateLocked(true);
+    }
+
+    private void writeStateLocked(boolean sync) {
+        if (mShuttingDown) {
+            return;
+        }
+        boolean commitPending = mCommitPending;
+        mCommitPending = false;
+        writeStateLocked(sync, commitPending);
+    }
+
+    public void writeStateLocked(boolean sync, final boolean commit) {
+        synchronized (mPendingWriteLock) {
+            long now = SystemClock.uptimeMillis();
+            if (mPendingWrite == null || !mPendingWriteCommitted) {
+                mPendingWrite = Parcel.obtain();
+                mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+                if (commit) {
+                    mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
+                }
+                mProcessStats.writeToParcel(mPendingWrite);
+                mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
+                mPendingWriteCommitted = commit;
+            }
+            if (commit) {
+                mProcessStats.resetSafely();
+                updateFile();
+            }
+            mLastWriteTime = SystemClock.uptimeMillis();
+            Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms");
+            if (!sync) {
+                BackgroundThread.getHandler().post(new Runnable() {
+                    @Override public void run() {
+                        performWriteState();
+                    }
+                });
+                return;
+            }
+        }
+
+        performWriteState();
+    }
+
+    private void updateFile() {
+        mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
+                + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+        mLastWriteTime = SystemClock.uptimeMillis();
+    }
+
+    void performWriteState() {
+        if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
+        Parcel data;
+        AtomicFile file;
+        synchronized (mPendingWriteLock) {
+            data = mPendingWrite;
+            file = mPendingWriteFile;
+            mPendingWriteCommitted = false;
+            if (data == null) {
+                return;
+            }
+            mPendingWrite = null;
+            mPendingWriteFile = null;
+            mWriteLock.lock();
+        }
+
+        FileOutputStream stream = null;
+        try {
+            stream = file.startWrite();
+            stream.write(data.marshall());
+            stream.flush();
+            file.finishWrite(stream);
+            if (DEBUG) Slog.d(TAG, "Write completed successfully!");
+        } catch (IOException e) {
+            Slog.w(TAG, "Error writing process statistics", e);
+            file.failWrite(stream);
+        } finally {
+            data.recycle();
+            trimHistoricStatesWriteLocked();
+            mWriteLock.unlock();
+        }
+    }
+
+    static byte[] readFully(FileInputStream stream) throws java.io.IOException {
+        int pos = 0;
+        int avail = stream.available();
+        byte[] data = new byte[avail];
+        while (true) {
+            int amt = stream.read(data, pos, data.length-pos);
+            //Log.i("foo", "Read " + amt + " bytes at " + pos
+            //        + " of avail " + data.length);
+            if (amt <= 0) {
+                //Log.i("foo", "**** FINISHED READING: pos=" + pos
+                //        + " len=" + data.length);
+                return data;
+            }
+            pos += amt;
+            avail = stream.available();
+            if (avail > data.length-pos) {
+                byte[] newData = new byte[pos+avail];
+                System.arraycopy(data, 0, newData, 0, pos);
+                data = newData;
+            }
+        }
+    }
+
+    boolean readLocked(ProcessStats stats, AtomicFile file) {
+        try {
+            FileInputStream stream = file.openRead();
+
+            byte[] raw = readFully(stream);
+            Parcel in = Parcel.obtain();
+            in.unmarshall(raw, 0, raw.length);
+            in.setDataPosition(0);
+            stream.close();
+
+            stats.readFromParcel(in);
+            if (stats.mReadError != null) {
+                Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError);
+                if (DEBUG) {
+                    ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap
+                            = stats.mProcesses.getMap();
+                    final int NPROC = procMap.size();
+                    for (int ip=0; ip<NPROC; ip++) {
+                        Slog.w(TAG, "Process: " + procMap.keyAt(ip));
+                        SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip);
+                        final int NUID = uids.size();
+                        for (int iu=0; iu<NUID; iu++) {
+                            Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
+                        }
+                    }
+                    ArrayMap<String, SparseArray<ProcessStats.PackageState>> pkgMap
+                            = stats.mPackages.getMap();
+                    final int NPKG = pkgMap.size();
+                    for (int ip=0; ip<NPKG; ip++) {
+                        Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
+                        SparseArray<ProcessStats.PackageState> uids = pkgMap.valueAt(ip);
+                        final int NUID = uids.size();
+                        for (int iu=0; iu<NUID; iu++) {
+                            Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
+                            ProcessStats.PackageState pkgState = uids.valueAt(iu);
+                            final int NPROCS = pkgState.mProcesses.size();
+                            for (int iproc=0; iproc<NPROCS; iproc++) {
+                                Slog.w(TAG, "    Process " + pkgState.mProcesses.keyAt(iproc)
+                                        + ": " + pkgState.mProcesses.valueAt(iproc));
+                            }
+                            final int NSRVS = pkgState.mServices.size();
+                            for (int isvc=0; isvc<NSRVS; isvc++) {
+                                Slog.w(TAG, "    Service " + pkgState.mServices.keyAt(isvc)
+                                        + ": " + pkgState.mServices.valueAt(isvc));
+                            }
+                        }
+                    }
+                }
+                return false;
+            }
+        } catch (Throwable e) {
+            stats.mReadError = "caught exception: " + e;
+            Slog.e(TAG, "Error reading process statistics", e);
+            return false;
+        }
+        return true;
+    }
+
+    private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) {
+        File[] files = mBaseDir.listFiles();
+        if (files == null || files.length <= minNum) {
+            return null;
+        }
+        ArrayList<String> filesArray = new ArrayList<String>(files.length);
+        String currentFile = mFile.getBaseFile().getPath();
+        if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
+        for (int i=0; i<files.length; i++) {
+            File file = files[i];
+            String fileStr = file.getPath();
+            if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
+            if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
+                if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
+                continue;
+            }
+            if (fileStr.equals(currentFile)) {
+                if (DEBUG) Slog.d(TAG, "Skipping: current stats");
+                continue;
+            }
+            filesArray.add(fileStr);
+        }
+        Collections.sort(filesArray);
+        return filesArray;
+    }
+
+    public void trimHistoricStatesWriteLocked() {
+        ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true);
+        if (filesArray == null) {
+            return;
+        }
+        while (filesArray.size() > MAX_HISTORIC_STATES) {
+            String file = filesArray.remove(0);
+            Slog.i(TAG, "Pruning old procstats: " + file);
+            (new File(file)).delete();
+        }
+    }
+
+    boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
+            boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+            boolean sepProcStates, int[] procStates, long now, String reqPackage) {
+        ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked(
+                screenStates, memStates, procStates, now, reqPackage);
+        if (procs.size() > 0) {
+            if (header != null) {
+                pw.println(header);
+            }
+            ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
+                    sepMemStates, memStates, sepProcStates, procStates, now);
+            return true;
+        }
+        return false;
+    }
+
+    static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
+            String[] outError) {
+        ArrayList<Integer> res = new ArrayList<Integer>();
+        int lastPos = 0;
+        for (int i=0; i<=arg.length(); i++) {
+            char c = i < arg.length() ? arg.charAt(i) : 0;
+            if (c != ',' && c != '+' && c != ' ' && c != 0) {
+                continue;
+            }
+            boolean isSep = c == ',';
+            if (lastPos == 0) {
+                // We now know the type of op.
+                outSep[0] = isSep;
+            } else if (c != 0 && outSep[0] != isSep) {
+                outError[0] = "inconsistent separators (can't mix ',' with '+')";
+                return null;
+            }
+            if (lastPos < (i-1)) {
+                String str = arg.substring(lastPos, i);
+                for (int j=0; j<states.length; j++) {
+                    if (str.equals(states[j])) {
+                        res.add(j);
+                        str = null;
+                        break;
+                    }
+                }
+                if (str != null) {
+                    outError[0] = "invalid word \"" + str + "\"";
+                    return null;
+                }
+            }
+            lastPos = i + 1;
+        }
+
+        int[] finalRes = new int[res.size()];
+        for (int i=0; i<res.size(); i++) {
+            finalRes[i] = res.get(i) * mult;
+        }
+        return finalRes;
+    }
+
+    static private void dumpHelp(PrintWriter pw) {
+        pw.println("Process stats (procstats) dump options:");
+        pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
+        pw.println("    [--details] [--current] [--commit] [--write] [-h] [<package.name>]");
+        pw.println("  --checkin: perform a checkin: print and delete old committed states.");
+        pw.println("  --c: print only state in checkin format.");
+        pw.println("  --csv: output data suitable for putting in a spreadsheet.");
+        pw.println("  --csv-screen: on, off.");
+        pw.println("  --csv-mem: norm, mod, low, crit.");
+        pw.println("  --csv-proc: pers, top, fore, vis, precept, backup,");
+        pw.println("    service, home, prev, cached");
+        pw.println("  --details: dump all execution details, not just summary.");
+        pw.println("  --current: only dump current state.");
+        pw.println("  --commit: commit current stats to disk and reset to start new stats.");
+        pw.println("  --write: write current in-memory stats to disk.");
+        pw.println("  --read: replace current stats with last-written stats.");
+        pw.println("  -a: print everything.");
+        pw.println("  -h: print this help text.");
+        pw.println("  <package.name>: optional name of package to filter output by.");
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final long now = SystemClock.uptimeMillis();
+
+        boolean isCheckin = false;
+        boolean isCompact = false;
+        boolean isCsv = false;
+        boolean currentOnly = false;
+        boolean dumpDetails = false;
+        boolean dumpAll = false;
+        String reqPackage = null;
+        boolean csvSepScreenStats = false;
+        int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
+        boolean csvSepMemStats = false;
+        int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
+        boolean csvSepProcStats = true;
+        int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
+        if (args != null) {
+            for (int i=0; i<args.length; i++) {
+                String arg = args[i];
+                if ("--checkin".equals(arg)) {
+                    isCheckin = true;
+                } else if ("-c".equals(arg)) {
+                    isCompact = true;
+                } else if ("--csv".equals(arg)) {
+                    isCsv = true;
+                } else if ("--csv-screen".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --csv-screen");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    boolean[] sep = new boolean[1];
+                    String[] error = new String[1];
+                    csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD,
+                            args[i], sep, error);
+                    if (csvScreenStats == null) {
+                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
+                        dumpHelp(pw);
+                        return;
+                    }
+                    csvSepScreenStats = sep[0];
+                } else if ("--csv-mem".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --csv-mem");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    boolean[] sep = new boolean[1];
+                    String[] error = new String[1];
+                    csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
+                    if (csvMemStats == null) {
+                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
+                        dumpHelp(pw);
+                        return;
+                    }
+                    csvSepMemStats = sep[0];
+                } else if ("--csv-proc".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --csv-proc");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    boolean[] sep = new boolean[1];
+                    String[] error = new String[1];
+                    csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error);
+                    if (csvProcStats == null) {
+                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
+                        dumpHelp(pw);
+                        return;
+                    }
+                    csvSepProcStats = sep[0];
+                } else if ("--details".equals(arg)) {
+                    dumpDetails = true;
+                } else if ("--current".equals(arg)) {
+                    currentOnly = true;
+                } else if ("--commit".equals(arg)) {
+                    mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
+                    writeStateLocked(true, true);
+                    pw.println("Process stats committed.");
+                    return;
+                } else if ("--write".equals(arg)) {
+                    writeStateSyncLocked();
+                    pw.println("Process stats written.");
+                    return;
+                } else if ("--read".equals(arg)) {
+                    readLocked(mProcessStats, mFile);
+                    pw.println("Process stats read.");
+                    return;
+                } else if ("-h".equals(arg)) {
+                    dumpHelp(pw);
+                    return;
+                } else if ("-a".equals(arg)) {
+                    dumpDetails = true;
+                    dumpAll = true;
+                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
+                    pw.println("Unknown option: " + arg);
+                    dumpHelp(pw);
+                    return;
+                } else {
+                    // Not an option, last argument must be a package name.
+                    try {
+                        IPackageManager pm = AppGlobals.getPackageManager();
+                        if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
+                            reqPackage = arg;
+                            // Include all details, since we know we are only going to
+                            // be dumping a smaller set of data.  In fact only the details
+                            // container per-package data, so that are needed to be able
+                            // to dump anything at all when filtering by package.
+                            dumpDetails = true;
+                        }
+                    } catch (RemoteException e) {
+                    }
+                    if (reqPackage == null) {
+                        pw.println("Unknown package: " + arg);
+                        dumpHelp(pw);
+                        return;
+                    }
+                }
+            }
+        }
+
+        if (isCsv) {
+            pw.print("Processes running summed over");
+            if (!csvSepScreenStats) {
+                for (int i=0; i<csvScreenStats.length; i++) {
+                    pw.print(" ");
+                    ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]);
+                }
+            }
+            if (!csvSepMemStats) {
+                for (int i=0; i<csvMemStats.length; i++) {
+                    pw.print(" ");
+                    ProcessStats.printMemLabelCsv(pw, csvMemStats[i]);
+                }
+            }
+            if (!csvSepProcStats) {
+                for (int i=0; i<csvProcStats.length; i++) {
+                    pw.print(" ");
+                    pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]);
+                }
+            }
+            pw.println();
+            synchronized (mLock) {
+                dumpFilteredProcessesCsvLocked(pw, null,
+                        csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
+                        csvSepProcStats, csvProcStats, now, reqPackage);
+                /*
+                dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
+                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+                        true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
+                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
+                                STATE_PREVIOUS, STATE_CACHED},
+                        now, reqPackage);
+                dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
+                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+                        false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
+                                ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
+                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
+                                STATE_PREVIOUS, STATE_CACHED},
+                        now, reqPackage);
+                */
+            }
+            return;
+        }
+
+        boolean sepNeeded = false;
+        if (!currentOnly || isCheckin) {
+            mWriteLock.lock();
+            try {
+                ArrayList<String> files = getCommittedFiles(0, !isCheckin);
+                if (files != null) {
+                    for (int i=0; i<files.size(); i++) {
+                        if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
+                        try {
+                            AtomicFile file = new AtomicFile(new File(files.get(i)));
+                            ProcessStats processStats = new ProcessStats(false);
+                            readLocked(processStats, file);
+                            if (processStats.mReadError != null) {
+                                if (isCheckin || isCompact) pw.print("err,");
+                                pw.print("Failure reading "); pw.print(files.get(i));
+                                pw.print("; "); pw.println(processStats.mReadError);
+                                if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
+                                (new File(files.get(i))).delete();
+                                continue;
+                            }
+                            String fileStr = file.getBaseFile().getPath();
+                            boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
+                            if (isCheckin || isCompact) {
+                                // Don't really need to lock because we uniquely own this object.
+                                processStats.dumpCheckinLocked(pw, reqPackage);
+                            } else {
+                                if (sepNeeded) {
+                                    pw.println();
+                                } else {
+                                    sepNeeded = true;
+                                }
+                                pw.print("COMMITTED STATS FROM ");
+                                pw.print(processStats.mTimePeriodStartClockStr);
+                                if (checkedIn) pw.print(" (checked in)");
+                                pw.println(":");
+                                // Don't really need to lock because we uniquely own this object.
+                                if (dumpDetails) {
+                                    processStats.dumpLocked(pw, reqPackage, now, dumpAll);
+                                } else {
+                                    processStats.dumpSummaryLocked(pw, reqPackage, now);
+                                }
+                            }
+                            if (isCheckin) {
+                                // Rename file suffix to mark that it has checked in.
+                                file.getBaseFile().renameTo(new File(
+                                        fileStr + STATE_FILE_CHECKIN_SUFFIX));
+                            }
+                        } catch (Throwable e) {
+                            pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
+                            e.printStackTrace(pw);
+                        }
+                    }
+                }
+            } finally {
+                mWriteLock.unlock();
+            }
+        }
+        if (!isCheckin) {
+            synchronized (mLock) {
+                if (isCompact) {
+                    mProcessStats.dumpCheckinLocked(pw, reqPackage);
+                } else {
+                    if (sepNeeded) {
+                        pw.println();
+                        pw.println("CURRENT STATS:");
+                    }
+                    if (dumpDetails) {
+                        mProcessStats.dumpLocked(pw, reqPackage, now, dumpAll);
+                        if (dumpAll) {
+                            pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                        }
+                    } else {
+                        mProcessStats.dumpSummaryLocked(pw, reqPackage, now);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
deleted file mode 100644
index d78fd5c..0000000
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ /dev/null
@@ -1,3126 +0,0 @@
-/*
- * 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.server.am;
-
-import android.app.AppGlobals;
-import android.content.pm.IPackageManager;
-import android.os.Parcel;
-import android.os.RemoteException;
-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.AtomicFile;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TimeUtils;
-import android.webkit.WebViewFactory;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.ProcessMap;
-import dalvik.system.VMRuntime;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Objects;
-import java.util.concurrent.locks.ReentrantLock;
-
-public final class ProcessTracker {
-    static final String TAG = "ProcessTracker";
-    static final boolean DEBUG = false;
-
-    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;
-
-    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 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, STATE_HOME
-    };
-
-    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;
-
-    static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
-    static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
-            ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
-
-    // Most data is kept in a sparse data structure: an integer array which integer
-    // holds the type of the entry, and the identifier for a long array that data
-    // exists in and the offset into the array to find it.  The constants below
-    // define the encoding of that data in an integer.
-
-    // 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;
-
-    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 "
-    };
-
-    static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
-            "off", "on"
-    };
-
-    static final String[] ADJ_MEM_NAMES_CSV = new String[] {
-            "norm", "mod",  "low", "crit"
-    };
-
-    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"
-    };
-
-    // 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
-    };
-
-    static final String CSV_SEP = "\t";
-
-    static final int MAX_HISTORIC_STATES = 4;   // Maximum number of historic states we will keep.
-    static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
-    static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
-    static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
-    static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
-    static long COMMIT_PERIOD = 24*60*60*1000;  // Commit current stats every day.
-
-    final Object mLock;
-    final File mBaseDir;
-    State mState;
-    boolean mCommitPending;
-    boolean mShuttingDown;
-    int mLastMemOnlyState = -1;
-    boolean mMemFactorLowered;
-
-    final ReentrantLock mWriteLock = new ReentrantLock();
-
-    public static final class ProcessState {
-        final State mState;
-        final ProcessState mCommonProcess;
-        final String mPackage;
-        final int mUid;
-        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;
-
-        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(State state, String pkg, int uid, String name) {
-            mState = state;
-            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) {
-            mState = commonProcess.mState;
-            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) {
-                mState.mAddLongTable = new int[mDurationsTable.length];
-                mState.mAddLongTableSize = 0;
-                for (int i=0; i<mDurationsTableSize; i++) {
-                    int origEnt = mDurationsTable[i];
-                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mState.addLongData(i, type, 1);
-                    mState.mAddLongTable[i] = newOff | type;
-                    mState.setLong(newOff, 0, mState.getLong(origEnt, 0));
-                }
-                pnew.mDurationsTable = mState.mAddLongTable;
-                pnew.mDurationsTableSize = mState.mAddLongTableSize;
-            }
-            if (mPssTable != null) {
-                mState.mAddLongTable = new int[mPssTable.length];
-                mState.mAddLongTableSize = 0;
-                for (int i=0; i<mPssTableSize; i++) {
-                    int origEnt = mPssTable[i];
-                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mState.addLongData(i, type, PSS_COUNT);
-                    mState.mAddLongTable[i] = newOff | type;
-                    for (int j=0; j<PSS_COUNT; j++) {
-                        mState.setLong(newOff, j, mState.getLong(origEnt, j));
-                    }
-                }
-                pnew.mPssTable = mState.mAddLongTable;
-                pnew.mPssTableSize = mState.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) {
-            commitStateTime(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 + ": "
-                        + State.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 + ": "
-                        + State.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 = mState.readTableFromParcel(in, mName, "durations");
-            if (mDurationsTable == State.BAD_TABLE) {
-                return false;
-            }
-            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
-            if (DEBUG) Slog.d(TAG, "Reading pss table...");
-            mPssTable = mState.readTableFromParcel(in, mName, "pss");
-            if (mPssTable == State.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, ProcessTracker.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 = State.binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
-                    int off;
-                    if (idx >= 0) {
-                        off = mDurationsTable[idx];
-                    } else {
-                        mState.mAddLongTable = mDurationsTable;
-                        mState.mAddLongTableSize = mDurationsTableSize;
-                        off = mState.addLongData(~idx, mCurState, 1);
-                        mDurationsTable = mState.mAddLongTable;
-                        mDurationsTableSize = mState.mAddLongTableSize;
-                    }
-                    long[] longs = mState.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 = State.binarySearch(mPssTable, mPssTableSize, mCurState);
-                int off;
-                if (idx >= 0) {
-                    off = mPssTable[idx];
-                } else {
-                    mState.mAddLongTable = mPssTable;
-                    mState.mAddLongTableSize = mPssTableSize;
-                    off = mState.addLongData(~idx, mCurState, PSS_COUNT);
-                    mPssTable = mState.mAddLongTable;
-                    mPssTableSize = mState.mAddLongTableSize;
-                }
-                long[] longs = mState.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, ProcessTracker.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, ProcessTracker.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 = mState.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, ProcessTracker.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 = mState.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 = State.binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0;
-            if (mCurState == state) {
-                time += now - mStartTime;
-            }
-            return time;
-        }
-
-        long getPssSampleCount(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
-        }
-
-        long getPssMinimum(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
-        }
-
-        long getPssAverage(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
-        }
-
-        long getPssMaximum(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
-        }
-
-        long getPssUssMinimum(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
-        }
-
-        long getPssUssAverage(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
-        }
-
-        long getPssUssMaximum(int state) {
-            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
-            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
-        }
-    }
-
-    public static final class ServiceState {
-        final State mState;
-        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;
-        int mStartedState = STATE_NOTHING;
-        long mStartedStartTime;
-
-        int mBoundCount;
-        int mBoundState = STATE_NOTHING;
-        long mBoundStartTime;
-
-        int mExecCount;
-        int mExecState = STATE_NOTHING;
-        long mExecStartTime;
-
-        ServiceState(State state, String pkg, ProcessState proc) {
-            mState = state;
-            mPackage = pkg;
-            mProc = proc;
-        }
-
-        void makeActive() {
-            mActive++;
-        }
-
-        void makeInactive() {
-            /*
-            RuntimeException here = new RuntimeException("here");
-            here.fillInStackTrace();
-            Slog.i(TAG, "Making " + this + " inactive", here);
-            */
-            mActive--;
-        }
-
-        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) {
-            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;
-            }
-            out.writeInt(mDurationsTableSize);
-            for (int i=0; i<mDurationsTableSize; i++) {
-                if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": "
-                        + State.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 = mState.readTableFromParcel(in, mPackage, "service");
-            if (mDurationsTable == State.BAD_TABLE) {
-                return false;
-            }
-            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 = State.binarySearch(mDurationsTable, mDurationsTableSize, state);
-                int off;
-                if (idx >= 0) {
-                    off = mDurationsTable[idx];
-                } else {
-                    mState.mAddLongTable = mDurationsTable;
-                    mState.mAddLongTableSize = mDurationsTableSize;
-                    off = mState.addLongData(~idx, state, 1);
-                    mDurationsTable = mState.mAddLongTable;
-                    mDurationsTableSize = mState.mAddLongTableSize;
-                }
-                long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
-                longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
-            }
-        }
-
-        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;
-            }
-        }
-
-        long getStartDuration(int opType, int memFactor, long now) {
-            switch (opType) {
-                case SERVICE_STARTED:
-                    return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now);
-                case SERVICE_BOUND:
-                    return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now);
-                case SERVICE_EXEC:
-                    return getDuration(opType, mExecState, mExecStartTime, memFactor, now);
-                default:
-                    throw new IllegalArgumentException("Bad opType: " + opType);
-            }
-        }
-
-
-        private long getDuration(int opType, int curState, long startTime, int memFactor,
-                long now) {
-            int state = opType + (memFactor*SERVICE_COUNT);
-            int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0;
-            if (curState == memFactor) {
-                time += now - startTime;
-            }
-            return time;
-        }
-    }
-
-    public static final class PackageState {
-        final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<String, ProcessState>();
-        final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>();
-        final int mUid;
-
-        public PackageState(int uid) {
-            mUid = uid;
-        }
-    }
-
-    static final class State {
-        // 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;
-
-        static final int FLAG_COMPLETE = 1<<0;
-        static final int FLAG_SHUTDOWN = 1<<1;
-        static final int FLAG_SYSPROPS = 1<<2;
-
-        final File mBaseDir;
-        final ProcessTracker mProcessTracker;
-        AtomicFile mFile;
-        String mReadError;
-
-        long mTimePeriodStartClock;
-        String mTimePeriodStartClockStr;
-        long mTimePeriodStartRealtime;
-        long mTimePeriodEndRealtime;
-        String mRuntime;
-        String mWebView;
-        boolean mRunning;
-        int mFlags;
-
-        final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
-        final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
-        final long[] mMemFactorDurations = new long[ADJ_COUNT];
-        int mMemFactor = STATE_NOTHING;
-        long mStartTime;
-
-        static final int LONGS_SIZE = 4096;
-        final ArrayList<long[]> mLongs = new ArrayList<long[]>();
-        int mNextLong;
-
-        int[] mAddLongTable;
-        int mAddLongTableSize;
-
-        final Object mPendingWriteLock = new Object();
-        AtomicFile mPendingWriteFile;
-        Parcel mPendingWrite;
-        boolean mPendingWriteCommitted;
-        long mLastWriteTime;
-
-        State(File baseDir, ProcessTracker tracker) {
-            mBaseDir = baseDir;
-            reset();
-            mProcessTracker = tracker;
-        }
-
-        State(String file) {
-            mBaseDir = null;
-            reset();
-            mFile = new AtomicFile(new File(file));
-            mProcessTracker = null;
-            readLocked();
-        }
-
-        void reset() {
-            if (DEBUG && mFile != null) Slog.d(TAG, "Resetting state of " + mFile.getBaseFile());
-            resetCommon();
-            mPackages.getMap().clear();
-            mProcesses.getMap().clear();
-            mMemFactor = STATE_NOTHING;
-            mStartTime = 0;
-            if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
-        }
-
-        void resetSafely() {
-            if (DEBUG && mFile != null) Slog.d(TAG, "Safely resetting state of " + mFile.getBaseFile());
-            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 && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
-        }
-
-        private void resetCommon() {
-            mLastWriteTime = SystemClock.uptimeMillis();
-            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();
-            if (mBaseDir != null) {
-                mFile = new AtomicFile(new File(mBaseDir,
-                        STATE_FILE_PREFIX + mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
-            }
-        }
-
-        static byte[] readFully(FileInputStream stream) throws java.io.IOException {
-            int pos = 0;
-            int avail = stream.available();
-            byte[] data = new byte[avail];
-            while (true) {
-                int amt = stream.read(data, pos, data.length-pos);
-                //Log.i("foo", "Read " + amt + " bytes at " + pos
-                //        + " of avail " + data.length);
-                if (amt <= 0) {
-                    //Log.i("foo", "**** FINISHED READING: pos=" + pos
-                    //        + " len=" + data.length);
-                    return data;
-                }
-                pos += amt;
-                avail = stream.available();
-                if (avail > data.length-pos) {
-                    byte[] newData = new byte[pos+avail];
-                    System.arraycopy(data, 0, newData, 0, pos);
-                    data = newData;
-                }
-            }
-        }
-
-        boolean readLocked() {
-            try {
-                FileInputStream stream = mFile.openRead();
-
-                byte[] raw = readFully(stream);
-                Parcel in = Parcel.obtain();
-                in.unmarshall(raw, 0, raw.length);
-                in.setDataPosition(0);
-                stream.close();
-
-                readFromParcel(in);
-                if (mReadError != null) {
-                    Slog.w(TAG, "Ignoring existing stats; " + mReadError);
-                    if (DEBUG) {
-                        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-                        final int NPROC = procMap.size();
-                        for (int ip=0; ip<NPROC; ip++) {
-                            Slog.w(TAG, "Process: " + procMap.keyAt(ip));
-                            SparseArray<ProcessState> uids = procMap.valueAt(ip);
-                            final int NUID = uids.size();
-                            for (int iu=0; iu<NUID; iu++) {
-                                Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
-                            }
-                        }
-                        ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
-                        final int NPKG = pkgMap.size();
-                        for (int ip=0; ip<NPKG; ip++) {
-                            Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
-                            SparseArray<PackageState> uids = pkgMap.valueAt(ip);
-                            final int NUID = uids.size();
-                            for (int iu=0; iu<NUID; iu++) {
-                                Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
-                                PackageState pkgState = uids.valueAt(iu);
-                                final int NPROCS = pkgState.mProcesses.size();
-                                for (int iproc=0; iproc<NPROCS; iproc++) {
-                                    Slog.w(TAG, "    Process " + pkgState.mProcesses.keyAt(iproc)
-                                            + ": " + pkgState.mProcesses.valueAt(iproc));
-                                }
-                                final int NSRVS = pkgState.mServices.size();
-                                for (int isvc=0; isvc<NSRVS; isvc++) {
-                                    Slog.w(TAG, "    Service " + pkgState.mServices.keyAt(isvc)
-                                            + ": " + pkgState.mServices.valueAt(isvc));
-                                }
-                            }
-                        }
-                    }
-                    return false;
-                }
-            } catch (Throwable e) {
-                mReadError = "caught exception: " + e;
-                Slog.e(TAG, "Error reading process statistics", e);
-                return false;
-            }
-            return true;
-        }
-
-        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 + ": "
-                        + State.printLongOffset(table[i]));
-                if (!validateLongOffset(table[i])) {
-                    Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
-                            + State.printLongOffset(table[i]));
-                    return null;
-                }
-            }
-            return table;
-        }
-
-        private void writeStateLocked(boolean sync, final boolean commit) {
-            synchronized (mPendingWriteLock) {
-                long now = SystemClock.uptimeMillis();
-                if (mPendingWrite == null || !mPendingWriteCommitted) {
-                    mPendingWrite = Parcel.obtain();
-                    mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
-                    if (commit) {
-                        mFlags |= State.FLAG_COMPLETE;
-                    }
-                    writeToParcel(mPendingWrite);
-                    mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
-                    mPendingWriteCommitted = commit;
-                }
-                if (commit) {
-                    resetSafely();
-                } else {
-                    mLastWriteTime = SystemClock.uptimeMillis();
-                }
-                Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms");
-                if (!sync) {
-                    BackgroundThread.getHandler().post(new Runnable() {
-                        @Override public void run() {
-                            performWriteState();
-                        }
-                    });
-                    return;
-                }
-            }
-
-            performWriteState();
-        }
-
-        void performWriteState() {
-            if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
-            Parcel data;
-            AtomicFile file;
-            synchronized (mPendingWriteLock) {
-                data = mPendingWrite;
-                file = mPendingWriteFile;
-                mPendingWriteCommitted = false;
-                if (data == null) {
-                    return;
-                }
-                mPendingWrite = null;
-                mPendingWriteFile = null;
-                if (mProcessTracker != null) {
-                    mProcessTracker.mWriteLock.lock();
-                }
-            }
-
-            FileOutputStream stream = null;
-            try {
-                stream = file.startWrite();
-                stream.write(data.marshall());
-                stream.flush();
-                file.finishWrite(stream);
-                if (DEBUG) Slog.d(TAG, "Write completed successfully!");
-            } catch (IOException e) {
-                Slog.w(TAG, "Error writing process statistics", e);
-                file.failWrite(stream);
-            } finally {
-                data.recycle();
-                if (mProcessTracker != null) {
-                    mProcessTracker.trimHistoricStatesWriteLocked();
-                    mProcessTracker.mWriteLock.unlock();
-                }
-            }
-
-        }
-
-        void writeToParcel(Parcel out) {
-            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);
-
-            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);
-
-            ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-            final int NPROC = procMap.size();
-            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);
-                }
-            }
-            ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
-            final int NPKG = pkgMap.size();
-            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;
-        }
-
-        private 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
-        }
-
-        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;
-        }
-
-        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;
-        }
-
-        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("  mFile="); pw.println(mFile.getBaseFile());
-                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) {
-                        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;
-        }
-
-        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();
-                }
-            }
-        }
-
-        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);
-            }
-        }
-
-        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;
-        }
-
-        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 ProcessTracker(Object lock, File file) {
-        mLock = lock;
-        mBaseDir = file;
-        mBaseDir.mkdirs();
-        mState = new State(mBaseDir, this);
-        mState.mRunning = true;
-        SystemProperties.addChangeCallback(new Runnable() {
-            @Override public void run() {
-                synchronized (mLock) {
-                    if (mState.evaluateSystemProperties(false)) {
-                        mState.mFlags |= State.FLAG_SYSPROPS;
-                        mState.writeStateLocked(true, true);
-                        mState.evaluateSystemProperties(true);
-                    }
-                }
-            }
-        });
-    }
-
-    public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
-        return mState.getProcessStateLocked(packageName, uid, processName);
-    }
-
-    public ServiceState getServiceStateLocked(String packageName, int uid,
-            String processName, String className) {
-        final PackageState as = mState.getPackageStateLocked(packageName, uid);
-        ServiceState ss = as.mServices.get(className);
-        if (ss != null) {
-            ss.makeActive();
-            return ss;
-        }
-        final ProcessState ps = mState.getProcessStateLocked(packageName, uid, processName);
-        ss = new ServiceState(mState, packageName, ps);
-        as.mServices.put(className, ss);
-        return ss;
-    }
-
-    public boolean isMemFactorLowered() {
-        return mMemFactorLowered;
-    }
-
-    public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
-        mMemFactorLowered = memFactor < mLastMemOnlyState;
-        mLastMemOnlyState = memFactor;
-        if (screenOn) {
-            memFactor += ADJ_SCREEN_ON;
-        }
-        if (memFactor != mState.mMemFactor) {
-            if (mState.mMemFactor != STATE_NOTHING) {
-                mState.mMemFactorDurations[mState.mMemFactor] += now - mState.mStartTime;
-            }
-            mState.mMemFactor = memFactor;
-            mState.mStartTime = now;
-            ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
-            for (int i=0; i<pmap.size(); i++) {
-                SparseArray<PackageState> uids = pmap.valueAt(i);
-                for (int j=0; j<uids.size(); j++) {
-                    PackageState pkg = uids.valueAt(j);
-                    ArrayMap<String, ServiceState> services = pkg.mServices;
-                    for (int k=0; k<services.size(); k++) {
-                        ServiceState service = services.valueAt(k);
-                        if (service.isActive()) {
-                            if (service.mStartedState != STATE_NOTHING) {
-                                service.setStarted(true, memFactor, now);
-                            }
-                            if (service.mBoundState != STATE_NOTHING) {
-                                service.setBound(true, memFactor, now);
-                            }
-                            if (service.mExecState != STATE_NOTHING) {
-                                service.setExecuting(true, memFactor, now);
-                            }
-                        }
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public int getMemFactorLocked() {
-        return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
-    }
-
-    public void readLocked() {
-        mState.readLocked();
-    }
-
-    public boolean shouldWriteNowLocked(long now) {
-        if (now > (mState.mLastWriteTime+WRITE_PERIOD)) {
-            if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
-                mCommitPending = true;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public void shutdownLocked() {
-        Slog.w(TAG, "Writing process stats before shutdown...");
-        mState.mFlags |= State.FLAG_SHUTDOWN;
-        writeStateSyncLocked();
-        mShuttingDown = true;
-    }
-
-    public void writeStateAsyncLocked() {
-        writeStateLocked(false);
-    }
-
-    public void writeStateSyncLocked() {
-        writeStateLocked(true);
-    }
-
-    private void writeStateLocked(boolean sync) {
-        if (mShuttingDown) {
-            return;
-        }
-        boolean commitPending = mCommitPending;
-        mCommitPending = false;
-        mState.writeStateLocked(sync, commitPending);
-    }
-
-    private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) {
-        File[] files = mBaseDir.listFiles();
-        if (files == null || files.length <= minNum) {
-            return null;
-        }
-        ArrayList<String> filesArray = new ArrayList<String>(files.length);
-        String currentFile = mState.mFile.getBaseFile().getPath();
-        if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
-        for (int i=0; i<files.length; i++) {
-            File file = files[i];
-            String fileStr = file.getPath();
-            if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
-            if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
-                if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
-                continue;
-            }
-            if (fileStr.equals(currentFile)) {
-                if (DEBUG) Slog.d(TAG, "Skipping: current stats");
-                continue;
-            }
-            filesArray.add(fileStr);
-        }
-        Collections.sort(filesArray);
-        return filesArray;
-    }
-
-    public void trimHistoricStatesWriteLocked() {
-        ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true);
-        if (filesArray == null) {
-            return;
-        }
-        while (filesArray.size() > MAX_HISTORIC_STATES) {
-            String file = filesArray.remove(0);
-            Slog.i(TAG, "Pruning old procstats: " + file);
-            (new File(file)).delete();
-        }
-    }
-
-    static private 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;
-        }
-    }
-
-    static private 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;
-        }
-    }
-
-    static private 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;
-        }
-    }
-
-    static private 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("???");
-            }
-        }
-    }
-
-    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.mState.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();
-    }
-
-    static final class ProcessDataCollection {
-        final int[] screenStates;
-        final int[] memStates;
-        final int[] procStates;
-
-        long totalTime;
-        long numPss;
-        long minPss;
-        long avgPss;
-        long maxPss;
-        long minUss;
-        long avgUss;
-        long maxUss;
-
-        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(")");
-            }
-        }
-    }
-
-    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);
-    }
-
-    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();
-        }
-    }
-
-    boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
-            boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
-            boolean sepProcStates, int[] procStates, long now, String reqPackage) {
-        ArrayList<ProcessState> procs = mState.collectProcessesLocked(screenStates, memStates,
-                procStates, now, reqPackage);
-        if (procs.size() > 0) {
-            if (header != null) {
-                pw.println(header);
-            }
-            dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
-                    sepMemStates, memStates, sepProcStates, procStates, now);
-            return true;
-        }
-        return false;
-    }
-
-    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.mState.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.mState.getLong(off, PSS_SAMPLE_COUNT);
-            long min = proc.mState.getLong(off, PSS_MINIMUM);
-            long avg = proc.mState.getLong(off, PSS_AVERAGE);
-            long max = proc.mState.getLong(off, PSS_MAXIMUM);
-            long umin = proc.mState.getLong(off, PSS_USS_MINIMUM);
-            long uavg = proc.mState.getLong(off, PSS_USS_AVERAGE);
-            long umax = proc.mState.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);
-        }
-    }
-
-    static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
-            String[] outError) {
-        ArrayList<Integer> res = new ArrayList<Integer>();
-        int lastPos = 0;
-        for (int i=0; i<=arg.length(); i++) {
-            char c = i < arg.length() ? arg.charAt(i) : 0;
-            if (c != ',' && c != '+' && c != ' ' && c != 0) {
-                continue;
-            }
-            boolean isSep = c == ',';
-            if (lastPos == 0) {
-                // We now know the type of op.
-                outSep[0] = isSep;
-            } else if (c != 0 && outSep[0] != isSep) {
-                outError[0] = "inconsistent separators (can't mix ',' with '+')";
-                return null;
-            }
-            if (lastPos < (i-1)) {
-                String str = arg.substring(lastPos, i);
-                for (int j=0; j<states.length; j++) {
-                    if (str.equals(states[j])) {
-                        res.add(j);
-                        str = null;
-                        break;
-                    }
-                }
-                if (str != null) {
-                    outError[0] = "invalid word \"" + str + "\"";
-                    return null;
-                }
-            }
-            lastPos = i + 1;
-        }
-
-        int[] finalRes = new int[res.size()];
-        for (int i=0; i<res.size(); i++) {
-            finalRes[i] = res.get(i) * mult;
-        }
-        return finalRes;
-    }
-
-    static private void dumpHelp(PrintWriter pw) {
-        pw.println("Process stats (procstats) dump options:");
-        pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
-        pw.println("    [--details] [--current] [--commit] [--write] [-h] [<package.name>]");
-        pw.println("  --checkin: perform a checkin: print and delete old committed states.");
-        pw.println("  --c: print only state in checkin format.");
-        pw.println("  --csv: output data suitable for putting in a spreadsheet.");
-        pw.println("  --csv-screen: on, off.");
-        pw.println("  --csv-mem: norm, mod, low, crit.");
-        pw.println("  --csv-proc: pers, top, fore, vis, precept, backup,");
-        pw.println("    service, home, prev, cached");
-        pw.println("  --details: dump all execution details, not just summary.");
-        pw.println("  --current: only dump current state.");
-        pw.println("  --commit: commit current stats to disk and reset to start new stats.");
-        pw.println("  --write: write current in-memory stats to disk.");
-        pw.println("  --read: replace current stats with last-written stats.");
-        pw.println("  -a: print everything.");
-        pw.println("  -h: print this help text.");
-        pw.println("  <package.name>: optional name of package to filter output by.");
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        final long now = SystemClock.uptimeMillis();
-
-        boolean isCheckin = false;
-        boolean isCompact = false;
-        boolean isCsv = false;
-        boolean currentOnly = false;
-        boolean dumpDetails = false;
-        boolean dumpAll = false;
-        String reqPackage = null;
-        boolean csvSepScreenStats = false;
-        int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON};
-        boolean csvSepMemStats = false;
-        int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL};
-        boolean csvSepProcStats = true;
-        int[] csvProcStats = ALL_PROC_STATES;
-        if (args != null) {
-            for (int i=0; i<args.length; i++) {
-                String arg = args[i];
-                if ("--checkin".equals(arg)) {
-                    isCheckin = true;
-                } else if ("-c".equals(arg)) {
-                    isCompact = true;
-                } else if ("--csv".equals(arg)) {
-                    isCsv = true;
-                } else if ("--csv-screen".equals(arg)) {
-                    i++;
-                    if (i >= args.length) {
-                        pw.println("Error: argument required for --csv-screen");
-                        dumpHelp(pw);
-                        return;
-                    }
-                    boolean[] sep = new boolean[1];
-                    String[] error = new String[1];
-                    csvScreenStats = parseStateList(ADJ_SCREEN_NAMES_CSV, ADJ_SCREEN_MOD,
-                            args[i], sep, error);
-                    if (csvScreenStats == null) {
-                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
-                        dumpHelp(pw);
-                        return;
-                    }
-                    csvSepScreenStats = sep[0];
-                } else if ("--csv-mem".equals(arg)) {
-                    i++;
-                    if (i >= args.length) {
-                        pw.println("Error: argument required for --csv-mem");
-                        dumpHelp(pw);
-                        return;
-                    }
-                    boolean[] sep = new boolean[1];
-                    String[] error = new String[1];
-                    csvMemStats = parseStateList(ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
-                    if (csvMemStats == null) {
-                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
-                        dumpHelp(pw);
-                        return;
-                    }
-                    csvSepMemStats = sep[0];
-                } else if ("--csv-proc".equals(arg)) {
-                    i++;
-                    if (i >= args.length) {
-                        pw.println("Error: argument required for --csv-proc");
-                        dumpHelp(pw);
-                        return;
-                    }
-                    boolean[] sep = new boolean[1];
-                    String[] error = new String[1];
-                    csvProcStats = parseStateList(STATE_NAMES_CSV, 1, args[i], sep, error);
-                    if (csvProcStats == null) {
-                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
-                        dumpHelp(pw);
-                        return;
-                    }
-                    csvSepProcStats = sep[0];
-                } else if ("--details".equals(arg)) {
-                    dumpDetails = true;
-                } else if ("--current".equals(arg)) {
-                    currentOnly = true;
-                } else if ("--commit".equals(arg)) {
-                    mState.mFlags |= State.FLAG_COMPLETE;
-                    mState.writeStateLocked(true, true);
-                    pw.println("Process stats committed.");
-                    return;
-                } else if ("--write".equals(arg)) {
-                    writeStateSyncLocked();
-                    pw.println("Process stats written.");
-                    return;
-                } else if ("--read".equals(arg)) {
-                    readLocked();
-                    pw.println("Process stats read.");
-                    return;
-                } else if ("-h".equals(arg)) {
-                    dumpHelp(pw);
-                    return;
-                } else if ("-a".equals(arg)) {
-                    dumpDetails = true;
-                    dumpAll = true;
-                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
-                    pw.println("Unknown option: " + arg);
-                    dumpHelp(pw);
-                    return;
-                } else {
-                    // Not an option, last argument must be a package name.
-                    try {
-                        IPackageManager pm = AppGlobals.getPackageManager();
-                        if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
-                            reqPackage = arg;
-                            // Include all details, since we know we are only going to
-                            // be dumping a smaller set of data.  In fact only the details
-                            // container per-package data, so that are needed to be able
-                            // to dump anything at all when filtering by package.
-                            dumpDetails = true;
-                        }
-                    } catch (RemoteException e) {
-                    }
-                    if (reqPackage == null) {
-                        pw.println("Unknown package: " + arg);
-                        dumpHelp(pw);
-                        return;
-                    }
-                }
-            }
-        }
-
-        if (isCsv) {
-            pw.print("Processes running summed over");
-            if (!csvSepScreenStats) {
-                for (int i=0; i<csvScreenStats.length; i++) {
-                    pw.print(" ");
-                    printScreenLabelCsv(pw, csvScreenStats[i]);
-                }
-            }
-            if (!csvSepMemStats) {
-                for (int i=0; i<csvMemStats.length; i++) {
-                    pw.print(" ");
-                    printMemLabelCsv(pw, csvMemStats[i]);
-                }
-            }
-            if (!csvSepProcStats) {
-                for (int i=0; i<csvProcStats.length; i++) {
-                    pw.print(" ");
-                    pw.print(STATE_NAMES_CSV[csvProcStats[i]]);
-                }
-            }
-            pw.println();
-            synchronized (mLock) {
-                dumpFilteredProcessesCsvLocked(pw, null,
-                        csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
-                        csvSepProcStats, csvProcStats, now, reqPackage);
-                /*
-                dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
-                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
-                        true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
-                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
-                                STATE_PREVIOUS, STATE_CACHED},
-                        now, reqPackage);
-                dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
-                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
-                        false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
-                                ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
-                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
-                                STATE_PREVIOUS, STATE_CACHED},
-                        now, reqPackage);
-                */
-            }
-            return;
-        }
-
-        boolean sepNeeded = false;
-        if (!currentOnly || isCheckin) {
-            mWriteLock.lock();
-            try {
-                ArrayList<String> files = getCommittedFiles(0, !isCheckin);
-                if (files != null) {
-                    for (int i=0; i<files.size(); i++) {
-                        if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
-                        try {
-                            State state = new State(files.get(i));
-                            if (state.mReadError != null) {
-                                if (isCheckin || isCompact) pw.print("err,");
-                                pw.print("Failure reading "); pw.print(files.get(i));
-                                pw.print("; "); pw.println(state.mReadError);
-                                if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
-                                (new File(files.get(i))).delete();
-                                continue;
-                            }
-                            String fileStr = state.mFile.getBaseFile().getPath();
-                            boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
-                            if (isCheckin || isCompact) {
-                                // Don't really need to lock because we uniquely own this object.
-                                state.dumpCheckinLocked(pw, reqPackage);
-                            } else {
-                                if (sepNeeded) {
-                                    pw.println();
-                                } else {
-                                    sepNeeded = true;
-                                }
-                                pw.print("COMMITTED STATS FROM ");
-                                pw.print(state.mTimePeriodStartClockStr);
-                                if (checkedIn) pw.print(" (checked in)");
-                                pw.println(":");
-                                // Don't really need to lock because we uniquely own this object.
-                                if (dumpDetails) {
-                                    state.dumpLocked(pw, reqPackage, now, dumpAll);
-                                } else {
-                                    state.dumpSummaryLocked(pw, reqPackage, now);
-                                }
-                            }
-                            if (isCheckin) {
-                                // Rename file suffix to mark that it has checked in.
-                                state.mFile.getBaseFile().renameTo(new File(
-                                        fileStr + STATE_FILE_CHECKIN_SUFFIX));
-                            }
-                        } catch (Throwable e) {
-                            pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
-                            e.printStackTrace(pw);
-                        }
-                    }
-                }
-            } finally {
-                mWriteLock.unlock();
-            }
-        }
-        if (!isCheckin) {
-            synchronized (mLock) {
-                if (isCompact) {
-                    mState.dumpCheckinLocked(pw, reqPackage);
-                } else {
-                    if (sepNeeded) {
-                        pw.println();
-                        pw.println("CURRENT STATS:");
-                    }
-                    if (dumpDetails) {
-                        mState.dumpLocked(pw, reqPackage, now, dumpAll);
-                    } else {
-                        mState.dumpSummaryLocked(pw, reqPackage, now);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 14ccece..39756c3 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.NotificationManagerService;
 
@@ -83,7 +84,7 @@
 
     ProcessRecord app;      // where this service is running or null.
     ProcessRecord isolatedProc; // keep track of isolated process, if requested
-    ProcessTracker.ServiceState tracker; // tracking service execution, may be null
+    ProcessStats.ServiceState tracker; // tracking service execution, may be null
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
@@ -310,12 +311,12 @@
         createdFromFg = callerIsFg;
     }
 
-    public ProcessTracker.ServiceState getTracker() {
+    public ProcessStats.ServiceState getTracker() {
         if (tracker != null) {
             return tracker;
         }
         if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
-            tracker = ams.mProcessTracker.getServiceStateLocked(serviceInfo.packageName,
+            tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
                     serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
         }
         return tracker;
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
index 4496aae..ab4ac04 100644
--- a/services/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -48,9 +48,8 @@
 public class IntentFirewall {
     private static final String TAG = "IntentFirewall";
 
-    // e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml
-    private static final File RULES_FILE =
-            new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml");
+    // e.g. /data/system/ifw or /data/secure/system/ifw
+    private static final File RULES_DIR = new File(Environment.getSystemSecureDirectory(), "ifw");
 
     private static final int LOG_PACKAGES_MAX_LENGTH = 150;
     private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
@@ -106,12 +105,12 @@
 
     public IntentFirewall(AMSInterface ams) {
         mAms = ams;
-        File rulesFile = getRulesFile();
-        rulesFile.getParentFile().mkdirs();
+        File rulesDir = getRulesDir();
+        rulesDir.mkdirs();
 
-        readRules(rulesFile);
+        readRulesDir(rulesDir);
 
-        mObserver = new RuleObserver(rulesFile);
+        mObserver = new RuleObserver(rulesDir);
         mObserver.startWatching();
     }
 
@@ -216,22 +215,58 @@
         return null;
     }
 
-    public static File getRulesFile() {
-        return RULES_FILE;
+    public static File getRulesDir() {
+        return RULES_DIR;
     }
 
     /**
-     * Reads rules from the given file and replaces our set of rules with the newly read rules
+     * Reads rules from all xml files (*.xml) in the given directory, and replaces our set of rules
+     * with the newly read rules.
+     *
+     * We only check for files ending in ".xml", to allow for temporary files that are atomically
+     * renamed to .xml
      *
      * All calls to this method from the file observer come through a handler and are inherently
      * serialized
      */
-    private void readRules(File rulesFile) {
+    private void readRulesDir(File rulesDir) {
         FirewallIntentResolver[] resolvers = new FirewallIntentResolver[3];
         for (int i=0; i<resolvers.length; i++) {
             resolvers[i] = new FirewallIntentResolver();
         }
 
+        File[] files = rulesDir.listFiles();
+        for (int i=0; i<files.length; i++) {
+            File file = files[i];
+
+            if (file.getName().endsWith(".xml")) {
+                readRules(file, resolvers);
+            }
+        }
+
+        Slog.i(TAG, "Read new rules (A:" + resolvers[TYPE_ACTIVITY].filterSet().size() +
+                " B:" + resolvers[TYPE_BROADCAST].filterSet().size() +
+                " S:" + resolvers[TYPE_SERVICE].filterSet().size() + ")");
+
+        synchronized (mAms.getAMSLock()) {
+            mActivityResolver = resolvers[TYPE_ACTIVITY];
+            mBroadcastResolver = resolvers[TYPE_BROADCAST];
+            mServiceResolver = resolvers[TYPE_SERVICE];
+        }
+    }
+
+    /**
+     * Reads rules from the given file and add them to the given resolvers
+     */
+    private void readRules(File rulesFile, FirewallIntentResolver[] resolvers) {
+        // some temporary lists to hold the rules while we parse the xml file, so that we can
+        // add the rules all at once, after we know there weren't any major structural problems
+        // with the xml file
+        List<List<Rule>> rulesByType = new ArrayList<List<Rule>>(3);
+        for (int i=0; i<3; i++) {
+            rulesByType.add(new ArrayList<Rule>());
+        }
+
         FileInputStream fis;
         try {
             fis = new FileInputStream(rulesFile);
@@ -247,8 +282,6 @@
 
             XmlUtils.beginDocument(parser, TAG_RULES);
 
-            int[] numRules = new int[3];
-
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 int ruleType = -1;
@@ -265,41 +298,28 @@
                 if (ruleType != -1) {
                     Rule rule = new Rule();
 
-                    FirewallIntentResolver resolver = resolvers[ruleType];
+                    List<Rule> rules = rulesByType.get(ruleType);
 
                     // if we get an error while parsing a particular rule, we'll just ignore
                     // that rule and continue on with the next rule
                     try {
                         rule.readFromXml(parser);
                     } catch (XmlPullParserException ex) {
-                        Slog.e(TAG, "Error reading intent firewall rule", ex);
+                        Slog.e(TAG, "Error reading an intent firewall rule from " + rulesFile, ex);
                         continue;
                     }
 
-                    numRules[ruleType]++;
-
-                    for (int i=0; i<rule.getIntentFilterCount(); i++) {
-                        resolver.addFilter(rule.getIntentFilter(i));
-                    }
+                    rules.add(rule);
                 }
             }
-
-            Slog.i(TAG, "Read new rules (A:" + numRules[TYPE_ACTIVITY] +
-                    " B:" + numRules[TYPE_BROADCAST] + " S:" + numRules[TYPE_SERVICE] + ")");
-
-            synchronized (mAms.getAMSLock()) {
-                mActivityResolver = resolvers[TYPE_ACTIVITY];
-                mBroadcastResolver = resolvers[TYPE_BROADCAST];
-                mServiceResolver = resolvers[TYPE_SERVICE];
-            }
         } catch (XmlPullParserException ex) {
             // if there was an error outside of a specific rule, then there are probably
             // structural problems with the xml file, and we should completely ignore it
-            Slog.e(TAG, "Error reading intent firewall rules", ex);
-            clearRules();
+            Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
+            return;
         } catch (IOException ex) {
-            Slog.e(TAG, "Error reading intent firewall rules", ex);
-            clearRules();
+            Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
+            return;
         } finally {
             try {
                 fis.close();
@@ -307,21 +327,17 @@
                 Slog.e(TAG, "Error while closing " + rulesFile, ex);
             }
         }
-    }
 
-    /**
-     * Clears out all of our rules
-     *
-     * All calls to this method from the file observer come through a handler and are inherently
-     * serialized
-     */
-    private void clearRules() {
-        Slog.i(TAG, "Clearing all rules");
+        for (int ruleType=0; ruleType<rulesByType.size(); ruleType++) {
+            List<Rule> rules = rulesByType.get(ruleType);
+            FirewallIntentResolver resolver = resolvers[ruleType];
 
-        synchronized (mAms.getAMSLock())  {
-            mActivityResolver = new FirewallIntentResolver();
-            mBroadcastResolver = new FirewallIntentResolver();
-            mServiceResolver = new FirewallIntentResolver();
+            for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
+                Rule rule = rules.get(ruleIndex);
+                for (int filterIndex=0; filterIndex<rule.getIntentFilterCount(); filterIndex++) {
+                    resolver.addFilter(rule.getIntentFilter(filterIndex));
+                }
+            }
         }
     }
 
@@ -421,54 +437,32 @@
         }
     }
 
-    private static final int READ_RULES = 0;
-    private static final int CLEAR_RULES = 1;
-
     final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case READ_RULES:
-                    readRules(getRulesFile());
-                    break;
-                case CLEAR_RULES:
-                    clearRules();
-                    break;
-            }
+            readRulesDir(getRulesDir());
         }
     };
 
     /**
-     * Monitors for the creation/deletion/modification of the rule file
+     * Monitors for the creation/deletion/modification of any .xml files in the rule directory
      */
     private class RuleObserver extends FileObserver {
-        // The file name we're monitoring, with no path component
-        private final String mMonitoredFile;
+        private static final int MONITORED_EVENTS = FileObserver.CREATE|FileObserver.MOVED_TO|
+                FileObserver.CLOSE_WRITE|FileObserver.DELETE|FileObserver.MOVED_FROM;
 
-        private static final int CREATED_FLAGS = FileObserver.CREATE|FileObserver.MOVED_TO|
-                FileObserver.CLOSE_WRITE;
-        private static final int DELETED_FLAGS = FileObserver.DELETE|FileObserver.MOVED_FROM;
-
-        public RuleObserver(File monitoredFile) {
-            super(monitoredFile.getParentFile().getAbsolutePath(), CREATED_FLAGS|DELETED_FLAGS);
-            mMonitoredFile = monitoredFile.getName();
+        public RuleObserver(File monitoredDir) {
+            super(monitoredDir.getAbsolutePath(), MONITORED_EVENTS);
         }
 
         @Override
         public void onEvent(int event, String path) {
-            if (path.equals(mMonitoredFile)) {
+            if (path.endsWith(".xml")) {
                 // we wait 250ms before taking any action on an event, in order to dedup multiple
                 // events. E.g. a delete event followed by a create event followed by a subsequent
-                // write+close event;
-                if ((event & CREATED_FLAGS) != 0) {
-                    mHandler.removeMessages(READ_RULES);
-                    mHandler.removeMessages(CLEAR_RULES);
-                    mHandler.sendEmptyMessageDelayed(READ_RULES, 250);
-                } else if ((event & DELETED_FLAGS) != 0) {
-                    mHandler.removeMessages(READ_RULES);
-                    mHandler.removeMessages(CLEAR_RULES);
-                    mHandler.sendEmptyMessageDelayed(CLEAR_RULES, 250);
-                }
+                // write+close event
+                mHandler.removeMessages(0);
+                mHandler.sendEmptyMessageDelayed(0, 250);
             }
         }
     }
diff --git a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
index 9185903..0b54f92 100644
--- a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
+++ b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
@@ -21,7 +21,8 @@
 public class IntentFirewallInstallReceiver extends ConfigUpdateInstallReceiver {
 
     public IntentFirewallInstallReceiver() {
-        super(IntentFirewall.getRulesFile().getParent(), IntentFirewall.getRulesFile().getName(),
-                "metadata/", "version");
+        // TODO: should we dynamically generate a filename and store the name in metadata?
+        super(IntentFirewall.getRulesDir().getAbsolutePath(), "ifw.xml", "metadata/",
+                "gservices.version");
     }
 }