1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
20 
21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION;
22 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FREEZER;
23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
24 
25 import android.app.ActivityManager;
26 import android.app.ActivityThread;
27 import android.os.Debug;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.Process;
31 import android.os.SystemClock;
32 import android.os.Trace;
33 import android.provider.DeviceConfig;
34 import android.provider.DeviceConfig.OnPropertiesChangedListener;
35 import android.provider.DeviceConfig.Properties;
36 import android.provider.Settings;
37 import android.text.TextUtils;
38 import android.util.EventLog;
39 import android.util.Slog;
40 
41 import com.android.internal.annotations.GuardedBy;
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.FrameworkStatsLog;
44 import com.android.server.ServiceThread;
45 
46 import java.io.FileOutputStream;
47 import java.io.FileReader;
48 import java.io.IOException;
49 import java.io.PrintWriter;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashSet;
53 import java.util.LinkedHashMap;
54 import java.util.Map;
55 import java.util.Random;
56 import java.util.Set;
57 
58 public final class CachedAppOptimizer {
59 
60     // Flags stored in the DeviceConfig API.
61     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
62     @VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer";
63     @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
64     @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
65     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
66     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
67     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
68     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
69     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5";
70     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
71     @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
72             "compact_statsd_sample_rate";
73     @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE =
74             "freeze_statsd_sample_rate";
75     @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB =
76             "compact_full_rss_throttle_kb";
77     @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB =
78             "compact_full_delta_rss_throttle_kb";
79     @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE =
80             "compact_proc_state_throttle";
81 
82     // Phenotype sends int configurations and we map them to the strings we'll use on device,
83     // preventing a weird string value entering the kernel.
84     private static final int COMPACT_ACTION_FILE_FLAG = 1;
85     private static final int COMPACT_ACTION_ANON_FLAG = 2;
86     private static final int COMPACT_ACTION_FULL_FLAG = 3;
87     private static final int COMPACT_ACTION_NONE_FLAG = 4;
88     private static final String COMPACT_ACTION_NONE = "";
89     private static final String COMPACT_ACTION_FILE = "file";
90     private static final String COMPACT_ACTION_ANON = "anon";
91     private static final String COMPACT_ACTION_FULL = "all";
92 
93     // Defaults for phenotype flags.
94     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
95     @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false;
96     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
97     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
98     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
99     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
100     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
101     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
102     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000;
103     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000;
104     // The sampling rate to push app compaction events into statsd for upload.
105     @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
106     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L;
107     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 8_000L;
108     // Format of this string should be a comma separated list of integers.
109     @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
110             String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
111 
112     @VisibleForTesting
113     interface PropertyChangedCallbackForTest {
onPropertyChanged()114         void onPropertyChanged();
115     }
116     private PropertyChangedCallbackForTest mTestCallback;
117 
118     // This interface is for functions related to the Process object that need a different
119     // implementation in the tests as we are not creating real processes when testing compaction.
120     @VisibleForTesting
121     interface ProcessDependencies {
getRss(int pid)122         long[] getRss(int pid);
performCompaction(String action, int pid)123         void performCompaction(String action, int pid) throws IOException;
124     }
125 
126     // Handler constants.
127     static final int COMPACT_PROCESS_SOME = 1;
128     static final int COMPACT_PROCESS_FULL = 2;
129     static final int COMPACT_PROCESS_PERSISTENT = 3;
130     static final int COMPACT_PROCESS_BFGS = 4;
131     static final int COMPACT_PROCESS_MSG = 1;
132     static final int COMPACT_SYSTEM_MSG = 2;
133     static final int SET_FROZEN_PROCESS_MSG = 3;
134     static final int REPORT_UNFREEZE_MSG = 4;
135 
136     //TODO:change this static definition into a configurable flag.
137     static final int FREEZE_TIMEOUT_MS = 500;
138 
139     static final int DO_FREEZE = 1;
140     static final int REPORT_UNFREEZE = 2;
141 
142     /**
143      * This thread must be moved to the system background cpuset.
144      * If that doesn't happen, it's probably going to draw a lot of power.
145      * However, this has to happen after the first updateOomAdjLocked, because
146      * that will wipe out the cpuset assignment for system_server threads.
147      * Accordingly, this is in the AMS constructor.
148      */
149     final ServiceThread mCachedAppOptimizerThread;
150 
151     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
152             new ArrayList<ProcessRecord>();
153     private final ActivityManagerService mAm;
154     private final OnPropertiesChangedListener mOnFlagsChangedListener =
155             new OnPropertiesChangedListener() {
156                 @Override
157                 public void onPropertiesChanged(Properties properties) {
158                     synchronized (mPhenotypeFlagLock) {
159                         for (String name : properties.getKeyset()) {
160                             if (KEY_USE_COMPACTION.equals(name)) {
161                                 updateUseCompaction();
162                             } else if (KEY_COMPACT_ACTION_1.equals(name)
163                                     || KEY_COMPACT_ACTION_2.equals(name)) {
164                                 updateCompactionActions();
165                             } else if (KEY_COMPACT_THROTTLE_1.equals(name)
166                                     || KEY_COMPACT_THROTTLE_2.equals(name)
167                                     || KEY_COMPACT_THROTTLE_3.equals(name)
168                                     || KEY_COMPACT_THROTTLE_4.equals(name)) {
169                                 updateCompactionThrottles();
170                             } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
171                                 updateCompactStatsdSampleRate();
172                             } else if (KEY_FREEZER_STATSD_SAMPLE_RATE.equals(name)) {
173                                 updateFreezerStatsdSampleRate();
174                             } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
175                                 updateFullRssThrottle();
176                             } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
177                                 updateFullDeltaRssThrottle();
178                             } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
179                                 updateProcStateThrottle();
180                             }
181                         }
182                     }
183                     if (mTestCallback != null) {
184                         mTestCallback.onPropertyChanged();
185                     }
186                 }
187             };
188 
189     private final Object mPhenotypeFlagLock = new Object();
190 
191     // Configured by phenotype. Updates from the server take effect immediately.
192     @GuardedBy("mPhenotypeFlagLock")
193     @VisibleForTesting volatile String mCompactActionSome =
194             compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
195     @GuardedBy("mPhenotypeFlagLock")
196     @VisibleForTesting volatile String mCompactActionFull =
197             compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
198     @GuardedBy("mPhenotypeFlagLock")
199     @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
200     @GuardedBy("mPhenotypeFlagLock")
201     @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
202     @GuardedBy("mPhenotypeFlagLock")
203     @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
204     @GuardedBy("mPhenotypeFlagLock")
205     @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
206     @GuardedBy("mPhenotypeFlagLock")
207     @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
208     @GuardedBy("mPhenotypeFlagLock")
209     @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
210     @GuardedBy("mPhenotypeFlagLock")
211     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
212     private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
213     private final Random mRandom = new Random();
214     @GuardedBy("mPhenotypeFlagLock")
215     @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
216     @VisibleForTesting volatile float mFreezerStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
217     @GuardedBy("mPhenotypeFlagLock")
218     @VisibleForTesting volatile long mFullAnonRssThrottleKb =
219             DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
220     @GuardedBy("mPhenotypeFlagLock")
221     @VisibleForTesting volatile long mFullDeltaRssThrottleKb =
222             DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
223     @GuardedBy("mPhenotypeFlagLock")
224     @VisibleForTesting final Set<Integer> mProcStateThrottle;
225 
226     // Handler on which compaction runs.
227     @VisibleForTesting
228     Handler mCompactionHandler;
229     private Handler mFreezeHandler;
230 
231     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
232     // when evaluating throttles that we only consider for "full" compaction, so we don't store
233     // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
234     // facilitate removal of the oldest entry.
235     @VisibleForTesting
236     LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
237             new LinkedHashMap<Integer, LastCompactionStats>() {
238                 @Override
239                 protected boolean removeEldestEntry(Map.Entry eldest) {
240                     return size() > 100;
241                 }
242     };
243 
244     private int mSomeCompactionCount;
245     private int mFullCompactionCount;
246     private int mPersistentCompactionCount;
247     private int mBfgsCompactionCount;
248     private final ProcessDependencies mProcessDependencies;
249 
CachedAppOptimizer(ActivityManagerService am)250     public CachedAppOptimizer(ActivityManagerService am) {
251         this(am, null, new DefaultProcessDependencies());
252     }
253 
254     @VisibleForTesting
CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback, ProcessDependencies processDependencies)255     CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
256             ProcessDependencies processDependencies) {
257         mAm = am;
258         mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
259             THREAD_PRIORITY_FOREGROUND, true);
260         mProcStateThrottle = new HashSet<>();
261         mProcessDependencies = processDependencies;
262         mTestCallback = callback;
263     }
264 
265     /**
266      * Reads phenotype config to determine whether app compaction is enabled or not and
267      * starts the background thread if necessary.
268      */
init()269     public void init() {
270         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
271                 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
272         synchronized (mPhenotypeFlagLock) {
273             updateUseCompaction();
274             updateCompactionActions();
275             updateCompactionThrottles();
276             updateCompactStatsdSampleRate();
277             updateFreezerStatsdSampleRate();
278             updateFullRssThrottle();
279             updateFullDeltaRssThrottle();
280             updateProcStateThrottle();
281             updateUseFreezer();
282         }
283         Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
284                 Process.THREAD_GROUP_SYSTEM);
285     }
286 
287     /**
288      * Returns whether compaction is enabled.
289      */
useCompaction()290     public boolean useCompaction() {
291         synchronized (mPhenotypeFlagLock) {
292             return mUseCompaction;
293         }
294     }
295 
296     /**
297      * Returns whether freezer is enabled.
298      */
useFreezer()299     public boolean useFreezer() {
300         synchronized (mPhenotypeFlagLock) {
301             return mUseFreezer;
302         }
303     }
304 
305     @GuardedBy("mAm")
dump(PrintWriter pw)306     void dump(PrintWriter pw) {
307         pw.println("CachedAppOptimizer settings");
308         synchronized (mPhenotypeFlagLock) {
309             pw.println("  " + KEY_USE_COMPACTION + "=" + mUseCompaction);
310             pw.println("  " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
311             pw.println("  " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull);
312             pw.println("  " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome);
313             pw.println("  " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
314             pw.println("  " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
315             pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
316             pw.println("  " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
317             pw.println("  " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
318             pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate);
319             pw.println("  " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "="
320                     + mFullAnonRssThrottleKb);
321             pw.println("  " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "="
322                     + mFullDeltaRssThrottleKb);
323             pw.println("  "  + KEY_COMPACT_PROC_STATE_THROTTLE + "="
324                     + Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
325 
326             pw.println("  " + mSomeCompactionCount + " some, " + mFullCompactionCount
327                     + " full, " + mPersistentCompactionCount + " persistent, "
328                     + mBfgsCompactionCount + " BFGS compactions.");
329 
330             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
331                     + " processes.");
332             pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
333             pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
334             if (DEBUG_COMPACTION) {
335                 for (Map.Entry<Integer, LastCompactionStats> entry
336                         : mLastCompactionStats.entrySet()) {
337                     int pid = entry.getKey();
338                     LastCompactionStats stats = entry.getValue();
339                     pw.println("    " + pid + ": "
340                             + Arrays.toString(stats.getRssAfterCompaction()));
341                 }
342             }
343         }
344     }
345 
346     @GuardedBy("mAm")
compactAppSome(ProcessRecord app)347     void compactAppSome(ProcessRecord app) {
348         app.reqCompactAction = COMPACT_PROCESS_SOME;
349         mPendingCompactionProcesses.add(app);
350         mCompactionHandler.sendMessage(
351                 mCompactionHandler.obtainMessage(
352                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
353     }
354 
355     @GuardedBy("mAm")
compactAppFull(ProcessRecord app)356     void compactAppFull(ProcessRecord app) {
357         app.reqCompactAction = COMPACT_PROCESS_FULL;
358         mPendingCompactionProcesses.add(app);
359         mCompactionHandler.sendMessage(
360                 mCompactionHandler.obtainMessage(
361                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
362 
363     }
364 
365     @GuardedBy("mAm")
compactAppPersistent(ProcessRecord app)366     void compactAppPersistent(ProcessRecord app) {
367         app.reqCompactAction = COMPACT_PROCESS_PERSISTENT;
368         mPendingCompactionProcesses.add(app);
369         mCompactionHandler.sendMessage(
370                 mCompactionHandler.obtainMessage(
371                     COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
372     }
373 
374     @GuardedBy("mAm")
shouldCompactPersistent(ProcessRecord app, long now)375     boolean shouldCompactPersistent(ProcessRecord app, long now) {
376         return (app.lastCompactTime == 0
377                 || (now - app.lastCompactTime) > mCompactThrottlePersistent);
378     }
379 
380     @GuardedBy("mAm")
compactAppBfgs(ProcessRecord app)381     void compactAppBfgs(ProcessRecord app) {
382         app.reqCompactAction = COMPACT_PROCESS_BFGS;
383         mPendingCompactionProcesses.add(app);
384         mCompactionHandler.sendMessage(
385                 mCompactionHandler.obtainMessage(
386                     COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
387     }
388 
389     @GuardedBy("mAm")
shouldCompactBFGS(ProcessRecord app, long now)390     boolean shouldCompactBFGS(ProcessRecord app, long now) {
391         return (app.lastCompactTime == 0
392                 || (now - app.lastCompactTime) > mCompactThrottleBFGS);
393     }
394 
395     @GuardedBy("mAm")
compactAllSystem()396     void compactAllSystem() {
397         if (mUseCompaction) {
398             mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
399                                               COMPACT_SYSTEM_MSG));
400         }
401     }
402 
compactSystem()403     private native void compactSystem();
404 
405     /**
406      * Reads the flag value from DeviceConfig to determine whether app compaction
407      * should be enabled, and starts the freeze/compaction thread if needed.
408      */
409     @GuardedBy("mPhenotypeFlagLock")
updateUseCompaction()410     private void updateUseCompaction() {
411         mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
412                     KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
413 
414         if (mUseCompaction) {
415             if (!mCachedAppOptimizerThread.isAlive()) {
416                 mCachedAppOptimizerThread.start();
417             }
418 
419             mCompactionHandler = new MemCompactionHandler();
420         }
421     }
422 
423     /**
424      * Determines whether the freezer is correctly supported by this system
425      */
isFreezerSupported()426     public static boolean isFreezerSupported() {
427         boolean supported = false;
428         FileReader fr = null;
429 
430         try {
431             fr = new FileReader("/dev/freezer/frozen/freezer.killable");
432             int i = fr.read();
433 
434             if ((char) i == '1') {
435                 supported = true;
436             } else {
437                 Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer");
438             }
439         } catch (java.io.FileNotFoundException e) {
440             Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer");
441         } catch (Exception e) {
442             Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString());
443         }
444 
445         if (fr != null) {
446             try {
447                 fr.close();
448             } catch (java.io.IOException e) {
449                 Slog.e(TAG_AM, "Exception closing freezer.killable: " + e.toString());
450             }
451         }
452 
453         return supported;
454     }
455 
456     /**
457      * Reads the flag value from DeviceConfig to determine whether app freezer
458      * should be enabled, and starts the freeze/compaction thread if needed.
459      */
460     @GuardedBy("mPhenotypeFlagLock")
updateUseFreezer()461     private void updateUseFreezer() {
462         final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),
463                 Settings.Global.CACHED_APPS_FREEZER_ENABLED);
464 
465         if ("disabled".equals(configOverride)) {
466             mUseFreezer = false;
467         } else if ("enabled".equals(configOverride)
468                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
469                     KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
470             mUseFreezer = isFreezerSupported();
471         }
472 
473         if (mUseFreezer) {
474             Slog.d(TAG_AM, "Freezer enabled");
475             if (!mCachedAppOptimizerThread.isAlive()) {
476                 mCachedAppOptimizerThread.start();
477             }
478 
479             mFreezeHandler = new FreezeHandler();
480         }
481     }
482 
483     @GuardedBy("mPhenotypeFlagLock")
updateCompactionActions()484     private void updateCompactionActions() {
485         int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
486                 KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
487 
488         int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
489                 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
490 
491         mCompactActionSome = compactActionIntToString(compactAction1);
492         mCompactActionFull = compactActionIntToString(compactAction2);
493     }
494 
495     @GuardedBy("mPhenotypeFlagLock")
updateCompactionThrottles()496     private void updateCompactionThrottles() {
497         boolean useThrottleDefaults = false;
498         String throttleSomeSomeFlag =
499                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
500                     KEY_COMPACT_THROTTLE_1);
501         String throttleSomeFullFlag =
502                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
503                     KEY_COMPACT_THROTTLE_2);
504         String throttleFullSomeFlag =
505                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
506                     KEY_COMPACT_THROTTLE_3);
507         String throttleFullFullFlag =
508                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
509                     KEY_COMPACT_THROTTLE_4);
510         String throttleBFGSFlag =
511                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
512                     KEY_COMPACT_THROTTLE_5);
513         String throttlePersistentFlag =
514                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
515                     KEY_COMPACT_THROTTLE_6);
516 
517         if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
518                 || TextUtils.isEmpty(throttleFullSomeFlag)
519                 || TextUtils.isEmpty(throttleFullFullFlag)
520                 || TextUtils.isEmpty(throttleBFGSFlag)
521                 || TextUtils.isEmpty(throttlePersistentFlag)) {
522             // Set defaults for all if any are not set.
523             useThrottleDefaults = true;
524         } else {
525             try {
526                 mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
527                 mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
528                 mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
529                 mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
530                 mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
531                 mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
532             } catch (NumberFormatException e) {
533                 useThrottleDefaults = true;
534             }
535         }
536 
537         if (useThrottleDefaults) {
538             mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
539             mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
540             mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
541             mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
542             mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
543             mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
544         }
545     }
546 
547     @GuardedBy("mPhenotypeFlagLock")
updateCompactStatsdSampleRate()548     private void updateCompactStatsdSampleRate() {
549         mCompactStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
550                 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
551         mCompactStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mCompactStatsdSampleRate));
552     }
553 
554     @GuardedBy("mPhenotypeFlagLock")
updateFreezerStatsdSampleRate()555     private void updateFreezerStatsdSampleRate() {
556         mFreezerStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
557                 KEY_FREEZER_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
558         mFreezerStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mFreezerStatsdSampleRate));
559     }
560 
561     @GuardedBy("mPhenotypeFlagLock")
updateFullRssThrottle()562     private void updateFullRssThrottle() {
563         mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
564                 KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
565 
566         // Don't allow negative values. 0 means don't apply the throttle.
567         if (mFullAnonRssThrottleKb < 0) {
568             mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
569         }
570     }
571 
572     @GuardedBy("mPhenotypeFlagLock")
updateFullDeltaRssThrottle()573     private void updateFullDeltaRssThrottle() {
574         mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
575                 KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
576 
577         if (mFullDeltaRssThrottleKb < 0) {
578             mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
579         }
580     }
581 
582     @GuardedBy("mPhenotypeFlagLock")
updateProcStateThrottle()583     private void updateProcStateThrottle() {
584         String procStateThrottleString = DeviceConfig.getString(
585                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,
586                 DEFAULT_COMPACT_PROC_STATE_THROTTLE);
587         if (!parseProcStateThrottle(procStateThrottleString)) {
588             Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""
589                     + procStateThrottleString + "\" falling back to default.");
590             if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {
591                 Slog.wtf(TAG_AM,
592                         "Unable to parse default app compact proc state throttle "
593                                 + DEFAULT_COMPACT_PROC_STATE_THROTTLE);
594             }
595         }
596     }
597 
parseProcStateThrottle(String procStateThrottleString)598     private boolean parseProcStateThrottle(String procStateThrottleString) {
599         String[] procStates = TextUtils.split(procStateThrottleString, ",");
600         mProcStateThrottle.clear();
601         for (String procState : procStates) {
602             try {
603                 mProcStateThrottle.add(Integer.parseInt(procState));
604             } catch (NumberFormatException e) {
605                 Slog.e(TAG_AM, "Failed to parse default app compaction proc state: "
606                         + procState);
607                 return false;
608             }
609         }
610         return true;
611     }
612 
613     @VisibleForTesting
compactActionIntToString(int action)614     static String compactActionIntToString(int action) {
615         switch(action) {
616             case COMPACT_ACTION_NONE_FLAG:
617                 return COMPACT_ACTION_NONE;
618             case COMPACT_ACTION_FILE_FLAG:
619                 return COMPACT_ACTION_FILE;
620             case COMPACT_ACTION_ANON_FLAG:
621                 return COMPACT_ACTION_ANON;
622             case COMPACT_ACTION_FULL_FLAG:
623                 return COMPACT_ACTION_FULL;
624             default:
625                 return COMPACT_ACTION_NONE;
626         }
627     }
628 
629     @GuardedBy("mAm")
freezeAppAsync(ProcessRecord app)630     void freezeAppAsync(ProcessRecord app) {
631         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
632 
633         mFreezeHandler.sendMessageDelayed(
634                 mFreezeHandler.obtainMessage(
635                     SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
636                 FREEZE_TIMEOUT_MS);
637     }
638 
639     @GuardedBy("mAm")
unfreezeAppLocked(ProcessRecord app)640     void unfreezeAppLocked(ProcessRecord app) {
641         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
642 
643         if (!app.frozen) {
644             if (DEBUG_FREEZER) {
645                 Slog.d(TAG_AM,
646                         "Skipping unfreeze for process " + app.pid + " "
647                         + app.processName + " (not frozen)");
648             }
649             return;
650         }
651 
652         long freezeTime = app.freezeUnfreezeTime;
653 
654         try {
655             Process.setProcessFrozen(app.pid, app.uid, false);
656 
657             app.freezeUnfreezeTime = SystemClock.uptimeMillis();
658             app.frozen = false;
659         } catch (Exception e) {
660             Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
661                     + ". Any related user experience might be hanged.");
662         }
663 
664         if (!app.frozen) {
665             if (DEBUG_FREEZER) {
666                 Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
667             }
668 
669             mFreezeHandler.sendMessage(
670                     mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
671                         app.pid,
672                         (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE),
673                         app.processName));
674         }
675     }
676 
677     @VisibleForTesting
678     static final class LastCompactionStats {
679         private final long[] mRssAfterCompaction;
680 
LastCompactionStats(long[] rss)681         LastCompactionStats(long[] rss) {
682             mRssAfterCompaction = rss;
683         }
684 
getRssAfterCompaction()685         long[] getRssAfterCompaction() {
686             return mRssAfterCompaction;
687         }
688     }
689 
690     private final class MemCompactionHandler extends Handler {
MemCompactionHandler()691         private MemCompactionHandler() {
692             super(mCachedAppOptimizerThread.getLooper());
693         }
694 
695         @Override
handleMessage(Message msg)696         public void handleMessage(Message msg) {
697             switch (msg.what) {
698                 case COMPACT_PROCESS_MSG: {
699                     long start = SystemClock.uptimeMillis();
700                     ProcessRecord proc;
701                     int pid;
702                     String action;
703                     final String name;
704                     int pendingAction, lastCompactAction;
705                     long lastCompactTime;
706                     LastCompactionStats lastCompactionStats;
707                     int lastOomAdj = msg.arg1;
708                     int procState = msg.arg2;
709                     synchronized (mAm) {
710                         proc = mPendingCompactionProcesses.remove(0);
711 
712                         pendingAction = proc.reqCompactAction;
713                         pid = proc.pid;
714                         name = proc.processName;
715 
716                         // don't compact if the process has returned to perceptible
717                         // and this is only a cached/home/prev compaction
718                         if ((pendingAction == COMPACT_PROCESS_SOME
719                                 || pendingAction == COMPACT_PROCESS_FULL)
720                                 && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
721                             if (DEBUG_COMPACTION) {
722                                 Slog.d(TAG_AM,
723                                         "Skipping compaction as process " + name + " is "
724                                         + "now perceptible.");
725                             }
726                             return;
727                         }
728 
729                         lastCompactAction = proc.lastCompactAction;
730                         lastCompactTime = proc.lastCompactTime;
731                         lastCompactionStats = mLastCompactionStats.get(pid);
732                     }
733 
734                     if (pid == 0) {
735                         // not a real process, either one being launched or one being killed
736                         return;
737                     }
738 
739                     // basic throttling
740                     // use the Phenotype flag knobs to determine whether current/prevous
741                     // compaction combo should be throtted or not
742 
743                     // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
744                     // should very seldom change, and taking the risk of using the wrong action is
745                     // preferable to taking the lock for every single compaction action.
746                     if (lastCompactTime != 0) {
747                         if (pendingAction == COMPACT_PROCESS_SOME) {
748                             if ((lastCompactAction == COMPACT_PROCESS_SOME
749                                     && (start - lastCompactTime < mCompactThrottleSomeSome))
750                                     || (lastCompactAction == COMPACT_PROCESS_FULL
751                                         && (start - lastCompactTime
752                                                 < mCompactThrottleSomeFull))) {
753                                 if (DEBUG_COMPACTION) {
754                                     Slog.d(TAG_AM, "Skipping some compaction for " + name
755                                             + ": too soon. throttle=" + mCompactThrottleSomeSome
756                                             + "/" + mCompactThrottleSomeFull + " last="
757                                             + (start - lastCompactTime) + "ms ago");
758                                 }
759                                 return;
760                             }
761                         } else if (pendingAction == COMPACT_PROCESS_FULL) {
762                             if ((lastCompactAction == COMPACT_PROCESS_SOME
763                                     && (start - lastCompactTime < mCompactThrottleFullSome))
764                                     || (lastCompactAction == COMPACT_PROCESS_FULL
765                                         && (start - lastCompactTime
766                                                 < mCompactThrottleFullFull))) {
767                                 if (DEBUG_COMPACTION) {
768                                     Slog.d(TAG_AM, "Skipping full compaction for " + name
769                                             + ": too soon. throttle=" + mCompactThrottleFullSome
770                                             + "/" + mCompactThrottleFullFull + " last="
771                                             + (start - lastCompactTime) + "ms ago");
772                                 }
773                                 return;
774                             }
775                         } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
776                             if (start - lastCompactTime < mCompactThrottlePersistent) {
777                                 if (DEBUG_COMPACTION) {
778                                     Slog.d(TAG_AM, "Skipping persistent compaction for " + name
779                                             + ": too soon. throttle=" + mCompactThrottlePersistent
780                                             + " last=" + (start - lastCompactTime) + "ms ago");
781                                 }
782                                 return;
783                             }
784                         } else if (pendingAction == COMPACT_PROCESS_BFGS) {
785                             if (start - lastCompactTime < mCompactThrottleBFGS) {
786                                 if (DEBUG_COMPACTION) {
787                                     Slog.d(TAG_AM, "Skipping bfgs compaction for " + name
788                                             + ": too soon. throttle=" + mCompactThrottleBFGS
789                                             + " last=" + (start - lastCompactTime) + "ms ago");
790                                 }
791                                 return;
792                             }
793                         }
794                     }
795 
796                     switch (pendingAction) {
797                         case COMPACT_PROCESS_SOME:
798                             action = mCompactActionSome;
799                             break;
800                         // For the time being, treat these as equivalent.
801                         case COMPACT_PROCESS_FULL:
802                         case COMPACT_PROCESS_PERSISTENT:
803                         case COMPACT_PROCESS_BFGS:
804                             action = mCompactActionFull;
805                             break;
806                         default:
807                             action = COMPACT_ACTION_NONE;
808                             break;
809                     }
810 
811                     if (COMPACT_ACTION_NONE.equals(action)) {
812                         return;
813                     }
814 
815                     if (mProcStateThrottle.contains(procState)) {
816                         if (DEBUG_COMPACTION) {
817                             Slog.d(TAG_AM, "Skipping full compaction for process " + name
818                                     + "; proc state is " + procState);
819                         }
820                         return;
821                     }
822 
823                     long[] rssBefore = mProcessDependencies.getRss(pid);
824                     long anonRssBefore = rssBefore[2];
825 
826                     if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0
827                             && rssBefore[3] == 0) {
828                         if (DEBUG_COMPACTION) {
829                             Slog.d(TAG_AM, "Skipping compaction for" + "process " + pid
830                                     + " with no memory usage. Dead?");
831                         }
832                         return;
833                     }
834 
835                     if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) {
836                         if (mFullAnonRssThrottleKb > 0L
837                                 && anonRssBefore < mFullAnonRssThrottleKb) {
838                             if (DEBUG_COMPACTION) {
839                                 Slog.d(TAG_AM, "Skipping full compaction for process "
840                                         + name + "; anon RSS is too small: " + anonRssBefore
841                                         + "KB.");
842                             }
843                             return;
844                         }
845 
846                         if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) {
847                             long[] lastRss = lastCompactionStats.getRssAfterCompaction();
848                             long absDelta = Math.abs(rssBefore[1] - lastRss[1])
849                                     + Math.abs(rssBefore[2] - lastRss[2])
850                                     + Math.abs(rssBefore[3] - lastRss[3]);
851                             if (absDelta <= mFullDeltaRssThrottleKb) {
852                                 if (DEBUG_COMPACTION) {
853                                     Slog.d(TAG_AM, "Skipping full compaction for process "
854                                             + name + "; abs delta is too small: " + absDelta
855                                             + "KB.");
856                                 }
857                                 return;
858                             }
859                         }
860                     }
861 
862                     // Now we've passed through all the throttles and are going to compact, update
863                     // bookkeeping.
864                     switch (pendingAction) {
865                         case COMPACT_PROCESS_SOME:
866                             mSomeCompactionCount++;
867                             break;
868                         case COMPACT_PROCESS_FULL:
869                             mFullCompactionCount++;
870                             break;
871                         case COMPACT_PROCESS_PERSISTENT:
872                             mPersistentCompactionCount++;
873                             break;
874                         case COMPACT_PROCESS_BFGS:
875                             mBfgsCompactionCount++;
876                             break;
877                         default:
878                             break;
879                     }
880                     try {
881                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
882                                 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
883                                 + ": " + name);
884                         long zramFreeKbBefore = Debug.getZramFreeKb();
885                         mProcessDependencies.performCompaction(action, pid);
886                         long[] rssAfter = mProcessDependencies.getRss(pid);
887                         long end = SystemClock.uptimeMillis();
888                         long time = end - start;
889                         long zramFreeKbAfter = Debug.getZramFreeKb();
890                         EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
891                                 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
892                                 rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1],
893                                 rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time,
894                                 lastCompactAction, lastCompactTime, lastOomAdj, procState,
895                                 zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore);
896                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
897                         // on every single compaction for a flag that will seldom change and the
898                         // impact of reading the wrong value here is low.
899                         if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
900                             FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
901                                     pendingAction, rssBefore[0], rssBefore[1], rssBefore[2],
902                                     rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2],
903                                     rssAfter[3], time, lastCompactAction, lastCompactTime,
904                                     lastOomAdj, ActivityManager.processStateAmToProto(procState),
905                                     zramFreeKbBefore, zramFreeKbAfter);
906                         }
907                         synchronized (mAm) {
908                             proc.lastCompactTime = end;
909                             proc.lastCompactAction = pendingAction;
910                         }
911                         if (action.equals(COMPACT_ACTION_FULL)
912                                 || action.equals(COMPACT_ACTION_ANON)) {
913                             // Remove entry and insert again to update insertion order.
914                             mLastCompactionStats.remove(pid);
915                             mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
916                         }
917                     } catch (Exception e) {
918                         // nothing to do, presumably the process died
919                     } finally {
920                         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
921                     }
922                     break;
923                 }
924                 case COMPACT_SYSTEM_MSG: {
925                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
926                     compactSystem();
927                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
928                     break;
929                 }
930             }
931         }
932     }
933 
934     private final class FreezeHandler extends Handler {
FreezeHandler()935         private FreezeHandler() {
936             super(mCachedAppOptimizerThread.getLooper());
937         }
938 
939         @Override
handleMessage(Message msg)940         public void handleMessage(Message msg) {
941             switch (msg.what) {
942                 case SET_FROZEN_PROCESS_MSG:
943                     freezeProcess((ProcessRecord) msg.obj);
944                     break;
945                 case REPORT_UNFREEZE_MSG:
946                     int pid = msg.arg1;
947                     int frozenDuration = msg.arg2;
948                     String processName = (String) msg.obj;
949 
950                     reportUnfreeze(pid, frozenDuration, processName);
951                     break;
952                 default:
953                     return;
954             }
955         }
956 
freezeProcess(ProcessRecord proc)957         private void freezeProcess(ProcessRecord proc) {
958             final int pid;
959             final String name;
960             final long unfrozenDuration;
961             final boolean frozen;
962 
963             synchronized (mAm) {
964                 pid = proc.pid;
965                 name = proc.processName;
966 
967                 if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
968                         || proc.shouldNotFreeze) {
969                     if (DEBUG_FREEZER) {
970                         Slog.d(TAG_AM, "Skipping freeze for process " + pid
971                                 + " " + name + " curAdj = " + proc.curAdj
972                                 + ", shouldNotFreeze = " + proc.shouldNotFreeze);
973                     }
974                     return;
975                 }
976 
977                 if (pid == 0 || proc.frozen) {
978                     // Already frozen or not a real process, either one being
979                     // launched or one being killed
980                     return;
981                 }
982 
983                 long unfreezeTime = proc.freezeUnfreezeTime;
984 
985                 try {
986                     Process.setProcessFrozen(pid, proc.uid, true);
987 
988                     proc.freezeUnfreezeTime = SystemClock.uptimeMillis();
989                     proc.frozen = true;
990                 } catch (Exception e) {
991                     Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
992                 }
993 
994                 unfrozenDuration = proc.freezeUnfreezeTime - unfreezeTime;
995                 frozen = proc.frozen;
996             }
997 
998             if (frozen) {
999                 if (DEBUG_FREEZER) {
1000                     Slog.d(TAG_AM, "froze " + pid + " " + name);
1001                 }
1002 
1003                 EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
1004 
1005                 // See above for why we're not taking mPhenotypeFlagLock here
1006                 if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
1007                     FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
1008                             FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
1009                             pid,
1010                             name,
1011                             unfrozenDuration);
1012                 }
1013             }
1014         }
1015 
reportUnfreeze(int pid, int frozenDuration, String processName)1016         private void reportUnfreeze(int pid, int frozenDuration, String processName) {
1017 
1018             EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName);
1019 
1020             // See above for why we're not taking mPhenotypeFlagLock here
1021             if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
1022                 FrameworkStatsLog.write(
1023                         FrameworkStatsLog.APP_FREEZE_CHANGED,
1024                         FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP,
1025                         pid,
1026                         processName,
1027                         frozenDuration);
1028             }
1029         }
1030     }
1031 
1032     /**
1033      * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
1034      */
1035     private static final class DefaultProcessDependencies implements ProcessDependencies {
1036         // Get memory RSS from process.
1037         @Override
getRss(int pid)1038         public long[] getRss(int pid) {
1039             return Process.getRss(pid);
1040         }
1041 
1042         // Compact process.
1043         @Override
performCompaction(String action, int pid)1044         public void performCompaction(String action, int pid) throws IOException {
1045             try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) {
1046                 fos.write(action.getBytes());
1047             }
1048         }
1049     }
1050 }
1051