1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import static android.os.Process.THREAD_PRIORITY_FOREGROUND; 20 21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION; 22 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FREEZER; 23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 24 25 import android.app.ActivityManager; 26 import android.app.ActivityThread; 27 import android.os.Debug; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.Process; 31 import android.os.SystemClock; 32 import android.os.Trace; 33 import android.provider.DeviceConfig; 34 import android.provider.DeviceConfig.OnPropertiesChangedListener; 35 import android.provider.DeviceConfig.Properties; 36 import android.provider.Settings; 37 import android.text.TextUtils; 38 import android.util.EventLog; 39 import android.util.Slog; 40 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.util.FrameworkStatsLog; 44 import com.android.server.ServiceThread; 45 46 import java.io.FileOutputStream; 47 import java.io.FileReader; 48 import java.io.IOException; 49 import java.io.PrintWriter; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.HashSet; 53 import java.util.LinkedHashMap; 54 import java.util.Map; 55 import java.util.Random; 56 import java.util.Set; 57 58 public final class CachedAppOptimizer { 59 60 // Flags stored in the DeviceConfig API. 61 @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; 62 @VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer"; 63 @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; 64 @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; 65 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; 66 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; 67 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; 68 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; 69 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5"; 70 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6"; 71 @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = 72 "compact_statsd_sample_rate"; 73 @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE = 74 "freeze_statsd_sample_rate"; 75 @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB = 76 "compact_full_rss_throttle_kb"; 77 @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 78 "compact_full_delta_rss_throttle_kb"; 79 @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE = 80 "compact_proc_state_throttle"; 81 82 // Phenotype sends int configurations and we map them to the strings we'll use on device, 83 // preventing a weird string value entering the kernel. 84 private static final int COMPACT_ACTION_FILE_FLAG = 1; 85 private static final int COMPACT_ACTION_ANON_FLAG = 2; 86 private static final int COMPACT_ACTION_FULL_FLAG = 3; 87 private static final int COMPACT_ACTION_NONE_FLAG = 4; 88 private static final String COMPACT_ACTION_NONE = ""; 89 private static final String COMPACT_ACTION_FILE = "file"; 90 private static final String COMPACT_ACTION_ANON = "anon"; 91 private static final String COMPACT_ACTION_FULL = "all"; 92 93 // Defaults for phenotype flags. 94 @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false; 95 @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false; 96 @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG; 97 @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG; 98 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000; 99 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000; 100 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500; 101 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000; 102 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000; 103 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000; 104 // The sampling rate to push app compaction events into statsd for upload. 105 @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f; 106 @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L; 107 @VisibleForTesting static final long DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 8_000L; 108 // Format of this string should be a comma separated list of integers. 109 @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE = 110 String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER); 111 112 @VisibleForTesting 113 interface PropertyChangedCallbackForTest { onPropertyChanged()114 void onPropertyChanged(); 115 } 116 private PropertyChangedCallbackForTest mTestCallback; 117 118 // This interface is for functions related to the Process object that need a different 119 // implementation in the tests as we are not creating real processes when testing compaction. 120 @VisibleForTesting 121 interface ProcessDependencies { getRss(int pid)122 long[] getRss(int pid); performCompaction(String action, int pid)123 void performCompaction(String action, int pid) throws IOException; 124 } 125 126 // Handler constants. 127 static final int COMPACT_PROCESS_SOME = 1; 128 static final int COMPACT_PROCESS_FULL = 2; 129 static final int COMPACT_PROCESS_PERSISTENT = 3; 130 static final int COMPACT_PROCESS_BFGS = 4; 131 static final int COMPACT_PROCESS_MSG = 1; 132 static final int COMPACT_SYSTEM_MSG = 2; 133 static final int SET_FROZEN_PROCESS_MSG = 3; 134 static final int REPORT_UNFREEZE_MSG = 4; 135 136 //TODO:change this static definition into a configurable flag. 137 static final int FREEZE_TIMEOUT_MS = 500; 138 139 static final int DO_FREEZE = 1; 140 static final int REPORT_UNFREEZE = 2; 141 142 /** 143 * This thread must be moved to the system background cpuset. 144 * If that doesn't happen, it's probably going to draw a lot of power. 145 * However, this has to happen after the first updateOomAdjLocked, because 146 * that will wipe out the cpuset assignment for system_server threads. 147 * Accordingly, this is in the AMS constructor. 148 */ 149 final ServiceThread mCachedAppOptimizerThread; 150 151 private final ArrayList<ProcessRecord> mPendingCompactionProcesses = 152 new ArrayList<ProcessRecord>(); 153 private final ActivityManagerService mAm; 154 private final OnPropertiesChangedListener mOnFlagsChangedListener = 155 new OnPropertiesChangedListener() { 156 @Override 157 public void onPropertiesChanged(Properties properties) { 158 synchronized (mPhenotypeFlagLock) { 159 for (String name : properties.getKeyset()) { 160 if (KEY_USE_COMPACTION.equals(name)) { 161 updateUseCompaction(); 162 } else if (KEY_COMPACT_ACTION_1.equals(name) 163 || KEY_COMPACT_ACTION_2.equals(name)) { 164 updateCompactionActions(); 165 } else if (KEY_COMPACT_THROTTLE_1.equals(name) 166 || KEY_COMPACT_THROTTLE_2.equals(name) 167 || KEY_COMPACT_THROTTLE_3.equals(name) 168 || KEY_COMPACT_THROTTLE_4.equals(name)) { 169 updateCompactionThrottles(); 170 } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) { 171 updateCompactStatsdSampleRate(); 172 } else if (KEY_FREEZER_STATSD_SAMPLE_RATE.equals(name)) { 173 updateFreezerStatsdSampleRate(); 174 } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) { 175 updateFullRssThrottle(); 176 } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) { 177 updateFullDeltaRssThrottle(); 178 } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { 179 updateProcStateThrottle(); 180 } 181 } 182 } 183 if (mTestCallback != null) { 184 mTestCallback.onPropertyChanged(); 185 } 186 } 187 }; 188 189 private final Object mPhenotypeFlagLock = new Object(); 190 191 // Configured by phenotype. Updates from the server take effect immediately. 192 @GuardedBy("mPhenotypeFlagLock") 193 @VisibleForTesting volatile String mCompactActionSome = 194 compactActionIntToString(DEFAULT_COMPACT_ACTION_1); 195 @GuardedBy("mPhenotypeFlagLock") 196 @VisibleForTesting volatile String mCompactActionFull = 197 compactActionIntToString(DEFAULT_COMPACT_ACTION_2); 198 @GuardedBy("mPhenotypeFlagLock") 199 @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1; 200 @GuardedBy("mPhenotypeFlagLock") 201 @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2; 202 @GuardedBy("mPhenotypeFlagLock") 203 @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3; 204 @GuardedBy("mPhenotypeFlagLock") 205 @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; 206 @GuardedBy("mPhenotypeFlagLock") 207 @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; 208 @GuardedBy("mPhenotypeFlagLock") 209 @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; 210 @GuardedBy("mPhenotypeFlagLock") 211 private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; 212 private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; 213 private final Random mRandom = new Random(); 214 @GuardedBy("mPhenotypeFlagLock") 215 @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; 216 @VisibleForTesting volatile float mFreezerStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; 217 @GuardedBy("mPhenotypeFlagLock") 218 @VisibleForTesting volatile long mFullAnonRssThrottleKb = 219 DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB; 220 @GuardedBy("mPhenotypeFlagLock") 221 @VisibleForTesting volatile long mFullDeltaRssThrottleKb = 222 DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB; 223 @GuardedBy("mPhenotypeFlagLock") 224 @VisibleForTesting final Set<Integer> mProcStateThrottle; 225 226 // Handler on which compaction runs. 227 @VisibleForTesting 228 Handler mCompactionHandler; 229 private Handler mFreezeHandler; 230 231 // Maps process ID to last compaction statistics for processes that we've fully compacted. Used 232 // when evaluating throttles that we only consider for "full" compaction, so we don't store 233 // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and 234 // facilitate removal of the oldest entry. 235 @VisibleForTesting 236 LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats = 237 new LinkedHashMap<Integer, LastCompactionStats>() { 238 @Override 239 protected boolean removeEldestEntry(Map.Entry eldest) { 240 return size() > 100; 241 } 242 }; 243 244 private int mSomeCompactionCount; 245 private int mFullCompactionCount; 246 private int mPersistentCompactionCount; 247 private int mBfgsCompactionCount; 248 private final ProcessDependencies mProcessDependencies; 249 CachedAppOptimizer(ActivityManagerService am)250 public CachedAppOptimizer(ActivityManagerService am) { 251 this(am, null, new DefaultProcessDependencies()); 252 } 253 254 @VisibleForTesting CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback, ProcessDependencies processDependencies)255 CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback, 256 ProcessDependencies processDependencies) { 257 mAm = am; 258 mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread", 259 THREAD_PRIORITY_FOREGROUND, true); 260 mProcStateThrottle = new HashSet<>(); 261 mProcessDependencies = processDependencies; 262 mTestCallback = callback; 263 } 264 265 /** 266 * Reads phenotype config to determine whether app compaction is enabled or not and 267 * starts the background thread if necessary. 268 */ init()269 public void init() { 270 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 271 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); 272 synchronized (mPhenotypeFlagLock) { 273 updateUseCompaction(); 274 updateCompactionActions(); 275 updateCompactionThrottles(); 276 updateCompactStatsdSampleRate(); 277 updateFreezerStatsdSampleRate(); 278 updateFullRssThrottle(); 279 updateFullDeltaRssThrottle(); 280 updateProcStateThrottle(); 281 updateUseFreezer(); 282 } 283 Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), 284 Process.THREAD_GROUP_SYSTEM); 285 } 286 287 /** 288 * Returns whether compaction is enabled. 289 */ useCompaction()290 public boolean useCompaction() { 291 synchronized (mPhenotypeFlagLock) { 292 return mUseCompaction; 293 } 294 } 295 296 /** 297 * Returns whether freezer is enabled. 298 */ useFreezer()299 public boolean useFreezer() { 300 synchronized (mPhenotypeFlagLock) { 301 return mUseFreezer; 302 } 303 } 304 305 @GuardedBy("mAm") dump(PrintWriter pw)306 void dump(PrintWriter pw) { 307 pw.println("CachedAppOptimizer settings"); 308 synchronized (mPhenotypeFlagLock) { 309 pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction); 310 pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome); 311 pw.println(" " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull); 312 pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome); 313 pw.println(" " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull); 314 pw.println(" " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome); 315 pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull); 316 pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS); 317 pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent); 318 pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate); 319 pw.println(" " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "=" 320 + mFullAnonRssThrottleKb); 321 pw.println(" " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "=" 322 + mFullDeltaRssThrottleKb); 323 pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "=" 324 + Arrays.toString(mProcStateThrottle.toArray(new Integer[0]))); 325 326 pw.println(" " + mSomeCompactionCount + " some, " + mFullCompactionCount 327 + " full, " + mPersistentCompactionCount + " persistent, " 328 + mBfgsCompactionCount + " BFGS compactions."); 329 330 pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size() 331 + " processes."); 332 pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer); 333 pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate); 334 if (DEBUG_COMPACTION) { 335 for (Map.Entry<Integer, LastCompactionStats> entry 336 : mLastCompactionStats.entrySet()) { 337 int pid = entry.getKey(); 338 LastCompactionStats stats = entry.getValue(); 339 pw.println(" " + pid + ": " 340 + Arrays.toString(stats.getRssAfterCompaction())); 341 } 342 } 343 } 344 } 345 346 @GuardedBy("mAm") compactAppSome(ProcessRecord app)347 void compactAppSome(ProcessRecord app) { 348 app.reqCompactAction = COMPACT_PROCESS_SOME; 349 mPendingCompactionProcesses.add(app); 350 mCompactionHandler.sendMessage( 351 mCompactionHandler.obtainMessage( 352 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); 353 } 354 355 @GuardedBy("mAm") compactAppFull(ProcessRecord app)356 void compactAppFull(ProcessRecord app) { 357 app.reqCompactAction = COMPACT_PROCESS_FULL; 358 mPendingCompactionProcesses.add(app); 359 mCompactionHandler.sendMessage( 360 mCompactionHandler.obtainMessage( 361 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); 362 363 } 364 365 @GuardedBy("mAm") compactAppPersistent(ProcessRecord app)366 void compactAppPersistent(ProcessRecord app) { 367 app.reqCompactAction = COMPACT_PROCESS_PERSISTENT; 368 mPendingCompactionProcesses.add(app); 369 mCompactionHandler.sendMessage( 370 mCompactionHandler.obtainMessage( 371 COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); 372 } 373 374 @GuardedBy("mAm") shouldCompactPersistent(ProcessRecord app, long now)375 boolean shouldCompactPersistent(ProcessRecord app, long now) { 376 return (app.lastCompactTime == 0 377 || (now - app.lastCompactTime) > mCompactThrottlePersistent); 378 } 379 380 @GuardedBy("mAm") compactAppBfgs(ProcessRecord app)381 void compactAppBfgs(ProcessRecord app) { 382 app.reqCompactAction = COMPACT_PROCESS_BFGS; 383 mPendingCompactionProcesses.add(app); 384 mCompactionHandler.sendMessage( 385 mCompactionHandler.obtainMessage( 386 COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); 387 } 388 389 @GuardedBy("mAm") shouldCompactBFGS(ProcessRecord app, long now)390 boolean shouldCompactBFGS(ProcessRecord app, long now) { 391 return (app.lastCompactTime == 0 392 || (now - app.lastCompactTime) > mCompactThrottleBFGS); 393 } 394 395 @GuardedBy("mAm") compactAllSystem()396 void compactAllSystem() { 397 if (mUseCompaction) { 398 mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage( 399 COMPACT_SYSTEM_MSG)); 400 } 401 } 402 compactSystem()403 private native void compactSystem(); 404 405 /** 406 * Reads the flag value from DeviceConfig to determine whether app compaction 407 * should be enabled, and starts the freeze/compaction thread if needed. 408 */ 409 @GuardedBy("mPhenotypeFlagLock") updateUseCompaction()410 private void updateUseCompaction() { 411 mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 412 KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION); 413 414 if (mUseCompaction) { 415 if (!mCachedAppOptimizerThread.isAlive()) { 416 mCachedAppOptimizerThread.start(); 417 } 418 419 mCompactionHandler = new MemCompactionHandler(); 420 } 421 } 422 423 /** 424 * Determines whether the freezer is correctly supported by this system 425 */ isFreezerSupported()426 public static boolean isFreezerSupported() { 427 boolean supported = false; 428 FileReader fr = null; 429 430 try { 431 fr = new FileReader("/dev/freezer/frozen/freezer.killable"); 432 int i = fr.read(); 433 434 if ((char) i == '1') { 435 supported = true; 436 } else { 437 Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer"); 438 } 439 } catch (java.io.FileNotFoundException e) { 440 Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer"); 441 } catch (Exception e) { 442 Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString()); 443 } 444 445 if (fr != null) { 446 try { 447 fr.close(); 448 } catch (java.io.IOException e) { 449 Slog.e(TAG_AM, "Exception closing freezer.killable: " + e.toString()); 450 } 451 } 452 453 return supported; 454 } 455 456 /** 457 * Reads the flag value from DeviceConfig to determine whether app freezer 458 * should be enabled, and starts the freeze/compaction thread if needed. 459 */ 460 @GuardedBy("mPhenotypeFlagLock") updateUseFreezer()461 private void updateUseFreezer() { 462 final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(), 463 Settings.Global.CACHED_APPS_FREEZER_ENABLED); 464 465 if ("disabled".equals(configOverride)) { 466 mUseFreezer = false; 467 } else if ("enabled".equals(configOverride) 468 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, 469 KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) { 470 mUseFreezer = isFreezerSupported(); 471 } 472 473 if (mUseFreezer) { 474 Slog.d(TAG_AM, "Freezer enabled"); 475 if (!mCachedAppOptimizerThread.isAlive()) { 476 mCachedAppOptimizerThread.start(); 477 } 478 479 mFreezeHandler = new FreezeHandler(); 480 } 481 } 482 483 @GuardedBy("mPhenotypeFlagLock") updateCompactionActions()484 private void updateCompactionActions() { 485 int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 486 KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1); 487 488 int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 489 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2); 490 491 mCompactActionSome = compactActionIntToString(compactAction1); 492 mCompactActionFull = compactActionIntToString(compactAction2); 493 } 494 495 @GuardedBy("mPhenotypeFlagLock") updateCompactionThrottles()496 private void updateCompactionThrottles() { 497 boolean useThrottleDefaults = false; 498 String throttleSomeSomeFlag = 499 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 500 KEY_COMPACT_THROTTLE_1); 501 String throttleSomeFullFlag = 502 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 503 KEY_COMPACT_THROTTLE_2); 504 String throttleFullSomeFlag = 505 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 506 KEY_COMPACT_THROTTLE_3); 507 String throttleFullFullFlag = 508 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 509 KEY_COMPACT_THROTTLE_4); 510 String throttleBFGSFlag = 511 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 512 KEY_COMPACT_THROTTLE_5); 513 String throttlePersistentFlag = 514 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 515 KEY_COMPACT_THROTTLE_6); 516 517 if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) 518 || TextUtils.isEmpty(throttleFullSomeFlag) 519 || TextUtils.isEmpty(throttleFullFullFlag) 520 || TextUtils.isEmpty(throttleBFGSFlag) 521 || TextUtils.isEmpty(throttlePersistentFlag)) { 522 // Set defaults for all if any are not set. 523 useThrottleDefaults = true; 524 } else { 525 try { 526 mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag); 527 mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag); 528 mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag); 529 mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag); 530 mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag); 531 mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag); 532 } catch (NumberFormatException e) { 533 useThrottleDefaults = true; 534 } 535 } 536 537 if (useThrottleDefaults) { 538 mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1; 539 mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2; 540 mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3; 541 mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; 542 mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; 543 mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; 544 } 545 } 546 547 @GuardedBy("mPhenotypeFlagLock") updateCompactStatsdSampleRate()548 private void updateCompactStatsdSampleRate() { 549 mCompactStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 550 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE); 551 mCompactStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mCompactStatsdSampleRate)); 552 } 553 554 @GuardedBy("mPhenotypeFlagLock") updateFreezerStatsdSampleRate()555 private void updateFreezerStatsdSampleRate() { 556 mFreezerStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 557 KEY_FREEZER_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE); 558 mFreezerStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mFreezerStatsdSampleRate)); 559 } 560 561 @GuardedBy("mPhenotypeFlagLock") updateFullRssThrottle()562 private void updateFullRssThrottle() { 563 mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 564 KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); 565 566 // Don't allow negative values. 0 means don't apply the throttle. 567 if (mFullAnonRssThrottleKb < 0) { 568 mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB; 569 } 570 } 571 572 @GuardedBy("mPhenotypeFlagLock") updateFullDeltaRssThrottle()573 private void updateFullDeltaRssThrottle() { 574 mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 575 KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); 576 577 if (mFullDeltaRssThrottleKb < 0) { 578 mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB; 579 } 580 } 581 582 @GuardedBy("mPhenotypeFlagLock") updateProcStateThrottle()583 private void updateProcStateThrottle() { 584 String procStateThrottleString = DeviceConfig.getString( 585 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE, 586 DEFAULT_COMPACT_PROC_STATE_THROTTLE); 587 if (!parseProcStateThrottle(procStateThrottleString)) { 588 Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \"" 589 + procStateThrottleString + "\" falling back to default."); 590 if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) { 591 Slog.wtf(TAG_AM, 592 "Unable to parse default app compact proc state throttle " 593 + DEFAULT_COMPACT_PROC_STATE_THROTTLE); 594 } 595 } 596 } 597 parseProcStateThrottle(String procStateThrottleString)598 private boolean parseProcStateThrottle(String procStateThrottleString) { 599 String[] procStates = TextUtils.split(procStateThrottleString, ","); 600 mProcStateThrottle.clear(); 601 for (String procState : procStates) { 602 try { 603 mProcStateThrottle.add(Integer.parseInt(procState)); 604 } catch (NumberFormatException e) { 605 Slog.e(TAG_AM, "Failed to parse default app compaction proc state: " 606 + procState); 607 return false; 608 } 609 } 610 return true; 611 } 612 613 @VisibleForTesting compactActionIntToString(int action)614 static String compactActionIntToString(int action) { 615 switch(action) { 616 case COMPACT_ACTION_NONE_FLAG: 617 return COMPACT_ACTION_NONE; 618 case COMPACT_ACTION_FILE_FLAG: 619 return COMPACT_ACTION_FILE; 620 case COMPACT_ACTION_ANON_FLAG: 621 return COMPACT_ACTION_ANON; 622 case COMPACT_ACTION_FULL_FLAG: 623 return COMPACT_ACTION_FULL; 624 default: 625 return COMPACT_ACTION_NONE; 626 } 627 } 628 629 @GuardedBy("mAm") freezeAppAsync(ProcessRecord app)630 void freezeAppAsync(ProcessRecord app) { 631 mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); 632 633 mFreezeHandler.sendMessageDelayed( 634 mFreezeHandler.obtainMessage( 635 SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app), 636 FREEZE_TIMEOUT_MS); 637 } 638 639 @GuardedBy("mAm") unfreezeAppLocked(ProcessRecord app)640 void unfreezeAppLocked(ProcessRecord app) { 641 mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); 642 643 if (!app.frozen) { 644 if (DEBUG_FREEZER) { 645 Slog.d(TAG_AM, 646 "Skipping unfreeze for process " + app.pid + " " 647 + app.processName + " (not frozen)"); 648 } 649 return; 650 } 651 652 long freezeTime = app.freezeUnfreezeTime; 653 654 try { 655 Process.setProcessFrozen(app.pid, app.uid, false); 656 657 app.freezeUnfreezeTime = SystemClock.uptimeMillis(); 658 app.frozen = false; 659 } catch (Exception e) { 660 Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName 661 + ". Any related user experience might be hanged."); 662 } 663 664 if (!app.frozen) { 665 if (DEBUG_FREEZER) { 666 Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName); 667 } 668 669 mFreezeHandler.sendMessage( 670 mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG, 671 app.pid, 672 (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE), 673 app.processName)); 674 } 675 } 676 677 @VisibleForTesting 678 static final class LastCompactionStats { 679 private final long[] mRssAfterCompaction; 680 LastCompactionStats(long[] rss)681 LastCompactionStats(long[] rss) { 682 mRssAfterCompaction = rss; 683 } 684 getRssAfterCompaction()685 long[] getRssAfterCompaction() { 686 return mRssAfterCompaction; 687 } 688 } 689 690 private final class MemCompactionHandler extends Handler { MemCompactionHandler()691 private MemCompactionHandler() { 692 super(mCachedAppOptimizerThread.getLooper()); 693 } 694 695 @Override handleMessage(Message msg)696 public void handleMessage(Message msg) { 697 switch (msg.what) { 698 case COMPACT_PROCESS_MSG: { 699 long start = SystemClock.uptimeMillis(); 700 ProcessRecord proc; 701 int pid; 702 String action; 703 final String name; 704 int pendingAction, lastCompactAction; 705 long lastCompactTime; 706 LastCompactionStats lastCompactionStats; 707 int lastOomAdj = msg.arg1; 708 int procState = msg.arg2; 709 synchronized (mAm) { 710 proc = mPendingCompactionProcesses.remove(0); 711 712 pendingAction = proc.reqCompactAction; 713 pid = proc.pid; 714 name = proc.processName; 715 716 // don't compact if the process has returned to perceptible 717 // and this is only a cached/home/prev compaction 718 if ((pendingAction == COMPACT_PROCESS_SOME 719 || pendingAction == COMPACT_PROCESS_FULL) 720 && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) { 721 if (DEBUG_COMPACTION) { 722 Slog.d(TAG_AM, 723 "Skipping compaction as process " + name + " is " 724 + "now perceptible."); 725 } 726 return; 727 } 728 729 lastCompactAction = proc.lastCompactAction; 730 lastCompactTime = proc.lastCompactTime; 731 lastCompactionStats = mLastCompactionStats.get(pid); 732 } 733 734 if (pid == 0) { 735 // not a real process, either one being launched or one being killed 736 return; 737 } 738 739 // basic throttling 740 // use the Phenotype flag knobs to determine whether current/prevous 741 // compaction combo should be throtted or not 742 743 // Note that we explicitly don't take mPhenotypeFlagLock here as the flags 744 // should very seldom change, and taking the risk of using the wrong action is 745 // preferable to taking the lock for every single compaction action. 746 if (lastCompactTime != 0) { 747 if (pendingAction == COMPACT_PROCESS_SOME) { 748 if ((lastCompactAction == COMPACT_PROCESS_SOME 749 && (start - lastCompactTime < mCompactThrottleSomeSome)) 750 || (lastCompactAction == COMPACT_PROCESS_FULL 751 && (start - lastCompactTime 752 < mCompactThrottleSomeFull))) { 753 if (DEBUG_COMPACTION) { 754 Slog.d(TAG_AM, "Skipping some compaction for " + name 755 + ": too soon. throttle=" + mCompactThrottleSomeSome 756 + "/" + mCompactThrottleSomeFull + " last=" 757 + (start - lastCompactTime) + "ms ago"); 758 } 759 return; 760 } 761 } else if (pendingAction == COMPACT_PROCESS_FULL) { 762 if ((lastCompactAction == COMPACT_PROCESS_SOME 763 && (start - lastCompactTime < mCompactThrottleFullSome)) 764 || (lastCompactAction == COMPACT_PROCESS_FULL 765 && (start - lastCompactTime 766 < mCompactThrottleFullFull))) { 767 if (DEBUG_COMPACTION) { 768 Slog.d(TAG_AM, "Skipping full compaction for " + name 769 + ": too soon. throttle=" + mCompactThrottleFullSome 770 + "/" + mCompactThrottleFullFull + " last=" 771 + (start - lastCompactTime) + "ms ago"); 772 } 773 return; 774 } 775 } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) { 776 if (start - lastCompactTime < mCompactThrottlePersistent) { 777 if (DEBUG_COMPACTION) { 778 Slog.d(TAG_AM, "Skipping persistent compaction for " + name 779 + ": too soon. throttle=" + mCompactThrottlePersistent 780 + " last=" + (start - lastCompactTime) + "ms ago"); 781 } 782 return; 783 } 784 } else if (pendingAction == COMPACT_PROCESS_BFGS) { 785 if (start - lastCompactTime < mCompactThrottleBFGS) { 786 if (DEBUG_COMPACTION) { 787 Slog.d(TAG_AM, "Skipping bfgs compaction for " + name 788 + ": too soon. throttle=" + mCompactThrottleBFGS 789 + " last=" + (start - lastCompactTime) + "ms ago"); 790 } 791 return; 792 } 793 } 794 } 795 796 switch (pendingAction) { 797 case COMPACT_PROCESS_SOME: 798 action = mCompactActionSome; 799 break; 800 // For the time being, treat these as equivalent. 801 case COMPACT_PROCESS_FULL: 802 case COMPACT_PROCESS_PERSISTENT: 803 case COMPACT_PROCESS_BFGS: 804 action = mCompactActionFull; 805 break; 806 default: 807 action = COMPACT_ACTION_NONE; 808 break; 809 } 810 811 if (COMPACT_ACTION_NONE.equals(action)) { 812 return; 813 } 814 815 if (mProcStateThrottle.contains(procState)) { 816 if (DEBUG_COMPACTION) { 817 Slog.d(TAG_AM, "Skipping full compaction for process " + name 818 + "; proc state is " + procState); 819 } 820 return; 821 } 822 823 long[] rssBefore = mProcessDependencies.getRss(pid); 824 long anonRssBefore = rssBefore[2]; 825 826 if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0 827 && rssBefore[3] == 0) { 828 if (DEBUG_COMPACTION) { 829 Slog.d(TAG_AM, "Skipping compaction for" + "process " + pid 830 + " with no memory usage. Dead?"); 831 } 832 return; 833 } 834 835 if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) { 836 if (mFullAnonRssThrottleKb > 0L 837 && anonRssBefore < mFullAnonRssThrottleKb) { 838 if (DEBUG_COMPACTION) { 839 Slog.d(TAG_AM, "Skipping full compaction for process " 840 + name + "; anon RSS is too small: " + anonRssBefore 841 + "KB."); 842 } 843 return; 844 } 845 846 if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) { 847 long[] lastRss = lastCompactionStats.getRssAfterCompaction(); 848 long absDelta = Math.abs(rssBefore[1] - lastRss[1]) 849 + Math.abs(rssBefore[2] - lastRss[2]) 850 + Math.abs(rssBefore[3] - lastRss[3]); 851 if (absDelta <= mFullDeltaRssThrottleKb) { 852 if (DEBUG_COMPACTION) { 853 Slog.d(TAG_AM, "Skipping full compaction for process " 854 + name + "; abs delta is too small: " + absDelta 855 + "KB."); 856 } 857 return; 858 } 859 } 860 } 861 862 // Now we've passed through all the throttles and are going to compact, update 863 // bookkeeping. 864 switch (pendingAction) { 865 case COMPACT_PROCESS_SOME: 866 mSomeCompactionCount++; 867 break; 868 case COMPACT_PROCESS_FULL: 869 mFullCompactionCount++; 870 break; 871 case COMPACT_PROCESS_PERSISTENT: 872 mPersistentCompactionCount++; 873 break; 874 case COMPACT_PROCESS_BFGS: 875 mBfgsCompactionCount++; 876 break; 877 default: 878 break; 879 } 880 try { 881 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " 882 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") 883 + ": " + name); 884 long zramFreeKbBefore = Debug.getZramFreeKb(); 885 mProcessDependencies.performCompaction(action, pid); 886 long[] rssAfter = mProcessDependencies.getRss(pid); 887 long end = SystemClock.uptimeMillis(); 888 long time = end - start; 889 long zramFreeKbAfter = Debug.getZramFreeKb(); 890 EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action, 891 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], 892 rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1], 893 rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time, 894 lastCompactAction, lastCompactTime, lastOomAdj, procState, 895 zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore); 896 // Note that as above not taking mPhenoTypeFlagLock here to avoid locking 897 // on every single compaction for a flag that will seldom change and the 898 // impact of reading the wrong value here is low. 899 if (mRandom.nextFloat() < mCompactStatsdSampleRate) { 900 FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name, 901 pendingAction, rssBefore[0], rssBefore[1], rssBefore[2], 902 rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2], 903 rssAfter[3], time, lastCompactAction, lastCompactTime, 904 lastOomAdj, ActivityManager.processStateAmToProto(procState), 905 zramFreeKbBefore, zramFreeKbAfter); 906 } 907 synchronized (mAm) { 908 proc.lastCompactTime = end; 909 proc.lastCompactAction = pendingAction; 910 } 911 if (action.equals(COMPACT_ACTION_FULL) 912 || action.equals(COMPACT_ACTION_ANON)) { 913 // Remove entry and insert again to update insertion order. 914 mLastCompactionStats.remove(pid); 915 mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter)); 916 } 917 } catch (Exception e) { 918 // nothing to do, presumably the process died 919 } finally { 920 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 921 } 922 break; 923 } 924 case COMPACT_SYSTEM_MSG: { 925 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); 926 compactSystem(); 927 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 928 break; 929 } 930 } 931 } 932 } 933 934 private final class FreezeHandler extends Handler { FreezeHandler()935 private FreezeHandler() { 936 super(mCachedAppOptimizerThread.getLooper()); 937 } 938 939 @Override handleMessage(Message msg)940 public void handleMessage(Message msg) { 941 switch (msg.what) { 942 case SET_FROZEN_PROCESS_MSG: 943 freezeProcess((ProcessRecord) msg.obj); 944 break; 945 case REPORT_UNFREEZE_MSG: 946 int pid = msg.arg1; 947 int frozenDuration = msg.arg2; 948 String processName = (String) msg.obj; 949 950 reportUnfreeze(pid, frozenDuration, processName); 951 break; 952 default: 953 return; 954 } 955 } 956 freezeProcess(ProcessRecord proc)957 private void freezeProcess(ProcessRecord proc) { 958 final int pid; 959 final String name; 960 final long unfrozenDuration; 961 final boolean frozen; 962 963 synchronized (mAm) { 964 pid = proc.pid; 965 name = proc.processName; 966 967 if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ 968 || proc.shouldNotFreeze) { 969 if (DEBUG_FREEZER) { 970 Slog.d(TAG_AM, "Skipping freeze for process " + pid 971 + " " + name + " curAdj = " + proc.curAdj 972 + ", shouldNotFreeze = " + proc.shouldNotFreeze); 973 } 974 return; 975 } 976 977 if (pid == 0 || proc.frozen) { 978 // Already frozen or not a real process, either one being 979 // launched or one being killed 980 return; 981 } 982 983 long unfreezeTime = proc.freezeUnfreezeTime; 984 985 try { 986 Process.setProcessFrozen(pid, proc.uid, true); 987 988 proc.freezeUnfreezeTime = SystemClock.uptimeMillis(); 989 proc.frozen = true; 990 } catch (Exception e) { 991 Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name); 992 } 993 994 unfrozenDuration = proc.freezeUnfreezeTime - unfreezeTime; 995 frozen = proc.frozen; 996 } 997 998 if (frozen) { 999 if (DEBUG_FREEZER) { 1000 Slog.d(TAG_AM, "froze " + pid + " " + name); 1001 } 1002 1003 EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); 1004 1005 // See above for why we're not taking mPhenotypeFlagLock here 1006 if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { 1007 FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, 1008 FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP, 1009 pid, 1010 name, 1011 unfrozenDuration); 1012 } 1013 } 1014 } 1015 reportUnfreeze(int pid, int frozenDuration, String processName)1016 private void reportUnfreeze(int pid, int frozenDuration, String processName) { 1017 1018 EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName); 1019 1020 // See above for why we're not taking mPhenotypeFlagLock here 1021 if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { 1022 FrameworkStatsLog.write( 1023 FrameworkStatsLog.APP_FREEZE_CHANGED, 1024 FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP, 1025 pid, 1026 processName, 1027 frozenDuration); 1028 } 1029 } 1030 } 1031 1032 /** 1033 * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class. 1034 */ 1035 private static final class DefaultProcessDependencies implements ProcessDependencies { 1036 // Get memory RSS from process. 1037 @Override getRss(int pid)1038 public long[] getRss(int pid) { 1039 return Process.getRss(pid); 1040 } 1041 1042 // Compact process. 1043 @Override performCompaction(String action, int pid)1044 public void performCompaction(String action, int pid) throws IOException { 1045 try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) { 1046 fos.write(action.getBytes()); 1047 } 1048 } 1049 } 1050 } 1051