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