blob: 0d6020c3d06d3c2289bb01a27cbc88b9c3ce37f4 [file] [log] [blame]
bpetrivsa7101952019-02-07 16:01:24 +00001/*
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
17package com.android.server;
18
19import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
20import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
21import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
22import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
23import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
24import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
25import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
26import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
27
28import static org.junit.Assert.assertEquals;
29import static org.junit.Assert.assertTrue;
30import static org.mockito.ArgumentMatchers.eq;
31import static org.mockito.ArgumentMatchers.isNull;
32
33import android.content.ContentResolver;
34import android.content.Context;
35import android.os.RecoverySystem;
36import android.os.SystemProperties;
37import android.os.UserHandle;
bpetrivs93075f42019-02-28 12:08:12 +000038import android.provider.DeviceConfig;
bpetrivsa7101952019-02-07 16:01:24 +000039import android.provider.Settings;
40
41import com.android.dx.mockito.inline.extended.ExtendedMockito;
bpetrivs93075f42019-02-28 12:08:12 +000042import com.android.server.am.SettingsToPropertiesMapper;
43import com.android.server.utils.FlagNamespaceUtils;
bpetrivsa7101952019-02-07 16:01:24 +000044
45import org.junit.After;
46import org.junit.Before;
47import org.junit.Test;
48import org.mockito.Answers;
49import org.mockito.Mock;
50import org.mockito.MockitoSession;
51import org.mockito.quality.Strictness;
52import org.mockito.stubbing.Answer;
53
54import java.util.HashMap;
55
56/**
57 * Test RescueParty.
58 */
59public class RescuePartyTest {
60 private static final int PERSISTENT_APP_UID = 12;
61 private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
bpetrivs93075f42019-02-28 12:08:12 +000062 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};
bpetrivsa7101952019-02-07 16:01:24 +000066
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
78 public void setUp() throws Exception {
79 mSession =
80 ExtendedMockito.mockitoSession().initMocks(
81 this)
82 .strictness(Strictness.LENIENT)
bpetrivs93075f42019-02-28 12:08:12 +000083 .spyStatic(DeviceConfig.class)
bpetrivsa7101952019-02-07 16:01:24 +000084 .spyStatic(SystemProperties.class)
85 .spyStatic(Settings.Global.class)
86 .spyStatic(Settings.Secure.class)
bpetrivs93075f42019-02-28 12:08:12 +000087 .spyStatic(SettingsToPropertiesMapper.class)
bpetrivsa7101952019-02-07 16:01:24 +000088 .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
bpetrivs93075f42019-02-28 12:08:12 +0000133 // 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
bpetrivsa7101952019-02-07 16:01:24 +0000141 doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
142 RescueParty.resetAllThresholds();
bpetrivs93075f42019-02-28 12:08:12 +0000143 FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
bpetrivsa7101952019-02-07 16:01:24 +0000144
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
152 public void tearDown() throws Exception {
153 mSession.finishMocking();
154 }
155
156 @Test
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
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
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
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
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
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
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
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
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
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
bpetrivs93075f42019-02-28 12:08:12 +0000299 @Test
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
bpetrivsa7101952019-02-07 16:01:24 +0000322 private void verifySettingsResets(int resetMode) {
323 verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
bpetrivs93075f42019-02-28 12:08:12 +0000324 resetMode, UserHandle.USER_SYSTEM));
bpetrivsa7101952019-02-07 16:01:24 +0000325 verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
326 eq(resetMode), anyInt()));
327 }
328
329 private void noteBoot(int numTimes) {
330 for (int i = 0; i < numTimes; i++) {
331 RescueParty.noteBoot(mMockContext);
332 }
333 }
334
335 private void notePersistentAppCrash(int numTimes) {
336 for (int i = 0; i < numTimes; i++) {
bpetrivs0254ff62019-03-01 11:50:45 +0000337 RescueParty.noteAppCrash(mMockContext, PERSISTENT_APP_UID);
bpetrivsa7101952019-02-07 16:01:24 +0000338 }
339 }
340}