• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.packagewatchdog.cts;
18 
19 import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.content.Context;
24 import android.content.pm.VersionedPackage;
25 import android.test.UiThreadTest;
26 
27 import androidx.test.platform.app.InstrumentationRegistry;
28 
29 import com.android.server.PackageWatchdog;
30 
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Ignore;
34 import org.junit.Test;
35 
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 public class PackageWatchdogTest {
43 
44     private PackageWatchdog mPackageWatchdog;
45     private Context mContext;
46 
47     private static final String APP_A = "com.app.a";
48     private static final String APP_B = "com.app.b";
49     private static final String OBSERVER_NAME_1 = "observer-1";
50     private static final String OBSERVER_NAME_2 = "observer-2";
51     private static final int VERSION_CODE = 1;
52     private static final long SHORT_DURATION = TimeUnit.MINUTES.toMillis(5);
53     private static final int FAILURE_COUNT_THRESHOLD = 5;
54 
55     private CountDownLatch mLatch1, mLatch2;
56     private TestObserver mTestObserver1, mTestObserver2;
57 
58     @Before
59     @UiThreadTest
setUp()60     public void setUp() {
61         mContext = InstrumentationRegistry.getInstrumentation().getContext();
62         mPackageWatchdog = PackageWatchdog.getInstance(mContext);
63         mLatch1 = new CountDownLatch(1);
64         mLatch2 = new CountDownLatch(1);
65     }
66 
67     @After
tearDown()68     public void tearDown() {
69         if (mTestObserver1 != null) {
70             mPackageWatchdog.unregisterHealthObserver(mTestObserver1);
71         }
72         if (mTestObserver2 != null) {
73             mPackageWatchdog.unregisterHealthObserver(mTestObserver2);
74         }
75     }
76 
77     @Test
testAppCrashIsMitigated()78     public void testAppCrashIsMitigated() throws Exception {
79         CountDownLatch latch = new CountDownLatch(1);
80         mTestObserver1 = new TestObserver(OBSERVER_NAME_1, latch);
81         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
82         mPackageWatchdog.startExplicitHealthCheck(List.of(APP_A), SHORT_DURATION, mTestObserver1);
83         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A,
84                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
85         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
86         assertThat(mTestObserver1.mMitigatedPackages).isEqualTo(List.of(APP_A));
87     }
88 
89     /** Test that nothing happens if an app crashes that is not watched by any observer.*/
90     @Test
testAppCrashWithoutObserver()91     public void testAppCrashWithoutObserver() throws Exception {
92         mTestObserver1 = new TestObserver(OBSERVER_NAME_1, mLatch1);
93 
94         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
95         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
96                 mTestObserver1);
97         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_B,
98                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
99 
100         // Small break to allow failure to be noted.
101         Thread.sleep(1000);
102         assertThat(mTestObserver1.mMitigatedPackages).isEmpty();
103     }
104 
105     /**
106      * Test that multiple observers may register to watch certain packages and that they receive
107      * the correct callbacks.
108      */
109     @Test
testRegisteringMultipleObservers()110     public void testRegisteringMultipleObservers() throws Exception {
111         mTestObserver1 = new TestObserver(OBSERVER_NAME_1, mLatch1);
112         mTestObserver2 = new TestObserver(OBSERVER_NAME_2, mLatch2);
113 
114         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
115         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
116                 mTestObserver1);
117         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver2);
118         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION,
119                 mTestObserver2);
120         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
121                 new VersionedPackage(APP_B, VERSION_CODE)),
122                 PackageWatchdog.FAILURE_REASON_APP_CRASH);
123         assertThat(mLatch1.await(5, TimeUnit.SECONDS)).isTrue();
124         assertThat(mLatch2.await(5, TimeUnit.SECONDS)).isTrue();
125 
126         // The failed packages should be the same as the registered ones to ensure registration is
127         // done successfully
128         assertThat(mTestObserver1.mHealthCheckFailedPackages).containsExactly(APP_A);
129         assertThat(mTestObserver2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
130     }
131 
132 
133     /**
134      * Test that an unregistered observer is not notified for a failing package it previous
135      * observed.
136      */
137     @Test
138     @Ignore("b/354112511")
testUnregistration()139     public void testUnregistration() throws Exception {
140         mTestObserver1 = new TestObserver(OBSERVER_NAME_1);
141         mTestObserver2 = new TestObserver(OBSERVER_NAME_2, mLatch2);
142         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver2);
143         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
144                 mTestObserver2);
145         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
146         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
147                 mTestObserver1);
148 
149         mPackageWatchdog.unregisterHealthObserver(mTestObserver1);
150 
151         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A,
152                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
153 
154         assertThat(mLatch2.await(1, TimeUnit.MINUTES)).isTrue();
155 
156 
157         assertThat(mTestObserver1.mHealthCheckFailedPackages).isEmpty();
158         assertThat(mTestObserver2.mHealthCheckFailedPackages).containsExactly(APP_A);
159 
160     }
161 
162     /**
163      * Test package failure under threshold does not notify observers
164      */
165     @Test
testNoPackageFailureBeforeThreshold()166     public void testNoPackageFailureBeforeThreshold() throws Exception {
167         mTestObserver1 = new TestObserver(OBSERVER_NAME_1);
168         mTestObserver2 = new TestObserver(OBSERVER_NAME_2);
169 
170         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver2);
171         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
172                 mTestObserver2);
173         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
174         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
175                 mTestObserver1);
176 
177         for (int i = 0; i < FAILURE_COUNT_THRESHOLD - 1; i++) {
178             mPackageWatchdog.notifyPackageFailure(Arrays.asList(
179                     new VersionedPackage(APP_A, VERSION_CODE)),
180                     PackageWatchdog.FAILURE_REASON_UNKNOWN);
181         }
182 
183         // Small break to allow failure to be noted.
184         Thread.sleep(1000);
185 
186         // Verify that observers are not notified
187         assertThat(mTestObserver1.mHealthCheckFailedPackages).isEmpty();
188         assertThat(mTestObserver2.mHealthCheckFailedPackages).isEmpty();
189     }
190 
191     /** Test that observers execute correctly for failures reasons that skip thresholding. */
192     @Test
193     @Ignore("b/354112511")
testImmediateFailures()194     public void testImmediateFailures() throws Exception {
195         mLatch1 = new CountDownLatch(2);
196         mTestObserver1 = new TestObserver(OBSERVER_NAME_1, mLatch1);
197 
198         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
199         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION,
200                 mTestObserver1);
201 
202         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
203                 PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
204         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
205                 PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
206 
207         assertThat(mLatch1.await(5, TimeUnit.SECONDS)).isTrue();
208         assertThat(mTestObserver1.mMitigatedPackages).containsExactly(APP_A, APP_B);
209     }
210 
211     /**
212      * Test that a persistent observer will mitigate failures if it wishes to observe a package.
213      */
214     @Test
testPersistentObserverWatchesPackage()215     public void testPersistentObserverWatchesPackage() throws Exception {
216         mTestObserver1 = new TestObserver(OBSERVER_NAME_1, mLatch1);
217         mTestObserver1.setPersistent(true);
218         mTestObserver1.setMayObservePackages(true);
219 
220         mTestObserver2 = new TestObserver(OBSERVER_NAME_2, mLatch2);
221 
222         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
223         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION,
224                 mTestObserver1);
225 
226         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A,
227                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
228         assertThat(mLatch1.await(5, TimeUnit.SECONDS)).isTrue();
229 
230         // Persistent observer will observe the failing package.
231         assertThat(mTestObserver1.mHealthCheckFailedPackages).containsExactly(APP_A);
232 
233         // A non-persistent observer will not observe the failing package.
234         assertThat(mTestObserver2.mHealthCheckFailedPackages).isEmpty();
235     }
236 
237     /**
238      * Test that a persistent observer will not mitigate failures if it does not wish to observe
239      * a given package.
240      */
241     @Test
242     @Ignore("b/354112511")
testPersistentObserverDoesNotWatchPackage()243     public void testPersistentObserverDoesNotWatchPackage() {
244         mTestObserver1 = new TestObserver(OBSERVER_NAME_1);
245         mTestObserver1.setPersistent(true);
246         mTestObserver1.setMayObservePackages(false);
247 
248         mPackageWatchdog.registerHealthObserver(mContext.getMainExecutor(), mTestObserver1);
249         mPackageWatchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION,
250                 mTestObserver1);
251 
252         raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A,
253                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
254         assertThat(mTestObserver1.mHealthCheckFailedPackages).isEmpty();
255     }
256 
raiseFatalFailure(List<VersionedPackage> failingPackages, int failureReason)257     private void raiseFatalFailure(List<VersionedPackage> failingPackages, int failureReason) {
258         int failureCount = FAILURE_COUNT_THRESHOLD;
259         if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
260                 || failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK) {
261             failureCount = 1;
262         }
263         for (int i = 0; i < failureCount; i++) {
264             mPackageWatchdog.notifyPackageFailure(failingPackages, failureReason);
265         }
266         try {
267             // Wait for DEFAULT_MITIGATION_WINDOW_MS before applying another mitigation
268             Thread.sleep(TimeUnit.SECONDS.toMillis(6));
269         } catch (InterruptedException e) {
270             e.printStackTrace();
271         }
272     }
273 
274     private static class TestObserver implements PackageWatchdog.PackageHealthObserver {
275         private final String mName;
276         private final int mImpact;
277         private boolean mIsPersistent = true;
278         private boolean mMayObservePackages = false;
279         final List<String> mMitigatedPackages = new ArrayList<>();
280         final List<String> mHealthCheckFailedPackages = new ArrayList<>();
281         private final CountDownLatch mLatch;
282 
TestObserver(String name, CountDownLatch latch)283         TestObserver(String name, CountDownLatch latch) {
284             mName = name;
285             mLatch = latch;
286             mImpact = PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
287         }
288 
TestObserver(String name)289         TestObserver(String name) {
290             mName = name;
291             mLatch = new CountDownLatch(1);
292             mImpact = PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
293         }
294 
onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason, int mitigationCount)295         public int onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason,
296                 int mitigationCount) {
297             mHealthCheckFailedPackages.add(versionedPackage.getPackageName());
298             return mImpact;
299         }
300 
onExecuteHealthCheckMitigation(VersionedPackage versionedPackage, int failureReason, int mitigationCount)301         public int onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
302                 int failureReason, int mitigationCount) {
303             mMitigatedPackages.add(versionedPackage.getPackageName());
304             mLatch.countDown();
305             return MITIGATION_RESULT_SUCCESS;
306         }
307 
getUniqueIdentifier()308         public String getUniqueIdentifier() {
309             return mName;
310         }
311 
onBootLoop(int level)312         public int onBootLoop(int level) {
313             return mImpact;
314         }
315 
onExecuteBootLoopMitigation(int level)316         public int onExecuteBootLoopMitigation(int level) {
317             return MITIGATION_RESULT_SUCCESS;
318         }
319 
isPersistent()320         public boolean isPersistent() {
321             return mIsPersistent;
322         }
323 
mayObservePackage(String packageName)324         public boolean mayObservePackage(String packageName) {
325             return mMayObservePackages;
326         }
327 
setPersistent(boolean isPersistent)328         private void setPersistent(boolean isPersistent) {
329             mIsPersistent = isPersistent;
330         }
331 
setMayObservePackages(boolean mayObservePackages)332         private void setMayObservePackages(boolean mayObservePackages) {
333             mMayObservePackages = mayObservePackages;
334         }
335     }
336 }
337