Re-evaluate tent/wedge mode on display rotation/wakelock changes
Currently, we re-evaluate if the device is likely in tent/wedge
mode on accelerometer/device rotation sensor changes.
We didn't do this whenever display rotates or when screen
wakelock state changes. It worked only because it piggybacked
on sensor events: when new sensor events received we also
took into account display/wakelock conditions.
But in case there are no sensor events, these conditions won't work.
Added explicit re-evaluation requests for these cases as well.
Bug: 419294518
Test: BookStyleDeviceStatePolicyTest
Flag: EXEMPT bugfix
Change-Id: I2c6f70048ae220d31ad3a3a1cbabaaa790b4aade
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
index 55d23585..87ed18f 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
@@ -34,7 +34,10 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
@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 @@
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 @@
@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 @@
@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 @@
mLastDeviceOrientationSensorEvent = event;
}
- mOnSensorUpdatedListener.run();
+ mOnEstimationChanged.run();
}
@Override
@@ -328,6 +316,16 @@
}
+ /**
+ * 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 @@
}
/**
- * 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 @@
return true;
}
+ if (mFeatureFlags.forceFoldablesTentModeWithScreenWakelock()
+ && mScreenTimeoutPolicy == PowerManager.SCREEN_TIMEOUT_KEEP_DISPLAY_ON) {
+ return true;
+ }
+
return false;
}
@@ -414,6 +420,7 @@
*/
public void onDisplayRotationChanged(int rotation) {
mLastScreenRotation = rotation;
+ mOnEstimationChanged.run();
}
/**
@@ -430,6 +437,7 @@
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));
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
index c25d5ef..d8c0104 100644
--- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
@@ -476,6 +476,21 @@
}
@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);
sendRightSideFlatSensorEvent(false);
@@ -644,6 +659,47 @@
}
@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 {
mFakeFeatureFlags.setFlag(Flags.FLAG_FORCE_FOLDABLES_TENT_MODE_WITH_SCREEN_WAKELOCK, false);