1 /* 2 * Copyright (C) 2019 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; 18 19 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.assertTrue; 26 import static org.mockito.ArgumentMatchers.anyInt; 27 import static org.mockito.ArgumentMatchers.anyLong; 28 import static org.mockito.ArgumentMatchers.anyString; 29 import static org.mockito.Mockito.reset; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.Manifest; 35 import android.content.Context; 36 import android.content.pm.PackageInfo; 37 import android.content.pm.PackageManager; 38 import android.content.pm.VersionedPackage; 39 import android.crashrecovery.flags.Flags; 40 import android.net.ConnectivityModuleConnector; 41 import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener; 42 import android.os.Handler; 43 import android.os.SystemProperties; 44 import android.os.test.TestLooper; 45 import android.platform.test.flag.junit.SetFlagsRule; 46 import android.provider.DeviceConfig; 47 import android.util.AtomicFile; 48 import android.util.LongArrayQueue; 49 import android.util.Xml; 50 51 import androidx.test.InstrumentationRegistry; 52 53 import com.android.dx.mockito.inline.extended.ExtendedMockito; 54 import com.android.internal.util.XmlUtils; 55 import com.android.modules.utils.TypedXmlPullParser; 56 import com.android.modules.utils.TypedXmlSerializer; 57 import com.android.server.PackageWatchdog.HealthCheckState; 58 import com.android.server.PackageWatchdog.MonitoredPackage; 59 import com.android.server.PackageWatchdog.ObserverInternal; 60 import com.android.server.PackageWatchdog.PackageHealthObserver; 61 import com.android.server.PackageWatchdog.PackageHealthObserverImpact; 62 63 import org.junit.After; 64 import org.junit.Before; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.mockito.ArgumentCaptor; 68 import org.mockito.Captor; 69 import org.mockito.Mock; 70 import org.mockito.MockitoAnnotations; 71 import org.mockito.MockitoSession; 72 import org.mockito.quality.Strictness; 73 import org.mockito.stubbing.Answer; 74 75 import java.io.File; 76 import java.io.FileOutputStream; 77 import java.lang.reflect.Field; 78 import java.util.ArrayList; 79 import java.util.Arrays; 80 import java.util.Collections; 81 import java.util.HashMap; 82 import java.util.List; 83 import java.util.Set; 84 import java.util.concurrent.TimeUnit; 85 import java.util.function.Consumer; 86 import java.util.function.Supplier; 87 88 /** 89 * Test PackageWatchdog. 90 */ 91 public class PackageWatchdogTest { 92 private static final long RETRY_MAX_COUNT = 30; 93 private static final long RETRY_TIMEOUT_MILLIS = 500; 94 95 private static final String APP_A = "com.package.a"; 96 private static final String APP_B = "com.package.b"; 97 private static final String APP_C = "com.package.c"; 98 private static final String APP_D = "com.package.d"; 99 private static final long VERSION_CODE = 1L; 100 private static final String OBSERVER_NAME_1 = "observer1"; 101 private static final String OBSERVER_NAME_2 = "observer2"; 102 private static final String OBSERVER_NAME_3 = "observer3"; 103 private static final String OBSERVER_NAME_4 = "observer4"; 104 private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(10); 105 private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(50); 106 107 @Rule 108 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 109 110 private final TestClock mTestClock = new TestClock(); 111 private TestLooper mTestLooper; 112 private Context mSpyContext; 113 // Keep track of all created watchdogs to apply device config changes 114 private List<PackageWatchdog> mAllocatedWatchdogs; 115 @Mock 116 private ConnectivityModuleConnector mConnectivityModuleConnector; 117 @Mock 118 private PackageManager mMockPackageManager; 119 // Mock only sysprop apis 120 private PackageWatchdog.BootThreshold mSpyBootThreshold; 121 @Captor 122 private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor; 123 private MockitoSession mSession; 124 private HashMap<String, String> mSystemSettingsMap; 125 private HashMap<String, String> mCrashRecoveryPropertiesMap; 126 retry(Supplier<Boolean> supplier)127 private boolean retry(Supplier<Boolean> supplier) throws Exception { 128 for (int i = 0; i < RETRY_MAX_COUNT; ++i) { 129 if (supplier.get()) { 130 return true; 131 } 132 Thread.sleep(RETRY_TIMEOUT_MILLIS); 133 } 134 return false; 135 } 136 137 @Before setUp()138 public void setUp() throws Exception { 139 mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 140 MockitoAnnotations.initMocks(this); 141 new File(InstrumentationRegistry.getContext().getFilesDir(), 142 "package-watchdog.xml").delete(); 143 adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG, 144 Manifest.permission.WRITE_DEVICE_CONFIG); 145 mTestLooper = new TestLooper(); 146 mSpyContext = spy(InstrumentationRegistry.getContext()); 147 when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager); 148 when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> { 149 final PackageInfo res = new PackageInfo(); 150 res.packageName = inv.getArgument(0); 151 res.setLongVersionCode(VERSION_CODE); 152 return res; 153 }); 154 mSession = ExtendedMockito.mockitoSession() 155 .initMocks(this) 156 .strictness(Strictness.LENIENT) 157 .spyStatic(SystemProperties.class) 158 .startMocking(); 159 mSystemSettingsMap = new HashMap<>(); 160 161 162 // Mock SystemProperties setter and various getters 163 doAnswer((Answer<Void>) invocationOnMock -> { 164 String key = invocationOnMock.getArgument(0); 165 String value = invocationOnMock.getArgument(1); 166 167 mSystemSettingsMap.put(key, value); 168 return null; 169 } 170 ).when(() -> SystemProperties.set(anyString(), anyString())); 171 172 doAnswer((Answer<Integer>) invocationOnMock -> { 173 String key = invocationOnMock.getArgument(0); 174 int defaultValue = invocationOnMock.getArgument(1); 175 176 String storedValue = mSystemSettingsMap.get(key); 177 return storedValue == null ? defaultValue : Integer.parseInt(storedValue); 178 } 179 ).when(() -> SystemProperties.getInt(anyString(), anyInt())); 180 181 doAnswer((Answer<Long>) invocationOnMock -> { 182 String key = invocationOnMock.getArgument(0); 183 long defaultValue = invocationOnMock.getArgument(1); 184 185 String storedValue = mSystemSettingsMap.get(key); 186 return storedValue == null ? defaultValue : Long.parseLong(storedValue); 187 } 188 ).when(() -> SystemProperties.getLong(anyString(), anyLong())); 189 190 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 191 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 192 Boolean.toString(true), false); 193 194 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 195 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 196 Integer.toString(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT), false); 197 198 mAllocatedWatchdogs = new ArrayList<>(); 199 } 200 201 @After tearDown()202 public void tearDown() throws Exception { 203 dropShellPermissions(); 204 mSession.finishMocking(); 205 // Clean up listeners since too many listeners will delay notifications significantly 206 for (PackageWatchdog watchdog : mAllocatedWatchdogs) { 207 watchdog.removePropertyChangedListener(); 208 } 209 mAllocatedWatchdogs.clear(); 210 } 211 212 @Test testRegistration_singleObserver()213 public void testRegistration_singleObserver() { 214 PackageWatchdog watchdog = createWatchdog(); 215 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 216 217 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 218 raiseFatalFailureAndDispatch(watchdog, 219 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 220 PackageWatchdog.FAILURE_REASON_UNKNOWN); 221 222 // The failed packages should be the same as the registered ones to ensure registration is 223 // done successfully 224 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); 225 } 226 227 @Test testRegistration_multiObservers()228 public void testRegistration_multiObservers() { 229 PackageWatchdog watchdog = createWatchdog(); 230 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 231 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 232 233 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 234 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION); 235 raiseFatalFailureAndDispatch(watchdog, 236 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), 237 new VersionedPackage(APP_B, VERSION_CODE)), 238 PackageWatchdog.FAILURE_REASON_UNKNOWN); 239 240 // The failed packages should be the same as the registered ones to ensure registration is 241 // done successfully 242 assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A); 243 assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B); 244 } 245 246 @Test testUnregistration_singleObserver()247 public void testUnregistration_singleObserver() { 248 PackageWatchdog watchdog = createWatchdog(); 249 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 250 251 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 252 watchdog.unregisterHealthObserver(observer); 253 raiseFatalFailureAndDispatch(watchdog, 254 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 255 PackageWatchdog.FAILURE_REASON_UNKNOWN); 256 257 // We should have no failed packages to ensure unregistration is done successfully 258 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 259 } 260 261 @Test testUnregistration_multiObservers()262 public void testUnregistration_multiObservers() { 263 PackageWatchdog watchdog = createWatchdog(); 264 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 265 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 266 267 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 268 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); 269 watchdog.unregisterHealthObserver(observer2); 270 raiseFatalFailureAndDispatch(watchdog, 271 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 272 PackageWatchdog.FAILURE_REASON_UNKNOWN); 273 274 // observer1 should receive failed packages as intended. 275 assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A); 276 // observer2 should have no failed packages to ensure unregistration is done successfully 277 assertThat(observer2.mHealthCheckFailedPackages).isEmpty(); 278 } 279 280 @Test testExpiration_singleObserver()281 public void testExpiration_singleObserver() { 282 PackageWatchdog watchdog = createWatchdog(); 283 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 284 285 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 286 moveTimeForwardAndDispatch(SHORT_DURATION); 287 raiseFatalFailureAndDispatch(watchdog, 288 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 289 PackageWatchdog.FAILURE_REASON_UNKNOWN); 290 291 // We should have no failed packages for the fatal failure is raised after expiration 292 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 293 } 294 295 @Test testExpiration_multiObservers()296 public void testExpiration_multiObservers() { 297 PackageWatchdog watchdog = createWatchdog(); 298 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 299 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 300 301 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 302 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION); 303 moveTimeForwardAndDispatch(SHORT_DURATION); 304 raiseFatalFailureAndDispatch(watchdog, 305 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 306 PackageWatchdog.FAILURE_REASON_UNKNOWN); 307 308 // We should have no failed packages for the fatal failure is raised after expiration 309 assertThat(observer1.mHealthCheckFailedPackages).isEmpty(); 310 // We should have failed packages since observer2 hasn't expired 311 assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A); 312 } 313 314 /** Observing already observed package extends the observation time. */ 315 @Test testObserveAlreadyObservedPackage()316 public void testObserveAlreadyObservedPackage() { 317 PackageWatchdog watchdog = createWatchdog(); 318 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 319 320 // Start observing APP_A 321 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 322 323 // Then advance time half-way 324 moveTimeForwardAndDispatch(SHORT_DURATION / 2); 325 326 // Start observing APP_A again 327 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 328 329 // Then advance time such that it should have expired were it not for the second observation 330 moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1); 331 332 raiseFatalFailureAndDispatch(watchdog, 333 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 334 PackageWatchdog.FAILURE_REASON_UNKNOWN); 335 336 // Verify that we receive failed packages as expected for APP_A not expired 337 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); 338 } 339 340 /** 341 * Test package observers are persisted and loaded on startup 342 */ 343 @Test testPersistence()344 public void testPersistence() { 345 PackageWatchdog watchdog1 = createWatchdog(); 346 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 347 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 348 349 watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 350 watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION); 351 // Then advance time and run IO Handler so file is saved 352 mTestLooper.dispatchAll(); 353 // Then start a new watchdog 354 PackageWatchdog watchdog2 = createWatchdog(); 355 // Then resume observer1 and observer2 356 watchdog2.registerHealthObserver(observer1); 357 watchdog2.registerHealthObserver(observer2); 358 raiseFatalFailureAndDispatch(watchdog2, 359 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), 360 new VersionedPackage(APP_B, VERSION_CODE)), 361 PackageWatchdog.FAILURE_REASON_UNKNOWN); 362 363 // We should receive failed packages as expected to ensure observers are persisted and 364 // resumed correctly 365 assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A); 366 assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B); 367 } 368 369 /** 370 * Test package failure under threshold does not notify observers 371 */ 372 @Test testNoPackageFailureBeforeThreshold()373 public void testNoPackageFailureBeforeThreshold() throws Exception { 374 PackageWatchdog watchdog = createWatchdog(); 375 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 376 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 377 378 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); 379 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 380 381 // Then fail APP_A below the threshold 382 for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) { 383 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 384 PackageWatchdog.FAILURE_REASON_UNKNOWN); 385 } 386 387 // Run handler so package failures are dispatched to observers 388 mTestLooper.dispatchAll(); 389 390 // Verify that observers are not notified 391 assertThat(observer1.mHealthCheckFailedPackages).isEmpty(); 392 assertThat(observer2.mHealthCheckFailedPackages).isEmpty(); 393 } 394 395 /** 396 * Test package failure and does not notify any observer because they are not observing 397 * the failed packages. 398 */ 399 @Test testPackageFailureDifferentPackageNotifyNone()400 public void testPackageFailureDifferentPackageNotifyNone() throws Exception { 401 PackageWatchdog watchdog = createWatchdog(); 402 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 403 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 404 405 406 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); 407 watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION); 408 409 // Then fail APP_C (not observed) above the threshold 410 raiseFatalFailureAndDispatch(watchdog, 411 Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)), 412 PackageWatchdog.FAILURE_REASON_UNKNOWN); 413 414 // Verify that observers are not notified 415 assertThat(observer1.mHealthCheckFailedPackages).isEmpty(); 416 assertThat(observer2.mHealthCheckFailedPackages).isEmpty(); 417 } 418 419 /** 420 * Test package failure and does not notify any observer because the failed package version 421 * does not match the available rollback-from-version. 422 */ 423 @Test testPackageFailureDifferentVersionNotifyNone()424 public void testPackageFailureDifferentVersionNotifyNone() throws Exception { 425 PackageWatchdog watchdog = createWatchdog(); 426 long differentVersionCode = 2L; 427 TestObserver observer = new TestObserver(OBSERVER_NAME_1) { 428 @Override 429 public int onHealthCheckFailed(VersionedPackage versionedPackage, 430 int failureReason, int mitigationCount) { 431 if (versionedPackage.getVersionCode() == VERSION_CODE) { 432 // Only rollback for specific versionCode 433 return PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; 434 } 435 return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 436 } 437 }; 438 439 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 440 441 // Then fail APP_A (different version) above the threshold 442 raiseFatalFailureAndDispatch(watchdog, 443 Arrays.asList(new VersionedPackage(APP_A, differentVersionCode)), 444 PackageWatchdog.FAILURE_REASON_UNKNOWN); 445 446 // Verify that observers are not notified 447 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 448 } 449 450 451 /** 452 * Test package failure and notifies only least impact observers. 453 */ 454 @Test testPackageFailureNotifyAllDifferentImpacts()455 public void testPackageFailureNotifyAllDifferentImpacts() throws Exception { 456 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 457 PackageWatchdog watchdog = createWatchdog(); 458 TestObserver observerNone = new TestObserver(OBSERVER_NAME_1, 459 PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); 460 TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2, 461 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 462 TestObserver observerMid = new TestObserver(OBSERVER_NAME_3, 463 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 464 TestObserver observerLow = new TestObserver(OBSERVER_NAME_4, 465 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 466 467 // Start observing for all impact observers 468 watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D), 469 SHORT_DURATION); 470 watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C), 471 SHORT_DURATION); 472 watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B), 473 SHORT_DURATION); 474 watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A), 475 SHORT_DURATION); 476 477 // Then fail all apps above the threshold 478 raiseFatalFailureAndDispatch(watchdog, 479 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), 480 new VersionedPackage(APP_B, VERSION_CODE), 481 new VersionedPackage(APP_C, VERSION_CODE), 482 new VersionedPackage(APP_D, VERSION_CODE)), 483 PackageWatchdog.FAILURE_REASON_UNKNOWN); 484 485 // Verify least impact observers are notifed of package failures 486 List<String> observerNonePackages = observerNone.mMitigatedPackages; 487 List<String> observerHighPackages = observerHigh.mMitigatedPackages; 488 List<String> observerMidPackages = observerMid.mMitigatedPackages; 489 List<String> observerLowPackages = observerLow.mMitigatedPackages; 490 491 // APP_D failure observed by only observerNone is not caught cos its impact is none 492 assertThat(observerNonePackages).isEmpty(); 493 // APP_C failure is caught by observerHigh cos it's the lowest impact observer 494 assertThat(observerHighPackages).containsExactly(APP_C); 495 // APP_B failure is caught by observerMid cos it's the lowest impact observer 496 assertThat(observerMidPackages).containsExactly(APP_B); 497 // APP_A failure is caught by observerLow cos it's the lowest impact observer 498 assertThat(observerLowPackages).containsExactly(APP_A); 499 } 500 501 @Test testPackageFailureNotifyAllDifferentImpactsRecoverability()502 public void testPackageFailureNotifyAllDifferentImpactsRecoverability() throws Exception { 503 PackageWatchdog watchdog = createWatchdog(); 504 TestObserver observerNone = new TestObserver(OBSERVER_NAME_1, 505 PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); 506 TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2, 507 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); 508 TestObserver observerMid = new TestObserver(OBSERVER_NAME_3, 509 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 510 TestObserver observerLow = new TestObserver(OBSERVER_NAME_4, 511 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 512 513 // Start observing for all impact observers 514 watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D), 515 SHORT_DURATION); 516 watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C), 517 SHORT_DURATION); 518 watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B), 519 SHORT_DURATION); 520 watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A), 521 SHORT_DURATION); 522 523 // Then fail all apps above the threshold 524 raiseFatalFailureAndDispatch(watchdog, 525 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), 526 new VersionedPackage(APP_B, VERSION_CODE), 527 new VersionedPackage(APP_C, VERSION_CODE), 528 new VersionedPackage(APP_D, VERSION_CODE)), 529 PackageWatchdog.FAILURE_REASON_UNKNOWN); 530 531 // Verify least impact observers are notifed of package failures 532 List<String> observerNonePackages = observerNone.mMitigatedPackages; 533 List<String> observerHighPackages = observerHigh.mMitigatedPackages; 534 List<String> observerMidPackages = observerMid.mMitigatedPackages; 535 List<String> observerLowPackages = observerLow.mMitigatedPackages; 536 537 // APP_D failure observed by only observerNone is not caught cos its impact is none 538 assertThat(observerNonePackages).isEmpty(); 539 // APP_C failure is caught by observerHigh cos it's the lowest impact observer 540 assertThat(observerHighPackages).containsExactly(APP_C); 541 // APP_B failure is caught by observerMid cos it's the lowest impact observer 542 assertThat(observerMidPackages).containsExactly(APP_B); 543 // APP_A failure is caught by observerLow cos it's the lowest impact observer 544 assertThat(observerLowPackages).containsExactly(APP_A); 545 } 546 547 /** 548 * Test package failure and least impact observers are notified successively. 549 * State transistions: 550 * 551 * <ul> 552 * <li>(observer1:low, observer2:mid) -> {observer1} 553 * <li>(observer1:high, observer2:mid) -> {observer2} 554 * <li>(observer1:high, observer2:none) -> {observer1} 555 * <li>(observer1:none, observer2:none) -> {} 556 * <ul> 557 */ 558 @Test testPackageFailureNotifyLeastImpactSuccessively()559 public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception { 560 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 561 PackageWatchdog watchdog = createWatchdog(); 562 TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1, 563 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 564 TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2, 565 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 566 567 // Start observing for observerFirst and observerSecond with failure handling 568 watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION); 569 watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION); 570 571 // Then fail APP_A above the threshold 572 raiseFatalFailureAndDispatch(watchdog, 573 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 574 PackageWatchdog.FAILURE_REASON_UNKNOWN); 575 576 // Verify only observerFirst is notifed 577 assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); 578 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 579 580 // After observerFirst handles failure, next action it has is high impact 581 observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_100; 582 observerFirst.mMitigatedPackages.clear(); 583 observerSecond.mMitigatedPackages.clear(); 584 585 // Then fail APP_A again above the threshold 586 raiseFatalFailureAndDispatch(watchdog, 587 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 588 PackageWatchdog.FAILURE_REASON_UNKNOWN); 589 590 // Verify only observerSecond is notifed cos it has least impact 591 assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A); 592 assertThat(observerFirst.mMitigatedPackages).isEmpty(); 593 594 // After observerSecond handles failure, it has no further actions 595 observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 596 observerFirst.mMitigatedPackages.clear(); 597 observerSecond.mMitigatedPackages.clear(); 598 599 // Then fail APP_A again above the threshold 600 raiseFatalFailureAndDispatch(watchdog, 601 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 602 PackageWatchdog.FAILURE_REASON_UNKNOWN); 603 604 // Verify only observerFirst is notifed cos it has the only action 605 assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); 606 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 607 608 // After observerFirst handles failure, it too has no further actions 609 observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 610 observerFirst.mMitigatedPackages.clear(); 611 observerSecond.mMitigatedPackages.clear(); 612 613 // Then fail APP_A again above the threshold 614 raiseFatalFailureAndDispatch(watchdog, 615 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 616 PackageWatchdog.FAILURE_REASON_UNKNOWN); 617 618 // Verify no observer is notified cos no actions left 619 assertThat(observerFirst.mMitigatedPackages).isEmpty(); 620 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 621 } 622 623 @Test testPackageFailureNotifyLeastImpactSuccessivelyRecoverability()624 public void testPackageFailureNotifyLeastImpactSuccessivelyRecoverability() throws Exception { 625 PackageWatchdog watchdog = createWatchdog(); 626 TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1, 627 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 628 TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2, 629 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 630 631 // Start observing for observerFirst and observerSecond with failure handling 632 watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION); 633 watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION); 634 635 // Then fail APP_A above the threshold 636 raiseFatalFailureAndDispatch(watchdog, 637 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 638 PackageWatchdog.FAILURE_REASON_UNKNOWN); 639 640 // Verify only observerFirst is notifed 641 assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); 642 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 643 644 // After observerFirst handles failure, next action it has is high impact 645 observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_50; 646 observerFirst.mMitigatedPackages.clear(); 647 observerSecond.mMitigatedPackages.clear(); 648 649 // Then fail APP_A again above the threshold 650 raiseFatalFailureAndDispatch(watchdog, 651 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 652 PackageWatchdog.FAILURE_REASON_UNKNOWN); 653 654 // Verify only observerSecond is notifed cos it has least impact 655 assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A); 656 assertThat(observerFirst.mMitigatedPackages).isEmpty(); 657 658 // After observerSecond handles failure, it has no further actions 659 observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 660 observerFirst.mMitigatedPackages.clear(); 661 observerSecond.mMitigatedPackages.clear(); 662 663 // Then fail APP_A again above the threshold 664 raiseFatalFailureAndDispatch(watchdog, 665 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 666 PackageWatchdog.FAILURE_REASON_UNKNOWN); 667 668 // Verify only observerFirst is notifed cos it has the only action 669 assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); 670 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 671 672 // After observerFirst handles failure, it too has no further actions 673 observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 674 observerFirst.mMitigatedPackages.clear(); 675 observerSecond.mMitigatedPackages.clear(); 676 677 // Then fail APP_A again above the threshold 678 raiseFatalFailureAndDispatch(watchdog, 679 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 680 PackageWatchdog.FAILURE_REASON_UNKNOWN); 681 682 // Verify no observer is notified cos no actions left 683 assertThat(observerFirst.mMitigatedPackages).isEmpty(); 684 assertThat(observerSecond.mMitigatedPackages).isEmpty(); 685 } 686 687 /** 688 * Test package failure and notifies only one observer even with observer impact tie. 689 */ 690 @Test testPackageFailureNotifyOneSameImpact()691 public void testPackageFailureNotifyOneSameImpact() throws Exception { 692 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 693 PackageWatchdog watchdog = createWatchdog(); 694 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, 695 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 696 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, 697 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 698 699 // Start observing for observer1 and observer2 with failure handling 700 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); 701 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 702 703 // Then fail APP_A above the threshold 704 raiseFatalFailureAndDispatch(watchdog, 705 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 706 PackageWatchdog.FAILURE_REASON_UNKNOWN); 707 708 // Verify only one observer is notifed 709 assertThat(observer1.mMitigatedPackages).containsExactly(APP_A); 710 assertThat(observer2.mMitigatedPackages).isEmpty(); 711 } 712 713 @Test testPackageFailureNotifyOneSameImpactRecoverabilityDetection()714 public void testPackageFailureNotifyOneSameImpactRecoverabilityDetection() throws Exception { 715 PackageWatchdog watchdog = createWatchdog(); 716 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, 717 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); 718 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, 719 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); 720 721 // Start observing for observer1 and observer2 with failure handling 722 watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); 723 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 724 725 // Then fail APP_A above the threshold 726 raiseFatalFailureAndDispatch(watchdog, 727 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 728 PackageWatchdog.FAILURE_REASON_UNKNOWN); 729 730 // Verify only one observer is notifed 731 assertThat(observer1.mMitigatedPackages).containsExactly(APP_A); 732 assertThat(observer2.mMitigatedPackages).isEmpty(); 733 } 734 735 /** 736 * Test package passing explicit health checks does not fail and vice versa. 737 */ 738 @Test testExplicitHealthChecks()739 public void testExplicitHealthChecks() throws Exception { 740 TestController controller = new TestController(); 741 PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); 742 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, 743 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 744 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, 745 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 746 TestObserver observer3 = new TestObserver(OBSERVER_NAME_3, 747 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 748 749 750 // Start observing with explicit health checks for APP_A and APP_B respectively 751 // with observer1 and observer2 752 controller.setSupportedPackages(Arrays.asList(APP_A, APP_B)); 753 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 754 watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION); 755 756 // Run handler so requests are dispatched to the controller 757 mTestLooper.dispatchAll(); 758 759 // Verify we requested health checks for APP_A and APP_B 760 List<String> requestedPackages = controller.getRequestedPackages(); 761 assertThat(requestedPackages).containsExactly(APP_A, APP_B); 762 763 // Then health check passed for APP_A (observer1 is aware) 764 controller.setPackagePassed(APP_A); 765 766 // Then start observing APP_A with explicit health checks for observer3. 767 // Observer3 didn't exist when we got the explicit health check above, so 768 // it starts out with a non-passing explicit health check and has to wait for a pass 769 // otherwise it would be notified of APP_A failure on expiry 770 watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION); 771 772 // Then expire observers 773 moveTimeForwardAndDispatch(SHORT_DURATION); 774 775 // Verify we cancelled all requests on expiry 776 assertThat(controller.getRequestedPackages()).isEmpty(); 777 778 // Verify observer1 is not notified 779 assertThat(observer1.mMitigatedPackages).isEmpty(); 780 781 // Verify observer2 is notifed because health checks for APP_B never passed 782 assertThat(observer2.mMitigatedPackages).containsExactly(APP_B); 783 784 // Verify observer3 is notifed because health checks for APP_A did not pass before expiry 785 assertThat(observer3.mMitigatedPackages).containsExactly(APP_A); 786 } 787 788 /** 789 * Test explicit health check state can be disabled and enabled correctly. 790 */ 791 @Test testExplicitHealthCheckStateChanges()792 public void testExplicitHealthCheckStateChanges() throws Exception { 793 TestController controller = new TestController(); 794 PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); 795 TestObserver observer = new TestObserver(OBSERVER_NAME_1, 796 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 797 798 // Start observing with explicit health checks for APP_A and APP_B 799 controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C)); 800 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 801 watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION); 802 803 // Run handler so requests are dispatched to the controller 804 mTestLooper.dispatchAll(); 805 806 // Verify we requested health checks for APP_A and APP_B 807 List<String> requestedPackages = controller.getRequestedPackages(); 808 assertThat(requestedPackages).containsExactly(APP_A, APP_B); 809 810 // Disable explicit health checks (marks APP_A and APP_B as passed) 811 setExplicitHealthCheckEnabled(false); 812 813 // Run handler so requests/cancellations are dispatched to the controller 814 mTestLooper.dispatchAll(); 815 816 // Verify all checks are cancelled 817 assertThat(controller.getRequestedPackages()).isEmpty(); 818 819 // Then expire APP_A 820 moveTimeForwardAndDispatch(SHORT_DURATION); 821 822 // Verify APP_A is not failed (APP_B) is not expired yet 823 assertThat(observer.mMitigatedPackages).isEmpty(); 824 825 // Re-enable explicit health checks 826 setExplicitHealthCheckEnabled(true); 827 828 // Run handler so requests/cancellations are dispatched to the controller 829 mTestLooper.dispatchAll(); 830 831 // Verify no requests are made cos APP_A is expired and APP_B was marked as passed 832 assertThat(controller.getRequestedPackages()).isEmpty(); 833 834 // Then set new supported packages 835 controller.setSupportedPackages(Arrays.asList(APP_C)); 836 // Start observing APP_A and APP_C; only APP_C has support for explicit health checks 837 watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION); 838 839 // Run handler so requests/cancellations are dispatched to the controller 840 mTestLooper.dispatchAll(); 841 842 // Verify requests are only made for APP_C 843 requestedPackages = controller.getRequestedPackages(); 844 assertThat(requestedPackages).containsExactly(APP_C); 845 846 // Then expire APP_A and APP_C 847 moveTimeForwardAndDispatch(SHORT_DURATION); 848 849 // Verify only APP_C is failed because explicit health checks was not supported for APP_A 850 assertThat(observer.mMitigatedPackages).containsExactly(APP_C); 851 } 852 853 /** 854 * Tests failure when health check duration is different from package observation duration 855 * Failure is also notified only once. 856 */ 857 @Test testExplicitHealthCheckFailureBeforeExpiry()858 public void testExplicitHealthCheckFailureBeforeExpiry() throws Exception { 859 TestController controller = new TestController(); 860 PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); 861 TestObserver observer = new TestObserver(OBSERVER_NAME_1, 862 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 863 864 // Start observing with explicit health checks for APP_A and 865 // package observation duration == LONG_DURATION 866 // health check duration == SHORT_DURATION (set by default in the TestController) 867 controller.setSupportedPackages(Arrays.asList(APP_A)); 868 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION); 869 870 // Then APP_A has exceeded health check duration 871 moveTimeForwardAndDispatch(SHORT_DURATION); 872 873 // Verify that health check is failed 874 assertThat(observer.mMitigatedPackages).containsExactly(APP_A); 875 876 // Clear failed packages and forward time to expire the observation duration 877 observer.mMitigatedPackages.clear(); 878 moveTimeForwardAndDispatch(LONG_DURATION); 879 880 // Verify that health check failure is not notified again 881 assertThat(observer.mMitigatedPackages).isEmpty(); 882 } 883 884 /** 885 * Tests failure when health check duration is different from package observation duration 886 * Failure is also notified only once. 887 */ 888 @Test testExplicitHealthCheckFailureAfterExpiry()889 public void testExplicitHealthCheckFailureAfterExpiry() { 890 TestController controller = new TestController(); 891 PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); 892 TestObserver observer = new TestObserver(OBSERVER_NAME_1, 893 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 894 895 // Start observing with explicit health checks for APP_A and 896 // package observation duration == SHORT_DURATION / 2 897 // health check duration == SHORT_DURATION (set by default in the TestController) 898 controller.setSupportedPackages(Arrays.asList(APP_A)); 899 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION / 2); 900 901 // Forward time to expire the observation duration 902 moveTimeForwardAndDispatch(SHORT_DURATION / 2); 903 904 // Verify that health check is failed 905 assertThat(observer.mMitigatedPackages).containsExactly(APP_A); 906 907 // Clear failed packages and forward time to expire the health check duration 908 observer.mMitigatedPackages.clear(); 909 moveTimeForwardAndDispatch(SHORT_DURATION); 910 911 // Verify that health check failure is not notified again 912 assertThat(observer.mMitigatedPackages).isEmpty(); 913 } 914 915 /** Tests {@link MonitoredPackage} health check state transitions. */ 916 @Test testPackageHealthCheckStateTransitions()917 public void testPackageHealthCheckStateTransitions() { 918 TestController controller = new TestController(); 919 PackageWatchdog wd = createWatchdog(controller, true /* withPackagesReady */); 920 MonitoredPackage m1 = wd.newMonitoredPackage(APP_A, LONG_DURATION, 921 false /* hasPassedHealthCheck */); 922 MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false); 923 MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false); 924 MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true, 925 new LongArrayQueue()); 926 927 // Verify transition: inactive -> active -> passed 928 // Verify initially inactive 929 assertThat(m1.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE); 930 // Verify still inactive, until we #setHealthCheckActiveLocked 931 assertThat(m1.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.INACTIVE); 932 // Verify now active 933 assertThat(m1.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo( 934 HealthCheckState.ACTIVE); 935 // Verify now passed 936 assertThat(m1.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.PASSED); 937 938 // Verify transition: inactive -> active -> failed 939 // Verify initially inactive 940 assertThat(m2.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE); 941 // Verify now active 942 assertThat(m2.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo( 943 HealthCheckState.ACTIVE); 944 // Verify now failed 945 assertThat(m2.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.FAILED); 946 947 // Verify transition: inactive -> failed 948 // Verify initially inactive 949 assertThat(m3.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE); 950 // Verify now failed because package expired 951 assertThat(m3.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.FAILED); 952 // Verify remains failed even when asked to pass 953 assertThat(m3.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.FAILED); 954 955 // Verify transition: passed 956 assertThat(m4.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.PASSED); 957 // Verify remains passed even if health check fails 958 assertThat(m4.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.PASSED); 959 // Verify remains passed even if package expires 960 assertThat(m4.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.PASSED); 961 } 962 963 @Test testNetworkStackFailure()964 public void testNetworkStackFailure() { 965 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 966 final PackageWatchdog wd = createWatchdog(); 967 968 // Start observing with failure handling 969 TestObserver observer = new TestObserver(OBSERVER_NAME_1, 970 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 971 wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION); 972 973 // Notify of NetworkStack failure 974 mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A); 975 976 // Run handler so package failures are dispatched to observers 977 mTestLooper.dispatchAll(); 978 979 // Verify the NetworkStack observer is notified 980 assertThat(observer.mMitigatedPackages).containsExactly(APP_A); 981 } 982 983 @Test testNetworkStackFailureRecoverabilityDetection()984 public void testNetworkStackFailureRecoverabilityDetection() { 985 final PackageWatchdog wd = createWatchdog(); 986 987 // Start observing with failure handling 988 TestObserver observer = new TestObserver(OBSERVER_NAME_1, 989 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); 990 wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION); 991 992 // Notify of NetworkStack failure 993 mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A); 994 995 // Run handler so package failures are dispatched to observers 996 mTestLooper.dispatchAll(); 997 998 // Verify the NetworkStack observer is notified 999 assertThat(observer.mMitigatedPackages).isEmpty(); 1000 } 1001 1002 /** Test default values are used when device property is invalid. */ 1003 @Test testInvalidConfig_watchdogTriggerFailureCount()1004 public void testInvalidConfig_watchdogTriggerFailureCount() { 1005 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1006 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1007 Integer.toString(-1), /*makeDefault*/false); 1008 PackageWatchdog watchdog = createWatchdog(); 1009 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1010 1011 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); 1012 // Fail APP_A below the threshold which should not trigger package failures 1013 for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { 1014 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1015 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1016 } 1017 mTestLooper.dispatchAll(); 1018 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 1019 1020 // One more to trigger the package failure 1021 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1022 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1023 mTestLooper.dispatchAll(); 1024 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); 1025 } 1026 1027 /** Test default values are used when device property is invalid. */ 1028 @Test testInvalidConfig_watchdogTriggerDurationMillis()1029 public void testInvalidConfig_watchdogTriggerDurationMillis() { 1030 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1031 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1032 Integer.toString(2), /*makeDefault*/false); 1033 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1034 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 1035 Integer.toString(-1), /*makeDefault*/false); 1036 PackageWatchdog watchdog = createWatchdog(); 1037 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1038 1039 watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE); 1040 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1041 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1042 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1); 1043 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1044 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1045 mTestLooper.dispatchAll(); 1046 1047 // We shouldn't receive APP_A since the interval of 2 failures is greater than 1048 // DEFAULT_TRIGGER_FAILURE_DURATION_MS. 1049 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 1050 1051 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), 1052 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1053 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1); 1054 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), 1055 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1056 mTestLooper.dispatchAll(); 1057 1058 // We should receive APP_B since the interval of 2 failures is less than 1059 // DEFAULT_TRIGGER_FAILURE_DURATION_MS. 1060 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_B); 1061 } 1062 1063 /** 1064 * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered 1065 * an invalid durationMs. 1066 */ 1067 @Test testInvalidMonitoringDuration_beforeExpiry()1068 public void testInvalidMonitoringDuration_beforeExpiry() { 1069 PackageWatchdog watchdog = createWatchdog(); 1070 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1071 1072 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1); 1073 // Note: Don't move too close to the expiration time otherwise the handler will be thrashed 1074 // by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very 1075 // small timeouts. 1076 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100); 1077 raiseFatalFailureAndDispatch(watchdog, 1078 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1079 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1080 1081 // We should receive APP_A since the observer hasn't expired 1082 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); 1083 } 1084 1085 /** 1086 * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered 1087 * an invalid durationMs. 1088 */ 1089 @Test testInvalidMonitoringDuration_afterExpiry()1090 public void testInvalidMonitoringDuration_afterExpiry() { 1091 PackageWatchdog watchdog = createWatchdog(); 1092 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1093 1094 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1); 1095 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1); 1096 raiseFatalFailureAndDispatch(watchdog, 1097 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1098 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1099 1100 // We should receive nothing since the observer has expired 1101 assertThat(observer.mHealthCheckFailedPackages).isEmpty(); 1102 } 1103 1104 /** Test we are notified when enough failures are triggered within any window. */ 1105 @Test testFailureTriggerWindow()1106 public void testFailureTriggerWindow() { 1107 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1108 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1109 Integer.toString(3), /*makeDefault*/false); 1110 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1111 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 1112 Integer.toString(1000), /*makeDefault*/false); 1113 PackageWatchdog watchdog = createWatchdog(); 1114 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1115 1116 watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE); 1117 // Raise 2 failures at t=0 and t=900 respectively 1118 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1119 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1120 moveTimeForwardAndDispatch(900); 1121 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1122 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1123 1124 // Raise 2 failures at t=1100 1125 moveTimeForwardAndDispatch(200); 1126 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1127 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1128 watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), 1129 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1130 mTestLooper.dispatchAll(); 1131 1132 // We should receive APP_A since there are 3 failures within 1000ms window 1133 assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); 1134 } 1135 1136 /** Test that observers execute correctly for failures reasons that go through thresholding. */ 1137 @Test testNonImmediateFailureReasons()1138 public void testNonImmediateFailureReasons() { 1139 PackageWatchdog watchdog = createWatchdog(); 1140 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 1141 TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); 1142 1143 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 1144 watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION); 1145 1146 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1147 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH); 1148 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, 1149 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); 1150 1151 assertThat(observer1.getLastFailureReason()).isEqualTo( 1152 PackageWatchdog.FAILURE_REASON_APP_CRASH); 1153 assertThat(observer2.getLastFailureReason()).isEqualTo( 1154 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); 1155 } 1156 1157 /** Test that observers execute correctly for failures reasons that skip thresholding. */ 1158 @Test testImmediateFailures()1159 public void testImmediateFailures() { 1160 PackageWatchdog watchdog = createWatchdog(); 1161 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 1162 1163 watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); 1164 1165 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1166 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); 1167 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, 1168 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); 1169 1170 assertThat(observer1.mMitigatedPackages).containsExactly(APP_A, APP_B); 1171 } 1172 1173 /** 1174 * Test that a persistent observer will mitigate failures if it wishes to observe a package. 1175 */ 1176 @Test testPersistentObserverWatchesPackage()1177 public void testPersistentObserverWatchesPackage() { 1178 PackageWatchdog watchdog = createWatchdog(); 1179 TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1); 1180 persistentObserver.setPersistent(true); 1181 persistentObserver.setMayObservePackages(true); 1182 1183 watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION); 1184 1185 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1186 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1187 assertThat(persistentObserver.mHealthCheckFailedPackages).containsExactly(APP_A); 1188 } 1189 1190 /** 1191 * Test that a persistent observer will not mitigate failures if it does not wish to observe 1192 * a given package. 1193 */ 1194 @Test testPersistentObserverDoesNotWatchPackage()1195 public void testPersistentObserverDoesNotWatchPackage() { 1196 PackageWatchdog watchdog = createWatchdog(); 1197 TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1); 1198 persistentObserver.setPersistent(true); 1199 persistentObserver.setMayObservePackages(false); 1200 1201 watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION); 1202 1203 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1204 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1205 assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty(); 1206 } 1207 1208 1209 /** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */ 1210 @Test testBootLoopDetection_meetsThreshold()1211 public void testBootLoopDetection_meetsThreshold() { 1212 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 1213 PackageWatchdog watchdog = createWatchdog(); 1214 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); 1215 watchdog.registerHealthObserver(bootObserver); 1216 for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { 1217 watchdog.noteBoot(); 1218 } 1219 assertThat(bootObserver.mitigatedBootLoop()).isTrue(); 1220 } 1221 1222 @Test testBootLoopDetection_meetsThresholdRecoverability()1223 public void testBootLoopDetection_meetsThresholdRecoverability() { 1224 PackageWatchdog watchdog = createWatchdog(); 1225 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); 1226 watchdog.registerHealthObserver(bootObserver); 1227 for (int i = 0; i < 15; i++) { 1228 watchdog.noteBoot(); 1229 } 1230 assertThat(bootObserver.mitigatedBootLoop()).isTrue(); 1231 } 1232 1233 /** 1234 * Ensure that boot loop mitigation is not done when the number of boots does not meet the 1235 * threshold. 1236 */ 1237 @Test testBootLoopDetection_doesNotMeetThreshold()1238 public void testBootLoopDetection_doesNotMeetThreshold() { 1239 PackageWatchdog watchdog = createWatchdog(); 1240 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); 1241 watchdog.registerHealthObserver(bootObserver); 1242 for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) { 1243 watchdog.noteBoot(); 1244 } 1245 assertThat(bootObserver.mitigatedBootLoop()).isFalse(); 1246 } 1247 1248 /** 1249 * Ensure that boot loop mitigation is not done when the number of boots does not meet the 1250 * threshold. 1251 */ 1252 @Test testBootLoopDetection_doesNotMeetThresholdRecoverabilityLowImpact()1253 public void testBootLoopDetection_doesNotMeetThresholdRecoverabilityLowImpact() { 1254 PackageWatchdog watchdog = createWatchdog(); 1255 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1, 1256 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 1257 watchdog.registerHealthObserver(bootObserver); 1258 for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) { 1259 watchdog.noteBoot(); 1260 } 1261 assertThat(bootObserver.mitigatedBootLoop()).isFalse(); 1262 } 1263 1264 /** 1265 * Ensure that boot loop mitigation is done for the observer with the lowest user impact 1266 */ 1267 @Test testBootLoopMitigationDoneForLowestUserImpact()1268 public void testBootLoopMitigationDoneForLowestUserImpact() { 1269 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 1270 PackageWatchdog watchdog = createWatchdog(); 1271 TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1); 1272 bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 1273 TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2); 1274 bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 1275 watchdog.registerHealthObserver(bootObserver1); 1276 watchdog.registerHealthObserver(bootObserver2); 1277 for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { 1278 watchdog.noteBoot(); 1279 } 1280 assertThat(bootObserver1.mitigatedBootLoop()).isTrue(); 1281 assertThat(bootObserver2.mitigatedBootLoop()).isFalse(); 1282 } 1283 1284 @Test testBootLoopMitigationDoneForLowestUserImpactRecoverability()1285 public void testBootLoopMitigationDoneForLowestUserImpactRecoverability() { 1286 PackageWatchdog watchdog = createWatchdog(); 1287 TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1); 1288 bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); 1289 TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2); 1290 bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 1291 watchdog.registerHealthObserver(bootObserver1); 1292 watchdog.registerHealthObserver(bootObserver2); 1293 for (int i = 0; i < 15; i++) { 1294 watchdog.noteBoot(); 1295 } 1296 assertThat(bootObserver1.mitigatedBootLoop()).isTrue(); 1297 assertThat(bootObserver2.mitigatedBootLoop()).isFalse(); 1298 } 1299 1300 /** 1301 * Ensure that the correct mitigation counts are sent to the boot loop observer. 1302 */ 1303 @Test testMultipleBootLoopMitigation()1304 public void testMultipleBootLoopMitigation() { 1305 mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); 1306 PackageWatchdog watchdog = createWatchdog(); 1307 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); 1308 watchdog.registerHealthObserver(bootObserver); 1309 for (int i = 0; i < 4; i++) { 1310 for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { 1311 watchdog.noteBoot(); 1312 } 1313 } 1314 1315 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); 1316 1317 for (int i = 0; i < 4; i++) { 1318 for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { 1319 watchdog.noteBoot(); 1320 } 1321 } 1322 1323 assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4)); 1324 } 1325 1326 @Test testMultipleBootLoopMitigationRecoverabilityLowImpact()1327 public void testMultipleBootLoopMitigationRecoverabilityLowImpact() { 1328 PackageWatchdog watchdog = createWatchdog(); 1329 TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1, 1330 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30); 1331 watchdog.registerHealthObserver(bootObserver); 1332 for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) { 1333 watchdog.noteBoot(); 1334 } 1335 for (int i = 0; i < 4; i++) { 1336 watchdog.noteBoot(); 1337 } 1338 1339 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); 1340 1341 for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) { 1342 watchdog.noteBoot(); 1343 } 1344 for (int i = 0; i < 4; i++) { 1345 watchdog.noteBoot(); 1346 } 1347 1348 assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4)); 1349 } 1350 1351 /** 1352 * Ensure that passing a null list of failed packages does not cause any mitigation logic to 1353 * execute. 1354 */ 1355 @Test testNullFailedPackagesList()1356 public void testNullFailedPackagesList() { 1357 PackageWatchdog watchdog = createWatchdog(); 1358 TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); 1359 watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION); 1360 1361 raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH); 1362 assertThat(observer1.mMitigatedPackages).isEmpty(); 1363 } 1364 1365 /** 1366 * Test to verify that Package Watchdog syncs health check requests with the controller 1367 * correctly, and that the requests are only synced when the set of observed packages 1368 * changes. 1369 */ 1370 @Test testSyncHealthCheckRequests()1371 public void testSyncHealthCheckRequests() { 1372 TestController testController = spy(TestController.class); 1373 testController.setSupportedPackages(List.of(APP_A, APP_B, APP_C)); 1374 PackageWatchdog watchdog = createWatchdog(testController, true); 1375 1376 TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1); 1377 watchdog.registerHealthObserver(testObserver1); 1378 watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION); 1379 mTestLooper.dispatchAll(); 1380 1381 TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2); 1382 watchdog.registerHealthObserver(testObserver2); 1383 watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION); 1384 mTestLooper.dispatchAll(); 1385 1386 TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3); 1387 watchdog.registerHealthObserver(testObserver3); 1388 watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION); 1389 mTestLooper.dispatchAll(); 1390 1391 watchdog.unregisterHealthObserver(testObserver1); 1392 mTestLooper.dispatchAll(); 1393 1394 watchdog.unregisterHealthObserver(testObserver2); 1395 mTestLooper.dispatchAll(); 1396 1397 watchdog.unregisterHealthObserver(testObserver3); 1398 mTestLooper.dispatchAll(); 1399 1400 List<Set> expectedSyncRequests = List.of( 1401 Set.of(), 1402 Set.of(APP_A), 1403 Set.of(APP_A, APP_B), 1404 Set.of(APP_A, APP_B, APP_C), 1405 Set.of(APP_B, APP_C), 1406 Set.of(APP_C), 1407 Set.of() 1408 ); 1409 assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests); 1410 } 1411 1412 /** 1413 * Ensure that the failure history of a package is preserved when making duplicate calls to 1414 * observe the package. 1415 */ 1416 @Test testFailureHistoryIsPreserved()1417 public void testFailureHistoryIsPreserved() { 1418 PackageWatchdog watchdog = createWatchdog(); 1419 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1420 watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION); 1421 for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { 1422 watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), 1423 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1424 } 1425 mTestLooper.dispatchAll(); 1426 assertThat(observer.mMitigatedPackages).isEmpty(); 1427 watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION); 1428 watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), 1429 PackageWatchdog.FAILURE_REASON_UNKNOWN); 1430 mTestLooper.dispatchAll(); 1431 assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A)); 1432 } 1433 1434 /** 1435 * Ensure that the sliding window logic results in the correct mitigation count being sent to 1436 * an observer. 1437 */ 1438 @Test testMitigationSlidingWindow()1439 public void testMitigationSlidingWindow() { 1440 PackageWatchdog watchdog = createWatchdog(); 1441 TestObserver observer = new TestObserver(OBSERVER_NAME_1); 1442 watchdog.startObservingHealth(observer, List.of(APP_A), 1443 PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2); 1444 1445 1446 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1447 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1448 1449 moveTimeForwardAndDispatch(TimeUnit.MINUTES.toMillis(10)); 1450 1451 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1452 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1453 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1454 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1455 1456 moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS 1457 - TimeUnit.MINUTES.toMillis(1)); 1458 1459 // The first failure will be outside the threshold. 1460 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1461 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1462 1463 moveTimeForwardAndDispatch(TimeUnit.MINUTES.toMillis(20)); 1464 1465 // The next 2 failures will also be outside the threshold. 1466 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1467 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1468 raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, 1469 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); 1470 1471 assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3)); 1472 } 1473 1474 @Test testNormalizingMitigationCalls()1475 public void testNormalizingMitigationCalls() { 1476 PackageWatchdog watchdog = createWatchdog(); 1477 1478 LongArrayQueue mitigationCalls = new LongArrayQueue(); 1479 mitigationCalls.addLast(1000); 1480 mitigationCalls.addLast(2000); 1481 mitigationCalls.addLast(3000); 1482 1483 MonitoredPackage pkg = watchdog.newMonitoredPackage( 1484 "test", 123, 456, true, mitigationCalls); 1485 1486 // Make current system uptime 10000ms. 1487 moveTimeForwardAndDispatch(9999); 1488 1489 LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls(); 1490 1491 assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size()); 1492 1493 for (int i = 0; i < mitigationCalls.size(); i++) { 1494 assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000); 1495 } 1496 } 1497 1498 /** 1499 * Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist 1500 * across reboots. 1501 */ 1502 @Test testWritingAndReadingMonitoredPackage()1503 public void testWritingAndReadingMonitoredPackage() throws Exception { 1504 PackageWatchdog watchdog = createWatchdog(); 1505 1506 LongArrayQueue mitigationCalls = new LongArrayQueue(); 1507 mitigationCalls.addLast(1000); 1508 mitigationCalls.addLast(2000); 1509 mitigationCalls.addLast(3000); 1510 MonitoredPackage writePkg = watchdog.newMonitoredPackage( 1511 "test.package", 1000, 2000, true, mitigationCalls); 1512 1513 // Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation 1514 // calls will each be reduced by 4000. 1515 moveTimeForwardAndDispatch(3999); 1516 LongArrayQueue expectedCalls = new LongArrayQueue(); 1517 expectedCalls.addLast(-3000); 1518 expectedCalls.addLast(-2000); 1519 expectedCalls.addLast(-1000); 1520 MonitoredPackage expectedPkg = watchdog.newMonitoredPackage( 1521 "test.package", 1000, 2000, true, expectedCalls); 1522 1523 // Write the package 1524 File tmpFile = File.createTempFile("package-watchdog-test", ".xml"); 1525 AtomicFile testFile = new AtomicFile(tmpFile); 1526 FileOutputStream stream = testFile.startWrite(); 1527 TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream); 1528 outputSerializer.startDocument(null, true); 1529 writePkg.writeLocked(outputSerializer); 1530 outputSerializer.endDocument(); 1531 testFile.finishWrite(stream); 1532 1533 // Read the package 1534 TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead()); 1535 XmlUtils.beginDocument(parser, "package"); 1536 MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser); 1537 1538 assertTrue(readPkg.isEqualTo(expectedPkg)); 1539 } 1540 1541 /** 1542 * Ensure that a {@link ObserverInternal} may be correctly written and read in order to persist 1543 * across reboots. 1544 */ 1545 @Test 1546 @SuppressWarnings("GuardedBy") testWritingAndReadingObserverInternalRecoverability()1547 public void testWritingAndReadingObserverInternalRecoverability() throws Exception { 1548 PackageWatchdog watchdog = createWatchdog(); 1549 1550 LongArrayQueue mitigationCalls = new LongArrayQueue(); 1551 mitigationCalls.addLast(1000); 1552 mitigationCalls.addLast(2000); 1553 mitigationCalls.addLast(3000); 1554 MonitoredPackage writePkg = watchdog.newMonitoredPackage( 1555 "test.package", 1000, 2000, true, mitigationCalls); 1556 final int bootMitigationCount = 4; 1557 ObserverInternal writeObserver = new ObserverInternal("test", List.of(writePkg), 1558 bootMitigationCount); 1559 1560 // Write the observer 1561 File tmpFile = File.createTempFile("observer-watchdog-test", ".xml"); 1562 AtomicFile testFile = new AtomicFile(tmpFile); 1563 FileOutputStream stream = testFile.startWrite(); 1564 TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream); 1565 outputSerializer.startDocument(null, true); 1566 writeObserver.writeLocked(outputSerializer); 1567 outputSerializer.endDocument(); 1568 testFile.finishWrite(stream); 1569 1570 // Read the observer 1571 TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead()); 1572 XmlUtils.beginDocument(parser, "observer"); 1573 ObserverInternal readObserver = ObserverInternal.read(parser, watchdog); 1574 1575 assertThat(readObserver.name).isEqualTo(writeObserver.name); 1576 assertThat(readObserver.getBootMitigationCount()).isEqualTo(bootMitigationCount); 1577 } 1578 1579 /** 1580 * Ensure that boot mitigation counts may be correctly written and read as metadata 1581 * in order to persist across reboots. 1582 */ 1583 @Test 1584 @SuppressWarnings("GuardedBy") testWritingAndReadingMetadataBootMitigationCountRecoverability()1585 public void testWritingAndReadingMetadataBootMitigationCountRecoverability() throws Exception { 1586 PackageWatchdog watchdog = createWatchdog(); 1587 String filePath = InstrumentationRegistry.getContext().getFilesDir().toString() 1588 + "metadata_file.txt"; 1589 1590 ObserverInternal observer1 = new ObserverInternal("test1", List.of(), 1); 1591 ObserverInternal observer2 = new ObserverInternal("test2", List.of(), 2); 1592 watchdog.registerObserverInternal(observer1); 1593 watchdog.registerObserverInternal(observer2); 1594 1595 mSpyBootThreshold = spy(watchdog.new BootThreshold( 1596 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, 1597 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS)); 1598 1599 watchdog.saveAllObserversBootMitigationCountToMetadata(filePath); 1600 1601 observer1.setBootMitigationCount(0); 1602 observer2.setBootMitigationCount(0); 1603 assertThat(observer1.getBootMitigationCount()).isEqualTo(0); 1604 assertThat(observer2.getBootMitigationCount()).isEqualTo(0); 1605 1606 mSpyBootThreshold.readAllObserversBootMitigationCountIfNecessary(filePath); 1607 1608 assertThat(observer1.getBootMitigationCount()).isEqualTo(1); 1609 assertThat(observer2.getBootMitigationCount()).isEqualTo(2); 1610 } 1611 1612 /** 1613 * Tests device config changes are propagated correctly. 1614 */ 1615 @Test testDeviceConfigChange_explicitHealthCheckEnabled()1616 public void testDeviceConfigChange_explicitHealthCheckEnabled() throws Exception { 1617 TestController controller = new TestController(); 1618 PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); 1619 assertThat(controller.mIsEnabled).isTrue(); 1620 1621 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1622 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 1623 Boolean.toString(false), /*makeDefault*/false); 1624 retry(() -> !controller.mIsEnabled); 1625 assertThat(controller.mIsEnabled).isFalse(); 1626 } 1627 1628 /** 1629 * Tests device config changes are propagated correctly. 1630 */ 1631 @Test testDeviceConfigChange_triggerFailureCount()1632 public void testDeviceConfigChange_triggerFailureCount() throws Exception { 1633 PackageWatchdog watchdog = createWatchdog(); 1634 1635 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1636 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1637 Integer.toString(777), false); 1638 retry(() -> watchdog.getTriggerFailureCount() == 777); 1639 assertThat(watchdog.getTriggerFailureCount()).isEqualTo(777); 1640 1641 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1642 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1643 Integer.toString(0), false); 1644 retry(() -> watchdog.getTriggerFailureCount() 1645 == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); 1646 assertThat(watchdog.getTriggerFailureCount()).isEqualTo( 1647 PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); 1648 } 1649 1650 /** 1651 * Tests device config changes are propagated correctly. 1652 */ 1653 @Test testDeviceConfigChange_triggerFailureDurationMs()1654 public void testDeviceConfigChange_triggerFailureDurationMs() throws Exception { 1655 PackageWatchdog watchdog = createWatchdog(); 1656 1657 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1658 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 1659 Integer.toString(888), false); 1660 retry(() -> watchdog.getTriggerFailureDurationMs() == 888); 1661 assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo(888); 1662 1663 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1664 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 1665 Integer.toString(0), false); 1666 retry(() -> watchdog.getTriggerFailureDurationMs() 1667 == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); 1668 assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo( 1669 PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); 1670 } 1671 adoptShellPermissions(String... permissions)1672 private void adoptShellPermissions(String... permissions) { 1673 InstrumentationRegistry 1674 .getInstrumentation() 1675 .getUiAutomation() 1676 .adoptShellPermissionIdentity(permissions); 1677 } 1678 dropShellPermissions()1679 private void dropShellPermissions() { 1680 InstrumentationRegistry 1681 .getInstrumentation() 1682 .getUiAutomation() 1683 .dropShellPermissionIdentity(); 1684 } 1685 setExplicitHealthCheckEnabled(boolean enabled)1686 private void setExplicitHealthCheckEnabled(boolean enabled) { 1687 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 1688 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 1689 Boolean.toString(enabled), /*makeDefault*/false); 1690 // Call updateConfigs() so device config changes take effect immediately 1691 for (PackageWatchdog watchdog : mAllocatedWatchdogs) { 1692 watchdog.updateConfigs(); 1693 } 1694 } 1695 moveTimeForwardAndDispatch(long milliSeconds)1696 private void moveTimeForwardAndDispatch(long milliSeconds) { 1697 // Exhaust all due runnables now which shouldn't be executed after time-leap 1698 mTestLooper.dispatchAll(); 1699 mTestClock.moveTimeForward(milliSeconds); 1700 mTestLooper.moveTimeForward(milliSeconds); 1701 mTestLooper.dispatchAll(); 1702 } 1703 1704 /** Trigger package failures above the threshold. */ raiseFatalFailureAndDispatch(PackageWatchdog watchdog, List<VersionedPackage> packages, int failureReason)1705 private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog, 1706 List<VersionedPackage> packages, int failureReason) { 1707 long triggerFailureCount = watchdog.getTriggerFailureCount(); 1708 if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK 1709 || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { 1710 triggerFailureCount = 1; 1711 } 1712 for (int i = 0; i < triggerFailureCount; i++) { 1713 watchdog.onPackageFailure(packages, failureReason); 1714 } 1715 mTestLooper.dispatchAll(); 1716 if (Flags.recoverabilityDetection()) { 1717 moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS); 1718 } 1719 } 1720 createWatchdog()1721 private PackageWatchdog createWatchdog() { 1722 return createWatchdog(new TestController(), true /* withPackagesReady */); 1723 } 1724 createWatchdog(TestController controller, boolean withPackagesReady)1725 private PackageWatchdog createWatchdog(TestController controller, boolean withPackagesReady) { 1726 AtomicFile policyFile = 1727 new AtomicFile(new File(mSpyContext.getFilesDir(), "package-watchdog.xml")); 1728 Handler handler = new Handler(mTestLooper.getLooper()); 1729 PackageWatchdog watchdog = 1730 new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller, 1731 mConnectivityModuleConnector, mTestClock); 1732 mockCrashRecoveryProperties(watchdog); 1733 1734 // Verify controller is not automatically started 1735 assertThat(controller.mIsEnabled).isFalse(); 1736 if (withPackagesReady) { 1737 // Only capture the NetworkStack callback for the latest registered watchdog 1738 reset(mConnectivityModuleConnector); 1739 watchdog.onPackagesReady(); 1740 // Verify controller by default is started when packages are ready 1741 assertThat(controller.mIsEnabled).isTrue(); 1742 1743 verify(mConnectivityModuleConnector).registerHealthListener( 1744 mConnectivityModuleCallbackCaptor.capture()); 1745 } 1746 mAllocatedWatchdogs.add(watchdog); 1747 return watchdog; 1748 } 1749 1750 // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions mockCrashRecoveryProperties(PackageWatchdog watchdog)1751 private void mockCrashRecoveryProperties(PackageWatchdog watchdog) { 1752 mCrashRecoveryPropertiesMap = new HashMap<>(); 1753 1754 try { 1755 mSpyBootThreshold = spy(watchdog.new BootThreshold( 1756 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, 1757 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS)); 1758 1759 doAnswer((Answer<Integer>) invocationOnMock -> { 1760 String storedValue = mCrashRecoveryPropertiesMap 1761 .getOrDefault("crashrecovery.rescue_boot_count", "0"); 1762 return Integer.parseInt(storedValue); 1763 }).when(mSpyBootThreshold).getCount(); 1764 doAnswer((Answer<Void>) invocationOnMock -> { 1765 int count = invocationOnMock.getArgument(0); 1766 mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count", 1767 Integer.toString(count)); 1768 return null; 1769 }).when(mSpyBootThreshold).setCount(anyInt()); 1770 1771 doAnswer((Answer<Integer>) invocationOnMock -> { 1772 String storedValue = mCrashRecoveryPropertiesMap 1773 .getOrDefault("crashrecovery.boot_mitigation_count", "0"); 1774 return Integer.parseInt(storedValue); 1775 }).when(mSpyBootThreshold).getMitigationCount(); 1776 doAnswer((Answer<Void>) invocationOnMock -> { 1777 int count = invocationOnMock.getArgument(0); 1778 mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count", 1779 Integer.toString(count)); 1780 return null; 1781 }).when(mSpyBootThreshold).setMitigationCount(anyInt()); 1782 1783 doAnswer((Answer<Long>) invocationOnMock -> { 1784 String storedValue = mCrashRecoveryPropertiesMap 1785 .getOrDefault("crashrecovery.rescue_boot_start", "0"); 1786 return Long.parseLong(storedValue); 1787 }).when(mSpyBootThreshold).getStart(); 1788 doAnswer((Answer<Void>) invocationOnMock -> { 1789 long count = invocationOnMock.getArgument(0); 1790 mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start", 1791 Long.toString(count)); 1792 return null; 1793 }).when(mSpyBootThreshold).setStart(anyLong()); 1794 1795 doAnswer((Answer<Long>) invocationOnMock -> { 1796 String storedValue = mCrashRecoveryPropertiesMap 1797 .getOrDefault("crashrecovery.boot_mitigation_start", "0"); 1798 return Long.parseLong(storedValue); 1799 }).when(mSpyBootThreshold).getMitigationStart(); 1800 doAnswer((Answer<Void>) invocationOnMock -> { 1801 long count = invocationOnMock.getArgument(0); 1802 mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start", 1803 Long.toString(count)); 1804 return null; 1805 }).when(mSpyBootThreshold).setMitigationStart(anyLong()); 1806 1807 Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold"); 1808 mBootThresholdField.setAccessible(true); 1809 mBootThresholdField.set(watchdog, mSpyBootThreshold); 1810 } catch (Exception e) { 1811 // tests will fail, just printing the error 1812 System.out.println("Error detected while spying BootThreshold" + e.getMessage()); 1813 } 1814 } 1815 1816 private static class TestObserver implements PackageHealthObserver { 1817 private final String mName; 1818 private int mImpact; 1819 private int mLastFailureReason; 1820 private boolean mIsPersistent = false; 1821 private boolean mMayObservePackages = false; 1822 private boolean mMitigatedBootLoop = false; 1823 final List<String> mHealthCheckFailedPackages = new ArrayList<>(); 1824 final List<String> mMitigatedPackages = new ArrayList<>(); 1825 final List<Integer> mMitigationCounts = new ArrayList<>(); 1826 final List<Integer> mBootMitigationCounts = new ArrayList<>(); 1827 TestObserver(String name)1828 TestObserver(String name) { 1829 mName = name; 1830 mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; 1831 } 1832 TestObserver(String name, int impact)1833 TestObserver(String name, int impact) { 1834 mName = name; 1835 mImpact = impact; 1836 } 1837 onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason, int mitigationCount)1838 public int onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason, 1839 int mitigationCount) { 1840 mHealthCheckFailedPackages.add(versionedPackage.getPackageName()); 1841 return mImpact; 1842 } 1843 execute(VersionedPackage versionedPackage, int failureReason, int mitigationCount)1844 public boolean execute(VersionedPackage versionedPackage, int failureReason, 1845 int mitigationCount) { 1846 mMitigatedPackages.add(versionedPackage.getPackageName()); 1847 mMitigationCounts.add(mitigationCount); 1848 mLastFailureReason = failureReason; 1849 return true; 1850 } 1851 getName()1852 public String getName() { 1853 return mName; 1854 } 1855 isPersistent()1856 public boolean isPersistent() { 1857 return mIsPersistent; 1858 } 1859 mayObservePackage(String packageName)1860 public boolean mayObservePackage(String packageName) { 1861 return mMayObservePackages; 1862 } 1863 onBootLoop(int level)1864 public int onBootLoop(int level) { 1865 return mImpact; 1866 } 1867 executeBootLoopMitigation(int level)1868 public boolean executeBootLoopMitigation(int level) { 1869 mMitigatedBootLoop = true; 1870 mBootMitigationCounts.add(level); 1871 return true; 1872 } 1873 mitigatedBootLoop()1874 public boolean mitigatedBootLoop() { 1875 return mMitigatedBootLoop; 1876 } 1877 getLastFailureReason()1878 public int getLastFailureReason() { 1879 return mLastFailureReason; 1880 } 1881 setPersistent(boolean persistent)1882 public void setPersistent(boolean persistent) { 1883 mIsPersistent = persistent; 1884 } 1885 setImpact(int impact)1886 public void setImpact(int impact) { 1887 mImpact = impact; 1888 } 1889 setMayObservePackages(boolean mayObservePackages)1890 public void setMayObservePackages(boolean mayObservePackages) { 1891 mMayObservePackages = mayObservePackages; 1892 } 1893 } 1894 1895 private static class TestController extends ExplicitHealthCheckController { TestController()1896 TestController() { 1897 super(null /* controller */); 1898 } 1899 1900 private boolean mIsEnabled; 1901 private List<String> mSupportedPackages = new ArrayList<>(); 1902 private List<String> mRequestedPackages = new ArrayList<>(); 1903 private Consumer<String> mPassedConsumer; 1904 private Consumer<List<PackageConfig>> mSupportedConsumer; 1905 private Runnable mNotifySyncRunnable; 1906 private List<Set> mSyncRequests = new ArrayList<>(); 1907 1908 @Override setEnabled(boolean enabled)1909 public void setEnabled(boolean enabled) { 1910 mIsEnabled = enabled; 1911 if (!mIsEnabled) { 1912 mSupportedPackages.clear(); 1913 } 1914 } 1915 1916 @Override setCallbacks(Consumer<String> passedConsumer, Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable)1917 public void setCallbacks(Consumer<String> passedConsumer, 1918 Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable) { 1919 mPassedConsumer = passedConsumer; 1920 mSupportedConsumer = supportedConsumer; 1921 mNotifySyncRunnable = notifySyncRunnable; 1922 } 1923 1924 @Override syncRequests(Set<String> packages)1925 public void syncRequests(Set<String> packages) { 1926 mSyncRequests.add(packages); 1927 mRequestedPackages.clear(); 1928 if (mIsEnabled) { 1929 packages.retainAll(mSupportedPackages); 1930 mRequestedPackages.addAll(packages); 1931 List<PackageConfig> packageConfigs = new ArrayList<>(); 1932 for (String packageName: packages) { 1933 packageConfigs.add(new PackageConfig(packageName, SHORT_DURATION)); 1934 } 1935 mSupportedConsumer.accept(packageConfigs); 1936 } else { 1937 mSupportedConsumer.accept(Collections.emptyList()); 1938 } 1939 } 1940 setSupportedPackages(List<String> packages)1941 public void setSupportedPackages(List<String> packages) { 1942 mSupportedPackages.clear(); 1943 mSupportedPackages.addAll(packages); 1944 } 1945 setPackagePassed(String packageName)1946 public void setPackagePassed(String packageName) { 1947 mPassedConsumer.accept(packageName); 1948 } 1949 getRequestedPackages()1950 public List<String> getRequestedPackages() { 1951 if (mIsEnabled) { 1952 return mRequestedPackages; 1953 } else { 1954 return Collections.emptyList(); 1955 } 1956 } 1957 getSyncRequests()1958 public List<Set> getSyncRequests() { 1959 return mSyncRequests; 1960 } 1961 } 1962 1963 private static class TestClock implements PackageWatchdog.SystemClock { 1964 // Note 0 is special to the internal clock of PackageWatchdog. We need to start from 1965 // a non-zero value in order not to disrupt the logic of PackageWatchdog. 1966 private long mUpTimeMillis = 1; 1967 @Override uptimeMillis()1968 public long uptimeMillis() { 1969 return mUpTimeMillis; 1970 } moveTimeForward(long milliSeconds)1971 public void moveTimeForward(long milliSeconds) { 1972 mUpTimeMillis += milliSeconds; 1973 } 1974 } 1975 } 1976