1 /* 2 * Copyright (C) 2020 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.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.spy; 26 27 import android.app.ActivityManager; 28 import android.app.IApplicationThread; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageManagerInternal; 33 import android.os.Environment; 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.os.Process; 37 import android.provider.DeviceConfig; 38 39 import androidx.test.platform.app.InstrumentationRegistry; 40 41 import com.android.modules.utils.testing.TestableDeviceConfig; 42 import com.android.server.LocalServices; 43 import com.android.server.ServiceThread; 44 import com.android.server.appop.AppOpsService; 45 import com.android.server.wm.ActivityTaskManagerService; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Mock; 52 import org.mockito.junit.MockitoJUnitRunner; 53 54 import java.io.File; 55 import java.time.Instant; 56 import java.time.LocalDate; 57 import java.time.ZoneOffset; 58 import java.time.temporal.ChronoUnit; 59 import java.util.ArrayList; 60 import java.util.Collections; 61 import java.util.HashMap; 62 import java.util.Map; 63 import java.util.concurrent.CountDownLatch; 64 import java.util.concurrent.Executor; 65 import java.util.concurrent.TimeUnit; 66 67 /** 68 * Tests for {@link CachedAppOptimizer}. 69 * 70 * Build/Install/Run: 71 * atest FrameworksMockingServicesTests:CacheOomRankerTest 72 */ 73 @SuppressWarnings("GuardedBy") // No tests are concurrent, so no need to test locking. 74 @RunWith(MockitoJUnitRunner.class) 75 public class CacheOomRankerTest { 76 private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay( 77 ZoneOffset.UTC).toInstant(); 78 79 @Mock 80 private AppOpsService mAppOpsService; 81 private Handler mHandler; 82 private ActivityManagerService mAms; 83 84 @Mock 85 private PackageManagerInternal mPackageManagerInt; 86 87 @Rule 88 public final TestableDeviceConfig.TestableDeviceConfigRule 89 mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); 90 @Rule 91 public final ApplicationExitInfoTest.ServiceThreadRule 92 mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); 93 94 private int mNextPid = 10000; 95 private int mNextUid = 30000; 96 private int mNextPackageUid = 40000; 97 private int mNextPackageName = 1; 98 private Map<Integer, Long> mPidToRss; 99 100 private TestExecutor mExecutor = new TestExecutor(); 101 private CacheOomRanker mCacheOomRanker; 102 103 @Before setUp()104 public void setUp() { 105 HandlerThread handlerThread = new HandlerThread(""); 106 handlerThread.start(); 107 mHandler = new Handler(handlerThread.getLooper()); 108 /* allowIo */ 109 ServiceThread thread = new ServiceThread("TestServiceThread", 110 Process.THREAD_PRIORITY_DEFAULT, 111 true /* allowIo */); 112 thread.start(); 113 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 114 mAms = new ActivityManagerService( 115 new TestInjector(context), mServiceThreadRule.getThread()); 116 mAms.mActivityTaskManager = new ActivityTaskManagerService(context); 117 mAms.mActivityTaskManager.initialize(null, null, context.getMainLooper()); 118 mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); 119 mAms.mPackageManagerInt = mPackageManagerInt; 120 doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); 121 LocalServices.removeServiceForTest(PackageManagerInternal.class); 122 LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); 123 124 mPidToRss = new HashMap<>(); 125 mCacheOomRanker = new CacheOomRanker( 126 mAms, 127 pid -> { 128 Long rss = mPidToRss.get(pid); 129 assertThat(rss).isNotNull(); 130 return new long[]{rss}; 131 } 132 ); 133 mCacheOomRanker.init(mExecutor); 134 } 135 136 @Test init_listensForConfigChanges()137 public void init_listensForConfigChanges() throws InterruptedException { 138 mExecutor.init(); 139 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 140 CacheOomRanker.KEY_USE_OOM_RE_RANKING, 141 Boolean.TRUE.toString(), true); 142 mExecutor.waitForLatch(); 143 assertThat(mCacheOomRanker.useOomReranking()).isTrue(); 144 mExecutor.init(); 145 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 146 CacheOomRanker.KEY_USE_OOM_RE_RANKING, Boolean.FALSE.toString(), false); 147 mExecutor.waitForLatch(); 148 assertThat(mCacheOomRanker.useOomReranking()).isFalse(); 149 150 mExecutor.init(); 151 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 152 CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, 153 Integer.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2), 154 false); 155 mExecutor.waitForLatch(); 156 assertThat(mCacheOomRanker.getNumberToReRank()) 157 .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2); 158 159 mExecutor.init(); 160 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 161 CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, 162 Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1), 163 false); 164 mExecutor.waitForLatch(); 165 assertThat(mCacheOomRanker.mPreserveTopNApps) 166 .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1); 167 168 mExecutor.init(); 169 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 170 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT, 171 Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f), 172 false); 173 mExecutor.waitForLatch(); 174 assertThat(mCacheOomRanker.mLruWeight) 175 .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f); 176 177 mExecutor.init(); 178 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 179 CacheOomRanker.KEY_OOM_RE_RANKING_RSS_WEIGHT, 180 Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_RSS_WEIGHT - 0.1f), 181 false); 182 mExecutor.waitForLatch(); 183 assertThat(mCacheOomRanker.mRssWeight) 184 .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_RSS_WEIGHT - 0.1f); 185 186 mExecutor.init(); 187 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 188 CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT, 189 Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_USES_WEIGHT + 0.2f), 190 false); 191 mExecutor.waitForLatch(); 192 assertThat(mCacheOomRanker.mUsesWeight) 193 .isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_USES_WEIGHT + 0.2f); 194 } 195 196 @Test reRankLruCachedApps_lruImpactsOrdering()197 public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException { 198 setConfig(/* numberToReRank= */ 5, 199 /* preserveTopNApps= */ 0, 200 /* useFrequentRss= */ true, 201 /* rssUpdateRateMs= */ 0, 202 /* usesWeight= */ 0.0f, 203 /* pssWeight= */ 0.0f, 204 /* lruWeight= */1.0f); 205 206 ProcessList list = new ProcessList(); 207 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 208 ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 209 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 210 processList.add(lastUsed40MinutesAgo); 211 ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 212 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 213 processList.add(lastUsed42MinutesAgo); 214 ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 215 NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000); 216 processList.add(lastUsed60MinutesAgo); 217 ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 218 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 219 processList.add(lastUsed15MinutesAgo); 220 ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 221 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20); 222 processList.add(lastUsed17MinutesAgo); 223 // Only re-ranking 5 entries so this should stay in most recent position. 224 ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 225 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20); 226 processList.add(lastUsed30MinutesAgo); 227 list.setLruProcessServiceStartLSP(processList.size()); 228 229 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 230 231 // First 5 ordered by least recently used first, then last processes position unchanged. 232 assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo, 233 lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo, 234 lastUsed30MinutesAgo).inOrder(); 235 } 236 237 @Test reRankLruCachedApps_rssImpactsOrdering()238 public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException { 239 setConfig(/* numberToReRank= */ 6, 240 /* preserveTopNApps= */ 0, 241 /* useFrequentRss= */ true, 242 /* rssUpdateRateMs= */ 0, 243 /* usesWeight= */ 0.0f, 244 /* pssWeight= */ 1.0f, 245 /* lruWeight= */ 0.0f); 246 247 ProcessList list = new ProcessList(); 248 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 249 ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 250 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 251 processList.add(rss10k); 252 ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 253 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 254 processList.add(rss20k); 255 ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 256 NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000); 257 processList.add(rss1k); 258 ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 259 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 260 processList.add(rss100k); 261 ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 262 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 263 processList.add(rss2k); 264 ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 265 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20); 266 processList.add(rss15k); 267 // Only re-ranking 6 entries so this should stay in most recent position. 268 ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 269 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20); 270 processList.add(rss16k); 271 list.setLruProcessServiceStartLSP(processList.size()); 272 273 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 274 275 // First 6 ordered by largest pss, then last processes position unchanged. 276 assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, 277 rss16k).inOrder(); 278 } 279 280 @Test reRankLruCachedApps_rssImpactsOrdering_cachedRssValues()281 public void reRankLruCachedApps_rssImpactsOrdering_cachedRssValues() 282 throws InterruptedException { 283 setConfig(/* numberToReRank= */ 6, 284 /* preserveTopNApps= */ 0, 285 /* useFrequentRss= */ true, 286 /* rssUpdateRateMs= */ 10000000, 287 /* usesWeight= */ 0.0f, 288 /* pssWeight= */ 1.0f, 289 /* lruWeight= */ 0.0f); 290 291 ProcessList list = new ProcessList(); 292 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 293 ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 294 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 295 processList.add(rss10k); 296 ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 297 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 298 processList.add(rss20k); 299 ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 300 NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000); 301 processList.add(rss1k); 302 ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 303 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 304 processList.add(rss100k); 305 ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 306 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 307 processList.add(rss2k); 308 ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 309 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20); 310 processList.add(rss15k); 311 // Only re-ranking 6 entries so this should stay in most recent position. 312 ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 313 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20); 314 processList.add(rss16k); 315 list.setLruProcessServiceStartLSP(processList.size()); 316 317 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 318 // First 6 ordered by largest pss, then last processes position unchanged. 319 assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, 320 rss16k).inOrder(); 321 322 // Clear mPidToRss so that Process.getRss calls fail. 323 mPidToRss.clear(); 324 // Mix up the process list to ensure that CacheOomRanker actually re-ranks. 325 Collections.swap(processList, 0, 1); 326 327 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 328 // Re ranking is the same. 329 assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, 330 rss16k).inOrder(); 331 } 332 333 @Test reRankLruCachedApps_rssImpactsOrdering_profileRss()334 public void reRankLruCachedApps_rssImpactsOrdering_profileRss() 335 throws InterruptedException { 336 setConfig(/* numberToReRank= */ 6, 337 /* preserveTopNApps= */ 0, 338 /* useFrequentRss= */ false, 339 /* rssUpdateRateMs= */ 10000000, 340 /* usesWeight= */ 0.0f, 341 /* pssWeight= */ 1.0f, 342 /* lruWeight= */ 0.0f); 343 344 ProcessList list = new ProcessList(); 345 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 346 ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 347 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 0L, 1000); 348 rss10k.mProfile.setLastRss(10 * 1024L); 349 processList.add(rss10k); 350 ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 351 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 0L, 2000); 352 rss20k.mProfile.setLastRss(20 * 1024L); 353 processList.add(rss20k); 354 ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 355 NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10000); 356 rss1k.mProfile.setLastRss(1024L); 357 processList.add(rss1k); 358 ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 359 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10); 360 rss100k.mProfile.setLastRss(100 * 1024L); 361 processList.add(rss100k); 362 ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 363 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 0L, 20); 364 rss2k.mProfile.setLastRss(2 * 1024L); 365 processList.add(rss2k); 366 ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 367 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20); 368 rss15k.mProfile.setLastRss(15 * 1024L); 369 processList.add(rss15k); 370 // Only re-ranking 6 entries so this should stay in most recent position. 371 ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 372 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20); 373 rss16k.mProfile.setLastRss(16 * 1024L); 374 processList.add(rss16k); 375 list.setLruProcessServiceStartLSP(processList.size()); 376 377 // This should not be used, as RSS values are taken from mProfile. 378 mPidToRss.clear(); 379 380 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 381 // First 6 ordered by largest pss, then last processes position unchanged. 382 assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, 383 rss16k).inOrder(); 384 385 // Clear mPidToRss so that Process.getRss calls fail. 386 mPidToRss.clear(); 387 // Mix up the process list to ensure that CacheOomRanker actually re-ranks. 388 Collections.swap(processList, 0, 1); 389 390 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 391 // Re ranking is the same. 392 assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k, 393 rss16k).inOrder(); 394 } 395 396 397 @Test reRankLruCachedApps_usesImpactsOrdering()398 public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException { 399 setConfig(/* numberToReRank= */ 4, 400 /* preserveTopNApps= */ 0, 401 /* useFrequentRss= */ true, 402 /* rssUpdateRateMs= */ 0, 403 /* usesWeight= */ 1.0f, 404 /* pssWeight= */ 0.0f, 405 /* lruWeight= */ 0.0f); 406 407 ProcessList list = new ProcessList(); 408 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 409 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 410 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 411 processList.add(used1000); 412 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 413 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 414 processList.add(used2000); 415 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 416 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 417 processList.add(used10); 418 ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 419 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 420 processList.add(used20); 421 // Only re-ranking 6 entries so last two should stay in most recent position. 422 ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 423 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 424 processList.add(used500); 425 ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 426 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 427 processList.add(used200); 428 list.setLruProcessServiceStartLSP(processList.size()); 429 430 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 431 432 // First 4 ordered by uses, then last processes position unchanged. 433 assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500, 434 used200).inOrder(); 435 } 436 437 @Test reRankLruCachedApps_fewProcesses()438 public void reRankLruCachedApps_fewProcesses() throws InterruptedException { 439 setConfig(/* numberToReRank= */ 4, 440 /* preserveTopNApps= */ 0, 441 /* useFrequentRss= */ true, 442 /* rssUpdateRateMs= */ 0, 443 /* usesWeight= */ 1.0f, 444 /* pssWeight= */ 0.0f, 445 /* lruWeight= */ 0.0f); 446 447 ProcessList list = new ProcessList(); 448 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 449 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 450 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 451 processList.add(used1000); 452 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 453 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 454 processList.add(used2000); 455 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 456 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 457 processList.add(used10); 458 ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ, 459 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 460 processList.add(foregroundAdj); 461 ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ, 462 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 463 processList.add(serviceAdj); 464 ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ, 465 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 466 processList.add(systemAdj); 467 list.setLruProcessServiceStartLSP(processList.size()); 468 469 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 470 471 // 6 processes, only 3 in eligible for cache, so only those are re-ranked. 472 assertThat(processList).containsExactly(used10, used1000, used2000, 473 foregroundAdj, serviceAdj, systemAdj).inOrder(); 474 } 475 476 @Test reRankLruCachedApps_fewNonServiceProcesses()477 public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException { 478 setConfig(/* numberToReRank= */ 4, 479 /* preserveTopNApps= */ 0, 480 /* useFrequentRss= */ true, 481 /* rssUpdateRateMs= */ 0, 482 /* usesWeight= */ 1.0f, 483 /* pssWeight= */ 0.0f, 484 /* lruWeight= */ 0.0f); 485 486 ProcessList list = new ProcessList(); 487 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 488 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 489 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 490 processList.add(used1000); 491 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 492 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 493 processList.add(used2000); 494 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 495 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 496 processList.add(used10); 497 ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 498 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 499 processList.add(service1); 500 ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 501 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 502 processList.add(service2); 503 ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 504 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 505 processList.add(service3); 506 list.setLruProcessServiceStartLSP(3); 507 508 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 509 510 // Services unchanged, rest re-ranked. 511 assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2, 512 service3).inOrder(); 513 } 514 515 @Test reRankLruCachedApps_manyProcessesThenFew()516 public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException { 517 setConfig(/* numberToReRank= */ 6, 518 /* preserveTopNApps= */ 0, 519 /* useFrequentRss= */ true, 520 /* rssUpdateRateMs= */ 0, 521 /* usesWeight= */ 1.0f, 522 /* pssWeight= */ 0.0f, 523 /* lruWeight= */ 0.0f); 524 525 ProcessList set1List = new ProcessList(); 526 ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP(); 527 ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 528 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 529 set1ProcessList.add(set1Used1000); 530 ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 531 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 532 set1ProcessList.add(set1Used2000); 533 ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 534 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 535 set1ProcessList.add(set1Used10); 536 ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 537 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 538 set1ProcessList.add(set1Uses20); 539 ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 540 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 541 set1ProcessList.add(set1Uses500); 542 ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 543 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 544 set1ProcessList.add(set1Uses200); 545 set1List.setLruProcessServiceStartLSP(set1ProcessList.size()); 546 547 mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList, 548 set1List.getLruProcessServiceStartLOSP()); 549 assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200, 550 set1Uses500, set1Used1000, set1Used2000).inOrder(); 551 552 ProcessList set2List = new ProcessList(); 553 ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP(); 554 ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 555 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 556 set2ProcessList.add(set2Used1000); 557 ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 558 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 559 set2ProcessList.add(set2Used2000); 560 ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 561 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 562 set2ProcessList.add(set2Used10); 563 ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ, 564 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 565 set2ProcessList.add(set2ForegroundAdj); 566 ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ, 567 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 568 set2ProcessList.add(set2ServiceAdj); 569 ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ, 570 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 571 set2ProcessList.add(set2SystemAdj); 572 set2List.setLruProcessServiceStartLSP(set2ProcessList.size()); 573 574 mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList, 575 set2List.getLruProcessServiceStartLOSP()); 576 assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000, 577 set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder(); 578 } 579 580 @Test reRankLruCachedApps_preservesTopNApps()581 public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException { 582 setConfig(/* numberToReRank= */ 6, 583 /* preserveTopNApps= */ 3, 584 /* useFrequentRss= */ true, 585 /* rssUpdateRateMs= */ 0, 586 /* usesWeight= */ 1.0f, 587 /* pssWeight= */ 0.0f, 588 /* lruWeight= */ 0.0f); 589 590 ProcessList list = new ProcessList(); 591 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 592 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 593 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 594 processList.add(used1000); 595 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 596 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 597 processList.add(used2000); 598 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 599 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 600 processList.add(used10); 601 // Preserving the top 3 processes, so these should not be re-ranked. 602 ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 603 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 604 processList.add(used20); 605 ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 606 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 607 processList.add(used500); 608 ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 609 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 610 processList.add(used200); 611 list.setLruProcessServiceStartLSP(processList.size()); 612 613 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 614 615 // First 3 ordered by uses, then last processes position unchanged. 616 assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500, 617 used200).inOrder(); 618 } 619 620 @Test reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()621 public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged() 622 throws InterruptedException { 623 setConfig(/* numberToReRank= */ 6, 624 /* preserveTopNApps= */ 100, 625 /* useFrequentRss= */ true, 626 /* rssUpdateRateMs= */ 0, 627 /* usesWeight= */ 1.0f, 628 /* pssWeight= */ 0.0f, 629 /* lruWeight= */ 0.0f); 630 631 ProcessList list = new ProcessList(); 632 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 633 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 634 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 635 processList.add(used1000); 636 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 637 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 638 processList.add(used2000); 639 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 640 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 641 processList.add(used10); 642 ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 643 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 644 processList.add(used20); 645 ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 646 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 647 processList.add(used500); 648 ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 649 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 650 processList.add(used200); 651 list.setLruProcessServiceStartLSP(processList.size()); 652 653 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 654 655 // Nothing reordered, as we preserve the top 100 apps. 656 assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500, 657 used200).inOrder(); 658 } 659 660 @Test reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()661 public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault() 662 throws InterruptedException { 663 setConfig(/* numberToReRank= */ 6, 664 /* preserveTopNApps= */ -100, 665 /* useFrequentRss= */ true, 666 /* rssUpdateRateMs= */ 0, 667 /* usesWeight= */ 1.0f, 668 /* pssWeight= */ 0.0f, 669 /* lruWeight= */ 0.0f); 670 671 ProcessList list = new ProcessList(); 672 ArrayList<ProcessRecord> processList = list.getLruProcessesLSP(); 673 ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 674 NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000); 675 processList.add(used1000); 676 ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 677 NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000); 678 processList.add(used2000); 679 ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 680 NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10); 681 processList.add(used10); 682 // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked. 683 ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 684 NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20); 685 processList.add(used20); 686 ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 687 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500); 688 processList.add(used500); 689 ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ, 690 NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200); 691 processList.add(used200); 692 list.setLruProcessServiceStartLSP(processList.size()); 693 694 mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP()); 695 696 // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3. 697 assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500, 698 used200).inOrder(); 699 } 700 setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss, long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight)701 private void setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss, 702 long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight) 703 throws InterruptedException { 704 mExecutor.init(4); 705 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 706 CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, 707 Integer.toString(numberToReRank), 708 false); 709 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 710 CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, 711 Integer.toString(preserveTopNApps), 712 false); 713 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 714 CacheOomRanker.KEY_OOM_RE_RANKING_USE_FREQUENT_RSS, 715 Boolean.toString(useFrequentRss), 716 false); 717 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 718 CacheOomRanker.KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS, 719 Long.toString(rssUpdateRateMs), 720 false); 721 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 722 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT, 723 Float.toString(lruWeight), 724 false); 725 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 726 CacheOomRanker.KEY_OOM_RE_RANKING_RSS_WEIGHT, 727 Float.toString(pssWeight), 728 false); 729 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 730 CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT, 731 Float.toString(usesWeight), 732 false); 733 mExecutor.waitForLatch(); 734 assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank); 735 assertThat(mCacheOomRanker.mUseFrequentRss).isEqualTo(useFrequentRss); 736 assertThat(mCacheOomRanker.mRssUpdateRateMs).isEqualTo(rssUpdateRateMs); 737 assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight); 738 assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight); 739 assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight); 740 } 741 nextProcessRecord(int setAdj, long lastActivityTime, long lastRss, int wentToForegroundCount)742 private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss, 743 int wentToForegroundCount) { 744 ApplicationInfo ai = new ApplicationInfo(); 745 ai.packageName = "a.package.name" + mNextPackageName++; 746 ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++); 747 app.setPid(mNextPid++); 748 app.info.uid = mNextPackageUid++; 749 // Exact value does not mater, it can be any state for which compaction is allowed. 750 app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); 751 app.mState.setCurAdj(setAdj); 752 app.setLastActivityTime(lastActivityTime); 753 mPidToRss.put(app.getPid(), lastRss); 754 for (int i = 0; i < wentToForegroundCount; ++i) { 755 app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 756 app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT); 757 } 758 // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the 759 // app is currently launching. 760 ProcessStatsService processStatsService = new ProcessStatsService( 761 mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(), 762 "procstats")); 763 app.makeActive(mock(IApplicationThread.class), processStatsService); 764 return app; 765 } 766 767 private class TestExecutor implements Executor { 768 private CountDownLatch mLatch; 769 init(int count)770 private void init(int count) { 771 mLatch = new CountDownLatch(count); 772 } 773 init()774 private void init() { 775 init(1); 776 } 777 waitForLatch()778 private void waitForLatch() throws InterruptedException { 779 mLatch.await(5, TimeUnit.SECONDS); 780 } 781 782 @Override execute(Runnable command)783 public void execute(Runnable command) { 784 command.run(); 785 mLatch.countDown(); 786 } 787 } 788 789 private class TestInjector extends ActivityManagerService.Injector { TestInjector(Context context)790 private TestInjector(Context context) { 791 super(context); 792 } 793 794 @Override getAppOpsService(File recentAccessesFile, File storageFile, Handler handler)795 public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile, 796 Handler handler) { 797 return mAppOpsService; 798 } 799 800 @Override getUiHandler(ActivityManagerService service)801 public Handler getUiHandler(ActivityManagerService service) { 802 return mHandler; 803 } 804 } 805 806 // TODO: [b/302724778] Remove manual JNI load 807 static { 808 System.loadLibrary("mockingservicestestjni"); 809 } 810 } 811