Loading packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java +10 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = "persist.device_config.configuration.disable_high_impact_rollback"; private final Context mContext; private final Handler mHandler; private final ApexManager mApexManager; Loading Loading @@ -605,6 +608,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { // Apply all available low impact rollbacks. mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) { // Check disable_high_impact_rollback device config before performing rollback if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { return; } // Rollback one package at a time. If that doesn't resolve the issue, rollback // next with same impact level. mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason)); Loading Loading @@ -718,7 +725,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; break; case PackageManager.ROLLBACK_USER_IMPACT_HIGH: if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90; } break; default: impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; Loading services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +101 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; Loading @@ -42,6 +44,7 @@ import android.content.rollback.RollbackManager; import android.crashrecovery.flags.Flags; import android.os.Handler; import android.os.MessageQueue; import android.os.SystemProperties; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.runner.AndroidJUnit4; Loading @@ -65,6 +68,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading @@ -90,7 +94,7 @@ public class RollbackPackageHealthObserverTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private HashMap<String, String> mSystemSettingsMap; private MockitoSession mSession; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; Loading @@ -99,6 +103,9 @@ public class RollbackPackageHealthObserverTest { private static final long VERSION_CODE_2 = 2L; private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = "persist.device_config.configuration.disable_high_impact_rollback"; private SystemConfig mSysConfig; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); Loading @@ -111,11 +118,34 @@ public class RollbackPackageHealthObserverTest { .initMocks(this) .strictness(Strictness.LENIENT) .spyStatic(PackageWatchdog.class) .spyStatic(SystemProperties.class) .startMocking(); mSystemSettingsMap = new HashMap<>(); // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); // Mock SystemProperties setter and various getters doAnswer((Answer<Void>) invocationOnMock -> { String key = invocationOnMock.getArgument(0); String value = invocationOnMock.getArgument(1); mSystemSettingsMap.put(key, value); return null; } ).when(() -> SystemProperties.set(anyString(), anyString())); doAnswer((Answer<Boolean>) invocationOnMock -> { String key = invocationOnMock.getArgument(0); boolean defaultValue = invocationOnMock.getArgument(1); String storedValue = mSystemSettingsMap.get(key); return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue); } ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean())); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false)); } @After Loading Loading @@ -609,6 +639,32 @@ public class RollbackPackageHealthObserverTest { observer.onBootLoop(1)); } @Test public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage() throws PackageManager.NameNotFoundException { mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, null, null, false, false, null); RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); RollbackPackageHealthObserver observer = spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); // Make the rollbacks available when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, observer.onBootLoop(1)); } /** * When the rollback impact level is manual only return user impact level 0. (User impact level * 0 is ignored by package watchdog) Loading Loading @@ -924,6 +980,50 @@ public class RollbackPackageHealthObserverTest { assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); } /** * Don't roll back if kill switch is enabled. */ @Test public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh() throws PackageManager.NameNotFoundException { mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); int rollbackId1 = 1; VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, null, null , false, false, null); RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); int rollbackId2 = 2; VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, null, null , false, false, null); RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); RollbackPackageHealthObserver observer = spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); // Make the rollbacks available when(mRollbackManager.getAvailableRollbacks()).thenReturn( List.of(rollbackInfo1, rollbackInfo2)); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); observer.executeBootLoopMitigation(1); waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); verify(mRollbackManager, never()).commitRollback( argument.capture(), any(), any()); } private void waitForIdleHandler(Handler handler, Duration timeout) { final MessageQueue queue = handler.getLooper().getQueue(); final CountDownLatch latch = new CountDownLatch(1); Loading Loading
packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java +10 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = "persist.device_config.configuration.disable_high_impact_rollback"; private final Context mContext; private final Handler mHandler; private final ApexManager mApexManager; Loading Loading @@ -605,6 +608,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { // Apply all available low impact rollbacks. mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) { // Check disable_high_impact_rollback device config before performing rollback if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { return; } // Rollback one package at a time. If that doesn't resolve the issue, rollback // next with same impact level. mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason)); Loading Loading @@ -718,7 +725,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; break; case PackageManager.ROLLBACK_USER_IMPACT_HIGH: if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90; } break; default: impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; Loading
services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +101 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; Loading @@ -42,6 +44,7 @@ import android.content.rollback.RollbackManager; import android.crashrecovery.flags.Flags; import android.os.Handler; import android.os.MessageQueue; import android.os.SystemProperties; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.runner.AndroidJUnit4; Loading @@ -65,6 +68,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading @@ -90,7 +94,7 @@ public class RollbackPackageHealthObserverTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private HashMap<String, String> mSystemSettingsMap; private MockitoSession mSession; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; Loading @@ -99,6 +103,9 @@ public class RollbackPackageHealthObserverTest { private static final long VERSION_CODE_2 = 2L; private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = "persist.device_config.configuration.disable_high_impact_rollback"; private SystemConfig mSysConfig; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); Loading @@ -111,11 +118,34 @@ public class RollbackPackageHealthObserverTest { .initMocks(this) .strictness(Strictness.LENIENT) .spyStatic(PackageWatchdog.class) .spyStatic(SystemProperties.class) .startMocking(); mSystemSettingsMap = new HashMap<>(); // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); // Mock SystemProperties setter and various getters doAnswer((Answer<Void>) invocationOnMock -> { String key = invocationOnMock.getArgument(0); String value = invocationOnMock.getArgument(1); mSystemSettingsMap.put(key, value); return null; } ).when(() -> SystemProperties.set(anyString(), anyString())); doAnswer((Answer<Boolean>) invocationOnMock -> { String key = invocationOnMock.getArgument(0); boolean defaultValue = invocationOnMock.getArgument(1); String storedValue = mSystemSettingsMap.get(key); return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue); } ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean())); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false)); } @After Loading Loading @@ -609,6 +639,32 @@ public class RollbackPackageHealthObserverTest { observer.onBootLoop(1)); } @Test public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage() throws PackageManager.NameNotFoundException { mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, null, null, false, false, null); RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); RollbackPackageHealthObserver observer = spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); // Make the rollbacks available when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, observer.onBootLoop(1)); } /** * When the rollback impact level is manual only return user impact level 0. (User impact level * 0 is ignored by package watchdog) Loading Loading @@ -924,6 +980,50 @@ public class RollbackPackageHealthObserverTest { assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); } /** * Don't roll back if kill switch is enabled. */ @Test public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh() throws PackageManager.NameNotFoundException { mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); int rollbackId1 = 1; VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, null, null , false, false, null); RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); int rollbackId2 = 2; VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, null, null , false, false, null); RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), false, null, 111, PackageManager.ROLLBACK_USER_IMPACT_HIGH); RollbackPackageHealthObserver observer = spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); // Make the rollbacks available when(mRollbackManager.getAvailableRollbacks()).thenReturn( List.of(rollbackInfo1, rollbackInfo2)); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); observer.executeBootLoopMitigation(1); waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); verify(mRollbackManager, never()).commitRollback( argument.capture(), any(), any()); } private void waitForIdleHandler(Handler handler, Duration timeout) { final MessageQueue queue = handler.getLooper().getQueue(); final CountDownLatch latch = new CountDownLatch(1); Loading