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

Commit fd3c215e authored by Nick Chameyev's avatar Nick Chameyev Committed by Android (Google) Code Review
Browse files

Merge "Re-evaluate tent/wedge mode on display rotation/wakelock changes" into main

parents 34449d5d 8110fba9
Loading
Loading
Loading
Loading
+44 −36
Original line number Diff line number Diff line
@@ -34,7 +34,10 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.PowerManager;
import android.os.PowerManager.ScreenTimeoutPolicy;
import android.os.PowerManager.ScreenTimeoutPolicyListener;
import android.util.ArraySet;
import android.util.Dumpable;
import android.util.Slog;
@@ -75,9 +78,6 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
    private final FeatureFlags mFeatureFlags;
    private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();

    @PowerManager.ScreenTimeoutPolicy
    private volatile int mScreenTimeoutPolicy;

    /**
     * Creates {@link BookStyleClosedStatePredicate}. It is expected that the device has a pair
     * of accelerometer sensors (one for each movable part of the device), see parameter
@@ -115,7 +115,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        final Sensor orientationSensor = sensorManager.getDefaultSensor(
                Sensor.TYPE_DEVICE_ORIENTATION);

        mPostureEstimator = new PostureEstimator(mHandler, sensorManager,
        mPostureEstimator = new PostureEstimator(mHandler, mFeatureFlags, sensorManager,
                leftAccelerometerSensor, rightAccelerometerSensor, orientationSensor,
                updatesListener::onClosedStateUpdated);
    }
@@ -127,8 +127,8 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
    public void init() {
        if (mFeatureFlags.forceFoldablesTentModeWithScreenWakelock()) {
            try {
                mPowerManager.addScreenTimeoutPolicyListener(DEFAULT_DISPLAY, Runnable::run,
                        new ScreenTimeoutPolicyListener());
                mPowerManager.addScreenTimeoutPolicyListener(DEFAULT_DISPLAY,
                        new HandlerExecutor(mHandler), mPostureEstimator);
            } catch (IllegalStateException exception) {
                // TODO: b/389613319 - remove after removing the screen timeout policy API flagging
                Slog.e(TAG, "Error subscribing to the screen timeout policy changes");
@@ -149,8 +149,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt

        mPostureEstimator.onDeviceClosedStatusChanged(hingeAngle == ANGLE_0);

        final boolean isLikelyTentOrWedgeMode = mPostureEstimator.isLikelyTentOrWedgeMode()
                || shouldForceTentOrWedgeMode();
        final boolean isLikelyTentOrWedgeMode = mPostureEstimator.isLikelyTentOrWedgeMode();

        final PreferredScreen preferredScreen = mClosedStateCalculator.
                calculatePreferredScreen(hingeAngle, isLikelyTentOrWedgeMode,
@@ -159,14 +158,6 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        return preferredScreen == OUTER;
    }

    private boolean shouldForceTentOrWedgeMode() {
        if (!mFeatureFlags.forceFoldablesTentModeWithScreenWakelock()) {
            return false;
        }

        return mScreenTimeoutPolicy == PowerManager.SCREEN_TIMEOUT_KEEP_DISPLAY_ON;
    }

    private HingeAngle hingeAngleFromFloat(float hingeAngle) {
        if (hingeAngle == 0f) {
            return ANGLE_0;
@@ -204,7 +195,6 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
    @Override
    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
        writer.println("  " + getDumpableName());
        writer.println("  mScreenTimeoutPolicy=" + mScreenTimeoutPolicy);
        mPostureEstimator.dump(writer, args);
        mClosedStateCalculator.dump(writer, args);
    }
@@ -213,19 +203,11 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        void onClosedStateUpdated();
    }

    private class ScreenTimeoutPolicyListener implements
            PowerManager.ScreenTimeoutPolicyListener {
        @Override
        public void onScreenTimeoutPolicyChanged(int screenTimeoutPolicy) {
            // called from the binder thread
            mScreenTimeoutPolicy = screenTimeoutPolicy;
        }
    }

    /**
     * Estimates if the device is going to enter wedge/tent mode based on the sensor data
     */
    private static class PostureEstimator implements SensorEventListener, Dumpable {
    private static class PostureEstimator implements SensorEventListener,
            ScreenTimeoutPolicyListener, Dumpable {

        private static final String FLAT_INCLINATION_THRESHOLD_DEGREES_PROPERTY
                = "persist.foldable_postures.wedge_inclination_threshold_degrees";
@@ -245,9 +227,10 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        @Nullable
        private final Sensor mRightAccelerometerSensor;
        private final Sensor mOrientationSensor;
        private final Runnable mOnSensorUpdatedListener;
        private final Runnable mOnEstimationChanged;

        private final ConditionSensorListener mConditionedSensorListener;
        private final FeatureFlags mFeatureFlags;

        @Nullable
        private float[] mRightGravityVector;
@@ -261,17 +244,22 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        @Nullable
        private SensorEvent mLastDeviceOrientationSensorEvent = null;

        @ScreenTimeoutPolicy
        private int mScreenTimeoutPolicy = PowerManager.SCREEN_TIMEOUT_ACTIVE;

        private boolean mScreenTurnedOn = false;
        private boolean mDeviceClosed = false;

        public PostureEstimator(Handler handler, SensorManager sensorManager,
                @Nullable Sensor leftAccelerometerSensor, @Nullable Sensor rightAccelerometerSensor,
                Sensor orientationSensor, Runnable onSensorUpdated) {
        public PostureEstimator(Handler handler, FeatureFlags featureFlags,
                SensorManager sensorManager, @Nullable Sensor leftAccelerometerSensor,
                @Nullable Sensor rightAccelerometerSensor, Sensor orientationSensor,
                Runnable onEstimationChanged) {
            mLeftAccelerometerSensor = leftAccelerometerSensor;
            mRightAccelerometerSensor = rightAccelerometerSensor;
            mOrientationSensor = orientationSensor;

            mOnSensorUpdatedListener = onSensorUpdated;
            mFeatureFlags = featureFlags;
            mOnEstimationChanged = onEstimationChanged;

            final List<SensorSubscription> sensorSubscriptions = new ArrayList<>();
            if (mLeftAccelerometerSensor != null) {
@@ -320,7 +308,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
                mLastDeviceOrientationSensorEvent = event;
            }

            mOnSensorUpdatedListener.run();
            mOnEstimationChanged.run();
        }

        @Override
@@ -328,6 +316,16 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt

        }

        /**
         * Called from {@link BookStyleClosedStatePredicate#mHandler}'s thread
         * (system server's main thread)
         */
        @Override
        public void onScreenTimeoutPolicyChanged(int screenTimeoutPolicy) {
            mScreenTimeoutPolicy = screenTimeoutPolicy;
            mOnEstimationChanged.run();
        }

        private void setNewValueWithHighPassFilter(float[] output, float[] newValues) {
            final float alpha = GRAVITY_VECTOR_LOW_PASS_ALPHA_VALUE;
            output[0] = alpha * output[0] + (1 - alpha) * newValues[0];
@@ -345,9 +343,12 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
        }

        /**
         * Returns true if the phone is likely in tent or wedge mode when unfolding. Tent mode
         * is detected by checking if the phone is in seascape position, screen is rotated to
         * landscape or seascape, or if the right side of the device is mostly flat.
         * Returns true if the phone is likely in tent or wedge mode when unfolding.
         * Tent/wedge mode is detected by checking if:
         *  - the phone is in seascape position
         *  - screen is rotated to landscape or seascape
         *  - if the right side of the device is mostly flat
         *  - if there is an active screen wake lock
         */
        public boolean isLikelyTentOrWedgeMode() {
            boolean isScreenLandscapeOrSeascape = Objects.equals(mLastScreenRotation,
@@ -369,6 +370,11 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
                return true;
            }

            if (mFeatureFlags.forceFoldablesTentModeWithScreenWakelock()
                    && mScreenTimeoutPolicy == PowerManager.SCREEN_TIMEOUT_KEEP_DISPLAY_ON) {
                return true;
            }

            return false;
        }

@@ -414,6 +420,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
         */
        public void onDisplayRotationChanged(int rotation) {
            mLastScreenRotation = rotation;
            mOnEstimationChanged.run();
        }

        /**
@@ -430,6 +437,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
            writer.println("      isLikelyTentOrWedgeMode = " + isLikelyTentOrWedgeMode());
            writer.println("      mScreenTurnedOn = " + mScreenTurnedOn);
            writer.println("      mLastScreenRotation = " + mLastScreenRotation);
            writer.println("      mScreenTimeoutPolicy=" + mScreenTimeoutPolicy);
            writer.println("      mDeviceClosed = " + mDeviceClosed);
            writer.println("      mLeftGravityVector = " + Arrays.toString(mLeftGravityVector));
            writer.println("      mRightGravityVector = " + Arrays.toString(mRightGravityVector));
+56 −0
Original line number Diff line number Diff line
@@ -475,6 +475,21 @@ public final class BookStyleDeviceStatePolicyTest {
        verify(mListener, never()).onStateChanged(mDeviceStateCaptor.capture());
    }

    @Test
    public void test_unfoldTo30Degrees_becomesLandscapeScreenRotation_keepsClosedState() {
        sendHingeAngle(0f);
        sendRightSideFlatSensorEvent(false);
        sendScreenRotation(Surface.ROTATION_0);
        mProvider.setListener(mListener);
        assertLatestReportedState(DEVICE_STATE_CLOSED);
        sendScreenRotation(Surface.ROTATION_90);
        clearInvocations(mListener);

        sendHingeAngle(30f);

        verify(mListener, never()).onStateChanged(mDeviceStateCaptor.capture());
    }

    @Test
    public void test_unfoldTo30Degrees_seascapeScreenRotation_keepsClosedState() {
        sendHingeAngle(0f);
@@ -643,6 +658,47 @@ public final class BookStyleDeviceStatePolicyTest {
        assertLatestReportedState(DEVICE_STATE_HALF_OPENED);
    }

    @Test
    public void test_unfoldTo85Degrees_afterScreenWakeLockBecomesActive_keepsClosedDeviceState()
            throws Exception {
        mFakeFeatureFlags.setFlag(Flags.FLAG_FORCE_FOLDABLES_TENT_MODE_WITH_SCREEN_WAKELOCK, true);
        mInstrumentation.runOnMainSync(() -> mProvider = createProvider());
        mPolicy.getDeviceStateProvider().onSystemReady();
        sendHingeAngle(0f);
        mProvider.setListener(mListener);
        assertLatestReportedState(DEVICE_STATE_CLOSED);

        final ScreenTimeoutPolicyListener listener = captureScreenTimeoutPolicyListener();
        listener.onScreenTimeoutPolicyChanged(PowerManager.SCREEN_TIMEOUT_KEEP_DISPLAY_ON);

        sendHingeAngle(15f);
        assertLatestReportedState(DEVICE_STATE_CLOSED);

        sendHingeAngle(85f);
        assertLatestReportedState(DEVICE_STATE_CLOSED);
    }

    @Test
    public void test_unfoldTo85Degrees_screenWakeLockPresentAndThenRemoved_movesToHalfOpenedState()
            throws Exception {
        mFakeFeatureFlags.setFlag(Flags.FLAG_FORCE_FOLDABLES_TENT_MODE_WITH_SCREEN_WAKELOCK, true);
        mInstrumentation.runOnMainSync(() -> mProvider = createProvider());
        mPolicy.getDeviceStateProvider().onSystemReady();
        sendHingeAngle(0f);
        mProvider.setListener(mListener);
        assertLatestReportedState(DEVICE_STATE_CLOSED);

        final ScreenTimeoutPolicyListener listener = captureScreenTimeoutPolicyListener();
        listener.onScreenTimeoutPolicyChanged(PowerManager.SCREEN_TIMEOUT_KEEP_DISPLAY_ON);
        listener.onScreenTimeoutPolicyChanged(PowerManager.SCREEN_TIMEOUT_ACTIVE);

        sendHingeAngle(15f);
        assertLatestReportedState(DEVICE_STATE_HALF_OPENED);

        sendHingeAngle(85f);
        assertLatestReportedState(DEVICE_STATE_HALF_OPENED);
    }

    @Test
    public void test_unfoldTo85Degrees_notSubscribedToWakeLocks_forceTentModeWithWakeLockDisabled()
            throws Exception {