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