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