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