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