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