• 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 android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
20 
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
22 import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.anyLong;
30 import static org.mockito.ArgumentMatchers.anyString;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.doReturn;
33 import static org.mockito.Mockito.reset;
34 import static org.mockito.Mockito.spy;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.Manifest;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.VersionedPackage;
44 import android.crashrecovery.flags.Flags;
45 import android.net.ConnectivityModuleConnector;
46 import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;
47 import android.os.Handler;
48 import android.os.SystemProperties;
49 import android.os.test.TestLooper;
50 import android.platform.test.annotations.RequiresFlagsDisabled;
51 import android.platform.test.flag.junit.CheckFlagsRule;
52 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
53 import android.platform.test.flag.junit.SetFlagsRule;
54 import android.provider.DeviceConfig;
55 import android.util.AtomicFile;
56 import android.util.LongArrayQueue;
57 import android.util.Xml;
58 
59 import androidx.test.InstrumentationRegistry;
60 
61 import com.android.dx.mockito.inline.extended.ExtendedMockito;
62 import com.android.internal.util.XmlUtils;
63 import com.android.modules.utils.TypedXmlPullParser;
64 import com.android.modules.utils.TypedXmlSerializer;
65 import com.android.server.PackageWatchdog.HealthCheckState;
66 import com.android.server.PackageWatchdog.MonitoredPackage;
67 import com.android.server.PackageWatchdog.ObserverInternal;
68 import com.android.server.PackageWatchdog.PackageHealthObserver;
69 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
70 
71 import org.junit.After;
72 import org.junit.Before;
73 import org.junit.Rule;
74 import org.junit.Test;
75 import org.mockito.ArgumentCaptor;
76 import org.mockito.Captor;
77 import org.mockito.Mock;
78 import org.mockito.MockitoAnnotations;
79 import org.mockito.MockitoSession;
80 import org.mockito.quality.Strictness;
81 import org.mockito.stubbing.Answer;
82 
83 import java.io.File;
84 import java.io.FileOutputStream;
85 import java.lang.reflect.Field;
86 import java.util.ArrayList;
87 import java.util.Arrays;
88 import java.util.Collections;
89 import java.util.HashMap;
90 import java.util.List;
91 import java.util.Set;
92 import java.util.concurrent.Executor;
93 import java.util.concurrent.TimeUnit;
94 import java.util.function.Consumer;
95 import java.util.function.Supplier;
96 
97 /**
98  * Test PackageWatchdog.
99  */
100 public class PackageWatchdogTest {
101     private static final long RETRY_MAX_COUNT = 30;
102     private static final long RETRY_TIMEOUT_MILLIS = 500;
103 
104     private static final String APP_A = "com.package.a";
105     private static final String APP_B = "com.package.b";
106     private static final String APP_C = "com.package.c";
107     private static final String APP_D = "com.package.d";
108     private static final long VERSION_CODE = 1L;
109     private static final String OBSERVER_NAME_1 = "observer1";
110     private static final String OBSERVER_NAME_2 = "observer2";
111     private static final String OBSERVER_NAME_3 = "observer3";
112     private static final String OBSERVER_NAME_4 = "observer4";
113     private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(10);
114     private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(50);
115 
116     @Rule
117     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
118 
119     @Rule
120     public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
121 
122     private final TestClock mTestClock = new TestClock();
123     private TestLooper mTestLooper;
124     private Executor mTestExecutor;
125     private Context mSpyContext;
126     // Keep track of all created watchdogs to apply device config changes
127     private List<PackageWatchdog> mAllocatedWatchdogs;
128     @Mock
129     private ConnectivityModuleConnector mConnectivityModuleConnector;
130     @Mock
131     private PackageManager mMockPackageManager;
132     @Mock Intent mMockIntent;
133     // Mock only sysprop apis
134     private PackageWatchdog.BootThreshold mSpyBootThreshold;
135     @Captor
136     private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor;
137     private MockitoSession mSession;
138     private HashMap<String, String> mSystemSettingsMap;
139     private HashMap<String, String> mCrashRecoveryPropertiesMap;
140 
retry(Supplier<Boolean> supplier)141     private boolean retry(Supplier<Boolean> supplier) throws Exception {
142         for (int i = 0; i < RETRY_MAX_COUNT; ++i) {
143             if (supplier.get()) {
144                 return true;
145             }
146             Thread.sleep(RETRY_TIMEOUT_MILLIS);
147         }
148         return false;
149     }
150 
151     @Before
setUp()152     public void setUp() throws Exception {
153         MockitoAnnotations.initMocks(this);
154         new File(InstrumentationRegistry.getContext().getFilesDir(),
155                 "package-watchdog.xml").delete();
156         adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG,
157                 Manifest.permission.WRITE_DEVICE_CONFIG,
158                 Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
159         mTestLooper = new TestLooper();
160         mTestExecutor = mTestLooper.getNewExecutor();
161         mSpyContext = spy(InstrumentationRegistry.getContext());
162         when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
163         when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
164             final PackageInfo res = new PackageInfo();
165             res.packageName = inv.getArgument(0);
166             res.setLongVersionCode(VERSION_CODE);
167             return res;
168         });
169         mSession = ExtendedMockito.mockitoSession()
170                 .initMocks(this)
171                 .strictness(Strictness.LENIENT)
172                 .spyStatic(SystemProperties.class)
173                 .startMocking();
174         mSystemSettingsMap = new HashMap<>();
175 
176 
177         // Mock SystemProperties setter and various getters
178         doAnswer((Answer<Void>) invocationOnMock -> {
179                     String key = invocationOnMock.getArgument(0);
180                     String value = invocationOnMock.getArgument(1);
181 
182                     mSystemSettingsMap.put(key, value);
183                     return null;
184                 }
185         ).when(() -> SystemProperties.set(anyString(), anyString()));
186 
187         doAnswer((Answer<Integer>) invocationOnMock -> {
188                     String key = invocationOnMock.getArgument(0);
189                     int defaultValue = invocationOnMock.getArgument(1);
190 
191                     String storedValue = mSystemSettingsMap.get(key);
192                     return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
193                 }
194         ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
195 
196         doAnswer((Answer<Long>) invocationOnMock -> {
197                     String key = invocationOnMock.getArgument(0);
198                     long defaultValue = invocationOnMock.getArgument(1);
199 
200                     String storedValue = mSystemSettingsMap.get(key);
201                     return storedValue == null ? defaultValue : Long.parseLong(storedValue);
202                 }
203         ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
204 
205         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
206                 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
207                 Boolean.toString(true), false);
208 
209         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
210                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
211                 Integer.toString(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT), false);
212 
213         mAllocatedWatchdogs = new ArrayList<>();
214     }
215 
216     @After
tearDown()217     public void tearDown() throws Exception {
218         dropShellPermissions();
219         mSession.finishMocking();
220         // Clean up listeners since too many listeners will delay notifications significantly
221         for (PackageWatchdog watchdog : mAllocatedWatchdogs) {
222             watchdog.removePropertyChangedListener();
223         }
224         mAllocatedWatchdogs.clear();
225     }
226 
227     @Test
testRegistration_singleObserver()228     public void testRegistration_singleObserver() {
229         PackageWatchdog watchdog = createWatchdog();
230         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
231 
232         watchdog.registerHealthObserver(mTestExecutor, observer);
233         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
234         raiseFatalFailureAndDispatch(watchdog,
235                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
236                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
237 
238         // The failed packages should be the same as the registered ones to ensure registration is
239         // done successfully
240         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
241     }
242 
243     @Test
testRegistration_multiObservers()244     public void testRegistration_multiObservers() {
245         PackageWatchdog watchdog = createWatchdog();
246         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
247         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
248 
249         watchdog.registerHealthObserver(mTestExecutor, observer1);
250         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
251         watchdog.registerHealthObserver(mTestExecutor, observer2);
252         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION, observer2);
253         raiseFatalFailureAndDispatch(watchdog,
254                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
255                         new VersionedPackage(APP_B, VERSION_CODE)),
256                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
257 
258         // The failed packages should be the same as the registered ones to ensure registration is
259         // done successfully
260         assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
261         assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
262     }
263 
264     @Test
testUnregistration_singleObserver()265     public void testUnregistration_singleObserver() {
266         PackageWatchdog watchdog = createWatchdog();
267         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
268 
269         watchdog.registerHealthObserver(mTestExecutor, observer);
270         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
271         watchdog.unregisterHealthObserver(observer);
272         raiseFatalFailureAndDispatch(watchdog,
273                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
274                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
275 
276         // We should have no failed packages to ensure unregistration is done successfully
277         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
278     }
279 
280     @Test
testUnregistration_multiObservers()281     public void testUnregistration_multiObservers() {
282         PackageWatchdog watchdog = createWatchdog();
283         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
284         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
285 
286         watchdog.registerHealthObserver(mTestExecutor, observer1);
287         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
288         watchdog.registerHealthObserver(mTestExecutor, observer2);
289         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
290         watchdog.unregisterHealthObserver(observer2);
291         raiseFatalFailureAndDispatch(watchdog,
292                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
293                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
294 
295         // observer1 should receive failed packages as intended.
296         assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
297         // observer2 should have no failed packages to ensure unregistration is done successfully
298         assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
299     }
300 
301     @Test
testExpiration_singleObserver()302     public void testExpiration_singleObserver() {
303         PackageWatchdog watchdog = createWatchdog();
304         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
305 
306         watchdog.registerHealthObserver(mTestExecutor, observer);
307         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
308         moveTimeForwardAndDispatch(SHORT_DURATION);
309         raiseFatalFailureAndDispatch(watchdog,
310                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
311                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
312 
313         // We should have no failed packages for the fatal failure is raised after expiration
314         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
315     }
316 
317     @Test
testExpiration_multiObservers()318     public void testExpiration_multiObservers() {
319         PackageWatchdog watchdog = createWatchdog();
320         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
321         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
322 
323         watchdog.registerHealthObserver(mTestExecutor, observer1);
324         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
325         watchdog.registerHealthObserver(mTestExecutor, observer2);
326         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observer2);
327         moveTimeForwardAndDispatch(SHORT_DURATION);
328         raiseFatalFailureAndDispatch(watchdog,
329                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
330                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
331 
332         // We should have no failed packages for the fatal failure is raised after expiration
333         assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
334         // We should have failed packages since observer2 hasn't expired
335         assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A);
336     }
337 
338     /** Observing already observed package extends the observation time. */
339     @Test
testObserveAlreadyObservedPackage()340     public void testObserveAlreadyObservedPackage() {
341         PackageWatchdog watchdog = createWatchdog();
342         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
343 
344         // Start observing APP_A
345         watchdog.registerHealthObserver(mTestExecutor, observer);
346         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
347 
348         // Then advance time half-way
349         moveTimeForwardAndDispatch(SHORT_DURATION / 2);
350 
351         // Start observing APP_A again
352         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
353 
354         // Then advance time such that it should have expired were it not for the second observation
355         moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
356 
357         raiseFatalFailureAndDispatch(watchdog,
358                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
359                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
360 
361         // Verify that we receive failed packages as expected for APP_A not expired
362         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
363     }
364 
365     /**
366      * Test package observers are persisted and loaded on startup
367      */
368     @Test
testPersistence()369     public void testPersistence() {
370         PackageWatchdog watchdog1 = createWatchdog();
371         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
372         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
373 
374         watchdog1.registerHealthObserver(mTestExecutor, observer1);
375         watchdog1.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
376         watchdog1.registerHealthObserver(mTestExecutor, observer2);
377         watchdog1.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION, observer2);
378         // Then advance time and run IO Handler so file is saved
379         mTestLooper.dispatchAll();
380         // Then start a new watchdog
381         PackageWatchdog watchdog2 = createWatchdog();
382         // Then resume observer1 and observer2
383         watchdog2.registerHealthObserver(mTestExecutor, observer1);
384         watchdog2.registerHealthObserver(mTestExecutor, observer2);
385         raiseFatalFailureAndDispatch(watchdog2,
386                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
387                         new VersionedPackage(APP_B, VERSION_CODE)),
388                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
389 
390         // We should receive failed packages as expected to ensure observers are persisted and
391         // resumed correctly
392         mTestLooper.dispatchAll();
393         assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
394         assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
395     }
396 
397     /**
398      * Test package failure under threshold does not notify observers
399      */
400     @Test
testNoPackageFailureBeforeThreshold()401     public void testNoPackageFailureBeforeThreshold() throws Exception {
402         PackageWatchdog watchdog = createWatchdog();
403         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
404         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
405 
406         watchdog.registerHealthObserver(mTestExecutor, observer2);
407         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
408         watchdog.registerHealthObserver(mTestExecutor, observer1);
409         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
410 
411         // Then fail APP_A below the threshold
412         for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) {
413             watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
414                     PackageWatchdog.FAILURE_REASON_UNKNOWN);
415         }
416 
417         // Run handler so package failures are dispatched to observers
418         mTestLooper.dispatchAll();
419 
420         // Verify that observers are not notified
421         assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
422         assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
423     }
424 
425     /**
426      * Test package failure and does not notify any observer because they are not observing
427      * the failed packages.
428      */
429     @Test
testPackageFailureDifferentPackageNotifyNone()430     public void testPackageFailureDifferentPackageNotifyNone() throws Exception {
431         PackageWatchdog watchdog = createWatchdog();
432         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
433         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
434 
435         watchdog.registerHealthObserver(mTestExecutor, observer2);
436         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
437         watchdog.registerHealthObserver(mTestExecutor, observer1);
438         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer1);
439 
440         // Then fail APP_C (not observed) above the threshold
441         raiseFatalFailureAndDispatch(watchdog,
442                 Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)),
443                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
444 
445         // Verify that observers are not notified
446         assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
447         assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
448     }
449 
450     /**
451      * Test package failure and does not notify any observer because the failed package version
452      * does not match the available rollback-from-version.
453      */
454     @Test
testPackageFailureDifferentVersionNotifyNone()455     public void testPackageFailureDifferentVersionNotifyNone() throws Exception {
456         PackageWatchdog watchdog = createWatchdog();
457         long differentVersionCode = 2L;
458         TestObserver observer = new TestObserver(OBSERVER_NAME_1) {
459                 @Override
460                 public int onHealthCheckFailed(VersionedPackage versionedPackage,
461                         int failureReason, int mitigationCount) {
462                     if (versionedPackage.getVersionCode() == VERSION_CODE) {
463                         // Only rollback for specific versionCode
464                         return PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
465                     }
466                     return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
467                 }
468             };
469 
470         watchdog.registerHealthObserver(mTestExecutor, observer);
471         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
472 
473         // Then fail APP_A (different version) above the threshold
474         raiseFatalFailureAndDispatch(watchdog,
475                 Arrays.asList(new VersionedPackage(APP_A, differentVersionCode)),
476                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
477 
478         // Verify that observers are not notified
479         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
480     }
481 
482     @Test
testPackageFailureNotifyAllDifferentImpactsRecoverability()483     public void testPackageFailureNotifyAllDifferentImpactsRecoverability() throws Exception {
484         PackageWatchdog watchdog = createWatchdog();
485         TestObserver observerNone = new TestObserver(OBSERVER_NAME_1,
486                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
487         TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2,
488                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
489         TestObserver observerMid = new TestObserver(OBSERVER_NAME_3,
490                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
491         TestObserver observerLow = new TestObserver(OBSERVER_NAME_4,
492                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
493 
494         // Start observing for all impact observers
495         watchdog.registerHealthObserver(mTestExecutor, observerNone);
496         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C, APP_D),
497                 SHORT_DURATION, observerNone);
498         watchdog.registerHealthObserver(mTestExecutor, observerHigh);
499         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C), SHORT_DURATION,
500                 observerHigh);
501         watchdog.registerHealthObserver(mTestExecutor, observerMid);
502         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION,
503                 observerMid);
504         watchdog.registerHealthObserver(mTestExecutor, observerLow);
505         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observerLow);
506 
507         // Then fail all apps above the threshold
508         raiseFatalFailureAndDispatch(watchdog,
509                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
510                         new VersionedPackage(APP_B, VERSION_CODE),
511                         new VersionedPackage(APP_C, VERSION_CODE),
512                         new VersionedPackage(APP_D, VERSION_CODE)),
513                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
514 
515         // Verify least impact observers are notifed of package failures
516         List<String> observerNonePackages = observerNone.mMitigatedPackages;
517         List<String> observerHighPackages = observerHigh.mMitigatedPackages;
518         List<String> observerMidPackages = observerMid.mMitigatedPackages;
519         List<String> observerLowPackages = observerLow.mMitigatedPackages;
520 
521         // APP_D failure observed by only observerNone is not caught cos its impact is none
522         assertThat(observerNonePackages).isEmpty();
523         // APP_C failure is caught by observerHigh cos it's the lowest impact observer
524         assertThat(observerHighPackages).containsExactly(APP_C);
525         // APP_B failure is caught by observerMid cos it's the lowest impact observer
526         assertThat(observerMidPackages).containsExactly(APP_B);
527         // APP_A failure is caught by observerLow cos it's the lowest impact observer
528         assertThat(observerLowPackages).containsExactly(APP_A);
529     }
530 
531     @Test
testPackageFailureNotifyLeastImpactSuccessivelyRecoverability()532     public void testPackageFailureNotifyLeastImpactSuccessivelyRecoverability() throws Exception {
533         PackageWatchdog watchdog = createWatchdog();
534         TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
535                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
536         TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2,
537                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
538 
539         // Start observing for observerFirst and observerSecond with failure handling
540         watchdog.registerHealthObserver(mTestExecutor, observerFirst);
541         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerFirst);
542         watchdog.registerHealthObserver(mTestExecutor, observerSecond);
543         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerSecond);
544 
545         // Then fail APP_A above the threshold
546         raiseFatalFailureAndDispatch(watchdog,
547                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
548                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
549 
550         // Verify only observerFirst is notifed
551         assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
552         assertThat(observerSecond.mMitigatedPackages).isEmpty();
553 
554         // After observerFirst handles failure, next action it has is high impact
555         observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
556         observerFirst.mMitigatedPackages.clear();
557         observerSecond.mMitigatedPackages.clear();
558 
559         // Then fail APP_A again above the threshold
560         raiseFatalFailureAndDispatch(watchdog,
561                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
562                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
563 
564         // Verify only observerSecond is notifed cos it has least impact
565         assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A);
566         assertThat(observerFirst.mMitigatedPackages).isEmpty();
567 
568         // After observerSecond handles failure, it has no further actions
569         observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
570         observerFirst.mMitigatedPackages.clear();
571         observerSecond.mMitigatedPackages.clear();
572 
573         // Then fail APP_A again above the threshold
574         raiseFatalFailureAndDispatch(watchdog,
575                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
576                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
577 
578         // Verify only observerFirst is notifed cos it has the only action
579         assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
580         assertThat(observerSecond.mMitigatedPackages).isEmpty();
581 
582         // After observerFirst handles failure, it too has no further actions
583         observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
584         observerFirst.mMitigatedPackages.clear();
585         observerSecond.mMitigatedPackages.clear();
586 
587         // Then fail APP_A again above the threshold
588         raiseFatalFailureAndDispatch(watchdog,
589                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
590                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
591 
592         // Verify no observer is notified cos no actions left
593         assertThat(observerFirst.mMitigatedPackages).isEmpty();
594         assertThat(observerSecond.mMitigatedPackages).isEmpty();
595     }
596 
597     @Test
testPackageFailureNotifyOneSameImpactRecoverabilityDetection()598     public void testPackageFailureNotifyOneSameImpactRecoverabilityDetection() throws Exception {
599         PackageWatchdog watchdog = createWatchdog();
600         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
601                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
602         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
603                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
604 
605         // Start observing for observer1 and observer2 with failure handling
606         watchdog.registerHealthObserver(mTestExecutor, observer2);
607         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
608         watchdog.registerHealthObserver(mTestExecutor, observer1);
609         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
610 
611         // Then fail APP_A above the threshold
612         raiseFatalFailureAndDispatch(watchdog,
613                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
614                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
615 
616         // Verify only one observer is notifed
617         assertThat(observer1.mMitigatedPackages).containsExactly(APP_A);
618         assertThat(observer2.mMitigatedPackages).isEmpty();
619     }
620 
621     /**
622      * Test package passing explicit health checks does not fail and vice versa.
623      */
624     @Test
testExplicitHealthChecks()625     public void testExplicitHealthChecks() throws Exception {
626         TestController controller = new TestController();
627         PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
628         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
629                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
630         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
631                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
632         TestObserver observer3 = new TestObserver(OBSERVER_NAME_3,
633                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
634 
635 
636         // Start observing with explicit health checks for APP_A and APP_B respectively
637         // with observer1 and observer2
638         controller.setSupportedPackages(Arrays.asList(APP_A, APP_B));
639         watchdog.registerHealthObserver(mTestExecutor, observer1);
640         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
641         watchdog.registerHealthObserver(mTestExecutor, observer2);
642         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer2);
643 
644         // Run handler so requests are dispatched to the controller
645         mTestLooper.dispatchAll();
646 
647         // Verify we requested health checks for APP_A and APP_B
648         List<String> requestedPackages = controller.getRequestedPackages();
649         assertThat(requestedPackages).containsExactly(APP_A, APP_B);
650 
651         // Then health check passed for APP_A (observer1 is aware)
652         controller.setPackagePassed(APP_A);
653 
654         // Then start observing APP_A with explicit health checks for observer3.
655         // Observer3 didn't exist when we got the explicit health check above, so
656         // it starts out with a non-passing explicit health check and has to wait for a pass
657         // otherwise it would be notified of APP_A failure on expiry
658         watchdog.registerHealthObserver(mTestExecutor, observer3);
659         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer3);
660 
661         // Then expire observers
662         moveTimeForwardAndDispatch(SHORT_DURATION);
663 
664         // Verify we cancelled all requests on expiry
665         assertThat(controller.getRequestedPackages()).isEmpty();
666 
667         // Verify observer1 is not notified
668         assertThat(observer1.mMitigatedPackages).isEmpty();
669 
670         // Verify observer2 is notifed because health checks for APP_B never passed
671         assertThat(observer2.mMitigatedPackages).containsExactly(APP_B);
672 
673         // Verify observer3 is notifed because health checks for APP_A did not pass before expiry
674         assertThat(observer3.mMitigatedPackages).containsExactly(APP_A);
675     }
676 
677     /**
678      * Test explicit health check state can be disabled and enabled correctly.
679      */
680     @Test
testExplicitHealthCheckStateChanges()681     public void testExplicitHealthCheckStateChanges() throws Exception {
682         TestController controller = new TestController();
683         PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
684         TestObserver observer = new TestObserver(OBSERVER_NAME_1,
685                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
686 
687         // Start observing with explicit health checks for APP_A and APP_B
688         controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
689         watchdog.registerHealthObserver(mTestExecutor, observer);
690         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
691         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), LONG_DURATION, observer);
692 
693         // Run handler so requests are dispatched to the controller
694         mTestLooper.dispatchAll();
695 
696         // Verify we requested health checks for APP_A and APP_B
697         List<String> requestedPackages = controller.getRequestedPackages();
698         assertThat(requestedPackages).containsExactly(APP_A, APP_B);
699 
700         // Disable explicit health checks (marks APP_A and APP_B as passed)
701         setExplicitHealthCheckEnabled(false);
702 
703         // Run handler so requests/cancellations are dispatched to the controller
704         mTestLooper.dispatchAll();
705 
706         // Verify all checks are cancelled
707         assertThat(controller.getRequestedPackages()).isEmpty();
708 
709         // Then expire APP_A
710         moveTimeForwardAndDispatch(SHORT_DURATION);
711 
712         // Verify APP_A is not failed (APP_B) is not expired yet
713         assertThat(observer.mMitigatedPackages).isEmpty();
714 
715         // Re-enable explicit health checks
716         setExplicitHealthCheckEnabled(true);
717 
718         // Run handler so requests/cancellations are dispatched to the controller
719         mTestLooper.dispatchAll();
720 
721         // Verify no requests are made cos APP_A is expired and APP_B was marked as passed
722         assertThat(controller.getRequestedPackages()).isEmpty();
723 
724         // Then set new supported packages
725         controller.setSupportedPackages(Arrays.asList(APP_C));
726         // Start observing APP_A and APP_C; only APP_C has support for explicit health checks
727         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_C), SHORT_DURATION, observer);
728 
729         // Run handler so requests/cancellations are dispatched to the controller
730         mTestLooper.dispatchAll();
731 
732         // Verify requests are only made for APP_C
733         requestedPackages = controller.getRequestedPackages();
734         assertThat(requestedPackages).containsExactly(APP_C);
735 
736         // Then expire APP_A and APP_C
737         moveTimeForwardAndDispatch(SHORT_DURATION);
738 
739         // Verify only APP_C is failed because explicit health checks was not supported for APP_A
740         assertThat(observer.mMitigatedPackages).containsExactly(APP_C);
741     }
742 
743     /**
744      * Tests failure when health check duration is different from package observation duration
745      * Failure is also notified only once.
746      */
747     @Test
testExplicitHealthCheckFailureBeforeExpiry()748     public void testExplicitHealthCheckFailureBeforeExpiry() throws Exception {
749         TestController controller = new TestController();
750         PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
751         TestObserver observer = new TestObserver(OBSERVER_NAME_1,
752                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
753 
754         // Start observing with explicit health checks for APP_A and
755         // package observation duration == LONG_DURATION
756         // health check duration == SHORT_DURATION (set by default in the TestController)
757         controller.setSupportedPackages(Arrays.asList(APP_A));
758         watchdog.registerHealthObserver(mTestExecutor, observer);
759         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observer);
760 
761         // Then APP_A has exceeded health check duration
762         moveTimeForwardAndDispatch(SHORT_DURATION);
763 
764         // Verify that health check is failed
765         assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
766 
767         // Clear failed packages and forward time to expire the observation duration
768         observer.mMitigatedPackages.clear();
769         moveTimeForwardAndDispatch(LONG_DURATION);
770 
771         // Verify that health check failure is not notified again
772         assertThat(observer.mMitigatedPackages).isEmpty();
773     }
774 
775     /**
776      * Tests failure when health check duration is different from package observation duration
777      * Failure is also notified only once.
778      */
779     @Test
testExplicitHealthCheckFailureAfterExpiry()780     public void testExplicitHealthCheckFailureAfterExpiry() {
781         TestController controller = new TestController();
782         PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
783         TestObserver observer = new TestObserver(OBSERVER_NAME_1,
784                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
785 
786         // Start observing with explicit health checks for APP_A and
787         // package observation duration == SHORT_DURATION / 2
788         // health check duration == SHORT_DURATION (set by default in the TestController)
789         controller.setSupportedPackages(Arrays.asList(APP_A));
790         watchdog.registerHealthObserver(mTestExecutor, observer);
791         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION / 2, observer);
792 
793         // Forward time to expire the observation duration
794         moveTimeForwardAndDispatch(SHORT_DURATION / 2);
795 
796         // Verify that health check is failed
797         assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
798 
799         // Clear failed packages and forward time to expire the health check duration
800         observer.mMitigatedPackages.clear();
801         moveTimeForwardAndDispatch(SHORT_DURATION);
802 
803         // Verify that health check failure is not notified again
804         assertThat(observer.mMitigatedPackages).isEmpty();
805     }
806 
807     /** Tests {@link MonitoredPackage} health check state transitions. */
808     @Test
testPackageHealthCheckStateTransitions()809     public void testPackageHealthCheckStateTransitions() {
810         TestController controller = new TestController();
811         PackageWatchdog wd = createWatchdog(controller, true /* withPackagesReady */);
812         MonitoredPackage m1 = wd.newMonitoredPackage(APP_A, LONG_DURATION,
813                 false /* hasPassedHealthCheck */);
814         MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false);
815         MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false);
816         MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true,
817                 new LongArrayQueue());
818 
819         // Verify transition: inactive -> active -> passed
820         // Verify initially inactive
821         assertThat(m1.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
822         // Verify still inactive, until we #setHealthCheckActiveLocked
823         assertThat(m1.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.INACTIVE);
824         // Verify now active
825         assertThat(m1.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo(
826                 HealthCheckState.ACTIVE);
827         // Verify now passed
828         assertThat(m1.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.PASSED);
829 
830         // Verify transition: inactive -> active -> failed
831         // Verify initially inactive
832         assertThat(m2.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
833         // Verify now active
834         assertThat(m2.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo(
835                 HealthCheckState.ACTIVE);
836         // Verify now failed
837         assertThat(m2.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.FAILED);
838 
839         // Verify transition: inactive -> failed
840         // Verify initially inactive
841         assertThat(m3.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
842         // Verify now failed because package expired
843         assertThat(m3.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.FAILED);
844         // Verify remains failed even when asked to pass
845         assertThat(m3.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.FAILED);
846 
847         // Verify transition: passed
848         assertThat(m4.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.PASSED);
849         // Verify remains passed even if health check fails
850         assertThat(m4.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.PASSED);
851         // Verify remains passed even if package expires
852         assertThat(m4.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.PASSED);
853     }
854 
855     @Test
856     @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_CRASHRECOVERY)
testNetworkStackFailureRecoverabilityDetection()857     public void testNetworkStackFailureRecoverabilityDetection() {
858         final PackageWatchdog wd = createWatchdog();
859 
860         // Start observing with failure handling
861         TestObserver observer = new TestObserver(OBSERVER_NAME_1,
862                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
863         wd.startExplicitHealthCheck(Collections.singletonList(APP_A), SHORT_DURATION, observer);
864 
865         // Notify of NetworkStack failure
866         mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
867 
868         // Run handler so package failures are dispatched to observers
869         mTestLooper.dispatchAll();
870 
871         // Verify the NetworkStack observer is notified
872         assertThat(observer.mMitigatedPackages).isEmpty();
873     }
874 
875     /** Test default values are used when device property is invalid. */
876     @Test
testInvalidConfig_watchdogTriggerFailureCount()877     public void testInvalidConfig_watchdogTriggerFailureCount() {
878         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
879                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
880                 Integer.toString(-1), /*makeDefault*/false);
881         PackageWatchdog watchdog = createWatchdog();
882         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
883 
884         watchdog.registerHealthObserver(mTestExecutor, observer);
885         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
886         // Fail APP_A below the threshold which should not trigger package failures
887         for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
888             watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
889                     PackageWatchdog.FAILURE_REASON_UNKNOWN);
890         }
891         mTestLooper.dispatchAll();
892         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
893 
894         // One more to trigger the package failure
895         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
896                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
897         mTestLooper.dispatchAll();
898         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
899     }
900 
901     /** Test default values are used when device property is invalid. */
902     @Test
testInvalidConfig_watchdogTriggerDurationMillis()903     public void testInvalidConfig_watchdogTriggerDurationMillis() {
904         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
905                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
906                 Integer.toString(2), /*makeDefault*/false);
907         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
908                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
909                 Integer.toString(-1), /*makeDefault*/false);
910         PackageWatchdog watchdog = createWatchdog();
911         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
912 
913         watchdog.registerHealthObserver(mTestExecutor, observer);
914         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), Long.MAX_VALUE, observer);
915         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
916                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
917         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
918         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
919                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
920         mTestLooper.dispatchAll();
921 
922         // We shouldn't receive APP_A since the interval of 2 failures is greater than
923         // DEFAULT_TRIGGER_FAILURE_DURATION_MS.
924         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
925 
926         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
927                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
928         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1);
929         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
930                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
931         mTestLooper.dispatchAll();
932 
933         // We should receive APP_B since the interval of 2 failures is less than
934         // DEFAULT_TRIGGER_FAILURE_DURATION_MS.
935         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_B);
936     }
937 
938     /**
939      * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
940      * offered an invalid durationMs.
941      */
942     @Test
testInvalidMonitoringDuration_beforeExpiry()943     public void testInvalidMonitoringDuration_beforeExpiry() {
944         PackageWatchdog watchdog = createWatchdog();
945         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
946 
947         watchdog.registerHealthObserver(mTestExecutor, observer);
948         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), -1, observer);
949         // Note: Don't move too close to the expiration time otherwise the handler will be thrashed
950         // by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
951         // small timeouts.
952         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100);
953         raiseFatalFailureAndDispatch(watchdog,
954                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
955                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
956 
957         // We should receive APP_A since the observer hasn't expired
958         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
959     }
960 
961     /**
962      * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
963      * offered an invalid durationMs.
964      */
965     @Test
testInvalidMonitoringDuration_afterExpiry()966     public void testInvalidMonitoringDuration_afterExpiry() {
967         PackageWatchdog watchdog = createWatchdog();
968         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
969 
970         watchdog.registerHealthObserver(mTestExecutor, observer);
971         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), -1, observer);
972         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
973         raiseFatalFailureAndDispatch(watchdog,
974                 Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
975                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
976 
977         // We should receive nothing since the observer has expired
978         assertThat(observer.mHealthCheckFailedPackages).isEmpty();
979     }
980 
981     /** Test we are notified when enough failures are triggered within any window. */
982     @Test
testFailureTriggerWindow()983     public void testFailureTriggerWindow() {
984         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
985                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
986                 Integer.toString(3), /*makeDefault*/false);
987         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
988                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
989                 Integer.toString(1000), /*makeDefault*/false);
990         PackageWatchdog watchdog = createWatchdog();
991         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
992 
993         watchdog.registerHealthObserver(mTestExecutor, observer);
994         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), Long.MAX_VALUE, observer);
995         // Raise 2 failures at t=0 and t=900 respectively
996         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
997                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
998         moveTimeForwardAndDispatch(900);
999         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
1000                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
1001 
1002         // Raise 2 failures at t=1100
1003         moveTimeForwardAndDispatch(200);
1004         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
1005                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
1006         watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
1007                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
1008         mTestLooper.dispatchAll();
1009 
1010         // We should receive APP_A since there are 3 failures within 1000ms window
1011         assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
1012     }
1013 
1014     /** Test that observers execute correctly for failures reasons that go through thresholding. */
1015     @Test
testNonImmediateFailureReasons()1016     public void testNonImmediateFailureReasons() {
1017         PackageWatchdog watchdog = createWatchdog();
1018         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
1019         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
1020 
1021         watchdog.registerHealthObserver(mTestExecutor, observer1);
1022         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
1023         watchdog.registerHealthObserver(mTestExecutor, observer2);
1024         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer2);
1025 
1026         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1027                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
1028         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
1029                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
1030 
1031         assertThat(observer1.getLastFailureReason()).isEqualTo(
1032                 PackageWatchdog.FAILURE_REASON_APP_CRASH);
1033         assertThat(observer2.getLastFailureReason()).isEqualTo(
1034                 PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
1035     }
1036 
1037     /** Test that observers execute correctly for failures reasons that skip thresholding. */
1038     @Test
testImmediateFailures()1039     public void testImmediateFailures() {
1040         PackageWatchdog watchdog = createWatchdog();
1041         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
1042 
1043         watchdog.registerHealthObserver(mTestExecutor, observer1);
1044         watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
1045 
1046         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1047                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
1048         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
1049                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
1050 
1051         assertThat(observer1.mMitigatedPackages).containsExactly(APP_A, APP_B);
1052     }
1053 
1054     /**
1055      * Test that a persistent observer will mitigate failures if it wishes to observe a package.
1056      */
1057     @Test
testPersistentObserverWatchesPackage()1058     public void testPersistentObserverWatchesPackage() {
1059         PackageWatchdog watchdog = createWatchdog();
1060         TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
1061         persistentObserver.setPersistent(true);
1062         persistentObserver.setMayObservePackages(true);
1063 
1064         watchdog.registerHealthObserver(mTestExecutor, persistentObserver);
1065         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, persistentObserver);
1066 
1067         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1068                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1069         assertThat(persistentObserver.mHealthCheckFailedPackages).containsExactly(APP_A);
1070     }
1071 
1072     /**
1073      * Test that a persistent observer will not mitigate failures if it does not wish to observe
1074      * a given package.
1075      */
1076     @Test
testPersistentObserverDoesNotWatchPackage()1077     public void testPersistentObserverDoesNotWatchPackage() {
1078         PackageWatchdog watchdog = createWatchdog();
1079         TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
1080         persistentObserver.setPersistent(true);
1081         persistentObserver.setMayObservePackages(false);
1082 
1083         watchdog.registerHealthObserver(mTestExecutor, persistentObserver);
1084         watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, persistentObserver);
1085 
1086         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1087                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1088         assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
1089     }
1090 
1091     @Test
testBootLoopDetection_meetsThresholdRecoverability()1092     public void testBootLoopDetection_meetsThresholdRecoverability() {
1093         PackageWatchdog watchdog = createWatchdog();
1094         TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
1095         watchdog.registerHealthObserver(mTestExecutor, bootObserver);
1096         for (int i = 0; i < 15; i++) {
1097             watchdog.noteBoot();
1098         }
1099         mTestLooper.dispatchAll();
1100         assertThat(bootObserver.mitigatedBootLoop()).isTrue();
1101     }
1102 
1103     /**
1104      * Ensure that boot loop mitigation is not done when the number of boots does not meet the
1105      * threshold.
1106      */
1107     @Test
testBootLoopDetection_doesNotMeetThreshold()1108     public void testBootLoopDetection_doesNotMeetThreshold() {
1109         PackageWatchdog watchdog = createWatchdog();
1110         TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
1111         watchdog.registerHealthObserver(mTestExecutor, bootObserver);
1112         for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
1113             watchdog.noteBoot();
1114         }
1115         mTestLooper.dispatchAll();
1116         assertThat(bootObserver.mitigatedBootLoop()).isFalse();
1117     }
1118 
1119     /**
1120      * Ensure that boot loop mitigation is not done when the number of boots does not meet the
1121      * threshold.
1122      */
1123     @Test
testBootLoopDetection_doesNotMeetThresholdRecoverabilityLowImpact()1124     public void testBootLoopDetection_doesNotMeetThresholdRecoverabilityLowImpact() {
1125         PackageWatchdog watchdog = createWatchdog();
1126         TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
1127                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
1128         watchdog.registerHealthObserver(mTestExecutor, bootObserver);
1129         for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
1130             watchdog.noteBoot();
1131         }
1132         mTestLooper.dispatchAll();
1133         assertThat(bootObserver.mitigatedBootLoop()).isFalse();
1134     }
1135 
1136     @Test
testBootLoopMitigationDoneForLowestUserImpactRecoverability()1137     public void testBootLoopMitigationDoneForLowestUserImpactRecoverability() {
1138         PackageWatchdog watchdog = createWatchdog();
1139         TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
1140         bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
1141         TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
1142         bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
1143         watchdog.registerHealthObserver(mTestExecutor, bootObserver1);
1144         watchdog.registerHealthObserver(mTestExecutor, bootObserver2);
1145         for (int i = 0; i < 15; i++) {
1146             watchdog.noteBoot();
1147         }
1148         mTestLooper.dispatchAll();
1149         assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
1150         assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
1151     }
1152 
1153     @Test
testMultipleBootLoopMitigationRecoverabilityLowImpact()1154     public void testMultipleBootLoopMitigationRecoverabilityLowImpact() {
1155         PackageWatchdog watchdog = createWatchdog();
1156         TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
1157                 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
1158         watchdog.registerHealthObserver(mTestExecutor, bootObserver);
1159         for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) {
1160             watchdog.noteBoot();
1161         }
1162         for (int i = 0; i < 4; i++) {
1163                 watchdog.noteBoot();
1164         }
1165 
1166         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
1167 
1168         for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) {
1169             watchdog.noteBoot();
1170         }
1171         for (int i = 0; i < 4; i++) {
1172                 watchdog.noteBoot();
1173         }
1174         mTestLooper.dispatchAll();
1175         assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
1176     }
1177 
1178     /**
1179      * Ensure that passing a null list of failed packages does not cause any mitigation logic to
1180      * execute.
1181      */
1182     @Test
testNullFailedPackagesList()1183     public void testNullFailedPackagesList() {
1184         PackageWatchdog watchdog = createWatchdog();
1185         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
1186         watchdog.registerHealthObserver(mTestExecutor, observer1);
1187         watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, observer1);
1188 
1189         raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
1190         assertThat(observer1.mMitigatedPackages).isEmpty();
1191     }
1192 
1193     /**
1194      * Test to verify that Package Watchdog syncs health check requests with the controller
1195      * correctly, and that the requests are only synced when the set of observed packages
1196      * changes.
1197      */
1198     @Test
testSyncHealthCheckRequests()1199     public void testSyncHealthCheckRequests() {
1200         TestController testController = spy(TestController.class);
1201         testController.setSupportedPackages(List.of(APP_A, APP_B, APP_C));
1202         PackageWatchdog watchdog = createWatchdog(testController, true);
1203 
1204         TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1);
1205         watchdog.registerHealthObserver(mTestExecutor, testObserver1);
1206         watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, testObserver1);
1207         mTestLooper.dispatchAll();
1208 
1209         TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2);
1210         watchdog.registerHealthObserver(mTestExecutor, testObserver2);
1211         watchdog.startExplicitHealthCheck(List.of(APP_B), LONG_DURATION, testObserver2);
1212         mTestLooper.dispatchAll();
1213 
1214         TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3);
1215         watchdog.registerHealthObserver(mTestExecutor, testObserver3);
1216         watchdog.startExplicitHealthCheck(List.of(APP_C), LONG_DURATION, testObserver3);
1217         mTestLooper.dispatchAll();
1218 
1219         watchdog.unregisterHealthObserver(testObserver1);
1220         mTestLooper.dispatchAll();
1221 
1222         watchdog.unregisterHealthObserver(testObserver2);
1223         mTestLooper.dispatchAll();
1224 
1225         watchdog.unregisterHealthObserver(testObserver3);
1226         mTestLooper.dispatchAll();
1227 
1228         List<Set> expectedSyncRequests = List.of(
1229                 Set.of(),
1230                 Set.of(APP_A),
1231                 Set.of(APP_A, APP_B),
1232                 Set.of(APP_A, APP_B, APP_C),
1233                 Set.of(APP_B, APP_C),
1234                 Set.of(APP_C),
1235                 Set.of()
1236         );
1237         assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests);
1238     }
1239 
1240     /**
1241      * Ensure that the failure history of a package is preserved when making duplicate calls to
1242      * observe the package.
1243      */
1244     @Test
testFailureHistoryIsPreserved()1245     public void testFailureHistoryIsPreserved() {
1246         PackageWatchdog watchdog = createWatchdog();
1247         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
1248         watchdog.registerHealthObserver(mTestExecutor, observer);
1249         watchdog.startExplicitHealthCheck(List.of(APP_A), SHORT_DURATION, observer);
1250         for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
1251             watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
1252                     PackageWatchdog.FAILURE_REASON_UNKNOWN);
1253         }
1254         mTestLooper.dispatchAll();
1255         assertThat(observer.mMitigatedPackages).isEmpty();
1256         watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, observer);
1257         watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
1258                 PackageWatchdog.FAILURE_REASON_UNKNOWN);
1259         mTestLooper.dispatchAll();
1260         assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A));
1261     }
1262 
1263     /**
1264      * Ensure that the sliding window logic results in the correct mitigation count being sent to
1265      * an observer.
1266      */
1267     @Test
testMitigationSlidingWindow()1268     public void testMitigationSlidingWindow() {
1269         PackageWatchdog watchdog = createWatchdog();
1270         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
1271         watchdog.registerHealthObserver(mTestExecutor, observer);
1272         watchdog.startExplicitHealthCheck(List.of(APP_A),
1273                 PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2, observer);
1274 
1275 
1276         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1277                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1278 
1279         moveTimeForwardAndDispatch(TimeUnit.MINUTES.toMillis(10));
1280 
1281         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1282                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1283         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1284                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1285 
1286         moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS
1287                 - TimeUnit.MINUTES.toMillis(1));
1288 
1289         // The first failure will be outside the threshold.
1290         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1291                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1292 
1293         moveTimeForwardAndDispatch(TimeUnit.MINUTES.toMillis(20));
1294 
1295         // The next 2 failures will also be outside the threshold.
1296         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1297                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1298         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
1299                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
1300 
1301         assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3));
1302     }
1303 
1304     @Test
testNormalizingMitigationCalls()1305     public void testNormalizingMitigationCalls()  {
1306         PackageWatchdog watchdog = createWatchdog();
1307 
1308         LongArrayQueue mitigationCalls = new LongArrayQueue();
1309         mitigationCalls.addLast(1000);
1310         mitigationCalls.addLast(2000);
1311         mitigationCalls.addLast(3000);
1312 
1313         MonitoredPackage pkg = watchdog.newMonitoredPackage(
1314                 "test", 123, 456, true, mitigationCalls);
1315 
1316         // Make current system uptime 10000ms.
1317         moveTimeForwardAndDispatch(9999);
1318 
1319         LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls();
1320 
1321         assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size());
1322 
1323         for (int i = 0; i < mitigationCalls.size(); i++) {
1324             assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000);
1325         }
1326     }
1327 
1328     /**
1329      * Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist
1330      * across reboots.
1331      */
1332     @Test
testWritingAndReadingMonitoredPackage()1333     public void testWritingAndReadingMonitoredPackage() throws Exception {
1334         PackageWatchdog watchdog = createWatchdog();
1335 
1336         LongArrayQueue mitigationCalls = new LongArrayQueue();
1337         mitigationCalls.addLast(1000);
1338         mitigationCalls.addLast(2000);
1339         mitigationCalls.addLast(3000);
1340         MonitoredPackage writePkg = watchdog.newMonitoredPackage(
1341                 "test.package", 1000, 2000, true, mitigationCalls);
1342 
1343         // Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation
1344         // calls will each be reduced by 4000.
1345         moveTimeForwardAndDispatch(3999);
1346         LongArrayQueue expectedCalls = new LongArrayQueue();
1347         expectedCalls.addLast(-3000);
1348         expectedCalls.addLast(-2000);
1349         expectedCalls.addLast(-1000);
1350         MonitoredPackage expectedPkg = watchdog.newMonitoredPackage(
1351                 "test.package", 1000, 2000, true, expectedCalls);
1352 
1353         // Write the package
1354         File tmpFile = File.createTempFile("package-watchdog-test", ".xml");
1355         AtomicFile testFile = new AtomicFile(tmpFile);
1356         FileOutputStream stream = testFile.startWrite();
1357         TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream);
1358         outputSerializer.startDocument(null, true);
1359         writePkg.writeLocked(outputSerializer);
1360         outputSerializer.endDocument();
1361         testFile.finishWrite(stream);
1362 
1363         // Read the package
1364         TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead());
1365         XmlUtils.beginDocument(parser, "package");
1366         MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser);
1367 
1368         assertTrue(readPkg.isEqualTo(expectedPkg));
1369     }
1370 
1371     /**
1372      * Ensure that a {@link ObserverInternal} may be correctly written and read in order to persist
1373      * across reboots.
1374      */
1375     @Test
1376     @SuppressWarnings("GuardedBy")
testWritingAndReadingObserverInternalRecoverability()1377     public void testWritingAndReadingObserverInternalRecoverability() throws Exception {
1378         PackageWatchdog watchdog = createWatchdog();
1379 
1380         LongArrayQueue mitigationCalls = new LongArrayQueue();
1381         mitigationCalls.addLast(1000);
1382         mitigationCalls.addLast(2000);
1383         mitigationCalls.addLast(3000);
1384         MonitoredPackage writePkg = watchdog.newMonitoredPackage(
1385                 "test.package", 1000, 2000, true, mitigationCalls);
1386         final int bootMitigationCount = 4;
1387         ObserverInternal writeObserver = new ObserverInternal("test", List.of(writePkg),
1388                 bootMitigationCount);
1389 
1390         // Write the observer
1391         File tmpFile = File.createTempFile("observer-watchdog-test", ".xml");
1392         AtomicFile testFile = new AtomicFile(tmpFile);
1393         FileOutputStream stream = testFile.startWrite();
1394         TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream);
1395         outputSerializer.startDocument(null, true);
1396         writeObserver.writeLocked(outputSerializer);
1397         outputSerializer.endDocument();
1398         testFile.finishWrite(stream);
1399 
1400         // Read the observer
1401         TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead());
1402         XmlUtils.beginDocument(parser, "observer");
1403         ObserverInternal readObserver = ObserverInternal.read(parser, watchdog);
1404 
1405         assertThat(readObserver.name).isEqualTo(writeObserver.name);
1406         assertThat(readObserver.getBootMitigationCount()).isEqualTo(bootMitigationCount);
1407     }
1408 
1409     /**
1410      * Ensure that boot mitigation counts may be correctly written and read as metadata
1411      * in order to persist across reboots.
1412      */
1413     @Test
1414     @SuppressWarnings("GuardedBy")
testWritingAndReadingMetadataBootMitigationCountRecoverability()1415     public void testWritingAndReadingMetadataBootMitigationCountRecoverability() throws Exception {
1416         PackageWatchdog watchdog = createWatchdog();
1417         String filePath = InstrumentationRegistry.getContext().getFilesDir().toString()
1418                 + "metadata_file.txt";
1419 
1420         ObserverInternal observer1 = new ObserverInternal("test1", List.of(), 1);
1421         ObserverInternal observer2 = new ObserverInternal("test2", List.of(), 2);
1422         watchdog.registerObserverInternal(observer1);
1423         watchdog.registerObserverInternal(observer2);
1424 
1425         mSpyBootThreshold = spy(watchdog.new BootThreshold(
1426                 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
1427                 PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
1428 
1429         watchdog.saveAllObserversBootMitigationCountToMetadata(filePath);
1430 
1431         observer1.setBootMitigationCount(0);
1432         observer2.setBootMitigationCount(0);
1433         assertThat(observer1.getBootMitigationCount()).isEqualTo(0);
1434         assertThat(observer2.getBootMitigationCount()).isEqualTo(0);
1435 
1436         mSpyBootThreshold.readAllObserversBootMitigationCountIfNecessary(filePath);
1437 
1438         assertThat(observer1.getBootMitigationCount()).isEqualTo(1);
1439         assertThat(observer2.getBootMitigationCount()).isEqualTo(2);
1440     }
1441 
1442     /**
1443      * Tests device config changes are propagated correctly.
1444      */
1445     @Test
testDeviceConfigChange_explicitHealthCheckEnabled()1446     public void testDeviceConfigChange_explicitHealthCheckEnabled() throws Exception {
1447         TestController controller = new TestController();
1448         PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
1449         assertThat(controller.mIsEnabled).isTrue();
1450 
1451         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1452                 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
1453                 Boolean.toString(false), /*makeDefault*/false);
1454         retry(() -> !controller.mIsEnabled);
1455         assertThat(controller.mIsEnabled).isFalse();
1456     }
1457 
1458     /**
1459      * Tests device config changes are propagated correctly.
1460      */
1461     @Test
testDeviceConfigChange_triggerFailureCount()1462     public void testDeviceConfigChange_triggerFailureCount() throws Exception {
1463         PackageWatchdog watchdog = createWatchdog();
1464 
1465         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1466                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
1467                 Integer.toString(777), false);
1468         retry(() -> watchdog.getTriggerFailureCount() == 777);
1469         assertThat(watchdog.getTriggerFailureCount()).isEqualTo(777);
1470 
1471         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1472                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
1473                 Integer.toString(0), false);
1474         retry(() -> watchdog.getTriggerFailureCount()
1475                 == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT);
1476         assertThat(watchdog.getTriggerFailureCount()).isEqualTo(
1477                 PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT);
1478     }
1479 
1480     /**
1481      * Tests device config changes are propagated correctly.
1482      */
1483     @Test
testDeviceConfigChange_triggerFailureDurationMs()1484     public void testDeviceConfigChange_triggerFailureDurationMs() throws Exception {
1485         PackageWatchdog watchdog = createWatchdog();
1486 
1487         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1488                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
1489                 Integer.toString(888), false);
1490         retry(() -> watchdog.getTriggerFailureDurationMs() == 888);
1491         assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo(888);
1492 
1493         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1494                 PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
1495                 Integer.toString(0), false);
1496         retry(() -> watchdog.getTriggerFailureDurationMs()
1497                 == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS);
1498         assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo(
1499                 PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS);
1500     }
1501 
1502     /**
1503      * Tests device config changes are propagated correctly.
1504      */
1505     @Test
testRegisterShutdownBroadcastReceiver()1506     public void testRegisterShutdownBroadcastReceiver() {
1507         PackageWatchdog watchdog = createWatchdog();
1508         doReturn(mMockIntent).when(mSpyContext)
1509                 .registerReceiverForAllUsers(any(), any(), any(), any());
1510 
1511         watchdog.registerShutdownBroadcastReceiver();
1512         verify(mSpyContext).registerReceiverForAllUsers(any(), any(), eq(null), eq(null));
1513     }
1514 
adoptShellPermissions(String... permissions)1515     private void adoptShellPermissions(String... permissions) {
1516         InstrumentationRegistry
1517                 .getInstrumentation()
1518                 .getUiAutomation()
1519                 .adoptShellPermissionIdentity(permissions);
1520     }
1521 
dropShellPermissions()1522     private void dropShellPermissions() {
1523         InstrumentationRegistry
1524                 .getInstrumentation()
1525                 .getUiAutomation()
1526                 .dropShellPermissionIdentity();
1527     }
1528 
setExplicitHealthCheckEnabled(boolean enabled)1529     private void setExplicitHealthCheckEnabled(boolean enabled) {
1530         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
1531                 PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
1532                 Boolean.toString(enabled), /*makeDefault*/false);
1533         // Call updateConfigs() so device config changes take effect immediately
1534         for (PackageWatchdog watchdog : mAllocatedWatchdogs) {
1535             watchdog.updateConfigs();
1536         }
1537     }
1538 
moveTimeForwardAndDispatch(long milliSeconds)1539     private void moveTimeForwardAndDispatch(long milliSeconds) {
1540         // Exhaust all due runnables now which shouldn't be executed after time-leap
1541         mTestLooper.dispatchAll();
1542         mTestClock.moveTimeForward(milliSeconds);
1543         mTestLooper.moveTimeForward(milliSeconds);
1544         mTestLooper.dispatchAll();
1545     }
1546 
1547     /** Trigger package failures above the threshold. */
raiseFatalFailureAndDispatch(PackageWatchdog watchdog, List<VersionedPackage> packages, int failureReason)1548     private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
1549             List<VersionedPackage> packages, int failureReason) {
1550         long triggerFailureCount = watchdog.getTriggerFailureCount();
1551         if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
1552                 || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
1553             triggerFailureCount = 1;
1554         }
1555         for (int i = 0; i < triggerFailureCount; i++) {
1556             watchdog.notifyPackageFailure(packages, failureReason);
1557         }
1558         mTestLooper.dispatchAll();
1559         moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
1560     }
1561 
createWatchdog()1562     private PackageWatchdog createWatchdog() {
1563         return createWatchdog(new TestController(), true /* withPackagesReady */);
1564     }
1565 
createWatchdog(TestController controller, boolean withPackagesReady)1566     private PackageWatchdog createWatchdog(TestController controller, boolean withPackagesReady) {
1567         AtomicFile policyFile =
1568                 new AtomicFile(new File(mSpyContext.getFilesDir(), "package-watchdog.xml"));
1569         Handler handler = new Handler(mTestLooper.getLooper());
1570         PackageWatchdog watchdog =
1571                 new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
1572                          mTestClock);
1573         mockCrashRecoveryProperties(watchdog);
1574 
1575         // Verify controller is not automatically started
1576         assertThat(controller.mIsEnabled).isFalse();
1577         if (withPackagesReady) {
1578             // Only capture the NetworkStack callback for the latest registered watchdog
1579             reset(mConnectivityModuleConnector);
1580             watchdog.onPackagesReady();
1581             // Verify controller by default is started when packages are ready
1582             assertThat(controller.mIsEnabled).isTrue();
1583 
1584             if (!Flags.refactorCrashrecovery()) {
1585                 verify(mConnectivityModuleConnector).registerHealthListener(
1586                         mConnectivityModuleCallbackCaptor.capture());
1587             }
1588         }
1589         mAllocatedWatchdogs.add(watchdog);
1590         return watchdog;
1591     }
1592 
1593     // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
mockCrashRecoveryProperties(PackageWatchdog watchdog)1594     private void mockCrashRecoveryProperties(PackageWatchdog watchdog) {
1595         mCrashRecoveryPropertiesMap = new HashMap<>();
1596 
1597         try {
1598             mSpyBootThreshold = spy(watchdog.new BootThreshold(
1599                     PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
1600                     PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
1601 
1602             doAnswer((Answer<Integer>) invocationOnMock -> {
1603                 String storedValue = mCrashRecoveryPropertiesMap
1604                         .getOrDefault("crashrecovery.rescue_boot_count", "0");
1605                 return Integer.parseInt(storedValue);
1606             }).when(mSpyBootThreshold).getCount();
1607             doAnswer((Answer<Void>) invocationOnMock -> {
1608                 int count = invocationOnMock.getArgument(0);
1609                 mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count",
1610                         Integer.toString(count));
1611                 return null;
1612             }).when(mSpyBootThreshold).setCount(anyInt());
1613 
1614             doAnswer((Answer<Integer>) invocationOnMock -> {
1615                 String storedValue = mCrashRecoveryPropertiesMap
1616                         .getOrDefault("crashrecovery.boot_mitigation_count", "0");
1617                 return Integer.parseInt(storedValue);
1618             }).when(mSpyBootThreshold).getMitigationCount();
1619             doAnswer((Answer<Void>) invocationOnMock -> {
1620                 int count = invocationOnMock.getArgument(0);
1621                 mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count",
1622                         Integer.toString(count));
1623                 return null;
1624             }).when(mSpyBootThreshold).setMitigationCount(anyInt());
1625 
1626             doAnswer((Answer<Long>) invocationOnMock -> {
1627                 String storedValue = mCrashRecoveryPropertiesMap
1628                         .getOrDefault("crashrecovery.rescue_boot_start", "0");
1629                 return Long.parseLong(storedValue);
1630             }).when(mSpyBootThreshold).getStart();
1631             doAnswer((Answer<Void>) invocationOnMock -> {
1632                 long count = invocationOnMock.getArgument(0);
1633                 mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start",
1634                         Long.toString(count));
1635                 return null;
1636             }).when(mSpyBootThreshold).setStart(anyLong());
1637 
1638             doAnswer((Answer<Long>) invocationOnMock -> {
1639                 String storedValue = mCrashRecoveryPropertiesMap
1640                         .getOrDefault("crashrecovery.boot_mitigation_start", "0");
1641                 return Long.parseLong(storedValue);
1642             }).when(mSpyBootThreshold).getMitigationStart();
1643             doAnswer((Answer<Void>) invocationOnMock -> {
1644                 long count = invocationOnMock.getArgument(0);
1645                 mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start",
1646                         Long.toString(count));
1647                 return null;
1648             }).when(mSpyBootThreshold).setMitigationStart(anyLong());
1649 
1650             Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold");
1651             mBootThresholdField.setAccessible(true);
1652             mBootThresholdField.set(watchdog, mSpyBootThreshold);
1653         } catch (Exception e) {
1654             // tests will fail, just printing the error
1655             System.out.println("Error detected while spying BootThreshold" + e.getMessage());
1656         }
1657     }
1658 
1659     private static class TestObserver implements PackageHealthObserver {
1660         private final String mName;
1661         private int mImpact;
1662         private int mLastFailureReason;
1663         private boolean mIsPersistent = false;
1664         private boolean mMayObservePackages = false;
1665         private boolean mMitigatedBootLoop = false;
1666         final List<String> mHealthCheckFailedPackages = new ArrayList<>();
1667         final List<String> mMitigatedPackages = new ArrayList<>();
1668         final List<Integer> mMitigationCounts = new ArrayList<>();
1669         final List<Integer> mBootMitigationCounts = new ArrayList<>();
1670 
TestObserver(String name)1671         TestObserver(String name) {
1672             mName = name;
1673             mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
1674         }
1675 
TestObserver(String name, int impact)1676         TestObserver(String name, int impact) {
1677             mName = name;
1678             mImpact = impact;
1679         }
1680 
onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason, int mitigationCount)1681         public int onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason,
1682                 int mitigationCount) {
1683             mHealthCheckFailedPackages.add(versionedPackage.getPackageName());
1684             return mImpact;
1685         }
1686 
onExecuteHealthCheckMitigation(VersionedPackage versionedPackage, int failureReason, int mitigationCount)1687         public int onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
1688                 int failureReason, int mitigationCount) {
1689             mMitigatedPackages.add(versionedPackage.getPackageName());
1690             mMitigationCounts.add(mitigationCount);
1691             mLastFailureReason = failureReason;
1692             return MITIGATION_RESULT_SUCCESS;
1693         }
1694 
getUniqueIdentifier()1695         public String getUniqueIdentifier() {
1696             return mName;
1697         }
1698 
isPersistent()1699         public boolean isPersistent() {
1700             return mIsPersistent;
1701         }
1702 
mayObservePackage(String packageName)1703         public boolean mayObservePackage(String packageName) {
1704             return mMayObservePackages;
1705         }
1706 
onBootLoop(int level)1707         public int onBootLoop(int level) {
1708             return mImpact;
1709         }
1710 
onExecuteBootLoopMitigation(int level)1711         public int onExecuteBootLoopMitigation(int level) {
1712             mMitigatedBootLoop = true;
1713             mBootMitigationCounts.add(level);
1714             return MITIGATION_RESULT_SUCCESS;
1715         }
1716 
mitigatedBootLoop()1717         public boolean mitigatedBootLoop() {
1718             return mMitigatedBootLoop;
1719         }
1720 
getLastFailureReason()1721         public int getLastFailureReason() {
1722             return mLastFailureReason;
1723         }
1724 
setPersistent(boolean persistent)1725         public void setPersistent(boolean persistent) {
1726             mIsPersistent = persistent;
1727         }
1728 
setImpact(int impact)1729         public void setImpact(int impact) {
1730             mImpact = impact;
1731         }
1732 
setMayObservePackages(boolean mayObservePackages)1733         public void setMayObservePackages(boolean mayObservePackages) {
1734             mMayObservePackages = mayObservePackages;
1735         }
1736     }
1737 
1738     private static class TestController extends ExplicitHealthCheckController {
TestController()1739         TestController() {
1740             super(null /* controller */);
1741         }
1742 
1743         private boolean mIsEnabled;
1744         private List<String> mSupportedPackages = new ArrayList<>();
1745         private List<String> mRequestedPackages = new ArrayList<>();
1746         private Consumer<String> mPassedConsumer;
1747         private Consumer<List<PackageConfig>> mSupportedConsumer;
1748         private Runnable mNotifySyncRunnable;
1749         private List<Set> mSyncRequests = new ArrayList<>();
1750 
1751         @Override
setEnabled(boolean enabled)1752         public void setEnabled(boolean enabled) {
1753             mIsEnabled = enabled;
1754             if (!mIsEnabled) {
1755                 mSupportedPackages.clear();
1756             }
1757         }
1758 
1759         @Override
setCallbacks(Consumer<String> passedConsumer, Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable)1760         public void setCallbacks(Consumer<String> passedConsumer,
1761                 Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable) {
1762             mPassedConsumer = passedConsumer;
1763             mSupportedConsumer = supportedConsumer;
1764             mNotifySyncRunnable = notifySyncRunnable;
1765         }
1766 
1767         @Override
syncRequests(Set<String> packages)1768         public void syncRequests(Set<String> packages) {
1769             mSyncRequests.add(packages);
1770             mRequestedPackages.clear();
1771             if (mIsEnabled) {
1772                 packages.retainAll(mSupportedPackages);
1773                 mRequestedPackages.addAll(packages);
1774                 List<PackageConfig> packageConfigs = new ArrayList<>();
1775                 for (String packageName: packages) {
1776                     packageConfigs.add(new PackageConfig(packageName, SHORT_DURATION));
1777                 }
1778                 mSupportedConsumer.accept(packageConfigs);
1779             } else {
1780                 mSupportedConsumer.accept(Collections.emptyList());
1781             }
1782         }
1783 
setSupportedPackages(List<String> packages)1784         public void setSupportedPackages(List<String> packages) {
1785             mSupportedPackages.clear();
1786             mSupportedPackages.addAll(packages);
1787         }
1788 
setPackagePassed(String packageName)1789         public void setPackagePassed(String packageName) {
1790             mPassedConsumer.accept(packageName);
1791         }
1792 
getRequestedPackages()1793         public List<String> getRequestedPackages() {
1794             if (mIsEnabled) {
1795                 return mRequestedPackages;
1796             } else {
1797                 return Collections.emptyList();
1798             }
1799         }
1800 
getSyncRequests()1801         public List<Set> getSyncRequests() {
1802             return mSyncRequests;
1803         }
1804     }
1805 
1806     private static class TestClock implements PackageWatchdog.SystemClock {
1807         // Note 0 is special to the internal clock of PackageWatchdog. We need to start from
1808         // a non-zero value in order not to disrupt the logic of PackageWatchdog.
1809         private long mUpTimeMillis = 1;
1810         @Override
uptimeMillis()1811         public long uptimeMillis() {
1812             return mUpTimeMillis;
1813         }
moveTimeForward(long milliSeconds)1814         public void moveTimeForward(long milliSeconds) {
1815             mUpTimeMillis += milliSeconds;
1816         }
1817     }
1818 }
1819