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

Commit 7dde391f authored by Darryl L Johnson's avatar Darryl L Johnson Committed by Kenneth Ford
Browse files

Allow override requests to outlive the requesting process.

This change introduces a flag, FLAG_CANCEL_STICKY_REQUESTS, on the
device states that are provided by the DeviceStateProvider. If the flag
is set on a device state it indicates that the device state should
cancel any outstanding sticky requests when entered. If at least one
device state is configured to be a terminal device state (has the flag
set) the service will be configured to allow sticky requests, otherwise
requests will continue to be cancelled when the requesting process dies.

Fixes: 192671286

Test: atest DeviceStateManagerServiceTest
Test: atest DeviceStateProviderImplTest
Test: atest DeviceStateTest
Test: atest OverrideRequestControllerTest
Change-Id: I7a6bdc9334342fa818920b56a4f0c22629bb101f
parent a5ab448f
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -75,13 +75,13 @@ public final class DeviceStateManager {
    /**
     * 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:
     * By default, the request is kept active until one of the following occurs:
     * <ul>
     *     <li>The system deems the request can no longer be honored, for example if the requested
     *     state becomes unsupported.
     *     <li>A call to {@link #cancelRequest(DeviceStateRequest)}.
     *     <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}.
     *
+30 −3
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;

import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;

import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
@@ -39,6 +42,19 @@ import java.util.Objects;
 * @see DeviceStateManagerService
 */
public final class DeviceState {
    /**
     * Flag that indicates sticky requests should be cancelled when this device state becomes the
     * base device state.
     */
    public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0;

    /** @hide */
    @IntDef(prefix = {"FLAG_"}, flag = true, value = {
            FLAG_CANCEL_STICKY_REQUESTS,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface DeviceStateFlags {}

    /** Unique identifier for the device state. */
    @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
    private final int mIdentifier;
@@ -47,14 +63,19 @@ public final class DeviceState {
    @NonNull
    private final String mName;

    @DeviceStateFlags
    private final int mFlags;

    public DeviceState(
            @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
            @NonNull String name) {
            @NonNull String name,
            @DeviceStateFlags int flags) {
        Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
                "identifier");

        mIdentifier = identifier;
        mName = name;
        mFlags = flags;
    }

    /** Returns the unique identifier for the device state. */
@@ -69,6 +90,11 @@ public final class DeviceState {
        return mName;
    }

    @DeviceStateFlags
    public int getFlags() {
        return mFlags;
    }

    @Override
    public String toString() {
        return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
@@ -80,11 +106,12 @@ public final class DeviceState {
        if (o == null || getClass() != o.getClass()) return false;
        DeviceState that = (DeviceState) o;
        return mIdentifier == that.mIdentifier
                && Objects.equals(mName, that.mName);
                && Objects.equals(mName, that.mName)
                && mFlags == that.mFlags;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mIdentifier, mName);
        return Objects.hash(mIdentifier, mName, mFlags);
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -275,12 +275,21 @@ public final class DeviceStateManagerService extends SystemService {
        synchronized (mLock) {
            final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();

            // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS
            // set. If set to true, the OverrideRequestController will be configured to allow sticky
            // requests.
            boolean hasTerminalDeviceState = false;
            mDeviceStates.clear();
            for (int i = 0; i < supportedDeviceStates.length; i++) {
                DeviceState state = supportedDeviceStates[i];
                if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
                    hasTerminalDeviceState = true;
                }
                mDeviceStates.put(state.getIdentifier(), state);
            }

            mOverrideRequestController.setStickyRequestsAllowed(hasTerminalDeviceState);

            final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
            if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
                return;
@@ -338,6 +347,9 @@ public final class DeviceStateManagerService extends SystemService {
            }
            mBaseState = Optional.of(baseState);

            if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
                mOverrideRequestController.cancelStickyRequests();
            }
            mOverrideRequestController.handleBaseStateChanged();
            updatePendingStateLocked();

+73 −41
Original line number Diff line number Diff line
@@ -84,10 +84,28 @@ final class OverrideRequestController {
    // List of override requests with the most recent override request at the end.
    private final ArrayList<OverrideRequest> mRequests = new ArrayList<>();

    private boolean mStickyRequestsAllowed;
    // List of override requests that have outlived their process and will only be cancelled through
    // a call to cancelStickyRequests().
    private final ArrayList<OverrideRequest> mStickyRequests = new ArrayList<>();

    OverrideRequestController(@NonNull StatusChangeListener listener) {
        mListener = listener;
    }

    /**
     * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call
     * to {@link #handleProcessDied(int)} will not result in the request being cancelled
     * immediately. Instead, the request will be marked sticky and must be cancelled with a call
     * to {@link #cancelStickyRequests()}.
     */
    void setStickyRequestsAllowed(boolean stickyRequestsAllowed) {
        mStickyRequestsAllowed = stickyRequestsAllowed;
        if (!mStickyRequestsAllowed) {
            cancelStickyRequests();
        }
    }

    /**
     * Adds a request to the top of the stack and notifies the listener of all changes to request
     * status as a result of this operation.
@@ -122,6 +140,18 @@ final class OverrideRequestController {
        mListener.onStatusChanged(request, STATUS_CANCELED);
    }

    /**
     * Cancels all requests that are currently marked sticky and notifies the listener of all
     * changes to request status as a result of this operation.
     *
     * @see #setStickyRequestsAllowed(boolean)
     */
    void cancelStickyRequests() {
        mTmpRequestsToCancel.clear();
        mTmpRequestsToCancel.addAll(mStickyRequests);
        cancelRequestsLocked(mTmpRequestsToCancel);
    }

    /**
     * Returns {@code true} if this controller is current managing a request with the specified
     * {@code token}, {@code false} otherwise.
@@ -140,6 +170,7 @@ final class OverrideRequestController {
            return;
        }

        mTmpRequestsToCancel.clear();
        OverrideRequest prevActiveRequest = getLast(mRequests);
        for (OverrideRequest request : mRequests) {
            if (request.getPid() == pid) {
@@ -147,18 +178,14 @@ final class OverrideRequestController {
            }
        }

        mRequests.removeAll(mTmpRequestsToCancel);
        if (!mRequests.isEmpty()) {
            OverrideRequest newActiveRequest = getLast(mRequests);
            if (newActiveRequest != prevActiveRequest) {
                mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
            }
        if (mStickyRequestsAllowed) {
            // Do not cancel the requests now because sticky requests are allowed. These
            // requests will be cancelled on a call to cancelStickyRequests().
            mStickyRequests.addAll(mTmpRequestsToCancel);
            return;
        }

        for (int i = 0; i < mTmpRequestsToCancel.size(); i++) {
            mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED);
        }
        mTmpRequestsToCancel.clear();
        cancelRequestsLocked(mTmpRequestsToCancel);
    }

    /**
@@ -173,6 +200,7 @@ final class OverrideRequestController {
            return false;
        }

        mTmpRequestsToCancel.clear();
        OverrideRequest prevActiveRequest = getLast(mRequests);
        for (int i = 0; i < mRequests.size(); i++) {
            OverrideRequest request = mRequests.get(i);
@@ -181,21 +209,8 @@ final class OverrideRequestController {
            }
        }

        mRequests.removeAll(mTmpRequestsToCancel);
        OverrideRequest newActiveRequest = null;
        if (!mRequests.isEmpty()) {
            newActiveRequest = getLast(mRequests);
            if (newActiveRequest != prevActiveRequest) {
                mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
            }
        }

        for (int i = 0; i < mTmpRequestsToCancel.size(); i++) {
            mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED);
        }
        mTmpRequestsToCancel.clear();

        return newActiveRequest != prevActiveRequest;
        final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
        return newActiveRequest;
    }

    /**
@@ -210,7 +225,7 @@ final class OverrideRequestController {
            return false;
        }

        OverrideRequest prevActiveRequest = getLast(mRequests);
        mTmpRequestsToCancel.clear();
        for (int i = 0; i < mRequests.size(); i++) {
            OverrideRequest request = mRequests.get(i);
            if (!contains(newSupportedStates, request.getRequestedState())) {
@@ -218,21 +233,8 @@ final class OverrideRequestController {
            }
        }

        mRequests.removeAll(mTmpRequestsToCancel);
        OverrideRequest newActiveRequest = null;
        if (!mRequests.isEmpty()) {
            newActiveRequest = getLast(mRequests);
            if (newActiveRequest != prevActiveRequest) {
                mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
            }
        }

        for (int i = 0; i < mTmpRequestsToCancel.size(); i++) {
            mListener.onStatusChanged(mTmpRequestsToCancel.get(i), STATUS_CANCELED);
        }
        mTmpRequestsToCancel.clear();

        return newActiveRequest != prevActiveRequest;
        final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
        return newActiveRequest;
    }

    void dumpInternal(PrintWriter pw) {
@@ -249,6 +251,36 @@ final class OverrideRequestController {
        }
    }

    /**
     * Handles cancelling a set of requests. If the set of requests to cancel will lead to a new
     * request becoming active this request will also be notified of its change in state.
     *
     * @return {@code true} if calling this method has lead to a new active request, {@code false}
     * otherwise.
     */
    private boolean cancelRequestsLocked(List<OverrideRequest> requestsToCancel) {
        if (requestsToCancel.isEmpty()) {
            return false;
        }

        OverrideRequest prevActiveRequest = getLast(mRequests);
        boolean causedNewRequestToBecomeActive = false;
        mRequests.removeAll(requestsToCancel);
        mStickyRequests.removeAll(requestsToCancel);
        if (!mRequests.isEmpty()) {
            OverrideRequest newActiveRequest = getLast(mRequests);
            if (newActiveRequest != prevActiveRequest) {
                mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
                causedNewRequestToBecomeActive = true;
            }
        }

        for (int i = 0; i < requestsToCancel.size(); i++) {
            mListener.onStatusChanged(requestsToCancel.get(i), STATUS_CANCELED);
        }
        return causedNewRequestToBecomeActive;
    }

    private int getRequestIndex(@NonNull IBinder token) {
        final int numberOfRequests = mRequests.size();
        if (numberOfRequests == 0) {
+22 −2
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.Flags;
import com.android.server.policy.devicestate.config.LidSwitchCondition;
import com.android.server.policy.devicestate.config.NumericRange;
import com.android.server.policy.devicestate.config.SensorCondition;
@@ -87,7 +88,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,

    @VisibleForTesting
    static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
            "DEFAULT");
            "DEFAULT", 0 /* flags */);

    private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
    private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
@@ -131,7 +132,26 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
                        config.getDeviceState()) {
                    final int state = stateConfig.getIdentifier().intValue();
                    final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
                    deviceStateList.add(new DeviceState(state, name));

                    int flags = 0;
                    final Flags configFlags = stateConfig.getFlags();
                    if (configFlags != null) {
                        List<String> configFlagStrings = configFlags.getFlag();
                        for (int i = 0; i < configFlagStrings.size(); i++) {
                            final String configFlagString = configFlagStrings.get(i);
                            switch (configFlagString) {
                                case "FLAG_CANCEL_STICKY_REQUESTS":
                                    flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS;
                                    break;
                                default:
                                    Slog.w(TAG, "Parsed unknown flag with name: "
                                            + configFlagString);
                                    break;
                            }
                        }
                    }

                    deviceStateList.add(new DeviceState(state, name, flags));

                    final Conditions condition = stateConfig.getConditions();
                    conditionsList.add(condition);
Loading