1 /* 2 * Copyright (C) 2023 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.rollback; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.ArgumentMatchers.anyBoolean; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.anyString; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.spy; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.content.Context; 39 import android.content.pm.ApplicationInfo; 40 import android.content.pm.PackageInfo; 41 import android.content.pm.PackageManager; 42 import android.content.pm.VersionedPackage; 43 import android.content.rollback.PackageRollbackInfo; 44 import android.content.rollback.RollbackInfo; 45 import android.content.rollback.RollbackManager; 46 import android.os.Handler; 47 import android.os.MessageQueue; 48 import android.os.SystemProperties; 49 import android.platform.test.flag.junit.SetFlagsRule; 50 51 import androidx.test.platform.app.InstrumentationRegistry; 52 import androidx.test.runner.AndroidJUnit4; 53 54 import com.android.dx.mockito.inline.extended.ExtendedMockito; 55 import com.android.server.PackageWatchdog; 56 import com.android.server.SystemConfig; 57 import com.android.server.pm.ApexManager; 58 59 import org.junit.After; 60 import org.junit.Before; 61 import org.junit.Rule; 62 import org.junit.Test; 63 import org.junit.rules.TemporaryFolder; 64 import org.junit.runner.RunWith; 65 import org.mockito.Answers; 66 import org.mockito.ArgumentCaptor; 67 import org.mockito.Mock; 68 import org.mockito.MockitoSession; 69 import org.mockito.quality.Strictness; 70 import org.mockito.stubbing.Answer; 71 72 import java.time.Duration; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.concurrent.CountDownLatch; 76 import java.util.concurrent.TimeUnit; 77 78 79 @RunWith(AndroidJUnit4.class) 80 public class RollbackPackageHealthObserverTest { 81 @Mock 82 private Context mMockContext; 83 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 84 private PackageWatchdog mMockPackageWatchdog; 85 @Mock 86 RollbackManager mRollbackManager; 87 @Mock 88 RollbackInfo mRollbackInfo; 89 @Mock 90 PackageRollbackInfo mPackageRollbackInfo; 91 @Mock 92 PackageManager mMockPackageManager; 93 94 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 95 private ApexManager mApexManager; 96 97 @Rule 98 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 99 private HashMap<String, String> mSystemSettingsMap; 100 private MockitoSession mSession; 101 private static final String APP_A = "com.package.a"; 102 private static final String APP_B = "com.package.b"; 103 private static final String APP_C = "com.package.c"; 104 private static final long VERSION_CODE = 1L; 105 private static final long VERSION_CODE_2 = 2L; 106 private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; 107 108 private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = 109 "persist.device_config.configuration.disable_high_impact_rollback"; 110 111 112 @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); 113 114 @Before setup()115 public void setup() { 116 mSession = ExtendedMockito.mockitoSession() 117 .initMocks(this) 118 .strictness(Strictness.LENIENT) 119 .spyStatic(PackageWatchdog.class) 120 .spyStatic(SystemProperties.class) 121 .startMocking(); 122 mSystemSettingsMap = new HashMap<>(); 123 124 // Mock PackageWatchdog 125 doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) 126 .when(() -> PackageWatchdog.getInstance(mMockContext)); 127 128 // Mock SystemProperties setter and various getters 129 doAnswer((Answer<Void>) invocationOnMock -> { 130 String key = invocationOnMock.getArgument(0); 131 String value = invocationOnMock.getArgument(1); 132 133 mSystemSettingsMap.put(key, value); 134 return null; 135 } 136 ).when(() -> SystemProperties.set(anyString(), anyString())); 137 138 doAnswer((Answer<Boolean>) invocationOnMock -> { 139 String key = invocationOnMock.getArgument(0); 140 boolean defaultValue = invocationOnMock.getArgument(1); 141 142 String storedValue = mSystemSettingsMap.get(key); 143 return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue); 144 } 145 ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean())); 146 147 try { 148 when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> { 149 final PackageInfo res = new PackageInfo(); 150 res.packageName = inv.getArgument(0); 151 res.setApexPackageName(res.packageName); 152 return res; 153 }); 154 } catch (PackageManager.NameNotFoundException e) { 155 throw new RuntimeException(e); 156 } 157 158 Context testContext = InstrumentationRegistry.getInstrumentation() 159 .getTargetContext(); 160 when(mMockContext.getUser()).thenReturn(testContext.getUser()); 161 when(mMockContext.getPackageName()).thenReturn(testContext.getPackageName()); 162 163 SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false)); 164 } 165 166 @After tearDown()167 public void tearDown() throws Exception { 168 mSession.finishMocking(); 169 } 170 171 /** 172 * Subclass of SystemConfig without running the constructor. 173 */ 174 private class SystemConfigTestClass extends SystemConfig { SystemConfigTestClass()175 SystemConfigTestClass() { 176 super(false); 177 } 178 } 179 180 @Test testHealthCheckLevels()181 public void testHealthCheckLevels() { 182 RollbackPackageHealthObserver observer = 183 spy(new RollbackPackageHealthObserver(mMockContext)); 184 VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE); 185 VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); 186 187 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 188 189 // Crashes with no rollbacks available 190 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 191 observer.onHealthCheckFailed(null, 192 PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1)); 193 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 194 observer.onHealthCheckFailed(null, 195 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 196 197 // Make the rollbacks available 198 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); 199 when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); 200 when(mPackageRollbackInfo.getVersionRolledBackFrom()).thenReturn(testFailedPackage); 201 202 // native crash 203 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, 204 observer.onHealthCheckFailed(null, 205 PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1)); 206 // non-native crash for the package 207 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, 208 observer.onHealthCheckFailed(testFailedPackage, 209 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 210 // non-native crash for a different package 211 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 212 observer.onHealthCheckFailed(secondFailedPackage, 213 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 214 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 215 observer.onHealthCheckFailed(secondFailedPackage, 216 PackageWatchdog.FAILURE_REASON_APP_CRASH, 2)); 217 // Subsequent crashes when rollbacks have completed 218 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of()); 219 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 220 observer.onHealthCheckFailed(testFailedPackage, 221 PackageWatchdog.FAILURE_REASON_APP_CRASH, 3)); 222 } 223 224 @Test testIsPersistent()225 public void testIsPersistent() { 226 RollbackPackageHealthObserver observer = 227 spy(new RollbackPackageHealthObserver(mMockContext)); 228 assertTrue(observer.isPersistent()); 229 } 230 231 @Test testMayObservePackage_withoutAnyRollback()232 public void testMayObservePackage_withoutAnyRollback() { 233 RollbackPackageHealthObserver observer = 234 spy(new RollbackPackageHealthObserver(mMockContext)); 235 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 236 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of()); 237 assertFalse(observer.mayObservePackage(APP_A)); 238 } 239 240 @Test testMayObservePackage_forPersistentApp()241 public void testMayObservePackage_forPersistentApp() 242 throws PackageManager.NameNotFoundException { 243 RollbackPackageHealthObserver observer = 244 spy(new RollbackPackageHealthObserver(mMockContext)); 245 ApplicationInfo info = new ApplicationInfo(); 246 info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; 247 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 248 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); 249 when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); 250 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 251 when(mMockPackageManager.getApplicationInfo(APP_A, 0)).thenReturn(info); 252 assertTrue(observer.mayObservePackage(APP_A)); 253 } 254 255 @Test testMayObservePackage_forNonPersistentApp()256 public void testMayObservePackage_forNonPersistentApp() 257 throws PackageManager.NameNotFoundException { 258 RollbackPackageHealthObserver observer = 259 spy(new RollbackPackageHealthObserver(mMockContext)); 260 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 261 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); 262 when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); 263 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 264 when(mMockPackageManager.getApplicationInfo(APP_A, 0)) 265 .thenThrow(new PackageManager.NameNotFoundException()); 266 assertFalse(observer.mayObservePackage(APP_A)); 267 } 268 269 /** 270 * Test that when impactLevel is low returns user impact level 70 271 */ 272 @Test healthCheckFailed_impactLevelLow_onePackage()273 public void healthCheckFailed_impactLevelLow_onePackage() 274 throws PackageManager.NameNotFoundException { 275 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 276 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 277 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 278 null, null , false, false, 279 null); 280 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 281 false, null, 111, 282 PackageManager.ROLLBACK_USER_IMPACT_LOW); 283 RollbackPackageHealthObserver observer = 284 spy(new RollbackPackageHealthObserver(mMockContext)); 285 VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); 286 287 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 288 // Make the rollbacks available 289 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 290 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 291 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 292 293 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 294 observer.onHealthCheckFailed(secondFailedPackage, 295 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 296 } 297 298 /** 299 * HealthCheckFailed should only return low impact rollbacks. High impact rollbacks are only 300 * for bootloop. 301 */ 302 @Test healthCheckFailed_impactLevelHigh_onePackage()303 public void healthCheckFailed_impactLevelHigh_onePackage() 304 throws PackageManager.NameNotFoundException { 305 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 306 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 307 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 308 null, null, false, false, 309 null); 310 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 311 false, null, 111, 312 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 313 RollbackPackageHealthObserver observer = 314 spy(new RollbackPackageHealthObserver(mMockContext)); 315 VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); 316 317 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 318 // Make the rollbacks available 319 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 320 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 321 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 322 323 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 324 observer.onHealthCheckFailed(secondFailedPackage, 325 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 326 } 327 328 /** 329 * When the rollback impact level is manual only return user impact level 0. (User impact level 330 * 0 is ignored by package watchdog) 331 */ 332 @Test healthCheckFailed_impactLevelManualOnly_onePackage()333 public void healthCheckFailed_impactLevelManualOnly_onePackage() 334 throws PackageManager.NameNotFoundException { 335 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 336 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 337 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 338 null, null, false, false, 339 null); 340 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 341 false, null, 111, 342 PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); 343 RollbackPackageHealthObserver observer = 344 spy(new RollbackPackageHealthObserver(mMockContext)); 345 VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); 346 347 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 348 // Make the rollbacks available 349 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 350 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 351 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 352 353 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 354 observer.onHealthCheckFailed(secondFailedPackage, 355 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 356 } 357 358 /** 359 * When both low impact and high impact are present, return 70. 360 */ 361 @Test healthCheckFailed_impactLevelLowAndHigh_onePackage()362 public void healthCheckFailed_impactLevelLowAndHigh_onePackage() 363 throws PackageManager.NameNotFoundException { 364 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 365 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 366 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 367 null, null, false, false, 368 null); 369 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 370 false, null, 111, 371 PackageManager.ROLLBACK_USER_IMPACT_LOW); 372 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 373 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 374 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 375 null, null, false, false, 376 null); 377 RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), 378 false, null, 222, 379 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 380 RollbackPackageHealthObserver observer = 381 spy(new RollbackPackageHealthObserver(mMockContext)); 382 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 383 384 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 385 // Make the rollbacks available 386 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 387 List.of(rollbackInfo1, rollbackInfo2)); 388 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 389 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 390 391 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 392 observer.onHealthCheckFailed(failedPackage, 393 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); 394 } 395 396 /** 397 * When low impact rollback is available roll it back. 398 */ 399 @Test execute_impactLevelLow_nativeCrash_rollback()400 public void execute_impactLevelLow_nativeCrash_rollback() 401 throws PackageManager.NameNotFoundException { 402 int rollbackId = 1; 403 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 404 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 405 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 406 null, null , false, false, 407 null); 408 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId, List.of(packageRollbackInfo), 409 false, null, 111, 410 PackageManager.ROLLBACK_USER_IMPACT_LOW); 411 VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); 412 RollbackPackageHealthObserver observer = 413 spy(new RollbackPackageHealthObserver(mMockContext)); 414 415 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 416 // Make the rollbacks available 417 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 418 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 419 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 420 421 observer.onExecuteHealthCheckMitigation(secondFailedPackage, 422 PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1); 423 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 424 425 verify(mRollbackManager).getAvailableRollbacks(); 426 verify(mRollbackManager).commitRollback(eq(rollbackId), any(), any()); 427 } 428 429 /** 430 * Rollback the failing package if rollback is available for it 431 */ 432 @Test execute_impactLevelLow_rollbackFailedPackage()433 public void execute_impactLevelLow_rollbackFailedPackage() 434 throws PackageManager.NameNotFoundException { 435 int rollbackId1 = 1; 436 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 437 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 438 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 439 null, null , false, false, 440 null); 441 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 442 false, null, 111, 443 PackageManager.ROLLBACK_USER_IMPACT_LOW); 444 int rollbackId2 = 2; 445 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 446 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 447 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 448 null, null , false, false, 449 null); 450 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 451 false, null, 222, 452 PackageManager.ROLLBACK_USER_IMPACT_LOW); 453 RollbackPackageHealthObserver observer = 454 spy(new RollbackPackageHealthObserver(mMockContext)); 455 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 456 457 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 458 // Make the rollbacks available 459 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 460 List.of(rollbackInfo1, rollbackInfo2)); 461 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 462 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 463 464 observer.onExecuteHealthCheckMitigation(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH, 465 1); 466 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 467 468 verify(mRollbackManager).commitRollback(argument.capture(), any(), any()); 469 // Rollback package App B as the failing package is B 470 assertThat(argument.getValue()).isEqualTo(rollbackId2); 471 } 472 473 /** 474 * Rollback all available rollbacks if the rollback is not available for failing package. 475 */ 476 @Test execute_impactLevelLow_rollbackAll()477 public void execute_impactLevelLow_rollbackAll() 478 throws PackageManager.NameNotFoundException { 479 int rollbackId1 = 1; 480 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 481 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 482 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 483 null, null , false, false, 484 null); 485 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 486 false, null, 111, 487 PackageManager.ROLLBACK_USER_IMPACT_LOW); 488 int rollbackId2 = 2; 489 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 490 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 491 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 492 null, null , false, false, 493 null); 494 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 495 false, null, 222, 496 PackageManager.ROLLBACK_USER_IMPACT_LOW); 497 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 498 RollbackPackageHealthObserver observer = 499 spy(new RollbackPackageHealthObserver(mMockContext)); 500 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 501 502 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 503 // Make the rollbacks available 504 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 505 List.of(rollbackInfo1, rollbackInfo2)); 506 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 507 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 508 509 observer.onExecuteHealthCheckMitigation(failedPackage, 510 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 511 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 512 513 verify(mRollbackManager, times(2)).commitRollback( 514 argument.capture(), any(), any()); 515 // Rollback A and B when the failing package doesn't have a rollback 516 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); 517 } 518 519 /** 520 * rollback low impact package if both low and high impact packages are available 521 */ 522 @Test execute_impactLevelLowAndHigh_rollbackLow()523 public void execute_impactLevelLowAndHigh_rollbackLow() 524 throws PackageManager.NameNotFoundException { 525 int rollbackId1 = 1; 526 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 527 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 528 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 529 null, null , false, false, 530 null); 531 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 532 false, null, 111, 533 PackageManager.ROLLBACK_USER_IMPACT_LOW); 534 int rollbackId2 = 2; 535 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 536 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 537 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 538 null, null , false, false, 539 null); 540 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 541 false, null, 222, 542 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 543 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 544 RollbackPackageHealthObserver observer = 545 spy(new RollbackPackageHealthObserver(mMockContext)); 546 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 547 548 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 549 // Make the rollbacks available 550 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 551 List.of(rollbackInfo1, rollbackInfo2)); 552 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 553 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 554 555 observer.onExecuteHealthCheckMitigation(failedPackage, 556 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 557 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 558 559 verify(mRollbackManager, times(1)).commitRollback( 560 argument.capture(), any(), any()); 561 // Rollback A and B when the failing package doesn't have a rollback 562 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); 563 } 564 565 /** 566 * Don't roll back high impact package if only high impact package is available. high impact 567 * rollback to be rolled back only on bootloop. 568 */ 569 @Test execute_impactLevelHigh_rollbackHigh()570 public void execute_impactLevelHigh_rollbackHigh() 571 throws PackageManager.NameNotFoundException { 572 int rollbackId2 = 2; 573 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 574 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 575 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 576 null, null , false, false, 577 null); 578 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 579 false, null, 111, 580 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 581 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 582 RollbackPackageHealthObserver observer = 583 spy(new RollbackPackageHealthObserver(mMockContext)); 584 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 585 586 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 587 // Make the rollbacks available 588 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); 589 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 590 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 591 592 observer.onExecuteHealthCheckMitigation(failedPackage, 593 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 594 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 595 596 verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); 597 598 } 599 600 /** 601 * Test that when impactLevel is low returns user impact level 70 602 */ 603 @Test onBootLoop_impactLevelLow_onePackage()604 public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException { 605 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 606 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 607 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 608 null, null , false, false, 609 null); 610 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 611 false, null, 111, 612 PackageManager.ROLLBACK_USER_IMPACT_LOW); 613 RollbackPackageHealthObserver observer = 614 spy(new RollbackPackageHealthObserver(mMockContext)); 615 616 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 617 // Make the rollbacks available 618 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 619 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 620 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 621 622 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 623 observer.onBootLoop(1)); 624 } 625 626 @Test onBootLoop_impactLevelHigh_onePackage()627 public void onBootLoop_impactLevelHigh_onePackage() 628 throws PackageManager.NameNotFoundException { 629 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 630 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 631 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 632 null, null, false, false, 633 null); 634 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 635 false, null, 111, 636 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 637 RollbackPackageHealthObserver observer = 638 spy(new RollbackPackageHealthObserver(mMockContext)); 639 640 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 641 // Make the rollbacks available 642 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 643 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 644 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 645 646 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, 647 observer.onBootLoop(1)); 648 } 649 650 @Test onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage()651 public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage() 652 throws PackageManager.NameNotFoundException { 653 SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); 654 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 655 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 656 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 657 null, null, false, false, 658 null); 659 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 660 false, null, 111, 661 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 662 RollbackPackageHealthObserver observer = 663 spy(new RollbackPackageHealthObserver(mMockContext)); 664 665 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 666 // Make the rollbacks available 667 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 668 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 669 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 670 671 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 672 observer.onBootLoop(1)); 673 } 674 675 /** 676 * When the rollback impact level is manual only return user impact level 0. (User impact level 677 * 0 is ignored by package watchdog) 678 */ 679 @Test onBootLoop_impactLevelManualOnly_onePackage()680 public void onBootLoop_impactLevelManualOnly_onePackage() 681 throws PackageManager.NameNotFoundException { 682 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 683 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 684 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 685 null, null, false, false, 686 null); 687 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 688 false, null, 111, 689 PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); 690 RollbackPackageHealthObserver observer = 691 spy(new RollbackPackageHealthObserver(mMockContext)); 692 693 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 694 // Make the rollbacks available 695 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 696 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 697 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 698 699 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 700 observer.onBootLoop(1)); 701 } 702 703 /** 704 * When both low impact and high impact are present, return 70. 705 */ 706 @Test onBootLoop_impactLevelLowAndHigh_onePackage()707 public void onBootLoop_impactLevelLowAndHigh_onePackage() 708 throws PackageManager.NameNotFoundException { 709 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 710 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 711 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, 712 null, null, false, false, 713 null); 714 RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), 715 false, null, 111, 716 PackageManager.ROLLBACK_USER_IMPACT_LOW); 717 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 718 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 719 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 720 null, null, false, false, 721 null); 722 RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), 723 false, null, 222, 724 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 725 RollbackPackageHealthObserver observer = 726 spy(new RollbackPackageHealthObserver(mMockContext)); 727 728 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 729 // Make the rollbacks available 730 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 731 List.of(rollbackInfo1, rollbackInfo2)); 732 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 733 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 734 735 assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 736 observer.onBootLoop(1)); 737 } 738 739 /** 740 * Rollback all available rollbacks if the rollback is not available for failing package. 741 */ 742 @Test executeBootLoopMitigation_impactLevelLow_rollbackAll()743 public void executeBootLoopMitigation_impactLevelLow_rollbackAll() 744 throws PackageManager.NameNotFoundException { 745 int rollbackId1 = 1; 746 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 747 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 748 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 749 null, null , false, false, 750 null); 751 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 752 false, null, 111, 753 PackageManager.ROLLBACK_USER_IMPACT_LOW); 754 int rollbackId2 = 2; 755 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 756 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 757 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 758 null, null , false, false, 759 null); 760 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 761 false, null, 222, 762 PackageManager.ROLLBACK_USER_IMPACT_LOW); 763 RollbackPackageHealthObserver observer = 764 spy(new RollbackPackageHealthObserver(mMockContext)); 765 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 766 767 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 768 // Make the rollbacks available 769 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 770 List.of(rollbackInfo1, rollbackInfo2)); 771 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 772 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 773 774 observer.onExecuteBootLoopMitigation(1); 775 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 776 777 verify(mRollbackManager, times(2)).commitRollback( 778 argument.capture(), any(), any()); 779 // Rollback A and B when the failing package doesn't have a rollback 780 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); 781 } 782 783 /** 784 * rollback low impact package if both low and high impact packages are available 785 */ 786 @Test executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow()787 public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow() 788 throws PackageManager.NameNotFoundException { 789 int rollbackId1 = 1; 790 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 791 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 792 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 793 null, null , false, false, 794 null); 795 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 796 false, null, 111, 797 PackageManager.ROLLBACK_USER_IMPACT_LOW); 798 int rollbackId2 = 2; 799 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 800 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 801 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 802 null, null , false, false, 803 null); 804 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 805 false, null, 222, 806 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 807 RollbackPackageHealthObserver observer = 808 spy(new RollbackPackageHealthObserver(mMockContext)); 809 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 810 811 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 812 // Make the rollbacks available 813 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 814 List.of(rollbackInfo1, rollbackInfo2)); 815 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 816 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 817 818 observer.onExecuteBootLoopMitigation(1); 819 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 820 821 verify(mRollbackManager, times(1)).commitRollback( 822 argument.capture(), any(), any()); 823 // Rollback A and B when the failing package doesn't have a rollback 824 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); 825 } 826 827 /** 828 * Rollback high impact package if only high impact package is available 829 */ 830 @Test executeBootLoopMitigation_impactLevelHigh_rollbackHigh()831 public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh() 832 throws PackageManager.NameNotFoundException { 833 int rollbackId2 = 2; 834 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 835 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 836 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 837 null, null , false, false, 838 null); 839 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 840 false, null, 111, 841 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 842 RollbackPackageHealthObserver observer = 843 spy(new RollbackPackageHealthObserver(mMockContext)); 844 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 845 846 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 847 // Make the rollbacks available 848 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); 849 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 850 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 851 852 observer.onExecuteBootLoopMitigation(1); 853 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 854 855 verify(mRollbackManager, times(1)).commitRollback( 856 argument.capture(), any(), any()); 857 // Rollback high impact packages when no other rollback available 858 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); 859 } 860 861 /** 862 * Rollback only low impact available rollbacks if both low and manual only are available. 863 */ 864 @Test execute_impactLevelLowAndManual_rollbackLowImpactOnly()865 public void execute_impactLevelLowAndManual_rollbackLowImpactOnly() 866 throws PackageManager.NameNotFoundException, InterruptedException { 867 int rollbackId1 = 1; 868 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 869 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 870 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 871 null, null , false, false, 872 null); 873 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 874 false, null, 111, 875 PackageManager.ROLLBACK_USER_IMPACT_LOW); 876 int rollbackId2 = 2; 877 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 878 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 879 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 880 null, null , false, false, 881 null); 882 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), 883 false, null, 222, 884 PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); 885 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 886 RollbackPackageHealthObserver observer = 887 spy(new RollbackPackageHealthObserver(mMockContext)); 888 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 889 890 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 891 // Make the rollbacks available 892 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 893 List.of(rollbackInfo1, rollbackInfo2)); 894 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 895 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 896 897 observer.onExecuteHealthCheckMitigation(failedPackage, 898 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 899 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 900 901 verify(mRollbackManager, times(1)).commitRollback( 902 argument.capture(), any(), any()); 903 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); 904 } 905 906 /** 907 * Do not roll back if only manual rollback is available. 908 */ 909 @Test execute_impactLevelManual_rollbackLowImpactOnly()910 public void execute_impactLevelManual_rollbackLowImpactOnly() 911 throws PackageManager.NameNotFoundException { 912 int rollbackId1 = 1; 913 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 914 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 915 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 916 null, null , false, false, 917 null); 918 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), 919 false, null, 111, 920 PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); 921 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 922 RollbackPackageHealthObserver observer = 923 spy(new RollbackPackageHealthObserver(mMockContext)); 924 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 925 926 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 927 // Make the rollbacks available 928 when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); 929 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 930 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 931 932 observer.onExecuteHealthCheckMitigation(failedPackage, 933 PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 934 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 935 936 verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); 937 } 938 939 /** 940 * Rollback alphabetically first package if multiple high impact rollbacks are available. 941 */ 942 @Test executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh()943 public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh() 944 throws PackageManager.NameNotFoundException { 945 int rollbackId1 = 1; 946 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 947 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 948 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 949 null, null , false, false, 950 null); 951 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), 952 false, null, 111, 953 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 954 int rollbackId2 = 2; 955 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 956 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 957 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 958 null, null , false, false, 959 null); 960 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), 961 false, null, 111, 962 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 963 VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); 964 RollbackPackageHealthObserver observer = 965 spy(new RollbackPackageHealthObserver(mMockContext)); 966 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 967 968 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 969 // Make the rollbacks available 970 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 971 List.of(rollbackInfo1, rollbackInfo2)); 972 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 973 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 974 975 observer.onExecuteBootLoopMitigation(1); 976 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 977 978 verify(mRollbackManager, times(1)).commitRollback( 979 argument.capture(), any(), any()); 980 // Rollback APP_A because it is first alphabetically 981 assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); 982 } 983 984 /** 985 * Don't roll back if kill switch is enabled. 986 */ 987 @Test executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh()988 public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh() 989 throws PackageManager.NameNotFoundException { 990 SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); 991 int rollbackId1 = 1; 992 VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); 993 VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); 994 PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, 995 null, null , false, false, 996 null); 997 RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), 998 false, null, 111, 999 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 1000 int rollbackId2 = 2; 1001 VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); 1002 VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); 1003 PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, 1004 null, null , false, false, 1005 null); 1006 RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), 1007 false, null, 111, 1008 PackageManager.ROLLBACK_USER_IMPACT_HIGH); 1009 RollbackPackageHealthObserver observer = 1010 spy(new RollbackPackageHealthObserver(mMockContext)); 1011 ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); 1012 1013 when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); 1014 // Make the rollbacks available 1015 when(mRollbackManager.getAvailableRollbacks()).thenReturn( 1016 List.of(rollbackInfo1, rollbackInfo2)); 1017 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 1018 when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); 1019 1020 observer.onExecuteBootLoopMitigation(1); 1021 waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); 1022 1023 verify(mRollbackManager, never()).commitRollback( 1024 argument.capture(), any(), any()); 1025 } 1026 waitForIdleHandler(Handler handler, Duration timeout)1027 private void waitForIdleHandler(Handler handler, Duration timeout) { 1028 final MessageQueue queue = handler.getLooper().getQueue(); 1029 final CountDownLatch latch = new CountDownLatch(1); 1030 queue.addIdleHandler(() -> { 1031 latch.countDown(); 1032 // Remove idle handler 1033 return false; 1034 }); 1035 try { 1036 latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS); 1037 } catch (InterruptedException e) { 1038 fail("Interrupted unexpectedly: " + e); 1039 } 1040 } 1041 } 1042