1 /* 2 * Copyright (C) 2018 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 java.lang.annotation.RetentionPolicy.SOURCE; 22 23 import android.annotation.IntDef; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.content.pm.VersionedPackage; 28 import android.net.ConnectivityModuleConnector; 29 import android.os.Environment; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.SystemClock; 33 import android.provider.DeviceConfig; 34 import android.text.TextUtils; 35 import android.util.ArrayMap; 36 import android.util.ArraySet; 37 import android.util.AtomicFile; 38 import android.util.Slog; 39 import android.util.Xml; 40 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.os.BackgroundThread; 44 import com.android.internal.util.FastXmlSerializer; 45 import com.android.internal.util.XmlUtils; 46 47 import libcore.io.IoUtils; 48 49 import org.xmlpull.v1.XmlPullParser; 50 import org.xmlpull.v1.XmlPullParserException; 51 import org.xmlpull.v1.XmlSerializer; 52 53 import java.io.File; 54 import java.io.FileNotFoundException; 55 import java.io.FileOutputStream; 56 import java.io.IOException; 57 import java.io.InputStream; 58 import java.lang.annotation.Retention; 59 import java.lang.annotation.RetentionPolicy; 60 import java.nio.charset.StandardCharsets; 61 import java.util.ArrayList; 62 import java.util.Collections; 63 import java.util.Iterator; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.concurrent.TimeUnit; 68 69 /** 70 * Monitors the health of packages on the system and notifies interested observers when packages 71 * fail. On failure, the registered observer with the least user impacting mitigation will 72 * be notified. 73 */ 74 public class PackageWatchdog { 75 private static final String TAG = "PackageWatchdog"; 76 77 static final String PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS = 78 "watchdog_trigger_failure_duration_millis"; 79 static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT = 80 "watchdog_trigger_failure_count"; 81 static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = 82 "watchdog_explicit_health_check_enabled"; 83 84 public static final int FAILURE_REASON_UNKNOWN = 0; 85 public static final int FAILURE_REASON_NATIVE_CRASH = 1; 86 public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; 87 public static final int FAILURE_REASON_APP_CRASH = 3; 88 public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; 89 90 @IntDef(prefix = { "FAILURE_REASON_" }, value = { 91 FAILURE_REASON_UNKNOWN, 92 FAILURE_REASON_NATIVE_CRASH, 93 FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 94 FAILURE_REASON_APP_CRASH, 95 FAILURE_REASON_APP_NOT_RESPONDING 96 }) 97 @Retention(RetentionPolicy.SOURCE) 98 public @interface FailureReasons {} 99 100 // Duration to count package failures before it resets to 0 101 private static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = 102 (int) TimeUnit.MINUTES.toMillis(1); 103 // Number of package failures within the duration above before we notify observers 104 private static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5; 105 // Whether explicit health checks are enabled or not 106 private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true; 107 108 private static final int DB_VERSION = 1; 109 private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog"; 110 private static final String TAG_PACKAGE = "package"; 111 private static final String TAG_OBSERVER = "observer"; 112 private static final String ATTR_VERSION = "version"; 113 private static final String ATTR_NAME = "name"; 114 private static final String ATTR_DURATION = "duration"; 115 private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; 116 private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; 117 118 @GuardedBy("PackageWatchdog.class") 119 private static PackageWatchdog sPackageWatchdog; 120 121 private final Object mLock = new Object(); 122 // System server context 123 private final Context mContext; 124 // Handler to run short running tasks 125 private final Handler mShortTaskHandler; 126 // Handler for processing IO and long running tasks 127 private final Handler mLongTaskHandler; 128 // Contains (observer-name -> observer-handle) that have ever been registered from 129 // previous boots. Observers with all packages expired are periodically pruned. 130 // It is saved to disk on system shutdown and repouplated on startup so it survives reboots. 131 @GuardedBy("mLock") 132 private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>(); 133 // File containing the XML data of monitored packages /data/system/package-watchdog.xml 134 private final AtomicFile mPolicyFile; 135 private final ExplicitHealthCheckController mHealthCheckController; 136 private final ConnectivityModuleConnector mConnectivityModuleConnector; 137 @GuardedBy("mLock") 138 private boolean mIsPackagesReady; 139 // Flag to control whether explicit health checks are supported or not 140 @GuardedBy("mLock") 141 private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED; 142 @GuardedBy("mLock") 143 private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; 144 @GuardedBy("mLock") 145 private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; 146 // SystemClock#uptimeMillis when we last executed #syncState 147 // 0 if no prune is scheduled. 148 @GuardedBy("mLock") 149 private long mUptimeAtLastStateSync; 150 PackageWatchdog(Context context)151 private PackageWatchdog(Context context) { 152 // Needs to be constructed inline 153 this(context, new AtomicFile( 154 new File(new File(Environment.getDataDirectory(), "system"), 155 "package-watchdog.xml")), 156 new Handler(Looper.myLooper()), BackgroundThread.getHandler(), 157 new ExplicitHealthCheckController(context), 158 ConnectivityModuleConnector.getInstance()); 159 } 160 161 /** 162 * Creates a PackageWatchdog that allows injecting dependencies. 163 */ 164 @VisibleForTesting PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, Handler longTaskHandler, ExplicitHealthCheckController controller, ConnectivityModuleConnector connectivityModuleConnector)165 PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, 166 Handler longTaskHandler, ExplicitHealthCheckController controller, 167 ConnectivityModuleConnector connectivityModuleConnector) { 168 mContext = context; 169 mPolicyFile = policyFile; 170 mShortTaskHandler = shortTaskHandler; 171 mLongTaskHandler = longTaskHandler; 172 mHealthCheckController = controller; 173 mConnectivityModuleConnector = connectivityModuleConnector; 174 loadFromFile(); 175 } 176 177 /** Creates or gets singleton instance of PackageWatchdog. */ getInstance(Context context)178 public static PackageWatchdog getInstance(Context context) { 179 synchronized (PackageWatchdog.class) { 180 if (sPackageWatchdog == null) { 181 sPackageWatchdog = new PackageWatchdog(context); 182 } 183 return sPackageWatchdog; 184 } 185 } 186 187 /** 188 * Called during boot to notify when packages are ready on the device so we can start 189 * binding. 190 */ onPackagesReady()191 public void onPackagesReady() { 192 synchronized (mLock) { 193 mIsPackagesReady = true; 194 mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), 195 packages -> onSupportedPackages(packages), 196 () -> syncRequestsAsync()); 197 setPropertyChangedListenerLocked(); 198 updateConfigs(); 199 registerConnectivityModuleHealthListener(); 200 } 201 } 202 203 /** 204 * Registers {@code observer} to listen for package failures 205 * 206 * <p>Observers are expected to call this on boot. It does not specify any packages but 207 * it will resume observing any packages requested from a previous boot. 208 */ registerHealthObserver(PackageHealthObserver observer)209 public void registerHealthObserver(PackageHealthObserver observer) { 210 synchronized (mLock) { 211 ObserverInternal internalObserver = mAllObservers.get(observer.getName()); 212 if (internalObserver != null) { 213 internalObserver.mRegisteredObserver = observer; 214 } 215 } 216 } 217 218 /** 219 * Starts observing the health of the {@code packages} for {@code observer} and notifies 220 * {@code observer} of any package failures within the monitoring duration. 221 * 222 * <p>If monitoring a package supporting explicit health check, at the end of the monitoring 223 * duration if {@link #onHealthCheckPassed} was never called, 224 * {@link PackageHealthObserver#execute} will be called as if the package failed. 225 * 226 * <p>If {@code observer} is already monitoring a package in {@code packageNames}, 227 * the monitoring window of that package will be reset to {@code durationMs} and the health 228 * check state will be reset to a default depending on if the package is contained in 229 * {@link mPackagesWithExplicitHealthCheckEnabled}. 230 * 231 * @throws IllegalArgumentException if {@code packageNames} is empty 232 * or {@code durationMs} is less than 1 233 */ startObservingHealth(PackageHealthObserver observer, List<String> packageNames, long durationMs)234 public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames, 235 long durationMs) { 236 if (packageNames.isEmpty()) { 237 Slog.wtf(TAG, "No packages to observe, " + observer.getName()); 238 return; 239 } 240 if (durationMs < 1) { 241 // TODO: Instead of failing, monitor for default? 48hrs? 242 throw new IllegalArgumentException("Invalid duration " + durationMs + "ms for observer " 243 + observer.getName() + ". Not observing packages " + packageNames); 244 } 245 246 List<MonitoredPackage> packages = new ArrayList<>(); 247 for (int i = 0; i < packageNames.size(); i++) { 248 // Health checks not available yet so health check state will start INACTIVE 249 packages.add(new MonitoredPackage(packageNames.get(i), durationMs, false)); 250 } 251 252 // Sync before we add the new packages to the observers. This will #pruneObservers, 253 // causing any elapsed time to be deducted from all existing packages before we add new 254 // packages. This maintains the invariant that the elapsed time for ALL (new and existing) 255 // packages is the same. 256 syncState("observing new packages"); 257 258 synchronized (mLock) { 259 ObserverInternal oldObserver = mAllObservers.get(observer.getName()); 260 if (oldObserver == null) { 261 Slog.d(TAG, observer.getName() + " started monitoring health " 262 + "of packages " + packageNames); 263 mAllObservers.put(observer.getName(), 264 new ObserverInternal(observer.getName(), packages)); 265 } else { 266 Slog.d(TAG, observer.getName() + " added the following " 267 + "packages to monitor " + packageNames); 268 oldObserver.updatePackagesLocked(packages); 269 } 270 } 271 272 // Register observer in case not already registered 273 registerHealthObserver(observer); 274 275 // Sync after we add the new packages to the observers. We may have received packges 276 // requiring an earlier schedule than we are currently scheduled for. 277 syncState("updated observers"); 278 } 279 280 /** 281 * Unregisters {@code observer} from listening to package failure. 282 * Additionally, this stops observing any packages that may have previously been observed 283 * even from a previous boot. 284 */ unregisterHealthObserver(PackageHealthObserver observer)285 public void unregisterHealthObserver(PackageHealthObserver observer) { 286 synchronized (mLock) { 287 mAllObservers.remove(observer.getName()); 288 } 289 syncState("unregistering observer: " + observer.getName()); 290 } 291 292 /** 293 * Returns packages observed by {@code observer} 294 * 295 * @return an empty set if {@code observer} has some packages observerd from a previous boot 296 * but has not registered itself in the current boot to receive notifications. Returns null 297 * if there are no active packages monitored from any boot. 298 */ 299 @Nullable getPackages(PackageHealthObserver observer)300 public Set<String> getPackages(PackageHealthObserver observer) { 301 synchronized (mLock) { 302 for (int i = 0; i < mAllObservers.size(); i++) { 303 if (observer.getName().equals(mAllObservers.keyAt(i))) { 304 if (observer.equals(mAllObservers.valueAt(i).mRegisteredObserver)) { 305 return mAllObservers.valueAt(i).mPackages.keySet(); 306 } 307 return Collections.emptySet(); 308 } 309 } 310 } 311 return null; 312 } 313 314 /** 315 * Called when a process fails due to a crash, ANR or explicit health check. 316 * 317 * <p>For each package contained in the process, one registered observer with the least user 318 * impact will be notified for mitigation. 319 * 320 * <p>This method could be called frequently if there is a severe problem on the device. 321 */ onPackageFailure(List<VersionedPackage> packages, @FailureReasons int failureReason)322 public void onPackageFailure(List<VersionedPackage> packages, 323 @FailureReasons int failureReason) { 324 mLongTaskHandler.post(() -> { 325 synchronized (mLock) { 326 if (mAllObservers.isEmpty()) { 327 return; 328 } 329 330 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 331 VersionedPackage versionedPackage = packages.get(pIndex); 332 // Observer that will receive failure for versionedPackage 333 PackageHealthObserver currentObserverToNotify = null; 334 int currentObserverImpact = Integer.MAX_VALUE; 335 336 // Find observer with least user impact 337 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 338 ObserverInternal observer = mAllObservers.valueAt(oIndex); 339 PackageHealthObserver registeredObserver = observer.mRegisteredObserver; 340 if (registeredObserver != null 341 && observer.onPackageFailureLocked( 342 versionedPackage.getPackageName())) { 343 int impact = registeredObserver.onHealthCheckFailed(versionedPackage); 344 if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE 345 && impact < currentObserverImpact) { 346 currentObserverToNotify = registeredObserver; 347 currentObserverImpact = impact; 348 } 349 } 350 } 351 352 // Execute action with least user impact 353 if (currentObserverToNotify != null) { 354 currentObserverToNotify.execute(versionedPackage, failureReason); 355 } 356 } 357 } 358 }); 359 } 360 361 // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also 362 // avoid holding lock? 363 // This currently adds about 7ms extra to shutdown thread 364 /** Writes the package information to file during shutdown. */ writeNow()365 public void writeNow() { 366 synchronized (mLock) { 367 // Must only run synchronous tasks as this runs on the ShutdownThread and no other 368 // thread is guaranteed to run during shutdown. 369 if (!mAllObservers.isEmpty()) { 370 mLongTaskHandler.removeCallbacks(this::saveToFileAsync); 371 pruneObserversLocked(); 372 saveToFile(); 373 Slog.i(TAG, "Last write to update package durations"); 374 } 375 } 376 } 377 378 /** 379 * Enables or disables explicit health checks. 380 * <p> If explicit health checks are enabled, the health check service is started. 381 * <p> If explicit health checks are disabled, pending explicit health check requests are 382 * passed and the health check service is stopped. 383 */ setExplicitHealthCheckEnabled(boolean enabled)384 private void setExplicitHealthCheckEnabled(boolean enabled) { 385 synchronized (mLock) { 386 mIsHealthCheckEnabled = enabled; 387 mHealthCheckController.setEnabled(enabled); 388 // Prune to update internal state whenever health check is enabled/disabled 389 syncState("health check state " + (enabled ? "enabled" : "disabled")); 390 } 391 } 392 393 /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */ 394 @Retention(SOURCE) 395 @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE, 396 PackageHealthObserverImpact.USER_IMPACT_LOW, 397 PackageHealthObserverImpact.USER_IMPACT_MEDIUM, 398 PackageHealthObserverImpact.USER_IMPACT_HIGH}) 399 public @interface PackageHealthObserverImpact { 400 /** No action to take. */ 401 int USER_IMPACT_NONE = 0; 402 /* Action has low user impact, user of a device will barely notice. */ 403 int USER_IMPACT_LOW = 1; 404 /* Action has medium user impact, user of a device will likely notice. */ 405 int USER_IMPACT_MEDIUM = 3; 406 /* Action has high user impact, a last resort, user of a device will be very frustrated. */ 407 int USER_IMPACT_HIGH = 5; 408 } 409 410 /** Register instances of this interface to receive notifications on package failure. */ 411 public interface PackageHealthObserver { 412 /** 413 * Called when health check fails for the {@code versionedPackage}. 414 * 415 * @return any one of {@link PackageHealthObserverImpact} to express the impact 416 * to the user on {@link #execute} 417 */ onHealthCheckFailed(VersionedPackage versionedPackage)418 @PackageHealthObserverImpact int onHealthCheckFailed(VersionedPackage versionedPackage); 419 420 /** 421 * Executes mitigation for {@link #onHealthCheckFailed}. 422 * 423 * @return {@code true} if action was executed successfully, {@code false} otherwise 424 */ execute(VersionedPackage versionedPackage, @FailureReasons int failureReason)425 boolean execute(VersionedPackage versionedPackage, @FailureReasons int failureReason); 426 427 // TODO(b/120598832): Ensure uniqueness? 428 /** 429 * Identifier for the observer, should not change across device updates otherwise the 430 * watchdog may drop observing packages with the old name. 431 */ getName()432 String getName(); 433 } 434 getTriggerFailureCount()435 long getTriggerFailureCount() { 436 synchronized (mLock) { 437 return mTriggerFailureCount; 438 } 439 } 440 441 /** 442 * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. 443 */ syncRequestsAsync()444 private void syncRequestsAsync() { 445 mShortTaskHandler.removeCallbacks(this::syncRequests); 446 mShortTaskHandler.post(this::syncRequests); 447 } 448 449 /** 450 * Syncs health check requests with the {@link ExplicitHealthCheckController}. 451 * Calls to this must be serialized. 452 * 453 * @see #syncRequestsAsync 454 */ syncRequests()455 private void syncRequests() { 456 Set<String> packages = null; 457 synchronized (mLock) { 458 if (mIsPackagesReady) { 459 packages = getPackagesPendingHealthChecksLocked(); 460 } // else, we will sync requests when packages become ready 461 } 462 463 // Call outside lock to avoid holding lock when calling into the controller. 464 if (packages != null) { 465 Slog.i(TAG, "Syncing health check requests for packages: " + packages); 466 mHealthCheckController.syncRequests(packages); 467 } 468 } 469 470 /** 471 * Updates the observers monitoring {@code packageName} that explicit health check has passed. 472 * 473 * <p> This update is strictly for registered observers at the time of the call 474 * Observers that register after this signal will have no knowledge of prior signals and will 475 * effectively behave as if the explicit health check hasn't passed for {@code packageName}. 476 * 477 * <p> {@code packageName} can still be considered failed if reported by 478 * {@link #onPackageFailureLocked} before the package expires. 479 * 480 * <p> Triggered by components outside the system server when they are fully functional after an 481 * update. 482 */ onHealthCheckPassed(String packageName)483 private void onHealthCheckPassed(String packageName) { 484 Slog.i(TAG, "Health check passed for package: " + packageName); 485 boolean isStateChanged = false; 486 487 synchronized (mLock) { 488 for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { 489 ObserverInternal observer = mAllObservers.valueAt(observerIdx); 490 MonitoredPackage monitoredPackage = observer.mPackages.get(packageName); 491 492 if (monitoredPackage != null) { 493 int oldState = monitoredPackage.getHealthCheckStateLocked(); 494 int newState = monitoredPackage.tryPassHealthCheckLocked(); 495 isStateChanged |= oldState != newState; 496 } 497 } 498 } 499 500 if (isStateChanged) { 501 syncState("health check passed for " + packageName); 502 } 503 } 504 onSupportedPackages(List<PackageConfig> supportedPackages)505 private void onSupportedPackages(List<PackageConfig> supportedPackages) { 506 boolean isStateChanged = false; 507 508 Map<String, Long> supportedPackageTimeouts = new ArrayMap<>(); 509 Iterator<PackageConfig> it = supportedPackages.iterator(); 510 while (it.hasNext()) { 511 PackageConfig info = it.next(); 512 supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis()); 513 } 514 515 synchronized (mLock) { 516 Slog.d(TAG, "Received supported packages " + supportedPackages); 517 Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); 518 while (oit.hasNext()) { 519 Iterator<MonitoredPackage> pit = oit.next().mPackages.values().iterator(); 520 while (pit.hasNext()) { 521 MonitoredPackage monitoredPackage = pit.next(); 522 String packageName = monitoredPackage.getName(); 523 int oldState = monitoredPackage.getHealthCheckStateLocked(); 524 int newState; 525 526 if (supportedPackageTimeouts.containsKey(packageName)) { 527 // Supported packages become ACTIVE if currently INACTIVE 528 newState = monitoredPackage.setHealthCheckActiveLocked( 529 supportedPackageTimeouts.get(packageName)); 530 } else { 531 // Unsupported packages are marked as PASSED unless already FAILED 532 newState = monitoredPackage.tryPassHealthCheckLocked(); 533 } 534 isStateChanged |= oldState != newState; 535 } 536 } 537 } 538 539 if (isStateChanged) { 540 syncState("updated health check supported packages " + supportedPackages); 541 } 542 } 543 544 @GuardedBy("mLock") getPackagesPendingHealthChecksLocked()545 private Set<String> getPackagesPendingHealthChecksLocked() { 546 Slog.d(TAG, "Getting all observed packages pending health checks"); 547 Set<String> packages = new ArraySet<>(); 548 Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); 549 while (oit.hasNext()) { 550 ObserverInternal observer = oit.next(); 551 Iterator<MonitoredPackage> pit = 552 observer.mPackages.values().iterator(); 553 while (pit.hasNext()) { 554 MonitoredPackage monitoredPackage = pit.next(); 555 String packageName = monitoredPackage.getName(); 556 if (monitoredPackage.isPendingHealthChecksLocked()) { 557 packages.add(packageName); 558 } 559 } 560 } 561 return packages; 562 } 563 564 /** 565 * Syncs the state of the observers. 566 * 567 * <p> Prunes all observers, saves new state to disk, syncs health check requests with the 568 * health check service and schedules the next state sync. 569 */ syncState(String reason)570 private void syncState(String reason) { 571 synchronized (mLock) { 572 Slog.i(TAG, "Syncing state, reason: " + reason); 573 pruneObserversLocked(); 574 575 saveToFileAsync(); 576 syncRequestsAsync(); 577 578 // Done syncing state, schedule the next state sync 579 scheduleNextSyncStateLocked(); 580 } 581 } 582 syncStateWithScheduledReason()583 private void syncStateWithScheduledReason() { 584 syncState("scheduled"); 585 } 586 587 @GuardedBy("mLock") scheduleNextSyncStateLocked()588 private void scheduleNextSyncStateLocked() { 589 long durationMs = getNextStateSyncMillisLocked(); 590 mShortTaskHandler.removeCallbacks(this::syncStateWithScheduledReason); 591 if (durationMs == Long.MAX_VALUE) { 592 Slog.i(TAG, "Cancelling state sync, nothing to sync"); 593 mUptimeAtLastStateSync = 0; 594 } else { 595 Slog.i(TAG, "Scheduling next state sync in " + durationMs + "ms"); 596 mUptimeAtLastStateSync = SystemClock.uptimeMillis(); 597 mShortTaskHandler.postDelayed(this::syncStateWithScheduledReason, durationMs); 598 } 599 } 600 601 /** 602 * Returns the next duration in millis to sync the watchdog state. 603 * 604 * @returns Long#MAX_VALUE if there are no observed packages. 605 */ 606 @GuardedBy("mLock") getNextStateSyncMillisLocked()607 private long getNextStateSyncMillisLocked() { 608 long shortestDurationMs = Long.MAX_VALUE; 609 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 610 ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages; 611 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 612 MonitoredPackage mp = packages.valueAt(pIndex); 613 long duration = mp.getShortestScheduleDurationMsLocked(); 614 if (duration < shortestDurationMs) { 615 shortestDurationMs = duration; 616 } 617 } 618 } 619 return shortestDurationMs; 620 } 621 622 /** 623 * Removes {@code elapsedMs} milliseconds from all durations on monitored packages 624 * and updates other internal state. 625 */ 626 @GuardedBy("mLock") pruneObserversLocked()627 private void pruneObserversLocked() { 628 long elapsedMs = mUptimeAtLastStateSync == 0 629 ? 0 : SystemClock.uptimeMillis() - mUptimeAtLastStateSync; 630 if (elapsedMs <= 0) { 631 Slog.i(TAG, "Not pruning observers, elapsed time: " + elapsedMs + "ms"); 632 return; 633 } 634 635 Slog.i(TAG, "Removing " + elapsedMs + "ms from all packages on all observers"); 636 Iterator<ObserverInternal> it = mAllObservers.values().iterator(); 637 while (it.hasNext()) { 638 ObserverInternal observer = it.next(); 639 Set<MonitoredPackage> failedPackages = 640 observer.prunePackagesLocked(elapsedMs); 641 if (!failedPackages.isEmpty()) { 642 onHealthCheckFailed(observer, failedPackages); 643 } 644 if (observer.mPackages.isEmpty()) { 645 Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired"); 646 it.remove(); 647 } 648 } 649 } 650 onHealthCheckFailed(ObserverInternal observer, Set<MonitoredPackage> failedPackages)651 private void onHealthCheckFailed(ObserverInternal observer, 652 Set<MonitoredPackage> failedPackages) { 653 mLongTaskHandler.post(() -> { 654 synchronized (mLock) { 655 PackageHealthObserver registeredObserver = observer.mRegisteredObserver; 656 if (registeredObserver != null) { 657 Iterator<MonitoredPackage> it = failedPackages.iterator(); 658 while (it.hasNext()) { 659 String failedPackage = it.next().getName(); 660 Slog.i(TAG, "Explicit health check failed for package " + failedPackage); 661 VersionedPackage versionedPkg = getVersionedPackage(failedPackage); 662 if (versionedPkg == null) { 663 Slog.w(TAG, "Explicit health check failed but could not find package " 664 + failedPackage); 665 // TODO(b/120598832): Skip. We only continue to pass tests for now since 666 // the tests don't install any packages 667 versionedPkg = new VersionedPackage(failedPackage, 0L); 668 } 669 registeredObserver.execute(versionedPkg, 670 PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); 671 } 672 } 673 } 674 }); 675 } 676 677 @Nullable getVersionedPackage(String packageName)678 private VersionedPackage getVersionedPackage(String packageName) { 679 final PackageManager pm = mContext.getPackageManager(); 680 if (pm == null) { 681 return null; 682 } 683 try { 684 final long versionCode = pm.getPackageInfo( 685 packageName, 0 /* flags */).getLongVersionCode(); 686 return new VersionedPackage(packageName, versionCode); 687 } catch (PackageManager.NameNotFoundException e) { 688 return null; 689 } 690 } 691 692 /** 693 * Loads mAllObservers from file. 694 * 695 * <p>Note that this is <b>not</b> thread safe and should only called be called 696 * from the constructor. 697 */ loadFromFile()698 private void loadFromFile() { 699 InputStream infile = null; 700 mAllObservers.clear(); 701 try { 702 infile = mPolicyFile.openRead(); 703 final XmlPullParser parser = Xml.newPullParser(); 704 parser.setInput(infile, StandardCharsets.UTF_8.name()); 705 XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG); 706 int outerDepth = parser.getDepth(); 707 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 708 ObserverInternal observer = ObserverInternal.read(parser, this); 709 if (observer != null) { 710 mAllObservers.put(observer.mName, observer); 711 } 712 } 713 } catch (FileNotFoundException e) { 714 // Nothing to monitor 715 } catch (IOException | NumberFormatException | XmlPullParserException e) { 716 Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e); 717 mPolicyFile.delete(); 718 } finally { 719 IoUtils.closeQuietly(infile); 720 } 721 } 722 723 /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */ setPropertyChangedListenerLocked()724 private void setPropertyChangedListenerLocked() { 725 DeviceConfig.addOnPropertiesChangedListener( 726 DeviceConfig.NAMESPACE_ROLLBACK, 727 mContext.getMainExecutor(), 728 (properties) -> { 729 if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(properties.getNamespace())) { 730 return; 731 } 732 updateConfigs(); 733 }); 734 } 735 736 /** 737 * Health check is enabled or disabled after reading the flags 738 * from DeviceConfig. 739 */ updateConfigs()740 private void updateConfigs() { 741 synchronized (mLock) { 742 mTriggerFailureCount = DeviceConfig.getInt( 743 DeviceConfig.NAMESPACE_ROLLBACK, 744 PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 745 DEFAULT_TRIGGER_FAILURE_COUNT); 746 if (mTriggerFailureCount <= 0) { 747 mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; 748 } 749 750 mTriggerFailureDurationMs = DeviceConfig.getInt( 751 DeviceConfig.NAMESPACE_ROLLBACK, 752 PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 753 DEFAULT_TRIGGER_FAILURE_DURATION_MS); 754 if (mTriggerFailureDurationMs <= 0) { 755 mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_COUNT; 756 } 757 758 setExplicitHealthCheckEnabled(DeviceConfig.getBoolean( 759 DeviceConfig.NAMESPACE_ROLLBACK, 760 PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 761 DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED)); 762 } 763 } 764 registerConnectivityModuleHealthListener()765 private void registerConnectivityModuleHealthListener() { 766 // TODO: have an internal method to trigger a rollback by reporting high severity errors, 767 // and rely on ActivityManager to inform the watchdog of severe network stack crashes 768 // instead of having this listener in parallel. 769 mConnectivityModuleConnector.registerHealthListener( 770 packageName -> { 771 final VersionedPackage pkg = getVersionedPackage(packageName); 772 if (pkg == null) { 773 Slog.wtf(TAG, "NetworkStack failed but could not find its package"); 774 return; 775 } 776 // This is a severe failure and recovery should be attempted immediately. 777 // TODO: have a better way to handle such failures. 778 final List<VersionedPackage> pkgList = Collections.singletonList(pkg); 779 final long failureCount = getTriggerFailureCount(); 780 for (int i = 0; i < failureCount; i++) { 781 onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); 782 } 783 }); 784 } 785 786 /** 787 * Persists mAllObservers to file. Threshold information is ignored. 788 */ saveToFile()789 private boolean saveToFile() { 790 Slog.i(TAG, "Saving observer state to file"); 791 synchronized (mLock) { 792 FileOutputStream stream; 793 try { 794 stream = mPolicyFile.startWrite(); 795 } catch (IOException e) { 796 Slog.w(TAG, "Cannot update monitored packages", e); 797 return false; 798 } 799 800 try { 801 XmlSerializer out = new FastXmlSerializer(); 802 out.setOutput(stream, StandardCharsets.UTF_8.name()); 803 out.startDocument(null, true); 804 out.startTag(null, TAG_PACKAGE_WATCHDOG); 805 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 806 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 807 mAllObservers.valueAt(oIndex).writeLocked(out); 808 } 809 out.endTag(null, TAG_PACKAGE_WATCHDOG); 810 out.endDocument(); 811 mPolicyFile.finishWrite(stream); 812 return true; 813 } catch (IOException e) { 814 Slog.w(TAG, "Failed to save monitored packages, restoring backup", e); 815 mPolicyFile.failWrite(stream); 816 return false; 817 } finally { 818 IoUtils.closeQuietly(stream); 819 } 820 } 821 } 822 saveToFileAsync()823 private void saveToFileAsync() { 824 if (!mLongTaskHandler.hasCallbacks(this::saveToFile)) { 825 mLongTaskHandler.post(this::saveToFile); 826 } 827 } 828 829 /** 830 * Represents an observer monitoring a set of packages along with the failure thresholds for 831 * each package. 832 * 833 * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing 834 * instances of this class. 835 */ 836 //TODO(b/120598832): Remove 'm' from non-private fields 837 private static class ObserverInternal { 838 public final String mName; 839 //TODO(b/120598832): Add getter for mPackages 840 @GuardedBy("mLock") 841 public final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>(); 842 @Nullable 843 @GuardedBy("mLock") 844 public PackageHealthObserver mRegisteredObserver; 845 ObserverInternal(String name, List<MonitoredPackage> packages)846 ObserverInternal(String name, List<MonitoredPackage> packages) { 847 mName = name; 848 updatePackagesLocked(packages); 849 } 850 851 /** 852 * Writes important {@link MonitoredPackage} details for this observer to file. 853 * Does not persist any package failure thresholds. 854 */ 855 @GuardedBy("mLock") writeLocked(XmlSerializer out)856 public boolean writeLocked(XmlSerializer out) { 857 try { 858 out.startTag(null, TAG_OBSERVER); 859 out.attribute(null, ATTR_NAME, mName); 860 for (int i = 0; i < mPackages.size(); i++) { 861 MonitoredPackage p = mPackages.valueAt(i); 862 p.writeLocked(out); 863 } 864 out.endTag(null, TAG_OBSERVER); 865 return true; 866 } catch (IOException e) { 867 Slog.w(TAG, "Cannot save observer", e); 868 return false; 869 } 870 } 871 872 @GuardedBy("mLock") updatePackagesLocked(List<MonitoredPackage> packages)873 public void updatePackagesLocked(List<MonitoredPackage> packages) { 874 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 875 MonitoredPackage p = packages.get(pIndex); 876 mPackages.put(p.mName, p); 877 } 878 } 879 880 /** 881 * Reduces the monitoring durations of all packages observed by this observer by 882 * {@code elapsedMs}. If any duration is less than 0, the package is removed from 883 * observation. If any health check duration is less than 0, the health check result 884 * is evaluated. 885 * 886 * @return a {@link Set} of packages that were removed from the observer without explicit 887 * health check passing, or an empty list if no package expired for which an explicit health 888 * check was still pending 889 */ 890 @GuardedBy("mLock") prunePackagesLocked(long elapsedMs)891 private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) { 892 Set<MonitoredPackage> failedPackages = new ArraySet<>(); 893 Iterator<MonitoredPackage> it = mPackages.values().iterator(); 894 while (it.hasNext()) { 895 MonitoredPackage p = it.next(); 896 int oldState = p.getHealthCheckStateLocked(); 897 int newState = p.handleElapsedTimeLocked(elapsedMs); 898 if (oldState != MonitoredPackage.STATE_FAILED 899 && newState == MonitoredPackage.STATE_FAILED) { 900 Slog.i(TAG, "Package " + p.mName + " failed health check"); 901 failedPackages.add(p); 902 } 903 if (p.isExpiredLocked()) { 904 it.remove(); 905 } 906 } 907 return failedPackages; 908 } 909 910 /** 911 * Increments failure counts of {@code packageName}. 912 * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise 913 */ 914 @GuardedBy("mLock") onPackageFailureLocked(String packageName)915 public boolean onPackageFailureLocked(String packageName) { 916 MonitoredPackage p = mPackages.get(packageName); 917 if (p != null) { 918 return p.onFailureLocked(); 919 } 920 return false; 921 } 922 923 /** 924 * Returns one ObserverInternal from the {@code parser} and advances its state. 925 * 926 * <p>Note that this method is <b>not</b> thread safe. It should only be called from 927 * #loadFromFile which in turn is only called on construction of the 928 * singleton PackageWatchdog. 929 **/ read(XmlPullParser parser, PackageWatchdog watchdog)930 public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) { 931 String observerName = null; 932 if (TAG_OBSERVER.equals(parser.getName())) { 933 observerName = parser.getAttributeValue(null, ATTR_NAME); 934 if (TextUtils.isEmpty(observerName)) { 935 Slog.wtf(TAG, "Unable to read observer name"); 936 return null; 937 } 938 } 939 List<MonitoredPackage> packages = new ArrayList<>(); 940 int innerDepth = parser.getDepth(); 941 try { 942 while (XmlUtils.nextElementWithin(parser, innerDepth)) { 943 if (TAG_PACKAGE.equals(parser.getName())) { 944 try { 945 String packageName = parser.getAttributeValue(null, ATTR_NAME); 946 long duration = Long.parseLong( 947 parser.getAttributeValue(null, ATTR_DURATION)); 948 long healthCheckDuration = Long.parseLong( 949 parser.getAttributeValue(null, 950 ATTR_EXPLICIT_HEALTH_CHECK_DURATION)); 951 boolean hasPassedHealthCheck = Boolean.parseBoolean( 952 parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); 953 if (!TextUtils.isEmpty(packageName)) { 954 packages.add(watchdog.new MonitoredPackage(packageName, duration, 955 healthCheckDuration, hasPassedHealthCheck)); 956 } 957 } catch (NumberFormatException e) { 958 Slog.wtf(TAG, "Skipping package for observer " + observerName, e); 959 continue; 960 } 961 } 962 } 963 } catch (XmlPullParserException | IOException e) { 964 Slog.wtf(TAG, "Unable to read observer " + observerName, e); 965 return null; 966 } 967 if (packages.isEmpty()) { 968 return null; 969 } 970 return new ObserverInternal(observerName, packages); 971 } 972 } 973 974 /** 975 * Represents a package and its health check state along with the time 976 * it should be monitored for. 977 * 978 * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing 979 * instances of this class. 980 */ 981 class MonitoredPackage { 982 // Health check states 983 // TODO(b/120598832): Prefix with HEALTH_CHECK 984 // mName has not passed health check but has requested a health check 985 public static final int STATE_ACTIVE = 0; 986 // mName has not passed health check and has not requested a health check 987 public static final int STATE_INACTIVE = 1; 988 // mName has passed health check 989 public static final int STATE_PASSED = 2; 990 // mName has failed health check 991 public static final int STATE_FAILED = 3; 992 993 //TODO(b/120598832): VersionedPackage? 994 private final String mName; 995 // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after 996 // methods that could change the health check state: handleElapsedTimeLocked and 997 // tryPassHealthCheckLocked 998 private int mHealthCheckState = STATE_INACTIVE; 999 // Whether an explicit health check has passed. 1000 // This value in addition with mHealthCheckDurationMs determines the health check state 1001 // of the package, see #getHealthCheckStateLocked 1002 @GuardedBy("mLock") 1003 private boolean mHasPassedHealthCheck; 1004 // System uptime duration to monitor package. 1005 @GuardedBy("mLock") 1006 private long mDurationMs; 1007 // System uptime duration to check the result of an explicit health check 1008 // Initially, MAX_VALUE until we get a value from the health check service 1009 // and request health checks. 1010 // This value in addition with mHasPassedHealthCheck determines the health check state 1011 // of the package, see #getHealthCheckStateLocked 1012 @GuardedBy("mLock") 1013 private long mHealthCheckDurationMs = Long.MAX_VALUE; 1014 // System uptime of first package failure 1015 @GuardedBy("mLock") 1016 private long mUptimeStartMs; 1017 // Number of failures since mUptimeStartMs 1018 @GuardedBy("mLock") 1019 private int mFailures; 1020 MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck)1021 MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) { 1022 this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck); 1023 } 1024 MonitoredPackage(String name, long durationMs, long healthCheckDurationMs, boolean hasPassedHealthCheck)1025 MonitoredPackage(String name, long durationMs, long healthCheckDurationMs, 1026 boolean hasPassedHealthCheck) { 1027 mName = name; 1028 mDurationMs = durationMs; 1029 mHealthCheckDurationMs = healthCheckDurationMs; 1030 mHasPassedHealthCheck = hasPassedHealthCheck; 1031 updateHealthCheckStateLocked(); 1032 } 1033 1034 /** Writes the salient fields to disk using {@code out}. */ 1035 @GuardedBy("mLock") writeLocked(XmlSerializer out)1036 public void writeLocked(XmlSerializer out) throws IOException { 1037 out.startTag(null, TAG_PACKAGE); 1038 out.attribute(null, ATTR_NAME, mName); 1039 out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs)); 1040 out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, 1041 String.valueOf(mHealthCheckDurationMs)); 1042 out.attribute(null, ATTR_PASSED_HEALTH_CHECK, 1043 String.valueOf(mHasPassedHealthCheck)); 1044 out.endTag(null, TAG_PACKAGE); 1045 } 1046 1047 /** 1048 * Increment package failures or resets failure count depending on the last package failure. 1049 * 1050 * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise 1051 */ 1052 @GuardedBy("mLock") onFailureLocked()1053 public boolean onFailureLocked() { 1054 final long now = SystemClock.uptimeMillis(); 1055 final long duration = now - mUptimeStartMs; 1056 if (duration > mTriggerFailureDurationMs) { 1057 // TODO(b/120598832): Reseting to 1 is not correct 1058 // because there may be more than 1 failure in the last trigger window from now 1059 // This is the RescueParty impl, will leave for now 1060 mFailures = 1; 1061 mUptimeStartMs = now; 1062 } else { 1063 mFailures++; 1064 } 1065 boolean failed = mFailures >= mTriggerFailureCount; 1066 if (failed) { 1067 mFailures = 0; 1068 } 1069 return failed; 1070 } 1071 1072 /** 1073 * Sets the initial health check duration. 1074 * 1075 * @return the new health check state 1076 */ 1077 @GuardedBy("mLock") setHealthCheckActiveLocked(long initialHealthCheckDurationMs)1078 public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) { 1079 if (initialHealthCheckDurationMs <= 0) { 1080 Slog.wtf(TAG, "Cannot set non-positive health check duration " 1081 + initialHealthCheckDurationMs + "ms for package " + mName 1082 + ". Using total duration " + mDurationMs + "ms instead"); 1083 initialHealthCheckDurationMs = mDurationMs; 1084 } 1085 if (mHealthCheckState == STATE_INACTIVE) { 1086 // Transitions to ACTIVE 1087 mHealthCheckDurationMs = initialHealthCheckDurationMs; 1088 } 1089 return updateHealthCheckStateLocked(); 1090 } 1091 1092 /** 1093 * Updates the monitoring durations of the package. 1094 * 1095 * @return the new health check state 1096 */ 1097 @GuardedBy("mLock") handleElapsedTimeLocked(long elapsedMs)1098 public int handleElapsedTimeLocked(long elapsedMs) { 1099 if (elapsedMs <= 0) { 1100 Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + mName); 1101 return mHealthCheckState; 1102 } 1103 // Transitions to FAILED if now <= 0 and health check not passed 1104 mDurationMs -= elapsedMs; 1105 if (mHealthCheckState == STATE_ACTIVE) { 1106 // We only update health check durations if we have #setHealthCheckActiveLocked 1107 // This ensures we don't leave the INACTIVE state for an unexpected elapsed time 1108 // Transitions to FAILED if now <= 0 and health check not passed 1109 mHealthCheckDurationMs -= elapsedMs; 1110 } 1111 return updateHealthCheckStateLocked(); 1112 } 1113 1114 /** 1115 * Marks the health check as passed and transitions to {@link #STATE_PASSED} 1116 * if not yet {@link #STATE_FAILED}. 1117 * 1118 * @return the new health check state 1119 */ 1120 @GuardedBy("mLock") tryPassHealthCheckLocked()1121 public int tryPassHealthCheckLocked() { 1122 if (mHealthCheckState != STATE_FAILED) { 1123 // FAILED is a final state so only pass if we haven't failed 1124 // Transition to PASSED 1125 mHasPassedHealthCheck = true; 1126 } 1127 return updateHealthCheckStateLocked(); 1128 } 1129 1130 /** Returns the monitored package name. */ getName()1131 private String getName() { 1132 return mName; 1133 } 1134 1135 //TODO(b/120598832): IntDef 1136 /** 1137 * Returns the current health check state, any of {@link #STATE_ACTIVE}, 1138 * {@link #STATE_INACTIVE} or {@link #STATE_PASSED} 1139 */ 1140 @GuardedBy("mLock") getHealthCheckStateLocked()1141 public int getHealthCheckStateLocked() { 1142 return mHealthCheckState; 1143 } 1144 1145 /** 1146 * Returns the shortest duration before the package should be scheduled for a prune. 1147 * 1148 * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled 1149 */ 1150 @GuardedBy("mLock") getShortestScheduleDurationMsLocked()1151 public long getShortestScheduleDurationMsLocked() { 1152 // Consider health check duration only if #isPendingHealthChecksLocked is true 1153 return Math.min(toPositive(mDurationMs), 1154 isPendingHealthChecksLocked() 1155 ? toPositive(mHealthCheckDurationMs) : Long.MAX_VALUE); 1156 } 1157 1158 /** 1159 * Returns {@code true} if the total duration left to monitor the package is less than or 1160 * equal to 0 {@code false} otherwise. 1161 */ 1162 @GuardedBy("mLock") isExpiredLocked()1163 public boolean isExpiredLocked() { 1164 return mDurationMs <= 0; 1165 } 1166 1167 /** 1168 * Returns {@code true} if the package, {@link #getName} is expecting health check results 1169 * {@code false} otherwise. 1170 */ 1171 @GuardedBy("mLock") isPendingHealthChecksLocked()1172 public boolean isPendingHealthChecksLocked() { 1173 return mHealthCheckState == STATE_ACTIVE || mHealthCheckState == STATE_INACTIVE; 1174 } 1175 1176 /** 1177 * Updates the health check state based on {@link #mHasPassedHealthCheck} 1178 * and {@link #mHealthCheckDurationMs}. 1179 * 1180 * @return the new health check state 1181 */ 1182 @GuardedBy("mLock") updateHealthCheckStateLocked()1183 private int updateHealthCheckStateLocked() { 1184 int oldState = mHealthCheckState; 1185 if (mHasPassedHealthCheck) { 1186 // Set final state first to avoid ambiguity 1187 mHealthCheckState = STATE_PASSED; 1188 } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) { 1189 // Set final state first to avoid ambiguity 1190 mHealthCheckState = STATE_FAILED; 1191 } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { 1192 mHealthCheckState = STATE_INACTIVE; 1193 } else { 1194 mHealthCheckState = STATE_ACTIVE; 1195 } 1196 Slog.i(TAG, "Updated health check state for package " + mName + ": " 1197 + toString(oldState) + " -> " + toString(mHealthCheckState)); 1198 return mHealthCheckState; 1199 } 1200 1201 /** Returns a {@link String} representation of the current health check state. */ toString(int state)1202 private String toString(int state) { 1203 switch (state) { 1204 case STATE_ACTIVE: 1205 return "ACTIVE"; 1206 case STATE_INACTIVE: 1207 return "INACTIVE"; 1208 case STATE_PASSED: 1209 return "PASSED"; 1210 case STATE_FAILED: 1211 return "FAILED"; 1212 default: 1213 return "UNKNOWN"; 1214 } 1215 } 1216 1217 /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */ toPositive(long value)1218 private long toPositive(long value) { 1219 return value > 0 ? value : Long.MAX_VALUE; 1220 } 1221 } 1222 } 1223