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.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.VersionedPackage; 29 import android.net.ConnectivityModuleConnector; 30 import android.os.Environment; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Process; 34 import android.os.SystemProperties; 35 import android.provider.DeviceConfig; 36 import android.text.TextUtils; 37 import android.util.ArrayMap; 38 import android.util.ArraySet; 39 import android.util.AtomicFile; 40 import android.util.LongArrayQueue; 41 import android.util.MathUtils; 42 import android.util.Slog; 43 import android.util.Xml; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.internal.os.BackgroundThread; 48 import com.android.internal.util.IndentingPrintWriter; 49 import com.android.internal.util.XmlUtils; 50 import com.android.modules.utils.TypedXmlPullParser; 51 import com.android.modules.utils.TypedXmlSerializer; 52 53 import libcore.io.IoUtils; 54 55 import org.xmlpull.v1.XmlPullParserException; 56 57 import java.io.BufferedReader; 58 import java.io.BufferedWriter; 59 import java.io.File; 60 import java.io.FileNotFoundException; 61 import java.io.FileOutputStream; 62 import java.io.FileReader; 63 import java.io.FileWriter; 64 import java.io.IOException; 65 import java.io.InputStream; 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.util.ArrayList; 69 import java.util.Collections; 70 import java.util.Iterator; 71 import java.util.List; 72 import java.util.Map; 73 import java.util.NoSuchElementException; 74 import java.util.Set; 75 import java.util.concurrent.TimeUnit; 76 77 /** 78 * Monitors the health of packages on the system and notifies interested observers when packages 79 * fail. On failure, the registered observer with the least user impacting mitigation will 80 * be notified. 81 */ 82 public class PackageWatchdog { 83 private static final String TAG = "PackageWatchdog"; 84 85 static final String PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS = 86 "watchdog_trigger_failure_duration_millis"; 87 static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT = 88 "watchdog_trigger_failure_count"; 89 static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = 90 "watchdog_explicit_health_check_enabled"; 91 92 // TODO: make the following values configurable via DeviceConfig 93 private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS = 94 TimeUnit.SECONDS.toMillis(30); 95 private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10; 96 97 98 public static final int FAILURE_REASON_UNKNOWN = 0; 99 public static final int FAILURE_REASON_NATIVE_CRASH = 1; 100 public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; 101 public static final int FAILURE_REASON_APP_CRASH = 3; 102 public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; 103 104 @IntDef(prefix = { "FAILURE_REASON_" }, value = { 105 FAILURE_REASON_UNKNOWN, 106 FAILURE_REASON_NATIVE_CRASH, 107 FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 108 FAILURE_REASON_APP_CRASH, 109 FAILURE_REASON_APP_NOT_RESPONDING 110 }) 111 @Retention(RetentionPolicy.SOURCE) 112 public @interface FailureReasons {} 113 114 // Duration to count package failures before it resets to 0 115 @VisibleForTesting 116 static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = 117 (int) TimeUnit.MINUTES.toMillis(1); 118 // Number of package failures within the duration above before we notify observers 119 @VisibleForTesting 120 static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5; 121 @VisibleForTesting 122 static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); 123 // Sliding window for tracking how many mitigation calls were made for a package. 124 @VisibleForTesting 125 static final long DEFAULT_DEESCALATION_WINDOW_MS = TimeUnit.HOURS.toMillis(1); 126 // Whether explicit health checks are enabled or not 127 private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true; 128 129 @VisibleForTesting 130 static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5; 131 static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); 132 133 // These properties track individual system server boot events, and are reset once the boot 134 // threshold is met, or the boot loop trigger window is exceeded between boot events. 135 private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; 136 private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start"; 137 138 // These properties track multiple calls made to observers tracking boot loops. They are reset 139 // when the de-escalation window is exceeded between boot events. 140 private static final String PROP_BOOT_MITIGATION_WINDOW_START = "sys.boot_mitigation_start"; 141 private static final String PROP_BOOT_MITIGATION_COUNT = "sys.boot_mitigation_count"; 142 143 private long mNumberOfNativeCrashPollsRemaining; 144 145 private static final int DB_VERSION = 1; 146 private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog"; 147 private static final String TAG_PACKAGE = "package"; 148 private static final String TAG_OBSERVER = "observer"; 149 private static final String ATTR_VERSION = "version"; 150 private static final String ATTR_NAME = "name"; 151 private static final String ATTR_DURATION = "duration"; 152 private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; 153 private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; 154 private static final String ATTR_MITIGATION_CALLS = "mitigation-calls"; 155 156 // A file containing information about the current mitigation count in the case of a boot loop. 157 // This allows boot loop information to persist in the case of an fs-checkpoint being 158 // aborted. 159 private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt"; 160 161 @GuardedBy("PackageWatchdog.class") 162 private static PackageWatchdog sPackageWatchdog; 163 164 private final Object mLock = new Object(); 165 // System server context 166 private final Context mContext; 167 // Handler to run short running tasks 168 private final Handler mShortTaskHandler; 169 // Handler for processing IO and long running tasks 170 private final Handler mLongTaskHandler; 171 // Contains (observer-name -> observer-handle) that have ever been registered from 172 // previous boots. Observers with all packages expired are periodically pruned. 173 // It is saved to disk on system shutdown and repouplated on startup so it survives reboots. 174 @GuardedBy("mLock") 175 private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>(); 176 // File containing the XML data of monitored packages /data/system/package-watchdog.xml 177 private final AtomicFile mPolicyFile; 178 private final ExplicitHealthCheckController mHealthCheckController; 179 private final ConnectivityModuleConnector mConnectivityModuleConnector; 180 private final Runnable mSyncRequests = this::syncRequests; 181 private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason; 182 private final Runnable mSaveToFile = this::saveToFile; 183 private final SystemClock mSystemClock; 184 private final BootThreshold mBootThreshold; 185 private final DeviceConfig.OnPropertiesChangedListener 186 mOnPropertyChangedListener = this::onPropertyChanged; 187 188 // The set of packages that have been synced with the ExplicitHealthCheckController 189 @GuardedBy("mLock") 190 private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); 191 @GuardedBy("mLock") 192 private boolean mIsPackagesReady; 193 // Flag to control whether explicit health checks are supported or not 194 @GuardedBy("mLock") 195 private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED; 196 @GuardedBy("mLock") 197 private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; 198 @GuardedBy("mLock") 199 private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; 200 // SystemClock#uptimeMillis when we last executed #syncState 201 // 0 if no prune is scheduled. 202 @GuardedBy("mLock") 203 private long mUptimeAtLastStateSync; 204 // If true, sync explicit health check packages with the ExplicitHealthCheckController. 205 @GuardedBy("mLock") 206 private boolean mSyncRequired = false; 207 208 @FunctionalInterface 209 @VisibleForTesting 210 interface SystemClock { uptimeMillis()211 long uptimeMillis(); 212 } 213 PackageWatchdog(Context context)214 private PackageWatchdog(Context context) { 215 // Needs to be constructed inline 216 this(context, new AtomicFile( 217 new File(new File(Environment.getDataDirectory(), "system"), 218 "package-watchdog.xml")), 219 new Handler(Looper.myLooper()), BackgroundThread.getHandler(), 220 new ExplicitHealthCheckController(context), 221 ConnectivityModuleConnector.getInstance(), 222 android.os.SystemClock::uptimeMillis); 223 } 224 225 /** 226 * Creates a PackageWatchdog that allows injecting dependencies. 227 */ 228 @VisibleForTesting PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, Handler longTaskHandler, ExplicitHealthCheckController controller, ConnectivityModuleConnector connectivityModuleConnector, SystemClock clock)229 PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, 230 Handler longTaskHandler, ExplicitHealthCheckController controller, 231 ConnectivityModuleConnector connectivityModuleConnector, SystemClock clock) { 232 mContext = context; 233 mPolicyFile = policyFile; 234 mShortTaskHandler = shortTaskHandler; 235 mLongTaskHandler = longTaskHandler; 236 mHealthCheckController = controller; 237 mConnectivityModuleConnector = connectivityModuleConnector; 238 mSystemClock = clock; 239 mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; 240 mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT, 241 DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS); 242 loadFromFile(); 243 sPackageWatchdog = this; 244 } 245 246 /** Creates or gets singleton instance of PackageWatchdog. */ getInstance(Context context)247 public static PackageWatchdog getInstance(Context context) { 248 synchronized (PackageWatchdog.class) { 249 if (sPackageWatchdog == null) { 250 new PackageWatchdog(context); 251 } 252 return sPackageWatchdog; 253 } 254 } 255 256 /** 257 * Called during boot to notify when packages are ready on the device so we can start 258 * binding. 259 */ onPackagesReady()260 public void onPackagesReady() { 261 synchronized (mLock) { 262 mIsPackagesReady = true; 263 mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), 264 packages -> onSupportedPackages(packages), 265 this::onSyncRequestNotified); 266 setPropertyChangedListenerLocked(); 267 updateConfigs(); 268 registerConnectivityModuleHealthListener(); 269 } 270 } 271 272 /** 273 * Registers {@code observer} to listen for package failures. Add a new ObserverInternal for 274 * this observer if it does not already exist. 275 * 276 * <p>Observers are expected to call this on boot. It does not specify any packages but 277 * it will resume observing any packages requested from a previous boot. 278 */ registerHealthObserver(PackageHealthObserver observer)279 public void registerHealthObserver(PackageHealthObserver observer) { 280 synchronized (mLock) { 281 ObserverInternal internalObserver = mAllObservers.get(observer.getName()); 282 if (internalObserver != null) { 283 internalObserver.registeredObserver = observer; 284 } else { 285 internalObserver = new ObserverInternal(observer.getName(), new ArrayList<>()); 286 internalObserver.registeredObserver = observer; 287 mAllObservers.put(observer.getName(), internalObserver); 288 syncState("added new observer"); 289 } 290 } 291 } 292 293 /** 294 * Starts observing the health of the {@code packages} for {@code observer} and notifies 295 * {@code observer} of any package failures within the monitoring duration. 296 * 297 * <p>If monitoring a package supporting explicit health check, at the end of the monitoring 298 * duration if {@link #onHealthCheckPassed} was never called, 299 * {@link PackageHealthObserver#execute} will be called as if the package failed. 300 * 301 * <p>If {@code observer} is already monitoring a package in {@code packageNames}, 302 * the monitoring window of that package will be reset to {@code durationMs} and the health 303 * check state will be reset to a default depending on if the package is contained in 304 * {@link mPackagesWithExplicitHealthCheckEnabled}. 305 * 306 * <p>If {@code packageNames} is empty, this will be a no-op. 307 * 308 * <p>If {@code durationMs} is less than 1, a default monitoring duration 309 * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used. 310 */ startObservingHealth(PackageHealthObserver observer, List<String> packageNames, long durationMs)311 public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames, 312 long durationMs) { 313 if (packageNames.isEmpty()) { 314 Slog.wtf(TAG, "No packages to observe, " + observer.getName()); 315 return; 316 } 317 if (durationMs < 1) { 318 Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer " 319 + observer.getName() + ". Not observing packages " + packageNames); 320 durationMs = DEFAULT_OBSERVING_DURATION_MS; 321 } 322 323 List<MonitoredPackage> packages = new ArrayList<>(); 324 for (int i = 0; i < packageNames.size(); i++) { 325 // Health checks not available yet so health check state will start INACTIVE 326 MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false); 327 if (pkg != null) { 328 packages.add(pkg); 329 } else { 330 Slog.w(TAG, "Failed to create MonitoredPackage for pkg=" + packageNames.get(i)); 331 } 332 } 333 334 if (packages.isEmpty()) { 335 return; 336 } 337 338 // Sync before we add the new packages to the observers. This will #pruneObservers, 339 // causing any elapsed time to be deducted from all existing packages before we add new 340 // packages. This maintains the invariant that the elapsed time for ALL (new and existing) 341 // packages is the same. 342 mLongTaskHandler.post(() -> { 343 syncState("observing new packages"); 344 345 synchronized (mLock) { 346 ObserverInternal oldObserver = mAllObservers.get(observer.getName()); 347 if (oldObserver == null) { 348 Slog.d(TAG, observer.getName() + " started monitoring health " 349 + "of packages " + packageNames); 350 mAllObservers.put(observer.getName(), 351 new ObserverInternal(observer.getName(), packages)); 352 } else { 353 Slog.d(TAG, observer.getName() + " added the following " 354 + "packages to monitor " + packageNames); 355 oldObserver.updatePackagesLocked(packages); 356 } 357 } 358 359 // Register observer in case not already registered 360 registerHealthObserver(observer); 361 362 // Sync after we add the new packages to the observers. We may have received packges 363 // requiring an earlier schedule than we are currently scheduled for. 364 syncState("updated observers"); 365 }); 366 367 } 368 369 /** 370 * Unregisters {@code observer} from listening to package failure. 371 * Additionally, this stops observing any packages that may have previously been observed 372 * even from a previous boot. 373 */ unregisterHealthObserver(PackageHealthObserver observer)374 public void unregisterHealthObserver(PackageHealthObserver observer) { 375 mLongTaskHandler.post(() -> { 376 synchronized (mLock) { 377 mAllObservers.remove(observer.getName()); 378 } 379 syncState("unregistering observer: " + observer.getName()); 380 }); 381 } 382 383 /** 384 * Called when a process fails due to a crash, ANR or explicit health check. 385 * 386 * <p>For each package contained in the process, one registered observer with the least user 387 * impact will be notified for mitigation. 388 * 389 * <p>This method could be called frequently if there is a severe problem on the device. 390 */ onPackageFailure(List<VersionedPackage> packages, @FailureReasons int failureReason)391 public void onPackageFailure(List<VersionedPackage> packages, 392 @FailureReasons int failureReason) { 393 if (packages == null) { 394 Slog.w(TAG, "Could not resolve a list of failing packages"); 395 return; 396 } 397 mLongTaskHandler.post(() -> { 398 synchronized (mLock) { 399 if (mAllObservers.isEmpty()) { 400 return; 401 } 402 boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH 403 || failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK); 404 if (requiresImmediateAction) { 405 handleFailureImmediately(packages, failureReason); 406 } else { 407 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 408 VersionedPackage versionedPackage = packages.get(pIndex); 409 // Observer that will receive failure for versionedPackage 410 PackageHealthObserver currentObserverToNotify = null; 411 int currentObserverImpact = Integer.MAX_VALUE; 412 MonitoredPackage currentMonitoredPackage = null; 413 414 // Find observer with least user impact 415 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 416 ObserverInternal observer = mAllObservers.valueAt(oIndex); 417 PackageHealthObserver registeredObserver = observer.registeredObserver; 418 if (registeredObserver != null 419 && observer.onPackageFailureLocked( 420 versionedPackage.getPackageName())) { 421 MonitoredPackage p = observer.getMonitoredPackage( 422 versionedPackage.getPackageName()); 423 int mitigationCount = 1; 424 if (p != null) { 425 mitigationCount = p.getMitigationCountLocked() + 1; 426 } 427 int impact = registeredObserver.onHealthCheckFailed( 428 versionedPackage, failureReason, mitigationCount); 429 if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 430 && impact < currentObserverImpact) { 431 currentObserverToNotify = registeredObserver; 432 currentObserverImpact = impact; 433 currentMonitoredPackage = p; 434 } 435 } 436 } 437 438 // Execute action with least user impact 439 if (currentObserverToNotify != null) { 440 int mitigationCount = 1; 441 if (currentMonitoredPackage != null) { 442 currentMonitoredPackage.noteMitigationCallLocked(); 443 mitigationCount = 444 currentMonitoredPackage.getMitigationCountLocked(); 445 } 446 currentObserverToNotify.execute(versionedPackage, 447 failureReason, mitigationCount); 448 } 449 } 450 } 451 } 452 }); 453 } 454 455 /** 456 * For native crashes or explicit health check failures, call directly into each observer to 457 * mitigate the error without going through failure threshold logic. 458 */ handleFailureImmediately(List<VersionedPackage> packages, @FailureReasons int failureReason)459 private void handleFailureImmediately(List<VersionedPackage> packages, 460 @FailureReasons int failureReason) { 461 VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null; 462 PackageHealthObserver currentObserverToNotify = null; 463 int currentObserverImpact = Integer.MAX_VALUE; 464 for (ObserverInternal observer: mAllObservers.values()) { 465 PackageHealthObserver registeredObserver = observer.registeredObserver; 466 if (registeredObserver != null) { 467 int impact = registeredObserver.onHealthCheckFailed( 468 failingPackage, failureReason, 1); 469 if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 470 && impact < currentObserverImpact) { 471 currentObserverToNotify = registeredObserver; 472 currentObserverImpact = impact; 473 } 474 } 475 } 476 if (currentObserverToNotify != null) { 477 currentObserverToNotify.execute(failingPackage, failureReason, 1); 478 } 479 } 480 481 /** 482 * Called when the system server boots. If the system server is detected to be in a boot loop, 483 * query each observer and perform the mitigation action with the lowest user impact. 484 */ noteBoot()485 public void noteBoot() { 486 synchronized (mLock) { 487 if (mBootThreshold.incrementAndTest()) { 488 mBootThreshold.reset(); 489 int mitigationCount = mBootThreshold.getMitigationCount() + 1; 490 PackageHealthObserver currentObserverToNotify = null; 491 int currentObserverImpact = Integer.MAX_VALUE; 492 for (int i = 0; i < mAllObservers.size(); i++) { 493 final ObserverInternal observer = mAllObservers.valueAt(i); 494 PackageHealthObserver registeredObserver = observer.registeredObserver; 495 if (registeredObserver != null) { 496 int impact = registeredObserver.onBootLoop(mitigationCount); 497 if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 498 && impact < currentObserverImpact) { 499 currentObserverToNotify = registeredObserver; 500 currentObserverImpact = impact; 501 } 502 } 503 } 504 if (currentObserverToNotify != null) { 505 mBootThreshold.setMitigationCount(mitigationCount); 506 mBootThreshold.saveMitigationCountToMetadata(); 507 currentObserverToNotify.executeBootLoopMitigation(mitigationCount); 508 } 509 } 510 } 511 } 512 513 // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also 514 // avoid holding lock? 515 // This currently adds about 7ms extra to shutdown thread 516 /** Writes the package information to file during shutdown. */ writeNow()517 public void writeNow() { 518 synchronized (mLock) { 519 // Must only run synchronous tasks as this runs on the ShutdownThread and no other 520 // thread is guaranteed to run during shutdown. 521 if (!mAllObservers.isEmpty()) { 522 mLongTaskHandler.removeCallbacks(mSaveToFile); 523 pruneObserversLocked(); 524 saveToFile(); 525 Slog.i(TAG, "Last write to update package durations"); 526 } 527 } 528 } 529 530 /** 531 * Enables or disables explicit health checks. 532 * <p> If explicit health checks are enabled, the health check service is started. 533 * <p> If explicit health checks are disabled, pending explicit health check requests are 534 * passed and the health check service is stopped. 535 */ setExplicitHealthCheckEnabled(boolean enabled)536 private void setExplicitHealthCheckEnabled(boolean enabled) { 537 synchronized (mLock) { 538 mIsHealthCheckEnabled = enabled; 539 mHealthCheckController.setEnabled(enabled); 540 mSyncRequired = true; 541 // Prune to update internal state whenever health check is enabled/disabled 542 syncState("health check state " + (enabled ? "enabled" : "disabled")); 543 } 544 } 545 546 /** 547 * This method should be only called on mShortTaskHandler, since it modifies 548 * {@link #mNumberOfNativeCrashPollsRemaining}. 549 */ checkAndMitigateNativeCrashes()550 private void checkAndMitigateNativeCrashes() { 551 mNumberOfNativeCrashPollsRemaining--; 552 // Check if native watchdog reported a crash 553 if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { 554 // We rollback everything available when crash is unattributable 555 onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); 556 // we stop polling after an attempt to execute rollback, regardless of whether the 557 // attempt succeeds or not 558 } else { 559 if (mNumberOfNativeCrashPollsRemaining > 0) { 560 mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), 561 NATIVE_CRASH_POLLING_INTERVAL_MILLIS); 562 } 563 } 564 } 565 566 /** 567 * Since this method can eventually trigger a rollback, it should be called 568 * only once boot has completed {@code onBootCompleted} and not earlier, because the install 569 * session must be entirely completed before we try to rollback. 570 */ scheduleCheckAndMitigateNativeCrashes()571 public void scheduleCheckAndMitigateNativeCrashes() { 572 Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " 573 + "and mitigate native crashes"); 574 mShortTaskHandler.post(()->checkAndMitigateNativeCrashes()); 575 } 576 577 /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */ 578 @Retention(SOURCE) 579 @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, 580 PackageHealthObserverImpact.USER_IMPACT_LEVEL_10, 581 PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, 582 PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, 583 PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, 584 PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) 585 public @interface PackageHealthObserverImpact { 586 /** No action to take. */ 587 int USER_IMPACT_LEVEL_0 = 0; 588 /* Action has low user impact, user of a device will barely notice. */ 589 int USER_IMPACT_LEVEL_10 = 10; 590 /* Actions having medium user impact, user of a device will likely notice. */ 591 int USER_IMPACT_LEVEL_30 = 30; 592 int USER_IMPACT_LEVEL_50 = 50; 593 int USER_IMPACT_LEVEL_70 = 70; 594 /* Action has high user impact, a last resort, user of a device will be very frustrated. */ 595 int USER_IMPACT_LEVEL_100 = 100; 596 } 597 598 /** Register instances of this interface to receive notifications on package failure. */ 599 public interface PackageHealthObserver { 600 /** 601 * Called when health check fails for the {@code versionedPackage}. 602 * 603 * @param versionedPackage the package that is failing. This may be null if a native 604 * service is crashing. 605 * @param failureReason the type of failure that is occurring. 606 * @param mitigationCount the number of times mitigation has been called for this package 607 * (including this time). 608 * 609 * 610 * @return any one of {@link PackageHealthObserverImpact} to express the impact 611 * to the user on {@link #execute} 612 */ onHealthCheckFailed( @ullable VersionedPackage versionedPackage, @FailureReasons int failureReason, int mitigationCount)613 @PackageHealthObserverImpact int onHealthCheckFailed( 614 @Nullable VersionedPackage versionedPackage, 615 @FailureReasons int failureReason, 616 int mitigationCount); 617 618 /** 619 * Executes mitigation for {@link #onHealthCheckFailed}. 620 * 621 * @param versionedPackage the package that is failing. This may be null if a native 622 * service is crashing. 623 * @param failureReason the type of failure that is occurring. 624 * @param mitigationCount the number of times mitigation has been called for this package 625 * (including this time). 626 * @return {@code true} if action was executed successfully, {@code false} otherwise 627 */ execute(@ullable VersionedPackage versionedPackage, @FailureReasons int failureReason, int mitigationCount)628 boolean execute(@Nullable VersionedPackage versionedPackage, 629 @FailureReasons int failureReason, int mitigationCount); 630 631 632 /** 633 * Called when the system server has booted several times within a window of time, defined 634 * by {@link #mBootThreshold} 635 * 636 * @param mitigationCount the number of times mitigation has been attempted for this 637 * boot loop (including this time). 638 */ onBootLoop(int mitigationCount)639 default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { 640 return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; 641 } 642 643 /** 644 * Executes mitigation for {@link #onBootLoop} 645 * @param mitigationCount the number of times mitigation has been attempted for this 646 * boot loop (including this time). 647 */ executeBootLoopMitigation(int mitigationCount)648 default boolean executeBootLoopMitigation(int mitigationCount) { 649 return false; 650 } 651 652 // TODO(b/120598832): Ensure uniqueness? 653 /** 654 * Identifier for the observer, should not change across device updates otherwise the 655 * watchdog may drop observing packages with the old name. 656 */ getName()657 String getName(); 658 659 /** 660 * An observer will not be pruned if this is set, even if the observer is not explicitly 661 * monitoring any packages. 662 */ isPersistent()663 default boolean isPersistent() { 664 return false; 665 } 666 667 /** 668 * Returns {@code true} if this observer wishes to observe the given package, {@code false} 669 * otherwise 670 * 671 * <p> A persistent observer may choose to start observing certain failing packages, even if 672 * it has not explicitly asked to watch the package with {@link #startObservingHealth}. 673 */ mayObservePackage(String packageName)674 default boolean mayObservePackage(String packageName) { 675 return false; 676 } 677 } 678 679 @VisibleForTesting getTriggerFailureCount()680 long getTriggerFailureCount() { 681 synchronized (mLock) { 682 return mTriggerFailureCount; 683 } 684 } 685 686 @VisibleForTesting getTriggerFailureDurationMs()687 long getTriggerFailureDurationMs() { 688 synchronized (mLock) { 689 return mTriggerFailureDurationMs; 690 } 691 } 692 693 /** 694 * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. 695 */ syncRequestsAsync()696 private void syncRequestsAsync() { 697 mShortTaskHandler.removeCallbacks(mSyncRequests); 698 mShortTaskHandler.post(mSyncRequests); 699 } 700 701 /** 702 * Syncs health check requests with the {@link ExplicitHealthCheckController}. 703 * Calls to this must be serialized. 704 * 705 * @see #syncRequestsAsync 706 */ syncRequests()707 private void syncRequests() { 708 boolean syncRequired = false; 709 synchronized (mLock) { 710 if (mIsPackagesReady) { 711 Set<String> packages = getPackagesPendingHealthChecksLocked(); 712 if (mSyncRequired || !packages.equals(mRequestedHealthCheckPackages) 713 || packages.isEmpty()) { 714 syncRequired = true; 715 mRequestedHealthCheckPackages = packages; 716 } 717 } // else, we will sync requests when packages become ready 718 } 719 720 // Call outside lock to avoid holding lock when calling into the controller. 721 if (syncRequired) { 722 Slog.i(TAG, "Syncing health check requests for packages: " 723 + mRequestedHealthCheckPackages); 724 mHealthCheckController.syncRequests(mRequestedHealthCheckPackages); 725 mSyncRequired = false; 726 } 727 } 728 729 /** 730 * Updates the observers monitoring {@code packageName} that explicit health check has passed. 731 * 732 * <p> This update is strictly for registered observers at the time of the call 733 * Observers that register after this signal will have no knowledge of prior signals and will 734 * effectively behave as if the explicit health check hasn't passed for {@code packageName}. 735 * 736 * <p> {@code packageName} can still be considered failed if reported by 737 * {@link #onPackageFailureLocked} before the package expires. 738 * 739 * <p> Triggered by components outside the system server when they are fully functional after an 740 * update. 741 */ onHealthCheckPassed(String packageName)742 private void onHealthCheckPassed(String packageName) { 743 Slog.i(TAG, "Health check passed for package: " + packageName); 744 boolean isStateChanged = false; 745 746 synchronized (mLock) { 747 for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { 748 ObserverInternal observer = mAllObservers.valueAt(observerIdx); 749 MonitoredPackage monitoredPackage = observer.getMonitoredPackage(packageName); 750 751 if (monitoredPackage != null) { 752 int oldState = monitoredPackage.getHealthCheckStateLocked(); 753 int newState = monitoredPackage.tryPassHealthCheckLocked(); 754 isStateChanged |= oldState != newState; 755 } 756 } 757 } 758 759 if (isStateChanged) { 760 syncState("health check passed for " + packageName); 761 } 762 } 763 onSupportedPackages(List<PackageConfig> supportedPackages)764 private void onSupportedPackages(List<PackageConfig> supportedPackages) { 765 boolean isStateChanged = false; 766 767 Map<String, Long> supportedPackageTimeouts = new ArrayMap<>(); 768 Iterator<PackageConfig> it = supportedPackages.iterator(); 769 while (it.hasNext()) { 770 PackageConfig info = it.next(); 771 supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis()); 772 } 773 774 synchronized (mLock) { 775 Slog.d(TAG, "Received supported packages " + supportedPackages); 776 Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); 777 while (oit.hasNext()) { 778 Iterator<MonitoredPackage> pit = oit.next().getMonitoredPackages() 779 .values().iterator(); 780 while (pit.hasNext()) { 781 MonitoredPackage monitoredPackage = pit.next(); 782 String packageName = monitoredPackage.getName(); 783 int oldState = monitoredPackage.getHealthCheckStateLocked(); 784 int newState; 785 786 if (supportedPackageTimeouts.containsKey(packageName)) { 787 // Supported packages become ACTIVE if currently INACTIVE 788 newState = monitoredPackage.setHealthCheckActiveLocked( 789 supportedPackageTimeouts.get(packageName)); 790 } else { 791 // Unsupported packages are marked as PASSED unless already FAILED 792 newState = monitoredPackage.tryPassHealthCheckLocked(); 793 } 794 isStateChanged |= oldState != newState; 795 } 796 } 797 } 798 799 if (isStateChanged) { 800 syncState("updated health check supported packages " + supportedPackages); 801 } 802 } 803 onSyncRequestNotified()804 private void onSyncRequestNotified() { 805 synchronized (mLock) { 806 mSyncRequired = true; 807 syncRequestsAsync(); 808 } 809 } 810 811 @GuardedBy("mLock") getPackagesPendingHealthChecksLocked()812 private Set<String> getPackagesPendingHealthChecksLocked() { 813 Set<String> packages = new ArraySet<>(); 814 Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); 815 while (oit.hasNext()) { 816 ObserverInternal observer = oit.next(); 817 Iterator<MonitoredPackage> pit = 818 observer.getMonitoredPackages().values().iterator(); 819 while (pit.hasNext()) { 820 MonitoredPackage monitoredPackage = pit.next(); 821 String packageName = monitoredPackage.getName(); 822 if (monitoredPackage.isPendingHealthChecksLocked()) { 823 packages.add(packageName); 824 } 825 } 826 } 827 return packages; 828 } 829 830 /** 831 * Syncs the state of the observers. 832 * 833 * <p> Prunes all observers, saves new state to disk, syncs health check requests with the 834 * health check service and schedules the next state sync. 835 */ syncState(String reason)836 private void syncState(String reason) { 837 synchronized (mLock) { 838 Slog.i(TAG, "Syncing state, reason: " + reason); 839 pruneObserversLocked(); 840 841 saveToFileAsync(); 842 syncRequestsAsync(); 843 844 // Done syncing state, schedule the next state sync 845 scheduleNextSyncStateLocked(); 846 } 847 } 848 syncStateWithScheduledReason()849 private void syncStateWithScheduledReason() { 850 syncState("scheduled"); 851 } 852 853 @GuardedBy("mLock") scheduleNextSyncStateLocked()854 private void scheduleNextSyncStateLocked() { 855 long durationMs = getNextStateSyncMillisLocked(); 856 mShortTaskHandler.removeCallbacks(mSyncStateWithScheduledReason); 857 if (durationMs == Long.MAX_VALUE) { 858 Slog.i(TAG, "Cancelling state sync, nothing to sync"); 859 mUptimeAtLastStateSync = 0; 860 } else { 861 mUptimeAtLastStateSync = mSystemClock.uptimeMillis(); 862 mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs); 863 } 864 } 865 866 /** 867 * Returns the next duration in millis to sync the watchdog state. 868 * 869 * @returns Long#MAX_VALUE if there are no observed packages. 870 */ 871 @GuardedBy("mLock") getNextStateSyncMillisLocked()872 private long getNextStateSyncMillisLocked() { 873 long shortestDurationMs = Long.MAX_VALUE; 874 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 875 ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex) 876 .getMonitoredPackages(); 877 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 878 MonitoredPackage mp = packages.valueAt(pIndex); 879 long duration = mp.getShortestScheduleDurationMsLocked(); 880 if (duration < shortestDurationMs) { 881 shortestDurationMs = duration; 882 } 883 } 884 } 885 return shortestDurationMs; 886 } 887 888 /** 889 * Removes {@code elapsedMs} milliseconds from all durations on monitored packages 890 * and updates other internal state. 891 */ 892 @GuardedBy("mLock") pruneObserversLocked()893 private void pruneObserversLocked() { 894 long elapsedMs = mUptimeAtLastStateSync == 0 895 ? 0 : mSystemClock.uptimeMillis() - mUptimeAtLastStateSync; 896 if (elapsedMs <= 0) { 897 Slog.i(TAG, "Not pruning observers, elapsed time: " + elapsedMs + "ms"); 898 return; 899 } 900 901 Iterator<ObserverInternal> it = mAllObservers.values().iterator(); 902 while (it.hasNext()) { 903 ObserverInternal observer = it.next(); 904 Set<MonitoredPackage> failedPackages = 905 observer.prunePackagesLocked(elapsedMs); 906 if (!failedPackages.isEmpty()) { 907 onHealthCheckFailed(observer, failedPackages); 908 } 909 if (observer.getMonitoredPackages().isEmpty() && (observer.registeredObserver == null 910 || !observer.registeredObserver.isPersistent())) { 911 Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired"); 912 it.remove(); 913 } 914 } 915 } 916 onHealthCheckFailed(ObserverInternal observer, Set<MonitoredPackage> failedPackages)917 private void onHealthCheckFailed(ObserverInternal observer, 918 Set<MonitoredPackage> failedPackages) { 919 mLongTaskHandler.post(() -> { 920 synchronized (mLock) { 921 PackageHealthObserver registeredObserver = observer.registeredObserver; 922 if (registeredObserver != null) { 923 Iterator<MonitoredPackage> it = failedPackages.iterator(); 924 while (it.hasNext()) { 925 VersionedPackage versionedPkg = getVersionedPackage(it.next().getName()); 926 if (versionedPkg != null) { 927 Slog.i(TAG, 928 "Explicit health check failed for package " + versionedPkg); 929 registeredObserver.execute(versionedPkg, 930 PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1); 931 } 932 } 933 } 934 } 935 }); 936 } 937 938 /** 939 * Gets PackageInfo for the given package. Matches any user and apex. 940 * 941 * @throws PackageManager.NameNotFoundException if no such package is installed. 942 */ getPackageInfo(String packageName)943 private PackageInfo getPackageInfo(String packageName) 944 throws PackageManager.NameNotFoundException { 945 PackageManager pm = mContext.getPackageManager(); 946 try { 947 // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX 948 // flag, so make two separate attempts to get the package info. 949 // We don't need both flags at the same time because we assume 950 // apex files are always installed for all users. 951 return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER); 952 } catch (PackageManager.NameNotFoundException e) { 953 return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); 954 } 955 } 956 957 @Nullable getVersionedPackage(String packageName)958 private VersionedPackage getVersionedPackage(String packageName) { 959 final PackageManager pm = mContext.getPackageManager(); 960 if (pm == null || TextUtils.isEmpty(packageName)) { 961 return null; 962 } 963 try { 964 final long versionCode = getPackageInfo(packageName).getLongVersionCode(); 965 return new VersionedPackage(packageName, versionCode); 966 } catch (PackageManager.NameNotFoundException e) { 967 return null; 968 } 969 } 970 971 /** 972 * Loads mAllObservers from file. 973 * 974 * <p>Note that this is <b>not</b> thread safe and should only called be called 975 * from the constructor. 976 */ loadFromFile()977 private void loadFromFile() { 978 InputStream infile = null; 979 mAllObservers.clear(); 980 try { 981 infile = mPolicyFile.openRead(); 982 final TypedXmlPullParser parser = Xml.resolvePullParser(infile); 983 XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG); 984 int outerDepth = parser.getDepth(); 985 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 986 ObserverInternal observer = ObserverInternal.read(parser, this); 987 if (observer != null) { 988 mAllObservers.put(observer.name, observer); 989 } 990 } 991 } catch (FileNotFoundException e) { 992 // Nothing to monitor 993 } catch (IOException | NumberFormatException | XmlPullParserException e) { 994 Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e); 995 mPolicyFile.delete(); 996 } finally { 997 IoUtils.closeQuietly(infile); 998 } 999 } 1000 onPropertyChanged(DeviceConfig.Properties properties)1001 private void onPropertyChanged(DeviceConfig.Properties properties) { 1002 try { 1003 updateConfigs(); 1004 } catch (Exception ignore) { 1005 Slog.w(TAG, "Failed to reload device config changes"); 1006 } 1007 } 1008 1009 /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */ setPropertyChangedListenerLocked()1010 private void setPropertyChangedListenerLocked() { 1011 DeviceConfig.addOnPropertiesChangedListener( 1012 DeviceConfig.NAMESPACE_ROLLBACK, 1013 mContext.getMainExecutor(), 1014 mOnPropertyChangedListener); 1015 } 1016 1017 @VisibleForTesting removePropertyChangedListener()1018 void removePropertyChangedListener() { 1019 DeviceConfig.removeOnPropertiesChangedListener(mOnPropertyChangedListener); 1020 } 1021 1022 /** 1023 * Health check is enabled or disabled after reading the flags 1024 * from DeviceConfig. 1025 */ 1026 @VisibleForTesting updateConfigs()1027 void updateConfigs() { 1028 synchronized (mLock) { 1029 mTriggerFailureCount = DeviceConfig.getInt( 1030 DeviceConfig.NAMESPACE_ROLLBACK, 1031 PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, 1032 DEFAULT_TRIGGER_FAILURE_COUNT); 1033 if (mTriggerFailureCount <= 0) { 1034 mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; 1035 } 1036 1037 mTriggerFailureDurationMs = DeviceConfig.getInt( 1038 DeviceConfig.NAMESPACE_ROLLBACK, 1039 PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, 1040 DEFAULT_TRIGGER_FAILURE_DURATION_MS); 1041 if (mTriggerFailureDurationMs <= 0) { 1042 mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; 1043 } 1044 1045 setExplicitHealthCheckEnabled(DeviceConfig.getBoolean( 1046 DeviceConfig.NAMESPACE_ROLLBACK, 1047 PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 1048 DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED)); 1049 } 1050 } 1051 registerConnectivityModuleHealthListener()1052 private void registerConnectivityModuleHealthListener() { 1053 // TODO: have an internal method to trigger a rollback by reporting high severity errors, 1054 // and rely on ActivityManager to inform the watchdog of severe network stack crashes 1055 // instead of having this listener in parallel. 1056 mConnectivityModuleConnector.registerHealthListener( 1057 packageName -> { 1058 final VersionedPackage pkg = getVersionedPackage(packageName); 1059 if (pkg == null) { 1060 Slog.wtf(TAG, "NetworkStack failed but could not find its package"); 1061 return; 1062 } 1063 final List<VersionedPackage> pkgList = Collections.singletonList(pkg); 1064 onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); 1065 }); 1066 } 1067 1068 /** 1069 * Persists mAllObservers to file. Threshold information is ignored. 1070 */ saveToFile()1071 private boolean saveToFile() { 1072 Slog.i(TAG, "Saving observer state to file"); 1073 synchronized (mLock) { 1074 FileOutputStream stream; 1075 try { 1076 stream = mPolicyFile.startWrite(); 1077 } catch (IOException e) { 1078 Slog.w(TAG, "Cannot update monitored packages", e); 1079 return false; 1080 } 1081 1082 try { 1083 TypedXmlSerializer out = Xml.resolveSerializer(stream); 1084 out.startDocument(null, true); 1085 out.startTag(null, TAG_PACKAGE_WATCHDOG); 1086 out.attributeInt(null, ATTR_VERSION, DB_VERSION); 1087 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { 1088 mAllObservers.valueAt(oIndex).writeLocked(out); 1089 } 1090 out.endTag(null, TAG_PACKAGE_WATCHDOG); 1091 out.endDocument(); 1092 mPolicyFile.finishWrite(stream); 1093 return true; 1094 } catch (IOException e) { 1095 Slog.w(TAG, "Failed to save monitored packages, restoring backup", e); 1096 mPolicyFile.failWrite(stream); 1097 return false; 1098 } finally { 1099 IoUtils.closeQuietly(stream); 1100 } 1101 } 1102 } 1103 saveToFileAsync()1104 private void saveToFileAsync() { 1105 if (!mLongTaskHandler.hasCallbacks(mSaveToFile)) { 1106 mLongTaskHandler.post(mSaveToFile); 1107 } 1108 } 1109 1110 /** Convert a {@code LongArrayQueue} to a String of comma-separated values. */ longArrayQueueToString(LongArrayQueue queue)1111 public static String longArrayQueueToString(LongArrayQueue queue) { 1112 if (queue.size() > 0) { 1113 StringBuilder sb = new StringBuilder(); 1114 sb.append(queue.get(0)); 1115 for (int i = 1; i < queue.size(); i++) { 1116 sb.append(","); 1117 sb.append(queue.get(i)); 1118 } 1119 return sb.toString(); 1120 } 1121 return ""; 1122 } 1123 1124 /** Parse a comma-separated String of longs into a LongArrayQueue. */ parseLongArrayQueue(String commaSeparatedValues)1125 public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) { 1126 LongArrayQueue result = new LongArrayQueue(); 1127 if (!TextUtils.isEmpty(commaSeparatedValues)) { 1128 String[] values = commaSeparatedValues.split(","); 1129 for (String value : values) { 1130 result.addLast(Long.parseLong(value)); 1131 } 1132 } 1133 return result; 1134 } 1135 1136 1137 /** Dump status of every observer in mAllObservers. */ dump(IndentingPrintWriter pw)1138 public void dump(IndentingPrintWriter pw) { 1139 pw.println("Package Watchdog status"); 1140 pw.increaseIndent(); 1141 synchronized (mLock) { 1142 for (String observerName : mAllObservers.keySet()) { 1143 pw.println("Observer name: " + observerName); 1144 pw.increaseIndent(); 1145 ObserverInternal observerInternal = mAllObservers.get(observerName); 1146 observerInternal.dump(pw); 1147 pw.decreaseIndent(); 1148 } 1149 } 1150 } 1151 1152 /** 1153 * Represents an observer monitoring a set of packages along with the failure thresholds for 1154 * each package. 1155 * 1156 * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing 1157 * instances of this class. 1158 */ 1159 private static class ObserverInternal { 1160 public final String name; 1161 @GuardedBy("mLock") 1162 private final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>(); 1163 @Nullable 1164 @GuardedBy("mLock") 1165 public PackageHealthObserver registeredObserver; 1166 ObserverInternal(String name, List<MonitoredPackage> packages)1167 ObserverInternal(String name, List<MonitoredPackage> packages) { 1168 this.name = name; 1169 updatePackagesLocked(packages); 1170 } 1171 1172 /** 1173 * Writes important {@link MonitoredPackage} details for this observer to file. 1174 * Does not persist any package failure thresholds. 1175 */ 1176 @GuardedBy("mLock") writeLocked(TypedXmlSerializer out)1177 public boolean writeLocked(TypedXmlSerializer out) { 1178 try { 1179 out.startTag(null, TAG_OBSERVER); 1180 out.attribute(null, ATTR_NAME, name); 1181 for (int i = 0; i < mPackages.size(); i++) { 1182 MonitoredPackage p = mPackages.valueAt(i); 1183 p.writeLocked(out); 1184 } 1185 out.endTag(null, TAG_OBSERVER); 1186 return true; 1187 } catch (IOException e) { 1188 Slog.w(TAG, "Cannot save observer", e); 1189 return false; 1190 } 1191 } 1192 1193 @GuardedBy("mLock") updatePackagesLocked(List<MonitoredPackage> packages)1194 public void updatePackagesLocked(List<MonitoredPackage> packages) { 1195 for (int pIndex = 0; pIndex < packages.size(); pIndex++) { 1196 MonitoredPackage p = packages.get(pIndex); 1197 MonitoredPackage existingPackage = getMonitoredPackage(p.getName()); 1198 if (existingPackage != null) { 1199 existingPackage.updateHealthCheckDuration(p.mDurationMs); 1200 } else { 1201 putMonitoredPackage(p); 1202 } 1203 } 1204 } 1205 1206 /** 1207 * Reduces the monitoring durations of all packages observed by this observer by 1208 * {@code elapsedMs}. If any duration is less than 0, the package is removed from 1209 * observation. If any health check duration is less than 0, the health check result 1210 * is evaluated. 1211 * 1212 * @return a {@link Set} of packages that were removed from the observer without explicit 1213 * health check passing, or an empty list if no package expired for which an explicit health 1214 * check was still pending 1215 */ 1216 @GuardedBy("mLock") prunePackagesLocked(long elapsedMs)1217 private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) { 1218 Set<MonitoredPackage> failedPackages = new ArraySet<>(); 1219 Iterator<MonitoredPackage> it = mPackages.values().iterator(); 1220 while (it.hasNext()) { 1221 MonitoredPackage p = it.next(); 1222 int oldState = p.getHealthCheckStateLocked(); 1223 int newState = p.handleElapsedTimeLocked(elapsedMs); 1224 if (oldState != HealthCheckState.FAILED 1225 && newState == HealthCheckState.FAILED) { 1226 Slog.i(TAG, "Package " + p.getName() + " failed health check"); 1227 failedPackages.add(p); 1228 } 1229 if (p.isExpiredLocked()) { 1230 it.remove(); 1231 } 1232 } 1233 return failedPackages; 1234 } 1235 1236 /** 1237 * Increments failure counts of {@code packageName}. 1238 * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise 1239 */ 1240 @GuardedBy("mLock") onPackageFailureLocked(String packageName)1241 public boolean onPackageFailureLocked(String packageName) { 1242 if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent() 1243 && registeredObserver.mayObservePackage(packageName)) { 1244 putMonitoredPackage(sPackageWatchdog.newMonitoredPackage( 1245 packageName, DEFAULT_OBSERVING_DURATION_MS, false)); 1246 } 1247 MonitoredPackage p = getMonitoredPackage(packageName); 1248 if (p != null) { 1249 return p.onFailureLocked(); 1250 } 1251 return false; 1252 } 1253 1254 /** 1255 * Returns the map of packages monitored by this observer. 1256 * 1257 * @return a mapping of package names to {@link MonitoredPackage} objects. 1258 */ 1259 @GuardedBy("mLock") getMonitoredPackages()1260 public ArrayMap<String, MonitoredPackage> getMonitoredPackages() { 1261 return mPackages; 1262 } 1263 1264 /** 1265 * Returns the {@link MonitoredPackage} associated with a given package name if the 1266 * package is being monitored by this observer. 1267 * 1268 * @param packageName: the name of the package. 1269 * @return the {@link MonitoredPackage} object associated with the package name if one 1270 * exists, {@code null} otherwise. 1271 */ 1272 @GuardedBy("mLock") 1273 @Nullable getMonitoredPackage(String packageName)1274 public MonitoredPackage getMonitoredPackage(String packageName) { 1275 return mPackages.get(packageName); 1276 } 1277 1278 /** 1279 * Associates a {@link MonitoredPackage} with the observer. 1280 * 1281 * @param p: the {@link MonitoredPackage} to store. 1282 */ 1283 @GuardedBy("mLock") putMonitoredPackage(MonitoredPackage p)1284 public void putMonitoredPackage(MonitoredPackage p) { 1285 mPackages.put(p.getName(), p); 1286 } 1287 1288 /** 1289 * Returns one ObserverInternal from the {@code parser} and advances its state. 1290 * 1291 * <p>Note that this method is <b>not</b> thread safe. It should only be called from 1292 * #loadFromFile which in turn is only called on construction of the 1293 * singleton PackageWatchdog. 1294 **/ read(TypedXmlPullParser parser, PackageWatchdog watchdog)1295 public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) { 1296 String observerName = null; 1297 if (TAG_OBSERVER.equals(parser.getName())) { 1298 observerName = parser.getAttributeValue(null, ATTR_NAME); 1299 if (TextUtils.isEmpty(observerName)) { 1300 Slog.wtf(TAG, "Unable to read observer name"); 1301 return null; 1302 } 1303 } 1304 List<MonitoredPackage> packages = new ArrayList<>(); 1305 int innerDepth = parser.getDepth(); 1306 try { 1307 while (XmlUtils.nextElementWithin(parser, innerDepth)) { 1308 if (TAG_PACKAGE.equals(parser.getName())) { 1309 try { 1310 MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser); 1311 if (pkg != null) { 1312 packages.add(pkg); 1313 } 1314 } catch (NumberFormatException e) { 1315 Slog.wtf(TAG, "Skipping package for observer " + observerName, e); 1316 continue; 1317 } 1318 } 1319 } 1320 } catch (XmlPullParserException | IOException e) { 1321 Slog.wtf(TAG, "Unable to read observer " + observerName, e); 1322 return null; 1323 } 1324 if (packages.isEmpty()) { 1325 return null; 1326 } 1327 return new ObserverInternal(observerName, packages); 1328 } 1329 1330 /** Dumps information about this observer and the packages it watches. */ dump(IndentingPrintWriter pw)1331 public void dump(IndentingPrintWriter pw) { 1332 boolean isPersistent = registeredObserver != null && registeredObserver.isPersistent(); 1333 pw.println("Persistent: " + isPersistent); 1334 for (String packageName : mPackages.keySet()) { 1335 MonitoredPackage p = getMonitoredPackage(packageName); 1336 pw.println(packageName + ": "); 1337 pw.increaseIndent(); 1338 pw.println("# Failures: " + p.mFailureHistory.size()); 1339 pw.println("Monitoring duration remaining: " + p.mDurationMs + "ms"); 1340 pw.println("Explicit health check duration: " + p.mHealthCheckDurationMs + "ms"); 1341 pw.println("Health check state: " + p.toString(p.mHealthCheckState)); 1342 pw.decreaseIndent(); 1343 } 1344 } 1345 } 1346 1347 @Retention(SOURCE) 1348 @IntDef(value = { 1349 HealthCheckState.ACTIVE, 1350 HealthCheckState.INACTIVE, 1351 HealthCheckState.PASSED, 1352 HealthCheckState.FAILED}) 1353 public @interface HealthCheckState { 1354 // The package has not passed health check but has requested a health check 1355 int ACTIVE = 0; 1356 // The package has not passed health check and has not requested a health check 1357 int INACTIVE = 1; 1358 // The package has passed health check 1359 int PASSED = 2; 1360 // The package has failed health check 1361 int FAILED = 3; 1362 } 1363 newMonitoredPackage( String name, long durationMs, boolean hasPassedHealthCheck)1364 MonitoredPackage newMonitoredPackage( 1365 String name, long durationMs, boolean hasPassedHealthCheck) { 1366 return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck, 1367 new LongArrayQueue()); 1368 } 1369 newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs, boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls)1370 MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs, 1371 boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) { 1372 return new MonitoredPackage(name, durationMs, healthCheckDurationMs, 1373 hasPassedHealthCheck, mitigationCalls); 1374 } 1375 parseMonitoredPackage(TypedXmlPullParser parser)1376 MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser) 1377 throws XmlPullParserException { 1378 String packageName = parser.getAttributeValue(null, ATTR_NAME); 1379 long duration = parser.getAttributeLong(null, ATTR_DURATION); 1380 long healthCheckDuration = parser.getAttributeLong(null, 1381 ATTR_EXPLICIT_HEALTH_CHECK_DURATION); 1382 boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK); 1383 LongArrayQueue mitigationCalls = parseLongArrayQueue( 1384 parser.getAttributeValue(null, ATTR_MITIGATION_CALLS)); 1385 return newMonitoredPackage(packageName, 1386 duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls); 1387 } 1388 1389 /** 1390 * Represents a package and its health check state along with the time 1391 * it should be monitored for. 1392 * 1393 * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing 1394 * instances of this class. 1395 */ 1396 class MonitoredPackage { 1397 private final String mPackageName; 1398 // Times when package failures happen sorted in ascending order 1399 @GuardedBy("mLock") 1400 private final LongArrayQueue mFailureHistory = new LongArrayQueue(); 1401 // Times when an observer was called to mitigate this package's failure. Sorted in 1402 // ascending order. 1403 @GuardedBy("mLock") 1404 private final LongArrayQueue mMitigationCalls; 1405 // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after 1406 // methods that could change the health check state: handleElapsedTimeLocked and 1407 // tryPassHealthCheckLocked 1408 private int mHealthCheckState = HealthCheckState.INACTIVE; 1409 // Whether an explicit health check has passed. 1410 // This value in addition with mHealthCheckDurationMs determines the health check state 1411 // of the package, see #getHealthCheckStateLocked 1412 @GuardedBy("mLock") 1413 private boolean mHasPassedHealthCheck; 1414 // System uptime duration to monitor package. 1415 @GuardedBy("mLock") 1416 private long mDurationMs; 1417 // System uptime duration to check the result of an explicit health check 1418 // Initially, MAX_VALUE until we get a value from the health check service 1419 // and request health checks. 1420 // This value in addition with mHasPassedHealthCheck determines the health check state 1421 // of the package, see #getHealthCheckStateLocked 1422 @GuardedBy("mLock") 1423 private long mHealthCheckDurationMs = Long.MAX_VALUE; 1424 MonitoredPackage(String packageName, long durationMs, long healthCheckDurationMs, boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls)1425 MonitoredPackage(String packageName, long durationMs, 1426 long healthCheckDurationMs, boolean hasPassedHealthCheck, 1427 LongArrayQueue mitigationCalls) { 1428 mPackageName = packageName; 1429 mDurationMs = durationMs; 1430 mHealthCheckDurationMs = healthCheckDurationMs; 1431 mHasPassedHealthCheck = hasPassedHealthCheck; 1432 mMitigationCalls = mitigationCalls; 1433 updateHealthCheckStateLocked(); 1434 } 1435 1436 /** Writes the salient fields to disk using {@code out}. */ 1437 @GuardedBy("mLock") writeLocked(TypedXmlSerializer out)1438 public void writeLocked(TypedXmlSerializer out) throws IOException { 1439 out.startTag(null, TAG_PACKAGE); 1440 out.attribute(null, ATTR_NAME, getName()); 1441 out.attributeLong(null, ATTR_DURATION, mDurationMs); 1442 out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); 1443 out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); 1444 LongArrayQueue normalizedCalls = normalizeMitigationCalls(); 1445 out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls)); 1446 out.endTag(null, TAG_PACKAGE); 1447 } 1448 1449 /** 1450 * Increment package failures or resets failure count depending on the last package failure. 1451 * 1452 * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise 1453 */ 1454 @GuardedBy("mLock") onFailureLocked()1455 public boolean onFailureLocked() { 1456 // Sliding window algorithm: find out if there exists a window containing failures >= 1457 // mTriggerFailureCount. 1458 final long now = mSystemClock.uptimeMillis(); 1459 mFailureHistory.addLast(now); 1460 while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) { 1461 // Prune values falling out of the window 1462 mFailureHistory.removeFirst(); 1463 } 1464 boolean failed = mFailureHistory.size() >= mTriggerFailureCount; 1465 if (failed) { 1466 mFailureHistory.clear(); 1467 } 1468 return failed; 1469 } 1470 1471 /** 1472 * Notes the timestamp of a mitigation call into the observer. 1473 */ 1474 @GuardedBy("mLock") noteMitigationCallLocked()1475 public void noteMitigationCallLocked() { 1476 mMitigationCalls.addLast(mSystemClock.uptimeMillis()); 1477 } 1478 1479 /** 1480 * Prunes any mitigation calls outside of the de-escalation window, and returns the 1481 * number of calls that are in the window afterwards. 1482 * 1483 * @return the number of mitigation calls made in the de-escalation window. 1484 */ 1485 @GuardedBy("mLock") getMitigationCountLocked()1486 public int getMitigationCountLocked() { 1487 try { 1488 final long now = mSystemClock.uptimeMillis(); 1489 while (now - mMitigationCalls.peekFirst() > DEFAULT_DEESCALATION_WINDOW_MS) { 1490 mMitigationCalls.removeFirst(); 1491 } 1492 } catch (NoSuchElementException ignore) { 1493 } 1494 1495 return mMitigationCalls.size(); 1496 } 1497 1498 /** 1499 * Before writing to disk, make the mitigation call timestamps relative to the current 1500 * system uptime. This is because they need to be relative to the uptime which will reset 1501 * at the next boot. 1502 * 1503 * @return a LongArrayQueue of the mitigation calls relative to the current system uptime. 1504 */ 1505 @GuardedBy("mLock") normalizeMitigationCalls()1506 public LongArrayQueue normalizeMitigationCalls() { 1507 LongArrayQueue normalized = new LongArrayQueue(); 1508 final long now = mSystemClock.uptimeMillis(); 1509 for (int i = 0; i < mMitigationCalls.size(); i++) { 1510 normalized.addLast(mMitigationCalls.get(i) - now); 1511 } 1512 return normalized; 1513 } 1514 1515 /** 1516 * Sets the initial health check duration. 1517 * 1518 * @return the new health check state 1519 */ 1520 @GuardedBy("mLock") setHealthCheckActiveLocked(long initialHealthCheckDurationMs)1521 public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) { 1522 if (initialHealthCheckDurationMs <= 0) { 1523 Slog.wtf(TAG, "Cannot set non-positive health check duration " 1524 + initialHealthCheckDurationMs + "ms for package " + getName() 1525 + ". Using total duration " + mDurationMs + "ms instead"); 1526 initialHealthCheckDurationMs = mDurationMs; 1527 } 1528 if (mHealthCheckState == HealthCheckState.INACTIVE) { 1529 // Transitions to ACTIVE 1530 mHealthCheckDurationMs = initialHealthCheckDurationMs; 1531 } 1532 return updateHealthCheckStateLocked(); 1533 } 1534 1535 /** 1536 * Updates the monitoring durations of the package. 1537 * 1538 * @return the new health check state 1539 */ 1540 @GuardedBy("mLock") handleElapsedTimeLocked(long elapsedMs)1541 public int handleElapsedTimeLocked(long elapsedMs) { 1542 if (elapsedMs <= 0) { 1543 Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName()); 1544 return mHealthCheckState; 1545 } 1546 // Transitions to FAILED if now <= 0 and health check not passed 1547 mDurationMs -= elapsedMs; 1548 if (mHealthCheckState == HealthCheckState.ACTIVE) { 1549 // We only update health check durations if we have #setHealthCheckActiveLocked 1550 // This ensures we don't leave the INACTIVE state for an unexpected elapsed time 1551 // Transitions to FAILED if now <= 0 and health check not passed 1552 mHealthCheckDurationMs -= elapsedMs; 1553 } 1554 return updateHealthCheckStateLocked(); 1555 } 1556 1557 /** Explicitly update the monitoring duration of the package. */ 1558 @GuardedBy("mLock") updateHealthCheckDuration(long newDurationMs)1559 public void updateHealthCheckDuration(long newDurationMs) { 1560 mDurationMs = newDurationMs; 1561 } 1562 1563 /** 1564 * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED} 1565 * if not yet {@link HealthCheckState.FAILED}. 1566 * 1567 * @return the new {@link HealthCheckState health check state} 1568 */ 1569 @GuardedBy("mLock") 1570 @HealthCheckState tryPassHealthCheckLocked()1571 public int tryPassHealthCheckLocked() { 1572 if (mHealthCheckState != HealthCheckState.FAILED) { 1573 // FAILED is a final state so only pass if we haven't failed 1574 // Transition to PASSED 1575 mHasPassedHealthCheck = true; 1576 } 1577 return updateHealthCheckStateLocked(); 1578 } 1579 1580 /** Returns the monitored package name. */ getName()1581 private String getName() { 1582 return mPackageName; 1583 } 1584 1585 /** 1586 * Returns the current {@link HealthCheckState health check state}. 1587 */ 1588 @GuardedBy("mLock") 1589 @HealthCheckState getHealthCheckStateLocked()1590 public int getHealthCheckStateLocked() { 1591 return mHealthCheckState; 1592 } 1593 1594 /** 1595 * Returns the shortest duration before the package should be scheduled for a prune. 1596 * 1597 * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled 1598 */ 1599 @GuardedBy("mLock") getShortestScheduleDurationMsLocked()1600 public long getShortestScheduleDurationMsLocked() { 1601 // Consider health check duration only if #isPendingHealthChecksLocked is true 1602 return Math.min(toPositive(mDurationMs), 1603 isPendingHealthChecksLocked() 1604 ? toPositive(mHealthCheckDurationMs) : Long.MAX_VALUE); 1605 } 1606 1607 /** 1608 * Returns {@code true} if the total duration left to monitor the package is less than or 1609 * equal to 0 {@code false} otherwise. 1610 */ 1611 @GuardedBy("mLock") isExpiredLocked()1612 public boolean isExpiredLocked() { 1613 return mDurationMs <= 0; 1614 } 1615 1616 /** 1617 * Returns {@code true} if the package, {@link #getName} is expecting health check results 1618 * {@code false} otherwise. 1619 */ 1620 @GuardedBy("mLock") isPendingHealthChecksLocked()1621 public boolean isPendingHealthChecksLocked() { 1622 return mHealthCheckState == HealthCheckState.ACTIVE 1623 || mHealthCheckState == HealthCheckState.INACTIVE; 1624 } 1625 1626 /** 1627 * Updates the health check state based on {@link #mHasPassedHealthCheck} 1628 * and {@link #mHealthCheckDurationMs}. 1629 * 1630 * @return the new {@link HealthCheckState health check state} 1631 */ 1632 @GuardedBy("mLock") 1633 @HealthCheckState updateHealthCheckStateLocked()1634 private int updateHealthCheckStateLocked() { 1635 int oldState = mHealthCheckState; 1636 if (mHasPassedHealthCheck) { 1637 // Set final state first to avoid ambiguity 1638 mHealthCheckState = HealthCheckState.PASSED; 1639 } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) { 1640 // Set final state first to avoid ambiguity 1641 mHealthCheckState = HealthCheckState.FAILED; 1642 } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { 1643 mHealthCheckState = HealthCheckState.INACTIVE; 1644 } else { 1645 mHealthCheckState = HealthCheckState.ACTIVE; 1646 } 1647 1648 if (oldState != mHealthCheckState) { 1649 Slog.i(TAG, "Updated health check state for package " + getName() + ": " 1650 + toString(oldState) + " -> " + toString(mHealthCheckState)); 1651 } 1652 return mHealthCheckState; 1653 } 1654 1655 /** Returns a {@link String} representation of the current health check state. */ toString(@ealthCheckState int state)1656 private String toString(@HealthCheckState int state) { 1657 switch (state) { 1658 case HealthCheckState.ACTIVE: 1659 return "ACTIVE"; 1660 case HealthCheckState.INACTIVE: 1661 return "INACTIVE"; 1662 case HealthCheckState.PASSED: 1663 return "PASSED"; 1664 case HealthCheckState.FAILED: 1665 return "FAILED"; 1666 default: 1667 return "UNKNOWN"; 1668 } 1669 } 1670 1671 /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */ toPositive(long value)1672 private long toPositive(long value) { 1673 return value > 0 ? value : Long.MAX_VALUE; 1674 } 1675 1676 /** Compares the equality of this object with another {@link MonitoredPackage}. */ 1677 @VisibleForTesting isEqualTo(MonitoredPackage pkg)1678 boolean isEqualTo(MonitoredPackage pkg) { 1679 return (getName().equals(pkg.getName())) 1680 && mDurationMs == pkg.mDurationMs 1681 && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck 1682 && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs 1683 && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString()); 1684 } 1685 } 1686 1687 /** 1688 * Handles the thresholding logic for system server boots. 1689 */ 1690 class BootThreshold { 1691 1692 private final int mBootTriggerCount; 1693 private final long mTriggerWindow; 1694 BootThreshold(int bootTriggerCount, long triggerWindow)1695 BootThreshold(int bootTriggerCount, long triggerWindow) { 1696 this.mBootTriggerCount = bootTriggerCount; 1697 this.mTriggerWindow = triggerWindow; 1698 } 1699 reset()1700 public void reset() { 1701 setStart(0); 1702 setCount(0); 1703 } 1704 getCount()1705 private int getCount() { 1706 return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0); 1707 } 1708 setCount(int count)1709 private void setCount(int count) { 1710 SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count)); 1711 } 1712 getStart()1713 public long getStart() { 1714 return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0); 1715 } 1716 getMitigationCount()1717 public int getMitigationCount() { 1718 return SystemProperties.getInt(PROP_BOOT_MITIGATION_COUNT, 0); 1719 } 1720 setStart(long start)1721 public void setStart(long start) { 1722 setPropertyStart(PROP_RESCUE_BOOT_START, start); 1723 } 1724 setMitigationStart(long start)1725 public void setMitigationStart(long start) { 1726 setPropertyStart(PROP_BOOT_MITIGATION_WINDOW_START, start); 1727 } 1728 getMitigationStart()1729 public long getMitigationStart() { 1730 return SystemProperties.getLong(PROP_BOOT_MITIGATION_WINDOW_START, 0); 1731 } 1732 setMitigationCount(int count)1733 public void setMitigationCount(int count) { 1734 SystemProperties.set(PROP_BOOT_MITIGATION_COUNT, Integer.toString(count)); 1735 } 1736 setPropertyStart(String property, long start)1737 public void setPropertyStart(String property, long start) { 1738 final long now = mSystemClock.uptimeMillis(); 1739 final long newStart = MathUtils.constrain(start, 0, now); 1740 SystemProperties.set(property, Long.toString(newStart)); 1741 } 1742 saveMitigationCountToMetadata()1743 public void saveMitigationCountToMetadata() { 1744 try (BufferedWriter writer = new BufferedWriter(new FileWriter(METADATA_FILE))) { 1745 writer.write(String.valueOf(getMitigationCount())); 1746 } catch (Exception e) { 1747 Slog.e(TAG, "Could not save metadata to file: " + e); 1748 } 1749 } 1750 readMitigationCountFromMetadataIfNecessary()1751 public void readMitigationCountFromMetadataIfNecessary() { 1752 File bootPropsFile = new File(METADATA_FILE); 1753 if (bootPropsFile.exists()) { 1754 try (BufferedReader reader = new BufferedReader(new FileReader(METADATA_FILE))) { 1755 String mitigationCount = reader.readLine(); 1756 setMitigationCount(Integer.parseInt(mitigationCount)); 1757 bootPropsFile.delete(); 1758 } catch (Exception e) { 1759 Slog.i(TAG, "Could not read metadata file: " + e); 1760 } 1761 } 1762 } 1763 1764 1765 /** Increments the boot counter, and returns whether the device is bootlooping. */ incrementAndTest()1766 public boolean incrementAndTest() { 1767 readMitigationCountFromMetadataIfNecessary(); 1768 final long now = mSystemClock.uptimeMillis(); 1769 if (now - getStart() < 0) { 1770 Slog.e(TAG, "Window was less than zero. Resetting start to current time."); 1771 setStart(now); 1772 setMitigationStart(now); 1773 } 1774 if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) { 1775 setMitigationCount(0); 1776 setMitigationStart(now); 1777 } 1778 final long window = now - getStart(); 1779 if (window >= mTriggerWindow) { 1780 setCount(1); 1781 setStart(now); 1782 return false; 1783 } else { 1784 int count = getCount() + 1; 1785 setCount(count); 1786 EventLogTags.writeRescueNote(Process.ROOT_UID, count, window); 1787 return count >= mBootTriggerCount; 1788 } 1789 } 1790 1791 } 1792 } 1793