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