Merge "Make DeviceStateController generic" into tm-qpr-dev

This commit is contained in:
Kevin Chyn
2023-01-26 08:03:20 +00:00
committed by Android (Google) Code Review
10 changed files with 179 additions and 147 deletions

View File

@@ -652,6 +652,16 @@
The default is false. --> The default is false. -->
<bool name="config_lidControlsSleep">false</bool> <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 <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
display fold controller. Default is empty. --> display fold controller. Default is empty. -->
<integer-array name="config_foldedDeviceStates"> <integer-array name="config_foldedDeviceStates">
@@ -672,6 +682,16 @@
--> -->
</integer-array> </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 <!-- Indicates whether the window manager reacts to half-fold device states by overriding
rotation. --> rotation. -->
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool> <bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>

View File

@@ -4014,8 +4014,10 @@
<java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" /> <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
<!-- For Foldables --> <!-- For Foldables -->
<java-symbol type="array" name="config_openDeviceStates" />
<java-symbol type="array" name="config_foldedDeviceStates" /> <java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="array" name="config_halfFoldedDeviceStates" /> <java-symbol type="array" name="config_halfFoldedDeviceStates" />
<java-symbol type="array" name="config_rearDisplayDeviceStates" />
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" /> <java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" /> <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" /> <java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />

View File

@@ -16,80 +16,92 @@
package com.android.server.wm; package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerExecutor; import android.os.HandlerExecutor;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* Class that registers callbacks with the {@link DeviceStateManager} and * Class that registers callbacks with the {@link DeviceStateManager} and responds to device
* responds to fold state changes by forwarding such events to a delegate. * changes.
*/ */
final class DeviceStateController { final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {
@NonNull
private final DeviceStateManager mDeviceStateManager; 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 { public enum DeviceState {
UNKNOWN, OPEN, FOLDED, HALF_FOLDED UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
} }
DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) { DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
mContext = context; mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
mDeviceStateManager = mContext.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) { if (mDeviceStateManager != null) {
mDeviceStateListener = new FoldStateListener(mContext, delegate); mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
mDeviceStateManager
.registerCallback(new HandlerExecutor(handler),
mDeviceStateListener);
} }
} }
void unregisterFromDeviceStateManager() { void unregisterFromDeviceStateManager() {
if (mDeviceStateListener != null) { if (mDeviceStateManager != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateListener); mDeviceStateManager.unregisterCallback(this);
} }
} }
/** void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
* A listener for half-fold device state events that dispatches state changes to a delegate. mDeviceStateCallbacks.add(callback);
*/ }
static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
private final int[] mHalfFoldedDeviceStates; @Override
private final int[] mFoldedDeviceStates; public void onStateChanged(int state) {
final DeviceState deviceState;
@Nullable if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
private FoldState mLastResult; deviceState = DeviceState.HALF_FOLDED;
private final Consumer<FoldState> mDelegate; } else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
deviceState = DeviceState.FOLDED;
FoldStateListener(Context context, Consumer<FoldState> delegate) { } else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
mFoldedDeviceStates = context.getResources().getIntArray( deviceState = DeviceState.REAR;
com.android.internal.R.array.config_foldedDeviceStates); } else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
mHalfFoldedDeviceStates = context.getResources().getIntArray( deviceState = DeviceState.OPEN;
com.android.internal.R.array.config_halfFoldedDeviceStates); } else {
mDelegate = delegate; deviceState = DeviceState.UNKNOWN;
} }
@Override if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
public void onStateChanged(int state) { mLastDeviceState = deviceState;
final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
FoldState result; for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
if (halfFolded) { callback.accept(mLastDeviceState);
result = FoldState.HALF_FOLDED;
} else {
final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
result = folded ? FoldState.FOLDED : FoldState.OPEN;
}
if (mLastResult == null || !mLastResult.equals(result)) {
mLastResult = result;
mDelegate.accept(result);
} }
} }
} }

View File

@@ -1125,14 +1125,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mAtmService.getRecentTasks().getInputListener()); mWmService.mAtmService.getRecentTasks().getInputListener());
} }
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
mDisplayPolicy = new DisplayPolicy(mWmService, this); mDisplayPolicy = new DisplayPolicy(mWmService, this);
mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address); mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH, final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
newFoldState -> { (@NonNull DeviceStateController.DeviceState newFoldState) -> {
mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState); mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
mDisplayRotation.foldStateChanged(newFoldState); mDisplayRotation.foldStateChanged(newFoldState);
}); };
mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);
mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat( mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
R.dimen.config_closeToSquareDisplayMaxAspectRatio); R.dimen.config_closeToSquareDisplayMaxAspectRatio);

View File

@@ -1573,7 +1573,7 @@ public class DisplayRotation {
proto.end(token); proto.end(token);
} }
boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) { boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
if (mFoldController == null) return false; if (mFoldController == null) return false;
return mFoldController.isDeviceInPosture(state, isTabletop); return mFoldController.isDeviceInPosture(state, isTabletop);
} }
@@ -1585,10 +1585,10 @@ public class DisplayRotation {
/** /**
* Called by the DeviceStateManager callback when the device state changes. * Called by the DeviceStateManager callback when the device state changes.
*/ */
void foldStateChanged(DeviceStateController.FoldState foldState) { void foldStateChanged(DeviceStateController.DeviceState deviceState) {
if (mFoldController != null) { if (mFoldController != null) {
synchronized (mLock) { synchronized (mLock) {
mFoldController.foldStateChanged(foldState); mFoldController.foldStateChanged(deviceState);
} }
} }
} }
@@ -1596,8 +1596,8 @@ public class DisplayRotation {
private class FoldController { private class FoldController {
@Surface.Rotation @Surface.Rotation
private int mHalfFoldSavedRotation = -1; // No saved rotation private int mHalfFoldSavedRotation = -1; // No saved rotation
private DeviceStateController.FoldState mFoldState = private DeviceStateController.DeviceState mDeviceState =
DeviceStateController.FoldState.UNKNOWN; DeviceStateController.DeviceState.UNKNOWN;
private boolean mInHalfFoldTransition = false; private boolean mInHalfFoldTransition = false;
private final boolean mIsDisplayAlwaysSeparatingHinge; private final boolean mIsDisplayAlwaysSeparatingHinge;
private final Set<Integer> mTabletopRotations; private final Set<Integer> mTabletopRotations;
@@ -1637,32 +1637,33 @@ public class DisplayRotation {
R.bool.config_isDisplayHingeAlwaysSeparating); R.bool.config_isDisplayHingeAlwaysSeparating);
} }
boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) { boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
if (state != mFoldState) { if (state != mDeviceState) {
return false; return false;
} }
if (mFoldState == DeviceStateController.FoldState.HALF_FOLDED) { if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
return !(isTabletop ^ mTabletopRotations.contains(mRotation)); return !(isTabletop ^ mTabletopRotations.contains(mRotation));
} }
return true; return true;
} }
DeviceStateController.FoldState getFoldState() { DeviceStateController.DeviceState getFoldState() {
return mFoldState; return mDeviceState;
} }
boolean isSeparatingHinge() { boolean isSeparatingHinge() {
return mFoldState == DeviceStateController.FoldState.HALF_FOLDED return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
|| (mFoldState == DeviceStateController.FoldState.OPEN || (mDeviceState == DeviceStateController.DeviceState.OPEN
&& mIsDisplayAlwaysSeparatingHinge); && mIsDisplayAlwaysSeparatingHinge);
} }
boolean overrideFrozenRotation() { boolean overrideFrozenRotation() {
return mFoldState == DeviceStateController.FoldState.HALF_FOLDED; return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
} }
boolean shouldRevertOverriddenRotation() { boolean shouldRevertOverriddenRotation() {
return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open. // When transitioning to open.
return mDeviceState == DeviceStateController.DeviceState.OPEN
&& mInHalfFoldTransition && mInHalfFoldTransition
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted. && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
&& mUserRotationMode && mUserRotationMode
@@ -1676,30 +1677,30 @@ public class DisplayRotation {
return savedRotation; return savedRotation;
} }
void foldStateChanged(DeviceStateController.FoldState newState) { void foldStateChanged(DeviceStateController.DeviceState newState) {
ProtoLog.v(WM_DEBUG_ORIENTATION, ProtoLog.v(WM_DEBUG_ORIENTATION,
"foldStateChanged: displayId %d, halfFoldStateChanged %s, " "foldStateChanged: displayId %d, halfFoldStateChanged %s, "
+ "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, " + "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
+ "mLastOrientation: %d, mRotation: %d", + "mLastOrientation: %d, mRotation: %d",
mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation, mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
mUserRotation, mLastSensorRotation, mLastOrientation, mRotation); mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
if (mFoldState == DeviceStateController.FoldState.UNKNOWN) { if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
mFoldState = newState; mDeviceState = newState;
return; return;
} }
if (newState == DeviceStateController.FoldState.HALF_FOLDED if (newState == DeviceStateController.DeviceState.HALF_FOLDED
&& mFoldState != DeviceStateController.FoldState.HALF_FOLDED) { && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
// The device has transitioned to HALF_FOLDED state: save the current rotation and // The device has transitioned to HALF_FOLDED state: save the current rotation and
// update the device rotation. // update the device rotation.
mHalfFoldSavedRotation = mRotation; mHalfFoldSavedRotation = mRotation;
mFoldState = newState; mDeviceState = newState;
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
// return true, so rotation is unlocked. // return true, so rotation is unlocked.
mService.updateRotation(false /* alwaysSendConfiguration */, mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */); false /* forceRelayout */);
} else { } else {
mInHalfFoldTransition = true; mInHalfFoldTransition = true;
mFoldState = newState; mDeviceState = newState;
// Tell the device to update its orientation. // Tell the device to update its orientation.
mService.updateRotation(false /* alwaysSendConfiguration */, mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */); false /* forceRelayout */);
@@ -1822,7 +1823,7 @@ public class DisplayRotation {
final long mTimestamp = System.currentTimeMillis(); final long mTimestamp = System.currentTimeMillis();
final int mHalfFoldSavedRotation; final int mHalfFoldSavedRotation;
final boolean mInHalfFoldTransition; final boolean mInHalfFoldTransition;
final DeviceStateController.FoldState mFoldState; final DeviceStateController.DeviceState mDeviceState;
@Nullable final String mDisplayRotationCompatPolicySummary; @Nullable final String mDisplayRotationCompatPolicySummary;
Record(DisplayRotation dr, int fromRotation, int toRotation) { Record(DisplayRotation dr, int fromRotation, int toRotation) {
@@ -1852,11 +1853,11 @@ public class DisplayRotation {
if (dr.mFoldController != null) { if (dr.mFoldController != null) {
mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation; mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition; mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
mFoldState = dr.mFoldController.mFoldState; mDeviceState = dr.mFoldController.mDeviceState;
} else { } else {
mHalfFoldSavedRotation = NO_FOLD_CONTROLLER; mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
mInHalfFoldTransition = false; mInHalfFoldTransition = false;
mFoldState = DeviceStateController.FoldState.UNKNOWN; mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
} }
mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
? null ? null
@@ -1882,7 +1883,7 @@ public class DisplayRotation {
pw.println(prefix + " halfFoldSavedRotation=" pw.println(prefix + " halfFoldSavedRotation="
+ mHalfFoldSavedRotation + mHalfFoldSavedRotation
+ " mInHalfFoldTransition=" + mInHalfFoldTransition + " mInHalfFoldTransition=" + mInHalfFoldTransition
+ " mFoldState=" + mFoldState); + " mFoldState=" + mDeviceState);
} }
if (mDisplayRotationCompatPolicySummary != null) { if (mDisplayRotationCompatPolicySummary != null) {
pw.println(prefix + mDisplayRotationCompatPolicySummary); pw.println(prefix + mDisplayRotationCompatPolicySummary);

View File

@@ -542,7 +542,7 @@ final class LetterboxUiController {
// Note that we check the task rather than the parent as with ActivityEmbedding the parent might // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
// be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
// actually fullscreen. // actually fullscreen.
private boolean isDisplayFullScreenAndInPosture(DeviceStateController.FoldState state, private boolean isDisplayFullScreenAndInPosture(DeviceStateController.DeviceState state,
boolean isTabletop) { boolean isTabletop) {
Task task = mActivityRecord.getTask(); Task task = mActivityRecord.getTask();
return mActivityRecord.mDisplayContent != null return mActivityRecord.mDisplayContent != null
@@ -568,7 +568,7 @@ final class LetterboxUiController {
// Don't check resolved configuration because it may not be updated yet during // Don't check resolved configuration because it may not be updated yet during
// configuration change. // configuration change.
boolean bookMode = isDisplayFullScreenAndInPosture( boolean bookMode = isDisplayFullScreenAndInPosture(
DeviceStateController.FoldState.HALF_FOLDED, false /* isTabletop */); DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */);
return isHorizontalReachabilityEnabled(parentConfiguration) return isHorizontalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving // Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities. // between apps or activities.
@@ -580,7 +580,7 @@ final class LetterboxUiController {
// Don't check resolved configuration because it may not be updated yet during // Don't check resolved configuration because it may not be updated yet during
// configuration change. // configuration change.
boolean tabletopMode = isDisplayFullScreenAndInPosture( boolean tabletopMode = isDisplayFullScreenAndInPosture(
DeviceStateController.FoldState.HALF_FOLDED, true /* isTabletop */); DeviceStateController.DeviceState.HALF_FOLDED, true /* isTabletop */);
return isVerticalReachabilityEnabled(parentConfiguration) return isVerticalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving // Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities. // between apps or activities.
@@ -1107,7 +1107,7 @@ final class LetterboxUiController {
int letterboxPositionForHorizontalReachability = getLetterboxConfiguration() int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
.getLetterboxPositionForHorizontalReachability( .getLetterboxPositionForHorizontalReachability(
isDisplayFullScreenAndInPosture( isDisplayFullScreenAndInPosture(
DeviceStateController.FoldState.HALF_FOLDED, DeviceStateController.DeviceState.HALF_FOLDED,
false /* isTabletop */)); false /* isTabletop */));
positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition( positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
letterboxPositionForHorizontalReachability); letterboxPositionForHorizontalReachability);
@@ -1115,7 +1115,7 @@ final class LetterboxUiController {
int letterboxPositionForVerticalReachability = getLetterboxConfiguration() int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
.getLetterboxPositionForVerticalReachability( .getLetterboxPositionForVerticalReachability(
isDisplayFullScreenAndInPosture( isDisplayFullScreenAndInPosture(
DeviceStateController.FoldState.HALF_FOLDED, DeviceStateController.DeviceState.HALF_FOLDED,
true /* isTabletop */)); true /* isTabletop */));
positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition( positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
letterboxPositionForVerticalReachability); letterboxPositionForVerticalReachability);

View File

@@ -51,10 +51,10 @@ public class PhysicalDisplaySwitchTransitionLauncher {
/** /**
* Called by the DeviceStateManager callback when the state changes. * Called by the DeviceStateManager callback when the state changes.
*/ */
void foldStateChanged(DeviceStateController.FoldState newFoldState) { void foldStateChanged(DeviceStateController.DeviceState newDeviceState) {
// Ignore transitions to/from half-folded. // Ignore transitions to/from half-folded.
if (newFoldState == DeviceStateController.FoldState.HALF_FOLDED) return; if (newDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) return;
mIsFolded = newFoldState == DeviceStateController.FoldState.FOLDED; mIsFolded = newDeviceState == DeviceStateController.DeviceState.FOLDED;
} }
/** /**

View File

@@ -16,13 +16,12 @@
package com.android.server.wm; package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
@@ -32,9 +31,10 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.internal.R;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -48,92 +48,76 @@ import java.util.function.Consumer;
@Presubmit @Presubmit
public class DeviceStateControllerTests { public class DeviceStateControllerTests {
private DeviceStateController.FoldStateListener mFoldStateListener;
private DeviceStateController mTarget; private DeviceStateController mTarget;
private DeviceStateControllerBuilder mBuilder; private DeviceStateControllerBuilder mBuilder;
private Context mMockContext; private Context mMockContext;
private Handler mMockHandler;
private Resources mMockRes;
private DeviceStateManager mMockDeviceStateManager; private DeviceStateManager mMockDeviceStateManager;
private DeviceStateController.DeviceState mCurrentState =
private Consumer<DeviceStateController.FoldState> mDelegate; DeviceStateController.DeviceState.UNKNOWN;
private DeviceStateController.FoldState mCurrentState = DeviceStateController.FoldState.UNKNOWN;
@Before @Before
public void setUp() { public void setUp() {
mBuilder = new DeviceStateControllerBuilder(); mBuilder = new DeviceStateControllerBuilder();
mCurrentState = DeviceStateController.FoldState.UNKNOWN; mCurrentState = DeviceStateController.DeviceState.UNKNOWN;
} }
private void initialize(boolean supportFold, boolean supportHalfFold) throws Exception { private void initialize(boolean supportFold, boolean supportHalfFold) {
mBuilder.setSupportFold(supportFold, supportHalfFold); mBuilder.setSupportFold(supportFold, supportHalfFold);
mDelegate = (newFoldState) -> { Consumer<DeviceStateController.DeviceState> delegate = (newFoldState) -> {
mCurrentState = newFoldState; mCurrentState = newFoldState;
}; };
mBuilder.setDelegate(mDelegate); mBuilder.setDelegate(delegate);
mBuilder.build(); mBuilder.build();
verifyFoldStateListenerRegistration(1); verify(mMockDeviceStateManager).registerCallback(any(), any());
} }
@Test @Test
public void testInitialization() throws Exception { public void testInitialization() {
initialize(true /* supportFold */, true /* supportHalfFolded */); initialize(true /* supportFold */, true /* supportHalfFolded */);
mFoldStateListener.onStateChanged(mUnfoldedStates[0]); mTarget.onStateChanged(mOpenDeviceStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
} }
@Test @Test
public void testInitializationWithNoFoldSupport() throws Exception { public void testInitializationWithNoFoldSupport() {
initialize(false /* supportFold */, false /* supportHalfFolded */); initialize(false /* supportFold */, false /* supportHalfFolded */);
mFoldStateListener.onStateChanged(mFoldedStates[0]); mTarget.onStateChanged(mFoldedStates[0]);
// Note that the folded state is ignored. // Note that the folded state is ignored.
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
} }
@Test @Test
public void testWithFoldSupported() throws Exception { public void testWithFoldSupported() {
initialize(true /* supportFold */, false /* supportHalfFolded */); initialize(true /* supportFold */, false /* supportHalfFolded */);
mFoldStateListener.onStateChanged(mUnfoldedStates[0]); mTarget.onStateChanged(mOpenDeviceStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
mFoldStateListener.onStateChanged(mFoldedStates[0]); mTarget.onStateChanged(mFoldedStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
mFoldStateListener.onStateChanged(mHalfFoldedStates[0]); mTarget.onStateChanged(mHalfFoldedStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); // Ignored assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
} }
@Test @Test
public void testWithHalfFoldSupported() throws Exception { public void testWithHalfFoldSupported() {
initialize(true /* supportFold */, true /* supportHalfFolded */); initialize(true /* supportFold */, true /* supportHalfFolded */);
mFoldStateListener.onStateChanged(mUnfoldedStates[0]); mTarget.onStateChanged(mOpenDeviceStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
mFoldStateListener.onStateChanged(mFoldedStates[0]); mTarget.onStateChanged(mFoldedStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
mFoldStateListener.onStateChanged(mHalfFoldedStates[0]); mTarget.onStateChanged(mHalfFoldedStates[0]);
assertEquals(mCurrentState, DeviceStateController.FoldState.HALF_FOLDED); assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
} }
private final int[] mFoldedStates = {0}; private final int[] mFoldedStates = {0};
private final int[] mUnfoldedStates = {1}; private final int[] mOpenDeviceStates = {1};
private final int[] mHalfFoldedStates = {2}; private final int[] mHalfFoldedStates = {2};
private final int[] mRearDisplayStates = {3};
private void verifyFoldStateListenerRegistration(int numOfInvocation) {
final ArgumentCaptor<DeviceStateController.FoldStateListener> listenerCaptor =
ArgumentCaptor.forClass(DeviceStateController.FoldStateListener.class);
verify(mMockDeviceStateManager, times(numOfInvocation)).registerCallback(
any(),
listenerCaptor.capture());
if (numOfInvocation > 0) {
mFoldStateListener = listenerCaptor.getValue();
}
}
private class DeviceStateControllerBuilder { private class DeviceStateControllerBuilder {
private boolean mSupportFold = false; private boolean mSupportFold = false;
private boolean mSupportHalfFold = false; private boolean mSupportHalfFold = false;
private Consumer<DeviceStateController.FoldState> mDelegate; private Consumer<DeviceStateController.DeviceState> mDelegate;
DeviceStateControllerBuilder setSupportFold( DeviceStateControllerBuilder setSupportFold(
boolean supportFold, boolean supportHalfFold) { boolean supportFold, boolean supportHalfFold) {
@@ -143,34 +127,44 @@ public class DeviceStateControllerTests {
} }
DeviceStateControllerBuilder setDelegate( DeviceStateControllerBuilder setDelegate(
Consumer<DeviceStateController.FoldState> delegate) { Consumer<DeviceStateController.DeviceState> delegate) {
mDelegate = delegate; mDelegate = delegate;
return this; return this;
} }
private void mockFold(boolean enableFold, boolean enableHalfFold) { private void mockFold(boolean enableFold, boolean enableHalfFold) {
if (enableFold || enableHalfFold) {
when(mMockContext.getResources()
.getIntArray(R.array.config_openDeviceStates))
.thenReturn(mOpenDeviceStates);
when(mMockContext.getResources()
.getIntArray(R.array.config_rearDisplayDeviceStates))
.thenReturn(mRearDisplayStates);
}
if (enableFold) { if (enableFold) {
when(mMockContext.getResources().getIntArray( when(mMockContext.getResources()
com.android.internal.R.array.config_foldedDeviceStates)) .getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(mFoldedStates); .thenReturn(mFoldedStates);
} }
if (enableHalfFold) { if (enableHalfFold) {
when(mMockContext.getResources().getIntArray( when(mMockContext.getResources()
com.android.internal.R.array.config_halfFoldedDeviceStates)) .getIntArray(R.array.config_halfFoldedDeviceStates))
.thenReturn(mHalfFoldedStates); .thenReturn(mHalfFoldedStates);
} }
} }
private void build() throws Exception { private void build() {
mMockContext = mock(Context.class); mMockContext = mock(Context.class);
mMockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mMockRes));
mMockDeviceStateManager = mock(DeviceStateManager.class); mMockDeviceStateManager = mock(DeviceStateManager.class);
when(mMockContext.getSystemService(DeviceStateManager.class)) when(mMockContext.getSystemService(DeviceStateManager.class))
.thenReturn(mMockDeviceStateManager); .thenReturn(mMockDeviceStateManager);
Resources mockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mockRes));
mockFold(mSupportFold, mSupportHalfFold); mockFold(mSupportFold, mSupportHalfFold);
mMockHandler = mock(Handler.class); Handler mockHandler = mock(Handler.class);
mTarget = new DeviceStateController(mMockContext, mMockHandler, mDelegate); mTarget = new DeviceStateController(mMockContext, mockHandler);
mTarget.registerDeviceStateCallback(mDelegate);
} }
} }
} }

View File

@@ -705,7 +705,7 @@ public class DisplayRotationTests {
enableOrientationSensor(); enableOrientationSensor();
mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN); mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
freezeRotation(Surface.ROTATION_270); freezeRotation(Surface.ROTATION_270);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0)); mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
@@ -715,7 +715,7 @@ public class DisplayRotationTests {
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... until half-fold // ... until half-fold
mTarget.foldStateChanged(DeviceStateController.FoldState.HALF_FOLDED); mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
assertTrue(waitForUiHandler()); assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false); verify(sMockWm).updateRotation(false, false);
assertTrue(waitForUiHandler()); assertTrue(waitForUiHandler());
@@ -723,7 +723,7 @@ public class DisplayRotationTests {
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... then transition back to flat // ... then transition back to flat
mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN); mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
assertTrue(waitForUiHandler()); assertTrue(waitForUiHandler());
verify(sMockWm, atLeast(1)).updateRotation(false, false); verify(sMockWm, atLeast(1)).updateRotation(false, false);
assertTrue(waitForUiHandler()); assertTrue(waitForUiHandler());

View File

@@ -98,7 +98,7 @@ import androidx.test.filters.MediumTest;
import com.android.internal.policy.SystemBarUtils; import com.android.internal.policy.SystemBarUtils;
import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.DeviceStateController.FoldState; import com.android.server.wm.DeviceStateController.DeviceState;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -3186,9 +3186,9 @@ public class SizeCompatTests extends WindowTestsBase {
private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) { private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
final DisplayRotation r = mActivity.mDisplayContent.getDisplayRotation(); final DisplayRotation r = mActivity.mDisplayContent.getDisplayRotation();
doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge(); doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
doReturn(false).when(r).isDeviceInPosture(any(FoldState.class), anyBoolean()); doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
if (isHalfFolded) { if (isHalfFolded) {
doReturn(true).when(r).isDeviceInPosture(FoldState.HALF_FOLDED, isTabletop); doReturn(true).when(r).isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
} }
mActivity.recomputeConfiguration(); mActivity.recomputeConfiguration();
} }