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