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