diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java index b5cf011c32a6911ee05cdee6d3a09aece5fc3976..ce8fb6568bd509620ce0940ed39fe1039ffa1f91 100644 --- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java @@ -100,13 +100,15 @@ public class PackageWatchdog { public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; public static final int FAILURE_REASON_APP_CRASH = 3; public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; + public static final int FAILURE_REASON_BOOT_LOOP = 5; @IntDef(prefix = { "FAILURE_REASON_" }, value = { FAILURE_REASON_UNKNOWN, FAILURE_REASON_NATIVE_CRASH, FAILURE_REASON_EXPLICIT_HEALTH_CHECK, FAILURE_REASON_APP_CRASH, - FAILURE_REASON_APP_NOT_RESPONDING + FAILURE_REASON_APP_NOT_RESPONDING, + FAILURE_REASON_BOOT_LOOP }) @Retention(RetentionPolicy.SOURCE) public @interface FailureReasons {} @@ -542,7 +544,7 @@ public class PackageWatchdog { mNumberOfNativeCrashPollsRemaining--; // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { - // We rollback everything available when crash is unattributable + // We rollback all available low impact rollbacks when crash is unattributable onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not @@ -572,6 +574,7 @@ public class PackageWatchdog { PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) public @interface PackageHealthObserverImpact { /** No action to take. */ @@ -582,6 +585,7 @@ public class PackageWatchdog { int USER_IMPACT_LEVEL_30 = 30; int USER_IMPACT_LEVEL_50 = 50; int USER_IMPACT_LEVEL_70 = 70; + int USER_IMPACT_LEVEL_90 = 90; /* Action has high user impact, a last resort, user of a device will be very frustrated. */ int USER_IMPACT_LEVEL_100 = 100; } diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java index dd74a2a978b254b118e36a0ff5650de52b00325a..5fb47dd9b95adfc280d0f3370c37d340200c5263 100644 --- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -28,6 +28,7 @@ import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; +import android.crashrecovery.flags.Flags; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -45,7 +46,6 @@ import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; -import com.android.server.SystemConfig; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import com.android.server.pm.ApexManager; @@ -57,6 +57,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -84,7 +85,8 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { // True if needing to roll back only rebootless apexes when native crash happens private boolean mTwoPhaseRollbackEnabled; - RollbackPackageHealthObserver(Context context) { + @VisibleForTesting + RollbackPackageHealthObserver(Context context, ApexManager apexManager) { mContext = context; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); @@ -94,7 +96,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled"); PackageWatchdog.getInstance(mContext).registerHealthObserver(this); - mApexManager = ApexManager.getInstance(); + mApexManager = apexManager; if (SystemProperties.getBoolean("sys.boot_completed", false)) { // Load the value from the file if system server has crashed and restarted @@ -107,24 +109,46 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } } + RollbackPackageHealthObserver(Context context) { + this(context, ApexManager.getInstance()); + } + @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { - boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) - .getAvailableRollbacks().isEmpty(); int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; - - if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH - && anyRollbackAvailable) { - // For native crashes, we will directly roll back any available rollbacks - // Note: For non-native crashes the rollback-all step has higher impact - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; - } else if (getAvailableRollback(failedPackage) != null) { - // Rollback is available, we may get a callback into #execute - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; - } else if (anyRollbackAvailable) { - // If any rollbacks are available, we will commit them - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (!lowImpactRollbacks.isEmpty()) { + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + // For native crashes, we will directly roll back any available rollbacks at low + // impact level + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getRollbackForPackage(failedPackage, lowImpactRollbacks) != null) { + // Rollback is available for crashing low impact package + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else { + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } + } + } else { + boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) + .getAvailableRollbacks().isEmpty(); + + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH + && anyRollbackAvailable) { + // For native crashes, we will directly roll back any available rollbacks + // Note: For non-native crashes the rollback-all step has higher impact + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getAvailableRollback(failedPackage) != null) { + // Rollback is available, we may get a callback into #execute + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (anyRollbackAvailable) { + // If any rollbacks are available, we will commit them + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } } return impact; @@ -133,22 +157,65 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @Override public boolean execute(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason, int mitigationCount) { - if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { - mHandler.post(() -> rollbackAll(rollbackReason)); - return true; - } + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + return true; + } - RollbackInfo rollback = getAvailableRollback(failedPackage); - if (rollback != null) { - mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackInfo rollback = getRollbackForPackage(failedPackage, lowImpactRollbacks); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else if (!lowImpactRollbacks.isEmpty()) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } } else { - mHandler.post(() -> rollbackAll(rollbackReason)); + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAll(rollbackReason)); + return true; + } + + RollbackInfo rollback = getAvailableRollback(failedPackage); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else { + mHandler.post(() -> rollbackAll(rollbackReason)); + } } // Assume rollbacks executed successfully return true; } + @Override + public int onBootLoop(int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (!availableRollbacks.isEmpty()) { + impact = getUserImpactBasedOnRollbackImpactLevel(availableRollbacks); + } + } + return impact; + } + + @Override + public boolean executeBootLoopMitigation(int mitigationCount) { + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + + triggerLeastImpactLevelRollback(availableRollbacks, + PackageWatchdog.FAILURE_REASON_BOOT_LOOP); + return true; + } + return false; + } + + @Override public String getName() { return NAME; @@ -161,13 +228,16 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @Override public boolean mayObservePackage(String packageName) { - if (mContext.getSystemService(RollbackManager.class) - .getAvailableRollbacks().isEmpty()) { + if (getAvailableRollbacks().isEmpty()) { return false; } return isPersistentSystemApp(packageName); } + private List<RollbackInfo> getAvailableRollbacks() { + return mContext.getSystemService(RollbackManager.class).getAvailableRollbacks(); + } + private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { @@ -272,6 +342,40 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { return null; } + @AnyThread + private RollbackInfo getRollbackForPackage(@Nullable VersionedPackage failedPackage, + List<RollbackInfo> availableRollbacks) { + if (failedPackage == null) { + return null; + } + + for (RollbackInfo rollback : availableRollbacks) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { + return rollback; + } + // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have + // to rely on complicated reasoning as below + + // Due to b/147666157, for apk in apex, we do not know the version we are rolling + // back from. But if a package X is embedded in apex A exclusively (not embedded in + // any other apex), which is not guaranteed, then it is sufficient to check only + // package names here, as the version of failedPackage and the PackageRollbackInfo + // can't be different. If failedPackage has a higher version, then it must have + // been updated somehow. There are two ways: it was updated by an update of apex A + // or updated directly as apk. In both cases, this rollback would have gotten + // expired when onPackageReplaced() was called. Since the rollback exists, it has + // same version as failedPackage. + if (packageRollback.isApkInApex() + && packageRollback.getVersionRolledBackFrom().getPackageName() + .equals(failedPackage.getPackageName())) { + return rollback; + } + } + } + return null; + } + /** * Returns {@code true} if staged session associated with {@code rollbackId} was marked * as handled, {@code false} if already handled. @@ -396,12 +500,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @FailureReasons int rollbackReason) { assertInWorkerThread(); - if (isAutomaticRollbackDenied(SystemConfig.getInstance(), failedPackage)) { - Slog.d(TAG, "Automatic rollback not allowed for package " - + failedPackage.getPackageName()); - return; - } - final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); final String failedPackageToLog; @@ -464,17 +562,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender()); } - /** - * Returns true if this package is not eligible for automatic rollback. - */ - @VisibleForTesting - @AnyThread - public static boolean isAutomaticRollbackDenied(SystemConfig systemConfig, - VersionedPackage versionedPackage) { - return systemConfig.getAutomaticRollbackDenylistedPackages() - .contains(versionedPackage.getPackageName()); - } - /** * Two-phase rollback: * 1. roll back rebootless apexes first @@ -495,14 +582,62 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { boolean found = false; for (RollbackInfo rollback : rollbacks) { if (isRebootlessApex(rollback)) { - VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); - rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); found = true; } } return found; } + /** + * Rollback the package that has minimum rollback impact level. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + private void triggerLeastImpactLevelRollback(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + int minRollbackImpactLevel = getMinRollbackImpactLevel(availableRollbacks); + + if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_LOW) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) { + // Rollback one package at a time. If that doesn't resolve the issue, rollback + // next with same impact level. + mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason)); + } + } + + /** + * sort the available high impact rollbacks by first package name to have a deterministic order. + * Apply the first available rollback. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + @WorkerThread + private void rollbackHighImpact(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + assertInWorkerThread(); + List<RollbackInfo> highImpactRollbacks = + getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_HIGH); + + // sort rollbacks based on package name of the first package. This is to have a + // deterministic order of rollbacks. + List<RollbackInfo> sortedHighImpactRollbacks = highImpactRollbacks.stream().sorted( + Comparator.comparing(a -> a.getPackages().get(0).getPackageName())).toList(); + VersionedPackage firstRollback = + sortedHighImpactRollbacks + .get(0) + .getPackages() + .get(0) + .getVersionRolledBackFrom(); + rollbackPackage(sortedHighImpactRollbacks.get(0), firstRollback, rollbackReason); + } + @WorkerThread private void rollbackAll(@FailureReasons int rollbackReason) { assertInWorkerThread(); @@ -522,8 +657,77 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } for (RollbackInfo rollback : rollbacks) { - VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); - rollbackPackage(rollback, sample, rollbackReason); + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); } } + + /** + * Rollback all available low impact rollbacks + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollbacks + */ + @WorkerThread + private void rollbackAllLowImpact( + List<RollbackInfo> availableRollbacks, @FailureReasons int rollbackReason) { + assertInWorkerThread(); + + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (useTwoPhaseRollback(lowImpactRollbacks)) { + return; + } + + Slog.i(TAG, "Rolling back all available low impact rollbacks"); + // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all + // pending staged rollbacks are handled. + for (RollbackInfo rollback : lowImpactRollbacks) { + if (rollback.isStaged()) { + mPendingStagedRollbackIds.add(rollback.getRollbackId()); + } + } + + for (RollbackInfo rollback : lowImpactRollbacks) { + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); + } + } + + private List<RollbackInfo> getRollbacksAvailableForImpactLevel( + List<RollbackInfo> availableRollbacks, int impactLevel) { + return availableRollbacks.stream() + .filter(rollbackInfo -> rollbackInfo.getRollbackImpactLevel() == impactLevel) + .toList(); + } + + private int getMinRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + return availableRollbacks.stream() + .mapToInt(RollbackInfo::getRollbackImpactLevel) + .min() + .orElse(-1); + } + + private int getUserImpactBasedOnRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + int minImpact = getMinRollbackImpactLevel(availableRollbacks); + switch (minImpact) { + case PackageManager.ROLLBACK_USER_IMPACT_LOW: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + break; + case PackageManager.ROLLBACK_USER_IMPACT_HIGH: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90; + break; + default: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + return impact; + } + + @VisibleForTesting + Handler getHandler() { + return mHandler; + } } diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java index 898c5439a293f05aa4d2c10c29aef776d33a09e8..519c0edfc5321de074cd7fc142ffd0c60cda317f 100644 --- a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java +++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java @@ -18,6 +18,7 @@ package com.android.server.rollback; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT; @@ -258,6 +259,8 @@ public final class WatchdogRollbackLogger { return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; + case PackageWatchdog.FAILURE_REASON_BOOT_LOOP: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; default: return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index b04c7c52efbd9c2784c08ba9f8eee3ece22f742f..31db840dac00d5f602659b8068bb5e14fac2f443 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -326,7 +326,6 @@ public class SystemConfig { private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>(); private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>(); - private final ArraySet<String> mAutomaticRollbackDenylistedPackages = new ArraySet<>(); private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>(); // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. @@ -475,10 +474,6 @@ public class SystemConfig { return mRollbackWhitelistedPackages; } - public Set<String> getAutomaticRollbackDenylistedPackages() { - return mAutomaticRollbackDenylistedPackages; - } - public Set<String> getWhitelistedStagedInstallers() { return mWhitelistedStagedInstallers; } @@ -1396,16 +1391,6 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; - case "automatic-rollback-denylisted-app": { - String pkgname = parser.getAttributeValue(null, "package"); - if (pkgname == null) { - Slog.w(TAG, "<" + name + "> without package in " + permFile - + " at " + parser.getPositionDescription()); - } else { - mAutomaticRollbackDenylistedPackages.add(pkgname); - } - XmlUtils.skipCurrentTag(parser); - } break; case "whitelisted-staged-installer": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index d96fc332ce5a36f91e8e3174908f1035a7125ea4..e5cdf4577408038c7b486622ea21a2128f85d19c 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -1212,13 +1212,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba rollback.makeAvailable(); mPackageHealthObserver.notifyRollbackAvailable(rollback.info); - // TODO(zezeozue): Provide API to explicitly start observing instead - // of doing this for all rollbacks. If we do this for all rollbacks, - // should document in PackageInstaller.SessionParams#setEnableRollback - // After enabling and committing any rollback, observe packages and - // prepare to rollback if packages crashes too frequently. - mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), - mRollbackLifetimeDurationInMillis); + if (Flags.recoverabilityDetection()) { + if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) { + // TODO(zezeozue): Provide API to explicitly start observing instead + // of doing this for all rollbacks. If we do this for all rollbacks, + // should document in PackageInstaller.SessionParams#setEnableRollback + // After enabling and committing any rollback, observe packages and + // prepare to rollback if packages crashes too frequently. + mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), + mRollbackLifetimeDurationInMillis); + } + } else { + mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), + mRollbackLifetimeDurationInMillis); + } runExpiration(); } diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java index a14073006c3185c3aefe6458ffbad4fe1997914d..d6e246fc7ee12c7b0b612c07278c4224ac7dd2a9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java @@ -23,7 +23,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -33,14 +39,17 @@ import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; -import android.util.Log; -import android.util.Xml; +import android.crashrecovery.flags.Flags; +import android.os.Handler; +import android.os.MessageQueue; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.PackageWatchdog; import com.android.server.SystemConfig; +import com.android.server.pm.ApexManager; import org.junit.After; import org.junit.Before; @@ -49,18 +58,16 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; -import org.xmlpull.v1.XmlPullParser; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; +import java.time.Duration; import java.util.List; -import java.util.Scanner; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @@ -78,10 +85,18 @@ public class RollbackPackageHealthObserverTest { @Mock PackageManager mMockPackageManager; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private ApexManager mApexManager; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private MockitoSession mSession; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; + private static final String APP_C = "com.package.c"; private static final long VERSION_CODE = 1L; + private static final long VERSION_CODE_2 = 2L; private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; private SystemConfig mSysConfig; @@ -101,7 +116,6 @@ public class RollbackPackageHealthObserverTest { // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); - } @After @@ -121,7 +135,7 @@ public class RollbackPackageHealthObserverTest { @Test public void testHealthCheckLevels() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE); VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); @@ -165,14 +179,14 @@ public class RollbackPackageHealthObserverTest { @Test public void testIsPersistent() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); assertTrue(observer.isPersistent()); } @Test public void testMayObservePackage_withoutAnyRollback() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of()); assertFalse(observer.mayObservePackage(APP_A)); @@ -182,7 +196,7 @@ public class RollbackPackageHealthObserverTest { public void testMayObservePackage_forPersistentApp() throws PackageManager.NameNotFoundException { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); ApplicationInfo info = new ApplicationInfo(); info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); @@ -197,7 +211,7 @@ public class RollbackPackageHealthObserverTest { public void testMayObservePackage_forNonPersistentApp() throws PackageManager.NameNotFoundException { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); @@ -208,96 +222,720 @@ public class RollbackPackageHealthObserverTest { } /** - * Test that isAutomaticRollbackDenied works correctly when packages that are not - * denied are sent. + * Test that when impactLevel is low returns user impact level 70 */ @Test - public void isRollbackAllowedTest_false() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.test.package", 1))).isEqualTo(false); + public void healthCheckFailed_impactLevelLow_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Test that isAutomaticRollbackDenied works correctly when packages that are - * denied are sent. + * HealthCheckFailed should only return low impact rollbacks. High impact rollbacks are only + * for bootloop. */ @Test - public void isRollbackAllowedTest_true() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(true); + public void healthCheckFailed_impactLevelHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Test that isAutomaticRollbackDenied works correctly when no config is present + * When the rollback impact level is manual only return user impact level 0. (User impact level + * 0 is ignored by package watchdog) */ @Test - public void isRollbackAllowedTest_noConfig() throws IOException { - final File folder = createTempSubfolder("folder"); + public void healthCheckFailed_impactLevelManualOnly_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); - readPermissions(folder, /* Grant all permission flags */ ~0); + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(false); + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. - * - * @param folder pre-existing subdirectory of mTemporaryFolder to put the file - * @param fileName name of the file (e.g. filename.xml) to create - * @param contents contents to write to the file - * @return the newly created file + * When both low impact and high impact are present, return 70. */ - private File createTempFile(File folder, String fileName, String contents) - throws IOException { - File file = new File(folder, fileName); - BufferedWriter bw = new BufferedWriter(new FileWriter(file)); - bw.write(contents); - bw.close(); - - // Print to logcat for test debugging. - Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath()); - Scanner input = new Scanner(file); - while (input.hasNextLine()) { - Log.d(LOG_TAG, input.nextLine()); - } + @Test + public void healthCheckFailed_impactLevelLowAndHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null, false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onHealthCheckFailed(failedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); + } + + /** + * When low impact rollback is available roll it back. + */ + @Test + public void execute_impactLevelLow_nativeCrash_rollback() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager).getAvailableRollbacks(); + verify(mRollbackManager).commitRollback(eq(rollbackId), any(), any()); + } + + /** + * Rollback the failing package if rollback is available for it + */ + @Test + public void execute_impactLevelLow_rollbackFailedPackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager).commitRollback(argument.capture(), any(), any()); + // Rollback package App B as the failing package is B + assertThat(argument.getValue()).isEqualTo(rollbackId2); + } + + /** + * Rollback all available rollbacks if the rollback is not available for failing package. + */ + @Test + public void execute_impactLevelLow_rollbackAll() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(2)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); + } + + /** + * rollback low impact package if both low and high impact packages are available + */ + @Test + public void execute_impactLevelLowAndHigh_rollbackLow() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Don't roll back high impact package if only high impact package is available. high impact + * rollback to be rolled back only on bootloop. + */ + @Test + public void execute_impactLevelHigh_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); + + } + + /** + * Test that when impactLevel is low returns user impact level 70 + */ + @Test + public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onBootLoop(1)); + } + + @Test + public void onBootLoop_impactLevelHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); - return file; + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, + observer.onBootLoop(1)); } - private void readPermissions(File libraryDir, int permissionFlag) { - final XmlPullParser parser = Xml.newPullParser(); - mSysConfig.readPermissions(parser, libraryDir, permissionFlag); + /** + * When the rollback impact level is manual only return user impact level 0. (User impact level + * 0 is ignored by package watchdog) + */ + @Test + public void onBootLoop_impactLevelManualOnly_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onBootLoop(1)); } /** - * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. - * - * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed - * @return the folder + * When both low impact and high impact are present, return 70. */ - private File createTempSubfolder(String folderName) - throws IOException { - File folder = new File(mTemporaryFolder.getRoot(), folderName); - folder.mkdirs(); - return folder; + @Test + public void onBootLoop_impactLevelLowAndHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null, false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onBootLoop(1)); + } + + /** + * Rollback all available rollbacks if the rollback is not available for failing package. + */ + @Test + public void executeBootLoopMitigation_impactLevelLow_rollbackAll() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(2)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); + } + + /** + * rollback low impact package if both low and high impact packages are available + */ + @Test + public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Rollback high impact package if only high impact package is available + */ + @Test + public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback high impact packages when no other rollback available + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); + } + + /** + * Rollback only low impact available rollbacks if both low and manual only are available. + */ + @Test + public void execute_impactLevelLowAndManual_rollbackLowImpactOnly() + throws PackageManager.NameNotFoundException, InterruptedException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Do not roll back if only manual rollback is available. + */ + @Test + public void execute_impactLevelManual_rollbackLowImpactOnly() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); + } + + /** + * Rollback alphabetically first package if multiple high impact rollbacks are available. + */ + @Test + public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + int rollbackId2 = 2; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback APP_A because it is first alphabetically + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); + } + + private void waitForIdleHandler(Handler handler, Duration timeout) { + final MessageQueue queue = handler.getLooper().getQueue(); + final CountDownLatch latch = new CountDownLatch(1); + queue.addIdleHandler(() -> { + latch.countDown(); + // Remove idle handler + return false; + }); + try { + latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + fail("Interrupted unexpectedly: " + e); + } } } diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index aca96ad20385372a1662360c7c1eb041c217e566..d073f5bfebe47a64a05dab721185285db8bc6301 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -594,56 +594,6 @@ public class SystemConfigTest { assertFooIsOnlySharedLibrary(); } - /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app}. - */ - @Test - public void automaticRollbackDeny_vending() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()) - .containsExactly("com.android.vending"); - } - - /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app} without any packages. - */ - @Test - public void automaticRollbackDeny_empty() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty(); - } - - /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app} without the corresponding config. - */ - @Test - public void automaticRollbackDeny_noConfig() throws IOException { - final File folder = createTempSubfolder("folder"); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty(); - } - /** * Tests that readPermissions works correctly for the tag: {@code update-ownership}. */