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.anyBoolean;
20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
27 import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
28 
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.ArgumentMatchers.isNull;
33 import static org.mockito.Mockito.times;
34 
35 import android.content.ContentResolver;
36 import android.content.Context;
37 import android.content.pm.VersionedPackage;
38 import android.os.Bundle;
39 import android.os.RecoverySystem;
40 import android.os.RemoteCallback;
41 import android.os.SystemProperties;
42 import android.os.UserHandle;
43 import android.provider.DeviceConfig;
44 import android.provider.Settings;
45 
46 import com.android.dx.mockito.inline.extended.ExtendedMockito;
47 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
48 import com.android.server.RescueParty.RescuePartyObserver;
49 import com.android.server.am.SettingsToPropertiesMapper;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.mockito.Answers;
55 import org.mockito.ArgumentCaptor;
56 import org.mockito.Captor;
57 import org.mockito.Mock;
58 import org.mockito.MockitoSession;
59 import org.mockito.quality.Strictness;
60 import org.mockito.stubbing.Answer;
61 
62 import java.util.Arrays;
63 import java.util.HashMap;
64 import java.util.List;
65 
66 /**
67  * Test RescueParty.
68  */
69 public class RescuePartyTest {
70     private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
71     private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
72     private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
73     private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
74             {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
75 
76     private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
77     private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
78     private static final String CALLING_PACKAGE1 = "com.package.name1";
79     private static final String CALLING_PACKAGE2 = "com.package.name2";
80     private static final String NAMESPACE1 = "namespace1";
81     private static final String NAMESPACE2 = "namespace2";
82     private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
83             "persist.device_config.configuration.disable_rescue_party";
84 
85     private MockitoSession mSession;
86     private HashMap<String, String> mSystemSettingsMap;
87 
88     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
89     private Context mMockContext;
90     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
91     private PackageWatchdog mMockPackageWatchdog;
92     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
93     private ContentResolver mMockContentResolver;
94 
95     @Captor
96     private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
97     @Captor
98     private ArgumentCaptor<List<String>> mPackageListCaptor;
99 
100     @Before
setUp()101     public void setUp() throws Exception {
102         mSession =
103                 ExtendedMockito.mockitoSession().initMocks(
104                         this)
105                         .strictness(Strictness.LENIENT)
106                         .spyStatic(DeviceConfig.class)
107                         .spyStatic(SystemProperties.class)
108                         .spyStatic(Settings.Global.class)
109                         .spyStatic(Settings.Secure.class)
110                         .spyStatic(Settings.Config.class)
111                         .spyStatic(SettingsToPropertiesMapper.class)
112                         .spyStatic(RecoverySystem.class)
113                         .spyStatic(RescueParty.class)
114                         .spyStatic(PackageWatchdog.class)
115                         .startMocking();
116         mSystemSettingsMap = new HashMap<>();
117 
118         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
119         // Reset observer instance to get new mock context on every run
120         RescuePartyObserver.reset();
121 
122         // Mock SystemProperties setter and various getters
123         doAnswer((Answer<Void>) invocationOnMock -> {
124                     String key = invocationOnMock.getArgument(0);
125                     String value = invocationOnMock.getArgument(1);
126 
127                     mSystemSettingsMap.put(key, value);
128                     return null;
129                 }
130         ).when(() -> SystemProperties.set(anyString(), anyString()));
131 
132         doAnswer((Answer<Boolean>) invocationOnMock -> {
133                     String key = invocationOnMock.getArgument(0);
134                     boolean defaultValue = invocationOnMock.getArgument(1);
135 
136                     String storedValue = mSystemSettingsMap.get(key);
137                     return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue);
138                 }
139         ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
140 
141         doAnswer((Answer<Integer>) invocationOnMock -> {
142                     String key = invocationOnMock.getArgument(0);
143                     int defaultValue = invocationOnMock.getArgument(1);
144 
145                     String storedValue = mSystemSettingsMap.get(key);
146                     return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
147                 }
148         ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
149 
150         doAnswer((Answer<Long>) invocationOnMock -> {
151                     String key = invocationOnMock.getArgument(0);
152                     long defaultValue = invocationOnMock.getArgument(1);
153 
154                     String storedValue = mSystemSettingsMap.get(key);
155                     return storedValue == null ? defaultValue : Long.parseLong(storedValue);
156                 }
157         ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
158 
159         // Mock DeviceConfig
160         doAnswer((Answer<Boolean>) invocationOnMock -> true)
161                 .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(),
162                         anyBoolean()));
163         doAnswer((Answer<Void>) invocationOnMock -> null)
164                 .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
165 
166         // Mock PackageWatchdog
167         doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
168                 .when(() -> PackageWatchdog.getInstance(mMockContext));
169 
170         doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
171 
172         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
173                 Integer.toString(RescueParty.LEVEL_NONE));
174         SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0));
175         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
176         SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
177     }
178 
179     @After
tearDown()180     public void tearDown() throws Exception {
181         mSession.finishMocking();
182     }
183 
184     @Test
testBootLoopDetectionWithExecutionForAllRescueLevels()185     public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
186         noteBoot();
187 
188         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
189         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
190                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
191 
192         noteBoot();
193 
194         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
195         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
196                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
197 
198         noteBoot();
199 
200         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
201         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
202                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
203 
204         noteBoot();
205 
206         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
207         assertEquals(LEVEL_FACTORY_RESET,
208                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
209     }
210 
211     @Test
testPersistentAppCrashDetectionWithExecutionForAllRescueLevels()212     public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
213         notePersistentAppCrash();
214 
215         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
216         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
217                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
218 
219         notePersistentAppCrash();
220 
221         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
222         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
223                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
224 
225         notePersistentAppCrash();
226 
227         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
228         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
229                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
230 
231         notePersistentAppCrash();
232 
233         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
234         assertEquals(LEVEL_FACTORY_RESET,
235                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
236     }
237 
238     @Test
testNonPersistentAppCrashDetectionWithScopedResets()239     public void testNonPersistentAppCrashDetectionWithScopedResets() {
240         RescueParty.onSettingsProviderPublished(mMockContext);
241         verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
242                 mMonitorCallbackCaptor.capture()));
243 
244         // Record DeviceConfig accesses
245         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
246         RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
247         monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
248         monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
249         monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
250         // Fake DeviceConfig value changes
251         monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
252         verify(mMockPackageWatchdog).startObservingHealth(observer,
253                 Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
254         monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
255         verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
256                 mPackageListCaptor.capture(),
257                 eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
258         assertTrue(mPackageListCaptor.getValue().containsAll(
259                 Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2)));
260         // Perform and verify scoped resets
261         final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
262         observer.execute(new VersionedPackage(
263                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
264         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces);
265         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
266                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
267 
268         observer.execute(new VersionedPackage(
269                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
270         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces);
271         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
272                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
273 
274         observer.execute(new VersionedPackage(
275                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
276         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/null);
277         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
278                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
279 
280         observer.execute(new VersionedPackage(
281                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
282         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
283         assertTrue(RescueParty.isAttemptingFactoryReset());
284     }
285 
286     @Test
testIsAttemptingFactoryReset()287     public void testIsAttemptingFactoryReset() {
288         for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
289             noteBoot();
290         }
291         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
292         assertTrue(RescueParty.isAttemptingFactoryReset());
293     }
294 
295     @Test
testOnSettingsProviderPublishedExecutesRescueLevels()296     public void testOnSettingsProviderPublishedExecutesRescueLevels() {
297         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(1));
298 
299         RescueParty.onSettingsProviderPublished(mMockContext);
300 
301         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
302         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
303                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
304     }
305 
306     @Test
testNativeRescuePartyResets()307     public void testNativeRescuePartyResets() {
308         doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());
309         doReturn(FAKE_RESET_NATIVE_NAMESPACES).when(
310                 () -> SettingsToPropertiesMapper.getResetNativeCategories());
311 
312         RescueParty.onSettingsProviderPublished(mMockContext);
313 
314         verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
315                 FAKE_NATIVE_NAMESPACE1));
316         verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
317                 FAKE_NATIVE_NAMESPACE2));
318     }
319 
320     @Test
testExplicitlyEnablingAndDisablingRescue()321     public void testExplicitlyEnablingAndDisablingRescue() {
322         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
323         SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
324         assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
325                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
326 
327         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
328         assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
329                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING));
330     }
331 
332     @Test
testDisablingRescueByDeviceConfigFlag()333     public void testDisablingRescueByDeviceConfigFlag() {
334         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
335         SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
336 
337         assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
338                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
339 
340         // Restore the property value initalized in SetUp()
341         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
342         SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
343     }
344 
345     @Test
testHealthCheckLevels()346     public void testHealthCheckLevels() {
347         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
348 
349         // Ensure that no action is taken for cases where the failure reason is unknown
350         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
351                 LEVEL_FACTORY_RESET));
352         assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN),
353                 PackageHealthObserverImpact.USER_IMPACT_NONE);
354 
355         /*
356         For the following cases, ensure that the returned user impact corresponds with the user
357         impact of the next available rescue level, not the current one.
358          */
359         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
360                 RescueParty.LEVEL_NONE));
361         assertEquals(observer.onHealthCheckFailed(null,
362                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
363                 PackageHealthObserverImpact.USER_IMPACT_LOW);
364 
365         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
366                 RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS));
367         assertEquals(observer.onHealthCheckFailed(null,
368                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
369                 PackageHealthObserverImpact.USER_IMPACT_LOW);
370 
371 
372         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
373                 RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES));
374         assertEquals(observer.onHealthCheckFailed(null,
375                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
376                 PackageHealthObserverImpact.USER_IMPACT_HIGH);
377 
378 
379         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
380                 RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS));
381         assertEquals(observer.onHealthCheckFailed(null,
382                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
383                 PackageHealthObserverImpact.USER_IMPACT_HIGH);
384 
385 
386         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
387                 LEVEL_FACTORY_RESET));
388         assertEquals(observer.onHealthCheckFailed(null,
389                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
390                 PackageHealthObserverImpact.USER_IMPACT_HIGH);
391     }
392 
393     @Test
testBootLoopLevels()394     public void testBootLoopLevels() {
395         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
396 
397         /*
398          Ensure that the returned user impact corresponds with the user impact of the next available
399          rescue level, not the current one.
400          */
401         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
402                 RescueParty.LEVEL_NONE));
403         assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW);
404 
405         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
406                 RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS));
407         assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW);
408 
409         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
410                 RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES));
411         assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH);
412 
413         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
414                 RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS));
415         assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH);
416 
417         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
418                 LEVEL_FACTORY_RESET));
419         assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH);
420     }
421 
422     @Test
testRescueLevelIncrementsWhenExecuted()423     public void testRescueLevelIncrementsWhenExecuted() {
424         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
425         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
426                 RescueParty.LEVEL_NONE));
427         observer.execute(sFailingPackage,
428                 PackageWatchdog.FAILURE_REASON_APP_CRASH);
429         assertEquals(SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, -1),
430                 RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
431     }
432 
verifySettingsResets(int resetMode, String[] resetNamespaces)433     private void verifySettingsResets(int resetMode, String[] resetNamespaces) {
434         verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
435                 resetMode, UserHandle.USER_SYSTEM));
436         verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
437                 eq(resetMode), anyInt()));
438         // Verify DeviceConfig resets
439         if (resetNamespaces == null) {
440             verify(() -> DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null));
441         } else {
442             for (String namespace : resetNamespaces) {
443                 verify(() -> DeviceConfig.resetToDefaults(resetMode, namespace));
444             }
445         }
446     }
447 
noteBoot()448     private void noteBoot() {
449         RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation();
450     }
451 
notePersistentAppCrash()452     private void notePersistentAppCrash() {
453         RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
454                 "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
455     }
456 
getConfigAccessBundle(String callingPackage, String namespace)457     private Bundle getConfigAccessBundle(String callingPackage, String namespace) {
458         Bundle result = new Bundle();
459         result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, Settings.EXTRA_ACCESS_CALLBACK);
460         result.putString(Settings.EXTRA_CALLING_PACKAGE, callingPackage);
461         result.putString(Settings.EXTRA_NAMESPACE, namespace);
462         return result;
463     }
464 
getConfigNamespaceUpdateBundle(String updatedNamespace)465     private Bundle getConfigNamespaceUpdateBundle(String updatedNamespace) {
466         Bundle result = new Bundle();
467         result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE,
468                 Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK);
469         result.putString(Settings.EXTRA_NAMESPACE, updatedNamespace);
470         return result;
471     }
472 }
473