summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/proto/android/providers/settings/global.proto1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java135
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java40
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java15
15 files changed, 229 insertions, 166 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bdc78342d42a..49710e5575a1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8803,6 +8803,13 @@ public final class Settings {
public static final String DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS =
"force_desktop_mode_on_external_displays";
+ /**
+ * Whether to allow non-resizable apps to be freeform.
+ * @hide
+ */
+ public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
+ "enable_sizecompat_freeform";
+
/**
* Whether user has enabled development settings.
*/
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a568c13d7dde..8d1fbe5def2e 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -281,6 +281,7 @@ message GlobalSettingsProto {
optional SettingProto force_rtl = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto emulate_display_cutout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto force_desktop_mode_on_external_displays = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Development development = 39;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6bd26bf03a55..dbb25abd537d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -548,6 +548,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
GlobalSettingsProto.Development.FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
+ dumpSetting(s, p,
+ Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
+ GlobalSettingsProto.Development.ENABLE_SIZECOMPAT_FREEFORM);
p.end(developmentToken);
final long deviceToken = p.start(GlobalSettingsProto.DEVICE);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 62827bc3a98e..7ea555de0177 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -222,6 +222,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
+ Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3853841b63ff..273a28303bfd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -299,10 +299,10 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.R;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
@@ -6599,14 +6599,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The role of CompatDisplayInsets is like the override bounds.
final ActivityDisplay display = getDisplay();
- if (display != null && display.mDisplayContent != null) {
- mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent);
+ if (display != null) {
+ if (display.inFreeformWindowingMode()) {
+ // in freeform, apps in compat mode still launch in windows so don't want to
+ // use display bounds, but rather use TaskRecord bounds.
+ if (getTaskRecord() != null) {
+ mCompatDisplayInsets = new CompatDisplayInsets(getTaskRecord());
+ }
+ } else if (display.mDisplayContent != null) {
+ mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent);
+ }
}
} else {
// We must base this on the parent configuration, because we set our override
// configuration's appBounds based on the result of this method. If we used our own
// configuration, it would be influenced by past invocations.
- computeBounds(mTmpBounds, getParent().getWindowConfiguration().getAppBounds());
+
+ // inherits from parent by default
+ mTmpBounds.set(getRequestedOverrideBounds());
+ applyAspectRatio(mTmpBounds, getParent().getWindowConfiguration().getAppBounds());
if (mTmpBounds.equals(getRequestedOverrideBounds())) {
// The bounds is not changed or the activity is resizable (both the 2 bounds are
@@ -6673,12 +6684,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// fixed orientation.
orientation = parentOrientation;
} else {
- if (!resolvedBounds.isEmpty()
- // The decor insets may be different according to the rotation.
- && getWindowConfiguration().getRotation() == parentRotation) {
- // Keep the computed resolved override configuration.
- return;
- }
final int requestedOrientation = getRequestedConfigurationOrientation();
if (requestedOrientation != ORIENTATION_UNDEFINED) {
orientation = requestedOrientation;
@@ -6687,57 +6692,53 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
super.resolveOverrideConfiguration(newParentConfiguration);
- boolean useParentOverrideBounds = false;
- final Rect displayBounds = mTmpBounds;
- final Rect containingAppBounds = new Rect();
- if (task.handlesOrientationChangeFromDescendant()) {
- // Prefer to use the orientation which is determined by this activity to calculate
- // bounds because the parent will follow the requested orientation.
- mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, orientation);
- } else {
- // The parent hierarchy doesn't handle the orientation changes. This is usually because
- // the aspect ratio of display is close to square or the display rotation is fixed.
- // In this case, task will compute override bounds to fit the app with respect to the
- // requested orientation. So here we perform similar calculation to have consistent
- // bounds even the original parent hierarchies were changed.
- final int baseOrientation = task.getParent().getConfiguration().orientation;
- mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, baseOrientation);
- task.computeFullscreenBounds(containingAppBounds, this, displayBounds, baseOrientation);
- useParentOverrideBounds = !containingAppBounds.isEmpty();
- }
-
- // The offsets will be non-zero if the parent has override bounds.
- final int containingOffsetX = containingAppBounds.left;
- final int containingOffsetY = containingAppBounds.top;
- if (!useParentOverrideBounds) {
- containingAppBounds.set(displayBounds);
- }
+ Rect containingAppBounds = new Rect();
+ Rect parentBounds = new Rect(newParentConfiguration.windowConfiguration.getBounds());
+
+ // Use compat insets to lock width and height. We should not use the parent width and height
+ // because apps in compat mode should have a constant width and height. The compat insets
+ // are locked when the app is first launched and are never changed after that, so we can
+ // rely on them to contain the original and unchanging width and height of the app.
+ final Rect compatDisplayBounds = mTmpBounds;
+ mCompatDisplayInsets.getDisplayBoundsByOrientation(compatDisplayBounds, orientation);
+ containingAppBounds.set(0, 0, compatDisplayBounds.width(), compatDisplayBounds.height());
+
+ resolvedBounds.set(containingAppBounds);
+
if (parentRotation != ROTATION_UNDEFINED) {
- // Ensure the container bounds won't overlap with the decors.
- TaskRecord.intersectWithInsetsIfFits(containingAppBounds, displayBounds,
+ // Ensure the parent and container bounds won't overlap with insets.
+ TaskRecord.intersectWithInsetsIfFits(containingAppBounds, compatDisplayBounds,
+ mCompatDisplayInsets.mNonDecorInsets[parentRotation]);
+ TaskRecord.intersectWithInsetsIfFits(parentBounds, compatDisplayBounds,
mCompatDisplayInsets.mNonDecorInsets[parentRotation]);
}
- computeBounds(resolvedBounds, containingAppBounds);
- if (resolvedBounds.isEmpty()) {
- // Use the entire available bounds because there is no restriction.
- resolvedBounds.set(useParentOverrideBounds ? containingAppBounds : displayBounds);
- } else {
- // The offsets are included in width and height by {@link #computeBounds}, so we have to
- // restore it.
- resolvedBounds.left += containingOffsetX;
- resolvedBounds.top += containingOffsetY;
- }
+ applyAspectRatio(resolvedBounds, containingAppBounds);
+
+ // Center horizontally in parent and align to top of parent - this is a UX choice
+ final int left = parentBounds.left + parentBounds.width() / 2 - resolvedBounds.width() / 2;
+ resolvedBounds.set(left, parentBounds.top, left + resolvedBounds.width(),
+ parentBounds.top + resolvedBounds.height());
+
+ // We want to get as much of the app on the screen even if insets cover it. This is because
+ // insets change but an app's bounds are more permanent after launch. After computing insets
+ // and horizontally centering resolvedBounds, the resolvedBounds may end up outside parent
+ // bounds. This is okay only if the resolvedBounds exceed their parent on the bottom and
+ // right, because that is clipped when the final bounds are computed. To reach this state,
+ // we first try and push the app as much inside the parent towards the top and left (the
+ // min). The app may then end up outside the parent by going too far left and top, so we
+ // push it back into the parent by taking the max with parent left and top.
+ Rect fullParentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ resolvedBounds.offsetTo(Math.max(fullParentBounds.left,
+ Math.min(fullParentBounds.right - resolvedBounds.width(), resolvedBounds.left)),
+ Math.max(fullParentBounds.top,
+ Math.min(fullParentBounds.bottom - resolvedBounds.height(),
+ resolvedBounds.top)));
+
+ // Use resolvedBounds to compute other override configurations such as appBounds
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
mCompatDisplayInsets);
- // The horizontal inset included in width is not needed if the activity cannot fill the
- // parent, because the offset will be applied by {@link ActivityRecord#mSizeCompatBounds}.
- final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
- if (resolvedBounds.width() < parentAppBounds.width()) {
- resolvedBounds.right -= resolvedAppBounds.left;
- }
// Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
// the parent bounds appropriately.
if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
@@ -6918,11 +6919,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
+ * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
+ * made to outBounds.
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
- private void computeBounds(Rect outBounds, Rect containingAppBounds) {
- outBounds.setEmpty();
+ private void applyAspectRatio(Rect outBounds, Rect containingAppBounds) {
final float maxAspectRatio = info.maxAspectRatio;
final ActivityStack stack = getActivityStack();
final float minAspectRatio = info.minAspectRatio;
@@ -6991,12 +6992,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (containingAppWidth <= activityWidth && containingAppHeight <= activityHeight) {
// The display matches or is less than the activity aspect ratio, so nothing else to do.
- // Return the existing bounds. If this method is running for the first time,
- // {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
- // the method has run before, then effect of {@link #getRequestedOverrideBounds()} will
- // already have been applied to the value returned from {@link getConfiguration}. Refer
- // to {@link TaskRecord#computeConfigResourceOverrides()}.
- outBounds.set(getRequestedOverrideBounds());
return;
}
@@ -7776,6 +7771,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /**
+ * Sets bounds to {@link TaskRecord} bounds. For apps in freeform, the task bounds are the
+ * parent bounds from the app's perspective. No insets because within a window.
+ */
+ CompatDisplayInsets(TaskRecord taskRecord) {
+ Rect taskBounds = taskRecord.getConfiguration().windowConfiguration.getBounds();
+ mDisplayWidth = taskBounds.width();
+ mDisplayHeight = taskBounds.height();
+ for (int rotation = 0; rotation < 4; rotation++) {
+ mNonDecorInsets[rotation] = new Rect();
+ mStableInsets[rotation] = new Rect();
+ }
+ }
+
void getDisplayBoundsByRotation(Rect outBounds, int rotation) {
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? mDisplayHeight : mDisplayWidth;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 14df505e8986..014b682ff8d6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -54,6 +54,7 @@ import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
@@ -569,6 +570,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
+ boolean mSizeCompatFreeform;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -744,6 +746,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+ final boolean sizeCompatFreeform = Settings.Global.getInt(
+ resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
@@ -757,6 +761,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
mForceResizableActivities = forceResizable;
+ mSizeCompatFreeform = sizeCompatFreeform;
final boolean multiWindowFormEnabled = freeformWindowManagement
|| supportsSplitScreenMultiWindow
|| supportsPictureInPicture
@@ -3393,6 +3398,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (stack.inFreeformWindowingMode()) {
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (!mSizeCompatFreeform) {
+ throw new IllegalStateException("Size-compat windows are currently not"
+ + "freeform-enabled");
} else if (stack.getParent().inFreeformWindowingMode()) {
// If the window is on a freeform display, set it to undefined. It will be
// resolved to freeform and it can adjust windowing mode when the display mode
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 9712277495df..31145deb449e 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -43,10 +43,8 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Build;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
@@ -65,15 +63,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
private static final boolean DEBUG = false;
- // A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
- private static final int SUPPORTS_SCREEN_RESIZEABLE_MASK =
- ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
- | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
- | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
- | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
-
// Screen size of Nexus 5x
private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412;
private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732;
@@ -253,10 +242,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (display.inFreeformWindowingMode()) {
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
- } else if (isTaskForcedMaximized(root)) {
- // We're launching an activity that probably can't handle resizing nicely, so force
- // it to be maximized even someone suggests launching it in freeform using launch
- // options.
+ } else if (!mSupervisor.mService.mSizeCompatFreeform && !root.isResizeable()) {
+ // We're launching an activity in size-compat mode and they aren't allowed in
+ // freeform, so force it to be maximized.
launchMode = WINDOWING_MODE_FULLSCREEN;
outParams.mBounds.setEmpty();
if (DEBUG) appendLog("forced-maximize");
@@ -460,28 +448,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
/**
- * Returns if task is forced to maximize.
- *
- * There are several cases where we force a task to maximize:
- * 1) Root activity is targeting pre-Donut, which by default can't handle multiple screen
- * densities, so resizing will likely cause issues;
- * 2) Root activity doesn't declare any flag that it supports any screen density, so resizing
- * may also cause issues;
- * 3) Root activity is not resizeable, for which we shouldn't allow user resize it.
- *
- * @param root the root activity to check against.
- * @return {@code true} if it should be forced to maximize; {@code false} otherwise.
- */
- private boolean isTaskForcedMaximized(@NonNull ActivityRecord root) {
- if (root.info.applicationInfo.targetSdkVersion < Build.VERSION_CODES.DONUT
- || (root.info.applicationInfo.flags & SUPPORTS_SCREEN_RESIZEABLE_MASK) == 0) {
- return true;
- }
-
- return !root.isResizeable();
- }
-
- /**
* Resolves activity requested orientation to 4 categories:
* 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down
* orientation;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index ed07f30fc9dc..e1123fa05b7d 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -126,6 +126,11 @@ class TaskPositioningController {
synchronized (mService.mGlobalLock) {
final Task task = displayContent.findTaskForResizePoint(x, y);
if (task != null) {
+ if (!task.isResizeable()) {
+ // The task is not resizable, so don't do anything when the user drags the
+ // the resize handles.
+ return;
+ }
if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
task.preserveOrientationOnResize(), x, y)) {
return;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 299b32cce039..09c25f061c5e 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -519,18 +519,7 @@ class TaskRecord extends ConfigurationContainer {
mAtmService.deferWindowLayout();
try {
- if (!isResizeable()) {
- Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
- return true;
- }
-
- // If this is a forced resize, let it go through even if the bounds is not changing,
- // as we might need a relayout due to surface size change (to/from fullscreen).
final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
- if (equivalentRequestedOverrideBounds(bounds) && !forced) {
- // Nothing to do here...
- return true;
- }
if (mTask == null) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e90f3da96409..415606b23478 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,6 +39,7 @@ import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EX
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -717,6 +718,8 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
private final Uri mForceResizableUri = Settings.Global.getUriFor(
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
+ private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor(
+ DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
public SettingsObserver() {
super(new Handler());
@@ -737,6 +740,8 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(mSizeCompatFreeformUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -775,6 +780,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mSizeCompatFreeformUri.equals(uri)) {
+ updateSizeCompatFreeform();
+ return;
+ }
+
@UpdateAnimationScaleMode
final int mode;
if (mWindowAnimationScaleUri.equals(uri)) {
@@ -844,6 +854,14 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mForceResizableActivities = forceResizable;
}
+
+ void updateSizeCompatFreeform() {
+ ContentResolver resolver = mContext.getContentResolver();
+ final boolean sizeCompatFreeform = Settings.Global.getInt(resolver,
+ DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
+
+ mAtmService.mSizeCompatFreeform = sizeCompatFreeform;
+ }
}
PowerManager mPowerManager;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b9cf29ad7790..991241dd645f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2319,29 +2319,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final Region region = inputWindowHandle.touchableRegion;
setTouchableRegionCropIfNeeded(inputWindowHandle);
- final Rect appOverrideBounds = mActivityRecord != null
- ? mActivityRecord.getResolvedOverrideBounds() : null;
- if (appOverrideBounds != null && !appOverrideBounds.isEmpty()) {
- // There may have touchable letterboxes around the activity, so in order to let the
- // letterboxes are able to receive touch event and slip to activity, the activity with
- // compatibility bounds cannot occupy full screen touchable region.
- if (modal) {
- // A modal window uses the whole compatibility bounds.
- flags |= FLAG_NOT_TOUCH_MODAL;
- mTmpRect.set(0, 0, appOverrideBounds.width(), appOverrideBounds.height());
- } else {
- // Non-modal uses the application based frame.
- mTmpRect.set(mWindowFrames.mCompatFrame);
- }
- // The offset of compatibility bounds is applied to surface of {@link #ActivityRecord}
- // and frame, so it is unnecessary to translate twice in surface based coordinates.
- final int surfaceOffsetX = mActivityRecord.hasSizeCompatBounds()
- ? mActivityRecord.getBounds().left : 0;
- mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
- region.set(mTmpRect);
- return flags;
- }
-
if (modal && mActivityRecord != null) {
// Limit the outer touch to the activity stack region.
flags |= FLAG_NOT_TOUCH_MODAL;
@@ -2398,6 +2375,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Translate to surface based coordinates.
region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
+ // TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
+ // scaling but the existing logic doesn't expect that. The result is that the already-
+ // scaled region ends up getting sent to surfaceflinger which then applies the scale
+ // (again). Until this is resolved, apply an inverse-scale here.
+ if (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
+ && mGlobalScale != 1.f) {
+ region.scale(mInvGlobalScale);
+ }
+
return flags;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index bf1508a34872..e43a364b45d9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -40,7 +40,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
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.when;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
@@ -70,10 +69,12 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PauseActivityItem;
@@ -485,6 +486,42 @@ public class ActivityRecordTests extends ActivityTestsBase {
}
@Test
+ public void testSizeCompatMode_KeepBoundsWhenChangingFromFreeformToFullscreen() {
+ setupDisplayContentForCompatDisplayInsets();
+
+ // put display in freeform mode
+ ActivityDisplay display = mActivity.getDisplay();
+ final Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
+ c.windowConfiguration.setBounds(new Rect(0, 0, 2000, 1000));
+ c.densityDpi = 300;
+ c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ display.onRequestedOverrideConfigurationChanged(c);
+
+ // launch compat activity in freeform and store bounds
+ mActivity.mAppWindowToken.mOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.visible = true;
+ ensureActivityConfiguration();
+
+ final Rect bounds = new Rect(mActivity.getBounds());
+ final int density = mActivity.getConfiguration().densityDpi;
+ final int windowingMode = mActivity.getWindowingMode();
+
+ // change display configuration to fullscreen
+ c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ display.onRequestedOverrideConfigurationChanged(c);
+
+ // check if dimensions stay the same
+ assertTrue(mActivity.inSizeCompatMode());
+ assertEquals(bounds.width(), mActivity.getBounds().width());
+ assertEquals(bounds.height(), mActivity.getBounds().height());
+ assertEquals(density, mActivity.getConfiguration().densityDpi);
+ assertEquals(windowingMode, mActivity.getWindowingMode());
+ }
+
+ @Test
public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() {
setupDisplayContentForCompatDisplayInsets();
final int decorHeight = 200; // e.g. The device has cutout.
@@ -506,6 +543,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.when(mActivity).getRequestedOrientation();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
+ mActivity.visible = true;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
// activity shouldn't be in the size compatibility mode.
@@ -555,7 +593,10 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
- assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
+ assertEquals(originalBounds.width(),
+ mActivity.getWindowConfiguration().getBounds().width());
+ assertEquals(originalBounds.height(),
+ mActivity.getWindowConfiguration().getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
assertTrue(mActivity.inSizeCompatMode());
}
@@ -566,9 +607,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
when(mActivity.getRequestedOrientation()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
+ mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mActivity.mAppWindowToken.mOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.visible = true;
+
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -576,7 +620,10 @@ public class ActivityRecordTests extends ActivityTestsBase {
setupDisplayAndParentSize(1000, 2000);
ensureActivityConfiguration();
- assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
+ assertEquals(originalBounds.width(),
+ mActivity.getWindowConfiguration().getBounds().width());
+ assertEquals(originalBounds.height(),
+ mActivity.getWindowConfiguration().getBounds().height());
assertTrue(mActivity.inSizeCompatMode());
}
@@ -1242,6 +1289,8 @@ public class ActivityRecordTests extends ActivityTestsBase {
c.windowConfiguration.setBounds(new Rect(0, 0, width, height));
c.windowConfiguration.setAppBounds(0, 0, width, height);
c.windowConfiguration.setRotation(ROTATION_0);
+ c.orientation = width > height
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
return displayContent;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index dd85f69b7160..5a141ae983f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -410,7 +410,8 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
}
@Test
- public void testForceMaximizesPreDApp() {
+ public void testForceMaximizesUnresizeableApp() {
+ mService.mSizeCompatFreeform = false;
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
@@ -422,7 +423,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
- mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUPCAKE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -432,46 +433,29 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
}
@Test
- public void testForceMaximizesAppWithoutMultipleDensitySupport() {
+ public void testLaunchesAppInWindowOnFreeformDisplay() {
+ mService.mSizeCompatFreeform = true;
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
- options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
- mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(0, 0, 200, 100);
-
- mActivity.info.applicationInfo.flags = 0;
-
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
- mActivity, /* source */ null, options, mCurrent, mResult));
-
- assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
- WINDOWING_MODE_FREEFORM);
- }
-
- @Test
- public void testForceMaximizesUnresizeableApp() {
- final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
- WINDOWING_MODE_FREEFORM);
+ Rect expectedLaunchBounds = new Rect(0, 0, 200, 100);
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
- options.setLaunchBounds(new Rect(0, 0, 200, 100));
+ options.setLaunchBounds(expectedLaunchBounds);
mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(0, 0, 200, 100);
+ mCurrent.mBounds.set(expectedLaunchBounds);
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+ assertEquals(expectedLaunchBounds, mResult.mBounds);
+
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
WINDOWING_MODE_FREEFORM);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index dc89f5080c4b..f8d49ad18664 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -64,6 +66,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
any(InputChannel.class))).thenReturn(true);
mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ mWindow.getTask().setResizeable(RESIZE_MODE_RESIZEABLE);
mWindow.mInputChannel = new InputChannel();
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
@@ -129,4 +132,23 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
}
+
+ @Test
+ public void testHandleTapOutsideNonResizableTask() {
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+
+ final DisplayContent content = mock(DisplayContent.class);
+ doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
+ assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
+
+ mWindow.getTask().setResizeable(RESIZE_MODE_UNRESIZEABLE);
+
+ mTarget.handleTapOutsideTask(content, 0, 0);
+ // Wait until the looper processes finishTaskPositioning.
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+
+ assertFalse(mTarget.isPositioningLocked());
+ }
+
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 86f0f8bc1bb9..1c9eed2e5622 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
@@ -77,6 +78,20 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
}
}
+ @Test
+ public void testEnableSizeCompatFreeform() {
+ try (SettingsSession enableSizeCompatFreeformSession = new
+ SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
+ final boolean enableSizeCompatFreeform =
+ !enableSizeCompatFreeformSession.getSetting();
+ final Uri enableSizeCompatFreeformUri =
+ enableSizeCompatFreeformSession.setSetting(enableSizeCompatFreeform);
+ mWm.mSettingsObserver.onChange(false, enableSizeCompatFreeformUri);
+
+ assertEquals(mWm.mAtmService.mSizeCompatFreeform, enableSizeCompatFreeform);
+ }
+ }
+
private class SettingsSession implements AutoCloseable {
private static final int SETTING_VALUE_OFF = 0;