Merge "Add device state request APIs to DeviceStateManager." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ef8d3b00e0
@@ -12,6 +12,7 @@ package android {
|
||||
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
|
||||
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
|
||||
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
|
||||
field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
|
||||
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
|
||||
field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
|
||||
field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
|
||||
@@ -900,6 +901,40 @@ package android.hardware.camera2 {
|
||||
|
||||
}
|
||||
|
||||
package android.hardware.devicestate {
|
||||
|
||||
public final class DeviceStateManager {
|
||||
method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
|
||||
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
||||
method @NonNull public int[] getSupportedStates();
|
||||
method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
|
||||
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
|
||||
}
|
||||
|
||||
public static interface DeviceStateManager.DeviceStateListener {
|
||||
method public void onDeviceStateChanged(int);
|
||||
}
|
||||
|
||||
public final class DeviceStateRequest {
|
||||
method public int getFlags();
|
||||
method public int getState();
|
||||
method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int);
|
||||
field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1
|
||||
}
|
||||
|
||||
public static final class DeviceStateRequest.Builder {
|
||||
method @NonNull public android.hardware.devicestate.DeviceStateRequest build();
|
||||
method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int);
|
||||
}
|
||||
|
||||
public static interface DeviceStateRequest.Callback {
|
||||
method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
||||
method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
||||
method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.hardware.display {
|
||||
|
||||
public class AmbientDisplayConfiguration {
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package android.hardware.devicestate;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -28,13 +32,19 @@ import java.util.concurrent.Executor;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@SystemService(Context.DEVICE_STATE_SERVICE)
|
||||
public final class DeviceStateManager {
|
||||
/** Invalid device state. */
|
||||
/**
|
||||
* Invalid device state.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int INVALID_DEVICE_STATE = -1;
|
||||
|
||||
private DeviceStateManagerGlobal mGlobal;
|
||||
private final DeviceStateManagerGlobal mGlobal;
|
||||
|
||||
/** @hide */
|
||||
public DeviceStateManager() {
|
||||
DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
|
||||
if (global == null) {
|
||||
@@ -44,24 +54,74 @@ public final class DeviceStateManager {
|
||||
mGlobal = global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of device states that are supported and can be requested with
|
||||
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
|
||||
*/
|
||||
@NonNull
|
||||
public int[] getSupportedStates() {
|
||||
return mGlobal.getSupportedStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a {@link DeviceStateRequest request} to modify the device state.
|
||||
* <p>
|
||||
* By default, the request is kept active until a call to
|
||||
* {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
|
||||
* <ul>
|
||||
* <li>Another processes submits a request succeeding this request in which case the request
|
||||
* will be suspended until the interrupting request is canceled.
|
||||
* <li>The requested state has become unsupported.
|
||||
* <li>The process submitting the request dies.
|
||||
* </ul>
|
||||
* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the requested state is unsupported.
|
||||
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
|
||||
* permission is not held.
|
||||
*
|
||||
* @see DeviceStateRequest
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
|
||||
public void requestState(@NonNull DeviceStateRequest request,
|
||||
@Nullable @CallbackExecutor Executor executor,
|
||||
@Nullable DeviceStateRequest.Callback callback) {
|
||||
mGlobal.requestState(request, callback, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a {@link DeviceStateRequest request} previously submitted with a call to
|
||||
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
|
||||
* <p>
|
||||
* This method is noop if the {@code request} has not been submitted with a call to
|
||||
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
|
||||
*
|
||||
* @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
|
||||
* permission is not held.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
|
||||
public void cancelRequest(@NonNull DeviceStateRequest request) {
|
||||
mGlobal.cancelRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to receive notifications about changes in device state.
|
||||
*
|
||||
* @param listener the listener to register.
|
||||
* @param executor the executor to process notifications.
|
||||
* @param listener the listener to register.
|
||||
*
|
||||
* @see DeviceStateListener
|
||||
*/
|
||||
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
|
||||
@NonNull Executor executor) {
|
||||
public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull DeviceStateListener listener) {
|
||||
mGlobal.registerDeviceStateListener(listener, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a listener previously registered with
|
||||
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
|
||||
* {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
|
||||
*/
|
||||
public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
|
||||
public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
|
||||
mGlobal.unregisterDeviceStateListener(listener);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -67,6 +69,9 @@ public final class DeviceStateManagerGlobal {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
|
||||
|
||||
@Nullable
|
||||
@GuardedBy("mLock")
|
||||
private Integer mLastReceivedState;
|
||||
@@ -76,10 +81,85 @@ public final class DeviceStateManagerGlobal {
|
||||
mDeviceStateManager = deviceStateManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of supported device states.
|
||||
*
|
||||
* @see DeviceStateManager#getSupportedStates()
|
||||
*/
|
||||
public int[] getSupportedStates() {
|
||||
try {
|
||||
return mDeviceStateManager.getSupportedDeviceStates();
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a {@link DeviceStateRequest request} to modify the device state.
|
||||
*
|
||||
* @see DeviceStateManager#requestState(DeviceStateRequest,
|
||||
* Executor, DeviceStateRequest.Callback)
|
||||
* @see DeviceStateRequest
|
||||
*/
|
||||
public void requestState(@NonNull DeviceStateRequest request,
|
||||
@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.");
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
try {
|
||||
mDeviceStateManager.requestState(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 #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
|
||||
*
|
||||
* @see DeviceStateManager#cancelRequest(DeviceStateRequest)
|
||||
*/
|
||||
public void cancelRequest(@NonNull DeviceStateRequest request) {
|
||||
synchronized (mLock) {
|
||||
registerCallbackIfNeededLocked();
|
||||
|
||||
final IBinder token = findRequestTokenLocked(request);
|
||||
if (token == null) {
|
||||
// This request has not been submitted.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mDeviceStateManager.cancelRequest(token);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to receive notifications about changes in device state.
|
||||
*
|
||||
* @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
|
||||
* @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PACKAGE)
|
||||
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
|
||||
@@ -112,7 +192,7 @@ public final class DeviceStateManagerGlobal {
|
||||
* Unregisters a listener previously registered with
|
||||
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
|
||||
*
|
||||
* @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
|
||||
* @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PACKAGE)
|
||||
public void unregisterDeviceStateListener(DeviceStateListener listener) {
|
||||
@@ -144,6 +224,17 @@ public final class DeviceStateManagerGlobal {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
|
||||
for (int i = 0; i < mRequests.size(); i++) {
|
||||
if (mRequests.valueAt(i).mRequest.equals(request)) {
|
||||
return mRequests.keyAt(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Handles a call from the server that the device state has changed. */
|
||||
private void handleDeviceStateChanged(int newDeviceState) {
|
||||
ArrayList<DeviceStateListenerWrapper> listeners;
|
||||
synchronized (mLock) {
|
||||
@@ -156,11 +247,68 @@ public final class DeviceStateManagerGlobal {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a call from the server that a request for the supplied {@code token} has become
|
||||
* active.
|
||||
*/
|
||||
private void handleRequestActive(IBinder token) {
|
||||
DeviceStateRequestWrapper request;
|
||||
synchronized (mLock) {
|
||||
request = mRequests.get(token);
|
||||
}
|
||||
if (request != null) {
|
||||
request.notifyRequestActive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a call from the server that a request for the supplied {@code token} has become
|
||||
* suspended.
|
||||
*/
|
||||
private void handleRequestSuspended(IBinder token) {
|
||||
DeviceStateRequestWrapper request;
|
||||
synchronized (mLock) {
|
||||
request = mRequests.get(token);
|
||||
}
|
||||
if (request != null) {
|
||||
request.notifyRequestSuspended();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a call from the server that a request for the supplied {@code token} has become
|
||||
* canceled.
|
||||
*/
|
||||
private void handleRequestCanceled(IBinder token) {
|
||||
DeviceStateRequestWrapper request;
|
||||
synchronized (mLock) {
|
||||
request = mRequests.remove(token);
|
||||
}
|
||||
if (request != null) {
|
||||
request.notifyRequestCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
|
||||
@Override
|
||||
public void onDeviceStateChanged(int deviceState) {
|
||||
handleDeviceStateChanged(deviceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestActive(IBinder token) {
|
||||
handleRequestActive(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestSuspended(IBinder token) {
|
||||
handleRequestSuspended(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCanceled(IBinder token) {
|
||||
handleRequestCanceled(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DeviceStateListenerWrapper {
|
||||
@@ -176,4 +324,43 @@ public final class DeviceStateManagerGlobal {
|
||||
mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DeviceStateRequestWrapper {
|
||||
private final DeviceStateRequest mRequest;
|
||||
@Nullable
|
||||
private final DeviceStateRequest.Callback mCallback;
|
||||
@Nullable
|
||||
private final Executor mExecutor;
|
||||
|
||||
DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
|
||||
@Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
|
||||
mRequest = request;
|
||||
mCallback = callback;
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
void notifyRequestActive() {
|
||||
if (mCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
|
||||
}
|
||||
|
||||
void notifyRequestSuspended() {
|
||||
if (mCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
|
||||
}
|
||||
|
||||
void notifyRequestCanceled() {
|
||||
if (mCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
163
core/java/android/hardware/devicestate/DeviceStateRequest.java
Normal file
163
core/java/android/hardware/devicestate/DeviceStateRequest.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.devicestate;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A request to alter the state of the device managed by {@link DeviceStateManager}.
|
||||
* <p>
|
||||
* Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to
|
||||
* {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
|
||||
* DeviceStateRequest.Callback)}.
|
||||
* <p>
|
||||
* By default, the request is kept active until a call to
|
||||
* {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
|
||||
* occurs:
|
||||
* <ul>
|
||||
* <li>Another processes submits a request succeeding this request in which case the request
|
||||
* will be suspended until the interrupting request is canceled.
|
||||
* <li>The requested state has become unsupported.
|
||||
* <li>The process submitting the request dies.
|
||||
* </ul>
|
||||
* However, this behavior can be changed by setting flags on the request. For example, the
|
||||
* {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the
|
||||
* request whenever the base (non-override) device state changes.
|
||||
*
|
||||
* @see DeviceStateManager
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class DeviceStateRequest {
|
||||
/**
|
||||
* Flag that indicates the request should be canceled automatically when the base
|
||||
* (non-override) device state changes. Useful when the requestor only wants the request to
|
||||
* remain active while the base state remains constant and automatically cancel when the user
|
||||
* manipulates the device into a different state.
|
||||
*/
|
||||
public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = {"FLAG_"}, flag = true, value = {
|
||||
FLAG_CANCEL_WHEN_BASE_CHANGES,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RequestFlags {}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported
|
||||
* states for the device which can be queried with a call to
|
||||
* {@link DeviceStateManager#getSupportedStates()}.
|
||||
*
|
||||
* @param requestedState the device state being requested.
|
||||
*/
|
||||
@NonNull
|
||||
public static Builder newBuilder(int requestedState) {
|
||||
return new Builder(requestedState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link DeviceStateRequest}. An instance can be obtained through
|
||||
* {@link #newBuilder(int)}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final int mRequestedState;
|
||||
private int mFlags;
|
||||
|
||||
private Builder(int requestedState) {
|
||||
mRequestedState = requestedState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag bits provided within {@code flags} with all other bits remaining
|
||||
* unchanged.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setFlags(@RequestFlags int flags) {
|
||||
mFlags |= flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link DeviceStateRequest} object whose state matches the state set on the
|
||||
* builder.
|
||||
*/
|
||||
@NonNull
|
||||
public DeviceStateRequest build() {
|
||||
return new DeviceStateRequest(mRequestedState, mFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback to track the status of a request. */
|
||||
public interface Callback {
|
||||
/**
|
||||
* Called to indicate the request has become active and the device state will match the
|
||||
* requested state.
|
||||
* <p>
|
||||
* Guaranteed to be called after a call to
|
||||
* {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
|
||||
* matching the requested state.
|
||||
*/
|
||||
default void onRequestActivated(@NonNull DeviceStateRequest request) {}
|
||||
|
||||
/**
|
||||
* Called to indicate the request has been temporarily suspended.
|
||||
* <p>
|
||||
* Guaranteed to be called before a call to
|
||||
* {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
|
||||
*/
|
||||
default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
|
||||
|
||||
/**
|
||||
* Called to indicate the request has been canceled. The request can be resubmitted with
|
||||
* another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
|
||||
* DeviceStateRequest.Callback)}.
|
||||
* <p>
|
||||
* Guaranteed to be called before a call to
|
||||
* {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
|
||||
* <p>
|
||||
* Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
|
||||
* occur before this method.
|
||||
*/
|
||||
default void onRequestCanceled(@NonNull DeviceStateRequest request) {}
|
||||
}
|
||||
|
||||
private final int mRequestedState;
|
||||
@RequestFlags
|
||||
private final int mFlags;
|
||||
|
||||
private DeviceStateRequest(int requestedState, @RequestFlags int flags) {
|
||||
mRequestedState = requestedState;
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return mRequestedState;
|
||||
}
|
||||
|
||||
@RequestFlags
|
||||
public int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,45 @@ import android.hardware.devicestate.IDeviceStateManagerCallback;
|
||||
|
||||
/** @hide */
|
||||
interface IDeviceStateManager {
|
||||
/**
|
||||
* Registers a callback to receive notifications from the device state manager. Only one
|
||||
* callback can be registered per-process.
|
||||
* <p>
|
||||
* As the callback mechanism is used to alert the caller of changes to request status a callback
|
||||
* <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
|
||||
* {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
|
||||
*
|
||||
* @throws SecurityException if a callback is already registered for the calling process.
|
||||
*/
|
||||
void registerCallback(in IDeviceStateManagerCallback callback);
|
||||
|
||||
/** Returns the array of supported device state identifiers. */
|
||||
int[] getSupportedDeviceStates();
|
||||
|
||||
/**
|
||||
* Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
|
||||
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
|
||||
* call to this method.
|
||||
*
|
||||
* @param token the request token previously registered with
|
||||
* {@link #requestState(IBinder, int, int)}
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void requestState(IBinder token, int state, int flags);
|
||||
|
||||
/**
|
||||
* Cancels a request previously submitted with a call to
|
||||
* {@link #requestState(IBinder, int, int)}.
|
||||
*
|
||||
* @param token the request token previously registered with
|
||||
* {@link #requestState(IBinder, int, int)}
|
||||
*
|
||||
* @throws IllegalStateException if the supplied {@code token} has not been previously
|
||||
* requested.
|
||||
*/
|
||||
void cancelRequest(IBinder token);
|
||||
}
|
||||
|
||||
@@ -18,5 +18,42 @@ package android.hardware.devicestate;
|
||||
|
||||
/** @hide */
|
||||
interface IDeviceStateManagerCallback {
|
||||
/**
|
||||
* Called in response to a change in device state. Guaranteed to be called once with the initial
|
||||
* value on registration of the callback.
|
||||
*
|
||||
* @param deviceState the new state of the device.
|
||||
*/
|
||||
oneway void onDeviceStateChanged(int deviceState);
|
||||
|
||||
/**
|
||||
* Called to notify the callback that a request has become active. Guaranteed to be called
|
||||
* after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
|
||||
* resulted in a device state change.
|
||||
*
|
||||
* @param token the request token previously registered with
|
||||
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
|
||||
*/
|
||||
oneway void onRequestActive(IBinder token);
|
||||
|
||||
/**
|
||||
* Called to notify the callback that a request has become suspended. Guaranteed to be called
|
||||
* before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
|
||||
* suspended resulted in a device state change.
|
||||
*
|
||||
* @param token the request token previously registered with
|
||||
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
|
||||
*/
|
||||
oneway void onRequestSuspended(IBinder token);
|
||||
|
||||
/**
|
||||
* Called to notify the callback that a request has become canceled. No further callbacks will
|
||||
* be triggered for this request. Guaranteed to be called before a subsequent call to
|
||||
* {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
|
||||
* state change.
|
||||
*
|
||||
* @param token the request token previously registered with
|
||||
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
|
||||
*/
|
||||
oneway void onRequestCanceled(IBinder token);
|
||||
}
|
||||
|
||||
@@ -5427,7 +5427,7 @@
|
||||
<permission android:name="android.permission.INPUT_CONSUMER"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @hide Allows an application to control the system's device state managed by the
|
||||
<!-- @hide @TestApi Allows an application to control the system's device state managed by the
|
||||
{@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
|
||||
devices this would grant access to toggle between the folded and unfolded states. -->
|
||||
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package android.hardware.devicestate;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -30,6 +33,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -41,6 +45,9 @@ import java.util.Set;
|
||||
@RunWith(JUnit4.class)
|
||||
@SmallTest
|
||||
public final class DeviceStateManagerGlobalTest {
|
||||
private static final int DEFAULT_DEVICE_STATE = 0;
|
||||
private static final int OTHER_DEVICE_STATE = 1;
|
||||
|
||||
private TestDeviceStateManagerService mService;
|
||||
private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
|
||||
|
||||
@@ -52,7 +59,7 @@ public final class DeviceStateManagerGlobalTest {
|
||||
|
||||
@Test
|
||||
public void registerListener() {
|
||||
mService.setDeviceState(0);
|
||||
mService.setBaseState(DEFAULT_DEVICE_STATE);
|
||||
|
||||
TestDeviceStateListener listener1 = new TestDeviceStateListener();
|
||||
TestDeviceStateListener listener2 = new TestDeviceStateListener();
|
||||
@@ -61,28 +68,58 @@ public final class DeviceStateManagerGlobalTest {
|
||||
ConcurrentUtils.DIRECT_EXECUTOR);
|
||||
mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
|
||||
ConcurrentUtils.DIRECT_EXECUTOR);
|
||||
assertEquals(0, listener1.getLastReportedState().intValue());
|
||||
assertEquals(0, listener2.getLastReportedState().intValue());
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
|
||||
|
||||
mService.setDeviceState(1);
|
||||
assertEquals(1, listener1.getLastReportedState().intValue());
|
||||
assertEquals(1, listener2.getLastReportedState().intValue());
|
||||
mService.setBaseState(OTHER_DEVICE_STATE);
|
||||
assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
|
||||
assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterListener() {
|
||||
mService.setDeviceState(0);
|
||||
mService.setBaseState(DEFAULT_DEVICE_STATE);
|
||||
|
||||
TestDeviceStateListener listener = new TestDeviceStateListener();
|
||||
|
||||
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
|
||||
ConcurrentUtils.DIRECT_EXECUTOR);
|
||||
assertEquals(0, listener.getLastReportedState().intValue());
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
|
||||
|
||||
mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
|
||||
|
||||
mService.setDeviceState(1);
|
||||
assertEquals(0, listener.getLastReportedState().intValue());
|
||||
mService.setBaseState(OTHER_DEVICE_STATE);
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submittingRequestRegisteredCallback() {
|
||||
assertTrue(mService.mCallbacks.isEmpty());
|
||||
|
||||
DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
|
||||
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
|
||||
|
||||
assertFalse(mService.mCallbacks.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitRequest() {
|
||||
mService.setBaseState(DEFAULT_DEVICE_STATE);
|
||||
|
||||
TestDeviceStateListener listener = new TestDeviceStateListener();
|
||||
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
|
||||
ConcurrentUtils.DIRECT_EXECUTOR);
|
||||
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
|
||||
|
||||
DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
|
||||
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
|
||||
|
||||
assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
|
||||
|
||||
mDeviceStateManagerGlobal.cancelRequest(request);
|
||||
|
||||
assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
|
||||
}
|
||||
|
||||
private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
|
||||
@@ -100,8 +137,23 @@ public final class DeviceStateManagerGlobalTest {
|
||||
}
|
||||
}
|
||||
|
||||
private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
|
||||
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
|
||||
public static final class Request {
|
||||
public final IBinder token;
|
||||
public final int state;
|
||||
public final int flags;
|
||||
|
||||
private Request(IBinder token, int state, int flags) {
|
||||
this.token = token;
|
||||
this.state = state;
|
||||
this.flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
private int mBaseState = DEFAULT_DEVICE_STATE;
|
||||
private int mMergedState = DEFAULT_DEVICE_STATE;
|
||||
private ArrayList<Request> mRequests = new ArrayList<>();
|
||||
|
||||
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
|
||||
|
||||
@Override
|
||||
@@ -112,19 +164,86 @@ public final class DeviceStateManagerGlobalTest {
|
||||
|
||||
mCallbacks.add(callback);
|
||||
try {
|
||||
callback.onDeviceStateChanged(mDeviceState);
|
||||
callback.onDeviceStateChanged(mMergedState);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing. Should never happen.
|
||||
}
|
||||
}
|
||||
|
||||
public void setDeviceState(int deviceState) {
|
||||
boolean stateChanged = mDeviceState != deviceState;
|
||||
mDeviceState = deviceState;
|
||||
if (stateChanged) {
|
||||
@Override
|
||||
public int[] getSupportedDeviceStates() {
|
||||
return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestState(IBinder token, int state, int flags) {
|
||||
if (!mRequests.isEmpty()) {
|
||||
final Request topRequest = mRequests.get(mRequests.size() - 1);
|
||||
for (IDeviceStateManagerCallback callback : mCallbacks) {
|
||||
try {
|
||||
callback.onDeviceStateChanged(mDeviceState);
|
||||
callback.onRequestSuspended(topRequest.token);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing. Should never happen.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Request request = new Request(token, state, flags);
|
||||
mRequests.add(request);
|
||||
notifyStateChangedIfNeeded();
|
||||
|
||||
for (IDeviceStateManagerCallback callback : mCallbacks) {
|
||||
try {
|
||||
callback.onRequestActive(token);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing. Should never happen.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelRequest(IBinder token) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < mRequests.size(); i++) {
|
||||
if (mRequests.get(i).token.equals(token)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Unknown request: " + token);
|
||||
}
|
||||
|
||||
mRequests.remove(index);
|
||||
for (IDeviceStateManagerCallback callback : mCallbacks) {
|
||||
try {
|
||||
callback.onRequestCanceled(token);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing. Should never happen.
|
||||
}
|
||||
}
|
||||
notifyStateChangedIfNeeded();
|
||||
}
|
||||
|
||||
public void setBaseState(int state) {
|
||||
mBaseState = state;
|
||||
notifyStateChangedIfNeeded();
|
||||
}
|
||||
|
||||
private void notifyStateChangedIfNeeded() {
|
||||
final int originalMergedState = mMergedState;
|
||||
|
||||
if (!mRequests.isEmpty()) {
|
||||
mMergedState = mRequests.get(mRequests.size() - 1).state;
|
||||
} else {
|
||||
mMergedState = mBaseState;
|
||||
}
|
||||
|
||||
if (mMergedState != originalMergedState) {
|
||||
for (IDeviceStateManagerCallback callback : mCallbacks) {
|
||||
try {
|
||||
callback.onDeviceStateChanged(mMergedState);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing. Should never happen.
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.server.devicestate;
|
||||
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
|
||||
@@ -37,16 +35,16 @@ import java.util.Objects;
|
||||
*/
|
||||
public final class DeviceState {
|
||||
/** Unique identifier for the device state. */
|
||||
@IntRange(from = INVALID_DEVICE_STATE)
|
||||
@IntRange(from = 0)
|
||||
private final int mIdentifier;
|
||||
|
||||
/** String description of the device state. */
|
||||
@NonNull
|
||||
private final String mName;
|
||||
|
||||
public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
|
||||
public DeviceState(@IntRange(from = 0) int identifier,
|
||||
@NonNull String name) {
|
||||
if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
|
||||
if (identifier < 0) {
|
||||
throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
|
||||
}
|
||||
mIdentifier = identifier;
|
||||
@@ -54,7 +52,7 @@ public final class DeviceState {
|
||||
}
|
||||
|
||||
/** Returns the unique identifier for the device state. */
|
||||
@IntRange(from = INVALID_DEVICE_STATE)
|
||||
@IntRange(from = 0)
|
||||
public int getIdentifier() {
|
||||
return mIdentifier;
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
package com.android.server.devicestate;
|
||||
|
||||
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.hardware.devicestate.IDeviceStateManager;
|
||||
import android.hardware.devicestate.IDeviceStateManagerCallback;
|
||||
import android.os.Binder;
|
||||
@@ -31,6 +31,8 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -62,8 +64,12 @@ import java.util.Optional;
|
||||
* the {@link DeviceStateProvider} to modify the current device state and communicating with the
|
||||
* {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
|
||||
* </p>
|
||||
* The service also provides the {@link DeviceStateManager} API allowing clients to listen for
|
||||
* changes in device state and submit requests to override the device state provided by the
|
||||
* {@link DeviceStateProvider}.
|
||||
*
|
||||
* @see DeviceStatePolicy
|
||||
* @see DeviceStateManager
|
||||
*/
|
||||
public final class DeviceStateManagerService extends SystemService {
|
||||
private static final String TAG = "DeviceStateManagerService";
|
||||
@@ -79,11 +85,11 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
@GuardedBy("mLock")
|
||||
private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
|
||||
|
||||
// The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
|
||||
// The current committed device state. The default of UNSET will be replaced by
|
||||
// the current state after the initial callback from the DeviceStateProvider.
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
|
||||
private DeviceState mCommittedState = new DeviceState(0, "UNSET");
|
||||
// The device state that is currently awaiting callback from the policy to be committed.
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
@@ -91,19 +97,23 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
// Whether or not the policy is currently waiting to be notified of the current pending state.
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsPolicyWaitingForState = false;
|
||||
// The device state that is currently requested and is next to be configured and committed.
|
||||
// Can be overwritten by an override state value if requested.
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private Optional<DeviceState> mRequestedState = Optional.empty();
|
||||
// The most recently requested override state, or empty if no override is requested.
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
|
||||
|
||||
// List of registered callbacks indexed by process id.
|
||||
// The device state that is set by the device state provider.
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
|
||||
@NonNull
|
||||
private Optional<DeviceState> mBaseState = Optional.empty();
|
||||
|
||||
// List of processes registered to receive notifications about changes to device state and
|
||||
// request status indexed by process id.
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
|
||||
// List of override requests with the highest precedence request at the end.
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
|
||||
// Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
|
||||
// of a change in status.
|
||||
@GuardedBy("mLock")
|
||||
private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
|
||||
|
||||
public DeviceStateManagerService(@NonNull Context context) {
|
||||
this(context, new DeviceStatePolicyImpl(context));
|
||||
@@ -148,55 +158,32 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested state. The service will configure the device to match the requested
|
||||
* state when possible.
|
||||
* Returns the base state. The service will configure the device to match the base state when
|
||||
* there is no active request to override the base state.
|
||||
*
|
||||
* @see #getOverrideState()
|
||||
*/
|
||||
@NonNull
|
||||
Optional<DeviceState> getRequestedState() {
|
||||
Optional<DeviceState> getBaseState() {
|
||||
synchronized (mLock) {
|
||||
return mRequestedState;
|
||||
return mBaseState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the current device state with the provided state.
|
||||
*
|
||||
* @return {@code true} if the override state is valid and supported, {@code false} otherwise.
|
||||
*/
|
||||
boolean setOverrideState(int overrideState) {
|
||||
if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRequestedOverrideState = getStateLocked(overrideState);
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an override state set with {@link #setOverrideState(int)}.
|
||||
*/
|
||||
void clearOverrideState() {
|
||||
setOverrideState(INVALID_DEVICE_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current requested override state, or {@link Optional#empty()} if no override
|
||||
* state is requested.
|
||||
* Returns the current override state, or {@link Optional#empty()} if no override state is
|
||||
* requested. If an override states is present, the returned state will take precedence over
|
||||
* the base state returned from {@link #getBaseState()}.
|
||||
*/
|
||||
@NonNull
|
||||
Optional<DeviceState> getOverrideState() {
|
||||
synchronized (mLock) {
|
||||
return mRequestedOverrideState;
|
||||
if (mRequestRecords.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
|
||||
return Optional.of(topRequest.mRequestedState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +198,17 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the list of currently supported device state identifiers. */
|
||||
private int[] getSupportedStateIdentifiers() {
|
||||
synchronized (mLock) {
|
||||
int[] supportedStates = new int[mDeviceStates.size()];
|
||||
for (int i = 0; i < supportedStates.length; i++) {
|
||||
supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
|
||||
}
|
||||
return supportedStates;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
IDeviceStateManager getBinderService() {
|
||||
return mBinderService;
|
||||
@@ -224,22 +222,26 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
mDeviceStates.put(state.getIdentifier(), state);
|
||||
}
|
||||
|
||||
if (mRequestedState.isPresent()
|
||||
&& !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
|
||||
// The current requested state is no longer valid. We'll clear it here, though
|
||||
if (mBaseState.isPresent()
|
||||
&& !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
|
||||
// The current base state is no longer valid. We'll clear it here, though
|
||||
// we won't actually update the current state until a callback comes from the
|
||||
// provider with the most recent state.
|
||||
mRequestedState = Optional.empty();
|
||||
mBaseState = Optional.empty();
|
||||
}
|
||||
if (mRequestedOverrideState.isPresent()
|
||||
&& !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
|
||||
// The current override state is no longer valid. We'll clear it here and update
|
||||
// the committed state if necessary.
|
||||
mRequestedOverrideState = Optional.empty();
|
||||
|
||||
final int requestSize = mRequestRecords.size();
|
||||
for (int i = 0; i < requestSize; i++) {
|
||||
OverrideRequestRecord request = mRequestRecords.get(i);
|
||||
if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
|
||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyRequestsOfStatusChangeIfNeeded();
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
@@ -261,20 +263,37 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the system enter the provided {@code state}. The request may not be honored
|
||||
* under certain conditions, for example if the provided state is not supported.
|
||||
* Requests to set the base state. The request may not be honored under certain conditions, for
|
||||
* example if the provided state is not supported.
|
||||
*
|
||||
* @see #isSupportedStateLocked(int)
|
||||
*/
|
||||
private void requestState(int identifier) {
|
||||
private void setBaseState(int identifier) {
|
||||
synchronized (mLock) {
|
||||
final Optional<DeviceState> requestedState = getStateLocked(identifier);
|
||||
if (requestedState.isPresent()) {
|
||||
mRequestedState = requestedState;
|
||||
if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
|
||||
// Base state hasn't changed. Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<DeviceState> baseState = getStateLocked(identifier);
|
||||
if (!baseState.isPresent()) {
|
||||
throw new IllegalArgumentException("Base state is not supported");
|
||||
}
|
||||
|
||||
mBaseState = baseState;
|
||||
|
||||
final int requestSize = mRequestRecords.size();
|
||||
for (int i = 0; i < requestSize; i++) {
|
||||
OverrideRequestRecord request = mRequestRecords.get(i);
|
||||
if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
|
||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyRequestsOfStatusChangeIfNeeded();
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
@@ -290,10 +309,10 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
|
||||
final DeviceState stateToConfigure;
|
||||
if (mRequestedOverrideState.isPresent()) {
|
||||
stateToConfigure = mRequestedOverrideState.get();
|
||||
if (!mRequestRecords.isEmpty()) {
|
||||
stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
|
||||
} else {
|
||||
stateToConfigure = mRequestedState.orElse(null);
|
||||
stateToConfigure = mBaseState.orElse(null);
|
||||
}
|
||||
|
||||
if (stateToConfigure == null) {
|
||||
@@ -360,6 +379,13 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
}
|
||||
mCommittedState = mPendingState.get();
|
||||
newState = mCommittedState.getIdentifier();
|
||||
|
||||
if (!mRequestRecords.isEmpty()) {
|
||||
final OverrideRequestRecord topRequest =
|
||||
mRequestRecords.get(mRequestRecords.size() - 1);
|
||||
topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
mPendingState = Optional.empty();
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
@@ -367,6 +393,9 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
// Notify callbacks of a change.
|
||||
notifyDeviceStateChanged(newState);
|
||||
|
||||
// Notify the top request that it's active.
|
||||
notifyRequestsOfStatusChangeIfNeeded();
|
||||
|
||||
// Try to configure the next state if needed.
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
@@ -377,43 +406,69 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
"Attempting to notify callbacks with service lock held.");
|
||||
}
|
||||
|
||||
// Grab the lock and copy the callbacks.
|
||||
ArrayList<CallbackRecord> callbacks;
|
||||
// Grab the lock and copy the process records.
|
||||
ArrayList<ProcessRecord> registeredProcesses;
|
||||
synchronized (mLock) {
|
||||
if (mCallbacks.size() == 0) {
|
||||
if (mProcessRecords.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks = new ArrayList<>();
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
callbacks.add(mCallbacks.valueAt(i));
|
||||
registeredProcesses = new ArrayList<>();
|
||||
for (int i = 0; i < mProcessRecords.size(); i++) {
|
||||
registeredProcesses.add(mProcessRecords.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
// After releasing the lock, send the notifications out.
|
||||
for (int i = 0; i < callbacks.size(); i++) {
|
||||
callbacks.get(i).notifyDeviceStateAsync(deviceState);
|
||||
for (int i = 0; i < registeredProcesses.size(); i++) {
|
||||
registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
|
||||
/**
|
||||
* Notifies all dirty requests (requests that have a change in status, but have not yet been
|
||||
* notified) that their status has changed.
|
||||
*/
|
||||
private void notifyRequestsOfStatusChangeIfNeeded() {
|
||||
if (Thread.holdsLock(mLock)) {
|
||||
throw new IllegalStateException(
|
||||
"Attempting to notify requests with service lock held.");
|
||||
}
|
||||
|
||||
ArraySet<OverrideRequestRecord> dirtyRequests;
|
||||
synchronized (mLock) {
|
||||
if (mRequestsPendingStatusChange.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
|
||||
mRequestsPendingStatusChange.clear();
|
||||
}
|
||||
|
||||
// After releasing the lock, send the notifications out.
|
||||
for (int i = 0; i < dirtyRequests.size(); i++) {
|
||||
dirtyRequests.valueAt(i).notifyStatusIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
|
||||
int currentState;
|
||||
CallbackRecord record;
|
||||
ProcessRecord record;
|
||||
// Grab the lock to register the callback and get the current state.
|
||||
synchronized (mLock) {
|
||||
if (mCallbacks.contains(callingPid)) {
|
||||
if (mProcessRecords.contains(pid)) {
|
||||
throw new SecurityException("The calling process has already registered an"
|
||||
+ " IDeviceStateManagerCallback.");
|
||||
}
|
||||
|
||||
record = new CallbackRecord(callback, callingPid);
|
||||
record = new ProcessRecord(callback, pid);
|
||||
try {
|
||||
callback.asBinder().linkToDeath(record, 0);
|
||||
} catch (RemoteException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
mCallbacks.put(callingPid, record);
|
||||
mProcessRecords.put(pid, record);
|
||||
currentState = mCommittedState.getIdentifier();
|
||||
}
|
||||
|
||||
@@ -421,10 +476,86 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
record.notifyDeviceStateAsync(currentState);
|
||||
}
|
||||
|
||||
private void unregisterCallbackInternal(CallbackRecord record) {
|
||||
private void handleProcessDied(ProcessRecord processRecord) {
|
||||
synchronized (mLock) {
|
||||
mCallbacks.remove(record.mPid);
|
||||
// Cancel all requests from this process.
|
||||
final int requestCount = processRecord.mRequestRecords.size();
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
|
||||
// Cancel the request but don't mark it as dirty since there's no need to send
|
||||
// notifications if the process has died.
|
||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
|
||||
false /* markDirty */);
|
||||
}
|
||||
|
||||
mProcessRecords.remove(processRecord.mPid);
|
||||
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
private void requestStateInternal(int state, int flags, int callingPid,
|
||||
@NonNull IBinder token) {
|
||||
synchronized (mLock) {
|
||||
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
|
||||
if (processRecord == null) {
|
||||
throw new IllegalStateException("Process " + callingPid
|
||||
+ " has no registered callback.");
|
||||
}
|
||||
|
||||
if (processRecord.mRequestRecords.get(token) != null) {
|
||||
throw new IllegalStateException("Request has already been made for the supplied"
|
||||
+ " token: " + token);
|
||||
}
|
||||
|
||||
final Optional<DeviceState> deviceState = getStateLocked(state);
|
||||
if (!deviceState.isPresent()) {
|
||||
throw new IllegalArgumentException("Requested state: " + state
|
||||
+ " is not supported.");
|
||||
}
|
||||
|
||||
OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
|
||||
? null : mRequestRecords.get(mRequestRecords.size() - 1);
|
||||
if (topRecord != null) {
|
||||
topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
|
||||
}
|
||||
|
||||
final OverrideRequestRecord request =
|
||||
new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
|
||||
mRequestRecords.add(request);
|
||||
processRecord.mRequestRecords.put(request.mToken, request);
|
||||
// We don't set the status of the new request to ACTIVE here as it will be set in
|
||||
// commitPendingState().
|
||||
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyRequestsOfStatusChangeIfNeeded();
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
|
||||
synchronized (mLock) {
|
||||
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
|
||||
if (processRecord == null) {
|
||||
throw new IllegalStateException("Process " + callingPid
|
||||
+ " has no registered callback.");
|
||||
}
|
||||
|
||||
OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
|
||||
if (request == null) {
|
||||
throw new IllegalStateException("No known request for the given token");
|
||||
}
|
||||
|
||||
request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
|
||||
|
||||
updatePendingStateLocked();
|
||||
}
|
||||
|
||||
notifyRequestsOfStatusChangeIfNeeded();
|
||||
notifyPolicyIfNeeded();
|
||||
}
|
||||
|
||||
private void dumpInternal(PrintWriter pw) {
|
||||
@@ -433,15 +564,26 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
pw.println(" mCommittedState=" + mCommittedState);
|
||||
pw.println(" mPendingState=" + mPendingState);
|
||||
pw.println(" mRequestedState=" + mRequestedState);
|
||||
pw.println(" mRequestedOverrideState=" + mRequestedOverrideState);
|
||||
pw.println(" mBaseState=" + mBaseState);
|
||||
pw.println(" mOverrideState=" + getOverrideState());
|
||||
|
||||
final int callbackCount = mCallbacks.size();
|
||||
final int processCount = mProcessRecords.size();
|
||||
pw.println();
|
||||
pw.println("Callbacks: size=" + callbackCount);
|
||||
for (int i = 0; i < callbackCount; i++) {
|
||||
CallbackRecord callback = mCallbacks.valueAt(i);
|
||||
pw.println(" " + i + ": mPid=" + callback.mPid);
|
||||
pw.println("Registered processes: size=" + processCount);
|
||||
for (int i = 0; i < processCount; i++) {
|
||||
ProcessRecord processRecord = mProcessRecords.valueAt(i);
|
||||
pw.println(" " + i + ": mPid=" + processRecord.mPid);
|
||||
}
|
||||
|
||||
final int requestCount = mRequestRecords.size();
|
||||
pw.println();
|
||||
pw.println("Override requests: size=" + requestCount);
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
OverrideRequestRecord requestRecord = mRequestRecords.get(i);
|
||||
pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
|
||||
+ ", mRequestedState=" + requestRecord.mRequestedState
|
||||
+ ", mFlags=" + requestRecord.mFlags
|
||||
+ ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,12 +594,6 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
if (newDeviceStates.length == 0) {
|
||||
throw new IllegalArgumentException("Supported device states must not be empty");
|
||||
}
|
||||
for (int i = 0; i < newDeviceStates.length; i++) {
|
||||
if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Supported device states includes INVALID_DEVICE_STATE identifier");
|
||||
}
|
||||
}
|
||||
updateSupportedStates(newDeviceStates);
|
||||
}
|
||||
|
||||
@@ -467,22 +603,24 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
throw new IllegalArgumentException("Invalid identifier: " + identifier);
|
||||
}
|
||||
|
||||
requestState(identifier);
|
||||
setBaseState(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
private final class CallbackRecord implements IBinder.DeathRecipient {
|
||||
private final class ProcessRecord implements IBinder.DeathRecipient {
|
||||
private final IDeviceStateManagerCallback mCallback;
|
||||
private final int mPid;
|
||||
|
||||
CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
|
||||
private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
|
||||
|
||||
ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
|
||||
mCallback = callback;
|
||||
mPid = pid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
unregisterCallbackInternal(this);
|
||||
handleProcessDied(this);
|
||||
}
|
||||
|
||||
public void notifyDeviceStateAsync(int devicestate) {
|
||||
@@ -493,6 +631,119 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyRequestActiveAsync(OverrideRequestRecord request) {
|
||||
try {
|
||||
mCallback.onRequestActive(request.mToken);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
|
||||
try {
|
||||
mCallback.onRequestSuspended(request.mToken);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
|
||||
try {
|
||||
mCallback.onRequestCanceled(request.mToken);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A record describing a request to override the state of the device. */
|
||||
private final class OverrideRequestRecord {
|
||||
public static final int STATUS_UNKNOWN = 0;
|
||||
public static final int STATUS_ACTIVE = 1;
|
||||
public static final int STATUS_SUSPENDED = 2;
|
||||
public static final int STATUS_CANCELED = 3;
|
||||
|
||||
@Nullable
|
||||
public String statusToString(int status) {
|
||||
switch (status) {
|
||||
case STATUS_ACTIVE:
|
||||
return "ACTIVE";
|
||||
case STATUS_SUSPENDED:
|
||||
return "SUSPENDED";
|
||||
case STATUS_CANCELED:
|
||||
return "CANCELED";
|
||||
case STATUS_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final ProcessRecord mProcessRecord;
|
||||
@NonNull
|
||||
private final IBinder mToken;
|
||||
@NonNull
|
||||
private final DeviceState mRequestedState;
|
||||
private final int mFlags;
|
||||
|
||||
private int mStatus = STATUS_UNKNOWN;
|
||||
private int mLastNotifiedStatus = STATUS_UNKNOWN;
|
||||
|
||||
OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
|
||||
@NonNull DeviceState requestedState, int flags) {
|
||||
mProcessRecord = processRecord;
|
||||
mToken = token;
|
||||
mRequestedState = requestedState;
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
public void setStatusLocked(int status) {
|
||||
setStatusLocked(status, true /* markDirty */);
|
||||
}
|
||||
|
||||
public void setStatusLocked(int status, boolean markDirty) {
|
||||
if (mStatus != status) {
|
||||
if (mStatus == STATUS_CANCELED) {
|
||||
throw new IllegalStateException(
|
||||
"Can not alter the status of a request after set to CANCELED.");
|
||||
}
|
||||
|
||||
mStatus = status;
|
||||
|
||||
if (mStatus == STATUS_CANCELED) {
|
||||
mRequestRecords.remove(this);
|
||||
mProcessRecord.mRequestRecords.remove(mToken);
|
||||
}
|
||||
|
||||
if (markDirty) {
|
||||
mRequestsPendingStatusChange.add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyStatusIfNeeded() {
|
||||
int stateToReport;
|
||||
synchronized (mLock) {
|
||||
if (mLastNotifiedStatus == mStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
stateToReport = mStatus;
|
||||
mLastNotifiedStatus = mStatus;
|
||||
}
|
||||
|
||||
if (stateToReport == STATUS_ACTIVE) {
|
||||
mProcessRecord.notifyRequestActiveAsync(this);
|
||||
} else if (stateToReport == STATUS_SUSPENDED) {
|
||||
mProcessRecord.notifyRequestSuspendedAsync(this);
|
||||
} else if (stateToReport == STATUS_CANCELED) {
|
||||
mProcessRecord.notifyRequestCanceledAsync(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
|
||||
@@ -506,12 +757,58 @@ public final class DeviceStateManagerService extends SystemService {
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
registerCallbackInternal(callback, callingPid);
|
||||
registerProcess(callingPid, callback);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public int[] getSupportedDeviceStates() {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return getSupportedStateIdentifiers();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void requestState(IBinder token, int state, int flags) {
|
||||
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
||||
"Permission required to request device state.");
|
||||
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("Request token must not be null.");
|
||||
}
|
||||
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
requestStateInternal(state, flags, callingPid, token);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void cancelRequest(IBinder token) {
|
||||
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
|
||||
"Permission required to clear requested device state.");
|
||||
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("Request token must not be null.");
|
||||
}
|
||||
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
cancelRequestInternal(callingPid, token);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
|
||||
String[] args, ShellCallback callback, ResultReceiver result) {
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
package com.android.server.devicestate;
|
||||
|
||||
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.hardware.devicestate.DeviceStateRequest;
|
||||
import android.os.Binder;
|
||||
import android.os.ShellCommand;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -27,10 +34,15 @@ import java.util.Optional;
|
||||
* Use with {@code adb shell cmd device_state ...}.
|
||||
*/
|
||||
public class DeviceStateManagerShellCommand extends ShellCommand {
|
||||
private final DeviceStateManagerService mInternal;
|
||||
@Nullable
|
||||
private static DeviceStateRequest sLastRequest;
|
||||
|
||||
private final DeviceStateManagerService mService;
|
||||
private final DeviceStateManager mClient;
|
||||
|
||||
public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
|
||||
mInternal = service;
|
||||
mService = service;
|
||||
mClient = service.getContext().getSystemService(DeviceStateManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,15 +63,15 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
|
||||
private void printState(PrintWriter pw) {
|
||||
DeviceState committedState = mInternal.getCommittedState();
|
||||
Optional<DeviceState> requestedState = mInternal.getRequestedState();
|
||||
Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
|
||||
DeviceState committedState = mService.getCommittedState();
|
||||
Optional<DeviceState> baseState = mService.getBaseState();
|
||||
Optional<DeviceState> overrideState = mService.getOverrideState();
|
||||
|
||||
pw.println("Committed state: " + committedState);
|
||||
if (requestedOverrideState.isPresent()) {
|
||||
if (overrideState.isPresent()) {
|
||||
pw.println("----------------------");
|
||||
pw.println("Base state: " + requestedState.orElse(null));
|
||||
pw.println("Override state: " + requestedOverrideState.get());
|
||||
pw.println("Base state: " + baseState.orElse(null));
|
||||
pw.println("Override state: " + overrideState.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,32 +79,51 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
|
||||
final String nextArg = getNextArg();
|
||||
if (nextArg == null) {
|
||||
printState(pw);
|
||||
} else if ("reset".equals(nextArg)) {
|
||||
mInternal.clearOverrideState();
|
||||
} else {
|
||||
int requestedState;
|
||||
try {
|
||||
requestedState = Integer.parseInt(nextArg);
|
||||
} catch (NumberFormatException e) {
|
||||
getErrPrintWriter().println("Error: requested state should be an integer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean success = mInternal.setOverrideState(requestedState);
|
||||
if (!success) {
|
||||
getErrPrintWriter().println("Error: failed to set override state. Run:");
|
||||
getErrPrintWriter().println("");
|
||||
getErrPrintWriter().println(" print-states");
|
||||
getErrPrintWriter().println("");
|
||||
getErrPrintWriter().println("to get the list of currently supported device states");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (sLastRequest != null) {
|
||||
mClient.cancelRequest(sLastRequest);
|
||||
sLastRequest = null;
|
||||
}
|
||||
} else {
|
||||
int requestedState = Integer.parseInt(nextArg);
|
||||
DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
|
||||
|
||||
mClient.requestState(request, null /* executor */, null /* callback */);
|
||||
if (sLastRequest != null) {
|
||||
mClient.cancelRequest(sLastRequest);
|
||||
}
|
||||
|
||||
sLastRequest = 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 runPrintStates(PrintWriter pw) {
|
||||
DeviceState[] states = mInternal.getSupportedStates();
|
||||
DeviceState[] states = mService.getSupportedStates();
|
||||
pw.print("Supported states: [\n");
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
pw.print(" " + states[i] + ",\n");
|
||||
|
||||
@@ -514,8 +514,8 @@ public final class DisplayManagerService extends SystemService {
|
||||
|
||||
DeviceStateManager deviceStateManager =
|
||||
mContext.getSystemService(DeviceStateManager.class);
|
||||
deviceStateManager.registerDeviceStateListener(new DeviceStateListener(),
|
||||
new HandlerExecutor(mHandler));
|
||||
deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
|
||||
new DeviceStateListener());
|
||||
|
||||
scheduleTraversalLocked(false);
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ class DisplayFoldController {
|
||||
mHandler = handler;
|
||||
|
||||
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
|
||||
deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context),
|
||||
new HandlerExecutor(handler));
|
||||
deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
|
||||
new DeviceStateListener(context));
|
||||
}
|
||||
|
||||
void finishedGoingToSleep() {
|
||||
|
||||
@@ -19,10 +19,14 @@ package com.android.server.devicestate;
|
||||
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.hardware.devicestate.DeviceStateRequest;
|
||||
import android.hardware.devicestate.IDeviceStateManagerCallback;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
@@ -33,6 +37,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -61,87 +66,69 @@ public final class DeviceStateManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange() {
|
||||
public void baseStateChanged() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_pendingState() {
|
||||
public void baseStateChanged_withStatePendingPolicyCallback() {
|
||||
mPolicy.blockConfigure();
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mPolicy.resumeConfigure();
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_unsupportedState() {
|
||||
mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
|
||||
public void baseStateChanged_unsupportedState() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
|
||||
});
|
||||
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestStateChange_invalidState() {
|
||||
public void baseStateChanged_invalidState() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
mProvider.notifyRequestState(INVALID_DEVICE_STATE);
|
||||
mProvider.setState(INVALID_DEVICE_STATE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestOverrideState() {
|
||||
mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
// Committed state is set back to the requested state once the override is cleared.
|
||||
mService.clearOverrideState();
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestOverrideState_unsupportedState() {
|
||||
mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
|
||||
// Committed state remains the same as the override state is unsupported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
@@ -150,7 +137,7 @@ public final class DeviceStateManagerServiceTest {
|
||||
public void supportedStatesChanged() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
||||
|
||||
@@ -158,46 +145,27 @@ public final class DeviceStateManagerServiceTest {
|
||||
// supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged_unsupportedRequestedState() {
|
||||
public void supportedStatesChanged_unsupportedBaseState() {
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
|
||||
|
||||
// The current requested state is cleared because it is no longer supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState(), Optional.empty());
|
||||
assertEquals(mService.getBaseState(), Optional.empty());
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getPendingState(), Optional.empty());
|
||||
assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportedStatesChanged_unsupportedOverrideState() {
|
||||
mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
||||
|
||||
// Committed state is set back to the requested state as the override state is no longer
|
||||
// supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,17 +173,17 @@ public final class DeviceStateManagerServiceTest {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
assertNotNull(callback.getLastNotifiedValue());
|
||||
assertEquals(callback.getLastNotifiedValue().intValue(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
assertEquals(callback.getLastNotifiedValue().intValue(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mPolicy.blockConfigure();
|
||||
mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
// The callback should not have been notified of the state change as the policy is still
|
||||
// pending callback.
|
||||
assertEquals(callback.getLastNotifiedValue().intValue(),
|
||||
@@ -237,6 +205,148 @@ public final class DeviceStateManagerServiceTest {
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSupportedDeviceStates() throws RemoteException {
|
||||
final int[] expectedStates = new int[] { 0, 1 };
|
||||
assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState() throws RemoteException {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
final IBinder token = new Binder();
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_UNKNOWN);
|
||||
|
||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||
0 /* flags */);
|
||||
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
|
||||
mService.getBinderService().cancelRequest(token);
|
||||
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_CANCELED);
|
||||
// Committed state is set back to the requested state once the override is cleared.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertFalse(mService.getOverrideState().isPresent());
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
final IBinder token = new Binder();
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_UNKNOWN);
|
||||
|
||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
|
||||
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
// Request is canceled because the base state changed.
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_CANCELED);
|
||||
// Committed state is set back to the requested state once the override is cleared.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
|
||||
assertFalse(mService.getOverrideState().isPresent());
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState_becomesUnsupported() throws RemoteException {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
final IBinder token = new Binder();
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_UNKNOWN);
|
||||
|
||||
mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
|
||||
0 /* flags */);
|
||||
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_ACTIVE);
|
||||
// Committed state changes as there is a requested override.
|
||||
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
OTHER_DEVICE_STATE.getIdentifier());
|
||||
|
||||
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
|
||||
|
||||
// Request is canceled because the state is no longer supported.
|
||||
assertEquals(callback.getLastNotifiedStatus(token),
|
||||
TestDeviceStateManagerCallback.STATUS_CANCELED);
|
||||
// Committed state is set back to the requested state as the override state is no longer
|
||||
// supported.
|
||||
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
|
||||
assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
|
||||
assertFalse(mService.getOverrideState().isPresent());
|
||||
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
|
||||
DEFAULT_DEVICE_STATE.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState_unsupportedState() throws RemoteException {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
final IBinder token = new Binder();
|
||||
mService.getBinderService().requestState(token,
|
||||
UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState_invalidState() throws RemoteException {
|
||||
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
|
||||
mService.getBinderService().registerCallback(callback);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
final IBinder token = new Binder();
|
||||
mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestState_beforeRegisteringCallback() {
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
final IBinder token = new Binder();
|
||||
mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
|
||||
0 /* flags */);
|
||||
});
|
||||
}
|
||||
|
||||
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
|
||||
private final DeviceStateProvider mProvider;
|
||||
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
|
||||
@@ -306,23 +416,48 @@ public final class DeviceStateManagerServiceTest {
|
||||
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
|
||||
}
|
||||
|
||||
public void notifyRequestState(int identifier) {
|
||||
public void setState(int identifier) {
|
||||
mListener.onStateChanged(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestDeviceStateManagerCallback extends
|
||||
IDeviceStateManagerCallback.Stub {
|
||||
Integer mLastNotifiedValue;
|
||||
public static final int STATUS_UNKNOWN = 0;
|
||||
public static final int STATUS_ACTIVE = 1;
|
||||
public static final int STATUS_SUSPENDED = 2;
|
||||
public static final int STATUS_CANCELED = 3;
|
||||
|
||||
private Integer mLastNotifiedValue;
|
||||
private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onDeviceStateChanged(int deviceState) {
|
||||
mLastNotifiedValue = deviceState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestActive(IBinder token) {
|
||||
mLastNotifiedStatus.put(token, STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestSuspended(IBinder token) {
|
||||
mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCanceled(IBinder token) {
|
||||
mLastNotifiedStatus.put(token, STATUS_CANCELED);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer getLastNotifiedValue() {
|
||||
return mLastNotifiedValue;
|
||||
}
|
||||
|
||||
int getLastNotifiedStatus(IBinder requestToken) {
|
||||
return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user