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 com.android.dx.mockito.inline.extended.ExtendedMockito.any; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 28 import static com.android.server.RescueParty.LEVEL_FACTORY_RESET; 29 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertFalse; 32 import static org.junit.Assert.assertTrue; 33 import static org.mockito.ArgumentMatchers.eq; 34 import static org.mockito.ArgumentMatchers.isNull; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.times; 37 38 import android.content.ContentResolver; 39 import android.content.Context; 40 import android.content.pm.VersionedPackage; 41 import android.os.Bundle; 42 import android.os.RecoverySystem; 43 import android.os.RemoteCallback; 44 import android.os.SystemProperties; 45 import android.os.UserHandle; 46 import android.provider.DeviceConfig; 47 import android.provider.Settings; 48 import android.util.ArraySet; 49 50 import com.android.dx.mockito.inline.extended.ExtendedMockito; 51 import com.android.server.PackageWatchdog.PackageHealthObserverImpact; 52 import com.android.server.RescueParty.RescuePartyObserver; 53 import com.android.server.am.SettingsToPropertiesMapper; 54 55 import org.junit.After; 56 import org.junit.Before; 57 import org.junit.Test; 58 import org.mockito.Answers; 59 import org.mockito.ArgumentCaptor; 60 import org.mockito.Captor; 61 import org.mockito.Mock; 62 import org.mockito.MockitoSession; 63 import org.mockito.quality.Strictness; 64 import org.mockito.stubbing.Answer; 65 66 import java.util.Arrays; 67 import java.util.HashMap; 68 import java.util.HashSet; 69 import java.util.List; 70 71 /** 72 * Test RescueParty. 73 */ 74 public class RescuePartyTest { 75 private static final long CURRENT_NETWORK_TIME_MILLIS = 0L; 76 private static final String FAKE_NATIVE_NAMESPACE1 = "native1"; 77 private static final String FAKE_NATIVE_NAMESPACE2 = "native2"; 78 private static final String[] FAKE_RESET_NATIVE_NAMESPACES = 79 {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2}; 80 81 private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1); 82 private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; 83 private static final String CALLING_PACKAGE1 = "com.package.name1"; 84 private static final String CALLING_PACKAGE2 = "com.package.name2"; 85 private static final String CALLING_PACKAGE3 = "com.package.name3"; 86 private static final String NAMESPACE1 = "namespace1"; 87 private static final String NAMESPACE2 = "namespace2"; 88 private static final String NAMESPACE3 = "namespace3"; 89 private static final String NAMESPACE4 = "namespace4"; 90 private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG = 91 "persist.device_config.configuration.disable_rescue_party"; 92 private static final String PROP_DISABLE_FACTORY_RESET_FLAG = 93 "persist.device_config.configuration.disable_rescue_party_factory_reset"; 94 95 private MockitoSession mSession; 96 private HashMap<String, String> mSystemSettingsMap; 97 //Records the namespaces wiped by setProperties(). 98 private HashSet<String> mNamespacesWiped; 99 100 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 101 private Context mMockContext; 102 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 103 private PackageWatchdog mMockPackageWatchdog; 104 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 105 private ContentResolver mMockContentResolver; 106 107 @Captor 108 private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor; 109 @Captor 110 private ArgumentCaptor<List<String>> mPackageListCaptor; 111 112 @Before setUp()113 public void setUp() throws Exception { 114 mSession = 115 ExtendedMockito.mockitoSession().initMocks( 116 this) 117 .strictness(Strictness.LENIENT) 118 .spyStatic(DeviceConfig.class) 119 .spyStatic(SystemProperties.class) 120 .spyStatic(Settings.Global.class) 121 .spyStatic(Settings.Secure.class) 122 .spyStatic(Settings.Config.class) 123 .spyStatic(SettingsToPropertiesMapper.class) 124 .spyStatic(RecoverySystem.class) 125 .spyStatic(RescueParty.class) 126 .spyStatic(PackageWatchdog.class) 127 .startMocking(); 128 mSystemSettingsMap = new HashMap<>(); 129 mNamespacesWiped = new HashSet<>(); 130 131 when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); 132 // Reset observer instance to get new mock context on every run 133 RescuePartyObserver.reset(); 134 135 // Mock SystemProperties setter and various getters 136 doAnswer((Answer<Void>) invocationOnMock -> { 137 String key = invocationOnMock.getArgument(0); 138 String value = invocationOnMock.getArgument(1); 139 140 mSystemSettingsMap.put(key, value); 141 return null; 142 } 143 ).when(() -> SystemProperties.set(anyString(), anyString())); 144 145 doAnswer((Answer<Boolean>) invocationOnMock -> { 146 String key = invocationOnMock.getArgument(0); 147 boolean defaultValue = invocationOnMock.getArgument(1); 148 149 String storedValue = mSystemSettingsMap.get(key); 150 return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue); 151 } 152 ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean())); 153 154 doAnswer((Answer<Integer>) invocationOnMock -> { 155 String key = invocationOnMock.getArgument(0); 156 int defaultValue = invocationOnMock.getArgument(1); 157 158 String storedValue = mSystemSettingsMap.get(key); 159 return storedValue == null ? defaultValue : Integer.parseInt(storedValue); 160 } 161 ).when(() -> SystemProperties.getInt(anyString(), anyInt())); 162 163 doAnswer((Answer<Long>) invocationOnMock -> { 164 String key = invocationOnMock.getArgument(0); 165 long defaultValue = invocationOnMock.getArgument(1); 166 167 String storedValue = mSystemSettingsMap.get(key); 168 return storedValue == null ? defaultValue : Long.parseLong(storedValue); 169 } 170 ).when(() -> SystemProperties.getLong(anyString(), anyLong())); 171 172 // Mock DeviceConfig 173 doAnswer((Answer<Boolean>) invocationOnMock -> true) 174 .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), 175 anyBoolean())); 176 doAnswer((Answer<Void>) invocationOnMock -> null) 177 .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString())); 178 doAnswer((Answer<Boolean>) invocationOnMock -> { 179 DeviceConfig.Properties properties = invocationOnMock.getArgument(0); 180 String namespace = properties.getNamespace(); 181 // record a wipe 182 if (properties.getKeyset().isEmpty()) { 183 mNamespacesWiped.add(namespace); 184 } 185 return true; 186 } 187 ).when(() -> DeviceConfig.setProperties(any(DeviceConfig.Properties.class))); 188 189 // Mock PackageWatchdog 190 doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) 191 .when(() -> PackageWatchdog.getInstance(mMockContext)); 192 193 doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); 194 195 SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0)); 196 SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); 197 SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); 198 } 199 200 @After tearDown()201 public void tearDown() throws Exception { 202 mSession.finishMocking(); 203 } 204 205 @Test testBootLoopDetectionWithExecutionForAllRescueLevels()206 public void testBootLoopDetectionWithExecutionForAllRescueLevels() { 207 RescueParty.onSettingsProviderPublished(mMockContext); 208 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 209 mMonitorCallbackCaptor.capture())); 210 HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); 211 212 noteBoot(1); 213 214 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, 215 verifiedTimesMap); 216 217 // Record DeviceConfig accesses 218 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 219 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 220 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 221 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); 222 223 final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; 224 225 noteBoot(2); 226 227 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces, 228 verifiedTimesMap); 229 230 noteBoot(3); 231 232 verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, 233 verifiedTimesMap); 234 235 noteBoot(4); 236 assertTrue(RescueParty.isRebootPropertySet()); 237 238 noteBoot(5); 239 assertTrue(RescueParty.isFactoryResetPropertySet()); 240 } 241 242 @Test testPersistentAppCrashDetectionWithExecutionForAllRescueLevels()243 public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { 244 notePersistentAppCrash(1); 245 246 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, 247 /*configResetVerifiedTimesMap=*/ null); 248 249 notePersistentAppCrash(2); 250 251 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, 252 /*configResetVerifiedTimesMap=*/ null); 253 254 notePersistentAppCrash(3); 255 256 verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, 257 /*configResetVerifiedTimesMap=*/ null); 258 259 notePersistentAppCrash(4); 260 assertTrue(RescueParty.isRebootPropertySet()); 261 262 notePersistentAppCrash(5); 263 assertTrue(RescueParty.isFactoryResetPropertySet()); 264 } 265 266 @Test testNonPersistentAppCrashDetectionWithScopedResets()267 public void testNonPersistentAppCrashDetectionWithScopedResets() { 268 RescueParty.onSettingsProviderPublished(mMockContext); 269 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 270 mMonitorCallbackCaptor.capture())); 271 272 // Record DeviceConfig accesses 273 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 274 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 275 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 276 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); 277 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); 278 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); 279 // Fake DeviceConfig value changes 280 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); 281 verify(mMockPackageWatchdog).startObservingHealth(observer, 282 Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS); 283 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); 284 verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer), 285 mPackageListCaptor.capture(), 286 eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS)); 287 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); 288 verify(mMockPackageWatchdog).startObservingHealth(observer, 289 Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS); 290 assertTrue(mPackageListCaptor.getValue().containsAll( 291 Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2))); 292 // Perform and verify scoped resets 293 final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; 294 final String[] expectedAllResetNamespaces = 295 new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3}; 296 HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); 297 observer.execute(new VersionedPackage( 298 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 299 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces, 300 verifiedTimesMap); 301 302 observer.execute(new VersionedPackage( 303 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2); 304 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces, 305 verifiedTimesMap); 306 307 observer.execute(new VersionedPackage( 308 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3); 309 verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, 310 verifiedTimesMap); 311 312 observer.execute(new VersionedPackage( 313 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); 314 assertTrue(RescueParty.isRebootPropertySet()); 315 316 observer.execute(new VersionedPackage( 317 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); 318 assertTrue(RescueParty.isFactoryResetPropertySet()); 319 } 320 321 @Test testNonDeviceConfigSettingsOnlyResetOncePerLevel()322 public void testNonDeviceConfigSettingsOnlyResetOncePerLevel() { 323 RescueParty.onSettingsProviderPublished(mMockContext); 324 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 325 mMonitorCallbackCaptor.capture())); 326 327 // Record DeviceConfig accesses 328 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 329 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 330 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 331 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); 332 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); 333 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); 334 // Fake DeviceConfig value changes 335 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); 336 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); 337 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); 338 // Perform and verify scoped resets 339 final String[] expectedPackage1ResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; 340 final String[] expectedPackage2ResetNamespaces = new String[]{NAMESPACE2, NAMESPACE3}; 341 final String[] expectedAllResetNamespaces = 342 new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3}; 343 HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); 344 observer.execute(new VersionedPackage( 345 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 346 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, 347 expectedPackage1ResetNamespaces, verifiedTimesMap); 348 349 // Settings.Global & Settings.Secure should still remain the same execution times. 350 observer.execute(new VersionedPackage( 351 CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); 352 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, 353 expectedPackage2ResetNamespaces, verifiedTimesMap); 354 355 observer.execute(new VersionedPackage( 356 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2); 357 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, 358 expectedPackage1ResetNamespaces, verifiedTimesMap); 359 360 // Settings.Global & Settings.Secure should still remain the same execution times. 361 observer.execute(new VersionedPackage( 362 CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2); 363 verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, 364 expectedPackage2ResetNamespaces, verifiedTimesMap); 365 366 observer.execute(new VersionedPackage( 367 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3); 368 verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, 369 verifiedTimesMap); 370 371 // Settings.Global & Settings.Secure should still remain the same execution times. 372 observer.execute(new VersionedPackage( 373 CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3); 374 verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, 375 verifiedTimesMap); 376 377 observer.execute(new VersionedPackage( 378 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); 379 assertTrue(RescueParty.isRebootPropertySet()); 380 381 observer.execute(new VersionedPackage( 382 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); 383 assertTrue(RescueParty.isFactoryResetPropertySet()); 384 } 385 386 @Test testIsAttemptingFactoryReset()387 public void testIsAttemptingFactoryReset() { 388 for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { 389 noteBoot(i + 1); 390 } 391 assertTrue(RescueParty.isAttemptingFactoryReset()); 392 assertTrue(RescueParty.isFactoryResetPropertySet()); 393 } 394 395 @Test testNativeRescuePartyResets()396 public void testNativeRescuePartyResets() { 397 doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed()); 398 doReturn(FAKE_RESET_NATIVE_NAMESPACES).when( 399 () -> SettingsToPropertiesMapper.getResetNativeCategories()); 400 401 RescueParty.onSettingsProviderPublished(mMockContext); 402 403 verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS, 404 FAKE_NATIVE_NAMESPACE1)); 405 verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS, 406 FAKE_NATIVE_NAMESPACE2)); 407 } 408 409 @Test testExplicitlyEnablingAndDisablingRescue()410 public void testExplicitlyEnablingAndDisablingRescue() { 411 SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); 412 SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); 413 assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, 414 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); 415 416 SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); 417 assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, 418 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1)); 419 } 420 421 @Test testDisablingRescueByDeviceConfigFlag()422 public void testDisablingRescueByDeviceConfigFlag() { 423 SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); 424 SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); 425 426 assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, 427 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); 428 429 // Restore the property value initialized in SetUp() 430 SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); 431 SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); 432 } 433 434 @Test testDisablingFactoryResetByDeviceConfigFlag()435 public void testDisablingFactoryResetByDeviceConfigFlag() { 436 SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); 437 438 for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { 439 noteBoot(i + 1); 440 } 441 assertFalse(RescueParty.isFactoryResetPropertySet()); 442 443 // Restore the property value initialized in SetUp() 444 SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, ""); 445 } 446 447 @Test testHealthCheckLevels()448 public void testHealthCheckLevels() { 449 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 450 451 // Ensure that no action is taken for cases where the failure reason is unknown 452 assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1), 453 PackageHealthObserverImpact.USER_IMPACT_NONE); 454 455 // Ensure the correct user impact is returned for each mitigation count. 456 assertEquals(observer.onHealthCheckFailed(null, 457 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), 458 PackageHealthObserverImpact.USER_IMPACT_LOW); 459 460 assertEquals(observer.onHealthCheckFailed(null, 461 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2), 462 PackageHealthObserverImpact.USER_IMPACT_LOW); 463 464 assertEquals(observer.onHealthCheckFailed(null, 465 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3), 466 PackageHealthObserverImpact.USER_IMPACT_HIGH); 467 468 assertEquals(observer.onHealthCheckFailed(null, 469 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4), 470 PackageHealthObserverImpact.USER_IMPACT_HIGH); 471 } 472 473 @Test testBootLoopLevels()474 public void testBootLoopLevels() { 475 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 476 477 assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_NONE); 478 assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LOW); 479 assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LOW); 480 assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_HIGH); 481 assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_HIGH); 482 assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_HIGH); 483 } 484 485 @Test testResetDeviceConfigForPackagesOnlyRuntimeMap()486 public void testResetDeviceConfigForPackagesOnlyRuntimeMap() { 487 RescueParty.onSettingsProviderPublished(mMockContext); 488 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 489 mMonitorCallbackCaptor.capture())); 490 491 // Record DeviceConfig accesses 492 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 493 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 494 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 495 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); 496 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); 497 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); 498 // Fake DeviceConfig value changes 499 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); 500 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); 501 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); 502 503 doReturn("").when(() -> DeviceConfig.getString( 504 eq(RescueParty.NAMESPACE_CONFIGURATION), 505 eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), 506 eq(""))); 507 508 RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1})); 509 ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( 510 Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2})); 511 assertEquals(mNamespacesWiped, expectedNamespacesWiped); 512 } 513 514 @Test testResetDeviceConfigForPackagesOnlyPresetMap()515 public void testResetDeviceConfigForPackagesOnlyPresetMap() { 516 RescueParty.onSettingsProviderPublished(mMockContext); 517 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 518 mMonitorCallbackCaptor.capture())); 519 520 String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," 521 + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," 522 + NAMESPACE3 + ":" + CALLING_PACKAGE1; 523 doReturn(presetMapping).when(() -> DeviceConfig.getString( 524 eq(RescueParty.NAMESPACE_CONFIGURATION), 525 eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), 526 eq(""))); 527 528 RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1})); 529 ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( 530 Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3})); 531 assertEquals(mNamespacesWiped, expectedNamespacesWiped); 532 } 533 534 @Test testResetDeviceConfigForPackagesBothMaps()535 public void testResetDeviceConfigForPackagesBothMaps() { 536 RescueParty.onSettingsProviderPublished(mMockContext); 537 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 538 mMonitorCallbackCaptor.capture())); 539 540 // Record DeviceConfig accesses 541 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 542 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 543 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 544 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); 545 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); 546 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); 547 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4)); 548 // Fake DeviceConfig value changes 549 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); 550 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); 551 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); 552 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4)); 553 554 String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," 555 + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," 556 + NAMESPACE4 + ":" + CALLING_PACKAGE3; 557 doReturn(presetMapping).when(() -> DeviceConfig.getString( 558 eq(RescueParty.NAMESPACE_CONFIGURATION), 559 eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), 560 eq(""))); 561 562 RescueParty.resetDeviceConfigForPackages( 563 Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2})); 564 ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( 565 Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3})); 566 assertEquals(mNamespacesWiped, expectedNamespacesWiped); 567 } 568 569 @Test testResetDeviceConfigNoExceptionWhenFlagMalformed()570 public void testResetDeviceConfigNoExceptionWhenFlagMalformed() { 571 RescueParty.onSettingsProviderPublished(mMockContext); 572 verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), 573 mMonitorCallbackCaptor.capture())); 574 575 // Record DeviceConfig accesses 576 RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); 577 RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); 578 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); 579 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); 580 monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4)); 581 // Fake DeviceConfig value changes 582 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); 583 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); 584 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); 585 monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4)); 586 587 String invalidPresetMapping = NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," 588 + NAMESPACE1 + "." + CALLING_PACKAGE2; 589 doReturn(invalidPresetMapping).when(() -> DeviceConfig.getString( 590 eq(RescueParty.NAMESPACE_CONFIGURATION), 591 eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), 592 eq(""))); 593 594 RescueParty.resetDeviceConfigForPackages( 595 Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2})); 596 ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( 597 Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3})); 598 assertEquals(mNamespacesWiped, expectedNamespacesWiped); 599 } 600 verifySettingsResets(int resetMode, String[] resetNamespaces, HashMap<String, Integer> configResetVerifiedTimesMap)601 private void verifySettingsResets(int resetMode, String[] resetNamespaces, 602 HashMap<String, Integer> configResetVerifiedTimesMap) { 603 verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null, 604 resetMode, UserHandle.USER_SYSTEM)); 605 verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(), 606 eq(resetMode), anyInt())); 607 // Verify DeviceConfig resets 608 if (resetNamespaces == null) { 609 verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never()); 610 } else { 611 for (String namespace : resetNamespaces) { 612 int verifiedTimes = 0; 613 if (configResetVerifiedTimesMap != null 614 && configResetVerifiedTimesMap.get(namespace) != null) { 615 verifiedTimes = configResetVerifiedTimesMap.get(namespace); 616 } 617 verify(() -> DeviceConfig.resetToDefaults(RescueParty.DEVICE_CONFIG_RESET_MODE, 618 namespace), times(verifiedTimes + 1)); 619 if (configResetVerifiedTimesMap != null) { 620 configResetVerifiedTimesMap.put(namespace, verifiedTimes + 1); 621 } 622 } 623 } 624 } 625 noteBoot(int mitigationCount)626 private void noteBoot(int mitigationCount) { 627 RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); 628 } 629 notePersistentAppCrash(int mitigationCount)630 private void notePersistentAppCrash(int mitigationCount) { 631 RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( 632 "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); 633 } 634 getConfigAccessBundle(String callingPackage, String namespace)635 private Bundle getConfigAccessBundle(String callingPackage, String namespace) { 636 Bundle result = new Bundle(); 637 result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, Settings.EXTRA_ACCESS_CALLBACK); 638 result.putString(Settings.EXTRA_CALLING_PACKAGE, callingPackage); 639 result.putString(Settings.EXTRA_NAMESPACE, namespace); 640 return result; 641 } 642 getConfigNamespaceUpdateBundle(String updatedNamespace)643 private Bundle getConfigNamespaceUpdateBundle(String updatedNamespace) { 644 Bundle result = new Bundle(); 645 result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, 646 Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK); 647 result.putString(Settings.EXTRA_NAMESPACE, updatedNamespace); 648 return result; 649 } 650 } 651