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