diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index ec40971c38ee2..0d17c13fcae1b 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -76,6 +76,7 @@ public class PackageWatchdog { private static final String ATTR_VERSION = "version"; private static final String ATTR_NAME = "name"; private static final String ATTR_DURATION = "duration"; + private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; private static PackageWatchdog sPackageWatchdog; @@ -102,7 +103,7 @@ public class PackageWatchdog { // 0 if mPackageCleanup not running. private long mDurationAtLastReschedule; - // TODO(zezeozue): Remove redundant context param + // TODO(b/120598832): Remove redundant context param private PackageWatchdog(Context context) { mContext = context; mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"), @@ -161,21 +162,29 @@ public class PackageWatchdog { * Starts observing the health of the {@code packages} for {@code observer} and notifies * {@code observer} of any package failures within the monitoring duration. * + *
If monitoring a package with {@code withExplicitHealthCheck}, at the end of the monitoring + * duration if {@link #onExplicitHealthCheckPassed} was never called, + * {@link PackageHealthObserver#execute} will be called as if the package failed. + * *
If {@code observer} is already monitoring a package in {@code packageNames},
- * the monitoring window of that package will be reset to {@code durationMs}.
+ * the monitoring window of that package will be reset to {@code durationMs} and the health
+ * check state will be reset to a default depending on {@code withExplictHealthCheck}.
*
* @throws IllegalArgumentException if {@code packageNames} is empty
* or {@code durationMs} is less than 1
*/
public void startObservingHealth(PackageHealthObserver observer, List This update is strictly for registered observers at the time of the call
+ * Observers that register after this signal will have no knowledge of prior signals and will
+ * effectively behave as if the explicit health check hasn't passed for {@code packageName}.
+ *
+ * {@code packageName} can still be considered failed if reported by
+ * {@link #onPackageFailure} before the package expires.
+ *
+ * Triggered by components outside the system server when they are fully functional after an
+ * update.
+ */
+ public void onExplicitHealthCheckPassed(String packageName) {
+ Slog.i(TAG, "Health check passed for package: " + packageName);
+ boolean shouldUpdateFile = false;
+ synchronized (mLock) {
+ for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
+ ObserverInternal observer = mAllObservers.valueAt(observerIdx);
+ MonitoredPackage monitoredPackage = observer.mPackages.get(packageName);
+ if (monitoredPackage != null && !monitoredPackage.mHasPassedHealthCheck) {
+ monitoredPackage.mHasPassedHealthCheck = true;
+ shouldUpdateFile = true;
+ }
+ }
+ }
+ if (shouldUpdateFile) {
+ saveToFileAsync();
+ }
+ }
+
+ // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file?
// This currently adds about 7ms extra to shutdown thread
/** Writes the package information to file during shutdown. */
public void writeNow() {
@@ -322,7 +362,7 @@ public class PackageWatchdog {
*/
boolean execute(VersionedPackage versionedPackage);
- // TODO(zezeozue): Ensure uniqueness?
+ // TODO(b/120598832): Ensure uniqueness?
/**
* Identifier for the observer, should not change across device updates otherwise the
* watchdog may drop observing packages with the old name.
@@ -505,6 +545,8 @@ public class PackageWatchdog {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATTR_NAME, p.mName);
out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs));
+ out.attribute(null, ATTR_PASSED_HEALTH_CHECK,
+ String.valueOf(p.mHasPassedHealthCheck));
out.endTag(null, TAG_PACKAGE);
}
out.endTag(null, TAG_OBSERVER);
@@ -524,6 +566,8 @@ public class PackageWatchdog {
}
}
+ // TODO(b/120598832): For packages failing explicit health check, delay removal of the
+ // observer until after PackageHealthObserver#execute
/**
* Reduces the monitoring durations of all packages observed by this observer by
* {@code elapsedMs}. If any duration is less than 0, the package is removed from
@@ -574,6 +618,7 @@ public class PackageWatchdog {
if (TAG_OBSERVER.equals(parser.getName())) {
observerName = parser.getAttributeValue(null, ATTR_NAME);
if (TextUtils.isEmpty(observerName)) {
+ Slog.wtf(TAG, "Unable to read observer name");
return null;
}
}
@@ -582,17 +627,24 @@ public class PackageWatchdog {
try {
while (XmlUtils.nextElementWithin(parser, innerDepth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
- String packageName = parser.getAttributeValue(null, ATTR_NAME);
- long duration = Long.parseLong(
- parser.getAttributeValue(null, ATTR_DURATION));
- if (!TextUtils.isEmpty(packageName)) {
- packages.add(new MonitoredPackage(packageName, duration));
+ try {
+ String packageName = parser.getAttributeValue(null, ATTR_NAME);
+ long duration = Long.parseLong(
+ parser.getAttributeValue(null, ATTR_DURATION));
+ boolean hasPassedHealthCheck = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK));
+ if (!TextUtils.isEmpty(packageName)) {
+ packages.add(new MonitoredPackage(packageName, duration,
+ hasPassedHealthCheck));
+ }
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Skipping package for observer " + observerName, e);
+ continue;
}
}
}
- } catch (IOException e) {
- return null;
- } catch (XmlPullParserException e) {
+ } catch (XmlPullParserException | IOException e) {
+ Slog.wtf(TAG, "Unable to read observer " + observerName, e);
return null;
}
if (packages.isEmpty()) {
@@ -605,6 +657,8 @@ public class PackageWatchdog {
/** Represents a package along with the time it should be monitored for. */
static class MonitoredPackage {
public final String mName;
+ // Whether an explicit health check has passed
+ public boolean mHasPassedHealthCheck;
// System uptime duration to monitor package
public long mDurationMs;
// System uptime of first package failure
@@ -612,9 +666,10 @@ public class PackageWatchdog {
// Number of failures since mUptimeStartMs
private int mFailures;
- MonitoredPackage(String name, long durationMs) {
+ MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) {
mName = name;
mDurationMs = durationMs;
+ mHasPassedHealthCheck = hasPassedHealthCheck;
}
/**
@@ -626,7 +681,7 @@ public class PackageWatchdog {
final long now = SystemClock.uptimeMillis();
final long duration = now - mUptimeStartMs;
if (duration > TRIGGER_DURATION_MS) {
- // TODO(zezeozue): Reseting to 1 is not correct
+ // TODO(b/120598832): Reseting to 1 is not correct
// because there may be more than 1 failure in the last trigger window from now
// This is the RescueParty impl, will leave for now
mFailures = 1;
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index d24f21781b398..9beeff2f59e02 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -149,7 +149,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
* This may cause {@code packages} to be rolled back if they crash too freqeuntly.
*/
public void startObservingHealth(List