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