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

Commit 98d80e10 authored by Kenneth Ford's avatar Kenneth Ford Committed by Android (Google) Code Review
Browse files

Merge "Allows an app to cancel a device state request they made at any point." into main

parents ff29ab29 144c8343
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -18,3 +18,15 @@ flag {
    bug: "336640888"
    is_fixed_read_only: true
}

flag {
    name: "device_state_requester_cancel_state"
    is_exported: true
    namespace: "windowing_sdk"
    description: "Removes foreground requirement if process attempting to cancel a state request is the requester"
    bug: "354772125"
    is_fixed_read_only: true
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+35 −20
Original line number Diff line number Diff line
@@ -50,8 +50,7 @@ import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.hardware.devicestate.feature.flags.FeatureFlags;
import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
import android.hardware.devicestate.feature.flags.Flags;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -190,9 +189,6 @@ public final class DeviceStateManagerService extends SystemService {
    @Nullable
    private OverrideRequest mRearDisplayPendingOverrideRequest;

    @NonNull
    private final FeatureFlags mFlags;

    @VisibleForTesting
    interface SystemPropertySetter {
        void setDebugTracingDeviceStateProperty(String value);
@@ -253,7 +249,6 @@ public final class DeviceStateManagerService extends SystemService {
            @NonNull SystemPropertySetter systemPropertySetter) {
        super(context);
        mSystemPropertySetter = systemPropertySetter;
        mFlags = new FeatureFlagsImpl();
        // We use the DisplayThread because this service indirectly drives
        // display (on/off) and window (position) events through its callbacks.
        DisplayThread displayThread = DisplayThread.get();
@@ -279,7 +274,7 @@ public final class DeviceStateManagerService extends SystemService {
        publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
        publishLocalService(DeviceStateManagerInternal.class, new LocalService());

        if (!mFlags.deviceStatePropertyMigration()) {
        if (!Flags.deviceStatePropertyMigration()) {
            synchronized (mLock) {
                readStatesAvailableForRequestFromApps();
                mFoldedDeviceStates = readFoldedStates();
@@ -848,7 +843,7 @@ public final class DeviceStateManagerService extends SystemService {
            OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
                    deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);

            if (mFlags.deviceStatePropertyMigration()) {
            if (Flags.deviceStatePropertyMigration()) {
                // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
                if (!hasControlDeviceStatePermission && deviceState.get().hasProperty(
                        PROPERTY_FEATURE_REAR_DISPLAY)) {
@@ -988,16 +983,16 @@ public final class DeviceStateManagerService extends SystemService {
     * @param callingPid Process ID that is requesting this state change
     * @param state state that is being requested.
     */
    private void assertCanRequestDeviceState(int callingPid, int callingUid, int state) {
    private void enforceRequestDeviceStatePermitted(int callingPid, int callingUid, int state) {
        final boolean isTopApp = isTopApp(callingPid);
        final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
        final boolean isStateAvailableForAppRequests = isStateAvailableForAppRequests(state);

        final boolean canRequestState = isTopApp
        final boolean isAllowedToRequestState = isTopApp
                && isForegroundApp
                && isStateAvailableForAppRequests;

        if (!canRequestState) {
        if (!isAllowedToRequestState) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                            + "or the call must come from the top app "
@@ -1006,19 +1001,29 @@ public final class DeviceStateManagerService extends SystemService {
    }

    /**
     * Checks if the process can control the device state. If the calling process ID is
     * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
     * Checks if the process can cancel a device state request. If the calling process ID is not
     * both the top app and foregrounded nor does the process ID and userID match the IDs that made
     * the device state request, then check if this process holds the CONTROL_DEVICE_STATE
     * permission.
     *
     * @param callingPid Process ID that is requesting this state change
     * @param callingUid UID that is requesting this state change
     */
    private void assertCanControlDeviceState(int callingPid, int callingUid) {
    private void enforceCancelDeviceStatePermitted(int callingPid, int callingUid) {
        final boolean isTopApp = isTopApp(callingPid);
        final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);

        final boolean canControlState = isTopApp && isForegroundApp;
        boolean isAllowedToControlState = isTopApp && isForegroundApp;

        if (Flags.deviceStateRequesterCancelState()) {
            synchronized (mLock) {
                isAllowedToControlState =
                        isAllowedToControlState || doCallingIdsMatchOverrideRequestIdsLocked(
                                callingPid, callingUid);
            }
        }

        if (!canControlState) {
        if (!isAllowedToControlState) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                            + "or the call must come from the top app.");
@@ -1052,9 +1057,19 @@ public final class DeviceStateManagerService extends SystemService {
        return topApp != null && topApp.getPid() == callingPid;
    }

    /**
     * Returns if the provided {@code callingPid} and {@code callingUid} match the same id's that
     * requested the current device state override.
     */
    @GuardedBy("mLock")
    private boolean doCallingIdsMatchOverrideRequestIdsLocked(int callingPid, int callingUid) {
        OverrideRequest request = mActiveOverride.orElse(null);
        return request != null && request.getPid() == callingPid && request.getUid() == callingUid;
    }

    private boolean isStateAvailableForAppRequests(int state) {
        synchronized (mLock) {
            if (mFlags.deviceStatePropertyMigration()) {
            if (Flags.deviceStatePropertyMigration()) {
                Optional<DeviceState> deviceState =  getStateLocked(state);
                return deviceState.isPresent() && deviceState.get().hasProperty(
                        PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST);
@@ -1122,7 +1137,7 @@ public final class DeviceStateManagerService extends SystemService {
     */
    @GuardedBy("mLock")
    private boolean isDeviceOpeningLocked(int newBaseState) {
        if (mFlags.deviceStatePropertyMigration()) {
        if (Flags.deviceStatePropertyMigration()) {
            final DeviceState currentBaseState = mBaseState.orElse(INVALID_DEVICE_STATE);
            final DeviceState newDeviceBaseState = getStateLocked(newBaseState).orElse(
                    INVALID_DEVICE_STATE);
@@ -1293,7 +1308,7 @@ public final class DeviceStateManagerService extends SystemService {
            // Allow top processes to request a device state change
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            assertCanRequestDeviceState(callingPid, callingUid, state);
            enforceRequestDeviceStatePermitted(callingPid, callingUid, state);

            if (token == null) {
                throw new IllegalArgumentException("Request token must not be null.");
@@ -1318,7 +1333,7 @@ public final class DeviceStateManagerService extends SystemService {
            // Allow top processes to cancel a device state change
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            assertCanControlDeviceState(callingPid, callingUid);
            enforceCancelDeviceStatePermitted(callingPid, callingUid);

            final long callingIdentity = Binder.clearCallingIdentity();
            try {