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