Loading core/api/test-current.txt +35 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 { Loading core/java/android/hardware/devicestate/DeviceStateManager.java +67 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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); } Loading core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +189 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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)); } } } core/java/android/hardware/devicestate/DeviceStateRequest.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * 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; } } core/java/android/hardware/devicestate/IDeviceStateManager.aidl +40 −0 Original line number Diff line number Diff line Loading @@ -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); } Loading
core/api/test-current.txt +35 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 { Loading
core/java/android/hardware/devicestate/DeviceStateManager.java +67 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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); } Loading
core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +189 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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)); } } }
core/java/android/hardware/devicestate/DeviceStateRequest.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * 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; } }
core/java/android/hardware/devicestate/IDeviceStateManager.aidl +40 −0 Original line number Diff line number Diff line Loading @@ -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); }