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