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

Commit d5fe5cc1 authored by Kenneth Ford's avatar Kenneth Ford
Browse files

Adds requestBaseStateOverride method on DeviceStateManagerService

Server side changes to accompany the addition of the
DeviceStateManager#requestBaseStateOverride test API

Bug: 234336979
Test: OverrideRequestControllerTest
 && DeviceStateManagerServiceTest
Change-Id: Ib915b245acd260be1e0d5016d765af5598ea390c
parent 8997144b
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@ interface IDeviceStateManager {
     * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
     * call to this method.
     *
     * Requesting a state does not cancel a base state override made through
     * {@link #requestBaseStateOverride}, but will still attempt to put the device into the
     * supplied {@code state}.
     *
     * @param token the request token provided
     * @param state the state of device the request is asking for
     * @param flags any flags that correspond to the request
@@ -50,14 +54,53 @@ interface IDeviceStateManager {
     * @throws IllegalStateException if the supplied {@code token} has already been registered.
     * @throws IllegalArgumentException if the supplied {@code state} is not supported.
     */
    @JavaPassthrough(annotation=
            "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
    void requestState(IBinder token, int state, int flags);

    /**
     * Cancels the active request previously submitted with a call to
     * {@link #requestState(IBinder, int, int)}.
     * {@link #requestState(IBinder, int, int)}. Will have no effect on any base state override that
     * was previously requested with {@link #requestBaseStateOverride}.
     *
     * @throws IllegalStateException if a callback has not yet been registered for the calling
     *         process.
     */
    @JavaPassthrough(annotation=
            "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
    void cancelStateRequest();

    /**
     * Requests that the device's base state be overridden to the supplied {@code state}. A callback
     * <b>MUST</b> have been previously registered with
     * {@link #registerCallback(IDeviceStateManagerCallback)} before a call to this method.
     *
     * This method should only be used for testing, when you want to simulate the device physically
     * changing states. If you are looking to change device state for a feature, where the system
     * should still be aware that the physical state is different than the emulated state, use
     * {@link #requestState}.
     *
     * @param token the request token provided
     * @param state the state of device the request is asking for
     * @param flags any flags that correspond to the request
     *
     * @throws IllegalStateException if a callback has not yet been registered for the calling
     *         process.
     * @throws IllegalStateException if the supplied {@code token} has already been registered.
     * @throws IllegalArgumentException if the supplied {@code state} is not supported.
     */
    @JavaPassthrough(annotation=
        "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
    void requestBaseStateOverride(IBinder token, int state, int flags);

    /**
     * Cancels the active base state request previously submitted with a call to
     * {@link #overrideBaseState(IBinder, int, int)}.
     *
     * @throws IllegalStateException if a callback has not yet been registered for the calling
     *         process.
     */
    @JavaPassthrough(annotation=
        "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
    void cancelBaseStateOverride();
}
+149 −11
Original line number Diff line number Diff line
@@ -21,8 +21,11 @@ import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STA
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;

import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN;

import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -106,6 +109,8 @@ public final class DeviceStateManagerService extends SystemService {
    private final BinderService mBinderService;
    @NonNull
    private final OverrideRequestController mOverrideRequestController;
    @NonNull
    private final DeviceStateProviderListener mDeviceStateProviderListener;
    @VisibleForTesting
    @NonNull
    public ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -139,6 +144,12 @@ public final class DeviceStateManagerService extends SystemService {
    @NonNull
    private Optional<OverrideRequest> mActiveOverride = Optional.empty();

    // The current active base state override request. When set the device state specified here will
    // replace the value in mBaseState.
    @GuardedBy("mLock")
    @NonNull
    private Optional<OverrideRequest> mActiveBaseStateOverride = Optional.empty();

    // List of processes registered to receive notifications about changes to device state and
    // request status indexed by process id.
    @GuardedBy("mLock")
@@ -177,7 +188,8 @@ public final class DeviceStateManagerService extends SystemService {
        mOverrideRequestController = new OverrideRequestController(
                this::onOverrideRequestStatusChangedLocked);
        mDeviceStatePolicy = policy;
        mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
        mDeviceStateProviderListener = new DeviceStateProviderListener();
        mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
        mBinderService = new BinderService();
        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
    }
@@ -257,6 +269,21 @@ public final class DeviceStateManagerService extends SystemService {
        }
    }

    /**
     * Returns the current override base state, or {@link Optional#empty()} if no override state is
     * requested. If an override base state is present, the returned state will be the same as
     * the base state returned from {@link #getBaseState()}.
     */
    @NonNull
    Optional<DeviceState> getOverrideBaseState() {
        synchronized (mLock) {
            if (mActiveBaseStateOverride.isPresent()) {
                return getStateLocked(mActiveBaseStateOverride.get().getRequestedState());
            }
            return Optional.empty();
        }
    }

    /** Returns the list of currently supported device states. */
    DeviceState[] getSupportedStates() {
        synchronized (mLock) {
@@ -366,6 +393,7 @@ public final class DeviceStateManagerService extends SystemService {
            }

            final DeviceState baseState = baseStateOptional.get();

            if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) {
                // Base state hasn't changed. Nothing to do.
                return;
@@ -375,7 +403,7 @@ public final class DeviceStateManagerService extends SystemService {
            if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
                mOverrideRequestController.cancelOverrideRequest();
            }
            mOverrideRequestController.handleBaseStateChanged();
            mOverrideRequestController.handleBaseStateChanged(identifier);
            updatePendingStateLocked();

            if (!mPendingState.isPresent()) {
@@ -528,17 +556,42 @@ public final class DeviceStateManagerService extends SystemService {
        }
    }

    @GuardedBy("mLock")
    private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
            @OverrideRequestController.RequestStatus int status) {
        if (status == STATUS_ACTIVE) {
        if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
            switch (status) {
                case STATUS_ACTIVE:
                    enableBaseStateRequestLocked(request);
                    return;
                case STATUS_CANCELED:
                    if (mActiveBaseStateOverride.isPresent()
                            && mActiveBaseStateOverride.get() == request) {
                        mActiveBaseStateOverride = Optional.empty();
                    }
                    break;
                case STATUS_UNKNOWN: // same as default
                default:
                    throw new IllegalArgumentException("Unknown request status: " + status);
            }
        } else if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_EMULATED_STATE) {
            switch (status) {
                case STATUS_ACTIVE:
                    mActiveOverride = Optional.of(request);
        } else if (status == STATUS_CANCELED) {
                    break;
                case STATUS_CANCELED:
                    if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
                        mActiveOverride = Optional.empty();
                    }
        } else {
                    break;
                case STATUS_UNKNOWN: // same as default
                default:
                    throw new IllegalArgumentException("Unknown request status: " + status);
            }
        } else {
            throw new IllegalArgumentException(
                        "Unknown OverrideRest type: " + request.getRequestType());
        }

        boolean updatedPendingState = updatePendingStateLocked();

@@ -564,6 +617,18 @@ public final class DeviceStateManagerService extends SystemService {
        mHandler.post(this::notifyPolicyIfNeeded);
    }

    /**
     * Sets the new base state of the device and notifies the process that made the base state
     * override request that the request is now active.
     */
    @GuardedBy("mLock")
    private void enableBaseStateRequestLocked(OverrideRequest request) {
        setBaseState(request.getRequestedState());
        mActiveBaseStateOverride = Optional.of(request);
        ProcessRecord processRecord = mProcessRecords.get(request.getPid());
        processRecord.notifyRequestActiveAsync(request.getToken());
    }

    private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
        synchronized (mLock) {
            if (mProcessRecords.contains(pid)) {
@@ -606,7 +671,8 @@ public final class DeviceStateManagerService extends SystemService {
                        + " has no registered callback.");
            }

            if (mOverrideRequestController.hasRequest(token)) {
            if (mOverrideRequestController.hasRequest(token,
                    OVERRIDE_REQUEST_TYPE_EMULATED_STATE)) {
                throw new IllegalStateException("Request has already been made for the supplied"
                        + " token: " + token);
            }
@@ -618,7 +684,7 @@ public final class DeviceStateManagerService extends SystemService {
            }

            OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
                    OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
                    OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
            mOverrideRequestController.addRequest(request);
        }
    }
@@ -634,6 +700,44 @@ public final class DeviceStateManagerService extends SystemService {
        }
    }

    private void requestBaseStateOverrideInternal(int state, int flags, int callingPid,
            @NonNull IBinder token) {
        synchronized (mLock) {
            final Optional<DeviceState> deviceState = getStateLocked(state);
            if (!deviceState.isPresent()) {
                throw new IllegalArgumentException("Requested state: " + state
                        + " is not supported.");
            }

            final ProcessRecord processRecord = mProcessRecords.get(callingPid);
            if (processRecord == null) {
                throw new IllegalStateException("Process " + callingPid
                        + " has no registered callback.");
            }

            if (mOverrideRequestController.hasRequest(token,
                    OVERRIDE_REQUEST_TYPE_BASE_STATE)) {
                throw new IllegalStateException("Request has already been made for the supplied"
                        + " token: " + token);
            }

            OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
                    OVERRIDE_REQUEST_TYPE_BASE_STATE);
            mOverrideRequestController.addBaseStateRequest(request);
        }
    }

    private void cancelBaseStateOverrideInternal(int callingPid) {
        synchronized (mLock) {
            final ProcessRecord processRecord = mProcessRecords.get(callingPid);
            if (processRecord == null) {
                throw new IllegalStateException("Process " + callingPid
                        + " has no registered callback.");
            }
            setBaseState(mDeviceStateProviderListener.mCurrentBaseState);
        }
    }

    private void dumpInternal(PrintWriter pw) {
        pw.println("DEVICE STATE MANAGER (dumpsys device_state)");

@@ -730,6 +834,8 @@ public final class DeviceStateManagerService extends SystemService {
    }

    private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
        @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;

        @Override
        public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
            if (newDeviceStates.length == 0) {
@@ -744,7 +850,7 @@ public final class DeviceStateManagerService extends SystemService {
            if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
                throw new IllegalArgumentException("Invalid identifier: " + identifier);
            }

            mCurrentBaseState = identifier;
            setBaseState(identifier);
        }
    }
@@ -895,6 +1001,38 @@ public final class DeviceStateManagerService extends SystemService {
            }
        }

        @Override // Binder call
        public void requestBaseStateOverride(IBinder token, int state, int flags) {
            final int callingPid = Binder.getCallingPid();
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to control base state of device.");

            if (token == null) {
                throw new IllegalArgumentException("Request token must not be null.");
            }

            final long callingIdentity = Binder.clearCallingIdentity();
            try {
                requestBaseStateOverrideInternal(state, flags, callingPid, token);
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }
        }

        @Override // Binder call
        public void cancelBaseStateOverride() {
            final int callingPid = Binder.getCallingPid();
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to control base state of device.");

            final long callingIdentity = Binder.clearCallingIdentity();
            try {
                cancelBaseStateOverrideInternal(callingPid);
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }
        }

        @Override // Binder call
        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                String[] args, ShellCallback callback, ResultReceiver result) {
+45 −7
Original line number Diff line number Diff line
@@ -16,11 +16,8 @@

package com.android.server.devicestate;

import static android.Manifest.permission.CONTROL_DEVICE_STATE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.Binder;
@@ -39,6 +36,8 @@ import java.util.stream.Collectors;
public class DeviceStateManagerShellCommand extends ShellCommand {
    @Nullable
    private static DeviceStateRequest sLastRequest;
    @Nullable
    private static DeviceStateRequest sLastBaseStateRequest;

    private final DeviceStateManagerService mService;
    private final DeviceStateManager mClient;
@@ -58,6 +57,8 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
        switch (cmd) {
            case "state":
                return runState(pw);
            case "base-state":
                return runBaseState(pw);
            case "print-state":
                return runPrintState(pw);
            case "print-states":
@@ -89,10 +90,6 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
            return 0;
        }

        final Context context = mService.getContext();
        context.enforceCallingOrSelfPermission(
                CONTROL_DEVICE_STATE,
                "Permission required to request device state.");
        final long callingIdentity = Binder.clearCallingIdentity();
        try {
            if ("reset".equals(nextArg)) {
@@ -127,6 +124,47 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
        return 0;
    }

    private int runBaseState(PrintWriter pw) {
        final String nextArg = getNextArg();
        if (nextArg == null) {
            printAllStates(pw);
            return 0;
        }

        final long callingIdentity = Binder.clearCallingIdentity();
        try {
            if ("reset".equals(nextArg)) {
                if (sLastBaseStateRequest != null) {
                    mClient.cancelBaseStateOverride();
                    sLastBaseStateRequest = null;
                }
            } else {
                int requestedState = Integer.parseInt(nextArg);
                DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();

                mClient.requestBaseStateOverride(request, null, null);

                sLastBaseStateRequest = request;
            }
        } catch (NumberFormatException e) {
            getErrPrintWriter().println("Error: requested state should be an integer");
            return -1;
        } catch (IllegalArgumentException e) {
            getErrPrintWriter().println("Error: " + e.getMessage());
            getErrPrintWriter().println("-------------------");
            getErrPrintWriter().println("Run:");
            getErrPrintWriter().println("");
            getErrPrintWriter().println("    print-states");
            getErrPrintWriter().println("");
            getErrPrintWriter().println("to get the list of currently supported device states");
            return -1;
        } finally {
            Binder.restoreCallingIdentity(callingIdentity);
        }

        return 0;
    }

    private int runPrintState(PrintWriter pw) {
        Optional<DeviceState> deviceState = mService.getCommittedState();
        if (deviceState.isPresent()) {
+53 −11
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ final class OverrideRequestController {

    // Handle to the current override request, null if none.
    private OverrideRequest mRequest;
    // Handle to the current base state override request, null if none.
    private OverrideRequest mBaseStateRequest;

    private boolean mStickyRequestsAllowed;
    // The current request has outlived their process.
@@ -111,13 +113,23 @@ final class OverrideRequestController {
        }
    }

    void addBaseStateRequest(@NonNull OverrideRequest request) {
        OverrideRequest previousRequest = mBaseStateRequest;
        mBaseStateRequest = request;
        mListener.onStatusChanged(request, STATUS_ACTIVE);

        if (previousRequest != null) {
            cancelRequestLocked(previousRequest);
        }
    }

    /**
     * Cancels the request with the specified {@code token} and notifies the listener of all changes
     * to request status as a result of this operation.
     */
    void cancelRequest(@NonNull OverrideRequest request) {
        // Either don't have a current request or attempting to cancel an already cancelled request
        if (!hasRequest(request.getToken())) {
        if (!hasRequest(request.getToken(), request.getRequestType())) {
            return;
        }
        cancelCurrentRequestLocked();
@@ -143,13 +155,26 @@ final class OverrideRequestController {
        cancelCurrentRequestLocked();
    }

    /**
     * Cancels the current base state override request, this could be due to the physical
     * configuration of the device changing.
     */
    void cancelBaseStateOverrideRequest() {
        cancelCurrentBaseStateRequestLocked();
    }

    /**
     * Returns {@code true} if this controller is current managing a request with the specified
     * {@code token}, {@code false} otherwise.
     */
    boolean hasRequest(@NonNull IBinder token) {
    boolean hasRequest(@NonNull IBinder token,
            @OverrideRequest.OverrideRequestType int requestType) {
        if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
            return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
        } else {
            return mRequest != null && token == mRequest.getToken();
        }
    }

    /**
     * Notifies the controller that the process with the specified {@code pid} has died. The
@@ -157,11 +182,11 @@ final class OverrideRequestController {
     * operation.
     */
    void handleProcessDied(int pid) {
        if (mRequest == null) {
            return;
        if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
            cancelCurrentBaseStateRequestLocked();
        }

        if (mRequest.getPid() == pid) {
        if (mRequest != null && mRequest.getPid() == pid) {
            if (mStickyRequestsAllowed) {
                // Do not cancel the requests now because sticky requests are allowed. These
                // requests will be cancelled on a call to cancelStickyRequests().
@@ -176,7 +201,10 @@ final class OverrideRequestController {
     * Notifies the controller that the base state has changed. The controller will notify the
     * listener of all changes to request status as a result of this change.
     */
    void handleBaseStateChanged() {
    void handleBaseStateChanged(int state) {
        if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
            cancelBaseStateOverrideRequest();
        }
        if (mRequest == null) {
            return;
        }
@@ -192,11 +220,12 @@ final class OverrideRequestController {
     * notify the listener of all changes to request status as a result of this change.
     */
    void handleNewSupportedStates(int[] newSupportedStates) {
        if (mRequest == null) {
            return;
        if (mBaseStateRequest != null && !contains(newSupportedStates,
                mBaseStateRequest.getRequestedState())) {
            cancelCurrentBaseStateRequestLocked();
        }

        if (!contains(newSupportedStates, mRequest.getRequestedState())) {
        if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
            cancelCurrentRequestLocked();
        }
    }
@@ -228,10 +257,23 @@ final class OverrideRequestController {
            return;
        }
        mStickyRequest = false;
        mListener.onStatusChanged(mRequest, STATUS_CANCELED);
        cancelRequestLocked(mRequest);
        mRequest = null;
    }

    /**
     * Handles cancelling {@code mBaseStateRequest}.
     * Notifies the listener of the canceled status as well.
     */
    private void cancelCurrentBaseStateRequestLocked() {
        if (mBaseStateRequest == null) {
            Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
            return;
        }
        cancelRequestLocked(mBaseStateRequest);
        mBaseStateRequest = null;
    }

    private static boolean contains(int[] array, int value) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == value) {
+151 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading