Fix IllegalStateException in DeviceStateManagerService
When the base state is unknown, DeviceStateManagerService should not
notify listeners.
Bug: 260876135
Test: atest DeviceStateManagerServiceTest
Change-Id: Ib56e2794eacb88fdfdc8a5252c1a8a3fbd3871b8
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 9645690..fb0fc43 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -203,7 +203,7 @@
@VisibleForTesting
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy,
- @NonNull SystemPropertySetter systemPropertySetter) {
+ @NonNull SystemPropertySetter systemPropertySetter) {
super(context);
mSystemPropertySetter = systemPropertySetter;
// We use the DisplayThread because this service indirectly drives
@@ -389,12 +389,7 @@
setRearDisplayStateLocked();
- if (!mPendingState.isPresent()) {
- // If the change in the supported states didn't result in a change of the pending
- // state commitPendingState() will never be called and the callbacks will never be
- // notified of the change.
- notifyDeviceStateInfoChangedAsync();
- }
+ notifyDeviceStateInfoChangedAsync();
mHandler.post(this::notifyPolicyIfNeeded);
}
@@ -458,12 +453,7 @@
mOverrideRequestController.handleBaseStateChanged(identifier);
updatePendingStateLocked();
- if (!mPendingState.isPresent()) {
- // If the change in base state didn't result in a change of the pending state
- // commitPendingState() will never be called and the callbacks will never be
- // notified of the change.
- notifyDeviceStateInfoChangedAsync();
- }
+ notifyDeviceStateInfoChangedAsync();
mHandler.post(this::notifyPolicyIfNeeded);
}
@@ -591,6 +581,18 @@
private void notifyDeviceStateInfoChangedAsync() {
synchronized (mLock) {
+ if (mPendingState.isPresent()) {
+ Slog.i(TAG,
+ "Cannot notify device state info change when pending state is present.");
+ return;
+ }
+
+ if (!mBaseState.isPresent() || !mCommittedState.isPresent()) {
+ Slog.e(TAG, "Cannot notify device state info change before the initial state has"
+ + " been committed.");
+ return;
+ }
+
if (mProcessRecords.size() == 0) {
return;
}
@@ -656,7 +658,7 @@
}
} else {
throw new IllegalArgumentException(
- "Unknown OverrideRest type: " + request.getRequestType());
+ "Unknown OverrideRest type: " + request.getRequestType());
}
boolean updatedPendingState = updatePendingStateLocked();
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 4d06855..4881012 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -88,6 +88,11 @@
mProvider = new TestDeviceStateProvider();
mPolicy = new TestDeviceStatePolicy(mProvider);
mSysPropSetter = new TestSystemPropertySetter();
+ setupDeviceStateManagerService();
+ flushHandler(); // Flush the handler to ensure the initial values are committed.
+ }
+
+ private void setupDeviceStateManagerService() {
mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy,
mSysPropSetter);
@@ -97,8 +102,6 @@
when(mService.mActivityTaskManagerInternal.getTopApp())
.thenReturn(mWindowProcessController);
when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
-
- flushHandler(); // Flush the handler to ensure the initial values are committed.
}
private void flushHandler() {
@@ -325,6 +328,21 @@
}
@Test
+ public void registerCallback_initialValueUnavailable() throws RemoteException {
+ // Create a provider and a service without an initial base state.
+ mProvider = new TestDeviceStateProvider(null /* initialState */);
+ mPolicy = new TestDeviceStatePolicy(mProvider);
+ setupDeviceStateManagerService();
+ flushHandler(); // Flush the handler to ensure the initial values are committed.
+
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ flushHandler();
+ // The callback should never be called when the base state is not set yet.
+ assertNull(callback.getLastNotifiedInfo());
+ }
+
+ @Test
public void requestState() throws RemoteException {
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
@@ -939,8 +957,18 @@
DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE,
DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP};
+
+ @Nullable private final DeviceState mInitialState;
private Listener mListener;
+ private TestDeviceStateProvider() {
+ this(DEFAULT_DEVICE_STATE);
+ }
+
+ private TestDeviceStateProvider(@Nullable DeviceState initialState) {
+ mInitialState = initialState;
+ }
+
@Override
public void setListener(Listener listener) {
if (mListener != null) {
@@ -950,7 +978,9 @@
mListener = listener;
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates,
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
- mListener.onStateChanged(mSupportedDeviceStates[0].getIdentifier());
+ if (mInitialState != null) {
+ mListener.onStateChanged(mInitialState.getIdentifier());
+ }
}
public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {