Merge "Coordinate rotation for concurrent mode"

This commit is contained in:
Kevin Chyn
2023-02-03 04:19:37 +00:00
committed by Android (Google) Code Review
13 changed files with 441 additions and 31 deletions

View File

@@ -27,6 +27,7 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -245,4 +246,23 @@ public class RotationUtils {
throw new IllegalArgumentException("Unknown rotation: " + rotation);
}
}
/**
* Reverses the rotation direction around the Z axis. Note that this method assumes all
* rotations are relative to {@link Surface.ROTATION_0}.
*
* @param rotation the original rotation.
* @return the new rotation that should be applied.
*/
@Surface.Rotation
public static int reverseRotationDirectionAroundZAxis(@Surface.Rotation int rotation) {
// Flipping 270 and 90 has the same effect as changing the direction which rotation is
// applied.
if (rotation == Surface.ROTATION_90) {
rotation = Surface.ROTATION_270;
} else if (rotation == Surface.ROTATION_270) {
rotation = Surface.ROTATION_90;
}
return rotation;
}
}

View File

@@ -983,6 +983,13 @@
<integer-array name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis">
</integer-array>
<!-- Boolean indicating whether secondary built-in displays should have their orientation
match the active default display. This config assumes that the secondary display only
requires swapping ROTATION_90 and ROTATION_270.
TODO(b/265991392): This should eventually be configured and parsed in
display_settings.xml -->
<bool name="config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay">true</bool>
<!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. -->
<integer-array name="config_availableColorModes">
<!-- Example:

View File

@@ -3404,6 +3404,13 @@
TODO(b/265312193): Remove this workaround when this bug is fixed.-->
<java-symbol type="array" name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis" />
<!-- Boolean indicating whether secondary built-in displays should have their orientation
match the active default display. This config assumes that the secondary display only
requires swapping ROTATION_90 and ROTATION_270.
TODO(b/265991392): This should eventually be configured and parsed in
display_settings.xml -->
<java-symbol type="bool" name="config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay" />
<!-- Default user restrictions for the SYSTEM user -->
<java-symbol type="array" name="config_defaultFirstUserRestrictions" />

View File

@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
@@ -48,9 +49,12 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
private final int[] mRearDisplayDeviceStates;
@NonNull
private final int[] mReverseRotationAroundZAxisStates;
@GuardedBy("this")
@NonNull
private final List<Consumer<DeviceState>> mDeviceStateCallbacks = new ArrayList<>();
private final boolean mMatchBuiltInDisplayOrientationToDefaultDisplay;
@Nullable
private DeviceState mLastDeviceState;
private int mCurrentState;
@@ -72,20 +76,19 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
.getIntArray(R.array.config_rearDisplayDeviceStates);
mReverseRotationAroundZAxisStates = context.getResources()
.getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis);
mMatchBuiltInDisplayOrientationToDefaultDisplay = context.getResources()
.getBoolean(R.bool
.config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay);
if (mDeviceStateManager != null) {
mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
}
}
void unregisterFromDeviceStateManager() {
if (mDeviceStateManager != null) {
mDeviceStateManager.unregisterCallback(this);
}
}
void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
mDeviceStateCallbacks.add(callback);
synchronized (this) {
mDeviceStateCallbacks.add(callback);
}
}
/**
@@ -95,6 +98,15 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState);
}
/**
* @return true if non-default built-in displays should match the default display's rotation.
*/
boolean shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay() {
// TODO(b/265991392): This should come from display_settings.xml once it's easier to
// extend with complex configurations.
return mMatchBuiltInDisplayOrientationToDefaultDisplay;
}
@Override
public void onStateChanged(int state) {
mCurrentState = state;
@@ -115,8 +127,10 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
mLastDeviceState = deviceState;
for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
callback.accept(mLastDeviceState);
synchronized (this) {
for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
callback.accept(mLastDeviceState);
}
}
}
}

View File

@@ -595,7 +595,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
private final DeviceStateController mDeviceStateController;
@VisibleForTesting
final DeviceStateController mDeviceStateController;
private final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
final RemoteDisplayChangeController mRemoteDisplayChangeController;
@@ -1091,7 +1092,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* @param display May not be null.
* @param root {@link RootWindowContainer}
*/
DisplayContent(Display display, RootWindowContainer root) {
DisplayContent(Display display, RootWindowContainer root,
@NonNull DeviceStateController deviceStateController) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
@@ -1151,11 +1153,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mAtmService.getRecentTasks().getInputListener());
}
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
mDeviceStateController = deviceStateController;
mDisplayPolicy = new DisplayPolicy(mWmService, this);
mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
mDeviceStateController);
mDeviceStateController, root.getDisplayRotationCoordinator());
final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
(@NonNull DeviceStateController.DeviceState newFoldState) -> {
@@ -3326,7 +3328,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
handleAnimatingStoppedAndTransition();
mWmService.stopFreezingDisplayLocked();
mDeviceStateController.unregisterFromDeviceStateManager();
mDisplayRotation.removeDefaultDisplayRotationChangedCallback();
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
mPointerEventDispatcher.dispose();

View File

@@ -58,6 +58,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.RotationUtils;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -120,6 +121,11 @@ public class DisplayRotation {
private FoldController mFoldController;
@NonNull
private final DeviceStateController mDeviceStateController;
@NonNull
private final DisplayRotationCoordinator mDisplayRotationCoordinator;
@NonNull
@VisibleForTesting
final Runnable mDefaultDisplayRotationChangedCallback;
@ScreenOrientation
private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -221,17 +227,19 @@ public class DisplayRotation {
private boolean mDemoRotationLock;
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController) {
DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController,
@NonNull DisplayRotationCoordinator displayRotationCoordinator) {
this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(),
deviceStateController);
deviceStateController, displayRotationCoordinator);
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
DisplayAddress displayAddress, DisplayPolicy displayPolicy,
DisplayWindowSettings displayWindowSettings, Context context, Object lock,
@NonNull DeviceStateController deviceStateController) {
@NonNull DeviceStateController deviceStateController,
@NonNull DisplayRotationCoordinator displayRotationCoordinator) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
@@ -252,6 +260,19 @@ public class DisplayRotation {
int defaultRotation = readDefaultDisplayRotation(displayAddress);
mRotation = defaultRotation;
mDisplayRotationCoordinator = displayRotationCoordinator;
if (isDefaultDisplay) {
mDisplayRotationCoordinator.setDefaultDisplayDefaultRotation(mRotation);
}
mDefaultDisplayRotationChangedCallback = this::updateRotationAndSendNewConfigIfChanged;
if (DisplayRotationCoordinator.isSecondaryInternalDisplay(displayContent)
&& mDeviceStateController
.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) {
mDisplayRotationCoordinator.setDefaultDisplayRotationChangedCallback(
mDefaultDisplayRotationChangedCallback);
}
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
mOrientationListener =
@@ -494,8 +515,11 @@ public class DisplayRotation {
return false;
}
@Surface.Rotation
final int oldRotation = mRotation;
@ScreenOrientation
final int lastOrientation = mLastOrientation;
@Surface.Rotation
int rotation = rotationForOrientation(lastOrientation, oldRotation);
// Use the saved rotation for tabletop mode, if set.
if (mFoldController != null && mFoldController.shouldRevertOverriddenRotation()) {
@@ -507,6 +531,14 @@ public class DisplayRotation {
Surface.rotationToString(oldRotation),
Surface.rotationToString(prevRotation));
}
if (DisplayRotationCoordinator.isSecondaryInternalDisplay(mDisplayContent)
&& mDeviceStateController
.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) {
rotation = RotationUtils.reverseRotationDirectionAroundZAxis(
mDisplayRotationCoordinator.getDefaultDisplayCurrentRotation());
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "
+ "oldRotation=%s (%d)",
@@ -525,6 +557,10 @@ public class DisplayRotation {
return false;
}
if (isDefaultDisplay) {
mDisplayRotationCoordinator.onDefaultDisplayRotationChanged(rotation);
}
// Preemptively cancel the running recents animation -- SysUI can't currently handle this
// case properly since the signals it receives all happen post-change. We do this earlier
// in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems
@@ -1142,17 +1178,12 @@ public class DisplayRotation {
return mUserRotation;
}
@Surface.Rotation
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
// Flipping 270 and 90 has the same effect as changing the direction which rotation is
// applied.
if (sensorRotation == Surface.ROTATION_90) {
sensorRotation = Surface.ROTATION_270;
} else if (sensorRotation == Surface.ROTATION_270) {
sensorRotation = Surface.ROTATION_90;
}
sensorRotation = RotationUtils.reverseRotationDirectionAroundZAxis(sensorRotation);
}
mLastSensorRotation = sensorRotation;
if (sensorRotation < 0) {
@@ -1167,6 +1198,7 @@ public class DisplayRotation {
final boolean deskDockEnablesAccelerometer =
mDisplayPolicy.isDeskDockEnablesAccelerometer();
@Surface.Rotation
final int preferredRotation;
if (!isDefaultDisplay) {
// For secondary displays we ignore things like displays sensors, docking mode and
@@ -1536,6 +1568,12 @@ public class DisplayRotation {
return shouldUpdateRotation;
}
void removeDefaultDisplayRotationChangedCallback() {
if (DisplayRotationCoordinator.isSecondaryInternalDisplay(mDisplayContent)) {
mDisplayRotationCoordinator.removeDefaultDisplayRotationChangedCallback();
}
}
void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "DisplayRotation");
pw.println(prefix + " mCurrentAppOrientation="

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.Display;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
/**
* Singleton for coordinating rotation across multiple displays. Used to notify non-default
* displays when the default display rotates.
*
* Note that this class does not need locking because it is always protected by WindowManagerService
* mGlobalLock.
*/
class DisplayRotationCoordinator {
private static final String TAG = "DisplayRotationCoordinator";
@Surface.Rotation
private int mDefaultDisplayDefaultRotation;
@Nullable
@VisibleForTesting
Runnable mDefaultDisplayRotationChangedCallback;
@Surface.Rotation
private int mDefaultDisplayCurrentRotation;
/**
* Notifies clients when the default display rotation changes.
*/
void onDefaultDisplayRotationChanged(@Surface.Rotation int rotation) {
mDefaultDisplayCurrentRotation = rotation;
if (mDefaultDisplayRotationChangedCallback != null) {
mDefaultDisplayRotationChangedCallback.run();
}
}
void setDefaultDisplayDefaultRotation(@Surface.Rotation int rotation) {
mDefaultDisplayDefaultRotation = rotation;
}
@Surface.Rotation
int getDefaultDisplayCurrentRotation() {
return mDefaultDisplayCurrentRotation;
}
/**
* Register a callback to be notified when the default display's rotation changes. Clients can
* query the default display's current rotation via {@link #getDefaultDisplayCurrentRotation()}.
*/
void setDefaultDisplayRotationChangedCallback(@NonNull Runnable callback) {
if (mDefaultDisplayRotationChangedCallback != null) {
throw new UnsupportedOperationException("Multiple clients unsupported");
}
mDefaultDisplayRotationChangedCallback = callback;
if (mDefaultDisplayCurrentRotation != mDefaultDisplayDefaultRotation) {
callback.run();
}
}
/**
* Removes the callback that was added via
* {@link #setDefaultDisplayRotationChangedCallback(Runnable)}.
*/
void removeDefaultDisplayRotationChangedCallback() {
mDefaultDisplayRotationChangedCallback = null;
}
static boolean isSecondaryInternalDisplay(@NonNull DisplayContent displayContent) {
if (displayContent.isDefaultDisplay) {
return false;
} else if (displayContent.mDisplay == null) {
return false;
}
return displayContent.mDisplay.getType() == Display.TYPE_INTERNAL;
}
}

View File

@@ -234,6 +234,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
private DisplayManagerInternal mDisplayManagerInternal;
@NonNull
private final DeviceStateController mDeviceStateController;
@NonNull
private final DisplayRotationCoordinator mDisplayRotationCoordinator;
/** Reference to default display so we can quickly look it up. */
private DisplayContent mDefaultDisplay;
@@ -440,6 +444,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
mDeviceStateController = new DeviceStateController(service.mContext, service.mH);
mDisplayRotationCoordinator = new DisplayRotationCoordinator();
}
/**
@@ -1279,7 +1285,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
final DisplayContent displayContent =
new DisplayContent(display, this, mDeviceStateController);
addChild(displayContent, POSITION_BOTTOM);
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
@@ -1297,6 +1304,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return mDefaultDisplay;
}
DisplayRotationCoordinator getDisplayRotationCoordinator() {
return mDisplayRotationCoordinator;
}
/**
* Get the default display area on the device dedicated to app windows. This one should be used
* only as a fallback location for activity launches when no target display area is specified,
@@ -1358,7 +1369,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return null;
}
// The display hasn't been added to ActivityManager yet, create a new record now.
displayContent = new DisplayContent(display, this);
displayContent = new DisplayContent(display, this, mDeviceStateController);
addChild(displayContent, POSITION_BOTTOM);
return displayContent;
}

View File

@@ -105,6 +105,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
@@ -123,6 +124,7 @@ import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -1830,6 +1832,111 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(recentsActivity.hasFixedRotationTransform());
}
@Test
public void testSecondaryInternalDisplayRotationFollowsDefaultDisplay() {
// Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb.
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
final DisplayRotationCoordinator coordinator =
mRootWindowContainer.getDisplayRotationCoordinator();
final DisplayContent defaultDisplayContent = mDisplayContent;
final DisplayRotation defaultDisplayRotation = defaultDisplayContent.getDisplayRotation();
coordinator.removeDefaultDisplayRotationChangedCallback();
DeviceStateController deviceStateController = mock(DeviceStateController.class);
when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay())
.thenReturn(true);
// Create secondary display
final DisplayContent secondaryDisplayContent =
createSecondaryDisplayContent(Display.TYPE_INTERNAL, deviceStateController);
final DisplayRotation secondaryDisplayRotation =
secondaryDisplayContent.getDisplayRotation();
try {
// TestDisplayContent bypasses this method but we need it for this test
doCallRealMethod().when(secondaryDisplayRotation).updateRotationUnchecked(anyBoolean());
// TestDisplayContent creates this as a mock. Lets set it up to test our use case.
when(secondaryDisplayContent.mDeviceStateController
.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()).thenReturn(
true);
// Check that secondary display registered callback
assertEquals(secondaryDisplayRotation.mDefaultDisplayRotationChangedCallback,
coordinator.mDefaultDisplayRotationChangedCallback);
// Set the default display to a known orientation. This may be a zero or non-zero
// rotation since mDisplayInfo.logicalWidth/Height depends on the DUT's default display
defaultDisplayRotation.updateOrientation(SCREEN_ORIENTATION_PORTRAIT, false);
assertEquals(defaultDisplayRotation.mPortraitRotation,
defaultDisplayRotation.getRotation());
assertEquals(defaultDisplayRotation.mPortraitRotation,
coordinator.getDefaultDisplayCurrentRotation());
// Check that in the initial state, the secondary display is in the right rotation
assertRotationsAreCorrectlyReversed(defaultDisplayRotation.getRotation(),
secondaryDisplayRotation.getRotation());
// Update primary display rotation, check display coordinator rotation is the default
// display's landscape rotation, and that the secondary display rotation is correct.
defaultDisplayRotation.updateOrientation(SCREEN_ORIENTATION_LANDSCAPE, false);
assertEquals(defaultDisplayRotation.mLandscapeRotation,
defaultDisplayRotation.getRotation());
assertEquals(defaultDisplayRotation.mLandscapeRotation,
coordinator.getDefaultDisplayCurrentRotation());
assertRotationsAreCorrectlyReversed(defaultDisplayRotation.getRotation(),
secondaryDisplayRotation.getRotation());
} finally {
secondaryDisplayRotation.removeDefaultDisplayRotationChangedCallback();
}
}
@Test
public void testSecondaryNonInternalDisplayDoesNotFollowDefaultDisplay() {
// Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb.
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
final DisplayRotationCoordinator coordinator =
mRootWindowContainer.getDisplayRotationCoordinator();
coordinator.removeDefaultDisplayRotationChangedCallback();
DeviceStateController deviceStateController = mock(DeviceStateController.class);
when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay())
.thenReturn(true);
// Create secondary non-internal displays
createSecondaryDisplayContent(Display.TYPE_EXTERNAL, deviceStateController);
assertNull(coordinator.mDefaultDisplayRotationChangedCallback);
createSecondaryDisplayContent(Display.TYPE_VIRTUAL, deviceStateController);
assertNull(coordinator.mDefaultDisplayRotationChangedCallback);
}
private DisplayContent createSecondaryDisplayContent(int displayType,
@NonNull DeviceStateController deviceStateController) {
final DisplayInfo secondaryDisplayInfo = new DisplayInfo();
secondaryDisplayInfo.copyFrom(mDisplayInfo);
secondaryDisplayInfo.type = displayType;
return new TestDisplayContent.Builder(mAtm, secondaryDisplayInfo)
.setDeviceStateController(deviceStateController)
.build();
}
private static void assertRotationsAreCorrectlyReversed(@Surface.Rotation int rotation1,
@Surface.Rotation int rotation2) {
if (rotation1 == ROTATION_0) {
assertEquals(rotation1, rotation2);
} else if (rotation1 == ROTATION_180) {
assertEquals(rotation1, rotation2);
} else if (rotation1 == ROTATION_90) {
assertEquals(ROTATION_270, rotation2);
} else if (rotation1 == ROTATION_270) {
assertEquals(ROTATION_90, rotation2);
} else {
throw new IllegalArgumentException("Unknown rotation: " + rotation1 + ", " + rotation2);
}
}
@Test
public void testRemoteRotation() {
final DisplayContent dc = mDisplayContent;

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import android.annotation.NonNull;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
* Test class for {@link DisplayRotationCoordinator}
*
* Build/Install/Run:
* atest DisplayRotationCoordinatorTests
*/
@SmallTest
@Presubmit
public class DisplayRotationCoordinatorTests {
@NonNull
private final DisplayRotationCoordinator mCoordinator = new DisplayRotationCoordinator();
@Test
public void testDefaultDisplayRotationChangedWhenNoCallbackRegistered() {
// Does not cause NPE
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
}
@Test (expected = UnsupportedOperationException.class)
public void testSecondRegistrationWithoutRemovingFirst() {
Runnable callback1 = mock(Runnable.class);
Runnable callback2 = mock(Runnable.class);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback1);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback2);
assertEquals(callback1, mCoordinator.mDefaultDisplayRotationChangedCallback);
}
@Test
public void testSecondRegistrationAfterRemovingFirst() {
Runnable callback1 = mock(Runnable.class);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback1);
mCoordinator.removeDefaultDisplayRotationChangedCallback();
Runnable callback2 = mock(Runnable.class);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback2);
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
verify(callback2).run();
verify(callback1, never()).run();
}
@Test
public void testRegisterThenDefaultDisplayRotationChanged() {
Runnable callback = mock(Runnable.class);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback);
assertEquals(Surface.ROTATION_0, mCoordinator.getDefaultDisplayCurrentRotation());
verify(callback, never()).run();
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
verify(callback).run();
assertEquals(Surface.ROTATION_90, mCoordinator.getDefaultDisplayCurrentRotation());
}
@Test
public void testDefaultDisplayRotationChangedThenRegister() {
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
Runnable callback = mock(Runnable.class);
mCoordinator.setDefaultDisplayRotationChangedCallback(callback);
verify(callback).run();
assertEquals(Surface.ROTATION_90, mCoordinator.getDefaultDisplayCurrentRotation());
}
}

View File

@@ -1135,7 +1135,7 @@ public class DisplayRotationTests {
mDeviceStateController = mock(DeviceStateController.class);
mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(),
mDeviceStateController) {
mDeviceStateController, mock(DisplayRotationCoordinator.class)) {
@Override
DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
WindowManagerService service, DisplayContent displayContent) {

View File

@@ -552,7 +552,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
/** Please use the {@link Builder} to create. */
DualDisplayContent(RootWindowContainer rootWindowContainer,
Display display) {
super(rootWindowContainer, display);
super(rootWindowContainer, display, mock(DeviceStateController.class));
mFirstRoot = getGroupRoot(FEATURE_FIRST_ROOT);
mSecondRoot = getGroupRoot(FEATURE_SECOND_ROOT);

View File

@@ -27,8 +27,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -51,8 +53,9 @@ class TestDisplayContent extends DisplayContent {
public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
/** Please use the {@link Builder} to create, visible for use in test builder overrides only. */
TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) {
super(display, rootWindowContainer);
TestDisplayContent(RootWindowContainer rootWindowContainer, Display display,
@NonNull DeviceStateController deviceStateController) {
super(display, rootWindowContainer, deviceStateController);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -97,6 +100,8 @@ class TestDisplayContent extends DisplayContent {
private int mStatusBarHeight = 0;
private SettingsEntry mOverrideSettings;
private DisplayMetrics mDisplayMetrics;
@NonNull
private DeviceStateController mDeviceStateController = mock(DeviceStateController.class);
@Mock
Context mMockContext;
@Mock
@@ -198,8 +203,13 @@ class TestDisplayContent extends DisplayContent {
com.android.internal.R.dimen.default_minimal_size_resizable_task);
return this;
}
Builder setDeviceStateController(@NonNull DeviceStateController deviceStateController) {
mDeviceStateController = deviceStateController;
return this;
}
TestDisplayContent createInternal(Display display) {
return new TestDisplayContent(mService.mRootWindowContainer, display);
return new TestDisplayContent(mService.mRootWindowContainer, display,
mDeviceStateController);
}
TestDisplayContent build() {
SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);