Add support for freezing cached apps

Freeze apps when cached and unfreeze them once removed from the cache or
killed. Frozen apps will not use any CPU cycles, reducing power
consumption for misbehaving processes which might attempt to run
while cached.

Change-Id: Ib055352062825db62927015c33ee0a285aa4630c
Depends-On: pa/1495867, ag/9976279, ag/9981529, ag/9987868¬
Bug: 143308662
Test: manual, atest CachedAppOptimizerTest
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index c7a8474..0f11d48 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -856,6 +856,17 @@
             throws IllegalArgumentException, SecurityException;
 
     /**
+     * Freeze or unfreeze the specified process.
+     *
+     * @param pid Identifier of the process to freeze or unfreeze.
+     * @param uid Identifier of the user the process is running under.
+     * @param frozen Specify whether to free (true) or unfreeze (false).
+     *
+     * @hide
+     */
+    public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
+
+    /**
      * Return the scheduling group of requested process.
      *
      * @hide
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 89dbca8..0eb364d 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -330,6 +330,22 @@
     closedir(d);
 }
 
+void android_os_Process_setProcessFrozen(
+        JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze)
+{
+    bool success = true;
+
+    if (freeze) {
+        success = SetProcessProfiles(uid, pid, {"Frozen"});
+    } else {
+        success = SetProcessProfiles(uid, pid, {"Unfrozen"});
+    }
+
+    if (!success) {
+        signalExceptionForGroupError(env, EINVAL, pid);
+    }
+}
+
 jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
 {
     SchedPolicy sp;
@@ -1327,6 +1343,7 @@
         {"setGid", "(I)I", (void*)android_os_Process_setGid},
         {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
         {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
+        {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
         {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
         {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
         {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index ed64475..b19a37e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -50,6 +50,7 @@
     static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
     static final boolean DEBUG_BROADCAST_DEFERRAL = DEBUG_BROADCAST || false;
     static final boolean DEBUG_COMPACTION = DEBUG_ALL || false;
+    static final boolean DEBUG_FREEZER = DEBUG_ALL || false;
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
     static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 3ca5ebc..6819578 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -19,6 +19,7 @@
 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FREEZER;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 
 import android.app.ActivityManager;
@@ -42,6 +43,7 @@
 import com.android.server.ServiceThread;
 
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -55,6 +57,7 @@
 
     // Flags stored in the DeviceConfig API.
     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
+    @VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer";
     @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
     @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
@@ -65,6 +68,8 @@
     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
     @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
             "compact_statsd_sample_rate";
+    @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE =
+            "freeze_statsd_sample_rate";
     @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB =
             "compact_full_rss_throttle_kb";
     @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB =
@@ -85,6 +90,7 @@
 
     // Defaults for phenotype flags.
     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
+    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@@ -114,6 +120,13 @@
     static final int COMPACT_PROCESS_BFGS = 4;
     static final int COMPACT_PROCESS_MSG = 1;
     static final int COMPACT_SYSTEM_MSG = 2;
+    static final int SET_FROZEN_PROCESS_MSG = 3;
+
+    //TODO:change this static definition into a configurable flag.
+    static final int FREEZE_TIMEOUT_MS = 500;
+
+    static final int DO_FREEZE = 1;
+    static final int DO_UNFREEZE = 2;
 
     /**
      * This thread must be moved to the system background cpuset.
@@ -144,7 +157,9 @@
                                     || KEY_COMPACT_THROTTLE_4.equals(name)) {
                                 updateCompactionThrottles();
                             } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
-                                updateStatsdSampleRate();
+                                updateCompactStatsdSampleRate();
+                            } else if (KEY_FREEZER_STATSD_SAMPLE_RATE.equals(name)) {
+                                updateFreezerStatsdSampleRate();
                             } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
                                 updateFullRssThrottle();
                             } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
@@ -183,9 +198,11 @@
     @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
     @GuardedBy("mPhenotypeFlagLock")
     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
+    private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
     private final Random mRandom = new Random();
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting volatile float mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
+    @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
+    @VisibleForTesting volatile float mFreezerStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting volatile long mFullAnonRssThrottleKb =
             DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
@@ -197,6 +214,7 @@
 
     // Handler on which compaction runs.
     private Handler mCompactionHandler;
+    private Handler mFreezeHandler;
 
     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
     // when evaluating throttles that we only consider for "full" compaction, so we don't store
@@ -238,10 +256,12 @@
             updateUseCompaction();
             updateCompactionActions();
             updateCompactionThrottles();
-            updateStatsdSampleRate();
+            updateCompactStatsdSampleRate();
+            updateFreezerStatsdSampleRate();
             updateFullRssThrottle();
             updateFullDeltaRssThrottle();
             updateProcStateThrottle();
+            updateUseFreezer();
         }
         Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                 Process.THREAD_GROUP_SYSTEM);
@@ -256,6 +276,15 @@
         }
     }
 
+    /**
+     * Returns whether freezer is enabled.
+     */
+    public boolean useFreezer() {
+        synchronized (mPhenotypeFlagLock) {
+            return mUseFreezer;
+        }
+    }
+
     @GuardedBy("mAm")
     void dump(PrintWriter pw) {
         pw.println("CachedAppOptimizer settings");
@@ -269,7 +298,7 @@
             pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
             pw.println("  " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
             pw.println("  " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
-            pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mStatsdSampleRate);
+            pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate);
             pw.println("  " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "="
                     + mFullAnonRssThrottleKb);
             pw.println("  " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "="
@@ -283,6 +312,8 @@
 
             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
                     + " processes.");
+            pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
+            pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
             if (DEBUG_COMPACTION) {
                 for (Map.Entry<Integer, LastCompactionStats> entry
                         : mLastCompactionStats.entrySet()) {
@@ -356,18 +387,76 @@
 
     /**
      * Reads the flag value from DeviceConfig to determine whether app compaction
-     * should be enabled, and starts the compaction thread if needed.
+     * should be enabled, and starts the freeze/compaction thread if needed.
      */
     @GuardedBy("mPhenotypeFlagLock")
     private void updateUseCompaction() {
         mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
-        if (mUseCompaction && !mCachedAppOptimizerThread.isAlive()) {
-            mCachedAppOptimizerThread.start();
+
+        if (mUseCompaction) {
+            if (!mCachedAppOptimizerThread.isAlive()) {
+                mCachedAppOptimizerThread.start();
+            }
+
             mCompactionHandler = new MemCompactionHandler();
         }
     }
 
+    /**
+     * Determines whether the freezer is correctly supported by this system
+     */
+    public boolean isFreezerSupported() {
+        boolean supported = false;
+        FileReader fr = null;
+
+        try {
+            fr = new FileReader("/dev/freezer/frozen/freezer.killable");
+            int i = fr.read();
+
+            if ((char) i == '1') {
+                supported = true;
+            } else {
+                Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer");
+            }
+        } catch (java.io.FileNotFoundException e) {
+            Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer");
+        } catch (Exception e) {
+            Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString());
+        }
+
+        if (fr != null) {
+            try {
+                fr.close();
+            } catch (java.io.IOException e) {
+                Slog.e(TAG_AM, "Exception closing freezer.killable: " + e.toString());
+            }
+        }
+
+        return supported;
+    }
+
+    /**
+     * Reads the flag value from DeviceConfig to determine whether app freezer
+     * should be enabled, and starts the freeze/compaction thread if needed.
+     */
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateUseFreezer() {
+        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
+            mUseFreezer = isFreezerSupported();
+        }
+
+        if (mUseFreezer) {
+            Slog.d(TAG_AM, "Freezer enabled");
+            if (!mCachedAppOptimizerThread.isAlive()) {
+                mCachedAppOptimizerThread.start();
+            }
+
+            mFreezeHandler = new FreezeHandler();
+        }
+    }
+
     @GuardedBy("mPhenotypeFlagLock")
     private void updateCompactionActions() {
         int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -433,10 +522,17 @@
     }
 
     @GuardedBy("mPhenotypeFlagLock")
-    private void updateStatsdSampleRate() {
-        mStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+    private void updateCompactStatsdSampleRate() {
+        mCompactStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
-        mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate));
+        mCompactStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mCompactStatsdSampleRate));
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateFreezerStatsdSampleRate() {
+        mFreezerStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FREEZER_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
+        mFreezerStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mFreezerStatsdSampleRate));
     }
 
     @GuardedBy("mPhenotypeFlagLock")
@@ -507,6 +603,24 @@
         }
     }
 
+    @GuardedBy("mAm")
+    void freezeAppAsync(ProcessRecord app) {
+        mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
+
+        mFreezeHandler.sendMessageDelayed(
+                mFreezeHandler.obtainMessage(
+                    SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
+                FREEZE_TIMEOUT_MS);
+    }
+
+    @GuardedBy("mAm")
+    void unfreezeAppAsync(ProcessRecord app) {
+        mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
+
+        mFreezeHandler.sendMessage(
+                mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_UNFREEZE, 0, app));
+    }
+
     private static final class LastCompactionStats {
         private final long[] mRssAfterCompaction;
 
@@ -734,7 +848,7 @@
                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
                         // on every single compaction for a flag that will seldom change and the
                         // impact of reading the wrong value here is low.
-                        if (mRandom.nextFloat() < mStatsdSampleRate) {
+                        if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
                             StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
                                     rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                     rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
@@ -768,4 +882,119 @@
             }
         }
     }
+
+    private final class FreezeHandler extends Handler {
+        private FreezeHandler() {
+            super(mCachedAppOptimizerThread.getLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what != SET_FROZEN_PROCESS_MSG) {
+                return;
+            }
+
+            if (msg.arg1 == DO_FREEZE) {
+                freezeProcess((ProcessRecord) msg.obj);
+            } else if (msg.arg1 == DO_UNFREEZE) {
+                unfreezeProcess((ProcessRecord) msg.obj);
+            }
+        }
+
+        private void freezeProcess(ProcessRecord proc) {
+            final int pid;
+            final String name;
+            final long unfrozenDuration;
+            final boolean frozen;
+
+            synchronized (mAm) {
+                pid = proc.pid;
+                name = proc.processName;
+
+                if (proc.curAdj <= ProcessList.CACHED_APP_MIN_ADJ) {
+                    if (DEBUG_FREEZER) {
+                        Slog.d(TAG_AM, "Skipping freeze for process " + pid
+                                + " " + name + " (not cached)");
+                    }
+                    return;
+                }
+
+                if (pid == 0 || proc.frozen) {
+                    // Already frozen or not a real process, either one being
+                    // launched or one being killed
+                    return;
+                }
+
+                long unfreezeTime = proc.freezeUnfreezeTime;
+
+                try {
+                    Process.setProcessFrozen(pid, proc.uid, true);
+
+                    proc.freezeUnfreezeTime = SystemClock.uptimeMillis();
+                    proc.frozen = true;
+                } catch (Exception e) {
+                    Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
+                }
+
+                unfrozenDuration = proc.freezeUnfreezeTime - unfreezeTime;
+                frozen = proc.frozen;
+            }
+
+            if (frozen) {
+                if (DEBUG_FREEZER) {
+                    Slog.d(TAG_AM, "froze " + pid + " " + name);
+                }
+
+                EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+            }
+        }
+
+        private void unfreezeProcess(ProcessRecord proc) {
+            final int pid;
+            final String name;
+            final long frozenDuration;
+            final boolean frozen;
+
+            synchronized (mAm) {
+                pid = proc.pid;
+                name = proc.processName;
+
+                if (!proc.frozen) {
+                    if (DEBUG_FREEZER) {
+                        Slog.d(TAG_AM,
+                                "Skipping unfreeze for process " + pid + " "
+                                + name + " (not frozen)");
+                    }
+                    return;
+                }
+
+                if (pid == 0) {
+                    // Not a real process, either being launched or killed
+                    return;
+                }
+
+                long freezeTime = proc.freezeUnfreezeTime;
+
+                try {
+                    Process.setProcessFrozen(proc.pid, proc.uid, false);
+
+                    proc.freezeUnfreezeTime = SystemClock.uptimeMillis();
+                    proc.frozen = false;
+                } catch (Exception e) {
+                    Slog.w(TAG_AM, "Unable to unfreeze " + pid + " " + name);
+                }
+
+                frozenDuration = proc.freezeUnfreezeTime - freezeTime;
+                frozen = proc.frozen;
+            }
+
+            if (!frozen) {
+                if (DEBUG_FREEZER) {
+                    Slog.d(TAG_AM, "unfroze " + pid + " " + name);
+                }
+
+                EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, name);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 23674bb..cc5df40 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -88,3 +88,8 @@
 # The task is being compacted
 30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(DeltaRssTotal|2|2),(DeltaRssFile|2|2),(DeltaRssAnon|2|2),(DeltaRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(DeltaZRAMFree|2|2)
 
+# The task is being frozen
+30068 am_freeze (Pid|1|5),(Process Name|3)
+
+# The task is being unfrozen
+30069 am_unfreeze (Pid|1|5),(Process Name|3)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0429782..c831ac4 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2173,6 +2173,9 @@
             app.repForegroundActivities = app.hasForegroundActivities();
             changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
         }
+
+        updateAppFreezeStateLocked(app);
+
         if (app.getReportedProcState() != app.getCurProcState()) {
             app.setReportedProcState(app.getCurProcState());
             if (app.thread != null) {
@@ -2489,4 +2492,18 @@
     void dumpCachedAppOptimizerSettings(PrintWriter pw) {
         mCachedAppOptimizer.dump(pw);
     }
+
+    @GuardedBy("mService")
+    void updateAppFreezeStateLocked(ProcessRecord app) {
+        if (!mCachedAppOptimizer.useFreezer()) {
+            return;
+        }
+
+        // Use current adjustment when freezing, set adjustment when unfreezing.
+        if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen) {
+            mCachedAppOptimizer.freezeAppAsync(app);
+        } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) {
+            mCachedAppOptimizer.unfreezeAppAsync(app);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0e1e0f9..1e2dd2d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -163,6 +163,8 @@
     long lastCompactTime;       // The last time that this process was compacted
     int reqCompactAction;       // The most recent compaction action requested for this app.
     int lastCompactAction;      // The most recent compaction action performed for this app.
+    boolean frozen;             // True when the process is frozen.
+    long freezeUnfreezeTime;    // Last time the app was (un)frozen, 0 for never
     private int mCurSchedGroup; // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
@@ -623,7 +625,7 @@
         curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
         mPersistent = false;
         removed = false;
-        lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
+        freezeUnfreezeTime = lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
         mWindowProcessController = new WindowProcessController(
                 mService.mActivityTaskManager, info, processName, uid, userId, this, this);
         pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index f037692..1985513 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -120,7 +120,7 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
         assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
@@ -209,7 +209,7 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
@@ -472,7 +472,7 @@
     public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
         mCachedAppOptimizerUnderTest.init();
 
-        // When we override mStatsdSampleRate with a reasonable value ...
+        // When we override mCompactStatsdSampleRate with a reasonable value ...
         mCountDown = new CountDownLatch(1);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
@@ -480,7 +480,7 @@
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
         // Then that override is reflected in the compactor.
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
     }
 
@@ -489,14 +489,14 @@
             throws InterruptedException {
         mCachedAppOptimizerUnderTest.init();
 
-        // When we override mStatsdSampleRate with an unreasonable value ...
+        // When we override mCompactStatsdSampleRate with an unreasonable value ...
         mCountDown = new CountDownLatch(1);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
         // Then that override is reflected in the compactor.
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
                 CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
     }
 
@@ -505,7 +505,7 @@
             throws InterruptedException {
         mCachedAppOptimizerUnderTest.init();
 
-        // When we override mStatsdSampleRate with an value outside of [0..1]...
+        // When we override mCompactStatsdSampleRate with an value outside of [0..1]...
         mCountDown = new CountDownLatch(1);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
@@ -513,7 +513,7 @@
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
         // Then the values is capped in the range.
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(0.0f);
 
         mCountDown = new CountDownLatch(1);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -522,7 +522,7 @@
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
         // Then the values is capped in the range.
-        assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
+        assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(1.0f);
     }
 
     @Test