Loading services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +35 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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; } if (rollback != null) { mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); } else if (mitigationCount > 1) { } else { mHandler.post(() -> rollbackAll(rollbackReason)); } Loading @@ -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()); } Loading Loading @@ -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"); } } Loading services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +59 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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); Loading @@ -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()); Loading @@ -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. Loading Loading
services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +35 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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; } if (rollback != null) { mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); } else if (mitigationCount > 1) { } else { mHandler.post(() -> rollbackAll(rollbackReason)); } Loading @@ -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()); } Loading Loading @@ -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"); } } Loading
services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +59 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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); Loading @@ -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()); Loading @@ -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. Loading