summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java47
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java85
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManager.aidl45
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java148
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java161
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java52
-rw-r--r--services/core/java/com/android/server/devicestate/OverrideRequest.java45
-rw-r--r--services/core/java/com/android/server/devicestate/OverrideRequestController.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java102
11 files changed, 818 insertions, 84 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d18a9c7da5a1..f5ee4672b560 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1155,9 +1155,11 @@ package android.hardware.camera2 {
package android.hardware.devicestate {
public final class DeviceStateManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
method @NonNull public int[] getSupportedStates();
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 30aa4db938da..bdd45e6df448 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,6 +16,7 @@
package android.hardware.devicestate;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -115,6 +116,52 @@ public final class DeviceStateManager {
}
/**
+ * Submits a {@link DeviceStateRequest request} to override the base state of the device. This
+ * should only be used for testing, where you want to simulate the physical change to the
+ * device state.
+ * <p>
+ * By default, the request is kept active until one of the following occurs:
+ * <ul>
+ * <li>The physical state of the device changes</li>
+ * <li>The system deems the request can no longer be honored, for example if the requested
+ * state becomes unsupported.
+ * <li>A call to {@link #cancelBaseStateOverride}.
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be canceled.
+ * </ul>
+ *
+ * Submitting a base state override request may not cause any change in the presentation
+ * of the system if there is an emulated request made through {@link #requestState}, as the
+ * emulated override requests take priority.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestBaseStateOverride(request, executor, callback);
+ }
+
+ /**
+ * Cancels the active {@link DeviceStateRequest} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if there is no base state request currently active.
+ *
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ mGlobal.cancelBaseStateOverride();
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @param executor the executor to process notifications.
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index aba538f51043..738045dafdf1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -18,6 +18,7 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -81,6 +82,7 @@ public final class DeviceStateManagerGlobal {
@VisibleForTesting
public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
mDeviceStateManager = deviceStateManager;
+ registerCallbackIfNeededLocked();
}
/**
@@ -116,27 +118,22 @@ public final class DeviceStateManagerGlobal {
* DeviceStateRequest.Callback)
* @see DeviceStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void requestState(@NonNull DeviceStateRequest request,
@Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
- if (callback == null && executor != null) {
- throw new IllegalArgumentException("Callback must be supplied with executor.");
- } else if (executor == null && callback != null) {
- throw new IllegalArgumentException("Executor must be supplied with callback.");
- }
-
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
if (findRequestTokenLocked(request) != null) {
// This request has already been submitted.
return;
}
-
// Add the request wrapper to the mRequests array before requesting the state as the
// callback could be triggered immediately if the mDeviceStateManager IBinder is in the
// same process as this instance.
IBinder token = new Binder();
- mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+ mRequests.put(token, requestWrapper);
try {
mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
@@ -153,10 +150,10 @@ public final class DeviceStateManagerGlobal {
*
* @see DeviceStateManager#cancelStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void cancelStateRequest() {
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
try {
mDeviceStateManager.cancelStateRequest();
} catch (RemoteException ex) {
@@ -166,6 +163,56 @@ public final class DeviceStateManagerGlobal {
}
/**
+ * Submits a {@link DeviceStateRequest request} to modify the base state of the device.
+ *
+ * @see DeviceStateManager#requestBaseStateOverride(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
+ synchronized (mLock) {
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, requestWrapper);
+
+ try {
+ mDeviceStateManager.requestBaseStateOverride(token, request.getState(),
+ request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @see DeviceStateManager#cancelBaseStateOverride
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ synchronized (mLock) {
+ try {
+ mDeviceStateManager.cancelBaseStateOverride();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback)
@@ -179,9 +226,6 @@ public final class DeviceStateManagerGlobal {
// This callback is already registered.
return;
}
-
- registerCallbackIfNeededLocked();
-
// Add the callback wrapper to the mCallbacks array after registering the callback as
// the callback could be triggered immediately if the mDeviceStateManager IBinder is in
// the same process as this instance.
@@ -357,6 +401,8 @@ public final class DeviceStateManagerGlobal {
DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
@Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ validateRequestWrapperParameters(callback, executor);
+
mRequest = request;
mCallback = callback;
mExecutor = executor;
@@ -377,5 +423,14 @@ public final class DeviceStateManagerGlobal {
mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
}
+
+ private void validateRequestWrapperParameters(
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+ }
}
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index e450e42497a0..7175eae58a26 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -41,6 +41,10 @@ interface IDeviceStateManager {
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
* call to this method.
*
+ * Requesting a state does not cancel a base state override made through
+ * {@link #requestBaseStateOverride}, but will still attempt to put the device into the
+ * supplied {@code state}.
+ *
* @param token the request token provided
* @param state the state of device the request is asking for
* @param flags any flags that correspond to the request
@@ -50,14 +54,53 @@ interface IDeviceStateManager {
* @throws IllegalStateException if the supplied {@code token} has already been registered.
* @throws IllegalArgumentException if the supplied {@code state} is not supported.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void requestState(IBinder token, int state, int flags);
/**
* Cancels the active request previously submitted with a call to
- * {@link #requestState(IBinder, int, int)}.
+ * {@link #requestState(IBinder, int, int)}. Will have no effect on any base state override that
+ * was previously requested with {@link #requestBaseStateOverride}.
*
* @throws IllegalStateException if a callback has not yet been registered for the calling
* process.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void cancelStateRequest();
+
+ /**
+ * Requests that the device's base state be overridden to the supplied {@code state}. A callback
+ * <b>MUST</b> have been previously registered with
+ * {@link #registerCallback(IDeviceStateManagerCallback)} before a call to this method.
+ *
+ * This method should only be used for testing, when you want to simulate the device physically
+ * changing states. If you are looking to change device state for a feature, where the system
+ * should still be aware that the physical state is different than the emulated state, use
+ * {@link #requestState}.
+ *
+ * @param token the request token provided
+ * @param state the state of device the request is asking for
+ * @param flags any flags that correspond to the request
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void requestBaseStateOverride(IBinder token, int state, int flags);
+
+ /**
+ * Cancels the active base state request previously submitted with a call to
+ * {@link #overrideBaseState(IBinder, int, int)}.
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void cancelBaseStateOverride();
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 4c247427ef8f..9e39e13265bd 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,12 @@
package android.hardware.devicestate;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -36,7 +37,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
import java.util.HashSet;
import java.util.Set;
@@ -59,6 +59,7 @@ public final class DeviceStateManagerGlobalTest {
public void setUp() {
mService = new TestDeviceStateManagerService();
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
+ assertFalse(mService.mCallbacks.isEmpty());
}
@Test
@@ -79,8 +80,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the supported states and verify callback
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
@@ -88,8 +89,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the base state and verify callback
mService.setBaseState(OTHER_DEVICE_STATE);
@@ -98,8 +99,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the requested state and verify callback
DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -120,7 +121,7 @@ public final class DeviceStateManagerGlobalTest {
verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);
@@ -130,33 +131,86 @@ public final class DeviceStateManagerGlobalTest {
}
@Test
- public void submittingRequestRegistersCallback() {
- assertTrue(mService.mCallbacks.isEmpty());
+ public void submitRequest() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
- DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
- assertFalse(mService.mCallbacks.isEmpty());
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelStateRequest();
+
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
}
@Test
- public void submitRequest() {
+ public void submitBaseStateOverrideRequest() {
DeviceStateCallback callback = mock(DeviceStateCallback.class);
mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
ConcurrentUtils.DIRECT_EXECUTOR);
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getBaseState()));
- Mockito.reset(callback);
+ reset(callback);
DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
- mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
- Mockito.reset(callback);
+ reset(callback);
- mDeviceStateManagerGlobal.cancelStateRequest();
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ }
+
+ @Test
+ public void submitBaseAndEmulatedStateOverride() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ reset(callback);
+
+ DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder(
+ DEFAULT_DEVICE_STATE).build();
+
+ mDeviceStateManagerGlobal.requestState(secondRequest, null, null);
+
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ verify(callback).onStateChanged(eq(DEFAULT_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelStateRequest();
+
+ verify(callback).onStateChanged(OTHER_DEVICE_STATE);
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(DEFAULT_DEVICE_STATE);
+ verify(callback).onStateChanged(DEFAULT_DEVICE_STATE);
}
@Test
@@ -169,7 +223,7 @@ public final class DeviceStateManagerGlobalTest {
callback /* callback */);
verify(callback).onRequestActivated(eq(request));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.cancelStateRequest();
@@ -203,13 +257,16 @@ public final class DeviceStateManagerGlobalTest {
private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
private int mBaseState = DEFAULT_DEVICE_STATE;
private Request mRequest;
+ private Request mBaseStateRequest;
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
private DeviceStateInfo getInfo() {
+ final int mergedBaseState = mBaseStateRequest == null
+ ? mBaseState : mBaseStateRequest.state;
final int mergedState = mRequest == null
- ? mBaseState : mRequest.state;
- return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
+ ? mergedBaseState : mRequest.state;
+ return new DeviceStateInfo(mSupportedStates, mergedBaseState, mergedState);
}
private void notifyDeviceStateInfoChanged() {
@@ -238,7 +295,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onDeviceStateInfoChanged(getInfo());
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
@@ -249,7 +306,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestCanceled(mRequest.token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -262,7 +319,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestActive(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -275,7 +332,46 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestCanceled(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
+ }
+ }
+ notifyDeviceStateInfoChanged();
+ }
+
+ @Override
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ if (mBaseStateRequest != null) {
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(mBaseStateRequest.token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mBaseStateRequest = request;
+ notifyDeviceStateInfoChanged();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void cancelBaseStateOverride() throws RemoteException {
+ IBinder token = mBaseStateRequest.token;
+ mBaseStateRequest = null;
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
notifyDeviceStateInfoChanged();
@@ -296,7 +392,7 @@ public final class DeviceStateManagerGlobalTest {
}
public int getBaseState() {
- return mBaseState;
+ return getInfo().baseState;
}
public int getMergedState() {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8e00ccf68fb1..44c8e18a22cf 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,11 @@ import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STA
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -106,6 +109,8 @@ public final class DeviceStateManagerService extends SystemService {
private final BinderService mBinderService;
@NonNull
private final OverrideRequestController mOverrideRequestController;
+ @NonNull
+ private final DeviceStateProviderListener mDeviceStateProviderListener;
@VisibleForTesting
@NonNull
public ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -139,6 +144,12 @@ public final class DeviceStateManagerService extends SystemService {
@NonNull
private Optional<OverrideRequest> mActiveOverride = Optional.empty();
+ // The current active base state override request. When set the device state specified here will
+ // replace the value in mBaseState.
+ @GuardedBy("mLock")
+ @NonNull
+ private Optional<OverrideRequest> mActiveBaseStateOverride = Optional.empty();
+
// List of processes registered to receive notifications about changes to device state and
// request status indexed by process id.
@GuardedBy("mLock")
@@ -177,7 +188,8 @@ public final class DeviceStateManagerService extends SystemService {
mOverrideRequestController = new OverrideRequestController(
this::onOverrideRequestStatusChangedLocked);
mDeviceStatePolicy = policy;
- mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
+ mDeviceStateProviderListener = new DeviceStateProviderListener();
+ mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
@@ -257,6 +269,21 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ /**
+ * Returns the current override base state, or {@link Optional#empty()} if no override state is
+ * requested. If an override base state is present, the returned state will be the same as
+ * the base state returned from {@link #getBaseState()}.
+ */
+ @NonNull
+ Optional<DeviceState> getOverrideBaseState() {
+ synchronized (mLock) {
+ if (mActiveBaseStateOverride.isPresent()) {
+ return getStateLocked(mActiveBaseStateOverride.get().getRequestedState());
+ }
+ return Optional.empty();
+ }
+ }
+
/** Returns the list of currently supported device states. */
DeviceState[] getSupportedStates() {
synchronized (mLock) {
@@ -366,6 +393,7 @@ public final class DeviceStateManagerService extends SystemService {
}
final DeviceState baseState = baseStateOptional.get();
+
if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) {
// Base state hasn't changed. Nothing to do.
return;
@@ -375,7 +403,7 @@ public final class DeviceStateManagerService extends SystemService {
if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
mOverrideRequestController.cancelOverrideRequest();
}
- mOverrideRequestController.handleBaseStateChanged();
+ mOverrideRequestController.handleBaseStateChanged(identifier);
updatePendingStateLocked();
if (!mPendingState.isPresent()) {
@@ -528,16 +556,41 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
@OverrideRequestController.RequestStatus int status) {
- if (status == STATUS_ACTIVE) {
- mActiveOverride = Optional.of(request);
- } else if (status == STATUS_CANCELED) {
- if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
- mActiveOverride = Optional.empty();
+ if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ enableBaseStateRequestLocked(request);
+ return;
+ case STATUS_CANCELED:
+ if (mActiveBaseStateOverride.isPresent()
+ && mActiveBaseStateOverride.get() == request) {
+ mActiveBaseStateOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
+ }
+ } else if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_EMULATED_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ mActiveOverride = Optional.of(request);
+ break;
+ case STATUS_CANCELED:
+ if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
+ mActiveOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
}
} else {
- throw new IllegalArgumentException("Unknown request status: " + status);
+ throw new IllegalArgumentException(
+ "Unknown OverrideRest type: " + request.getRequestType());
}
boolean updatedPendingState = updatePendingStateLocked();
@@ -564,6 +617,18 @@ public final class DeviceStateManagerService extends SystemService {
mHandler.post(this::notifyPolicyIfNeeded);
}
+ /**
+ * Sets the new base state of the device and notifies the process that made the base state
+ * override request that the request is now active.
+ */
+ @GuardedBy("mLock")
+ private void enableBaseStateRequestLocked(OverrideRequest request) {
+ setBaseState(request.getRequestedState());
+ mActiveBaseStateOverride = Optional.of(request);
+ ProcessRecord processRecord = mProcessRecords.get(request.getPid());
+ processRecord.notifyRequestActiveAsync(request.getToken());
+ }
+
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
synchronized (mLock) {
if (mProcessRecords.contains(pid)) {
@@ -606,7 +671,8 @@ public final class DeviceStateManagerService extends SystemService {
+ " has no registered callback.");
}
- if (mOverrideRequestController.hasRequest(token)) {
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE)) {
throw new IllegalStateException("Request has already been made for the supplied"
+ " token: " + token);
}
@@ -617,7 +683,8 @@ public final class DeviceStateManagerService extends SystemService {
+ " is not supported.");
}
- OverrideRequest request = new OverrideRequest(token, callingPid, state, flags);
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mOverrideRequestController.addRequest(request);
}
}
@@ -633,6 +700,44 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ private void requestBaseStateOverrideInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE)) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ mOverrideRequestController.addBaseStateRequest(request);
+ }
+ }
+
+ private void cancelBaseStateOverrideInternal(int callingPid) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+ setBaseState(mDeviceStateProviderListener.mCurrentBaseState);
+ }
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
@@ -729,6 +834,8 @@ public final class DeviceStateManagerService extends SystemService {
}
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
if (newDeviceStates.length == 0) {
@@ -743,7 +850,7 @@ public final class DeviceStateManagerService extends SystemService {
if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
-
+ mCurrentBaseState = identifier;
setBaseState(identifier);
}
}
@@ -895,6 +1002,38 @@ public final class DeviceStateManagerService extends SystemService {
}
@Override // Binder call
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestBaseStateOverrideInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelBaseStateOverride() {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelBaseStateOverrideInternal(callingPid);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 659ee75de453..8c6068d89296 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,8 @@
package com.android.server.devicestate;
-import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.Binder;
@@ -39,6 +36,8 @@ import java.util.stream.Collectors;
public class DeviceStateManagerShellCommand extends ShellCommand {
@Nullable
private static DeviceStateRequest sLastRequest;
+ @Nullable
+ private static DeviceStateRequest sLastBaseStateRequest;
private final DeviceStateManagerService mService;
private final DeviceStateManager mClient;
@@ -58,6 +57,8 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
switch (cmd) {
case "state":
return runState(pw);
+ case "base-state":
+ return runBaseState(pw);
case "print-state":
return runPrintState(pw);
case "print-states":
@@ -89,10 +90,6 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
- final Context context = mService.getContext();
- context.enforceCallingOrSelfPermission(
- CONTROL_DEVICE_STATE,
- "Permission required to request device state.");
final long callingIdentity = Binder.clearCallingIdentity();
try {
if ("reset".equals(nextArg)) {
@@ -127,6 +124,47 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runBaseState(PrintWriter pw) {
+ final String nextArg = getNextArg();
+ if (nextArg == null) {
+ printAllStates(pw);
+ return 0;
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastBaseStateRequest != null) {
+ mClient.cancelBaseStateOverride();
+ sLastBaseStateRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestBaseStateOverride(request, null, null);
+
+ sLastBaseStateRequest = request;
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ return 0;
+ }
+
private int runPrintState(PrintWriter pw) {
Optional<DeviceState> deviceState = mService.getCommittedState();
if (deviceState.isPresent()) {
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java
index 35a4c844c710..325e384cb8ad 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequest.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java
@@ -16,9 +16,13 @@
package com.android.server.devicestate;
+import android.annotation.IntDef;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.IBinder;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A request to override the state managed by {@link DeviceStateManagerService}.
*
@@ -30,13 +34,47 @@ final class OverrideRequest {
private final int mRequestedState;
@DeviceStateRequest.RequestFlags
private final int mFlags;
+ @OverrideRequestType
+ private final int mRequestType;
+
+ /**
+ * Denotes that the request is meant to override the emulated state of the device. This will
+ * not change the base (physical) state of the device.
+ *
+ * This request type should be used if you are looking to emulate a device state for a feature
+ * but want the system to be aware of the physical state of the device.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_EMULATED_STATE = 0;
+
+ /**
+ * Denotes that the request is meant to override the base (physical) state of the device.
+ * Overriding the base state may not change the emulated state of the device if there is also an
+ * override request active for that property.
+ *
+ * This request type should only be used for testing, where you want to simulate the physical
+ * state of the device changing.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_BASE_STATE = 1;
+
+ /**
+ * Flags for signifying the type of {@link OverrideRequest}.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = {
+ OVERRIDE_REQUEST_TYPE_BASE_STATE,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OverrideRequestType {}
OverrideRequest(IBinder token, int pid, int requestedState,
- @DeviceStateRequest.RequestFlags int flags) {
+ @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) {
mToken = token;
mPid = pid;
mRequestedState = requestedState;
mFlags = flags;
+ mRequestType = requestType;
}
IBinder getToken() {
@@ -55,4 +93,9 @@ final class OverrideRequest {
int getFlags() {
return mFlags;
}
+
+ @OverrideRequestType
+ int getRequestType() {
+ return mRequestType;
+ }
}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 01f5a09323cf..e39c7324d7bd 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -75,6 +75,8 @@ final class OverrideRequestController {
// Handle to the current override request, null if none.
private OverrideRequest mRequest;
+ // Handle to the current base state override request, null if none.
+ private OverrideRequest mBaseStateRequest;
private boolean mStickyRequestsAllowed;
// The current request has outlived their process.
@@ -111,13 +113,23 @@ final class OverrideRequestController {
}
}
+ void addBaseStateRequest(@NonNull OverrideRequest request) {
+ OverrideRequest previousRequest = mBaseStateRequest;
+ mBaseStateRequest = request;
+ mListener.onStatusChanged(request, STATUS_ACTIVE);
+
+ if (previousRequest != null) {
+ cancelRequestLocked(previousRequest);
+ }
+ }
+
/**
* Cancels the request with the specified {@code token} and notifies the listener of all changes
* to request status as a result of this operation.
*/
void cancelRequest(@NonNull OverrideRequest request) {
// Either don't have a current request or attempting to cancel an already cancelled request
- if (!hasRequest(request.getToken())) {
+ if (!hasRequest(request.getToken(), request.getRequestType())) {
return;
}
cancelCurrentRequestLocked();
@@ -144,11 +156,24 @@ final class OverrideRequestController {
}
/**
+ * Cancels the current base state override request, this could be due to the physical
+ * configuration of the device changing.
+ */
+ void cancelBaseStateOverrideRequest() {
+ cancelCurrentBaseStateRequestLocked();
+ }
+
+ /**
* Returns {@code true} if this controller is current managing a request with the specified
* {@code token}, {@code false} otherwise.
*/
- boolean hasRequest(@NonNull IBinder token) {
- return mRequest != null && token == mRequest.getToken();
+ boolean hasRequest(@NonNull IBinder token,
+ @OverrideRequest.OverrideRequestType int requestType) {
+ if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
+ } else {
+ return mRequest != null && token == mRequest.getToken();
+ }
}
/**
@@ -157,11 +182,11 @@ final class OverrideRequestController {
* operation.
*/
void handleProcessDied(int pid) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (mRequest.getPid() == pid) {
+ if (mRequest != null && mRequest.getPid() == pid) {
if (mStickyRequestsAllowed) {
// Do not cancel the requests now because sticky requests are allowed. These
// requests will be cancelled on a call to cancelStickyRequests().
@@ -176,7 +201,10 @@ final class OverrideRequestController {
* Notifies the controller that the base state has changed. The controller will notify the
* listener of all changes to request status as a result of this change.
*/
- void handleBaseStateChanged() {
+ void handleBaseStateChanged(int state) {
+ if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
+ cancelBaseStateOverrideRequest();
+ }
if (mRequest == null) {
return;
}
@@ -192,11 +220,12 @@ final class OverrideRequestController {
* notify the listener of all changes to request status as a result of this change.
*/
void handleNewSupportedStates(int[] newSupportedStates) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && !contains(newSupportedStates,
+ mBaseStateRequest.getRequestedState())) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (!contains(newSupportedStates, mRequest.getRequestedState())) {
+ if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
cancelCurrentRequestLocked();
}
}
@@ -228,10 +257,23 @@ final class OverrideRequestController {
return;
}
mStickyRequest = false;
- mListener.onStatusChanged(mRequest, STATUS_CANCELED);
+ cancelRequestLocked(mRequest);
mRequest = null;
}
+ /**
+ * Handles cancelling {@code mBaseStateRequest}.
+ * Notifies the listener of the canceled status as well.
+ */
+ private void cancelCurrentBaseStateRequestLocked() {
+ if (mBaseStateRequest == null) {
+ Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
+ return;
+ }
+ cancelRequestLocked(mBaseStateRequest);
+ mBaseStateRequest = null;
+ }
+
private static boolean contains(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
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 038cbc032375..94f88abf7301 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -587,6 +587,157 @@ public final class DeviceStateManagerServiceTest {
});
}
+ @Test
+ public void requestBaseStateOverride() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mService.getBinderService().cancelBaseStateOverride();
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseStateOverride_cancelledByBaseStateUpdate() throws RemoteException {
+ final DeviceState testDeviceState = new DeviceState(2, "TEST", 0);
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ mProvider.notifySupportedDeviceStates(
+ new DeviceState[]{DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE, testDeviceState });
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(testDeviceState.getIdentifier());
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set to the new base state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(testDeviceState));
+ assertEquals(mSysPropSetter.getValue(),
+ testDeviceState.getIdentifier() + ":" + testDeviceState.getName());
+ assertEquals(mService.getBaseState(), Optional.of(testDeviceState));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ testDeviceState.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ testDeviceState.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ testDeviceState.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token, INVALID_DEVICE_STATE,
+ 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
+ }
+
private static void assertArrayEquals(int[] expected, int[] actual) {
Assert.assertTrue(Arrays.equals(expected, actual));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index 2297c91818c0..430504ca2428 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.devicestate;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -57,7 +59,7 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest() {
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(request));
mController.addRequest(request);
@@ -67,14 +69,14 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest_cancelExistingRequestThroughNewRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(firstRequest));
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(secondRequest));
mController.addRequest(secondRequest);
@@ -85,7 +87,7 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest_cancelActiveRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
@@ -97,30 +99,90 @@ public final class OverrideRequestControllerTest {
}
@Test
+ public void addBaseStateRequest() {
+ OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(request));
+
+ mController.addBaseStateRequest(request);
+ assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(firstRequest));
+
+ mController.addBaseStateRequest(firstRequest);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(secondRequest));
+
+ mController.addBaseStateRequest(secondRequest);
+ assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelActiveBaseStateRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+
+ mController.addBaseStateRequest(firstRequest);
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ mController.cancelBaseStateOverrideRequest();
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
public void handleBaseStateChanged() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */,
- DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleBaseStateChanged();
+ mController.addBaseStateRequest(baseStateRequest);
+
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleBaseStateChanged(1);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
public void handleProcessDied() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
@@ -128,13 +190,20 @@ public final class OverrideRequestControllerTest {
mController.setStickyRequestsAllowed(true);
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
mController.cancelStickyRequest();
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
@@ -143,22 +212,31 @@ public final class OverrideRequestControllerTest {
@Test
public void handleNewSupportedStates() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0, 1 });
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleNewSupportedStates(new int[]{0, 1});
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0 });
+ mController.handleNewSupportedStates(new int[]{0});
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
public void cancelOverrideRequestsTest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);