Loading services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +67 −20 Original line number Diff line number Diff line Loading @@ -18,14 +18,18 @@ package com.android.server.rollback; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; import android.os.HandlerThread; import android.text.TextUtils; import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; Loading Loading @@ -54,9 +58,15 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public int onHealthCheckFailed(String packageName, long versionCode) { VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); if (moduleMetadataPackage == null) { // Ignore failure, no mainline update available return PackageHealthObserverImpact.USER_IMPACT_NONE; } RollbackInfo rollback = getAvailableRollback(mContext.getSystemService(RollbackManager.class), packageName, versionCode); getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class), packageName, versionCode, moduleMetadataPackage); if (rollback == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; Loading @@ -67,27 +77,41 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public boolean execute(String packageName, long versionCode) { VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); if (moduleMetadataPackage == null) { // Ignore failure, no mainline update available return false; } RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); RollbackInfo rollback = getAvailableRollback(rollbackManager, packageName, versionCode); RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager, packageName, versionCode, moduleMetadataPackage); if (rollback == null) { // Expected a rollback to be available, what happened? Slog.w(TAG, "Expected rollback but no rollback found for package: [ " + packageName + "] with versionCode: [" + versionCode + "]"); return false; } // TODO(zezeozue): Only rollback if rollback version == failed package version StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { // TODO(zezeozue); Log success metrics // Rolledback successfully, no action required by other observers int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); if (status == RollbackManager.STATUS_SUCCESS) { StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); } else { // TODO(zezeozue); Log failure metrics // Rollback failed other observers should have a shot StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); } }); // TODO(zezeozue): Log initiated metrics mHandler.post(() -> rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(new VersionedPackage(packageName, versionCode)), Loading @@ -109,17 +133,40 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } private RollbackInfo getAvailableRollback(RollbackManager rollbackManager, String packageName, long versionCode) { private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager, String packageName, long versionCode, VersionedPackage moduleMetadataPackage) { for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { // We only rollback mainline packages, so check if rollback contains the // module metadata provider, if it does, the rollback is a mainline rollback boolean hasModuleMetadataPackage = false; boolean hasFailedPackage = false; for (PackageRollbackInfo packageRollback : rollback.getPackages()) { if (packageName.equals(packageRollback.getPackageName()) hasModuleMetadataPackage |= packageRollback.getPackageName().equals( moduleMetadataPackage.getPackageName()); hasFailedPackage |= packageRollback.getPackageName().equals(packageName) && packageRollback.getVersionRolledBackFrom().getVersionCode() == versionCode) { == versionCode; } if (hasModuleMetadataPackage && hasFailedPackage) { return rollback; } } return null; } private VersionedPackage getModuleMetadataPackage() { String packageName = mContext.getResources().getString( R.string.config_defaultModuleMetadataProvider); if (!TextUtils.isEmpty(packageName)) { return null; } try { return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo( packageName, 0 /* flags */).getLongVersionCode()); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Module metadata provider not found"); return null; } } } tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading Loading @@ -628,9 +629,12 @@ public class RollbackTest { assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode()); } // TODO: Allow installing test app along atomically with module metadata package so that // a failed test app will be flagged as a failed mainline app /** * Test bad update automatic rollback. */ @Ignore @Test public void testBadUpdateRollback() throws Exception { BroadcastReceiver crashCountReceiver = null; Loading Loading
services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +67 −20 Original line number Diff line number Diff line Loading @@ -18,14 +18,18 @@ package com.android.server.rollback; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; import android.os.HandlerThread; import android.text.TextUtils; import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; Loading Loading @@ -54,9 +58,15 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public int onHealthCheckFailed(String packageName, long versionCode) { VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); if (moduleMetadataPackage == null) { // Ignore failure, no mainline update available return PackageHealthObserverImpact.USER_IMPACT_NONE; } RollbackInfo rollback = getAvailableRollback(mContext.getSystemService(RollbackManager.class), packageName, versionCode); getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class), packageName, versionCode, moduleMetadataPackage); if (rollback == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; Loading @@ -67,27 +77,41 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public boolean execute(String packageName, long versionCode) { VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); if (moduleMetadataPackage == null) { // Ignore failure, no mainline update available return false; } RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); RollbackInfo rollback = getAvailableRollback(rollbackManager, packageName, versionCode); RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager, packageName, versionCode, moduleMetadataPackage); if (rollback == null) { // Expected a rollback to be available, what happened? Slog.w(TAG, "Expected rollback but no rollback found for package: [ " + packageName + "] with versionCode: [" + versionCode + "]"); return false; } // TODO(zezeozue): Only rollback if rollback version == failed package version StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { // TODO(zezeozue); Log success metrics // Rolledback successfully, no action required by other observers int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); if (status == RollbackManager.STATUS_SUCCESS) { StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); } else { // TODO(zezeozue); Log failure metrics // Rollback failed other observers should have a shot StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); } }); // TODO(zezeozue): Log initiated metrics mHandler.post(() -> rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(new VersionedPackage(packageName, versionCode)), Loading @@ -109,17 +133,40 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } private RollbackInfo getAvailableRollback(RollbackManager rollbackManager, String packageName, long versionCode) { private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager, String packageName, long versionCode, VersionedPackage moduleMetadataPackage) { for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { // We only rollback mainline packages, so check if rollback contains the // module metadata provider, if it does, the rollback is a mainline rollback boolean hasModuleMetadataPackage = false; boolean hasFailedPackage = false; for (PackageRollbackInfo packageRollback : rollback.getPackages()) { if (packageName.equals(packageRollback.getPackageName()) hasModuleMetadataPackage |= packageRollback.getPackageName().equals( moduleMetadataPackage.getPackageName()); hasFailedPackage |= packageRollback.getPackageName().equals(packageName) && packageRollback.getVersionRolledBackFrom().getVersionCode() == versionCode) { == versionCode; } if (hasModuleMetadataPackage && hasFailedPackage) { return rollback; } } return null; } private VersionedPackage getModuleMetadataPackage() { String packageName = mContext.getResources().getString( R.string.config_defaultModuleMetadataProvider); if (!TextUtils.isEmpty(packageName)) { return null; } try { return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo( packageName, 0 /* flags */).getLongVersionCode()); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Module metadata provider not found"); return null; } } }
tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading Loading @@ -628,9 +629,12 @@ public class RollbackTest { assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode()); } // TODO: Allow installing test app along atomically with module metadata package so that // a failed test app will be flagged as a failed mainline app /** * Test bad update automatic rollback. */ @Ignore @Test public void testBadUpdateRollback() throws Exception { BroadcastReceiver crashCountReceiver = null; Loading