Initial super-primitive process tracker.

The goal of this is to keep track of what app processes
are doing, to determine who is being abusive, when the system
is getting into memory constrained situations, and help the
user determine how to resolve this.

Right now it doesn't really do any of that, just keeps track
of how long every process has been running since boot.

Also update the activity manager to use "cached" as the terminology
for what it used to interchangeably call hidden and background
processes, and switch ProcessMap over to using ArrayMap.

Change-Id: I270b0006aab1f38e17b7d9b65728679173c343f2
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index a77379e..98c4352 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -57,7 +57,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
-public class ActiveServices {
+public final class ActiveServices {
     static final boolean DEBUG_SERVICE = ActivityManagerService.DEBUG_SERVICE;
     static final boolean DEBUG_SERVICE_EXECUTING = ActivityManagerService.DEBUG_SERVICE_EXECUTING;
     static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index bb20174..f06b9a6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -26,6 +26,7 @@
 
 import android.app.AppOpsManager;
 import android.appwidget.AppWidgetManager;
+import android.util.ArrayMap;
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
@@ -401,6 +402,12 @@
     final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
 
     /**
+     * Tracking long-term execution of processes to look for abuse and other
+     * bad app behavior.
+     */
+    final ProcessTracker mProcessTracker = new ProcessTracker();
+
+    /**
      * The currently running isolated processes.
      */
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
@@ -614,8 +621,8 @@
      * by the user ID the sticky is for, and can include UserHandle.USER_ALL
      * for stickies that are sent to all users.
      */
-    final SparseArray<HashMap<String, ArrayList<Intent>>> mStickyBroadcasts =
-            new SparseArray<HashMap<String, ArrayList<Intent>>>();
+    final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
+            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
 
     final ActiveServices mServices;
 
@@ -803,16 +810,16 @@
     int mLruSeq = 0;
 
     /**
-     * Keep track of the non-hidden/empty process we last found, to help
-     * determine how to distribute hidden/empty processes next time.
+     * Keep track of the non-cached/empty process we last found, to help
+     * determine how to distribute cached/empty processes next time.
      */
-    int mNumNonHiddenProcs = 0;
+    int mNumNonCachedProcs = 0;
 
     /**
-     * Keep track of the number of hidden procs, to balance oom adj
+     * Keep track of the number of cached procs, to balance oom adj
      * distribution between those and empty procs.
      */
-    int mNumHiddenProcs = 0;
+    int mNumCachedProcs = 0;
 
     /**
      * Keep track of the number of service processes we last found, to
@@ -895,7 +902,7 @@
      */
     boolean mBooted = false;
 
-    int mProcessLimit = ProcessList.MAX_HIDDEN_APPS;
+    int mProcessLimit = ProcessList.MAX_CACHED_APPS;
     int mProcessLimitOverride = -1;
 
     WindowManagerService mWindowManager;
@@ -1476,6 +1483,7 @@
             ServiceManager.addService("meminfo", new MemBinder(m));
             ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             ServiceManager.addService("dbinfo", new DbBinder(m));
+            ServiceManager.addService("procstats", new ProcBinder(m));
             if (MONITOR_CPU_USAGE) {
                 ServiceManager.addService("cpuinfo", new CpuBinder(m));
             }
@@ -1692,6 +1700,26 @@
         }
     }
 
+    static class ProcBinder extends Binder {
+        ActivityManagerService mActivityManagerService;
+        ProcBinder(ActivityManagerService activityManagerService) {
+            mActivityManagerService = activityManagerService;
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump procstats from from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                        + " without permission " + android.Manifest.permission.DUMP);
+                return;
+            }
+
+            mActivityManagerService.dumpProcessTracker(fd, pw, args);
+        }
+    }
+
     private ActivityManagerService() {
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
 
@@ -1780,7 +1808,9 @@
             // We need to tell all apps about the system property change.
             ArrayList<IBinder> procs = new ArrayList<IBinder>();
             synchronized(this) {
-                for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+                final int NP = mProcessNames.getMap().size();
+                for (int ip=0; ip<NP; ip++) {
+                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
                     for (int ia=0; ia<NA; ia++) {
                         ProcessRecord app = apps.valueAt(ia);
@@ -2000,22 +2030,22 @@
             // If this process contains content providers, we want to keep
             // it a little more strongly.
             app.lruWeight = app.lastActivityTime - ProcessList.CONTENT_APP_IDLE_OFFSET;
-            // Also don't let it kick out the first few "real" hidden processes.
-            skipTop = ProcessList.MIN_HIDDEN_APPS;
+            // Also don't let it kick out the first few "real" cached processes.
+            skipTop = ProcessList.MIN_CACHED_APPS;
         } else {
             // If this process doesn't have activities, we less strongly
             // want to keep it around, and generally want to avoid getting
             // in front of any very recently used activities.
             app.lruWeight = app.lastActivityTime - ProcessList.EMPTY_APP_IDLE_OFFSET;
-            // Also don't let it kick out the first few "real" hidden processes.
-            skipTop = ProcessList.MIN_HIDDEN_APPS;
+            // Also don't let it kick out the first few "real" cached processes.
+            skipTop = ProcessList.MIN_CACHED_APPS;
         }
 
         while (i >= 0) {
             ProcessRecord p = mLruProcesses.get(i);
             // If this app shouldn't be in front of the first N background
-            // apps, then skip over that many that are currently hidden.
-            if (skipTop > 0 && p.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+            // apps, then skip over that many that are currently cached.
+            if (skipTop > 0 && p.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                 skipTop--;
             }
             if (p.lruWeight <= app.lruWeight || i < bestPos) {
@@ -3179,7 +3209,7 @@
                 boolean haveBg = false;
                 for (int i=mLruProcesses.size()-1; i>=0; i--) {
                     ProcessRecord rec = mLruProcesses.get(i);
-                    if (rec.thread != null && rec.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+                    if (rec.thread != null && rec.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                         haveBg = true;
                         break;
                     }
@@ -3703,7 +3733,9 @@
         try {
             synchronized(this) {
                 ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
-                for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+                final int NP = mProcessNames.getMap().size();
+                for (int ip=0; ip<NP; ip++) {
+                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
                     for (int ia=0; ia<NA; ia++) {
                         ProcessRecord app = apps.valueAt(ia);
@@ -3713,7 +3745,7 @@
                         }
                         if (app.removed) {
                             procs.add(app);
-                        } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+                        } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                             app.removed = true;
                             procs.add(app);
                         }
@@ -3940,7 +3972,9 @@
         // same UID (except for the system or root user), and all whose name
         // matches the package name.
         final String procNamePrefix = packageName != null ? (packageName + ":") : null;
-        for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+        final int NP = mProcessNames.getMap().size();
+        for (int ip=0; ip<NP; ip++) {
+            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
             final int NA = apps.size();
             for (int ia=0; ia<NA; ia++) {
                 ProcessRecord app = apps.valueAt(ia);
@@ -4026,9 +4060,9 @@
                 Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
             }
 
-            Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
-            while (badApps.hasNext()) {
-                SparseArray<Long> ba = badApps.next();
+            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+            for (int ip=pmap.size()-1; ip>=0; ip--) {
+                SparseArray<Long> ba = pmap.valueAt(ip);
                 for (i=ba.size()-1; i>=0; i--) {
                     boolean remove = false;
                     final int entUid = ba.keyAt(i);
@@ -4050,7 +4084,7 @@
                     }
                 }
                 if (ba.size() == 0) {
-                    badApps.remove();
+                    pmap.removeAt(ip);
                 }
             }
         }
@@ -4974,7 +5008,7 @@
         enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
                 "setProcessLimit()");
         synchronized (this) {
-            mProcessLimit = max < 0 ? ProcessList.MAX_HIDDEN_APPS : max;
+            mProcessLimit = max < 0 ? ProcessList.MAX_CACHED_APPS : max;
             mProcessLimitOverride = max;
         }
         trimApplications();
@@ -5945,12 +5979,12 @@
     @Override
     public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
         final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
-        final long hiddenAppMem = mProcessList.getMemLevel(ProcessList.HIDDEN_APP_MIN_ADJ);
+        final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
         outInfo.availMem = Process.getFreeMemory();
         outInfo.totalMem = Process.getTotalMemory();
         outInfo.threshold = homeAppMem;
-        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2));
-        outInfo.hiddenAppThreshold = hiddenAppMem;
+        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
+        outInfo.hiddenAppThreshold = cachedAppMem;
         outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
                 ProcessList.SERVICE_ADJ);
         outInfo.visibleAppThreshold = mProcessList.getMemLevel(
@@ -6177,10 +6211,11 @@
             // Find any running processes associated with this app.
             final String pkg = component.getPackageName();
             ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
-            HashMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
-            for (SparseArray<ProcessRecord> uids : pmap.values()) {
-                for (int i=0; i<uids.size(); i++) {
-                    ProcessRecord proc = uids.valueAt(i);
+            ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+            for (int i=0; i<pmap.size(); i++) {
+                SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+                for (int j=0; j<uids.size(); j++) {
+                    ProcessRecord proc = uids.valueAt(j);
                     if (proc.userId != tr.userId) {
                         continue;
                     }
@@ -7279,7 +7314,8 @@
         synchronized (stats) {
             ps = stats.getProcessStatsLocked(info.uid, proc);
         }
-        return new ProcessRecord(ps, thread, info, proc, uid);
+        return new ProcessRecord(ps, thread, info, proc, uid,
+                mProcessTracker.getStateLocked(info.processName, info.uid));
     }
 
     final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
@@ -7908,11 +7944,11 @@
                 }
             }
             
-            // If the worst oom_adj is somewhere in the hidden proc LRU range,
-            // then constrain it so we will kill all hidden procs.
-            if (worstType < ProcessList.HIDDEN_APP_MAX_ADJ
-                    && worstType > ProcessList.HIDDEN_APP_MIN_ADJ) {
-                worstType = ProcessList.HIDDEN_APP_MIN_ADJ;
+            // If the worst oom_adj is somewhere in the cached proc LRU range,
+            // then constrain it so we will kill all cached procs.
+            if (worstType < ProcessList.CACHED_APP_MAX_ADJ
+                    && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
+                worstType = ProcessList.CACHED_APP_MIN_ADJ;
             }
 
             // If this is not a secure call, don't let it kill processes that
@@ -8815,7 +8851,9 @@
         }
 
         synchronized (this) {
-            for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+            final int NP = mProcessNames.getMap().size();
+            for (int ip=0; ip<NP; ip++) {
+                SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                 final int NA = apps.size();
                 for (int ia=0; ia<NA; ia++) {
                     ProcessRecord p = apps.valueAt(ia);
@@ -9174,9 +9212,9 @@
     }
 
     static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
-        if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+        if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
             if (currApp != null) {
-                currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
+                currApp.lru = adj - ProcessList.CACHED_APP_MIN_ADJ + 1;
             }
             return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
         } else if (adj >= ProcessList.SERVICE_B_ADJ) {
@@ -9585,7 +9623,9 @@
         pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
 
         if (dumpAll) {
-            for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) {
+            final int NP = mProcessNames.getMap().size();
+            for (int ip=0; ip<NP; ip++) {
+                SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
                 final int NA = procs.size();
                 for (int ia=0; ia<NA; ia++) {
                     ProcessRecord r = procs.valueAt(ia);
@@ -9715,10 +9755,11 @@
         if (mProcessCrashTimes.getMap().size() > 0) {
             boolean printed = false;
             long now = SystemClock.uptimeMillis();
-            for (Map.Entry<String, SparseArray<Long>> procs
-                    : mProcessCrashTimes.getMap().entrySet()) {
-                String pname = procs.getKey();
-                SparseArray<Long> uids = procs.getValue();
+            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+            final int NP = pmap.size();
+            for (int ip=0; ip<NP; ip++) {
+                String pname = pmap.keyAt(ip);
+                SparseArray<Long> uids = pmap.valueAt(ip);
                 final int N = uids.size();
                 for (int i=0; i<N; i++) {
                     int puid = uids.keyAt(i);
@@ -9745,10 +9786,11 @@
 
         if (mBadProcesses.getMap().size() > 0) {
             boolean printed = false;
-            for (Map.Entry<String, SparseArray<Long>> procs
-                    : mBadProcesses.getMap().entrySet()) {
-                String pname = procs.getKey();
-                SparseArray<Long> uids = procs.getValue();
+            final ArrayMap<String, SparseArray<Long>> pmap = mBadProcesses.getMap();
+            final int NP = pmap.size();
+            for (int ip=0; ip<NP; ip++) {
+                String pname = pmap.keyAt(ip);
+                SparseArray<Long> uids = pmap.valueAt(ip);
                 final int N = uids.size();
                 for (int i=0; i<N; i++) {
                     int puid = uids.keyAt(i);
@@ -9915,8 +9957,8 @@
                 pw.println("  mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
                 pw.println("  mLaunchingActivity=" + getFocusedStack().mLaunchingActivity);
                 pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
-                pw.println("  mNumNonHiddenProcs=" + mNumNonHiddenProcs
-                        + " mNumHiddenProcs=" + mNumHiddenProcs
+                pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
+                        + " mNumCachedProcs=" + mNumCachedProcs
                         + " mNumServiceProcs=" + mNumServiceProcs
                         + " mNewNumServiceProcs=" + mNewNumServiceProcs);
             }
@@ -9975,8 +10017,8 @@
             pw.print("    HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ);
             pw.print("    PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ);
             pw.print("    SERVICE_B_ADJ: "); pw.println(ProcessList.SERVICE_B_ADJ);
-            pw.print("    HIDDEN_APP_MIN_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MIN_ADJ);
-            pw.print("    HIDDEN_APP_MAX_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MAX_ADJ);
+            pw.print("    CACHED_APP_MIN_ADJ: "); pw.println(ProcessList.CACHED_APP_MIN_ADJ);
+            pw.print("    CACHED_APP_MAX_ADJ: "); pw.println(ProcessList.CACHED_APP_MAX_ADJ);
 
             if (needSep) pw.println();
             needSep = true;
@@ -10462,8 +10504,8 @@
         for (int i=list.size()-1; i>=0; i--) {
             ProcessRecord r = list.get(i).first;
             String oomAdj;
-            if (r.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
-                oomAdj = buildOomTag("bak", "  ", r.setAdj, ProcessList.HIDDEN_APP_MIN_ADJ);
+            if (r.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                oomAdj = buildOomTag("cch", "  ", r.setAdj, ProcessList.CACHED_APP_MIN_ADJ);
             } else if (r.setAdj >= ProcessList.SERVICE_B_ADJ) {
                 oomAdj = buildOomTag("svcb ", null, r.setAdj, ProcessList.SERVICE_B_ADJ);
             } else if (r.setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
@@ -10538,8 +10580,8 @@
                 pw.print(prefix);
                 pw.print("    ");
                 pw.print("oom: max="); pw.print(r.maxAdj);
-                pw.print(" hidden="); pw.print(r.hiddenAdj);
-                pw.print(" client="); pw.print(r.clientHiddenAdj);
+                pw.print(" cached="); pw.print(r.cachedAdj);
+                pw.print(" client="); pw.print(r.clientCachedAdj);
                 pw.print(" empty="); pw.print(r.emptyAdj);
                 pw.print(" curRaw="); pw.print(r.curRawAdj);
                 pw.print(" setRaw="); pw.print(r.setRawAdj);
@@ -10548,7 +10590,7 @@
                 pw.print(prefix);
                 pw.print("    ");
                 pw.print("keeping="); pw.print(r.keeping);
-                pw.print(" hidden="); pw.print(r.hidden);
+                pw.print(" cached="); pw.print(r.cached);
                 pw.print(" empty="); pw.print(r.empty);
                 pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
 
@@ -10760,13 +10802,13 @@
             ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
             ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
             ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
-            ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ
+            ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ
     };
     static final String[] DUMP_MEM_OOM_LABEL = new String[] {
             "System", "Persistent", "Foreground",
             "Visible", "Perceptible", "Heavy Weight",
             "Backup", "A Services", "Home", "Previous",
-            "B Services", "Background"
+            "B Services", "Cached"
     };
 
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
@@ -11034,6 +11076,13 @@
         return false;
     }
 
+    final void dumpProcessTracker(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (this) {
+            pw.println("Process Stats:");
+            mProcessTracker.dumpLocked(fd, pw, args);
+        }
+    }
+
     private final boolean removeDyingProviderLocked(ProcessRecord proc,
             ContentProviderRecord cpr, boolean always) {
         final boolean inLaunching = mLaunchingProviders.contains(cpr);
@@ -11679,7 +11728,7 @@
     private final List getStickiesLocked(String action, IntentFilter filter,
             List cur, int userId) {
         final ContentResolver resolver = mContext.getContentResolver();
-        HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
         if (stickies == null) {
             return cur;
         }
@@ -12187,7 +12236,7 @@
                 // But first, if this is not a broadcast to all users, then
                 // make sure it doesn't conflict with an existing broadcast to
                 // all users.
-                HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
+                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                         UserHandle.USER_ALL);
                 if (stickies != null) {
                     ArrayList<Intent> list = stickies.get(intent.getAction());
@@ -12204,9 +12253,9 @@
                     }
                 }
             }
-            HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
             if (stickies == null) {
-                stickies = new HashMap<String, ArrayList<Intent>>();
+                stickies = new ArrayMap<String, ArrayList<Intent>>();
                 mStickyBroadcasts.put(userId, stickies);
             }
             ArrayList<Intent> list = stickies.get(intent.getAction());
@@ -12459,7 +12508,7 @@
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             }
-            HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
             if (stickies != null) {
                 ArrayList<Intent> list = stickies.get(intent.getAction());
                 if (list != null) {
@@ -12952,18 +13001,18 @@
         return null;
     }
 
-    private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, int clientHiddenAdj,
+    private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, int clientCachedAdj,
             int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
         if (mAdjSeq == app.adjSeq) {
             // This adjustment has already been computed.  If we are calling
             // from the top, we may have already computed our adjustment with
-            // an earlier hidden adjustment that isn't really for us... if
-            // so, use the new hidden adjustment.
-            if (!recursed && app.hidden) {
+            // an earlier cached adjustment that isn't really for us... if
+            // so, use the new cached adjustment.
+            if (!recursed && app.cached) {
                 if (app.hasActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj;
+                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = cachedAdj;
                 } else if (app.hasClientActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientHiddenAdj;
+                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientCachedAdj;
                 } else {
                     app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj;
                 }
@@ -12974,14 +13023,14 @@
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
+            return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
         }
 
         app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
         app.adjSource = null;
         app.adjTarget = null;
         app.empty = false;
-        app.hidden = false;
+        app.cached = false;
         app.hasClientActivities = false;
 
         final int activitiesSize = app.activities.size();
@@ -13059,12 +13108,12 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "exec-service";
         } else {
-            // Assume process is hidden (has activities); we will correct
+            // Assume process is cached (has activities); we will correct
             // later if this is not the case.
-            adj = hiddenAdj;
+            adj = cachedAdj;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.hidden = true;
-            app.adjType = "bg-act";
+            app.cached = true;
+            app.adjType = "cch-act";
         }
 
         boolean hasStoppingActivities = false;
@@ -13080,7 +13129,7 @@
                         app.adjType = "visible";
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
-                    app.hidden = false;
+                    app.cached = false;
                     app.hasActivities = true;
                     foregroundActivities = true;
                     break;
@@ -13089,13 +13138,13 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
-                    app.hidden = false;
+                    app.cached = false;
                     foregroundActivities = true;
                 } else if (r.state == ActivityState.STOPPING) {
                     // We will apply the actual adjustment later, because
                     // we want to allow this process to immediately go through
                     // any memory trimming that is in effect.
-                    app.hidden = false;
+                    app.cached = false;
                     foregroundActivities = true;
                     hasStoppingActivities = true;
                 }
@@ -13105,16 +13154,16 @@
             }
         }
 
-        if (adj == hiddenAdj && !app.hasActivities) {
+        if (adj == cachedAdj && !app.hasActivities) {
             if (app.hasClientActivities) {
-                adj = clientHiddenAdj;
-                app.adjType = "bg-client-act";
+                adj = clientCachedAdj;
+                app.adjType = "cch-client-act";
             } else {
                 // Whoops, this process is completely empty as far as we know
                 // at this point.
                 adj = emptyAdj;
                 app.empty = true;
-                app.adjType = "bg-empty";
+                app.adjType = "cch-empty";
             }
         }
 
@@ -13122,13 +13171,13 @@
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.hidden = false;
+                app.cached = false;
                 app.adjType = "fg-service";
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             } else if (app.forcingToForeground != null) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.hidden = false;
+                app.cached = false;
                 app.adjType = "force-fg";
                 app.adjSource = app.forcingToForeground;
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -13143,7 +13192,7 @@
             // We don't want to kill the current heavy-weight process.
             adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.hidden = false;
+            app.cached = false;
             app.adjType = "heavy";
         }
 
@@ -13152,7 +13201,7 @@
             // home app, so we don't want to let it go into the background.
             adj = ProcessList.HOME_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.hidden = false;
+            app.cached = false;
             app.adjType = "home";
         }
 
@@ -13163,7 +13212,7 @@
             // a good experience around switching between two apps.
             adj = ProcessList.PREVIOUS_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.hidden = false;
+            app.cached = false;
             app.adjType = "previous";
         }
 
@@ -13183,7 +13232,7 @@
                 if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);
                 adj = ProcessList.BACKUP_APP_ADJ;
                 app.adjType = "backup";
-                app.hidden = false;
+                app.cached = false;
             }
         }
 
@@ -13202,7 +13251,7 @@
                         // UI stuff.  We'll tag it with a label just to help
                         // debug and understand what is going on.
                         if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "started-bg-ui-services";
+                            app.adjType = "cch-started-ui-services";
                         }
                     } else {
                         if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
@@ -13212,14 +13261,14 @@
                             if (adj > ProcessList.SERVICE_ADJ) {
                                 adj = ProcessList.SERVICE_ADJ;
                                 app.adjType = "started-services";
-                                app.hidden = false;
+                                app.cached = false;
                             }
                         }
                         // If we have let the service slide into the background
                         // state, still have some text describing what it is doing
                         // even though the service no longer has an impact.
                         if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "started-bg-services";
+                            app.adjType = "cch-started-services";
                         }
                     }
                     // Don't kill this process because it is doing work; it
@@ -13245,20 +13294,20 @@
                         if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                             ProcessRecord client = cr.binding.client;
                             int clientAdj = adj;
-                            int myHiddenAdj = hiddenAdj;
-                            if (myHiddenAdj > client.hiddenAdj) {
-                                if (client.hiddenAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myHiddenAdj = client.hiddenAdj;
+                            int myCachedAdj = cachedAdj;
+                            if (myCachedAdj > client.cachedAdj) {
+                                if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
+                                    myCachedAdj = client.cachedAdj;
                                 } else {
-                                    myHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
+                                    myCachedAdj = ProcessList.VISIBLE_APP_ADJ;
                                 }
                             }
-                            int myClientHiddenAdj = clientHiddenAdj;
-                            if (myClientHiddenAdj > client.clientHiddenAdj) {
-                                if (client.clientHiddenAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myClientHiddenAdj = client.clientHiddenAdj;
+                            int myClientCachedAdj = clientCachedAdj;
+                            if (myClientCachedAdj > client.clientCachedAdj) {
+                                if (client.clientCachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
+                                    myClientCachedAdj = client.clientCachedAdj;
                                 } else {
-                                    myClientHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
+                                    myClientCachedAdj = ProcessList.VISIBLE_APP_ADJ;
                                 }
                             }
                             int myEmptyAdj = emptyAdj;
@@ -13269,8 +13318,8 @@
                                     myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
                                 }
                             }
-                            clientAdj = computeOomAdjLocked(client, myHiddenAdj,
-                                    myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll);
+                            clientAdj = computeOomAdjLocked(client, myCachedAdj,
+                                    myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
                             String adjType = null;
                             if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                                 // Not doing bind OOM management, so treat
@@ -13281,9 +13330,9 @@
                                     // UI stuff.  We'll tag it with a label just to help
                                     // debug and understand what is going on.
                                     if (adj > clientAdj) {
-                                        adjType = "bound-bg-ui-services";
+                                        adjType = "cch-bound-ui-services";
                                     }
-                                    app.hidden = false;
+                                    app.cached = false;
                                     clientAdj = adj;
                                 } else {
                                     if (now >= (s.lastActivity
@@ -13294,7 +13343,7 @@
                                         // it around.  We'll also tag it with a label just
                                         // to help debug and undertand what is going on.
                                         if (adj > clientAdj) {
-                                            adjType = "bound-bg-services";
+                                            adjType = "cch-bound-services";
                                         }
                                         clientAdj = adj;
                                     }
@@ -13305,7 +13354,7 @@
                                     // created, then we want to try to better follow
                                     // its memory management semantics for activities.
                                     // That is, if it is sitting in the background
-                                    // LRU list as a hidden process (with activities),
+                                    // LRU list as a cached process (with activities),
                                     // we don't want the service it is connected to
                                     // to go into the empty LRU and quickly get killed,
                                     // because I'll we'll do is just end up restarting
@@ -13323,7 +13372,7 @@
                                 // memory.
                                 if (app.hasShownUi && app != mHomeProcess
                                         && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    adjType = "bound-bg-ui-services";
+                                    adjType = "cch-bound-ui-services";
                                 } else {
                                     if ((cr.flags&(Context.BIND_ABOVE_CLIENT
                                             |Context.BIND_IMPORTANT)) != 0) {
@@ -13340,8 +13389,8 @@
                                             adj = ProcessList.VISIBLE_APP_ADJ;
                                         }
                                     }
-                                    if (!client.hidden) {
-                                        app.hidden = false;
+                                    if (!client.cached) {
+                                        app.cached = false;
                                     }
                                     if (client.keeping) {
                                         app.keeping = true;
@@ -13372,7 +13421,7 @@
                                 if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                                 }
-                                app.hidden = false;
+                                app.cached = false;
                                 app.adjType = "service";
                                 app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                         .REASON_SERVICE_IN_USE;
@@ -13390,10 +13439,10 @@
             // application from running.  By default we put the process in
             // with the rest of the background processes; as we scan through
             // its services we may bump it up from there.
-            if (adj > hiddenAdj) {
-                adj = hiddenAdj;
-                app.hidden = false;
-                app.adjType = "bg-services";
+            if (adj > cachedAdj) {
+                adj = cachedAdj;
+                app.cached = false;
+                app.adjType = "cch-services";
             }
         }
 
@@ -13412,20 +13461,20 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                int myHiddenAdj = hiddenAdj;
-                if (myHiddenAdj > client.hiddenAdj) {
-                    if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                        myHiddenAdj = client.hiddenAdj;
+                int myCachedAdj = cachedAdj;
+                if (myCachedAdj > client.cachedAdj) {
+                    if (client.cachedAdj > ProcessList.FOREGROUND_APP_ADJ) {
+                        myCachedAdj = client.cachedAdj;
                     } else {
-                        myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
+                        myCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
                     }
                 }
-                int myClientHiddenAdj = clientHiddenAdj;
-                if (myClientHiddenAdj > client.clientHiddenAdj) {
-                    if (client.clientHiddenAdj >= ProcessList.FOREGROUND_APP_ADJ) {
-                        myClientHiddenAdj = client.clientHiddenAdj;
+                int myClientCachedAdj = clientCachedAdj;
+                if (myClientCachedAdj > client.clientCachedAdj) {
+                    if (client.clientCachedAdj >= ProcessList.FOREGROUND_APP_ADJ) {
+                        myClientCachedAdj = client.clientCachedAdj;
                     } else {
-                        myClientHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
+                        myClientCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
                     }
                 }
                 int myEmptyAdj = emptyAdj;
@@ -13436,19 +13485,19 @@
                         myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
                     }
                 }
-                int clientAdj = computeOomAdjLocked(client, myHiddenAdj,
-                        myClientHiddenAdj, myEmptyAdj, TOP_APP, true, doingAll);
+                int clientAdj = computeOomAdjLocked(client, myCachedAdj,
+                        myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
                 if (adj > clientAdj) {
                     if (app.hasShownUi && app != mHomeProcess
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        app.adjType = "bg-ui-provider";
+                        app.adjType = "cch-ui-provider";
                     } else {
                         adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
                         app.adjType = "provider";
                     }
-                    if (!client.hidden) {
-                        app.hidden = false;
+                    if (!client.cached) {
+                        app.cached = false;
                     }
                     if (client.keeping) {
                         app.keeping = true;
@@ -13470,7 +13519,7 @@
                 if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                     adj = ProcessList.FOREGROUND_APP_ADJ;
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
-                    app.hidden = false;
+                    app.cached = false;
                     app.keeping = true;
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
@@ -13510,7 +13559,7 @@
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
-        if (adj < ProcessList.HIDDEN_APP_MIN_ADJ) {
+        if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
             app.keeping = true;
         }
 
@@ -13526,9 +13575,9 @@
                 adj = ProcessList.VISIBLE_APP_ADJ;
             } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-            } else if (adj < ProcessList.HIDDEN_APP_MIN_ADJ) {
-                adj = ProcessList.HIDDEN_APP_MIN_ADJ;
-            } else if (adj < ProcessList.HIDDEN_APP_MAX_ADJ) {
+            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+                adj = ProcessList.CACHED_APP_MIN_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
                 adj++;
             }
         }
@@ -13542,7 +13591,7 @@
                 // interesting in this process then we will push it to the
                 // background importance.
                 importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
-            } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+            } else if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
                 importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
             } else if (adj >= ProcessList.SERVICE_B_ADJ) {
                 importance =  ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
@@ -13848,10 +13897,10 @@
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
-            int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
-        app.hiddenAdj = hiddenAdj;
-        app.clientHiddenAdj = clientHiddenAdj;
+    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
+        app.cachedAdj = cachedAdj;
+        app.clientCachedAdj = clientCachedAdj;
         app.emptyAdj = emptyAdj;
 
         if (app.thread == null) {
@@ -13862,7 +13911,7 @@
 
         boolean success = true;
 
-        computeOomAdjLocked(app, hiddenAdj, clientHiddenAdj, emptyAdj, TOP_APP, false, doingAll);
+        computeOomAdjLocked(app, cachedAdj, clientCachedAdj, emptyAdj, TOP_APP, false, doingAll);
 
         if (app.curRawAdj != app.setRawAdj) {
             if (wasKeeping && !app.keeping) {
@@ -13886,6 +13935,10 @@
                     TAG, "Set " + app.pid + " " + app.processName +
                     " adj " + app.curAdj + ": " + app.adjType);
                 app.setAdj = app.curAdj;
+                if (app.setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
+                    app.tracker.setState(mProcessList.adjToTrackedState(app.setAdj),
+                            SystemClock.uptimeMillis());
+                }
             } else {
                 success = false;
                 Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
@@ -13937,17 +13990,17 @@
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         int curAdj = app.curAdj;
-        final boolean wasHidden = curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
-            && curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
+        final boolean wasCached = curAdj >= ProcessList.CACHED_APP_MIN_ADJ
+            && curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
 
         mAdjSeq++;
 
-        boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.clientHiddenAdj,
+        boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
                 app.emptyAdj, TOP_APP, false);
-        final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
-            && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
-        if (nowHidden != wasHidden) {
-            // Changed to/from hidden state, so apps after it in the LRU
+        final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
+            && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
+        if (nowCached != wasCached) {
+            // Changed to/from cached state, so apps after it in the LRU
             // list may also be changed.
             updateOomAdjLocked();
         }
@@ -13969,100 +14022,100 @@
         mNewNumServiceProcs = 0;
 
         final int emptyProcessLimit;
-        final int hiddenProcessLimit;
+        final int cachedProcessLimit;
         if (mProcessLimit <= 0) {
-            emptyProcessLimit = hiddenProcessLimit = 0;
+            emptyProcessLimit = cachedProcessLimit = 0;
         } else if (mProcessLimit == 1) {
             emptyProcessLimit = 1;
-            hiddenProcessLimit = 0;
+            cachedProcessLimit = 0;
         } else {
             emptyProcessLimit = (mProcessLimit*2)/3;
-            hiddenProcessLimit = mProcessLimit - emptyProcessLimit;
+            cachedProcessLimit = mProcessLimit - emptyProcessLimit;
         }
 
         // Let's determine how many processes we have running vs.
         // how many slots we have for background processes; we may want
         // to put multiple processes in a slot of there are enough of
         // them.
-        int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ
-                - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2;
-        int numEmptyProcs = mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs;
-        if (numEmptyProcs > hiddenProcessLimit) {
-            // If there are more empty processes than our limit on hidden
-            // processes, then use the hidden process limit for the factor.
+        int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
+        int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedProcs;
+        if (numEmptyProcs > cachedProcessLimit) {
+            // If there are more empty processes than our limit on cached
+            // processes, then use the cached process limit for the factor.
             // This ensures that the really old empty processes get pushed
             // down to the bottom, so if we are running low on memory we will
-            // have a better chance at keeping around more hidden processes
+            // have a better chance at keeping around more cached processes
             // instead of a gazillion empty processes.
-            numEmptyProcs = hiddenProcessLimit;
+            numEmptyProcs = cachedProcessLimit;
         }
         int emptyFactor = numEmptyProcs/numSlots;
         if (emptyFactor < 1) emptyFactor = 1;
-        int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots;
-        if (hiddenFactor < 1) hiddenFactor = 1;
-        int stepHidden = 0;
+        int cachedFactor = (mNumCachedProcs > 0 ? mNumCachedProcs : 1)/numSlots;
+        if (cachedFactor < 1) cachedFactor = 1;
+        int stepCached = 0;
         int stepEmpty = 0;
-        int numHidden = 0;
+        int numCached = 0;
         int numEmpty = 0;
         int numTrimming = 0;
 
-        mNumNonHiddenProcs = 0;
-        mNumHiddenProcs = 0;
+        mNumNonCachedProcs = 0;
+        mNumCachedProcs = 0;
 
         // First update the OOM adjustment for each of the
         // application processes based on their current state.
         int i = mLruProcesses.size();
-        int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
-        int nextHiddenAdj = curHiddenAdj+1;
-        int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
+        int nextCachedAdj = curCachedAdj+1;
+        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextEmptyAdj = curEmptyAdj+2;
-        int curClientHiddenAdj = curEmptyAdj;
+        int curClientCachedAdj = curEmptyAdj;
         while (i > 0) {
             i--;
             ProcessRecord app = mLruProcesses.get(i);
-            //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);
-            updateOomAdjLocked(app, curHiddenAdj, curClientHiddenAdj, curEmptyAdj, TOP_APP, true);
+            //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
+            updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, true);
             if (!app.killedBackground) {
-                if (app.curRawAdj == curHiddenAdj && app.hasActivities) {
-                    // This process was assigned as a hidden process...  step the
-                    // hidden level.
-                    mNumHiddenProcs++;
-                    if (curHiddenAdj != nextHiddenAdj) {
-                        stepHidden++;
-                        if (stepHidden >= hiddenFactor) {
-                            stepHidden = 0;
-                            curHiddenAdj = nextHiddenAdj;
-                            nextHiddenAdj += 2;
-                            if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
-                                nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                if (app.curRawAdj == curCachedAdj && app.hasActivities) {
+                    // This process was assigned as a cached process...  step the
+                    // cached level.
+                    mNumCachedProcs++;
+                    if (curCachedAdj != nextCachedAdj) {
+                        stepCached++;
+                        if (stepCached >= cachedFactor) {
+                            stepCached = 0;
+                            curCachedAdj = nextCachedAdj;
+                            nextCachedAdj += 2;
+                            if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                             }
-                            if (curClientHiddenAdj <= curHiddenAdj) {
-                                curClientHiddenAdj = curHiddenAdj + 1;
-                                if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
-                                    curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                            if (curClientCachedAdj <= curCachedAdj) {
+                                curClientCachedAdj = curCachedAdj + 1;
+                                if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                    curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                                 }
                             }
                         }
                     }
-                    numHidden++;
-                    if (numHidden > hiddenProcessLimit) {
+                    numCached++;
+                    if (numCached > cachedProcessLimit) {
                         Slog.i(TAG, "No longer want " + app.processName
-                                + " (pid " + app.pid + "): hidden #" + numHidden);
+                                + " (pid " + app.pid + "): cached #" + numCached);
                         EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
                                 app.processName, app.setAdj, "too many background");
                         app.killedBackground = true;
                         Process.killProcessQuiet(app.pid);
                     }
-                } else if (app.curRawAdj == curHiddenAdj && app.hasClientActivities) {
+                } else if (app.curRawAdj == curCachedAdj && app.hasClientActivities) {
                     // This process has a client that has activities.  We will have
-                    // given it the current hidden adj; here we will just leave it
-                    // without stepping the hidden adj.
-                    curClientHiddenAdj++;
-                    if (curClientHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
-                        curClientHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                    // given it the current cached adj; here we will just leave it
+                    // without stepping the cached adj.
+                    curClientCachedAdj++;
+                    if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                        curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                     }
                 } else {
-                    if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) {
+                    if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curCachedAdj) {
                         // This process was assigned as an empty process...  step the
                         // empty level.
                         if (curEmptyAdj != nextEmptyAdj) {
@@ -14071,15 +14124,15 @@
                                 stepEmpty = 0;
                                 curEmptyAdj = nextEmptyAdj;
                                 nextEmptyAdj += 2;
-                                if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
-                                    nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                                if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                    nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
                                 }
                             }
                         }
-                    } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
-                        mNumNonHiddenProcs++;
+                    } else if (app.curRawAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+                        mNumNonCachedProcs++;
                     }
-                    if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
+                    if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
                             && !app.hasClientActivities) {
                         if (numEmpty > ProcessList.TRIM_EMPTY_APPS
                                 && app.lastActivityTime < oldTime) {
@@ -14134,9 +14187,9 @@
         // are managing to keep around is less than half the maximum we desire;
         // if we are keeping a good number around, we'll let them use whatever
         // memory they want.
-        if (numHidden <= ProcessList.TRIM_HIDDEN_APPS
+        if (numCached <= ProcessList.TRIM_CACHED_APPS
                 && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
-            final int numHiddenAndEmpty = numHidden + numEmpty;
+            final int numCachedAndEmpty = numCached + numEmpty;
             final int N = mLruProcesses.size();
             int factor = numTrimming/3;
             int minFactor = 2;
@@ -14145,9 +14198,9 @@
             if (factor < minFactor) factor = minFactor;
             int step = 0;
             int fgTrimLevel;
-            if (numHiddenAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+            if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
-            } else if (numHiddenAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+            } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
             } else {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
@@ -14422,7 +14475,7 @@
         }
 
         if (proc == null) {
-            HashMap<String, SparseArray<ProcessRecord>> all
+            ArrayMap<String, SparseArray<ProcessRecord>> all
                     = mProcessNames.getMap();
             SparseArray<ProcessRecord> procs = all.get(process);
             if (procs != null && procs.size() > 0) {
diff --git a/services/java/com/android/server/am/ActivityResult.java b/services/java/com/android/server/am/ActivityResult.java
index 12eba34..6d5bdeb 100644
--- a/services/java/com/android/server/am/ActivityResult.java
+++ b/services/java/com/android/server/am/ActivityResult.java
@@ -23,7 +23,7 @@
 /**
  * Pending result information to send back to an activity.
  */
-class ActivityResult extends ResultInfo {
+final class ActivityResult extends ResultInfo {
     final ActivityRecord mFrom;
     
     public ActivityResult(ActivityRecord from, String resultWho,
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index b02a3e8..af61bfb 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -80,7 +80,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class ActivityStackSupervisor {
+public final class ActivityStackSupervisor {
     static final boolean DEBUG = ActivityManagerService.DEBUG || false;
     static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
     static final boolean DEBUG_APP = DEBUG || false;
diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java
index f1c54fa..06265fd 100644
--- a/services/java/com/android/server/am/AppBindRecord.java
+++ b/services/java/com/android/server/am/AppBindRecord.java
@@ -23,7 +23,7 @@
 /**
  * An association between a service and one of its client applications.
  */
-class AppBindRecord {
+final class AppBindRecord {
     final ServiceRecord service;    // The running service.
     final IntentBindRecord intent;  // The intent we are bound to.
     final ProcessRecord client;     // Who has started/bound the service.
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index ffa1e92..bfc86b0 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -25,7 +25,7 @@
 import android.os.Message;
 import android.view.WindowManager;
 
-class AppErrorDialog extends BaseErrorDialog {
+final class AppErrorDialog extends BaseErrorDialog {
     private final ActivityManagerService mService;
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
diff --git a/services/java/com/android/server/am/AppErrorResult.java b/services/java/com/android/server/am/AppErrorResult.java
index ebfcfe2..c6a5720 100644
--- a/services/java/com/android/server/am/AppErrorResult.java
+++ b/services/java/com/android/server/am/AppErrorResult.java
@@ -16,8 +16,7 @@
 
 package com.android.server.am;
 
-
-class AppErrorResult {
+final class AppErrorResult {
     public void set(int res) {
         synchronized (this) {
             mHasResult = true;
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index af61c9b..b5e0715 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -28,7 +28,7 @@
 import android.util.Slog;
 import android.view.WindowManager;
 
-class AppNotRespondingDialog extends BaseErrorDialog {
+final class AppNotRespondingDialog extends BaseErrorDialog {
     private static final String TAG = "AppNotRespondingDialog";
 
     // Event 'what' codes
diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index d08bb10..27865a8 100644
--- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -22,7 +22,7 @@
 import android.os.Message;
 import android.view.WindowManager;
 
-class AppWaitingForDebuggerDialog extends BaseErrorDialog {
+final class AppWaitingForDebuggerDialog extends BaseErrorDialog {
     final ActivityManagerService mService;
     final ProcessRecord mProc;
     private CharSequence mAppName;
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
index 7e73106..5fa7e6a 100644
--- a/services/java/com/android/server/am/BackupRecord.java
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -21,7 +21,7 @@
 import android.content.pm.ApplicationInfo;
 
 /** @hide */
-class BackupRecord {
+final class BackupRecord {
     // backup/restore modes
     public static final int BACKUP_NORMAL = 0;
     public static final int BACKUP_FULL = 1;
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index c631b6e..986b8ea 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -22,7 +22,7 @@
 
 import java.io.PrintWriter;
 
-class BroadcastFilter extends IntentFilter {
+final class BroadcastFilter extends IntentFilter {
     // Back-pointer to the list this filter is in.
     final ReceiverList receiverList;
     final String packageName;
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index ac7eb89..c7e6010 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -47,7 +47,7 @@
  * We keep two broadcast queues and associated bookkeeping, one for those at
  * foreground priority, and one for normal (background-priority) broadcasts.
  */
-public class BroadcastQueue {
+public final class BroadcastQueue {
     static final String TAG = "BroadcastQueue";
     static final String TAG_MU = ActivityManagerService.TAG_MU;
     static final boolean DEBUG_BROADCAST = ActivityManagerService.DEBUG_BROADCAST;
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 83cc0ea..eb1df6e 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -36,7 +36,7 @@
 /**
  * An active intent broadcast.
  */
-class BroadcastRecord extends Binder {
+final class BroadcastRecord extends Binder {
     final Intent intent;    // the original intent that generated us
     final ComponentName targetComp; // original component name set on the intent
     final ProcessRecord callerApp; // process that sent this
diff --git a/services/java/com/android/server/am/CompatModeDialog.java b/services/java/com/android/server/am/CompatModeDialog.java
index 0442bda..202cc7c 100644
--- a/services/java/com/android/server/am/CompatModeDialog.java
+++ b/services/java/com/android/server/am/CompatModeDialog.java
@@ -28,7 +28,7 @@
 import android.widget.CompoundButton;
 import android.widget.Switch;
 
-public class CompatModeDialog extends Dialog {
+public final class CompatModeDialog extends Dialog {
     final ActivityManagerService mService;
     final ApplicationInfo mAppInfo;
 
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index e697f88..f56371c 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -25,7 +25,7 @@
 import android.util.Slog;
 import android.util.Xml;
 
-public class CompatModePackages {
+public final class CompatModePackages {
     private final String TAG = ActivityManagerService.TAG;
     private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
 
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index 4ed3c31..dd81493 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -25,7 +25,7 @@
 /**
  * Description of a single binding to a service.
  */
-class ConnectionRecord {
+final class ConnectionRecord {
     final AppBindRecord binding;    // The application/service binding.
     final ActivityRecord activity;   // If non-null, the owning activity.
     final IServiceConnection conn;  // The client connection.
diff --git a/services/java/com/android/server/am/ContentProviderConnection.java b/services/java/com/android/server/am/ContentProviderConnection.java
index 7f69b24..f2c9e2f 100644
--- a/services/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/java/com/android/server/am/ContentProviderConnection.java
@@ -23,7 +23,7 @@
 /**
  * Represents a link between a content provider and client.
  */
-public class ContentProviderConnection extends Binder {
+public final class ContentProviderConnection extends Binder {
     public final ContentProviderRecord provider;
     public final ProcessRecord client;
     public final long createTime;
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 8fb6a93..646b7d2 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -33,7 +33,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 
-class ContentProviderRecord {
+final class ContentProviderRecord {
     final ActivityManagerService service;
     public final ProviderInfo info;
     final int uid;
diff --git a/services/java/com/android/server/am/CoreSettingsObserver.java b/services/java/com/android/server/am/CoreSettingsObserver.java
index 585cf2b..10ea67c 100644
--- a/services/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/java/com/android/server/am/CoreSettingsObserver.java
@@ -33,7 +33,7 @@
  * disk I/O operations. Note: This class assumes that all core settings reside
  * in {@link Settings.Secure}.
  */
-class CoreSettingsObserver extends ContentObserver {
+final class CoreSettingsObserver extends ContentObserver {
     private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
 
     // mapping form property name to its type
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java
deleted file mode 100644
index 21e7252..0000000
--- a/services/java/com/android/server/am/DeviceMonitor.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2008 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.os.SELinux;
-import android.util.Slog;
-
-import java.io.*;
-import java.util.Arrays;
-
-/**
- * Monitors device resources periodically for some period of time. Useful for
- * tracking down performance problems.
- */
-class DeviceMonitor {
-
-    private static final String LOG_TAG = DeviceMonitor.class.getName();
-
-    /** Number of samples to take. */
-    private static final int SAMPLE_COUNT = 10;
-
-    /** Time to wait in ms between samples. */
-    private static final int INTERVAL = 1000;
-
-    /** Time to wait in ms between samples. */
-    private static final int MAX_FILES = 30;
-
-    private final byte[] buffer = new byte[1024];
-
-    /** Is the monitor currently running? */
-    private boolean running = false;
-
-    private DeviceMonitor() {
-        new Thread() {
-            public void run() {
-                monitor();
-            }
-        }.start();
-    }
-
-    /**
-     * Loops continuously. Pauses until someone tells us to start monitoring.
-     */
-    @SuppressWarnings("InfiniteLoopStatement")
-    private void monitor() {
-        while (true) {
-            waitForStart();
-
-            purge();
-
-            for (int i = 0; i < SAMPLE_COUNT; i++) {
-                try {
-                    dump();
-                } catch (IOException e) {
-                    Slog.w(LOG_TAG, "Dump failed.", e);
-                }
-                pause();
-            }
-
-            stop();
-        }
-    }
-
-    private static final File PROC = new File("/proc");
-    private static final File BASE = new File("/data/anr/");
-    static {
-        if (!BASE.isDirectory() && !BASE.mkdirs()) {
-            throw new AssertionError("Couldn't create " + BASE + ".");
-        }
-        if (!SELinux.restorecon(BASE)) {
-            throw new AssertionError("Couldn't restorecon " + BASE + ".");
-        }
-    }
-
-    private static final File[] PATHS = {
-        new File(PROC, "zoneinfo"),
-        new File(PROC, "interrupts"),
-        new File(PROC, "meminfo"),
-        new File(PROC, "slabinfo"),
-    };
-
-
-    /**
-     * Deletes old files.
-     */
-    private void purge() {
-        File[] files = BASE.listFiles();
-        int count = files.length - MAX_FILES;
-        if (count > 0) {
-            Arrays.sort(files);
-            for (int i = 0; i < count; i++) {
-                if (!files[i].delete()) {
-                    Slog.w(LOG_TAG, "Couldn't delete " + files[i] + ".");
-                }
-            }
-        }
-    }
-
-    /**
-     * Dumps the current device stats to a new file.
-     */
-    private void dump() throws IOException {
-        OutputStream out = new FileOutputStream(
-                new File(BASE, String.valueOf(System.currentTimeMillis())));
-        try {
-            // Copy /proc/*/stat
-            for (File processDirectory : PROC.listFiles()) {
-                if (isProcessDirectory(processDirectory)) {
-                    dump(new File(processDirectory, "stat"), out);
-                }
-            }
-
-            // Copy other files.
-            for (File file : PATHS) {
-                dump(file, out);
-            }
-        } finally {
-            closeQuietly(out);
-        }
-    }
-
-    /**
-     * Returns true if the given file represents a process directory.
-     */
-    private static boolean isProcessDirectory(File file) {
-        try {
-            Integer.parseInt(file.getName());
-            return file.isDirectory();
-        } catch (NumberFormatException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Copies from a file to an output stream.
-     */
-    private void dump(File from, OutputStream out) throws IOException {
-        writeHeader(from, out);
-        
-        FileInputStream in = null;
-        try {
-            in = new FileInputStream(from);
-            int count;
-            while ((count = in.read(buffer)) != -1) {
-                out.write(buffer, 0, count);
-            }
-        } finally {
-            closeQuietly(in);
-        }
-    }
-
-    /**
-     * Writes a header for the given file.
-     */
-    private static void writeHeader(File file, OutputStream out)
-            throws IOException {
-        String header = "*** " + file.toString() + "\n";
-        out.write(header.getBytes());
-    }
-
-    /**
-     * Closes the given resource. Logs exceptions.
-     * @param closeable
-     */
-    private static void closeQuietly(Closeable closeable) {
-        try {
-            if (closeable != null) {
-                closeable.close();
-            }
-        } catch (IOException e) {
-            Slog.w(LOG_TAG, e);
-        }
-    }
-
-    /**
-     * Pauses momentarily before we start the next dump.
-     */
-    private void pause() {
-        try {
-            Thread.sleep(INTERVAL);
-        } catch (InterruptedException e) { /* ignore */ }
-    }
-
-    /**
-     * Stops dumping.
-     */
-    private synchronized void stop() {
-        running = false;        
-    }
-
-    /**
-     * Waits until someone starts us.
-     */
-    private synchronized void waitForStart() {
-        while (!running) {
-            try {
-                wait();
-            } catch (InterruptedException e) { /* ignore */ }
-        }
-    }
-
-    /**
-     * Instructs the monitoring to start if it hasn't already.
-     */
-    private synchronized void startMonitoring() {
-        if (!running) {
-            running = true;
-            notifyAll();
-        }
-    }
-
-    private static DeviceMonitor instance = new DeviceMonitor();
-
-    /**
-     * Starts monitoring if it hasn't started already.
-     */
-    static void start() {
-        instance.startMonitoring();
-    }
-}
diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java
index 0ffb588..f4632c1 100644
--- a/services/java/com/android/server/am/FactoryErrorDialog.java
+++ b/services/java/com/android/server/am/FactoryErrorDialog.java
@@ -22,7 +22,7 @@
 import android.os.Message;
 import android.view.WindowManager;
 
-class FactoryErrorDialog extends BaseErrorDialog {
+final class FactoryErrorDialog extends BaseErrorDialog {
     public FactoryErrorDialog(Context context, CharSequence msg) {
         super(context);
         setCancelable(false);
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index 0a92964..6c84b9f 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -27,7 +27,7 @@
 /**
  * A particular Intent that has been bound to a Service.
  */
-class IntentBindRecord {
+final class IntentBindRecord {
     /** The running service. */
     final ServiceRecord service;
     /** The intent that is bound.*/
diff --git a/services/java/com/android/server/am/LaunchWarningWindow.java b/services/java/com/android/server/am/LaunchWarningWindow.java
index cb2b7bb..30c3066 100644
--- a/services/java/com/android/server/am/LaunchWarningWindow.java
+++ b/services/java/com/android/server/am/LaunchWarningWindow.java
@@ -26,7 +26,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-public class LaunchWarningWindow extends Dialog {
+public final class LaunchWarningWindow extends Dialog {
     public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
         super(context, R.style.Theme_Toast);
 
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index a9454bd..307ab03 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -40,7 +40,7 @@
  *
  * Note that this component runs in a separate thread.
  */
-class NativeCrashListener extends Thread {
+final class NativeCrashListener extends Thread {
     static final String TAG = "NativeCrashListener";
     static final boolean DEBUG = false;
     static final boolean MORE_DEBUG = DEBUG && false;
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 28593fe..17f24a9 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -31,7 +31,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 
-class PendingIntentRecord extends IIntentSender.Stub {
+final class PendingIntentRecord extends IIntentSender.Stub {
     final ActivityManagerService owner;
     final Key key;
     final int uid;
diff --git a/services/java/com/android/server/am/PendingThumbnailsRecord.java b/services/java/com/android/server/am/PendingThumbnailsRecord.java
index c460791..e4eb4d0 100644
--- a/services/java/com/android/server/am/PendingThumbnailsRecord.java
+++ b/services/java/com/android/server/am/PendingThumbnailsRecord.java
@@ -24,7 +24,7 @@
  * This class keeps track of calls to getTasks() that are still
  * waiting for thumbnail images.
  */
-class PendingThumbnailsRecord
+final class PendingThumbnailsRecord
 {
     final IThumbnailReceiver receiver;   // who is waiting.
     final HashSet<ActivityRecord> pendingRecords; // HistoryRecord objects we still wait for.
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 1a635a9..e937d35 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -29,7 +29,7 @@
 /**
  * Activity manager code dealing with processes.
  */
-class ProcessList {
+final class ProcessList {
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
     static final int MIN_CRASH_INTERVAL = 60*1000;
@@ -38,8 +38,8 @@
 
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
-    static final int HIDDEN_APP_MAX_ADJ = 15;
-    static int HIDDEN_APP_MIN_ADJ = 9;
+    static final int CACHED_APP_MAX_ADJ = 15;
+    static int CACHED_APP_MIN_ADJ = 9;
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
@@ -94,37 +94,37 @@
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4*1024;
 
-    // The minimum number of hidden apps we want to be able to keep around,
+    // The minimum number of cached apps we want to be able to keep around,
     // without empty apps being able to push them out of memory.
-    static final int MIN_HIDDEN_APPS = 2;
+    static final int MIN_CACHED_APPS = 2;
 
-    // The maximum number of hidden processes we will keep around before
+    // The maximum number of cached processes we will keep around before
     // killing them; this is just a control to not let us go too crazy with
     // keeping around processes on devices with large amounts of RAM.
-    static final int MAX_HIDDEN_APPS = 24;
+    static final int MAX_CACHED_APPS = 24;
 
     // We allow empty processes to stick around for at most 30 minutes.
     static final long MAX_EMPTY_TIME = 30*60*1000;
 
-    // The number of hidden at which we don't consider it necessary to do
+    // The number of cached at which we don't consider it necessary to do
     // memory trimming.
-    static final int TRIM_HIDDEN_APPS = 3;
+    static final int TRIM_CACHED_APPS = 3;
 
     // The number of empty apps at which we don't consider it necessary to do
     // memory trimming.
     static final int TRIM_EMPTY_APPS = 3;
 
-    // Threshold of number of hidden+empty where we consider memory critical.
+    // Threshold of number of cached+empty where we consider memory critical.
     static final int TRIM_CRITICAL_THRESHOLD = 3;
 
-    // Threshold of number of hidden+empty where we consider memory critical.
+    // Threshold of number of cached+empty where we consider memory critical.
     static final int TRIM_LOW_THRESHOLD = 5;
 
-    // We put empty content processes after any hidden processes that have
+    // We put empty content processes after any cached processes that have
     // been idle for less than 15 seconds.
     static final long CONTENT_APP_IDLE_OFFSET = 15*1000;
 
-    // We put empty content processes after any hidden processes that have
+    // We put empty content processes after any cached processes that have
     // been idle for less than 120 seconds.
     static final long EMPTY_APP_IDLE_OFFSET = 120*1000;
 
@@ -133,7 +133,7 @@
     // can't give it a different value for every possible kind of process.
     private final int[] mOomAdj = new int[] {
             FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
-            BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ
+            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
     };
     // These are the low-end OOM level limits.  This is appropriate for an
     // HVGA or smaller phone with less than 512MB.  Values are in KB.
@@ -154,11 +154,34 @@
 
     private boolean mHaveDisplaySize;
 
+    private final int[] mAdjToTrackedState = new int[CACHED_APP_MAX_ADJ+1];
+
     ProcessList() {
         MemInfoReader minfo = new MemInfoReader();
         minfo.readMemInfo();
         mTotalMemMb = minfo.getTotalSize()/(1024*1024);
         updateOomLevels(0, 0, false);
+        for (int i=0; i<=CACHED_APP_MAX_ADJ; i++) {
+            if (i <= FOREGROUND_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_FOREGROUND;
+            } else if (i <= VISIBLE_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_VISIBLE;
+            } else if (i <= PERCEPTIBLE_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_PERCEPTIBLE;
+            } else if (i <= BACKUP_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_BACKUP;
+            } else if (i <= SERVICE_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
+            } else if (i <= HOME_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_HOME;
+            } else if (i <= PREVIOUS_APP_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_PREVIOUS;
+            } else if (i <= SERVICE_B_ADJ) {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
+            } else {
+                mAdjToTrackedState[i] = ProcessTracker.STATE_CACHED;
+            }
+        }
     }
 
     void applyDisplaySize(WindowManagerService wm) {
@@ -220,6 +243,10 @@
         return mOomMinFree[mOomAdj.length-1] * 1024;
     }
 
+    int adjToTrackedState(int adj) {
+        return mAdjToTrackedState[adj];
+    }
+
     private void writeFile(String path, String data) {
         FileOutputStream fos = null;
         try {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 3a4a34c..6cae67c 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -44,13 +44,14 @@
  * Full information about a particular process that
  * is currently running.
  */
-class ProcessRecord {
+final class ProcessRecord {
     final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
     final ApplicationInfo info; // all about the first app in the process
     final boolean isolated;     // true if this is a special isolated process
     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 tracker; // tracking execution of process
     // List of packages running in the process
     final HashSet<String> pkgList = new HashSet<String>();
     IApplicationThread thread;  // the actual proc...  may be null only if
@@ -61,8 +62,8 @@
     long lastActivityTime;      // For managing the LRU list
     long lruWeight;             // Weight for ordering in LRU list
     int maxAdj;                 // Maximum OOM adjustment for this process
-    int hiddenAdj;              // If hidden, this is the adjustment to use
-    int clientHiddenAdj;        // If empty but hidden client, this is the adjustment to use
+    int cachedAdj;              // If cached, this is the adjustment to use
+    int clientCachedAdj;        // If empty but cached client, this is the adjustment to use
     int emptyAdj;               // If empty, this is the adjustment to use
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
@@ -108,7 +109,7 @@
     long lastLowMemory;         // When we last told the app that memory is low
     boolean reportLowMemory;    // Set to true when waiting to report low mem
     boolean empty;              // Is this an empty background process?
-    boolean hidden;             // Is this a hidden process?
+    boolean cached;             // Is this a cached process?
     int lastPss;                // Last pss size reported by app.
     String adjType;             // Debugging: primary thing impacting oom_adj.
     int adjTypeCode;            // Debugging: adj code to report to app.
@@ -201,11 +202,11 @@
                 pw.print(" lruWeight="); pw.print(lruWeight);
                 pw.print(" serviceb="); pw.print(serviceb);
                 pw.print(" keeping="); pw.print(keeping);
-                pw.print(" hidden="); pw.print(hidden);
+                pw.print(" cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
-                pw.print(" hidden="); pw.print(hiddenAdj);
-                pw.print(" client="); pw.print(clientHiddenAdj);
+                pw.print(" cached="); pw.print(cachedAdj);
+                pw.print(" client="); pw.print(clientCachedAdj);
                 pw.print(" empty="); pw.print(emptyAdj);
                 pw.print(" curRaw="); pw.print(curRawAdj);
                 pw.print(" setRaw="); pw.print(setRawAdj);
@@ -325,17 +326,19 @@
     }
     
     ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
-            ApplicationInfo _info, String _processName, int _uid) {
+            ApplicationInfo _info, String _processName, int _uid,
+            ProcessTracker.ProcessState _tracker) {
         batteryStats = _batteryStats;
         info = _info;
         isolated = _info.uid != _uid;
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
+        tracker = _tracker;
         pkgList.add(_info.packageName);
         thread = _thread;
-        maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
-        hiddenAdj = clientHiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+        maxAdj = ProcessList.CACHED_APP_MAX_ADJ;
+        cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
         curRawAdj = setRawAdj = -100;
         curAdj = setAdj = -100;
         persistent = false;
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
new file mode 100644
index 0000000..4eb71c7
--- /dev/null
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -0,0 +1,110 @@
+/*
+ * 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.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import com.android.server.ProcessMap;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public final class ProcessTracker {
+    public static final int STATE_NOTHING = -1;
+    public static final int STATE_TOP = 0;
+    public static final int STATE_FOREGROUND = 1;
+    public static final int STATE_VISIBLE = 2;
+    public static final int STATE_PERCEPTIBLE = 3;
+    public static final int STATE_BACKUP = 4;
+    public static final int STATE_SERVICE = 5;
+    public static final int STATE_HOME = 6;
+    public static final int STATE_PREVIOUS = 7;
+    public static final int STATE_CACHED = 8;
+    public static final int STATE_SCREEN_ON_MOD = STATE_CACHED+1;
+    public static final int STATE_COUNT = STATE_SCREEN_ON_MOD*2;
+
+    static String[] STATE_NAMES = new String[] {
+            "Top        ", "Foreground ", "Visible    ", "Perceptible", "Backup     ",
+            "Service    ", "Home       ", "Previous   ", "Cached     "
+    };
+
+    public static final class ProcessState {
+        final long[] mTimes = new long[STATE_COUNT];
+        int mCurState = STATE_NOTHING;
+        long mStartTime;
+
+        public void setState(int state, long now) {
+            if (mCurState != STATE_NOTHING) {
+                mTimes[mCurState] += now - mStartTime;
+            }
+            mCurState = state;
+            mStartTime = now;
+        }
+    }
+
+    static final class State {
+        final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+    }
+
+    final State mState = new State();
+
+    public ProcessTracker() {
+    }
+
+    public ProcessState getStateLocked(String name, int uid) {
+        ProcessState ps = mState.mProcesses.get(name, uid);
+        if (ps != null) {
+            return ps;
+        }
+        ps = new ProcessState();
+        mState.mProcesses.put(name, uid, ps);
+        return ps;
+    }
+
+    public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final long now = SystemClock.uptimeMillis();
+        ArrayMap<String, SparseArray<ProcessState>> pmap = mState.mProcesses.getMap();
+        for (int ip=0; ip<pmap.size(); ip++) {
+            String procName = pmap.keyAt(ip);
+            SparseArray<ProcessState> procs = pmap.valueAt(ip);
+            for (int iu=0; iu<procs.size(); iu++) {
+                int uid = procs.keyAt(iu);
+                ProcessState state = procs.valueAt(iu);
+                pw.print("  "); pw.print(procName); pw.print(" / "); pw.print(uid); pw.println(":");
+                long totalTime = 0;
+                for (int is=0; is<STATE_NAMES.length; is++) {
+                    long time = state.mTimes[is];
+                    if (state.mCurState == is) {
+                        time += now - state.mStartTime;
+                    }
+                    if (time != 0) {
+                        pw.print("    "); pw.print(STATE_NAMES[is]); pw.print(": ");
+                        TimeUtils.formatDuration(time, pw); pw.println();
+                        totalTime += time;
+                    }
+                }
+                if (totalTime != 0) {
+                    pw.print("    TOTAL      : ");
+                    TimeUtils.formatDuration(totalTime, pw);
+                    pw.println();
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index ee406c8..5759a44 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -35,7 +35,7 @@
  * Keeps track of content providers by authority (name) and class. It separates the mapping by
  * user and ones that are not user-specific (system providers).
  */
-public class ProviderMap {
+public final class ProviderMap {
 
     private static final String TAG = "ProviderMap";
 
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 9b6701e..b19cc5d 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -32,7 +32,7 @@
  * A receiver object that has registered for one or more broadcasts.
  * The ArrayList holds BroadcastFilter objects.
  */
-class ReceiverList extends ArrayList<BroadcastFilter>
+final class ReceiverList extends ArrayList<BroadcastFilter>
         implements IBinder.DeathRecipient {
     final ActivityManagerService owner;
     public final IIntentReceiver receiver;
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 707e8ee..45e248b 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -47,7 +47,7 @@
 /**
  * A running application service.
  */
-class ServiceRecord extends Binder {
+final class ServiceRecord extends Binder {
     // Maximum number of delivery attempts before giving up.
     static final int MAX_DELIVERY_COUNT = 3;
 
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
index 35d50a1..b6d0daa 100644
--- a/services/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -25,7 +25,7 @@
 import android.os.Message;
 import android.util.Slog;
 
-class StrictModeViolationDialog extends BaseErrorDialog {
+final class StrictModeViolationDialog extends BaseErrorDialog {
     private final static String TAG = "StrictModeViolationDialog";
 
     private final ActivityManagerService mService;
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 58392ef..d03da20 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -33,7 +33,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-class TaskRecord extends ThumbnailHolder {
+final class TaskRecord extends ThumbnailHolder {
     final int taskId;       // Unique identifier for this task.
     final String affinity;  // The affinity name for this task, or null.
     Intent intent;          // The original intent that started the task.
diff --git a/services/java/com/android/server/am/TransferPipe.java b/services/java/com/android/server/am/TransferPipe.java
index c3f4f93..055c577 100644
--- a/services/java/com/android/server/am/TransferPipe.java
+++ b/services/java/com/android/server/am/TransferPipe.java
@@ -32,7 +32,7 @@
 /**
  * Helper for transferring data through a pipe from a client app.
  */
-class TransferPipe implements Runnable {
+final class TransferPipe implements Runnable {
     static final String TAG = "TransferPipe";
     static final boolean DEBUG = false;
 
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index cba8e0d..5e2ad009 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -35,7 +35,7 @@
  * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
  *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
  */
-class UriPermission {
+final class UriPermission {
     private static final String TAG = "UriPermission";
 
     final int userHandle;
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
index 90ac88d..7bbd3bc 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -24,7 +24,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 
-class UriPermissionOwner {
+final class UriPermissionOwner {
     final ActivityManagerService service;
     final Object owner;
 
diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
index 0e71f81..d3e73d5 100644
--- a/services/java/com/android/server/am/UserStartedState.java
+++ b/services/java/com/android/server/am/UserStartedState.java
@@ -22,7 +22,7 @@
 import android.app.IStopUserCallback;
 import android.os.UserHandle;
 
-public class UserStartedState {
+public final class UserStartedState {
     // User is first coming up.
     public final static int STATE_BOOTING = 0;
     // User is in the normal running state.