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

Commit b6e9d2fc authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Make DeviceStateController generic

Previously it was specific to fold-state, and used to notify a single
client of fold-state changes. DeviceStateController is now
generic and can be used (or modified) to cache, query, or receive
callbacks for other state changes.

Note that we are purposely removing the nested FoldStateListener class
since it's a very thin layer and makes it unnecessarily complex when
we add new functionality to DeviceStateController. A simple example would
be DeviceStateController#getCurrentState() needing to access an inner
FoldStateListener#getCurrentState().

Bug: 264499484
Test: atest DisplayRotationTests
Test: atest DeviceStateControllerTests
Change-Id: I81b0a61b52d74718fe1dee213a59509a44a9a920
parent 286074d5
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -652,6 +652,16 @@
         The default is false. -->
    <bool name="config_lidControlsSleep">false</bool>

    <!-- The device states (supplied by DeviceStateManager) that should be treated as open by the
         device fold controller. Default is empty. -->
    <integer-array name="config_openDeviceStates">
        <!-- Example:
        <item>0</item>
        <item>1</item>
        <item>2</item>
        -->
    </integer-array>

    <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
         display fold controller. Default is empty. -->
    <integer-array name="config_foldedDeviceStates">
@@ -672,6 +682,16 @@
        -->
    </integer-array>

    <!-- The device states (supplied by DeviceStateManager) that should be treated as a rear display
     state. Default is empty. -->
    <integer-array name="config_rearDisplayDeviceStates">
        <!-- Example:
        <item>0</item>
        <item>1</item>
        <item>2</item>
        -->
    </integer-array>

    <!-- Indicates whether the window manager reacts to half-fold device states by overriding
     rotation. -->
    <bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
+2 −0
Original line number Diff line number Diff line
@@ -4015,8 +4015,10 @@
  <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />

  <!-- For Foldables -->
  <java-symbol type="array" name="config_openDeviceStates" />
  <java-symbol type="array" name="config_foldedDeviceStates" />
  <java-symbol type="array" name="config_halfFoldedDeviceStates" />
  <java-symbol type="array" name="config_rearDisplayDeviceStates" />
  <java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
  <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
  <java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
+58 −46
Original line number Diff line number Diff line
@@ -16,80 +16,92 @@

package com.android.server.wm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Handler;
import android.os.HandlerExecutor;

import com.android.internal.R;
import com.android.internal.util.ArrayUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * Class that registers callbacks with the {@link DeviceStateManager} and
 * responds to fold state changes by forwarding such events to a delegate.
 * Class that registers callbacks with the {@link DeviceStateManager} and responds to device
 * changes.
 */
final class DeviceStateController {
final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {

    @NonNull
    private final DeviceStateManager mDeviceStateManager;
    private final Context mContext;
    @NonNull
    private final int[] mOpenDeviceStates;
    @NonNull
    private final int[] mHalfFoldedDeviceStates;
    @NonNull
    private final int[] mFoldedDeviceStates;
    @NonNull
    private final int[] mRearDisplayDeviceStates;
    @NonNull
    private final List<Consumer<DeviceState>> mDeviceStateCallbacks = new ArrayList<>();

    private FoldStateListener mDeviceStateListener;
    @Nullable
    private DeviceState mLastDeviceState;

    public enum FoldState {
        UNKNOWN, OPEN, FOLDED, HALF_FOLDED
    public enum DeviceState {
        UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
    }

    DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) {
        mContext = context;
        mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
    DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
        mOpenDeviceStates = context.getResources()
                .getIntArray(R.array.config_openDeviceStates);
        mHalfFoldedDeviceStates = context.getResources()
                .getIntArray(R.array.config_halfFoldedDeviceStates);
        mFoldedDeviceStates = context.getResources()
                .getIntArray(R.array.config_foldedDeviceStates);
        mRearDisplayDeviceStates = context.getResources()
                .getIntArray(R.array.config_rearDisplayDeviceStates);

        if (mDeviceStateManager != null) {
            mDeviceStateListener = new FoldStateListener(mContext, delegate);
            mDeviceStateManager
                    .registerCallback(new HandlerExecutor(handler),
                            mDeviceStateListener);
            mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
        }
    }

    void unregisterFromDeviceStateManager() {
        if (mDeviceStateListener != null) {
            mDeviceStateManager.unregisterCallback(mDeviceStateListener);
        if (mDeviceStateManager != null) {
            mDeviceStateManager.unregisterCallback(this);
        }
    }

    /**
     * A listener for half-fold device state events that dispatches state changes to a delegate.
     */
    static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {

        private final int[] mHalfFoldedDeviceStates;
        private final int[] mFoldedDeviceStates;

        @Nullable
        private FoldState mLastResult;
        private final Consumer<FoldState> mDelegate;

        FoldStateListener(Context context, Consumer<FoldState> delegate) {
            mFoldedDeviceStates = context.getResources().getIntArray(
                    com.android.internal.R.array.config_foldedDeviceStates);
            mHalfFoldedDeviceStates = context.getResources().getIntArray(
                    com.android.internal.R.array.config_halfFoldedDeviceStates);
            mDelegate = delegate;
    void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
        mDeviceStateCallbacks.add(callback);
    }

    @Override
    public void onStateChanged(int state) {
            final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
            FoldState result;
            if (halfFolded) {
                result = FoldState.HALF_FOLDED;
        final DeviceState deviceState;
        if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
            deviceState = DeviceState.HALF_FOLDED;
        } else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
            deviceState = DeviceState.FOLDED;
        } else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
            deviceState = DeviceState.REAR;
        } else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
            deviceState = DeviceState.OPEN;
        } else {
                final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
                result = folded ? FoldState.FOLDED : FoldState.OPEN;
            deviceState = DeviceState.UNKNOWN;
        }
            if (mLastResult == null || !mLastResult.equals(result)) {
                mLastResult = result;
                mDelegate.accept(result);

        if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
            mLastDeviceState = deviceState;

            for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
                callback.accept(mLastDeviceState);
            }
        }
    }
+6 −3
Original line number Diff line number Diff line
@@ -1125,14 +1125,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                    mWmService.mAtmService.getRecentTasks().getInputListener());
        }

        mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);

        mDisplayPolicy = new DisplayPolicy(mWmService, this);
        mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);

        mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
                newFoldState -> {
        final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
                (@NonNull DeviceStateController.DeviceState newFoldState) -> {
                    mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
                    mDisplayRotation.foldStateChanged(newFoldState);
                });
                };
        mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);

        mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
                R.dimen.config_closeToSquareDisplayMaxAspectRatio);
+26 −25
Original line number Diff line number Diff line
@@ -1573,7 +1573,7 @@ public class DisplayRotation {
        proto.end(token);
    }

    boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
    boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
        if (mFoldController == null) return false;
        return mFoldController.isDeviceInPosture(state, isTabletop);
    }
@@ -1585,10 +1585,10 @@ public class DisplayRotation {
    /**
     * Called by the DeviceStateManager callback when the device state changes.
     */
    void foldStateChanged(DeviceStateController.FoldState foldState) {
    void foldStateChanged(DeviceStateController.DeviceState deviceState) {
        if (mFoldController != null) {
            synchronized (mLock) {
                mFoldController.foldStateChanged(foldState);
                mFoldController.foldStateChanged(deviceState);
            }
        }
    }
@@ -1596,8 +1596,8 @@ public class DisplayRotation {
    private class FoldController {
        @Surface.Rotation
        private int mHalfFoldSavedRotation = -1; // No saved rotation
        private DeviceStateController.FoldState mFoldState =
                DeviceStateController.FoldState.UNKNOWN;
        private DeviceStateController.DeviceState mDeviceState =
                DeviceStateController.DeviceState.UNKNOWN;
        private boolean mInHalfFoldTransition = false;
        private final boolean mIsDisplayAlwaysSeparatingHinge;
        private final Set<Integer> mTabletopRotations;
@@ -1637,32 +1637,33 @@ public class DisplayRotation {
                    R.bool.config_isDisplayHingeAlwaysSeparating);
        }

        boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
            if (state != mFoldState) {
        boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
            if (state != mDeviceState) {
                return false;
            }
            if (mFoldState == DeviceStateController.FoldState.HALF_FOLDED) {
            if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
                return !(isTabletop ^ mTabletopRotations.contains(mRotation));
            }
            return true;
        }

        DeviceStateController.FoldState getFoldState() {
            return mFoldState;
        DeviceStateController.DeviceState getFoldState() {
            return mDeviceState;
        }

        boolean isSeparatingHinge() {
            return mFoldState == DeviceStateController.FoldState.HALF_FOLDED
                    || (mFoldState == DeviceStateController.FoldState.OPEN
            return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
                    || (mDeviceState == DeviceStateController.DeviceState.OPEN
                        && mIsDisplayAlwaysSeparatingHinge);
        }

        boolean overrideFrozenRotation() {
            return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
            return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
        }

        boolean shouldRevertOverriddenRotation() {
            return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
            // When transitioning to open.
            return mDeviceState == DeviceStateController.DeviceState.OPEN
                    && mInHalfFoldTransition
                    && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
                    && mUserRotationMode
@@ -1676,30 +1677,30 @@ public class DisplayRotation {
            return savedRotation;
        }

        void foldStateChanged(DeviceStateController.FoldState newState) {
        void foldStateChanged(DeviceStateController.DeviceState newState) {
            ProtoLog.v(WM_DEBUG_ORIENTATION,
                    "foldStateChanged: displayId %d, halfFoldStateChanged %s, "
                    + "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
                    + "mLastOrientation: %d, mRotation: %d",
                    mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
                    mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
            if (mFoldState == DeviceStateController.FoldState.UNKNOWN) {
                mFoldState = newState;
            if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
                mDeviceState = newState;
                return;
            }
            if (newState == DeviceStateController.FoldState.HALF_FOLDED
                    && mFoldState != DeviceStateController.FoldState.HALF_FOLDED) {
            if (newState == DeviceStateController.DeviceState.HALF_FOLDED
                    && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
                // The device has transitioned to HALF_FOLDED state: save the current rotation and
                // update the device rotation.
                mHalfFoldSavedRotation = mRotation;
                mFoldState = newState;
                mDeviceState = newState;
                // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
                // return true, so rotation is unlocked.
                mService.updateRotation(false /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
            } else {
                mInHalfFoldTransition = true;
                mFoldState = newState;
                mDeviceState = newState;
                // Tell the device to update its orientation.
                mService.updateRotation(false /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
@@ -1822,7 +1823,7 @@ public class DisplayRotation {
            final long mTimestamp = System.currentTimeMillis();
            final int mHalfFoldSavedRotation;
            final boolean mInHalfFoldTransition;
            final DeviceStateController.FoldState mFoldState;
            final DeviceStateController.DeviceState mDeviceState;
            @Nullable final String mDisplayRotationCompatPolicySummary;

            Record(DisplayRotation dr, int fromRotation, int toRotation) {
@@ -1852,11 +1853,11 @@ public class DisplayRotation {
                if (dr.mFoldController != null) {
                    mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
                    mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
                    mFoldState = dr.mFoldController.mFoldState;
                    mDeviceState = dr.mFoldController.mDeviceState;
                } else {
                    mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
                    mInHalfFoldTransition = false;
                    mFoldState = DeviceStateController.FoldState.UNKNOWN;
                    mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
                }
                mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
                        ? null
@@ -1882,7 +1883,7 @@ public class DisplayRotation {
                    pw.println(prefix + " halfFoldSavedRotation="
                            + mHalfFoldSavedRotation
                            + " mInHalfFoldTransition=" + mInHalfFoldTransition
                            + " mFoldState=" + mFoldState);
                            + " mFoldState=" + mDeviceState);
                }
                if (mDisplayRotationCompatPolicySummary != null) {
                    pw.println(prefix + mDisplayRotationCompatPolicySummary);
Loading