Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.am;
     18 
     19 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
     20 
     21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION;
     22 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
     23 
     24 import android.app.ActivityManager;
     25 import android.app.ActivityThread;
     26 import android.os.Debug;
     27 import android.os.Handler;
     28 import android.os.Message;
     29 import android.os.Process;
     30 import android.os.SystemClock;
     31 import android.os.Trace;
     32 import android.provider.DeviceConfig;
     33 import android.provider.DeviceConfig.OnPropertiesChangedListener;
     34 import android.provider.DeviceConfig.Properties;
     35 import android.text.TextUtils;
     36 import android.util.EventLog;
     37 import android.util.Slog;
     38 import android.util.StatsLog;
     39 
     40 import com.android.internal.annotations.GuardedBy;
     41 import com.android.internal.annotations.VisibleForTesting;
     42 import com.android.server.ServiceThread;
     43 
     44 import java.io.FileOutputStream;
     45 import java.io.PrintWriter;
     46 import java.util.ArrayList;
     47 import java.util.Arrays;
     48 import java.util.HashSet;
     49 import java.util.LinkedHashMap;
     50 import java.util.Map;
     51 import java.util.Random;
     52 import java.util.Set;
     53 
     54 public final class AppCompactor {
     55 
     56     // Flags stored in the DeviceConfig API.
     57     @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
     58     @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
     59     @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
     60     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
     61     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
     62     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
     63     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
     64     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5";
     65     @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
     66     @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
     67             "compact_statsd_sample_rate";
     68     @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB =
     69             "compact_full_rss_throttle_kb";
     70     @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB =
     71             "compact_full_delta_rss_throttle_kb";
     72     @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE =
     73             "compact_proc_state_throttle";
     74 
     75     // Phenotype sends int configurations and we map them to the strings we'll use on device,
     76     // preventing a weird string value entering the kernel.
     77     private static final int COMPACT_ACTION_FILE_FLAG = 1;
     78     private static final int COMPACT_ACTION_ANON_FLAG = 2;
     79     private static final int COMPACT_ACTION_FULL_FLAG = 3;
     80     private static final int COMPACT_ACTION_NONE_FLAG = 4;
     81     private static final String COMPACT_ACTION_NONE = "";
     82     private static final String COMPACT_ACTION_FILE = "file";
     83     private static final String COMPACT_ACTION_ANON = "anon";
     84     private static final String COMPACT_ACTION_FULL = "all";
     85 
     86     // Defaults for phenotype flags.
     87     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
     88     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
     89     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
     90     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
     91     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
     92     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
     93     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
     94     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000;
     95     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000;
     96     // The sampling rate to push app compaction events into statsd for upload.
     97     @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
     98     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L;
     99     @VisibleForTesting static final long DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 8_000L;
    100     // Format of this string should be a comma separated list of integers.
    101     @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
    102             String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
    103 
    104     @VisibleForTesting
    105     interface PropertyChangedCallbackForTest {
    106         void onPropertyChanged();
    107     }
    108     private PropertyChangedCallbackForTest mTestCallback;
    109 
    110     // Handler constants.
    111     static final int COMPACT_PROCESS_SOME = 1;
    112     static final int COMPACT_PROCESS_FULL = 2;
    113     static final int COMPACT_PROCESS_PERSISTENT = 3;
    114     static final int COMPACT_PROCESS_BFGS = 4;
    115     static final int COMPACT_PROCESS_MSG = 1;
    116     static final int COMPACT_SYSTEM_MSG = 2;
    117 
    118     /**
    119      * This thread must be moved to the system background cpuset.
    120      * If that doesn't happen, it's probably going to draw a lot of power.
    121      * However, this has to happen after the first updateOomAdjLocked, because
    122      * that will wipe out the cpuset assignment for system_server threads.
    123      * Accordingly, this is in the AMS constructor.
    124      */
    125     final ServiceThread mCompactionThread;
    126 
    127     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
    128             new ArrayList<ProcessRecord>();
    129     private final ActivityManagerService mAm;
    130     private final OnPropertiesChangedListener mOnFlagsChangedListener =
    131             new OnPropertiesChangedListener() {
    132                 @Override
    133                 public void onPropertiesChanged(Properties properties) {
    134                     synchronized (mPhenotypeFlagLock) {
    135                         for (String name : properties.getKeyset()) {
    136                             if (KEY_USE_COMPACTION.equals(name)) {
    137                                 updateUseCompaction();
    138                             } else if (KEY_COMPACT_ACTION_1.equals(name)
    139                                     || KEY_COMPACT_ACTION_2.equals(name)) {
    140                                 updateCompactionActions();
    141                             } else if (KEY_COMPACT_THROTTLE_1.equals(name)
    142                                     || KEY_COMPACT_THROTTLE_2.equals(name)
    143                                     || KEY_COMPACT_THROTTLE_3.equals(name)
    144                                     || KEY_COMPACT_THROTTLE_4.equals(name)) {
    145                                 updateCompactionThrottles();
    146                             } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
    147                                 updateStatsdSampleRate();
    148                             } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
    149                                 updateFullRssThrottle();
    150                             } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
    151                                 updateFullDeltaRssThrottle();
    152                             } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
    153                                 updateProcStateThrottle();
    154                             }
    155                         }
    156                     }
    157                     if (mTestCallback != null) {
    158                         mTestCallback.onPropertyChanged();
    159                     }
    160                 }
    161             };
    162 
    163     private final Object mPhenotypeFlagLock = new Object();
    164 
    165     // Configured by phenotype. Updates from the server take effect immediately.
    166     @GuardedBy("mPhenotypeFlagLock")
    167     @VisibleForTesting volatile String mCompactActionSome =
    168             compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
    169     @GuardedBy("mPhenotypeFlagLock")
    170     @VisibleForTesting volatile String mCompactActionFull =
    171             compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
    172     @GuardedBy("mPhenotypeFlagLock")
    173     @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
    174     @GuardedBy("mPhenotypeFlagLock")
    175     @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
    176     @GuardedBy("mPhenotypeFlagLock")
    177     @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
    178     @GuardedBy("mPhenotypeFlagLock")
    179     @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
    180     @GuardedBy("mPhenotypeFlagLock")
    181     @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
    182     @GuardedBy("mPhenotypeFlagLock")
    183     @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
    184     @GuardedBy("mPhenotypeFlagLock")
    185     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
    186     private final Random mRandom = new Random();
    187     @GuardedBy("mPhenotypeFlagLock")
    188     @VisibleForTesting volatile float mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
    189     @GuardedBy("mPhenotypeFlagLock")
    190     @VisibleForTesting volatile long mFullAnonRssThrottleKb =
    191             DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
    192     @GuardedBy("mPhenoypeFlagLock")
    193     @VisibleForTesting volatile long mFullDeltaRssThrottleKb =
    194             DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
    195     @GuardedBy("mPhenoypeFlagLock")
    196     @VisibleForTesting final Set<Integer> mProcStateThrottle;
    197 
    198     // Handler on which compaction runs.
    199     private Handler mCompactionHandler;
    200 
    201     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
    202     // when evaluating throttles that we only consider for "full" compaction, so we don't store
    203     // data for "some" compactions.
    204     private Map<Integer, LastCompactionStats> mLastCompactionStats =
    205             new LinkedHashMap<Integer, LastCompactionStats>() {
    206                 @Override
    207                 protected boolean removeEldestEntry(Map.Entry eldest) {
    208                     return size() > 100;
    209                 }
    210     };
    211 
    212     private int mSomeCompactionCount;
    213     private int mFullCompactionCount;
    214     private int mPersistentCompactionCount;
    215     private int mBfgsCompactionCount;
    216 
    217     public AppCompactor(ActivityManagerService am) {
    218         mAm = am;
    219         mCompactionThread = new ServiceThread("CompactionThread",
    220                 THREAD_PRIORITY_FOREGROUND, true);
    221         mProcStateThrottle = new HashSet<>();
    222     }
    223 
    224     @VisibleForTesting
    225     AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
    226         this(am);
    227         mTestCallback = callback;
    228     }
    229 
    230     /**
    231      * Reads phenotype config to determine whether app compaction is enabled or not and
    232      * starts the background thread if necessary.
    233      */
    234     public void init() {
    235         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    236                 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
    237         synchronized (mPhenotypeFlagLock) {
    238             updateUseCompaction();
    239             updateCompactionActions();
    240             updateCompactionThrottles();
    241             updateStatsdSampleRate();
    242             updateFullRssThrottle();
    243             updateFullDeltaRssThrottle();
    244             updateProcStateThrottle();
    245         }
    246         Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(),
    247                 Process.THREAD_GROUP_SYSTEM);
    248     }
    249 
    250     /**
    251      * Returns whether compaction is enabled.
    252      */
    253     public boolean useCompaction() {
    254         synchronized (mPhenotypeFlagLock) {
    255             return mUseCompaction;
    256         }
    257     }
    258 
    259     @GuardedBy("mAm")
    260     void dump(PrintWriter pw) {
    261         pw.println("AppCompactor settings");
    262         synchronized (mPhenotypeFlagLock) {
    263             pw.println("  " + KEY_USE_COMPACTION + "=" + mUseCompaction);
    264             pw.println("  " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
    265             pw.println("  " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull);
    266             pw.println("  " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome);
    267             pw.println("  " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
    268             pw.println("  " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
    269             pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
    270             pw.println("  " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
    271             pw.println("  " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
    272             pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mStatsdSampleRate);
    273             pw.println("  " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "="
    274                     + mFullAnonRssThrottleKb);
    275             pw.println("  " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "="
    276                     + mFullDeltaRssThrottleKb);
    277             pw.println("  "  + KEY_COMPACT_PROC_STATE_THROTTLE + "="
    278                     + Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
    279 
    280             pw.println("  " + mSomeCompactionCount + " some, " + mFullCompactionCount
    281                     + " full, " + mPersistentCompactionCount + " persistent, "
    282                     + mBfgsCompactionCount + " BFGS compactions.");
    283 
    284             pw.println("  Tracking last compaction stats for " + mLastCompactionStats.size()
    285                     + " processes.");
    286             if (DEBUG_COMPACTION) {
    287                 for (Map.Entry<Integer, LastCompactionStats> entry
    288                         : mLastCompactionStats.entrySet()) {
    289                     int pid = entry.getKey();
    290                     LastCompactionStats stats = entry.getValue();
    291                     pw.println("    " + pid + ": "
    292                             + Arrays.toString(stats.getRssAfterCompaction()));
    293                 }
    294             }
    295         }
    296     }
    297 
    298     @GuardedBy("mAm")
    299     void compactAppSome(ProcessRecord app) {
    300         app.reqCompactAction = COMPACT_PROCESS_SOME;
    301         mPendingCompactionProcesses.add(app);
    302         mCompactionHandler.sendMessage(
    303             mCompactionHandler.obtainMessage(
    304                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
    305     }
    306 
    307     @GuardedBy("mAm")
    308     void compactAppFull(ProcessRecord app) {
    309         app.reqCompactAction = COMPACT_PROCESS_FULL;
    310         mPendingCompactionProcesses.add(app);
    311         mCompactionHandler.sendMessage(
    312             mCompactionHandler.obtainMessage(
    313                 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
    314 
    315     }
    316 
    317     @GuardedBy("mAm")
    318     void compactAppPersistent(ProcessRecord app) {
    319         app.reqCompactAction = COMPACT_PROCESS_PERSISTENT;
    320         mPendingCompactionProcesses.add(app);
    321         mCompactionHandler.sendMessage(
    322                 mCompactionHandler.obtainMessage(
    323                     COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
    324     }
    325 
    326     @GuardedBy("mAm")
    327     boolean shouldCompactPersistent(ProcessRecord app, long now) {
    328         return (app.lastCompactTime == 0
    329                 || (now - app.lastCompactTime) > mCompactThrottlePersistent);
    330     }
    331 
    332     @GuardedBy("mAm")
    333     void compactAppBfgs(ProcessRecord app) {
    334         app.reqCompactAction = COMPACT_PROCESS_BFGS;
    335         mPendingCompactionProcesses.add(app);
    336         mCompactionHandler.sendMessage(
    337                 mCompactionHandler.obtainMessage(
    338                     COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
    339     }
    340 
    341     @GuardedBy("mAm")
    342     boolean shouldCompactBFGS(ProcessRecord app, long now) {
    343         return (app.lastCompactTime == 0
    344                 || (now - app.lastCompactTime) > mCompactThrottleBFGS);
    345     }
    346 
    347     @GuardedBy("mAm")
    348     void compactAllSystem() {
    349         if (mUseCompaction) {
    350             mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
    351                                               COMPACT_SYSTEM_MSG));
    352         }
    353     }
    354 
    355     private native void compactSystem();
    356 
    357     /**
    358      * Reads the flag value from DeviceConfig to determine whether app compaction
    359      * should be enabled, and starts the compaction thread if needed.
    360      */
    361     @GuardedBy("mPhenotypeFlagLock")
    362     private void updateUseCompaction() {
    363         mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    364                     KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
    365         if (mUseCompaction && !mCompactionThread.isAlive()) {
    366             mCompactionThread.start();
    367             mCompactionHandler = new MemCompactionHandler();
    368         }
    369     }
    370 
    371     @GuardedBy("mPhenotypeFlagLock")
    372     private void updateCompactionActions() {
    373         int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    374                 KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
    375 
    376         int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    377                 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
    378 
    379         mCompactActionSome = compactActionIntToString(compactAction1);
    380         mCompactActionFull = compactActionIntToString(compactAction2);
    381     }
    382 
    383     @GuardedBy("mPhenotypeFlagLock")
    384     private void updateCompactionThrottles() {
    385         boolean useThrottleDefaults = false;
    386         String throttleSomeSomeFlag =
    387                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    388                     KEY_COMPACT_THROTTLE_1);
    389         String throttleSomeFullFlag =
    390                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    391                     KEY_COMPACT_THROTTLE_2);
    392         String throttleFullSomeFlag =
    393                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    394                     KEY_COMPACT_THROTTLE_3);
    395         String throttleFullFullFlag =
    396                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    397                     KEY_COMPACT_THROTTLE_4);
    398         String throttleBFGSFlag =
    399                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    400                     KEY_COMPACT_THROTTLE_5);
    401         String throttlePersistentFlag =
    402                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    403                     KEY_COMPACT_THROTTLE_6);
    404 
    405         if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
    406                 || TextUtils.isEmpty(throttleFullSomeFlag)
    407                 || TextUtils.isEmpty(throttleFullFullFlag)
    408                 || TextUtils.isEmpty(throttleBFGSFlag)
    409                 || TextUtils.isEmpty(throttlePersistentFlag)) {
    410             // Set defaults for all if any are not set.
    411             useThrottleDefaults = true;
    412         } else {
    413             try {
    414                 mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
    415                 mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
    416                 mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
    417                 mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
    418                 mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
    419                 mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
    420             } catch (NumberFormatException e) {
    421                 useThrottleDefaults = true;
    422             }
    423         }
    424 
    425         if (useThrottleDefaults) {
    426             mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
    427             mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
    428             mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
    429             mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
    430             mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
    431             mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
    432         }
    433     }
    434 
    435     @GuardedBy("mPhenotypeFlagLock")
    436     private void updateStatsdSampleRate() {
    437         mStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    438                 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
    439         mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate));
    440     }
    441 
    442     @GuardedBy("mPhenotypeFlagLock")
    443     private void updateFullRssThrottle() {
    444         mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    445                 KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
    446 
    447         // Don't allow negative values. 0 means don't apply the throttle.
    448         if (mFullAnonRssThrottleKb < 0) {
    449             mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
    450         }
    451     }
    452 
    453     @GuardedBy("mPhenotypeFlagLock")
    454     private void updateFullDeltaRssThrottle() {
    455         mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
    456                 KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
    457 
    458         if (mFullDeltaRssThrottleKb < 0) {
    459             mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
    460         }
    461     }
    462 
    463     @GuardedBy("mPhenotypeFlagLock")
    464     private void updateProcStateThrottle() {
    465         String procStateThrottleString = DeviceConfig.getString(
    466                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,
    467                 DEFAULT_COMPACT_PROC_STATE_THROTTLE);
    468         if (!parseProcStateThrottle(procStateThrottleString)) {
    469             Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""
    470                     + procStateThrottleString + "\" falling back to default.");
    471             if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {
    472                 Slog.wtf(TAG_AM,
    473                         "Unable to parse default app compact proc state throttle "
    474                                 + DEFAULT_COMPACT_PROC_STATE_THROTTLE);
    475             }
    476         }
    477     }
    478 
    479     private boolean parseProcStateThrottle(String procStateThrottleString) {
    480         String[] procStates = TextUtils.split(procStateThrottleString, ",");
    481         mProcStateThrottle.clear();
    482         for (String procState : procStates) {
    483             try {
    484                 mProcStateThrottle.add(Integer.parseInt(procState));
    485             } catch (NumberFormatException e) {
    486                 Slog.e(TAG_AM, "Failed to parse default app compaction proc state: "
    487                         + procState);
    488                 return false;
    489             }
    490         }
    491         return true;
    492     }
    493 
    494     @VisibleForTesting
    495     static String compactActionIntToString(int action) {
    496         switch(action) {
    497             case COMPACT_ACTION_NONE_FLAG:
    498                 return COMPACT_ACTION_NONE;
    499             case COMPACT_ACTION_FILE_FLAG:
    500                 return COMPACT_ACTION_FILE;
    501             case COMPACT_ACTION_ANON_FLAG:
    502                 return COMPACT_ACTION_ANON;
    503             case COMPACT_ACTION_FULL_FLAG:
    504                 return COMPACT_ACTION_FULL;
    505             default:
    506                 return COMPACT_ACTION_NONE;
    507         }
    508     }
    509 
    510     private static final class LastCompactionStats {
    511         private final long[] mRssAfterCompaction;
    512 
    513         LastCompactionStats(long[] rss) {
    514             mRssAfterCompaction = rss;
    515         }
    516 
    517         long[] getRssAfterCompaction() {
    518             return mRssAfterCompaction;
    519         }
    520     }
    521 
    522     private final class MemCompactionHandler extends Handler {
    523         private MemCompactionHandler() {
    524             super(mCompactionThread.getLooper());
    525         }
    526 
    527         @Override
    528         public void handleMessage(Message msg) {
    529             switch (msg.what) {
    530                 case COMPACT_PROCESS_MSG: {
    531                     long start = SystemClock.uptimeMillis();
    532                     ProcessRecord proc;
    533                     int pid;
    534                     String action;
    535                     final String name;
    536                     int pendingAction, lastCompactAction;
    537                     long lastCompactTime;
    538                     LastCompactionStats lastCompactionStats;
    539                     int lastOomAdj = msg.arg1;
    540                     int procState = msg.arg2;
    541                     synchronized (mAm) {
    542                         proc = mPendingCompactionProcesses.remove(0);
    543 
    544                         pendingAction = proc.reqCompactAction;
    545                         pid = proc.pid;
    546                         name = proc.processName;
    547 
    548                         // don't compact if the process has returned to perceptible
    549                         // and this is only a cached/home/prev compaction
    550                         if ((pendingAction == COMPACT_PROCESS_SOME
    551                                 || pendingAction == COMPACT_PROCESS_FULL)
    552                                 && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
    553                             if (DEBUG_COMPACTION) {
    554                                 Slog.d(TAG_AM,
    555                                         "Skipping compaction as process " + name + " is "
    556                                         + "now perceptible.");
    557                             }
    558                             return;
    559                         }
    560 
    561                         lastCompactAction = proc.lastCompactAction;
    562                         lastCompactTime = proc.lastCompactTime;
    563                         // remove rather than get so that insertion order will be updated when we
    564                         // put the post-compaction stats back into the map.
    565                         lastCompactionStats = mLastCompactionStats.remove(pid);
    566                     }
    567 
    568                     if (pid == 0) {
    569                         // not a real process, either one being launched or one being killed
    570                         return;
    571                     }
    572 
    573                     // basic throttling
    574                     // use the Phenotype flag knobs to determine whether current/prevous
    575                     // compaction combo should be throtted or not
    576 
    577                     // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
    578                     // should very seldom change, and taking the risk of using the wrong action is
    579                     // preferable to taking the lock for every single compaction action.
    580                     if (lastCompactTime != 0) {
    581                         if (pendingAction == COMPACT_PROCESS_SOME) {
    582                             if ((lastCompactAction == COMPACT_PROCESS_SOME
    583                                     && (start - lastCompactTime < mCompactThrottleSomeSome))
    584                                     || (lastCompactAction == COMPACT_PROCESS_FULL
    585                                         && (start - lastCompactTime
    586                                                 < mCompactThrottleSomeFull))) {
    587                                 if (DEBUG_COMPACTION) {
    588                                     Slog.d(TAG_AM, "Skipping some compaction for " + name
    589                                             + ": too soon. throttle=" + mCompactThrottleSomeSome
    590                                             + "/" + mCompactThrottleSomeFull + " last="
    591                                             + (start - lastCompactTime) + "ms ago");
    592                                 }
    593                                 return;
    594                             }
    595                         } else if (pendingAction == COMPACT_PROCESS_FULL) {
    596                             if ((lastCompactAction == COMPACT_PROCESS_SOME
    597                                     && (start - lastCompactTime < mCompactThrottleFullSome))
    598                                     || (lastCompactAction == COMPACT_PROCESS_FULL
    599                                         && (start - lastCompactTime
    600                                                 < mCompactThrottleFullFull))) {
    601                                 if (DEBUG_COMPACTION) {
    602                                     Slog.d(TAG_AM, "Skipping full compaction for " + name
    603                                             + ": too soon. throttle=" + mCompactThrottleFullSome
    604                                             + "/" + mCompactThrottleFullFull + " last="
    605                                             + (start - lastCompactTime) + "ms ago");
    606                                 }
    607                                 return;
    608                             }
    609                         } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
    610                             if (start - lastCompactTime < mCompactThrottlePersistent) {
    611                                 if (DEBUG_COMPACTION) {
    612                                     Slog.d(TAG_AM, "Skipping persistent compaction for " + name
    613                                             + ": too soon. throttle=" + mCompactThrottlePersistent
    614                                             + " last=" + (start - lastCompactTime) + "ms ago");
    615                                 }
    616                                 return;
    617                             }
    618                         } else if (pendingAction == COMPACT_PROCESS_BFGS) {
    619                             if (start - lastCompactTime < mCompactThrottleBFGS) {
    620                                 if (DEBUG_COMPACTION) {
    621                                     Slog.d(TAG_AM, "Skipping bfgs compaction for " + name
    622                                             + ": too soon. throttle=" + mCompactThrottleBFGS
    623                                             + " last=" + (start - lastCompactTime) + "ms ago");
    624                                 }
    625                                 return;
    626                             }
    627                         }
    628                     }
    629 
    630                     switch (pendingAction) {
    631                         case COMPACT_PROCESS_SOME:
    632                             action = mCompactActionSome;
    633                             break;
    634                         // For the time being, treat these as equivalent.
    635                         case COMPACT_PROCESS_FULL:
    636                         case COMPACT_PROCESS_PERSISTENT:
    637                         case COMPACT_PROCESS_BFGS:
    638                             action = mCompactActionFull;
    639                             break;
    640                         default:
    641                             action = COMPACT_ACTION_NONE;
    642                             break;
    643                     }
    644 
    645                     if (COMPACT_ACTION_NONE.equals(action)) {
    646                         return;
    647                     }
    648 
    649                     if (mProcStateThrottle.contains(procState)) {
    650                         if (DEBUG_COMPACTION) {
    651                             Slog.d(TAG_AM, "Skipping full compaction for process " + name
    652                                     + "; proc state is " + procState);
    653                         }
    654                         return;
    655                     }
    656 
    657                     long[] rssBefore = Process.getRss(pid);
    658                     long anonRssBefore = rssBefore[2];
    659 
    660                     if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0
    661                             && rssBefore[3] == 0) {
    662                         if (DEBUG_COMPACTION) {
    663                             Slog.d(TAG_AM, "Skipping compaction for" + "process " + pid
    664                                     + " with no memory usage. Dead?");
    665                         }
    666                         return;
    667                     }
    668 
    669                     if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) {
    670                         if (mFullAnonRssThrottleKb > 0L
    671                                 && anonRssBefore < mFullAnonRssThrottleKb) {
    672                             if (DEBUG_COMPACTION) {
    673                                 Slog.d(TAG_AM, "Skipping full compaction for process "
    674                                         + name + "; anon RSS is too small: " + anonRssBefore
    675                                         + "KB.");
    676                             }
    677                             return;
    678                         }
    679 
    680                         if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) {
    681                             long[] lastRss = lastCompactionStats.getRssAfterCompaction();
    682                             long absDelta = Math.abs(rssBefore[1] - lastRss[1])
    683                                     + Math.abs(rssBefore[2] - lastRss[2])
    684                                     + Math.abs(rssBefore[3] - lastRss[3]);
    685                             if (absDelta <= mFullDeltaRssThrottleKb) {
    686                                 if (DEBUG_COMPACTION) {
    687                                     Slog.d(TAG_AM, "Skipping full compaction for process "
    688                                             + name + "; abs delta is too small: " + absDelta
    689                                             + "KB.");
    690                                 }
    691                                 return;
    692                             }
    693                         }
    694                     }
    695 
    696                     // Now we've passed through all the throttles and are going to compact, update
    697                     // bookkeeping.
    698                     switch (pendingAction) {
    699                         case COMPACT_PROCESS_SOME:
    700                             mSomeCompactionCount++;
    701                             break;
    702                         case COMPACT_PROCESS_FULL:
    703                             mFullCompactionCount++;
    704                             break;
    705                         case COMPACT_PROCESS_PERSISTENT:
    706                             mPersistentCompactionCount++;
    707                             break;
    708                         case COMPACT_PROCESS_BFGS:
    709                             mBfgsCompactionCount++;
    710                             break;
    711                         default:
    712                             break;
    713                     }
    714 
    715                     try {
    716                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
    717                                 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
    718                                 + ": " + name);
    719                         long zramFreeKbBefore = Debug.getZramFreeKb();
    720                         FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
    721                         fos.write(action.getBytes());
    722                         fos.close();
    723                         long[] rssAfter = Process.getRss(pid);
    724                         long end = SystemClock.uptimeMillis();
    725                         long time = end - start;
    726                         long zramFreeKbAfter = Debug.getZramFreeKb();
    727                         EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
    728                                 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
    729                                 rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1],
    730                                 rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time,
    731                                 lastCompactAction, lastCompactTime, lastOomAdj, procState,
    732                                 zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore);
    733 
    734                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
    735                         // on every single compaction for a flag that will seldom change and the
    736                         // impact of reading the wrong value here is low.
    737                         if (mRandom.nextFloat() < mStatsdSampleRate) {
    738                             StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
    739                                     rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
    740                                     rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
    741                                     lastCompactAction, lastCompactTime, lastOomAdj,
    742                                     ActivityManager.processStateAmToProto(procState),
    743                                     zramFreeKbBefore, zramFreeKbAfter);
    744                         }
    745 
    746                         synchronized (mAm) {
    747                             proc.lastCompactTime = end;
    748                             proc.lastCompactAction = pendingAction;
    749                         }
    750 
    751                         if (action.equals(COMPACT_ACTION_FULL)
    752                                 || action.equals(COMPACT_ACTION_ANON)) {
    753                             mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
    754                         }
    755                     } catch (Exception e) {
    756                         // nothing to do, presumably the process died
    757                     } finally {
    758                         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    759                     }
    760                     break;
    761                 }
    762                 case COMPACT_SYSTEM_MSG: {
    763                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
    764                     compactSystem();
    765                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    766                     break;
    767                 }
    768             }
    769         }
    770     }
    771 }
    772