Merge "Fix RollbackPackageHealthObserver for rollback-all" into udc-dev

This commit is contained in:
Harshit Mahajan
2023-04-14 14:20:39 +00:00
committed by Android (Google) Code Review
2 changed files with 94 additions and 13 deletions

View File

@@ -17,10 +17,12 @@
package com.android.server.rollback;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
@@ -68,6 +70,9 @@ import java.util.function.Consumer;
final class RollbackPackageHealthObserver implements PackageHealthObserver {
private static final String TAG = "RollbackPackageHealthObserver";
private static final String NAME = "rollback-observer";
private static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot";
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
private final Context mContext;
private final Handler mHandler;
@@ -114,10 +119,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
// 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 (mitigationCount == 1 && getAvailableRollback(failedPackage) != null) {
} else if (getAvailableRollback(failedPackage) != null) {
// Rollback is available, we may get a callback into #execute
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (mitigationCount > 1 && anyRollbackAvailable) {
} else if (anyRollbackAvailable) {
// If any rollbacks are available, we will commit them
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
}
@@ -133,14 +138,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
return true;
}
if (mitigationCount == 1) {
RollbackInfo rollback = getAvailableRollback(failedPackage);
if (rollback == null) {
Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
return false;
}
RollbackInfo rollback = getAvailableRollback(failedPackage);
if (rollback != null) {
mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
} else if (mitigationCount > 1) {
} else {
mHandler.post(() -> rollbackAll(rollbackReason));
}
@@ -153,6 +154,30 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
return NAME;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public boolean mayObservePackage(String packageName) {
if (mContext.getSystemService(RollbackManager.class)
.getAvailableRollbacks().isEmpty()) {
return false;
}
return isPersistentSystemApp(packageName);
}
private boolean isPersistentSystemApp(@NonNull String packageName) {
PackageManager pm = mContext.getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private void assertInWorkerThread() {
Preconditions.checkState(mHandler.getLooper().isCurrentThread());
}
@@ -425,6 +450,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
markStagedSessionHandled(rollback.getRollbackId());
// Wait for all pending staged sessions to get handled before rebooting.
if (isPendingStagedSessionsEmpty()) {
SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true");
mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
}
}

View File

@@ -21,10 +21,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
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.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
@@ -71,9 +75,12 @@ public class RollbackPackageHealthObserverTest {
RollbackInfo mRollbackInfo;
@Mock
PackageRollbackInfo mPackageRollbackInfo;
@Mock
PackageManager mMockPackageManager;
private MockitoSession mSession;
private static final String APP_A = "com.package.a";
private static final String APP_B = "com.package.b";
private static final long VERSION_CODE = 1L;
private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
@@ -116,7 +123,7 @@ public class RollbackPackageHealthObserverTest {
RollbackPackageHealthObserver observer =
spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE);
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -137,13 +144,16 @@ public class RollbackPackageHealthObserverTest {
assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
// non-native crash
// non-native crash for the package
assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
observer.onHealthCheckFailed(testFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
// Second non-native crash again
// non-native crash for a different package
assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
observer.onHealthCheckFailed(testFailedPackage,
observer.onHealthCheckFailed(secondFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
observer.onHealthCheckFailed(secondFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2));
// Subsequent crashes when rollbacks have completed
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
@@ -152,6 +162,51 @@ public class RollbackPackageHealthObserverTest {
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3));
}
@Test
public void testIsPersistent() {
RollbackPackageHealthObserver observer =
spy(new RollbackPackageHealthObserver(mMockContext));
assertTrue(observer.isPersistent());
}
@Test
public void testMayObservePackage_withoutAnyRollback() {
RollbackPackageHealthObserver observer =
spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
assertFalse(observer.mayObservePackage(APP_A));
}
@Test
public void testMayObservePackage_forPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
spy(new RollbackPackageHealthObserver(mMockContext));
ApplicationInfo info = new ApplicationInfo();
info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM;
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getApplicationInfo(APP_A, 0)).thenReturn(info);
assertTrue(observer.mayObservePackage(APP_A));
}
@Test
public void testMayObservePackage_forNonPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getApplicationInfo(APP_A, 0))
.thenThrow(new PackageManager.NameNotFoundException());
assertFalse(observer.mayObservePackage(APP_A));
}
/**
* Test that isAutomaticRollbackDenied works correctly when packages that are not
* denied are sent.