diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 84577f125f1d8..4507193a5aff6 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.VersionedPackage; import android.os.Environment; import android.os.Handler; import android.os.Looper; @@ -230,7 +231,6 @@ public class PackageWatchdog { return null; } - // TODO(zezeozue:) Accept current versionCodes of failing packages? /** * Called when a process fails either due to a crash or ANR. * @@ -239,15 +239,16 @@ public class PackageWatchdog { * *

This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(String[] packages) { + public void onPackageFailure(List packages) { mWorkerHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { return; } - for (int pIndex = 0; pIndex < packages.length; pIndex++) { - String packageToReport = packages[pIndex]; + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + String packageToReport = packages.get(pIndex).getPackageName(); + long packageVersionCode = packages.get(pIndex).getVersionCode(); // Observer that will receive failure for packageToReport PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; @@ -258,7 +259,8 @@ public class PackageWatchdog { PackageHealthObserver registeredObserver = observer.mRegisteredObserver; if (registeredObserver != null && observer.onPackageFailure(packageToReport)) { - int impact = registeredObserver.onHealthCheckFailed(packageToReport); + int impact = registeredObserver.onHealthCheckFailed(packageToReport, + packageVersionCode); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; @@ -269,7 +271,7 @@ public class PackageWatchdog { // Execute action with least user impact if (currentObserverToNotify != null) { - currentObserverToNotify.execute(packageToReport); + currentObserverToNotify.execute(packageToReport, packageVersionCode); } } } @@ -313,14 +315,14 @@ public class PackageWatchdog { * @return any one of {@link PackageHealthObserverImpact} to express the impact * to the user on {@link #execute} */ - @PackageHealthObserverImpact int onHealthCheckFailed(String packageName); + @PackageHealthObserverImpact int onHealthCheckFailed(String packageName, long versionCdoe); /** * Executes mitigation for {@link #onHealthCheckFailed}. * * @return {@code true} if action was executed successfully, {@code false} otherwise */ - boolean execute(String packageName); + boolean execute(String packageName, long versionCode); // TODO(zezeozue): Ensure uniqueness? /** diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index a634b577f506b..f153ab9cfa9ed 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -34,6 +34,7 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.VersionedPackage; import android.net.Uri; import android.os.Binder; import android.os.Message; @@ -60,6 +61,7 @@ import com.android.server.wm.WindowProcessController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; +import java.util.List; /** * Controls error conditions in applications. @@ -411,7 +413,7 @@ class AppErrors { } else { // If a non-persistent app is stuck in crash loop, we want to inform // the package watchdog, maybe an update or experiment can be rolled back. - mPackageWatchdog.onPackageFailure(r.getPackageList()); + mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode()); } } @@ -830,7 +832,7 @@ class AppErrors { void handleShowAnrUi(Message msg) { Dialog dialogToShow = null; - String[] packageList = null; + List packageList = null; synchronized (mService) { AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj; final ProcessRecord proc = data.proc; @@ -839,7 +841,7 @@ class AppErrors { return; } if (!proc.isPersistent()) { - packageList = proc.getPackageList(); + packageList = proc.getPackageListWithVersionCode(); } if (proc.anrDialog != null) { Slog.e(TAG, "App already has anr dialog: " + proc); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 580d6882a82e7..7ae77d5f09dd8 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -32,6 +32,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; +import android.content.pm.VersionedPackage; import android.content.res.CompatibilityInfo; import android.os.Binder; import android.os.Debug; @@ -66,6 +67,7 @@ import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Full information about a particular process that @@ -974,6 +976,18 @@ final class ProcessRecord implements WindowProcessListener { return list; } + public List getPackageListWithVersionCode() { + int size = pkgList.size(); + if (size == 0) { + return null; + } + List list = new ArrayList<>(); + for (int i = 0; i < pkgList.size(); i++) { + list.add(new VersionedPackage(pkgList.keyAt(i), pkgList.valueAt(i).appVersion)); + } + return list; + } + WindowProcessController getWindowProcessController() { return mWindowProcessController; } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 28801038ebd10..f50e776ad22dd 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -54,8 +54,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public int onHealthCheckFailed(String packageName) { - RollbackInfo rollback = getAvailableRollback(packageName); + public int onHealthCheckFailed(String packageName, long versionCode) { + RollbackInfo rollback = getAvailableRollback(packageName, versionCode); if (rollback == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; @@ -65,8 +65,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean execute(String packageName) { - RollbackInfo rollback = getAvailableRollback(packageName); + public boolean execute(String packageName, long versionCode) { + RollbackInfo rollback = getAvailableRollback(packageName, versionCode); if (rollback == null) { // Expected a rollback to be available, what happened? return false; @@ -110,11 +110,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } - private RollbackInfo getAvailableRollback(String packageName) { + private RollbackInfo getAvailableRollback(String packageName, long versionCode) { for (RollbackInfo rollback : mRollbackManager.getAvailableRollbacks()) { for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - if (packageName.equals(packageRollback.getPackageName())) { - // TODO(zezeozue): Only rollback if rollback version == failed package version + if (packageName.equals(packageRollback.getPackageName()) + && packageRollback.getVersionRolledBackFrom().getVersionCode() + == versionCode) { return rollback; } } diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 86af6422dad34..c1c598d52676e 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.content.pm.VersionedPackage; import android.os.test.TestLooper; import android.support.test.InstrumentationRegistry; @@ -47,6 +48,7 @@ public class PackageWatchdogTest { private static final String APP_B = "com.package.b"; private static final String APP_C = "com.package.c"; private static final String APP_D = "com.package.d"; + private static final long VERSION_CODE = 1L; private static final String OBSERVER_NAME_1 = "observer1"; private static final String OBSERVER_NAME_2 = "observer2"; private static final String OBSERVER_NAME_3 = "observer3"; @@ -193,7 +195,7 @@ public class PackageWatchdogTest { // Then fail APP_A below the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -209,12 +211,10 @@ public class PackageWatchdogTest { * the failed packages. */ @Test - public void testPackageFailureNotifyNone() throws Exception { + public void testPackageFailureDifferentPackageNotifyNone() throws Exception { PackageWatchdog watchdog = createWatchdog(); - TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_HIGH); - TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); + TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); @@ -222,7 +222,7 @@ public class PackageWatchdogTest { // Then fail APP_C (not observed) above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_C}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -233,6 +233,40 @@ public class PackageWatchdogTest { assertEquals(0, observer2.mFailedPackages.size()); } + /** + * Test package failure and does not notify any observer because the failed package version + * does not match the available rollback-from-version. + */ + @Test + public void testPackageFailureDifferentVersionNotifyNone() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + long differentVersionCode = 2L; + TestObserver observer = new TestObserver(OBSERVER_NAME_1) { + public int onHealthCheckFailed(String packageName, long versionCode) { + if (versionCode == VERSION_CODE) { + // Only rollback for specific versionCode + return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; + } + return PackageHealthObserverImpact.USER_IMPACT_NONE; + } + }; + + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); + + // Then fail APP_A (different version) above the threshold + for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + watchdog.onPackageFailure(Arrays.asList( + new VersionedPackage(APP_A, differentVersionCode))); + } + + // Run handler so package failures are dispatched to observers + mTestLooper.dispatchAll(); + + // Verify that observers are not notified + assertEquals(0, observer.mFailedPackages.size()); + } + + /** * Test package failure and notifies only least impact observers. */ @@ -260,7 +294,10 @@ public class PackageWatchdogTest { // Then fail all apps above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A, APP_B, APP_C, APP_D}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), + new VersionedPackage(APP_B, VERSION_CODE), + new VersionedPackage(APP_C, VERSION_CODE), + new VersionedPackage(APP_D, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -297,7 +334,7 @@ public class PackageWatchdogTest { *