Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e72cce52 authored by Darryl Johnson's avatar Darryl Johnson Committed by Android (Google) Code Review
Browse files

Merge "Add callbacks for change in supported device states and non-override state." into sc-dev

parents a25f62e2 2c35bdf9
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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 {
+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;
+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];
        }
    };
}
+43 −18
Original line number Diff line number Diff line
@@ -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);
    }
}
+104 −51
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -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) {
@@ -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,
@@ -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);
            }
        }
    }
@@ -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;
            }
        }
@@ -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);
            }
        }
    }

@@ -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
@@ -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