Loading services/core/java/com/android/server/PackageWatchdog.java +25 −10 Original line number Diff line number Diff line Loading @@ -182,6 +182,9 @@ public class PackageWatchdog { private final Runnable mSaveToFile = this::saveToFile; private final SystemClock mSystemClock; private final BootThreshold mBootThreshold; private final DeviceConfig.OnPropertiesChangedListener mOnPropertyChangedListener = this::onPropertyChanged; // The set of packages that have been synced with the ExplicitHealthCheckController @GuardedBy("mLock") private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); Loading Loading @@ -669,12 +672,20 @@ public class PackageWatchdog { } } @VisibleForTesting long getTriggerFailureCount() { synchronized (mLock) { return mTriggerFailureCount; } } @VisibleForTesting long getTriggerFailureDurationMs() { synchronized (mLock) { return mTriggerFailureDurationMs; } } /** * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. */ Loading Loading @@ -983,21 +994,25 @@ public class PackageWatchdog { } } private void onPropertyChanged(DeviceConfig.Properties properties) { try { updateConfigs(); } catch (Exception ignore) { Slog.w(TAG, "Failed to reload device config changes"); } } /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */ private void setPropertyChangedListenerLocked() { DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_ROLLBACK, mContext.getMainExecutor(), (properties) -> { if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(properties.getNamespace())) { return; mOnPropertyChangedListener); } try { updateConfigs(); } catch (Exception ignore) { Slog.w(TAG, "Failed to reload device config changes"); } }); @VisibleForTesting void removePropertyChangedListener() { DeviceConfig.removeOnPropertiesChangedListener(mOnPropertyChangedListener); } /** Loading tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +78 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -79,11 +78,15 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; /** * Test PackageWatchdog. */ public class PackageWatchdogTest { private static final long RETRY_MAX_COUNT = 30; private static final long RETRY_TIMEOUT_MILLIS = 500; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; private static final String APP_C = "com.package.c"; Loading @@ -109,6 +112,16 @@ public class PackageWatchdogTest { private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private boolean retry(Supplier<Boolean> supplier) throws Exception { for (int i = 0; i < RETRY_MAX_COUNT; ++i) { if (supplier.get()) { return true; } Thread.sleep(RETRY_TIMEOUT_MILLIS); } return false; } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading Loading @@ -176,6 +189,10 @@ public class PackageWatchdogTest { public void tearDown() throws Exception { dropShellPermissions(); mSession.finishMocking(); // Clean up listeners since too many listeners will delay notifications significantly for (PackageWatchdog watchdog : mAllocatedWatchdogs) { watchdog.removePropertyChangedListener(); } mAllocatedWatchdogs.clear(); } Loading Loading @@ -1282,6 +1299,66 @@ public class PackageWatchdogTest { assertTrue(readPkg.isEqualTo(expectedPkg)); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_explicitHealthCheckEnabled() throws Exception { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); assertThat(controller.mIsEnabled).isTrue(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, Boolean.toString(false), /*makeDefault*/false); retry(() -> !controller.mIsEnabled); assertThat(controller.mIsEnabled).isFalse(); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_triggerFailureCount() throws Exception { PackageWatchdog watchdog = createWatchdog(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, Integer.toString(777), false); retry(() -> watchdog.getTriggerFailureCount() == 777); assertThat(watchdog.getTriggerFailureCount()).isEqualTo(777); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, Integer.toString(0), false); retry(() -> watchdog.getTriggerFailureCount() == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); assertThat(watchdog.getTriggerFailureCount()).isEqualTo( PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_triggerFailureDurationMs() throws Exception { PackageWatchdog watchdog = createWatchdog(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, Integer.toString(888), false); retry(() -> watchdog.getTriggerFailureDurationMs() == 888); assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo(888); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, Integer.toString(0), false); retry(() -> watchdog.getTriggerFailureDurationMs() == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo( PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); } private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() Loading Loading
services/core/java/com/android/server/PackageWatchdog.java +25 −10 Original line number Diff line number Diff line Loading @@ -182,6 +182,9 @@ public class PackageWatchdog { private final Runnable mSaveToFile = this::saveToFile; private final SystemClock mSystemClock; private final BootThreshold mBootThreshold; private final DeviceConfig.OnPropertiesChangedListener mOnPropertyChangedListener = this::onPropertyChanged; // The set of packages that have been synced with the ExplicitHealthCheckController @GuardedBy("mLock") private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); Loading Loading @@ -669,12 +672,20 @@ public class PackageWatchdog { } } @VisibleForTesting long getTriggerFailureCount() { synchronized (mLock) { return mTriggerFailureCount; } } @VisibleForTesting long getTriggerFailureDurationMs() { synchronized (mLock) { return mTriggerFailureDurationMs; } } /** * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. */ Loading Loading @@ -983,21 +994,25 @@ public class PackageWatchdog { } } private void onPropertyChanged(DeviceConfig.Properties properties) { try { updateConfigs(); } catch (Exception ignore) { Slog.w(TAG, "Failed to reload device config changes"); } } /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */ private void setPropertyChangedListenerLocked() { DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_ROLLBACK, mContext.getMainExecutor(), (properties) -> { if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(properties.getNamespace())) { return; mOnPropertyChangedListener); } try { updateConfigs(); } catch (Exception ignore) { Slog.w(TAG, "Failed to reload device config changes"); } }); @VisibleForTesting void removePropertyChangedListener() { DeviceConfig.removeOnPropertiesChangedListener(mOnPropertyChangedListener); } /** Loading
tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +78 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -79,11 +78,15 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; /** * Test PackageWatchdog. */ public class PackageWatchdogTest { private static final long RETRY_MAX_COUNT = 30; private static final long RETRY_TIMEOUT_MILLIS = 500; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; private static final String APP_C = "com.package.c"; Loading @@ -109,6 +112,16 @@ public class PackageWatchdogTest { private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private boolean retry(Supplier<Boolean> supplier) throws Exception { for (int i = 0; i < RETRY_MAX_COUNT; ++i) { if (supplier.get()) { return true; } Thread.sleep(RETRY_TIMEOUT_MILLIS); } return false; } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Loading Loading @@ -176,6 +189,10 @@ public class PackageWatchdogTest { public void tearDown() throws Exception { dropShellPermissions(); mSession.finishMocking(); // Clean up listeners since too many listeners will delay notifications significantly for (PackageWatchdog watchdog : mAllocatedWatchdogs) { watchdog.removePropertyChangedListener(); } mAllocatedWatchdogs.clear(); } Loading Loading @@ -1282,6 +1299,66 @@ public class PackageWatchdogTest { assertTrue(readPkg.isEqualTo(expectedPkg)); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_explicitHealthCheckEnabled() throws Exception { TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); assertThat(controller.mIsEnabled).isTrue(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, Boolean.toString(false), /*makeDefault*/false); retry(() -> !controller.mIsEnabled); assertThat(controller.mIsEnabled).isFalse(); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_triggerFailureCount() throws Exception { PackageWatchdog watchdog = createWatchdog(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, Integer.toString(777), false); retry(() -> watchdog.getTriggerFailureCount() == 777); assertThat(watchdog.getTriggerFailureCount()).isEqualTo(777); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, Integer.toString(0), false); retry(() -> watchdog.getTriggerFailureCount() == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); assertThat(watchdog.getTriggerFailureCount()).isEqualTo( PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT); } /** * Tests device config changes are propagated correctly. */ @Test public void testDeviceConfigChange_triggerFailureDurationMs() throws Exception { PackageWatchdog watchdog = createWatchdog(); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, Integer.toString(888), false); retry(() -> watchdog.getTriggerFailureDurationMs() == 888); assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo(888); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, Integer.toString(0), false); retry(() -> watchdog.getTriggerFailureDurationMs() == PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); assertThat(watchdog.getTriggerFailureDurationMs()).isEqualTo( PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); } private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() Loading