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