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