• 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.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 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.ArgumentMatchers.isNull;
32 
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.os.RecoverySystem;
36 import android.os.SystemProperties;
37 import android.os.UserHandle;
38 import android.provider.DeviceConfig;
39 import android.provider.Settings;
40 
41 import com.android.dx.mockito.inline.extended.ExtendedMockito;
42 import com.android.server.am.SettingsToPropertiesMapper;
43 import com.android.server.utils.FlagNamespaceUtils;
44 
45 import org.junit.After;
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.mockito.Answers;
49 import org.mockito.Mock;
50 import org.mockito.MockitoSession;
51 import org.mockito.quality.Strictness;
52 import org.mockito.stubbing.Answer;
53 
54 import java.util.HashMap;
55 
56 /**
57  * Test RescueParty.
58  */
59 public class RescuePartyTest {
60     private static final int PERSISTENT_APP_UID = 12;
61     private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
62     private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
63     private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
64     private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
65             {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
66 
67     private MockitoSession mSession;
68 
69     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
70     private Context mMockContext;
71 
72     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
73     private ContentResolver mMockContentResolver;
74 
75     private HashMap<String, String> mSystemSettingsMap;
76 
77     @Before
setUp()78     public void setUp() throws Exception {
79         mSession =
80                 ExtendedMockito.mockitoSession().initMocks(
81                         this)
82                         .strictness(Strictness.LENIENT)
83                         .spyStatic(DeviceConfig.class)
84                         .spyStatic(SystemProperties.class)
85                         .spyStatic(Settings.Global.class)
86                         .spyStatic(Settings.Secure.class)
87                         .spyStatic(SettingsToPropertiesMapper.class)
88                         .spyStatic(RecoverySystem.class)
89                         .spyStatic(RescueParty.class)
90                         .startMocking();
91         mSystemSettingsMap = new HashMap<>();
92 
93         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
94 
95 
96         // Mock SystemProperties setter and various getters
97         doAnswer((Answer<Void>) invocationOnMock -> {
98                     String key = invocationOnMock.getArgument(0);
99                     String value = invocationOnMock.getArgument(1);
100 
101                     mSystemSettingsMap.put(key, value);
102                     return null;
103                 }
104         ).when(() -> SystemProperties.set(anyString(), anyString()));
105 
106         doAnswer((Answer<Boolean>) invocationOnMock -> {
107                     String key = invocationOnMock.getArgument(0);
108                     boolean defaultValue = invocationOnMock.getArgument(1);
109 
110                     String storedValue = mSystemSettingsMap.get(key);
111                     return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue);
112                 }
113         ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
114 
115         doAnswer((Answer<Integer>) invocationOnMock -> {
116                     String key = invocationOnMock.getArgument(0);
117                     int defaultValue = invocationOnMock.getArgument(1);
118 
119                     String storedValue = mSystemSettingsMap.get(key);
120                     return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
121                 }
122         ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
123 
124         doAnswer((Answer<Long>) invocationOnMock -> {
125                     String key = invocationOnMock.getArgument(0);
126                     long defaultValue = invocationOnMock.getArgument(1);
127 
128                     String storedValue = mSystemSettingsMap.get(key);
129                     return storedValue == null ? defaultValue : Long.parseLong(storedValue);
130                 }
131         ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
132 
133         // Mock DeviceConfig
134         doAnswer((Answer<Boolean>) invocationOnMock -> true)
135                 .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(),
136                         anyBoolean()));
137         doAnswer((Answer<Void>) invocationOnMock -> null)
138                 .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
139 
140 
141         doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
142         RescueParty.resetAllThresholds();
143         FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
144 
145         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
146                 Integer.toString(RescueParty.LEVEL_NONE));
147         SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0));
148         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
149     }
150 
151     @After
tearDown()152     public void tearDown() throws Exception {
153         mSession.finishMocking();
154     }
155 
156     @Test
testBootLoopDetectionWithExecutionForAllRescueLevels()157     public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
158         noteBoot(RescueParty.TRIGGER_COUNT);
159 
160         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
161         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
162                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
163 
164         noteBoot(RescueParty.TRIGGER_COUNT);
165 
166         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
167         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
168                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
169 
170         noteBoot(RescueParty.TRIGGER_COUNT);
171 
172         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
173         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
174                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
175 
176         noteBoot(RescueParty.TRIGGER_COUNT);
177 
178         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
179         assertEquals(RescueParty.LEVEL_FACTORY_RESET,
180                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
181     }
182 
183     @Test
testPersistentAppCrashDetectionWithExecutionForAllRescueLevels()184     public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
185         notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
186 
187         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
188         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
189                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
190 
191         notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
192 
193         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
194         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
195                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
196 
197         notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
198 
199         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
200         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
201                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
202 
203         notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
204 
205         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
206         assertEquals(RescueParty.LEVEL_FACTORY_RESET,
207                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
208     }
209 
210     @Test
testBootLoopDetectionWithWrongInterval()211     public void testBootLoopDetectionWithWrongInterval() {
212         noteBoot(RescueParty.TRIGGER_COUNT - 1);
213 
214         // last boot is just outside of the boot loop detection window
215         doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS + 1).when(
216                 () -> RescueParty.getElapsedRealtime());
217         noteBoot(/*numTimes=*/1);
218 
219         assertEquals(RescueParty.LEVEL_NONE,
220                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
221     }
222 
223     @Test
testPersistentAppCrashDetectionWithWrongInterval()224     public void testPersistentAppCrashDetectionWithWrongInterval() {
225         notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
226 
227         // last persistent app crash is just outside of the boot loop detection window
228         doReturn(CURRENT_NETWORK_TIME_MILLIS
229                 + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS + 1)
230                 .when(() -> RescueParty.getElapsedRealtime());
231         notePersistentAppCrash(/*numTimes=*/1);
232 
233         assertEquals(RescueParty.LEVEL_NONE,
234                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
235     }
236 
237     @Test
testBootLoopDetectionWithProperInterval()238     public void testBootLoopDetectionWithProperInterval() {
239         noteBoot(RescueParty.TRIGGER_COUNT - 1);
240 
241         // last boot is just inside of the boot loop detection window
242         doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS).when(
243                 () -> RescueParty.getElapsedRealtime());
244         noteBoot(/*numTimes=*/1);
245 
246         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
247         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
248                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
249     }
250 
251     @Test
testPersistentAppCrashDetectionWithProperInterval()252     public void testPersistentAppCrashDetectionWithProperInterval() {
253         notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
254 
255         // last persistent app crash is just inside of the boot loop detection window
256         doReturn(CURRENT_NETWORK_TIME_MILLIS
257                 + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS)
258                 .when(() -> RescueParty.getElapsedRealtime());
259         notePersistentAppCrash(/*numTimes=*/1);
260 
261         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
262         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
263                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
264     }
265 
266     @Test
testBootLoopDetectionWithWrongTriggerCount()267     public void testBootLoopDetectionWithWrongTriggerCount() {
268         noteBoot(RescueParty.TRIGGER_COUNT - 1);
269         assertEquals(RescueParty.LEVEL_NONE,
270                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
271     }
272 
273     @Test
testPersistentAppCrashDetectionWithWrongTriggerCount()274     public void testPersistentAppCrashDetectionWithWrongTriggerCount() {
275         notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
276         assertEquals(RescueParty.LEVEL_NONE,
277                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
278     }
279 
280     @Test
testIsAttemptingFactoryReset()281     public void testIsAttemptingFactoryReset() {
282         noteBoot(RescueParty.TRIGGER_COUNT * 4);
283 
284         verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
285         assertTrue(RescueParty.isAttemptingFactoryReset());
286     }
287 
288     @Test
testOnSettingsProviderPublishedExecutesRescueLevels()289     public void testOnSettingsProviderPublishedExecutesRescueLevels() {
290         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(1));
291 
292         RescueParty.onSettingsProviderPublished(mMockContext);
293 
294         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
295         assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
296                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
297     }
298 
299     @Test
testNativeRescuePartyResets()300     public void testNativeRescuePartyResets() {
301         doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());
302         doReturn(FAKE_RESET_NATIVE_NAMESPACES).when(
303                 () -> SettingsToPropertiesMapper.getResetNativeCategories());
304 
305         RescueParty.onSettingsProviderPublished(mMockContext);
306 
307         verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
308                 FAKE_NATIVE_NAMESPACE1));
309         verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
310                 FAKE_NATIVE_NAMESPACE2));
311 
312         ExtendedMockito.verify(
313                 () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
314                         FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 0,
315                         FAKE_NATIVE_NAMESPACE1, /*makeDefault=*/true));
316         ExtendedMockito.verify(
317                 () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
318                         FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 1,
319                         FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true));
320     }
321 
verifySettingsResets(int resetMode)322     private void verifySettingsResets(int resetMode) {
323         verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
324                 resetMode, UserHandle.USER_SYSTEM));
325         verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
326                 eq(resetMode), anyInt()));
327     }
328 
noteBoot(int numTimes)329     private void noteBoot(int numTimes) {
330         for (int i = 0; i < numTimes; i++) {
331             RescueParty.noteBoot(mMockContext);
332         }
333     }
334 
notePersistentAppCrash(int numTimes)335     private void notePersistentAppCrash(int numTimes) {
336         for (int i = 0; i < numTimes; i++) {
337             RescueParty.noteAppCrash(mMockContext, PERSISTENT_APP_UID);
338         }
339     }
340 }
341