diff options
10 files changed, 183 insertions, 102 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 3076b873058d..e7f4422b5958 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -233,7 +233,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_F import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked; -import static com.android.server.wm.DesktopModeLaunchParamsModifier.canEnterDesktopMode; +import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java index 53729a2a971c..c8955fdd7911 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java @@ -47,7 +47,7 @@ class AppCompatCameraPolicy { final boolean needsDisplayRotationCompatPolicy = wmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime(); final boolean needsCameraCompatFreeformPolicy = Flags.cameraCompatForFreeform() - && DesktopModeLaunchParamsModifier.canEnterDesktopMode(wmService.mContext); + && DesktopModeHelper.canEnterDesktopMode(wmService.mContext); if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) { mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH); mActivityRefresher = new ActivityRefresher(wmService, wmService.mH); diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java new file mode 100644 index 000000000000..f9f5058415c6 --- /dev/null +++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 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.server.wm.LaunchParamsUtil.applyLayoutGravity; +import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; +import android.os.SystemProperties; +import android.util.Size; +import android.view.Gravity; + +import java.util.function.Consumer; + +/** + * Calculates the value of the {@link LaunchParamsController.LaunchParams} bounds for the + * {@link DesktopModeLaunchParamsModifier}. + */ +public final class DesktopModeBoundsCalculator { + + public static final float DESKTOP_MODE_INITIAL_BOUNDS_SCALE = SystemProperties + .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f; + + /** + * Updates launch bounds for an activity with respect to its activity options, window layout, + * android manifest and task configuration. + */ + static void updateInitialBounds(@NonNull Task task, @Nullable ActivityInfo.WindowLayout layout, + @Nullable ActivityRecord activity, @Nullable ActivityOptions options, + @NonNull Rect outBounds, @NonNull Consumer<String> logger) { + // Use stable frame instead of raw frame to avoid launching freeform windows on top of + // stable insets, which usually are system widgets such as sysbar & navbar. + final TaskDisplayArea displayArea = task.getDisplayArea(); + final Rect screenBounds = displayArea.getBounds(); + final Rect stableBounds = new Rect(); + displayArea.getStableRect(stableBounds); + final int desiredWidth = (int) (stableBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final int desiredHeight = (int) (stableBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + + if (options != null && options.getLaunchBounds() != null) { + outBounds.set(options.getLaunchBounds()); + logger.accept("inherit-from-options=" + outBounds); + } else if (layout != null) { + final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK; + final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + if (layout.hasSpecifiedSize()) { + calculateLayoutBounds(stableBounds, layout, outBounds, + new Size(desiredWidth, desiredHeight)); + applyLayoutGravity(verticalGravity, horizontalGravity, outBounds, + stableBounds); + logger.accept("layout specifies sizes, inheriting size and applying gravity"); + } else if (verticalGravity > 0 || horizontalGravity > 0) { + calculateAndCentreInitialBounds(outBounds, screenBounds); + applyLayoutGravity(verticalGravity, horizontalGravity, outBounds, + stableBounds); + logger.accept("layout specifies gravity, applying desired bounds and gravity"); + } + } else { + calculateAndCentreInitialBounds(outBounds, screenBounds); + logger.accept("layout not specified, applying desired bounds"); + } + } + + /** + * Calculates the initial height and width of a task in desktop mode and centers it within the + * window bounds. + */ + private static void calculateAndCentreInitialBounds(@NonNull Rect outBounds, + @NonNull Rect screenBounds) { + // TODO(b/319819547): Account for app constraints so apps do not become letterboxed + // The desired dimensions that a fully resizable window should take when initially entering + // desktop mode. Calculated as a percentage of the available display area as defined by the + // DESKTOP_MODE_INITIAL_BOUNDS_SCALE. + final int desiredWidth = (int) (screenBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final int desiredHeight = (int) (screenBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + outBounds.right = desiredWidth; + outBounds.bottom = desiredHeight; + outBounds.offset(screenBounds.centerX() - outBounds.centerX(), + screenBounds.centerY() - outBounds.centerY()); + } +} diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java new file mode 100644 index 000000000000..1f341147deb1 --- /dev/null +++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 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.content.Context; +import android.os.SystemProperties; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; + +/** + * Constants for desktop mode feature + */ +public final class DesktopModeHelper { + /** + * Flag to indicate whether to restrict desktop mode to supported devices. + */ + private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); + + /** Whether desktop mode is enabled. */ + static boolean isDesktopModeEnabled() { + return Flags.enableDesktopWindowingMode(); + } + + /** + * Return {@code true} if desktop mode should be restricted to supported devices. + */ + @VisibleForTesting + static boolean shouldEnforceDeviceRestrictions() { + return ENFORCE_DEVICE_RESTRICTIONS; + } + + /** + * Return {@code true} if the current device supports desktop mode. + */ + // TODO(b/337819319): use a companion object instead. + @VisibleForTesting + static boolean isDesktopModeSupported(@NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported); + } + + /** + * Return {@code true} if desktop mode can be entered on the current device. + */ + static boolean canEnterDesktopMode(@NonNull Context context) { + return isDesktopModeEnabled() + && (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context)); + } +} diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java index 66653caaa73d..aacd3c65154f 100644 --- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java @@ -18,28 +18,21 @@ package com.android.server.wm; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity; -import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds; +import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; import android.content.Context; import android.content.pm.ActivityInfo; -import android.graphics.Rect; import android.os.SystemProperties; -import android.util.Size; import android.util.Slog; -import android.view.Gravity; -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; -import com.android.window.flags.Flags; /** * The class that defines default launch params for tasks in desktop mode */ -public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { +class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { private static final String TAG = TAG_WITH_CLASS_NAME ? "DesktopModeLaunchParamsModifier" : TAG_ATM; @@ -67,8 +60,8 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout, @Nullable ActivityRecord activity, @Nullable ActivityRecord source, @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase, - LaunchParamsController.LaunchParams currentParams, - LaunchParamsController.LaunchParams outParams) { + @NonNull LaunchParamsController.LaunchParams currentParams, + @NonNull LaunchParamsController.LaunchParams outParams) { initLogBuilder(task, activity); int result = calculate(task, layout, activity, source, options, request, phase, @@ -80,15 +73,15 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout, @Nullable ActivityRecord activity, @Nullable ActivityRecord source, @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase, - LaunchParamsController.LaunchParams currentParams, - LaunchParamsController.LaunchParams outParams) { + @NonNull LaunchParamsController.LaunchParams currentParams, + @NonNull LaunchParamsController.LaunchParams outParams) { if (!canEnterDesktopMode(mContext)) { appendLog("desktop mode is not enabled, skipping"); return RESULT_SKIP; } - if (task == null) { + if (task == null || !task.isAttached()) { appendLog("task null, skipping"); return RESULT_SKIP; } @@ -123,59 +116,12 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { return RESULT_SKIP; } - // Use stable frame instead of raw frame to avoid launching freeform windows on top of - // stable insets, which usually are system widgets such as sysbar & navbar. - final Rect stableBounds = new Rect(); - task.getDisplayArea().getStableRect(stableBounds); - final int desiredWidth = (int) (stableBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - final int desiredHeight = (int) (stableBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - - if (options != null && options.getLaunchBounds() != null) { - outParams.mBounds.set(options.getLaunchBounds()); - appendLog("inherit-from-options=" + outParams.mBounds); - } else if (layout != null) { - final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK; - final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if (layout.hasSpecifiedSize()) { - calculateLayoutBounds(stableBounds, layout, outParams.mBounds, - new Size(desiredWidth, desiredHeight)); - applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds, - stableBounds); - appendLog("layout specifies sizes, inheriting size and applying gravity"); - } else if (verticalGravity > 0 || horizontalGravity > 0) { - calculateAndCentreInitialBounds(task, outParams); - applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds, - stableBounds); - appendLog("layout specifies gravity, applying desired bounds and gravity"); - } - } else { - calculateAndCentreInitialBounds(task, outParams); - appendLog("layout not specified, applying desired bounds"); - } - + DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options, + outParams.mBounds, this::appendLog); appendLog("final desktop mode task bounds set to %s", outParams.mBounds); return RESULT_CONTINUE; } - /** - * Calculates the initial height and width of a task in desktop mode and centers it within the - * window bounds. - */ - private void calculateAndCentreInitialBounds(Task task, - LaunchParamsController.LaunchParams outParams) { - // TODO(b/319819547): Account for app constraints so apps do not become letterboxed - final Rect screenBounds = task.getDisplayArea().getBounds(); - // The desired dimensions that a fully resizable window should take when initially entering - // desktop mode. Calculated as a percentage of the available display area as defined by the - // DESKTOP_MODE_INITIAL_BOUNDS_SCALE. - final int desiredWidth = (int) (screenBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - final int desiredHeight = (int) (screenBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - outParams.mBounds.right = desiredWidth; - outParams.mBounds.bottom = desiredHeight; - outParams.mBounds.offset(screenBounds.centerX() - outParams.mBounds.centerX(), - screenBounds.centerY() - outParams.mBounds.centerY()); - } - private void initLogBuilder(Task task, ActivityRecord activity) { if (DEBUG) { mLogBuilder = new StringBuilder( @@ -190,34 +136,4 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { private void outputLog() { if (DEBUG) Slog.d(TAG, mLogBuilder.toString()); } - - /** Whether desktop mode is enabled. */ - static boolean isDesktopModeEnabled() { - return Flags.enableDesktopWindowingMode(); - } - - /** - * Return {@code true} if desktop mode should be restricted to supported devices. - */ - @VisibleForTesting - static boolean enforceDeviceRestrictions() { - return ENFORCE_DEVICE_RESTRICTIONS; - } - - /** - * Return {@code true} if the current device supports desktop mode. - */ - // TODO(b/337819319): use a companion object instead. - @VisibleForTesting - static boolean isDesktopModeSupported(@NonNull Context context) { - return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported); - } - - /** - * Return {@code true} if desktop mode can be entered on the current device. - */ - static boolean canEnterDesktopMode(@NonNull Context context) { - return isDesktopModeEnabled() - && (!enforceDeviceRestrictions() || isDesktopModeSupported(context)); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java index 006b37002e23..974a8e8712d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java @@ -168,7 +168,7 @@ public class AppCompatCameraPolicyTest extends WindowTestsBase { // TODO(b/350460645): Create Desktop Windowing Robot to reuse common functionalities. void allowEnterDesktopMode(boolean isAllowed) { doReturn(isAllowed).when(() -> - DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); + DesktopModeHelper.canEnterDesktopMode(any())); } private AppCompatCameraPolicy getTopAppCompatCameraPolicy() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java index ad3fba3084fe..23a88a1774f4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -430,8 +430,8 @@ public class DesktopModeLaunchParamsModifierTests extends private void setupDesktopModeLaunchParamsModifier(boolean isDesktopModeSupported, boolean enforceDeviceRestrictions) { doReturn(isDesktopModeSupported) - .when(() -> DesktopModeLaunchParamsModifier.isDesktopModeSupported(any())); + .when(() -> DesktopModeHelper.isDesktopModeSupported(any())); doReturn(enforceDeviceRestrictions) - .when(DesktopModeLaunchParamsModifier::enforceDeviceRestrictions); + .when(DesktopModeHelper::shouldEnforceDeviceRestrictions); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 65aaf605bdfe..af0856f6109f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -2870,7 +2870,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull() { doReturn(true).when(() -> - DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); + DesktopModeHelper.canEnterDesktopMode(any())); assertTrue(createNewDisplay().mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy()); } @@ -2879,7 +2879,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull() { doReturn(true).when(() -> - DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); + DesktopModeHelper.canEnterDesktopMode(any())); assertFalse(createNewDisplay().mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index ae88b1baa5b8..afe360430f02 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1658,7 +1658,7 @@ public class SizeCompatTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void testCompatScaling_freeformUnresizeableApp_smallerThanParent_upScaled() { doReturn(true).when(() -> - DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); + DesktopModeHelper.canEnterDesktopMode(any())); final int dw = 600; final int dh = 800; final DisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh) diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 4220f319b8f2..a816aa9b7598 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -202,7 +202,7 @@ public class SystemServicesTestRule implements TestRule { .mockStatic(DisplayControl.class, mockStubOnly) .mockStatic(LockGuard.class, mockStubOnly) .mockStatic(Watchdog.class, mockStubOnly) - .spyStatic(DesktopModeLaunchParamsModifier.class) + .spyStatic(DesktopModeHelper.class) .strictness(Strictness.LENIENT) .startMocking(); |