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