• 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.PowerManagerInternal;
32 import android.os.Process;
33 import android.os.SystemClock;
34 import android.os.Trace;
35 import android.provider.DeviceConfig;
36 import android.provider.DeviceConfig.OnPropertiesChangedListener;
37 import android.provider.DeviceConfig.Properties;
38 import android.provider.Settings;
39 import android.text.TextUtils;
40 import android.util.EventLog;
41 import android.util.Pair;
42 import android.util.Slog;
43 import android.util.SparseArray;
44 
45 import com.android.internal.annotations.GuardedBy;
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.internal.os.ProcLocksReader;
48 import com.android.internal.util.FrameworkStatsLog;
49 import com.android.server.ServiceThread;
50 
51 import java.io.FileReader;
52 import java.io.IOException;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.HashSet;
57 import java.util.LinkedHashMap;
58 import java.util.Map;
59 import java.util.Random;
60 import java.util.Set;
61 
62 public final class CachedAppOptimizer {
63 
64     // Flags stored in the DeviceConfig API.
65     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
66     @VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer";
67     @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
68     @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
69     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
70     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
71     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
72     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
73     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5";
74     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
75     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MIN_OOM_ADJ =
76             "compact_throttle_min_oom_adj";
77     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MAX_OOM_ADJ =
78             "compact_throttle_max_oom_adj";
79     @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
80             "compact_statsd_sample_rate";
81     @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE =
82             "freeze_statsd_sample_rate";
83     @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB =
84             "compact_full_rss_throttle_kb";
85     @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB =
86             "compact_full_delta_rss_throttle_kb";
87     @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE =
88             "compact_proc_state_throttle";
89     @VisibleForTesting static final String KEY_FREEZER_DEBOUNCE_TIMEOUT =
90             "freeze_debounce_timeout";
91 
92     // RSS Indices
93     private static final int RSS_TOTAL_INDEX = 0;
94     private static final int RSS_FILE_INDEX = 1;
95     private static final int RSS_ANON_INDEX = 2;
96     private static final int RSS_SWAP_INDEX = 3;
97 
98     // Phenotype sends int configurations and we map them to the strings we'll use on device,
99     // preventing a weird string value entering the kernel.
100     private static final int COMPACT_ACTION_NONE = 0;
101     private static final int COMPACT_ACTION_FILE = 1;
102     private static final int COMPACT_ACTION_ANON = 2;
103     private static final int COMPACT_ACTION_FULL = 3;
104 
105     private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
106 
107     // Keeps these flags in sync with services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
108     private static final int COMPACT_ACTION_FILE_FLAG = 1;
109     private static final int COMPACT_ACTION_ANON_FLAG = 2;
110 
111     private static final String ATRACE_COMPACTION_TRACK = "Compaction";
112 
113     @VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
114 
115     // Defaults for phenotype flags.
116     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
117     @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
118     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
119     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
120     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
121     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
122     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
123     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
124     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000;
125     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000;
126     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ =
127             ProcessList.CACHED_APP_MIN_ADJ;
128     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ =
129             ProcessList.CACHED_APP_MAX_ADJ;
130     // The sampling rate to push app compaction events into statsd for upload.
131     @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
132     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L;
133     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 8_000L;
134     // Format of this string should be a comma separated list of integers.
135     @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
136             String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
137     @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L;
138 
139     @VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
140                 Settings.Global.CACHED_APPS_FREEZER_ENABLED);
141 
142     @VisibleForTesting
143     interface PropertyChangedCallbackForTest {
onPropertyChanged()144         void onPropertyChanged();
145     }
146     private PropertyChangedCallbackForTest mTestCallback;
147 
148     // This interface is for functions related to the Process object that need a different
149     // implementation in the tests as we are not creating real processes when testing compaction.
150     @VisibleForTesting
151     interface ProcessDependencies {
getRss(int pid)152         long[] getRss(int pid);
performCompaction(String action, int pid)153         void performCompaction(String action, int pid) throws IOException;
154     }
155 
156     // Handler constants.
157     static final int COMPACT_PROCESS_SOME = 1;
158     static final int COMPACT_PROCESS_FULL = 2;
159     static final int COMPACT_PROCESS_PERSISTENT = 3;
160     static final int COMPACT_PROCESS_BFGS = 4;
161     static final int COMPACT_PROCESS_MSG = 1;
162     static final int COMPACT_SYSTEM_MSG = 2;
163     static final int SET_FROZEN_PROCESS_MSG = 3;
164     static final int REPORT_UNFREEZE_MSG = 4;
165 
166     // When free swap falls below this percentage threshold any full (file + anon)
167     // compactions will be downgraded to file only compactions to reduce pressure
168     // on swap resources as file.
169     static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2;
170 
171     static final int DO_FREEZE = 1;
172     static final int REPORT_UNFREEZE = 2;
173 
174     // Bitfield values for sync/async transactions reveived by frozen processes
175     static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
176     static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
177 
178     // Bitfield values for sync transactions received by frozen binder threads
179     static final int TXNS_PENDING_WHILE_FROZEN = 4;
180 
181     /**
182      * This thread must be moved to the system background cpuset.
183      * If that doesn't happen, it's probably going to draw a lot of power.
184      * However, this has to happen after the first updateOomAdjLocked, because
185      * that will wipe out the cpuset assignment for system_server threads.
186      * Accordingly, this is in the AMS constructor.
187      */
188     final ServiceThread mCachedAppOptimizerThread;
189 
190     @GuardedBy("mProcLock")
191     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
192             new ArrayList<ProcessRecord>();
193 
194     @GuardedBy("mProcLock")
195     private final SparseArray<ProcessRecord> mFrozenProcesses =
196             new SparseArray<>();
197 
198     private final ActivityManagerService mAm;
199 
200     private final ActivityManagerGlobalLock mProcLock;
201 
202     private final Object mFreezerLock = new Object();
203 
204     private final OnPropertiesChangedListener mOnFlagsChangedListener =
205             new OnPropertiesChangedListener() {
206                 @Override
207                 public void onPropertiesChanged(Properties properties) {
208                     synchronized (mPhenotypeFlagLock) {
209                         for (String name : properties.getKeyset()) {
210                             if (KEY_USE_COMPACTION.equals(name)) {
211                                 updateUseCompaction();
212                             } else if (KEY_COMPACT_ACTION_1.equals(name)
213                                     || KEY_COMPACT_ACTION_2.equals(name)) {
214                                 updateCompactionActions();
215                             } else if (KEY_COMPACT_THROTTLE_1.equals(name)
216                                     || KEY_COMPACT_THROTTLE_2.equals(name)
217                                     || KEY_COMPACT_THROTTLE_3.equals(name)
218                                     || KEY_COMPACT_THROTTLE_4.equals(name)
219                                     || KEY_COMPACT_THROTTLE_5.equals(name)
220                                     || KEY_COMPACT_THROTTLE_6.equals(name)) {
221                                 updateCompactionThrottles();
222                             } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
223                                 updateCompactStatsdSampleRate();
224                             } else if (KEY_FREEZER_STATSD_SAMPLE_RATE.equals(name)) {
225                                 updateFreezerStatsdSampleRate();
226                             } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
227                                 updateFullRssThrottle();
228                             } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
229                                 updateFullDeltaRssThrottle();
230                             } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
231                                 updateProcStateThrottle();
232                             } else if (KEY_COMPACT_THROTTLE_MIN_OOM_ADJ.equals(name)) {
233                                 updateMinOomAdjThrottle();
234                             } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
235                                 updateMaxOomAdjThrottle();
236                             }
237                         }
238                     }
239                     if (mTestCallback != null) {
240                         mTestCallback.onPropertyChanged();
241                     }
242                 }
243             };
244 
245     private final OnPropertiesChangedListener mOnNativeBootFlagsChangedListener =
246             new OnPropertiesChangedListener() {
247                 @Override
248                 public void onPropertiesChanged(Properties properties) {
249                     synchronized (mPhenotypeFlagLock) {
250                         for (String name : properties.getKeyset()) {
251                             if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
252                                 updateFreezerDebounceTimeout();
253                             }
254                         }
255                     }
256                     if (mTestCallback != null) {
257                         mTestCallback.onPropertyChanged();
258                     }
259                 }
260             };
261 
262     private final class SettingsContentObserver extends ContentObserver {
SettingsContentObserver()263         SettingsContentObserver() {
264             super(mAm.mHandler);
265         }
266 
267         @Override
onChange(boolean selfChange, Uri uri)268         public void onChange(boolean selfChange, Uri uri) {
269             if (CACHED_APP_FREEZER_ENABLED_URI.equals(uri)) {
270                 synchronized (mPhenotypeFlagLock) {
271                     updateUseFreezer();
272                 }
273             }
274         }
275     }
276 
277     private final SettingsContentObserver mSettingsObserver;
278 
279     private final Object mPhenotypeFlagLock = new Object();
280 
281     // Configured by phenotype. Updates from the server take effect immediately.
282     @GuardedBy("mPhenotypeFlagLock")
283     @VisibleForTesting volatile String mCompactActionSome =
284             compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
285     @GuardedBy("mPhenotypeFlagLock")
286     @VisibleForTesting volatile String mCompactActionFull =
287             compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
288     @GuardedBy("mPhenotypeFlagLock")
289     @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
290     @GuardedBy("mPhenotypeFlagLock")
291     @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
292     @GuardedBy("mPhenotypeFlagLock")
293     @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
294     @GuardedBy("mPhenotypeFlagLock")
295     @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
296     @GuardedBy("mPhenotypeFlagLock")
297     @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
298     @GuardedBy("mPhenotypeFlagLock")
299     @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
300     @GuardedBy("mPhenotypeFlagLock")
301     @VisibleForTesting volatile long mCompactThrottleMinOomAdj =
302             DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
303     @GuardedBy("mPhenotypeFlagLock")
304     @VisibleForTesting volatile long mCompactThrottleMaxOomAdj =
305             DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
306     @GuardedBy("mPhenotypeFlagLock")
307     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
308     private volatile boolean mUseFreezer = false; // set to DEFAULT in init()
309     @GuardedBy("this")
310     private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled
311     private final Random mRandom = new Random();
312     @GuardedBy("mPhenotypeFlagLock")
313     @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
314     @VisibleForTesting volatile float mFreezerStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
315     @GuardedBy("mPhenotypeFlagLock")
316     @VisibleForTesting volatile long mFullAnonRssThrottleKb =
317             DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
318     @GuardedBy("mPhenotypeFlagLock")
319     @VisibleForTesting volatile long mFullDeltaRssThrottleKb =
320             DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
321     @GuardedBy("mPhenotypeFlagLock")
322     @VisibleForTesting final Set<Integer> mProcStateThrottle;
323 
324     // Handler on which compaction runs.
325     @VisibleForTesting
326     Handler mCompactionHandler;
327     private Handler mFreezeHandler;
328     @GuardedBy("mProcLock")
329     private boolean mFreezerOverride = false;
330 
331     @VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
332 
333     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
334     // when evaluating throttles that we only consider for "full" compaction, so we don't store
335     // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
336     // facilitate removal of the oldest entry.
337     @VisibleForTesting
338     @GuardedBy("mProcLock")
339     LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
340             new LinkedHashMap<Integer, LastCompactionStats>() {
341                 @Override
342                 protected boolean removeEldestEntry(Map.Entry eldest) {
343                     return size() > 100;
344                 }
345     };
346 
347     // Compaction Stats
348     private int mSomeCompactionCount;
349     private int mFullCompactionCount;
350     private int mPersistentCompactionCount;
351     private int mBfgsCompactionCount;
352     private long mSomeCompactRequest;
353     private long mFullCompactRequest;
354     private long mPersistentCompactRequest;
355     private long mBfgsCompactRequest;
356     private long mProcCompactionsRequested;
357     private long mProcCompactionsPerformed;
358     private long mProcCompactionsNoPidThrottled;
359     private long mProcCompactionsOomAdjThrottled;
360     private long mProcCompactionsTimeThrottled;
361     private long mProcCompactionsRSSThrottled;
362     private long mProcCompactionsMiscThrottled;
363     private long mSystemCompactionsPerformed;
364 
365     private final ProcessDependencies mProcessDependencies;
366     private final ProcLocksReader mProcLocksReader;
367 
CachedAppOptimizer(ActivityManagerService am)368     public CachedAppOptimizer(ActivityManagerService am) {
369         this(am, null, new DefaultProcessDependencies());
370     }
371 
372     @VisibleForTesting
CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback, ProcessDependencies processDependencies)373     CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
374             ProcessDependencies processDependencies) {
375         mAm = am;
376         mProcLock = am.mProcLock;
377         mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
378             Process.THREAD_GROUP_SYSTEM, true);
379         mProcStateThrottle = new HashSet<>();
380         mProcessDependencies = processDependencies;
381         mTestCallback = callback;
382         mSettingsObserver = new SettingsContentObserver();
383         mProcLocksReader = new ProcLocksReader();
384     }
385 
386     /**
387      * Reads phenotype config to determine whether app compaction is enabled or not and
388      * starts the background thread if necessary.
389      */
init()390     public void init() {
391         // TODO: initialize flags to default and only update them if values are set in DeviceConfig
392         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
393                 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
394         DeviceConfig.addOnPropertiesChangedListener(
395                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
396                 ActivityThread.currentApplication().getMainExecutor(),
397                 mOnNativeBootFlagsChangedListener);
398         mAm.mContext.getContentResolver().registerContentObserver(
399                 CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
400         synchronized (mPhenotypeFlagLock) {
401             updateUseCompaction();
402             updateCompactionActions();
403             updateCompactionThrottles();
404             updateCompactStatsdSampleRate();
405             updateFreezerStatsdSampleRate();
406             updateFullRssThrottle();
407             updateFullDeltaRssThrottle();
408             updateProcStateThrottle();
409             updateUseFreezer();
410             updateMinOomAdjThrottle();
411             updateMaxOomAdjThrottle();
412         }
413     }
414 
415     /**
416      * Returns whether compaction is enabled.
417      */
useCompaction()418     public boolean useCompaction() {
419         synchronized (mPhenotypeFlagLock) {
420             return mUseCompaction;
421         }
422     }
423 
424     /**
425      * Returns whether freezer is enabled.
426      */
useFreezer()427     public boolean useFreezer() {
428         synchronized (mPhenotypeFlagLock) {
429             return mUseFreezer;
430         }
431     }
432 
433     @GuardedBy("mProcLock")
dump(PrintWriter pw)434     void dump(PrintWriter pw) {
435         pw.println("CachedAppOptimizer settings");
436         synchronized (mPhenotypeFlagLock) {
437             pw.println("  " + KEY_USE_COMPACTION + "=" + mUseCompaction);
438             pw.println("  " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
439             pw.println("  " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull);
440             pw.println("  " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome);
441             pw.println("  " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
442             pw.println("  " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
443             pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
444             pw.println("  " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
445             pw.println("  " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
446             pw.println("  " + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ + "=" + mCompactThrottleMinOomAdj);
447             pw.println("  " + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ + "=" + mCompactThrottleMaxOomAdj);
448             pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate);
449             pw.println("  " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "="
450                     + mFullAnonRssThrottleKb);
451             pw.println("  " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "="
452                     + mFullDeltaRssThrottleKb);
453             pw.println("  "  + KEY_COMPACT_PROC_STATE_THROTTLE + "="
454                     + Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
455 
456             pw.println(" Requested:  " + mSomeCompactRequest + " some, " + mFullCompactRequest
457                     + " full, " + mPersistentCompactRequest + " persistent, "
458                     + mBfgsCompactRequest + " BFGS compactions.");
459             pw.println(" Performed: " + mSomeCompactionCount + " some, " + mFullCompactionCount
460                     + " full, " + mPersistentCompactionCount + " persistent, "
461                     + mBfgsCompactionCount + " BFGS compactions.");
462             pw.println(" Process Compactions Requested: " + mProcCompactionsRequested);
463             pw.println(" Process Compactions Performed: " + mProcCompactionsPerformed);
464             long compactionsThrottled = mProcCompactionsRequested - mProcCompactionsPerformed;
465             pw.println(" Process Compactions Throttled: " + compactionsThrottled);
466             double compactThrottlePercentage =
467                     (compactionsThrottled / (double) mProcCompactionsRequested) * 100.0;
468             pw.println(" Process Compactions Throttle Percentage: " + compactThrottlePercentage);
469             pw.println("        NoPid Throttled: " + mProcCompactionsNoPidThrottled);
470             pw.println("        OomAdj Throttled: " + mProcCompactionsOomAdjThrottled);
471             pw.println("        Time Throttled: " + mProcCompactionsTimeThrottled);
472             pw.println("        RSS Throttled: " + mProcCompactionsRSSThrottled);
473             pw.println("        Misc Throttled: " + mProcCompactionsMiscThrottled);
474             long unaccountedThrottled = compactionsThrottled - mProcCompactionsNoPidThrottled
475                     - mProcCompactionsOomAdjThrottled - mProcCompactionsTimeThrottled
476                     - mProcCompactionsRSSThrottled - mProcCompactionsMiscThrottled;
477             // Any throttle that was not part of the previous categories
478             pw.println("        Unaccounted Throttled: " + unaccountedThrottled);
479 
480             pw.println(" System Compactions Performed: " + mSystemCompactionsPerformed);
481 
482             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
483                     + " processes.");
484             pw.println("  " + KEY_USE_FREEZER + "=" + mUseFreezer);
485             pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
486             pw.println("  " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
487             synchronized (mProcLock) {
488                 int size = mFrozenProcesses.size();
489                 pw.println("  Apps frozen: " + size);
490                 for (int i = 0; i < size; i++) {
491                     ProcessRecord app = mFrozenProcesses.valueAt(i);
492                     pw.println("    " + app.mOptRecord.getFreezeUnfreezeTime()
493                             + ": " + app.getPid() + " " + app.processName);
494                 }
495 
496                 if (!mPendingCompactionProcesses.isEmpty()) {
497                     pw.println("  Pending compactions:");
498                     size = mPendingCompactionProcesses.size();
499                     for (int i = 0; i < size; i++) {
500                         ProcessRecord app = mPendingCompactionProcesses.get(i);
501                         pw.println("    pid: " + app.getPid() + ". name: " + app.processName
502                                 + ". hasPendingCompact: " + app.mOptRecord.hasPendingCompact());
503                     }
504                 }
505             }
506             if (DEBUG_COMPACTION) {
507                 for (Map.Entry<Integer, LastCompactionStats> entry
508                         : mLastCompactionStats.entrySet()) {
509                     int pid = entry.getKey();
510                     LastCompactionStats stats = entry.getValue();
511                     pw.println("    " + pid + ": "
512                             + Arrays.toString(stats.getRssAfterCompaction()));
513                 }
514             }
515         }
516     }
517 
518     @GuardedBy("mProcLock")
compactAppSome(ProcessRecord app, boolean force)519     void compactAppSome(ProcessRecord app, boolean force) {
520         if (ENABLE_FILE_COMPACT) {
521             app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
522             ++mSomeCompactRequest;
523             compactApp(app, force, "some");
524         }
525     }
526 
527     // This method returns true only if requirements are met. Note, that requirements are different
528     // from throttles applied at the time a compaction is trying to be executed in the sense that
529     // these are not subject to change dependent on time or memory as throttles usually do.
530     @GuardedBy("mProcLock")
meetsCompactionRequirements(ProcessRecord proc)531     boolean meetsCompactionRequirements(ProcessRecord proc) {
532         if (mAm.mInternal.isPendingTopUid(proc.uid)) {
533             // In case the OOM Adjust has not yet been propagated we see if this is
534             // pending on becoming top app in which case we should not compact.
535             if (DEBUG_COMPACTION) {
536                 Slog.d(TAG_AM, "Skip compaction since UID is active for  " + proc.processName);
537             }
538             return false;
539         }
540 
541         if (proc.mState.hasForegroundActivities()) {
542             if (DEBUG_COMPACTION) {
543                 Slog.e(TAG_AM,
544                         "Skip compaction as process " + proc.processName
545                                 + " has foreground activities");
546             }
547             return false;
548         }
549 
550         return true;
551     }
552 
553     @GuardedBy("mProcLock")
compactAppFull(ProcessRecord app, boolean force)554     void compactAppFull(ProcessRecord app, boolean force) {
555         boolean oomAdjEnteredCached = (app.mState.getSetAdj() < mCompactThrottleMinOomAdj
556                                               || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
557                 && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
558                 && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj;
559         if (DEBUG_COMPACTION) {
560             Slog.d(TAG_AM,
561                     " compactAppFull requested for " + app.processName + " force: " + force
562                             + " oomAdjEnteredCached: " + oomAdjEnteredCached);
563         }
564         ++mFullCompactRequest;
565         // Apply OOM adj score throttle for Full App Compaction.
566         if (force || oomAdjEnteredCached) {
567             app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
568             compactApp(app, force, "Full");
569         } else {
570             if (DEBUG_COMPACTION) {
571                 Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
572                         + " oom adj score changed from " + app.mState.getSetAdj()
573                         + " to " + app.mState.getCurAdj());
574             }
575         }
576     }
577 
578     @GuardedBy("mProcLock")
compactAppPersistent(ProcessRecord app)579     void compactAppPersistent(ProcessRecord app) {
580         app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
581         ++mPersistentCompactRequest;
582         compactApp(app, false, "Persistent");
583     }
584 
585     @GuardedBy("mProcLock")
compactApp(ProcessRecord app, boolean force, String compactRequestType)586     boolean compactApp(ProcessRecord app, boolean force, String compactRequestType) {
587         if (!app.mOptRecord.hasPendingCompact() && meetsCompactionRequirements(app)) {
588             final String processName = (app.processName != null ? app.processName : "");
589             if (DEBUG_COMPACTION) {
590                 Slog.d(TAG_AM, "compactApp " + compactRequestType + " " + processName);
591             }
592             Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
593                     "compactApp " + compactRequestType + " " + processName);
594             app.mOptRecord.setHasPendingCompact(true);
595             app.mOptRecord.setForceCompact(force);
596             mPendingCompactionProcesses.add(app);
597             mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
598                     COMPACT_PROCESS_MSG, app.mState.getCurAdj(), app.mState.getSetProcState()));
599             return true;
600         }
601 
602         if (DEBUG_COMPACTION) {
603             Slog.d(TAG_AM,
604                     " compactApp Skipped for " + app.processName
605                             + " pendingCompact= " + app.mOptRecord.hasPendingCompact()
606                             + " meetsCompactionRequirements=" + meetsCompactionRequirements(app)
607                             + ". Requested compact: " + app.mOptRecord.getReqCompactAction());
608         }
609         return false;
610     }
611 
612     @GuardedBy("mProcLock")
shouldCompactPersistent(ProcessRecord app, long now)613     boolean shouldCompactPersistent(ProcessRecord app, long now) {
614         return (app.mOptRecord.getLastCompactTime() == 0
615                 || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottlePersistent);
616     }
617 
618     @GuardedBy("mProcLock")
compactAppBfgs(ProcessRecord app)619     void compactAppBfgs(ProcessRecord app) {
620         ++mBfgsCompactRequest;
621         app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
622         compactApp(app, false, " Bfgs");
623     }
624 
625     @GuardedBy("mProcLock")
shouldCompactBFGS(ProcessRecord app, long now)626     boolean shouldCompactBFGS(ProcessRecord app, long now) {
627         return (app.mOptRecord.getLastCompactTime() == 0
628                 || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
629     }
630 
compactAllSystem()631     void compactAllSystem() {
632         if (useCompaction()) {
633             if (DEBUG_COMPACTION) {
634                 Slog.d(TAG_AM, "compactAllSystem");
635             }
636             Trace.instantForTrack(
637                     Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK, "compactAllSystem");
638             mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
639                                               COMPACT_SYSTEM_MSG));
640         }
641     }
642 
compactSystem()643     private native void compactSystem();
644 
645     /**
646      * Compacts a process or app
647      * @param pid pid of process to compact
648      * @param compactionFlags selects the compaction type as defined by COMPACT_ACTION_{TYPE}_FLAG
649      *         constants
650      */
compactProcess(int pid, int compactionFlags)651     static private native void compactProcess(int pid, int compactionFlags);
652 
cancelCompaction()653     static private native void cancelCompaction();
654 
655     /**
656      * Retrieves the free swap percentage.
657      */
getFreeSwapPercent()658     static native double getFreeSwapPercent();
659 
660     /**
661      * Reads the flag value from DeviceConfig to determine whether app compaction
662      * should be enabled, and starts the freeze/compaction thread if needed.
663      */
664     @GuardedBy("mPhenotypeFlagLock")
updateUseCompaction()665     private void updateUseCompaction() {
666         mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
667                     KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
668 
669         if (mUseCompaction && mCompactionHandler == null) {
670             if (!mCachedAppOptimizerThread.isAlive()) {
671                 mCachedAppOptimizerThread.start();
672             }
673 
674             mCompactionHandler = new MemCompactionHandler();
675 
676             Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
677                     Process.THREAD_GROUP_SYSTEM);
678         }
679     }
680 
681     /**
682      * Enables or disabled the app freezer.
683      * @param enable Enables the freezer if true, disables it if false.
684      * @return true if the operation completed successfully, false otherwise.
685      */
enableFreezer(boolean enable)686     public synchronized boolean enableFreezer(boolean enable) {
687         if (!mUseFreezer) {
688             return false;
689         }
690 
691         if (enable) {
692             mFreezerDisableCount--;
693 
694             if (mFreezerDisableCount > 0) {
695                 return true;
696             } else if (mFreezerDisableCount < 0) {
697                 Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring");
698                 mFreezerDisableCount = 0;
699                 return false;
700             }
701         } else {
702             mFreezerDisableCount++;
703 
704             if (mFreezerDisableCount > 1) {
705                 return true;
706             }
707         }
708 
709         // Override is applied immediately, restore is delayed
710         synchronized (mAm) {
711             synchronized (mProcLock) {
712                 mFreezerOverride = !enable;
713                 Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
714 
715                 mAm.mProcessList.forEachLruProcessesLOSP(true, process -> {
716                     if (process == null) {
717                         return;
718                     }
719 
720                     final ProcessCachedOptimizerRecord opt = process.mOptRecord;
721                     if (enable && opt.hasFreezerOverride()) {
722                         freezeAppAsyncLSP(process);
723                         opt.setFreezerOverride(false);
724                     }
725 
726                     if (!enable && opt.isFrozen()) {
727                         unfreezeAppLSP(process, OomAdjuster.OOM_ADJ_REASON_NONE);
728 
729                         // Set freezerOverride *after* calling unfreezeAppLSP (it resets the flag)
730                         opt.setFreezerOverride(true);
731                     }
732                 });
733             }
734         }
735 
736         return true;
737     }
738 
739     /**
740      * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
741      * this method, this method will synchronously dispatch all pending transactions to the
742      * specified pid. This method will not add significant latencies when unfreezing.
743      * After freezing binder calls, binder will block all transaction to the frozen pid, and return
744      * an error to the sending process.
745      *
746      * @param pid the target pid for which binder transactions are to be frozen
747      * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
748      * binder for the specificed pid.
749      *
750      * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
751      * @return 0 if success, or -EAGAIN indicating there's pending transaction.
752      */
freezeBinder(int pid, boolean freeze)753     private static native int freezeBinder(int pid, boolean freeze);
754 
755     /**
756      * Retrieves binder freeze info about a process.
757      * @param pid the pid for which binder freeze info is to be retrieved.
758      *
759      * @throws RuntimeException if the operation could not complete successfully.
760      * @return a bit field reporting the binder freeze info for the process.
761      */
getBinderFreezeInfo(int pid)762     private static native int getBinderFreezeInfo(int pid);
763 
764     /**
765      * Returns the path to be checked to verify whether the freezer is supported by this system.
766      * @return absolute path to the file
767      */
getFreezerCheckPath()768     private static native String getFreezerCheckPath();
769 
770     /**
771      * Determines whether the freezer is supported by this system
772      */
isFreezerSupported()773     public static boolean isFreezerSupported() {
774         boolean supported = false;
775         FileReader fr = null;
776 
777         try {
778             fr = new FileReader(getFreezerCheckPath());
779             char state = (char) fr.read();
780 
781             if (state == '1' || state == '0') {
782                 // Also check freezer binder ioctl
783                 getBinderFreezeInfo(Process.myPid());
784                 supported = true;
785             } else {
786                 Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
787             }
788         } catch (java.io.FileNotFoundException e) {
789             Slog.w(TAG_AM, "cgroup.freeze not present");
790         } catch (RuntimeException e) {
791             Slog.w(TAG_AM, "unable to read freezer info");
792         } catch (Exception e) {
793             Slog.w(TAG_AM, "unable to read cgroup.freeze: " + e.toString());
794         }
795 
796         if (fr != null) {
797             try {
798                 fr.close();
799             } catch (java.io.IOException e) {
800                 Slog.e(TAG_AM, "Exception closing cgroup.freeze: " + e.toString());
801             }
802         }
803 
804         return supported;
805     }
806 
807     /**
808      * Reads the flag value from DeviceConfig to determine whether app freezer
809      * should be enabled, and starts the freeze/compaction thread if needed.
810      */
811     @GuardedBy("mPhenotypeFlagLock")
updateUseFreezer()812     private void updateUseFreezer() {
813         final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),
814                 Settings.Global.CACHED_APPS_FREEZER_ENABLED);
815 
816         if ("disabled".equals(configOverride)) {
817             mUseFreezer = false;
818         } else if ("enabled".equals(configOverride)
819                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
820                     KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
821             mUseFreezer = isFreezerSupported();
822             updateFreezerDebounceTimeout();
823         } else {
824             mUseFreezer = false;
825         }
826 
827         final boolean useFreezer = mUseFreezer;
828         // enableFreezer() would need the global ActivityManagerService lock, post it.
829         mAm.mHandler.post(() -> {
830             if (useFreezer) {
831                 Slog.d(TAG_AM, "Freezer enabled");
832                 enableFreezer(true);
833 
834                 if (!mCachedAppOptimizerThread.isAlive()) {
835                     mCachedAppOptimizerThread.start();
836                 }
837 
838                 if (mFreezeHandler == null) {
839                     mFreezeHandler = new FreezeHandler();
840                 }
841 
842                 Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
843                         Process.THREAD_GROUP_SYSTEM);
844             } else {
845                 Slog.d(TAG_AM, "Freezer disabled");
846                 enableFreezer(false);
847             }
848         });
849     }
850 
851     @GuardedBy("mPhenotypeFlagLock")
updateCompactionActions()852     private void updateCompactionActions() {
853         int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
854                 KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
855 
856         int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
857                 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
858 
859         mCompactActionSome = compactActionIntToString(compactAction1);
860         mCompactActionFull = compactActionIntToString(compactAction2);
861     }
862 
863     @GuardedBy("mPhenotypeFlagLock")
updateCompactionThrottles()864     private void updateCompactionThrottles() {
865         boolean useThrottleDefaults = false;
866         // TODO: improve efficiency by calling DeviceConfig only once for all flags.
867         String throttleSomeSomeFlag =
868                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
869                     KEY_COMPACT_THROTTLE_1);
870         String throttleSomeFullFlag =
871                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
872                     KEY_COMPACT_THROTTLE_2);
873         String throttleFullSomeFlag =
874                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
875                     KEY_COMPACT_THROTTLE_3);
876         String throttleFullFullFlag =
877                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
878                     KEY_COMPACT_THROTTLE_4);
879         String throttleBFGSFlag =
880                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
881                     KEY_COMPACT_THROTTLE_5);
882         String throttlePersistentFlag =
883                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
884                     KEY_COMPACT_THROTTLE_6);
885         String throttleMinOomAdjFlag =
886                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
887                     KEY_COMPACT_THROTTLE_MIN_OOM_ADJ);
888         String throttleMaxOomAdjFlag =
889                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
890                     KEY_COMPACT_THROTTLE_MAX_OOM_ADJ);
891 
892         if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
893                 || TextUtils.isEmpty(throttleFullSomeFlag)
894                 || TextUtils.isEmpty(throttleFullFullFlag)
895                 || TextUtils.isEmpty(throttleBFGSFlag)
896                 || TextUtils.isEmpty(throttlePersistentFlag)
897                 || TextUtils.isEmpty(throttleMinOomAdjFlag)
898                 || TextUtils.isEmpty(throttleMaxOomAdjFlag)) {
899             // Set defaults for all if any are not set.
900             useThrottleDefaults = true;
901         } else {
902             try {
903                 mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
904                 mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
905                 mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
906                 mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
907                 mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
908                 mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
909                 mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);
910                 mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);
911             } catch (NumberFormatException e) {
912                 useThrottleDefaults = true;
913             }
914         }
915 
916         if (useThrottleDefaults) {
917             mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
918             mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
919             mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
920             mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
921             mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
922             mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
923             mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
924             mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
925         }
926     }
927 
928     @GuardedBy("mPhenotypeFlagLock")
updateCompactStatsdSampleRate()929     private void updateCompactStatsdSampleRate() {
930         mCompactStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
931                 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
932         mCompactStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mCompactStatsdSampleRate));
933     }
934 
935     @GuardedBy("mPhenotypeFlagLock")
updateFreezerStatsdSampleRate()936     private void updateFreezerStatsdSampleRate() {
937         mFreezerStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
938                 KEY_FREEZER_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
939         mFreezerStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mFreezerStatsdSampleRate));
940     }
941 
942     @GuardedBy("mPhenotypeFlagLock")
updateFullRssThrottle()943     private void updateFullRssThrottle() {
944         mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
945                 KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
946 
947         // Don't allow negative values. 0 means don't apply the throttle.
948         if (mFullAnonRssThrottleKb < 0) {
949             mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
950         }
951     }
952 
953     @GuardedBy("mPhenotypeFlagLock")
updateFullDeltaRssThrottle()954     private void updateFullDeltaRssThrottle() {
955         mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
956                 KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
957 
958         if (mFullDeltaRssThrottleKb < 0) {
959             mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
960         }
961     }
962 
963     @GuardedBy("mPhenotypeFlagLock")
updateProcStateThrottle()964     private void updateProcStateThrottle() {
965         String procStateThrottleString = DeviceConfig.getString(
966                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,
967                 DEFAULT_COMPACT_PROC_STATE_THROTTLE);
968         if (!parseProcStateThrottle(procStateThrottleString)) {
969             Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""
970                     + procStateThrottleString + "\" falling back to default.");
971             if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {
972                 Slog.wtf(TAG_AM,
973                         "Unable to parse default app compact proc state throttle "
974                                 + DEFAULT_COMPACT_PROC_STATE_THROTTLE);
975             }
976         }
977     }
978 
979     @GuardedBy("mPhenotypeFlagLock")
updateMinOomAdjThrottle()980     private void updateMinOomAdjThrottle() {
981         mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
982             KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
983 
984         // Should only compact cached processes.
985         if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) {
986             mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
987         }
988     }
989 
990     @GuardedBy("mPhenotypeFlagLock")
updateMaxOomAdjThrottle()991     private void updateMaxOomAdjThrottle() {
992         mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
993             KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
994 
995         // Should only compact cached processes.
996         if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) {
997             mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
998         }
999     }
1000 
1001     @GuardedBy("mPhenotypeFlagLock")
updateFreezerDebounceTimeout()1002     private void updateFreezerDebounceTimeout() {
1003         mFreezerDebounceTimeout = DeviceConfig.getLong(
1004                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
1005                 KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
1006 
1007         if (mFreezerDebounceTimeout < 0) {
1008             mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
1009         }
1010     }
1011 
parseProcStateThrottle(String procStateThrottleString)1012     private boolean parseProcStateThrottle(String procStateThrottleString) {
1013         String[] procStates = TextUtils.split(procStateThrottleString, ",");
1014         mProcStateThrottle.clear();
1015         for (String procState : procStates) {
1016             try {
1017                 mProcStateThrottle.add(Integer.parseInt(procState));
1018             } catch (NumberFormatException e) {
1019                 Slog.e(TAG_AM, "Failed to parse default app compaction proc state: "
1020                         + procState);
1021                 return false;
1022             }
1023         }
1024         return true;
1025     }
1026 
1027     @VisibleForTesting
compactActionIntToString(int action)1028     static String compactActionIntToString(int action) {
1029         if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
1030             return "";
1031         }
1032 
1033         return COMPACT_ACTION_STRING[action];
1034     }
1035 
1036     // This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
1037     @GuardedBy("mAm")
unfreezeTemporarily(ProcessRecord app, String reason)1038     void unfreezeTemporarily(ProcessRecord app, String reason) {
1039         if (mUseFreezer) {
1040             synchronized (mProcLock) {
1041                 if (app.mOptRecord.isFrozen() || app.mOptRecord.isPendingFreeze()) {
1042                     unfreezeAppLSP(app, reason);
1043                     freezeAppAsyncLSP(app);
1044                 }
1045             }
1046         }
1047     }
1048 
1049     @GuardedBy({"mAm", "mProcLock"})
freezeAppAsyncLSP(ProcessRecord app)1050     void freezeAppAsyncLSP(ProcessRecord app) {
1051         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
1052         if (opt.isPendingFreeze()) {
1053             // Skip redundant DO_FREEZE message
1054             return;
1055         }
1056 
1057         mFreezeHandler.sendMessageDelayed(
1058                 mFreezeHandler.obtainMessage(
1059                     SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
1060                 mFreezerDebounceTimeout);
1061         opt.setPendingFreeze(true);
1062         if (DEBUG_FREEZER) {
1063             Slog.d(TAG_AM, "Async freezing " + app.getPid() + " " + app.processName);
1064         }
1065     }
1066 
1067     @GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
unfreezeAppInternalLSP(ProcessRecord app, String reason)1068     void unfreezeAppInternalLSP(ProcessRecord app, String reason) {
1069         final int pid = app.getPid();
1070         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
1071         if (opt.isPendingFreeze()) {
1072             // Remove pending DO_FREEZE message
1073             mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
1074             opt.setPendingFreeze(false);
1075             if (DEBUG_FREEZER) {
1076                 Slog.d(TAG_AM, "Cancel freezing " + pid + " " + app.processName);
1077             }
1078         }
1079 
1080         opt.setFreezerOverride(false);
1081         if (pid == 0 || !opt.isFrozen()) {
1082             return;
1083         }
1084 
1085         // Unfreeze the binder interface first, to avoid transactions triggered by timers fired
1086         // right after unfreezing the process to fail
1087         boolean processKilled = false;
1088 
1089         try {
1090             int freezeInfo = getBinderFreezeInfo(pid);
1091 
1092             if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
1093                 Slog.d(TAG_AM, "pid " + pid + " " + app.processName
1094                         + " received sync transactions while frozen, killing");
1095                 app.killLocked("Sync transaction while in frozen state",
1096                         ApplicationExitInfo.REASON_FREEZER,
1097                         ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true);
1098                 processKilled = true;
1099             }
1100 
1101             if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0 && DEBUG_FREEZER) {
1102                 Slog.d(TAG_AM, "pid " + pid + " " + app.processName
1103                         + " received async transactions while frozen");
1104             }
1105         } catch (Exception e) {
1106             Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "
1107                     + app.processName + ". Killing it. Exception: " + e);
1108             app.killLocked("Unable to query binder frozen stats",
1109                     ApplicationExitInfo.REASON_FREEZER,
1110                     ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
1111             processKilled = true;
1112         }
1113 
1114         if (processKilled) {
1115             return;
1116         }
1117 
1118         long freezeTime = opt.getFreezeUnfreezeTime();
1119 
1120         try {
1121             freezeBinder(pid, false);
1122         } catch (RuntimeException e) {
1123             Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
1124                     + ". Killing it");
1125             app.killLocked("Unable to unfreeze",
1126                     ApplicationExitInfo.REASON_FREEZER,
1127                     ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
1128             return;
1129         }
1130 
1131         try {
1132             Process.setProcessFrozen(pid, app.uid, false);
1133 
1134             opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
1135             opt.setFrozen(false);
1136             mFrozenProcesses.delete(pid);
1137         } catch (Exception e) {
1138             Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName
1139                     + ". This might cause inconsistency or UI hangs.");
1140         }
1141 
1142         if (!opt.isFrozen()) {
1143             Slog.d(TAG_AM, "sync unfroze " + pid + " " + app.processName);
1144 
1145             mFreezeHandler.sendMessage(
1146                     mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
1147                         pid,
1148                         (int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
1149                         new Pair<String, String>(app.processName, reason)));
1150         }
1151     }
1152 
1153     @GuardedBy({"mAm", "mProcLock"})
unfreezeAppLSP(ProcessRecord app, String reason)1154     void unfreezeAppLSP(ProcessRecord app, String reason) {
1155         synchronized (mFreezerLock) {
1156             unfreezeAppInternalLSP(app, reason);
1157         }
1158     }
1159 
1160     /**
1161      * This quick function works around the race condition between WM/ATMS and AMS, allowing
1162      * the former to directly unfreeze a frozen process before the latter runs updateOomAdj.
1163      * After the race issue is solved, this workaround can be removed. (b/213288355)
1164      * The caller of this function should still trigger updateOomAdj for AMS to unfreeze the app.
1165      * @param pid pid of the process to be unfrozen
1166      */
unfreezeProcess(int pid, String reason)1167     void unfreezeProcess(int pid, String reason) {
1168         synchronized (mFreezerLock) {
1169             ProcessRecord app = mFrozenProcesses.get(pid);
1170             if (app == null) {
1171                 return;
1172             }
1173             Slog.d(TAG_AM, "quick sync unfreeze " + pid);
1174             try {
1175                 freezeBinder(pid, false);
1176             } catch (RuntimeException e) {
1177                 Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
1178                 return;
1179             }
1180 
1181             try {
1182                 Process.setProcessFrozen(pid, app.uid, false);
1183             } catch (Exception e) {
1184                 Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
1185             }
1186         }
1187     }
1188 
1189     /**
1190      * To be called when the given app is killed.
1191      */
1192     @GuardedBy({"mAm", "mProcLock"})
onCleanupApplicationRecordLocked(ProcessRecord app)1193     void onCleanupApplicationRecordLocked(ProcessRecord app) {
1194         if (mUseFreezer) {
1195             final ProcessCachedOptimizerRecord opt = app.mOptRecord;
1196             if (opt.isPendingFreeze()) {
1197                 // Remove pending DO_FREEZE message
1198                 mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
1199                 opt.setPendingFreeze(false);
1200             }
1201 
1202             mFrozenProcesses.delete(app.getPid());
1203         }
1204     }
1205 
onWakefulnessChanged(int wakefulness)1206     void onWakefulnessChanged(int wakefulness) {
1207         if(wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
1208             // Remove any pending compaction we may have scheduled to happen while screen was off
1209             Slog.e(TAG_AM, "Cancel pending or running compactions as system is awake");
1210             cancelAllCompactions();
1211         }
1212     }
1213 
cancelAllCompactions()1214     void cancelAllCompactions() {
1215         synchronized (mProcLock) {
1216             int size = mPendingCompactionProcesses.size();
1217             ProcessRecord record;
1218             for (int i=0; i < size; ++i) {
1219                 record = mPendingCompactionProcesses.get(i);
1220                 // The process record is kept alive after compactions are cleared,
1221                 // so make sure to reset the compaction state to avoid skipping any future
1222                 // compactions due to a stale value here.
1223                 record.mOptRecord.setHasPendingCompact(false);
1224             }
1225             mPendingCompactionProcesses.clear();
1226         }
1227         cancelCompaction();
1228     }
1229 
1230     @GuardedBy({"mService", "mProcLock"})
onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app)1231     void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
1232         // Cancel any currently executing compactions
1233         // if the process moved out of cached state
1234         if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
1235                 && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
1236             cancelCompaction();
1237         }
1238 
1239         if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
1240                 && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
1241             // Perform a minor compaction when a perceptible app becomes the prev/home app
1242             compactAppSome(app, false);
1243         } else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
1244                 && newAdj >= ProcessList.CACHED_APP_MIN_ADJ
1245                 && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
1246             // Perform a major compaction when any app enters cached
1247             compactAppFull(app, false);
1248         }
1249     }
1250 
1251     /**
1252      * This method resolves which compaction method we should use for the proposed compaction.
1253      */
resolveCompactionAction(int pendingAction)1254     int resolveCompactionAction(int pendingAction) {
1255         int resolvedAction;
1256 
1257         switch (pendingAction) {
1258             case COMPACT_PROCESS_SOME:
1259                 resolvedAction = COMPACT_ACTION_FILE;
1260                 break;
1261             // For the time being, treat these as equivalent.
1262             case COMPACT_PROCESS_FULL:
1263             case COMPACT_PROCESS_PERSISTENT:
1264             case COMPACT_PROCESS_BFGS:
1265                 resolvedAction = COMPACT_ACTION_FULL;
1266                 break;
1267             default:
1268                 resolvedAction = COMPACT_ACTION_NONE;
1269                 break;
1270         }
1271 
1272         // Downgrade compaction under swap memory pressure
1273         if (resolvedAction == COMPACT_ACTION_FULL) {
1274             double swapFreePercent = getFreeSwapPercent();
1275             if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
1276                 resolvedAction = COMPACT_ACTION_FILE;
1277                 if (DEBUG_COMPACTION) {
1278                     Slog.d(TAG_AM,
1279                             "Downgraded compaction to file only due to low swap."
1280                                     + " Swap Free% " + swapFreePercent);
1281                 }
1282             }
1283         }
1284 
1285         if (!ENABLE_FILE_COMPACT) {
1286             // Turn off file compaction
1287             if (resolvedAction == COMPACT_ACTION_FULL) {
1288                 resolvedAction = COMPACT_ACTION_ANON;
1289             } else if (resolvedAction == COMPACT_ACTION_FILE) {
1290                 resolvedAction = COMPACT_ACTION_NONE;
1291             }
1292         }
1293 
1294         return resolvedAction;
1295     }
1296 
1297     @VisibleForTesting
1298     static final class LastCompactionStats {
1299         private final long[] mRssAfterCompaction;
1300 
LastCompactionStats(long[] rss)1301         LastCompactionStats(long[] rss) {
1302             mRssAfterCompaction = rss;
1303         }
1304 
getRssAfterCompaction()1305         long[] getRssAfterCompaction() {
1306             return mRssAfterCompaction;
1307         }
1308     }
1309 
1310     private final class MemCompactionHandler extends Handler {
MemCompactionHandler()1311         private MemCompactionHandler() {
1312             super(mCachedAppOptimizerThread.getLooper());
1313         }
1314 
shouldOomAdjThrottleCompaction(ProcessRecord proc, int action)1315         private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) {
1316             final String name = proc.processName;
1317 
1318             // don't compact if the process has returned to perceptible
1319             // and this is only a cached/home/prev compaction
1320             if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL)
1321                     && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
1322                 if (DEBUG_COMPACTION) {
1323                     Slog.d(TAG_AM,
1324                             "Skipping compaction as process " + name + " is "
1325                                     + "now perceptible.");
1326                 }
1327                 return true;
1328             }
1329 
1330             return false;
1331         }
1332 
shouldTimeThrottleCompaction( ProcessRecord proc, long start, int pendingAction)1333         private boolean shouldTimeThrottleCompaction(
1334                 ProcessRecord proc, long start, int pendingAction) {
1335             final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
1336             final String name = proc.processName;
1337 
1338             int lastCompactAction = opt.getLastCompactAction();
1339             long lastCompactTime = opt.getLastCompactTime();
1340 
1341             // basic throttling
1342             // use the Phenotype flag knobs to determine whether current/prevous
1343             // compaction combo should be throtted or not
1344 
1345             // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
1346             // should very seldom change, and taking the risk of using the wrong action is
1347             // preferable to taking the lock for every single compaction action.
1348             if (lastCompactTime != 0) {
1349                 if (pendingAction == COMPACT_PROCESS_SOME) {
1350                     if ((lastCompactAction == COMPACT_PROCESS_SOME
1351                                 && (start - lastCompactTime < mCompactThrottleSomeSome))
1352                             || (lastCompactAction == COMPACT_PROCESS_FULL
1353                                     && (start - lastCompactTime < mCompactThrottleSomeFull))) {
1354                         if (DEBUG_COMPACTION) {
1355                             Slog.d(TAG_AM,
1356                                     "Skipping some compaction for " + name
1357                                             + ": too soon. throttle=" + mCompactThrottleSomeSome
1358                                             + "/" + mCompactThrottleSomeFull
1359                                             + " last=" + (start - lastCompactTime) + "ms ago");
1360                         }
1361                         return true;
1362                     }
1363                 } else if (pendingAction == COMPACT_PROCESS_FULL) {
1364                     if ((lastCompactAction == COMPACT_PROCESS_SOME
1365                                 && (start - lastCompactTime < mCompactThrottleFullSome))
1366                             || (lastCompactAction == COMPACT_PROCESS_FULL
1367                                     && (start - lastCompactTime < mCompactThrottleFullFull))) {
1368                         if (DEBUG_COMPACTION) {
1369                             Slog.d(TAG_AM,
1370                                     "Skipping full compaction for " + name
1371                                             + ": too soon. throttle=" + mCompactThrottleFullSome
1372                                             + "/" + mCompactThrottleFullFull
1373                                             + " last=" + (start - lastCompactTime) + "ms ago");
1374                         }
1375                         return true;
1376                     }
1377                 } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
1378                     if (start - lastCompactTime < mCompactThrottlePersistent) {
1379                         if (DEBUG_COMPACTION) {
1380                             Slog.d(TAG_AM,
1381                                     "Skipping persistent compaction for " + name
1382                                             + ": too soon. throttle=" + mCompactThrottlePersistent
1383                                             + " last=" + (start - lastCompactTime) + "ms ago");
1384                         }
1385                         return true;
1386                     }
1387                 } else if (pendingAction == COMPACT_PROCESS_BFGS) {
1388                     if (start - lastCompactTime < mCompactThrottleBFGS) {
1389                         if (DEBUG_COMPACTION) {
1390                             Slog.d(TAG_AM,
1391                                     "Skipping bfgs compaction for " + name
1392                                             + ": too soon. throttle=" + mCompactThrottleBFGS
1393                                             + " last=" + (start - lastCompactTime) + "ms ago");
1394                         }
1395                         return true;
1396                     }
1397                 }
1398             }
1399 
1400             return false;
1401         }
1402 
shouldThrottleMiscCompaction( ProcessRecord proc, int procState, int action)1403         private boolean shouldThrottleMiscCompaction(
1404                 ProcessRecord proc, int procState, int action) {
1405             final String name = proc.processName;
1406             if (mProcStateThrottle.contains(procState)) {
1407                 if (DEBUG_COMPACTION) {
1408                     Slog.d(TAG_AM,
1409                             "Skipping full compaction for process " + name + "; proc state is "
1410                                     + procState);
1411                 }
1412                 return true;
1413             }
1414 
1415             if (COMPACT_ACTION_NONE == action) {
1416                 if (DEBUG_COMPACTION) {
1417                     Slog.d(TAG_AM,
1418                             "Skipping compaction for process " + name + "since action is None");
1419                 }
1420                 return true;
1421             }
1422 
1423             return false;
1424         }
1425 
shouldRssThrottleCompaction( int action, int pid, String name, long[] rssBefore)1426         private boolean shouldRssThrottleCompaction(
1427                 int action, int pid, String name, long[] rssBefore) {
1428             long anonRssBefore = rssBefore[RSS_ANON_INDEX];
1429             LastCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
1430 
1431             if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0
1432                     && rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) {
1433                 if (DEBUG_COMPACTION) {
1434                     Slog.d(TAG_AM,
1435                             "Skipping compaction for"
1436                                     + "process " + pid + " with no memory usage. Dead?");
1437                 }
1438                 return true;
1439             }
1440 
1441             if (action == COMPACT_ACTION_FULL || action == COMPACT_ACTION_ANON) {
1442                 if (mFullAnonRssThrottleKb > 0L && anonRssBefore < mFullAnonRssThrottleKb) {
1443                     if (DEBUG_COMPACTION) {
1444                         Slog.d(TAG_AM,
1445                                 "Skipping full compaction for process " + name
1446                                         + "; anon RSS is too small: " + anonRssBefore + "KB.");
1447                     }
1448                     return true;
1449                 }
1450 
1451                 if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) {
1452                     long[] lastRss = lastCompactionStats.getRssAfterCompaction();
1453                     long absDelta = Math.abs(rssBefore[RSS_FILE_INDEX] - lastRss[RSS_FILE_INDEX])
1454                             + Math.abs(rssBefore[RSS_ANON_INDEX] - lastRss[RSS_ANON_INDEX])
1455                             + Math.abs(rssBefore[RSS_SWAP_INDEX] - lastRss[RSS_SWAP_INDEX]);
1456                     if (absDelta <= mFullDeltaRssThrottleKb) {
1457                         if (DEBUG_COMPACTION) {
1458                             Slog.d(TAG_AM,
1459                                     "Skipping full compaction for process " + name
1460                                             + "; abs delta is too small: " + absDelta + "KB.");
1461                         }
1462                         return true;
1463                     }
1464                 }
1465             }
1466 
1467             return false;
1468         }
1469 
1470         @Override
handleMessage(Message msg)1471         public void handleMessage(Message msg) {
1472             switch (msg.what) {
1473                 case COMPACT_PROCESS_MSG: {
1474                     long start = SystemClock.uptimeMillis();
1475                     ProcessRecord proc;
1476                     final ProcessCachedOptimizerRecord opt;
1477                     int pid;
1478                     String action;
1479                     final String name;
1480                     int requestedAction, lastCompactAction;
1481                     long lastCompactTime;
1482                     int lastOomAdj = msg.arg1;
1483                     int procState = msg.arg2;
1484                     boolean forceCompaction;
1485                     synchronized (mProcLock) {
1486                         if (mPendingCompactionProcesses.isEmpty()) {
1487                             if (DEBUG_COMPACTION) {
1488                                 Slog.d(TAG_AM, "No processes pending compaction, bail out");
1489                             }
1490                             return;
1491                         }
1492                         proc = mPendingCompactionProcesses.remove(0);
1493                         opt = proc.mOptRecord;
1494                         forceCompaction = opt.isForceCompact();
1495                         opt.setForceCompact(false); // since this is a one-shot operation
1496 
1497                         requestedAction = opt.getReqCompactAction();
1498                         pid = proc.getPid();
1499                         name = proc.processName;
1500                         opt.setHasPendingCompact(false);
1501                         lastCompactAction = opt.getLastCompactAction();
1502                         lastCompactTime = opt.getLastCompactTime();
1503                     }
1504 
1505                     ++mProcCompactionsRequested;
1506                     long[] rssBefore;
1507                     if (pid == 0) {
1508                         // not a real process, either one being launched or one being killed
1509                         if (DEBUG_COMPACTION) {
1510                             Slog.d(TAG_AM, "Compaction failed, pid is 0");
1511                         }
1512                         ++mProcCompactionsNoPidThrottled;
1513                         return;
1514                     }
1515 
1516                     if (!forceCompaction) {
1517                         if (shouldOomAdjThrottleCompaction(proc, requestedAction)) {
1518                             ++mProcCompactionsOomAdjThrottled;
1519                             return;
1520                         }
1521                         if (shouldTimeThrottleCompaction(proc, start, requestedAction)) {
1522                             ++mProcCompactionsTimeThrottled;
1523                             return;
1524                         }
1525                         if (shouldThrottleMiscCompaction(proc, procState, requestedAction)) {
1526                             ++mProcCompactionsMiscThrottled;
1527                             return;
1528                         }
1529                         rssBefore = mProcessDependencies.getRss(pid);
1530                         if (shouldRssThrottleCompaction(requestedAction, pid, name, rssBefore)) {
1531                             ++mProcCompactionsRSSThrottled;
1532                             return;
1533                         }
1534                     } else {
1535                         rssBefore = mProcessDependencies.getRss(pid);
1536                         if (DEBUG_COMPACTION) {
1537                             Slog.d(TAG_AM, "Forcing compaction for " + name);
1538                         }
1539                     }
1540 
1541                     // Now we've passed through all the throttles and are going to compact, update
1542                     // bookkeeping.
1543                     switch (requestedAction) {
1544                         case COMPACT_PROCESS_SOME:
1545                             mSomeCompactionCount++;
1546                             break;
1547                         case COMPACT_PROCESS_FULL:
1548                             mFullCompactionCount++;
1549                             break;
1550                         case COMPACT_PROCESS_PERSISTENT:
1551                             mPersistentCompactionCount++;
1552                             break;
1553                         case COMPACT_PROCESS_BFGS:
1554                             mBfgsCompactionCount++;
1555                             break;
1556                         default:
1557                             break;
1558                     }
1559 
1560                     int resolvedAction = resolveCompactionAction(requestedAction);
1561                     if (resolvedAction == COMPACT_ACTION_NONE) {
1562                         return;
1563                     }
1564                     action = compactActionIntToString(resolvedAction);
1565 
1566                     try {
1567                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
1568                                 "Compact " + action + ": " + name);
1569                         ++mProcCompactionsPerformed;
1570                         long zramFreeKbBefore = Debug.getZramFreeKb();
1571                         mProcessDependencies.performCompaction(action, pid);
1572                         long[] rssAfter = mProcessDependencies.getRss(pid);
1573                         long end = SystemClock.uptimeMillis();
1574                         long time = end - start;
1575                         long zramFreeKbAfter = Debug.getZramFreeKb();
1576                         long deltaTotalRss = rssAfter[RSS_TOTAL_INDEX] - rssBefore[RSS_TOTAL_INDEX];
1577                         long deltaFileRss = rssAfter[RSS_FILE_INDEX] - rssBefore[RSS_FILE_INDEX];
1578                         long deltaAnonRss = rssAfter[RSS_ANON_INDEX] - rssBefore[RSS_ANON_INDEX];
1579                         long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX];
1580                         EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
1581                                 rssBefore[RSS_TOTAL_INDEX], rssBefore[RSS_FILE_INDEX],
1582                                 rssBefore[RSS_ANON_INDEX], rssBefore[RSS_SWAP_INDEX], deltaTotalRss,
1583                                 deltaFileRss, deltaAnonRss, deltaSwapRss, time, lastCompactAction,
1584                                 lastCompactTime, lastOomAdj, procState, zramFreeKbBefore,
1585                                 zramFreeKbAfter - zramFreeKbBefore);
1586                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
1587                         // on every single compaction for a flag that will seldom change and the
1588                         // impact of reading the wrong value here is low.
1589                         if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
1590                             FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
1591                                     requestedAction, rssBefore[RSS_TOTAL_INDEX],
1592                                     rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
1593                                     rssBefore[RSS_SWAP_INDEX], rssAfter[RSS_TOTAL_INDEX],
1594                                     rssAfter[RSS_FILE_INDEX], rssAfter[RSS_ANON_INDEX],
1595                                     rssAfter[RSS_SWAP_INDEX], time, lastCompactAction,
1596                                     lastCompactTime, lastOomAdj,
1597                                     ActivityManager.processStateAmToProto(procState),
1598                                     zramFreeKbBefore, zramFreeKbAfter);
1599                         }
1600                         synchronized (mProcLock) {
1601                             opt.setLastCompactTime(end);
1602                             opt.setLastCompactAction(resolvedAction);
1603                         }
1604                         if (resolvedAction == COMPACT_ACTION_FULL
1605                                 || resolvedAction == COMPACT_ACTION_ANON) {
1606                             // Remove entry and insert again to update insertion order.
1607                             mLastCompactionStats.remove(pid);
1608                             mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
1609                         }
1610                     } catch (Exception e) {
1611                         // nothing to do, presumably the process died
1612                         Slog.d(TAG_AM,
1613                                 "Exception occurred while compacting pid: " + name
1614                                         + ". Exception:" + e.getMessage());
1615                     } finally {
1616                         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1617                     }
1618                     break;
1619                 }
1620                 case COMPACT_SYSTEM_MSG: {
1621                     ++mSystemCompactionsPerformed;
1622                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
1623                     compactSystem();
1624                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1625                     break;
1626                 }
1627             }
1628         }
1629     }
1630 
1631     private final class FreezeHandler extends Handler implements
1632             ProcLocksReader.ProcLocksReaderCallback {
FreezeHandler()1633         private FreezeHandler() {
1634             super(mCachedAppOptimizerThread.getLooper());
1635         }
1636 
1637         @Override
handleMessage(Message msg)1638         public void handleMessage(Message msg) {
1639             switch (msg.what) {
1640                 case SET_FROZEN_PROCESS_MSG:
1641                     synchronized (mAm) {
1642                         freezeProcess((ProcessRecord) msg.obj);
1643                     }
1644                     break;
1645                 case REPORT_UNFREEZE_MSG:
1646                     int pid = msg.arg1;
1647                     int frozenDuration = msg.arg2;
1648                     Pair<String, String> obj = (Pair<String, String>) msg.obj;
1649                     String processName = obj.first;
1650                     String reason = obj.second;
1651 
1652                     reportUnfreeze(pid, frozenDuration, processName, reason);
1653                     break;
1654                 default:
1655                     return;
1656             }
1657         }
1658 
1659         @GuardedBy({"mAm", "mProcLock"})
rescheduleFreeze(final ProcessRecord proc, final String reason)1660         private void rescheduleFreeze(final ProcessRecord proc, final String reason) {
1661             Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid()
1662                     + " " + proc.processName + " (" + reason + ")");
1663             unfreezeAppLSP(proc, OomAdjuster.OOM_ADJ_REASON_NONE);
1664             freezeAppAsyncLSP(proc);
1665         }
1666 
1667         @GuardedBy({"mAm"})
freezeProcess(final ProcessRecord proc)1668         private void freezeProcess(final ProcessRecord proc) {
1669             int pid = proc.getPid(); // Unlocked intentionally
1670             final String name = proc.processName;
1671             final long unfrozenDuration;
1672             final boolean frozen;
1673             final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
1674 
1675             opt.setPendingFreeze(false);
1676 
1677             synchronized (mProcLock) {
1678                 pid = proc.getPid();
1679                 if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
1680                         || opt.shouldNotFreeze()) {
1681                     if (DEBUG_FREEZER) {
1682                         Slog.d(TAG_AM, "Skipping freeze for process " + pid
1683                                 + " " + name + " curAdj = " + proc.mState.getCurAdj()
1684                                 + ", shouldNotFreeze = " + opt.shouldNotFreeze());
1685                     }
1686                     return;
1687                 }
1688 
1689                 if (mFreezerOverride) {
1690                     opt.setFreezerOverride(true);
1691                     Slog.d(TAG_AM, "Skipping freeze for process " + pid
1692                             + " " + name + " curAdj = " + proc.mState.getCurAdj()
1693                             + "(override)");
1694                     return;
1695                 }
1696 
1697                 if (pid == 0 || opt.isFrozen()) {
1698                     // Already frozen or not a real process, either one being
1699                     // launched or one being killed
1700                     return;
1701                 }
1702 
1703                 Slog.d(TAG_AM, "freezing " + pid + " " + name);
1704 
1705                 // Freeze binder interface before the process, to flush any
1706                 // transactions that might be pending.
1707                 try {
1708                     if (freezeBinder(pid, true) != 0) {
1709                         rescheduleFreeze(proc, "outstanding txns");
1710                         return;
1711                     }
1712                 } catch (RuntimeException e) {
1713                     Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
1714                     mFreezeHandler.post(() -> {
1715                         synchronized (mAm) {
1716                             proc.killLocked("Unable to freeze binder interface",
1717                                     ApplicationExitInfo.REASON_FREEZER,
1718                                     ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
1719                         }
1720                     });
1721                 }
1722 
1723                 long unfreezeTime = opt.getFreezeUnfreezeTime();
1724 
1725                 try {
1726                     Process.setProcessFrozen(pid, proc.uid, true);
1727 
1728                     opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
1729                     opt.setFrozen(true);
1730                     mFrozenProcesses.put(pid, proc);
1731                 } catch (Exception e) {
1732                     Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
1733                 }
1734 
1735                 unfrozenDuration = opt.getFreezeUnfreezeTime() - unfreezeTime;
1736                 frozen = opt.isFrozen();
1737             }
1738 
1739             if (!frozen) {
1740                 return;
1741             }
1742 
1743             EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
1744 
1745             // See above for why we're not taking mPhenotypeFlagLock here
1746             if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
1747                 FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
1748                         FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
1749                         pid,
1750                         name,
1751                         unfrozenDuration,
1752                         FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__NONE);
1753             }
1754 
1755             try {
1756                 // post-check to prevent races
1757                 int freezeInfo = getBinderFreezeInfo(pid);
1758 
1759                 if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
1760                     synchronized (mProcLock) {
1761                         rescheduleFreeze(proc, "new pending txns");
1762                     }
1763                     return;
1764                 }
1765             } catch (RuntimeException e) {
1766                 Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
1767                 mFreezeHandler.post(() -> {
1768                     synchronized (mAm) {
1769                         proc.killLocked("Unable to freeze binder interface",
1770                                 ApplicationExitInfo.REASON_FREEZER,
1771                                 ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
1772                     }
1773                 });
1774             }
1775 
1776             try {
1777                 // post-check to prevent deadlock
1778                 mProcLocksReader.handleBlockingFileLocks(this);
1779             } catch (Exception e) {
1780                 Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
1781                 synchronized (mProcLock) {
1782                     unfreezeAppLSP(proc, OomAdjuster.OOM_ADJ_REASON_NONE);
1783                 }
1784             }
1785         }
1786 
reportUnfreeze(int pid, int frozenDuration, String processName, String reason)1787         private void reportUnfreeze(int pid, int frozenDuration, String processName,
1788                 String reason) {
1789 
1790             EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName);
1791 
1792             // See above for why we're not taking mPhenotypeFlagLock here
1793             if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
1794                 FrameworkStatsLog.write(
1795                         FrameworkStatsLog.APP_FREEZE_CHANGED,
1796                         FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP,
1797                         pid,
1798                         processName,
1799                         frozenDuration,
1800                         getUnfreezeReasonCode(reason));
1801             }
1802         }
1803 
getUnfreezeReasonCode(String oomAdjReason)1804         private int getUnfreezeReasonCode(String oomAdjReason) {
1805             switch (oomAdjReason) {
1806                 case OomAdjuster.OOM_ADJ_REASON_ACTIVITY:
1807                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__ACTIVITY;
1808                 case OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER:
1809                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__FINISH_RECEIVER;
1810                 case OomAdjuster.OOM_ADJ_REASON_START_RECEIVER:
1811                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__START_RECEIVER;
1812                 case OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE:
1813                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__BIND_SERVICE;
1814                 case OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE:
1815                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__UNBIND_SERVICE;
1816                 case OomAdjuster.OOM_ADJ_REASON_START_SERVICE:
1817                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__START_SERVICE;
1818                 case OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER:
1819                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__GET_PROVIDER;
1820                 case OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER:
1821                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__REMOVE_PROVIDER;
1822                 case OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY:
1823                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__UI_VISIBILITY;
1824                 case OomAdjuster.OOM_ADJ_REASON_ALLOWLIST:
1825                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__ALLOWLIST;
1826                 case OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN:
1827                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__PROCESS_BEGIN;
1828                 case OomAdjuster.OOM_ADJ_REASON_PROCESS_END:
1829                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__PROCESS_END;
1830                 default:
1831                     return FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON__NONE;
1832             }
1833         }
1834 
1835         @GuardedBy({"mAm"})
1836         @Override
onBlockingFileLock(int pid)1837         public void onBlockingFileLock(int pid) {
1838             if (DEBUG_FREEZER) {
1839                 Slog.d(TAG_AM, "Process (pid=" + pid + ") holds blocking file lock");
1840             }
1841             synchronized (mProcLock) {
1842                 ProcessRecord app = mFrozenProcesses.get(pid);
1843                 if (app != null) {
1844                     Slog.i(TAG_AM, app.processName + " (" + pid + ") holds blocking file lock");
1845                     unfreezeAppLSP(app, OomAdjuster.OOM_ADJ_REASON_NONE);
1846                 }
1847             }
1848         }
1849     }
1850 
1851     /**
1852      * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
1853      */
1854     private static final class DefaultProcessDependencies implements ProcessDependencies {
1855         public static volatile int mPidCompacting = -1;
1856 
1857         // Get memory RSS from process.
1858         @Override
getRss(int pid)1859         public long[] getRss(int pid) {
1860             return Process.getRss(pid);
1861         }
1862 
1863         // Compact process.
1864         @Override
performCompaction(String action, int pid)1865         public void performCompaction(String action, int pid) throws IOException {
1866             mPidCompacting = pid;
1867             if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
1868                 compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
1869             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
1870                 compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
1871             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
1872                 compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
1873             }
1874             mPidCompacting = -1;
1875         }
1876     }
1877 }
1878