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