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