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