Merge "Notify PackageHealthObservers of the package versionCode on package failure"
This commit is contained in:
committed by
Android (Google) Code Review
commit
aab0a275ae
@@ -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 {
|
||||
*
|
||||
* <p>This method could be called frequently if there is a severe problem on the device.
|
||||
*/
|
||||
public void onPackageFailure(String[] packages) {
|
||||
public void onPackageFailure(List<VersionedPackage> 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?
|
||||
/**
|
||||
|
||||
@@ -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<VersionedPackage> 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);
|
||||
|
||||
@@ -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<VersionedPackage> getPackageListWithVersionCode() {
|
||||
int size = pkgList.size();
|
||||
if (size == 0) {
|
||||
return null;
|
||||
}
|
||||
List<VersionedPackage> 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
* <ul>
|
||||
*/
|
||||
@Test
|
||||
public void testPackageFailureNotifyLeastSuccessively() throws Exception {
|
||||
public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception {
|
||||
PackageWatchdog watchdog = createWatchdog();
|
||||
TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
|
||||
PackageHealthObserverImpact.USER_IMPACT_LOW);
|
||||
@@ -310,7 +347,7 @@ public class PackageWatchdogTest {
|
||||
|
||||
// Then fail APP_A above the threshold
|
||||
for (int i = 0; i < TRIGGER_FAILURE_COUNT; 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
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -327,7 +364,7 @@ public class PackageWatchdogTest {
|
||||
|
||||
// Then fail APP_A again above the threshold
|
||||
for (int i = 0; i < TRIGGER_FAILURE_COUNT; 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
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -344,7 +381,7 @@ public class PackageWatchdogTest {
|
||||
|
||||
// Then fail APP_A again above the threshold
|
||||
for (int i = 0; i < TRIGGER_FAILURE_COUNT; 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
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -361,7 +398,7 @@ public class PackageWatchdogTest {
|
||||
|
||||
// Then fail APP_A again above the threshold
|
||||
for (int i = 0; i < TRIGGER_FAILURE_COUNT; 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
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -388,7 +425,7 @@ public class PackageWatchdogTest {
|
||||
|
||||
// Then fail APP_A above the threshold
|
||||
for (int i = 0; i < TRIGGER_FAILURE_COUNT; 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
|
||||
@@ -420,11 +457,11 @@ public class PackageWatchdogTest {
|
||||
mImpact = impact;
|
||||
}
|
||||
|
||||
public int onHealthCheckFailed(String packageName) {
|
||||
public int onHealthCheckFailed(String packageName, long versionCode) {
|
||||
return mImpact;
|
||||
}
|
||||
|
||||
public boolean execute(String packageName) {
|
||||
public boolean execute(String packageName, long versionCode) {
|
||||
mFailedPackages.add(packageName);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user