Loading core/api/test-current.txt +6 −4 Original line number Diff line number Diff line Loading @@ -1021,17 +1021,19 @@ 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 public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); 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); method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0 } public static interface DeviceStateManager.DeviceStateListener { method public void onDeviceStateChanged(int); public static interface DeviceStateManager.DeviceStateCallback { method public default void onBaseStateChanged(int); method public void onStateChanged(int); method public default void onSupportedStatesChanged(@NonNull int[]); } public final class DeviceStateRequest { Loading core/java/android/hardware/devicestate/DeviceStateInfo.aidl 0 → 100644 +19 −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; parcelable DeviceStateInfo; core/java/android/hardware/devicestate/DeviceStateInfo.java 0 → 100644 +162 −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.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; /** * Information about the state of the device. * * @hide */ public final class DeviceStateInfo implements Parcelable { /** Bit that indicates the {@link #supportedStates} field has changed. */ public static final int CHANGED_SUPPORTED_STATES = 1 << 0; /** Bit that indicates the {@link #baseState} field has changed. */ public static final int CHANGED_BASE_STATE = 1 << 1; /** Bit that indicates the {@link #currentState} field has changed. */ public static final int CHANGED_CURRENT_STATE = 1 << 2; @IntDef(prefix = {"CHANGED_"}, flag = true, value = { CHANGED_SUPPORTED_STATES, CHANGED_BASE_STATE, CHANGED_CURRENT_STATE, }) @Retention(RetentionPolicy.SOURCE) public @interface ChangeFlags {} /** * The list of states supported by the device. */ @NonNull public final int[] supportedStates; /** * The base (non-override) state of the device. The base state is the state of the device * ignoring any override requests made through a call to {@link DeviceStateManager#requestState( * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. */ public final int baseState; /** * The state of the device. */ public final int currentState; /** * Creates a new instance of {@link DeviceStateInfo}. * <p> * NOTE: Unlike {@link #DeviceStateInfo(DeviceStateInfo)}, this constructor does not copy the * supplied parameters. */ public DeviceStateInfo(@NonNull int[] supportedStates, int baseState, int state) { this.supportedStates = supportedStates; this.baseState = baseState; this.currentState = state; } /** * Creates a new instance of {@link DeviceStateInfo} copying the fields of {@code info} into * the fields of the returned instance. */ public DeviceStateInfo(@NonNull DeviceStateInfo info) { this(Arrays.copyOf(info.supportedStates, info.supportedStates.length), info.baseState, info.currentState); } @Override public boolean equals(@Nullable Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; DeviceStateInfo that = (DeviceStateInfo) other; return baseState == that.baseState && currentState == that.currentState && Arrays.equals(supportedStates, that.supportedStates); } @Override public int hashCode() { int result = Objects.hash(baseState, currentState); result = 31 * result + Arrays.hashCode(supportedStates); return result; } /** Returns a bitmask of the differences between this instance and {@code other}. */ @ChangeFlags public int diff(@NonNull DeviceStateInfo other) { int diff = 0; if (!Arrays.equals(supportedStates, other.supportedStates)) { diff |= CHANGED_SUPPORTED_STATES; } if (baseState != other.baseState) { diff |= CHANGED_BASE_STATE; } if (currentState != other.currentState) { diff |= CHANGED_CURRENT_STATE; } return diff; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(supportedStates.length); for (int i = 0; i < supportedStates.length; i++) { dest.writeInt(supportedStates[i]); } dest.writeInt(baseState); dest.writeInt(currentState); } @Override public int describeContents() { return 0; } public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<DeviceStateInfo>() { @Override public DeviceStateInfo createFromParcel(Parcel source) { final int numberOfSupportedStates = source.readInt(); final int[] supportedStates = new int[numberOfSupportedStates]; for (int i = 0; i < numberOfSupportedStates; i++) { supportedStates[i] = source.readInt(); } final int baseState = source.readInt(); final int currentState = source.readInt(); return new DeviceStateInfo(supportedStates, baseState, currentState); } @Override public DeviceStateInfo[] newArray(int size) { return new DeviceStateInfo[size]; } }; } core/java/android/hardware/devicestate/DeviceStateManager.java +43 −18 Original line number Diff line number Diff line Loading @@ -111,38 +111,63 @@ public final class DeviceStateManager { } /** * Registers a listener to receive notifications about changes in device state. * Registers a callback to receive notifications about changes in device state. * * @param executor the executor to process notifications. * @param listener the listener to register. * @param callback the callback to register. * * @see DeviceStateListener * @see DeviceStateCallback */ public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor, @NonNull DeviceStateListener listener) { mGlobal.registerDeviceStateListener(listener, executor); public void registerCallback(@NonNull @CallbackExecutor Executor executor, @NonNull DeviceStateCallback callback) { mGlobal.registerDeviceStateCallback(callback, executor); } /** * Unregisters a listener previously registered with * {@link #addDeviceStateListener(Executor, DeviceStateListener)}. * Unregisters a callback previously registered with * {@link #registerCallback(Executor, DeviceStateCallback)}. */ public void removeDeviceStateListener(@NonNull DeviceStateListener listener) { mGlobal.unregisterDeviceStateListener(listener); public void unregisterCallback(@NonNull DeviceStateCallback callback) { mGlobal.unregisterDeviceStateCallback(callback); } /** Callback to receive notifications about changes in device state. */ public interface DeviceStateCallback { /** * Listens for changes in device states. * Called in response to a change in the states supported by the device. * <p> * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in the supported states. * * @param supportedStates the new supported states. * * @see DeviceStateManager#getSupportedStates() */ default void onSupportedStatesChanged(@NonNull int[] supportedStates) {} /** * Called in response to a change in the base device state. * <p> * The base state is the state of the device without considering any requests made through * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)} * from any client process. The base state is guaranteed to match the state provided with a * call to {@link #onStateChanged(int)} when there are no active requests from any process. * <p> * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in the non-override state. * * @param state the new base device state. */ public interface DeviceStateListener { default void onBaseStateChanged(int state) {} /** * Called in response to device state changes. * <p> * Guaranteed to be called once on registration of the listener with the * initial value and then on every subsequent change in device state. * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in device state. * * @param deviceState the new device state. * @param state the new device state. */ void onDeviceStateChanged(int deviceState); void onStateChanged(int state); } } core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +104 −51 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.hardware.devicestate; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateListener; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -31,12 +31,14 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.Executor; /** * Provides communication with the device state system service on behalf of applications. * * @see DeviceStateManager * * @hide */ @VisibleForTesting(visibility = Visibility.PACKAGE) Loading Loading @@ -68,13 +70,13 @@ public final class DeviceStateManagerGlobal { private DeviceStateManagerCallback mCallback; @GuardedBy("mLock") private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>(); private final ArrayList<DeviceStateCallbackWrapper> mCallbacks = new ArrayList<>(); @GuardedBy("mLock") private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>(); @Nullable @GuardedBy("mLock") private Integer mLastReceivedState; private DeviceStateInfo mLastReceivedInfo; @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { Loading @@ -87,18 +89,31 @@ public final class DeviceStateManagerGlobal { * @see DeviceStateManager#getSupportedStates() */ public int[] getSupportedStates() { synchronized (mLock) { final DeviceStateInfo currentInfo; if (mLastReceivedInfo != null) { // If we have mLastReceivedInfo a callback is registered for this instance and it // is receiving the most recent info from the server. Use that info here. currentInfo = mLastReceivedInfo; } else { // If mLastReceivedInfo is null there is no registered callback so we manually // fetch the current info. try { return mDeviceStateManager.getSupportedDeviceStates(); currentInfo = mDeviceStateManager.getDeviceStateInfo(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } return Arrays.copyOf(currentInfo.supportedStates, currentInfo.supportedStates.length); } } /** * Submits a {@link DeviceStateRequest request} to modify the device state. * * @see DeviceStateManager#requestState(DeviceStateRequest, * Executor, DeviceStateRequest.Callback) * @see DeviceStateManager#requestState(DeviceStateRequest, Executor, * DeviceStateRequest.Callback) * @see DeviceStateRequest */ public void requestState(@NonNull DeviceStateRequest request, Loading Loading @@ -157,49 +172,56 @@ public final class DeviceStateManagerGlobal { } /** * Registers a listener to receive notifications about changes in device state. * Registers a callback to receive notifications about changes in device state. * * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void registerDeviceStateListener(@NonNull DeviceStateListener listener, public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { Integer stateToReport; DeviceStateListenerWrapper wrapper; DeviceStateCallbackWrapper wrapper; DeviceStateInfo currentInfo; synchronized (mLock) { registerCallbackIfNeededLocked(); int index = findListenerLocked(listener); int index = findCallbackLocked(callback); if (index != -1) { // This listener is already registered. // This callback is already registered. return; } wrapper = new DeviceStateListenerWrapper(listener, executor); mListeners.add(wrapper); stateToReport = mLastReceivedState; registerCallbackIfNeededLocked(); if (mLastReceivedInfo == null) { // Initialize the last received info with the current info if this is the first // callback being registered. try { mLastReceivedInfo = mDeviceStateManager.getDeviceStateInfo(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } if (stateToReport != null) { // Notify the listener with the most recent device state from the server. If the state // to report is null this is likely the first listener added and we're still waiting // from the callback from the server. wrapper.notifyDeviceStateChanged(stateToReport); currentInfo = new DeviceStateInfo(mLastReceivedInfo); wrapper = new DeviceStateCallbackWrapper(callback, executor); mCallbacks.add(wrapper); } wrapper.notifySupportedStatesChanged(currentInfo.supportedStates); wrapper.notifyBaseStateChanged(currentInfo.baseState); wrapper.notifyStateChanged(currentInfo.currentState); } /** * Unregisters a listener previously registered with * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. * Unregisters a callback previously registered with * {@link #registerDeviceStateCallback(DeviceStateCallback, Executor)}}. * * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) * @see DeviceStateManager#unregisterCallback(DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void unregisterDeviceStateListener(DeviceStateListener listener) { public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) { synchronized (mLock) { int indexToRemove = findListenerLocked(listener); int indexToRemove = findCallbackLocked(callback); if (indexToRemove != -1) { mListeners.remove(indexToRemove); mCallbacks.remove(indexToRemove); } } } Loading @@ -210,14 +232,15 @@ public final class DeviceStateManagerGlobal { try { mDeviceStateManager.registerCallback(mCallback); } catch (RemoteException ex) { mCallback = null; throw ex.rethrowFromSystemServer(); } } } private int findListenerLocked(DeviceStateListener listener) { for (int i = 0; i < mListeners.size(); i++) { if (mListeners.get(i).mDeviceStateListener.equals(listener)) { private int findCallbackLocked(DeviceStateCallback callback) { for (int i = 0; i < mCallbacks.size(); i++) { if (mCallbacks.get(i).mDeviceStateCallback.equals(callback)) { return i; } } Loading @@ -234,16 +257,34 @@ public final class DeviceStateManagerGlobal { return null; } /** Handles a call from the server that the device state has changed. */ private void handleDeviceStateChanged(int newDeviceState) { ArrayList<DeviceStateListenerWrapper> listeners; /** Handles a call from the server that the device state info has changed. */ private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) { ArrayList<DeviceStateCallbackWrapper> callbacks; DeviceStateInfo oldInfo; synchronized (mLock) { mLastReceivedState = newDeviceState; listeners = new ArrayList<>(mListeners); oldInfo = mLastReceivedInfo; mLastReceivedInfo = info; callbacks = new ArrayList<>(mCallbacks); } for (int i = 0; i < listeners.size(); i++) { listeners.get(i).notifyDeviceStateChanged(newDeviceState); final int diff = oldInfo == null ? 1 : info.diff(oldInfo); if ((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0) { for (int i = 0; i < callbacks.size(); i++) { // Copy the array to prevent callbacks from modifying the internal state. final int[] supportedStates = Arrays.copyOf(info.supportedStates, info.supportedStates.length); callbacks.get(i).notifySupportedStatesChanged(supportedStates); } } if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) { for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).notifyBaseStateChanged(info.baseState); } } if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) { for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).notifyStateChanged(info.currentState); } } } Loading Loading @@ -291,8 +332,8 @@ public final class DeviceStateManagerGlobal { private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override public void onDeviceStateChanged(int deviceState) { handleDeviceStateChanged(deviceState); public void onDeviceStateInfoChanged(DeviceStateInfo info) { handleDeviceStateInfoChanged(info); } @Override Loading @@ -311,17 +352,29 @@ public final class DeviceStateManagerGlobal { } } private static final class DeviceStateListenerWrapper { private final DeviceStateListener mDeviceStateListener; private static final class DeviceStateCallbackWrapper { @NonNull private final DeviceStateCallback mDeviceStateCallback; @NonNull private final Executor mExecutor; DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) { mDeviceStateListener = listener; DeviceStateCallbackWrapper(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { mDeviceStateCallback = callback; mExecutor = executor; } void notifyDeviceStateChanged(int newDeviceState) { mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState)); void notifySupportedStatesChanged(int[] newSupportedStates) { mExecutor.execute(() -> mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates)); } void notifyBaseStateChanged(int newBaseState) { mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); } void notifyStateChanged(int newDeviceState) { mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState)); } } Loading Loading
core/api/test-current.txt +6 −4 Original line number Diff line number Diff line Loading @@ -1021,17 +1021,19 @@ 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 public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); 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); method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0 } public static interface DeviceStateManager.DeviceStateListener { method public void onDeviceStateChanged(int); public static interface DeviceStateManager.DeviceStateCallback { method public default void onBaseStateChanged(int); method public void onStateChanged(int); method public default void onSupportedStatesChanged(@NonNull int[]); } public final class DeviceStateRequest { Loading
core/java/android/hardware/devicestate/DeviceStateInfo.aidl 0 → 100644 +19 −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; parcelable DeviceStateInfo;
core/java/android/hardware/devicestate/DeviceStateInfo.java 0 → 100644 +162 −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.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; /** * Information about the state of the device. * * @hide */ public final class DeviceStateInfo implements Parcelable { /** Bit that indicates the {@link #supportedStates} field has changed. */ public static final int CHANGED_SUPPORTED_STATES = 1 << 0; /** Bit that indicates the {@link #baseState} field has changed. */ public static final int CHANGED_BASE_STATE = 1 << 1; /** Bit that indicates the {@link #currentState} field has changed. */ public static final int CHANGED_CURRENT_STATE = 1 << 2; @IntDef(prefix = {"CHANGED_"}, flag = true, value = { CHANGED_SUPPORTED_STATES, CHANGED_BASE_STATE, CHANGED_CURRENT_STATE, }) @Retention(RetentionPolicy.SOURCE) public @interface ChangeFlags {} /** * The list of states supported by the device. */ @NonNull public final int[] supportedStates; /** * The base (non-override) state of the device. The base state is the state of the device * ignoring any override requests made through a call to {@link DeviceStateManager#requestState( * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. */ public final int baseState; /** * The state of the device. */ public final int currentState; /** * Creates a new instance of {@link DeviceStateInfo}. * <p> * NOTE: Unlike {@link #DeviceStateInfo(DeviceStateInfo)}, this constructor does not copy the * supplied parameters. */ public DeviceStateInfo(@NonNull int[] supportedStates, int baseState, int state) { this.supportedStates = supportedStates; this.baseState = baseState; this.currentState = state; } /** * Creates a new instance of {@link DeviceStateInfo} copying the fields of {@code info} into * the fields of the returned instance. */ public DeviceStateInfo(@NonNull DeviceStateInfo info) { this(Arrays.copyOf(info.supportedStates, info.supportedStates.length), info.baseState, info.currentState); } @Override public boolean equals(@Nullable Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; DeviceStateInfo that = (DeviceStateInfo) other; return baseState == that.baseState && currentState == that.currentState && Arrays.equals(supportedStates, that.supportedStates); } @Override public int hashCode() { int result = Objects.hash(baseState, currentState); result = 31 * result + Arrays.hashCode(supportedStates); return result; } /** Returns a bitmask of the differences between this instance and {@code other}. */ @ChangeFlags public int diff(@NonNull DeviceStateInfo other) { int diff = 0; if (!Arrays.equals(supportedStates, other.supportedStates)) { diff |= CHANGED_SUPPORTED_STATES; } if (baseState != other.baseState) { diff |= CHANGED_BASE_STATE; } if (currentState != other.currentState) { diff |= CHANGED_CURRENT_STATE; } return diff; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(supportedStates.length); for (int i = 0; i < supportedStates.length; i++) { dest.writeInt(supportedStates[i]); } dest.writeInt(baseState); dest.writeInt(currentState); } @Override public int describeContents() { return 0; } public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<DeviceStateInfo>() { @Override public DeviceStateInfo createFromParcel(Parcel source) { final int numberOfSupportedStates = source.readInt(); final int[] supportedStates = new int[numberOfSupportedStates]; for (int i = 0; i < numberOfSupportedStates; i++) { supportedStates[i] = source.readInt(); } final int baseState = source.readInt(); final int currentState = source.readInt(); return new DeviceStateInfo(supportedStates, baseState, currentState); } @Override public DeviceStateInfo[] newArray(int size) { return new DeviceStateInfo[size]; } }; }
core/java/android/hardware/devicestate/DeviceStateManager.java +43 −18 Original line number Diff line number Diff line Loading @@ -111,38 +111,63 @@ public final class DeviceStateManager { } /** * Registers a listener to receive notifications about changes in device state. * Registers a callback to receive notifications about changes in device state. * * @param executor the executor to process notifications. * @param listener the listener to register. * @param callback the callback to register. * * @see DeviceStateListener * @see DeviceStateCallback */ public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor, @NonNull DeviceStateListener listener) { mGlobal.registerDeviceStateListener(listener, executor); public void registerCallback(@NonNull @CallbackExecutor Executor executor, @NonNull DeviceStateCallback callback) { mGlobal.registerDeviceStateCallback(callback, executor); } /** * Unregisters a listener previously registered with * {@link #addDeviceStateListener(Executor, DeviceStateListener)}. * Unregisters a callback previously registered with * {@link #registerCallback(Executor, DeviceStateCallback)}. */ public void removeDeviceStateListener(@NonNull DeviceStateListener listener) { mGlobal.unregisterDeviceStateListener(listener); public void unregisterCallback(@NonNull DeviceStateCallback callback) { mGlobal.unregisterDeviceStateCallback(callback); } /** Callback to receive notifications about changes in device state. */ public interface DeviceStateCallback { /** * Listens for changes in device states. * Called in response to a change in the states supported by the device. * <p> * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in the supported states. * * @param supportedStates the new supported states. * * @see DeviceStateManager#getSupportedStates() */ default void onSupportedStatesChanged(@NonNull int[] supportedStates) {} /** * Called in response to a change in the base device state. * <p> * The base state is the state of the device without considering any requests made through * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)} * from any client process. The base state is guaranteed to match the state provided with a * call to {@link #onStateChanged(int)} when there are no active requests from any process. * <p> * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in the non-override state. * * @param state the new base device state. */ public interface DeviceStateListener { default void onBaseStateChanged(int state) {} /** * Called in response to device state changes. * <p> * Guaranteed to be called once on registration of the listener with the * initial value and then on every subsequent change in device state. * Guaranteed to be called once on registration of the callback with the initial value and * then on every subsequent change in device state. * * @param deviceState the new device state. * @param state the new device state. */ void onDeviceStateChanged(int deviceState); void onStateChanged(int state); } }
core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +104 −51 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.hardware.devicestate; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateListener; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -31,12 +31,14 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.Executor; /** * Provides communication with the device state system service on behalf of applications. * * @see DeviceStateManager * * @hide */ @VisibleForTesting(visibility = Visibility.PACKAGE) Loading Loading @@ -68,13 +70,13 @@ public final class DeviceStateManagerGlobal { private DeviceStateManagerCallback mCallback; @GuardedBy("mLock") private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>(); private final ArrayList<DeviceStateCallbackWrapper> mCallbacks = new ArrayList<>(); @GuardedBy("mLock") private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>(); @Nullable @GuardedBy("mLock") private Integer mLastReceivedState; private DeviceStateInfo mLastReceivedInfo; @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { Loading @@ -87,18 +89,31 @@ public final class DeviceStateManagerGlobal { * @see DeviceStateManager#getSupportedStates() */ public int[] getSupportedStates() { synchronized (mLock) { final DeviceStateInfo currentInfo; if (mLastReceivedInfo != null) { // If we have mLastReceivedInfo a callback is registered for this instance and it // is receiving the most recent info from the server. Use that info here. currentInfo = mLastReceivedInfo; } else { // If mLastReceivedInfo is null there is no registered callback so we manually // fetch the current info. try { return mDeviceStateManager.getSupportedDeviceStates(); currentInfo = mDeviceStateManager.getDeviceStateInfo(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } return Arrays.copyOf(currentInfo.supportedStates, currentInfo.supportedStates.length); } } /** * Submits a {@link DeviceStateRequest request} to modify the device state. * * @see DeviceStateManager#requestState(DeviceStateRequest, * Executor, DeviceStateRequest.Callback) * @see DeviceStateManager#requestState(DeviceStateRequest, Executor, * DeviceStateRequest.Callback) * @see DeviceStateRequest */ public void requestState(@NonNull DeviceStateRequest request, Loading Loading @@ -157,49 +172,56 @@ public final class DeviceStateManagerGlobal { } /** * Registers a listener to receive notifications about changes in device state. * Registers a callback to receive notifications about changes in device state. * * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void registerDeviceStateListener(@NonNull DeviceStateListener listener, public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { Integer stateToReport; DeviceStateListenerWrapper wrapper; DeviceStateCallbackWrapper wrapper; DeviceStateInfo currentInfo; synchronized (mLock) { registerCallbackIfNeededLocked(); int index = findListenerLocked(listener); int index = findCallbackLocked(callback); if (index != -1) { // This listener is already registered. // This callback is already registered. return; } wrapper = new DeviceStateListenerWrapper(listener, executor); mListeners.add(wrapper); stateToReport = mLastReceivedState; registerCallbackIfNeededLocked(); if (mLastReceivedInfo == null) { // Initialize the last received info with the current info if this is the first // callback being registered. try { mLastReceivedInfo = mDeviceStateManager.getDeviceStateInfo(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } if (stateToReport != null) { // Notify the listener with the most recent device state from the server. If the state // to report is null this is likely the first listener added and we're still waiting // from the callback from the server. wrapper.notifyDeviceStateChanged(stateToReport); currentInfo = new DeviceStateInfo(mLastReceivedInfo); wrapper = new DeviceStateCallbackWrapper(callback, executor); mCallbacks.add(wrapper); } wrapper.notifySupportedStatesChanged(currentInfo.supportedStates); wrapper.notifyBaseStateChanged(currentInfo.baseState); wrapper.notifyStateChanged(currentInfo.currentState); } /** * Unregisters a listener previously registered with * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. * Unregisters a callback previously registered with * {@link #registerDeviceStateCallback(DeviceStateCallback, Executor)}}. * * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) * @see DeviceStateManager#unregisterCallback(DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void unregisterDeviceStateListener(DeviceStateListener listener) { public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) { synchronized (mLock) { int indexToRemove = findListenerLocked(listener); int indexToRemove = findCallbackLocked(callback); if (indexToRemove != -1) { mListeners.remove(indexToRemove); mCallbacks.remove(indexToRemove); } } } Loading @@ -210,14 +232,15 @@ public final class DeviceStateManagerGlobal { try { mDeviceStateManager.registerCallback(mCallback); } catch (RemoteException ex) { mCallback = null; throw ex.rethrowFromSystemServer(); } } } private int findListenerLocked(DeviceStateListener listener) { for (int i = 0; i < mListeners.size(); i++) { if (mListeners.get(i).mDeviceStateListener.equals(listener)) { private int findCallbackLocked(DeviceStateCallback callback) { for (int i = 0; i < mCallbacks.size(); i++) { if (mCallbacks.get(i).mDeviceStateCallback.equals(callback)) { return i; } } Loading @@ -234,16 +257,34 @@ public final class DeviceStateManagerGlobal { return null; } /** Handles a call from the server that the device state has changed. */ private void handleDeviceStateChanged(int newDeviceState) { ArrayList<DeviceStateListenerWrapper> listeners; /** Handles a call from the server that the device state info has changed. */ private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) { ArrayList<DeviceStateCallbackWrapper> callbacks; DeviceStateInfo oldInfo; synchronized (mLock) { mLastReceivedState = newDeviceState; listeners = new ArrayList<>(mListeners); oldInfo = mLastReceivedInfo; mLastReceivedInfo = info; callbacks = new ArrayList<>(mCallbacks); } for (int i = 0; i < listeners.size(); i++) { listeners.get(i).notifyDeviceStateChanged(newDeviceState); final int diff = oldInfo == null ? 1 : info.diff(oldInfo); if ((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0) { for (int i = 0; i < callbacks.size(); i++) { // Copy the array to prevent callbacks from modifying the internal state. final int[] supportedStates = Arrays.copyOf(info.supportedStates, info.supportedStates.length); callbacks.get(i).notifySupportedStatesChanged(supportedStates); } } if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) { for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).notifyBaseStateChanged(info.baseState); } } if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) { for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).notifyStateChanged(info.currentState); } } } Loading Loading @@ -291,8 +332,8 @@ public final class DeviceStateManagerGlobal { private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override public void onDeviceStateChanged(int deviceState) { handleDeviceStateChanged(deviceState); public void onDeviceStateInfoChanged(DeviceStateInfo info) { handleDeviceStateInfoChanged(info); } @Override Loading @@ -311,17 +352,29 @@ public final class DeviceStateManagerGlobal { } } private static final class DeviceStateListenerWrapper { private final DeviceStateListener mDeviceStateListener; private static final class DeviceStateCallbackWrapper { @NonNull private final DeviceStateCallback mDeviceStateCallback; @NonNull private final Executor mExecutor; DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) { mDeviceStateListener = listener; DeviceStateCallbackWrapper(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { mDeviceStateCallback = callback; mExecutor = executor; } void notifyDeviceStateChanged(int newDeviceState) { mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState)); void notifySupportedStatesChanged(int[] newSupportedStates) { mExecutor.execute(() -> mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates)); } void notifyBaseStateChanged(int newBaseState) { mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); } void notifyStateChanged(int newDeviceState) { mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState)); } } Loading