• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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