Added support for maxAspectRatio manifest attribute.
- Allows an app to specify the maximum aspect ratio it supports.
- Support for overriding configuration and bounds at the activity
record and app window token level.
Test: cts/.../run-test CtsAppTestCases android.app.cts.AspectRatioTests
Test: bit FrameworksServicesTests:com.android.server.wm.WindowContainerTests
Test: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
Bug: 36505427
Bug: 33205955
Bug: 35810513
Change-Id: Ib2d46ed0c546dd903d09d6bb7162a98bd390ba81
diff --git a/api/current.txt b/api/current.txt
index b400b34..1b345d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -879,6 +879,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
+ field public static final int maxAspectRatio = 16844131; // 0x1010563
field public static final int maxButtonHeight = 16844029; // 0x10104fd
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
diff --git a/api/system-current.txt b/api/system-current.txt
index 844b897..add55f5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -992,6 +992,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
+ field public static final int maxAspectRatio = 16844131; // 0x1010563
field public static final int maxButtonHeight = 16844029; // 0x10104fd
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
diff --git a/api/test-current.txt b/api/test-current.txt
index a73ac18..2e1fd3a0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -879,6 +879,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
+ field public static final int maxAspectRatio = 16844131; // 0x1010563
field public static final int maxButtonHeight = 16844029; // 0x10104fd
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 6dbe5fb..b01e6a1 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -223,6 +223,15 @@
public int resizeMode = RESIZE_MODE_RESIZEABLE;
/**
+ * Value indicating the maximum aspect ratio the activity supports.
+ * <p>
+ * 0 means unset.
+ * @See {@link android.R.attr#maxAspectRatio}.
+ * @hide
+ */
+ public float maxAspectRatio;
+
+ /**
* Name of the VrListenerService component to run for this activity.
* @see android.R.attr#enableVrMode
* @hide
@@ -922,6 +931,7 @@
requestedVrComponent = orig.requestedVrComponent;
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
+ maxAspectRatio = orig.maxAspectRatio;
}
/**
@@ -1064,6 +1074,9 @@
if (requestedVrComponent != null) {
pw.println(prefix + "requestedVrComponent=" + requestedVrComponent);
}
+ if (maxAspectRatio != 0) {
+ pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
+ }
super.dumpBack(pw, prefix, flags);
}
@@ -1110,6 +1123,7 @@
dest.writeString(requestedVrComponent);
dest.writeInt(rotationAnimation);
dest.writeInt(colorMode);
+ dest.writeFloat(maxAspectRatio);
}
public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -1146,6 +1160,7 @@
requestedVrComponent = source.readString();
rotationAnimation = source.readInt();
colorMode = source.readInt();
+ maxAspectRatio = source.readFloat();
}
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ffc7719..939e20f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -603,6 +603,15 @@
public int largestWidthLimitDp = 0;
/**
+ * Value indicating the maximum aspect ratio the application supports.
+ * <p>
+ * 0 means unset.
+ * @See {@link android.R.attr#maxAspectRatio}.
+ * @hide
+ */
+ public float maxAspectRatio;
+
+ /**
* UUID of the storage volume on which this application is being hosted. For
* apps hosted on the default internal storage at
* {@link Environment#getDataDirectory()}, the UUID value is {@code null}.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fb69986..1fafe65 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -37,6 +37,7 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
@@ -147,6 +148,8 @@
public static final int APK_SIGNING_V1 = 1;
public static final int APK_SIGNING_V2 = 2;
+ private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -3465,6 +3468,8 @@
ai.privateFlags |= PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_VIA_SDK_VERSION;
}
+ ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
+
ai.networkSecurityConfigRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig,
0);
@@ -4161,6 +4166,8 @@
a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
}
+ setActivityMaxAspectRatio(a.info, sa, owner);
+
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
@@ -4376,6 +4383,27 @@
}
}
+ private void setActivityMaxAspectRatio(ActivityInfo aInfo, TypedArray sa, Package owner) {
+ if (aInfo.resizeMode == RESIZE_MODE_RESIZEABLE
+ || aInfo.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+ // Resizeable activities can be put in any aspect ratio.
+ aInfo.maxAspectRatio = 0;
+ return;
+ }
+
+ // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+ // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+ float defaultMaxAspectRatio = owner.applicationInfo.targetSdkVersion < O
+ ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+ if (owner.applicationInfo.maxAspectRatio != 0 ) {
+ // Use the application max aspect ration as default if set.
+ defaultMaxAspectRatio = owner.applicationInfo.maxAspectRatio;
+ }
+
+ aInfo.maxAspectRatio = Math.max(1.0f, sa.getFloat(
+ R.styleable.AndroidManifestActivity_maxAspectRatio, defaultMaxAspectRatio));
+ }
+
/**
* @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
* @param restartOnConfigChanges The bit mask restartOnConfigChanges fetched from
@@ -4512,6 +4540,7 @@
info.maxRecents = target.info.maxRecents;
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
+ info.maxAspectRatio = target.info.maxAspectRatio;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
Activity a = new Activity(mParseActivityAliasArgs, info);
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b9409f2..ed5a42b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1191,6 +1191,20 @@
<p>The default value is <code>false</code>. -->
<attr name="supportsPictureInPicture" format="boolean" />
+ <!-- This value indicates the maximum aspect ratio the activity supports. If the app runs on a
+ device with a wider aspect ratio, the system automatically letterboxes the app, leaving
+ portions of the screen unused so the app can run at its specified maximum aspect ratio.
+ <p>
+ Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+ form. For example, if the maximum aspect ratio is 7:3, set value to 2.33.
+ <p>
+ Value needs to be greater or equal to 1.0, otherwise it is ignored.
+ <p>
+ NOTE: This attribute is ignored if the activity has
+ {@link android.R.attr#resizeableActivity} set to true, since that means your activity
+ supports any size. -->
+ <attr name="maxAspectRatio" format="float" />
+
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
While in lockTask mode the system will not launch non-permitted tasks until
lockTask mode is disabled.
@@ -1408,6 +1422,7 @@
<attr name="defaultToDeviceProtectedStorage" format="boolean" />
<attr name="directBootAware" />
<attr name="resizeableActivity" />
+ <attr name="maxAspectRatio" />
<attr name="networkSecurityConfig" />
<!-- Declare the category of this app. Categories are used to cluster multiple apps
together into meaningful groups, such as when summarizing battery, network, or
@@ -2066,6 +2081,7 @@
<attr name="resumeWhilePausing" />
<attr name="resizeableActivity" />
<attr name="supportsPictureInPicture" />
+ <attr name="maxAspectRatio" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
<attr name="directBootAware" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d6b5527..876d44d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2812,6 +2812,7 @@
<public name="fontProviderCerts" />
<public name="iconTint" />
<public name="iconTintMode" />
+ <public name="maxAspectRatio"/>
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6cea483..9a1cd8c 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -47,6 +47,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
@@ -83,6 +84,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
@@ -130,7 +132,7 @@
/**
* An entry in the history stack, representing an activity.
*/
-final class ActivityRecord implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -286,11 +288,19 @@
// on the window.
int mRotationAnimationHint = -1;
+ // The bounds of this activity. Mainly used for aspect-ratio compatibility.
+ // TODO(b/36505427): Every level on ConfigurationContainer now has bounds information, which
+ // directly affects the configuration. We should probably move this into that class and have it
+ // handle calculating override configuration from the bounds.
+ private final Rect mBounds = new Rect();
+
/**
* Temp configs used in {@link #ensureActivityConfigurationLocked(int, boolean)}
*/
private final Configuration mTmpConfig1 = new Configuration();
private final Configuration mTmpConfig2 = new Configuration();
+ private final Point mTmpPoint = new Point();
+ private final Rect mTmpBounds = new Rect();
private static String startingWindowStateToString(int state) {
switch (state) {
@@ -344,6 +354,13 @@
pw.println(mLastReportedConfiguration);
pw.print(prefix); pw.print("mLastReportedOverrideConfiguration=");
pw.println(mLastReportedOverrideConfiguration);
+ pw.print(prefix); pw.print("CurrentConfiguration="); pw.println(getConfiguration());
+ if (!getOverrideConfiguration().equals(EMPTY)) {
+ pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration());
+ }
+ if (!mBounds.isEmpty()) {
+ pw.println(prefix + "mBounds=" + mBounds);
+ }
if (resultTo != null || resultWho != null) {
pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
pw.print(" resultWho="); pw.print(resultWho);
@@ -461,10 +478,15 @@
}
if (info != null) {
pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
- pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
+ if (info.supportsPictureInPicture()) {
+ pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
+ pw.println(prefix + "supportsPictureInPictureWhilePausing: "
+ + supportsPictureInPictureWhilePausing);
+ }
+ if (info.maxAspectRatio != 0) {
+ pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
+ }
}
- pw.println(prefix + "supportsPictureInPictureWhilePausing: "
- + supportsPictureInPictureWhilePausing);
}
private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
@@ -579,6 +601,22 @@
return task != null && task.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
}
+ @Override
+ protected int getChildCount() {
+ // {@link ActivityRecord} is a leaf node and has no children.
+ return 0;
+ }
+
+ @Override
+ protected ConfigurationContainer getChildAt(int index) {
+ return null;
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return task;
+ }
+
static class Token extends IApplicationToken.Stub {
private final WeakReference<ActivityRecord> weakActivity;
@@ -764,15 +802,20 @@
inHistory = true;
- task.updateOverrideConfigurationFromLaunchBounds();
final TaskWindowContainerController taskController = task.getWindowContainerController();
+ // TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
+ task.updateOverrideConfigurationFromLaunchBounds();
+ // Make sure override configuration is up-to-date before using to create window controller.
+ updateOverrideConfiguration();
+
mWindowContainerController = new AppWindowContainerController(taskController, appToken,
this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen,
(info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
appInfo.targetSdkVersion, mRotationAnimationHint,
- ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+ ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L,
+ getOverrideConfiguration(), mBounds);
task.addActivityToTop(this);
@@ -1994,8 +2037,73 @@
}
/** Call when override config was sent to the Window Manager to update internal records. */
+ // TODO(b/36505427): Why do we set last reported based on sending the config to WM? Seems like
+ // we should only set this when we actually report to the activity which is what the method
+ // setLastReportedMergedOverrideConfiguration() does. Investigate if this is really needed.
void onOverrideConfigurationSent() {
- mLastReportedOverrideConfiguration.setTo(task.getMergedOverrideConfiguration());
+ mLastReportedOverrideConfiguration.setTo(getMergedOverrideConfiguration());
+ }
+
+ @Override
+ void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ super.onOverrideConfigurationChanged(overrideConfiguration);
+ if (mWindowContainerController != null) {
+ mWindowContainerController.onOverrideConfigurationChanged(
+ overrideConfiguration, mBounds);
+ // TODO(b/36505427): Can we consolidate the call points of onOverrideConfigurationSent()
+ // to just use this method instead?
+ onOverrideConfigurationSent();
+ }
+ }
+
+ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
+ private boolean updateOverrideConfiguration() {
+ computeBounds(mTmpBounds);
+ if (mTmpBounds.equals(mBounds)) {
+ return false;
+ }
+ mBounds.set(mTmpBounds);
+ // Bounds changed...update configuration to match.
+ mTmpConfig1.unset();
+ task.computeOverrideConfiguration(mTmpConfig1, mBounds, null /* insetBounds */,
+ false /* overrideWidth */, false /* overrideHeight */);
+ onOverrideConfigurationChanged(mTmpConfig1);
+ return true;
+ }
+
+ /** Computes the override configuration for this activity */
+ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
+ private void computeBounds(Rect outBounds) {
+ outBounds.setEmpty();
+ final float maxAspectRatio = info.maxAspectRatio;
+ final ActivityStack stack = getStack();
+ if ((task != null && !task.mFullscreen) || maxAspectRatio == 0 || stack == null) {
+ // We don't set override configuration if that activity task isn't fullscreen. I.e. the
+ // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
+ // the activity.
+ return;
+ }
+
+ stack.getDisplaySize(mTmpPoint);
+ int maxActivityWidth = mTmpPoint.x;
+ int maxActivityHeight = mTmpPoint.y;
+ if (mTmpPoint.x < mTmpPoint.y) {
+ // Width is the shorter side, so we use that to figure-out what the max. height should
+ // be given the aspect ratio.
+ maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
+ } else {
+ // Height is the shorter side, so we use that to figure-out what the max. width should
+ // be given the aspect ratio.
+ maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
+ }
+
+ if (mTmpPoint.x <= maxActivityWidth && mTmpPoint.y <= maxActivityHeight) {
+ // The display matches or is less than the activity aspect ratio, so nothing else to do.
+ return;
+ }
+
+ // Compute configuration based on max supported width and height.
+ outBounds.set(0, 0, maxActivityWidth, maxActivityHeight);
}
/**
@@ -2028,13 +2136,16 @@
if (displayChanged) {
mLastReportedDisplayId = newDisplayId;
}
+ // TODO(b/36505427): Is there a better place to do this?
+ updateOverrideConfiguration();
+
// Short circuit: if the two full configurations are equal (the common case), then there is
// nothing to do. We test the full configuration instead of the global and merged override
// configurations because there are cases (like moving a task to the pinned stack) where
// the combine configurations are equal, but would otherwise differ in the override config
mTmpConfig1.setTo(mLastReportedConfiguration);
mTmpConfig1.updateFrom(mLastReportedOverrideConfiguration);
- if (task.getConfiguration().equals(mTmpConfig1) && !forceNewConfig && !displayChanged) {
+ if (getConfiguration().equals(mTmpConfig1) && !forceNewConfig && !displayChanged) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration & display unchanged in " + this);
return true;
@@ -2045,15 +2156,15 @@
// Find changes between last reported merged configuration and the current one. This is used
// to decide whether to relaunch an activity or just report a configuration change.
- final int changes = getTaskConfigurationChanges(mTmpConfig1);
+ final int changes = getConfigurationChanges(mTmpConfig1);
// Update last reported values.
final Configuration newGlobalConfig = service.getGlobalConfiguration();
- final Configuration newTaskMergedOverrideConfig = task.getMergedOverrideConfiguration();
+ final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
mTmpConfig1.setTo(mLastReportedConfiguration);
mTmpConfig2.setTo(mLastReportedOverrideConfiguration);
mLastReportedConfiguration.setTo(newGlobalConfig);
- mLastReportedOverrideConfiguration.setTo(newTaskMergedOverrideConfig);
+ mLastReportedOverrideConfiguration.setTo(newMergedOverrideConfig);
if (changes == 0 && !forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -2061,9 +2172,9 @@
// There are no significant differences, so we won't relaunch but should still deliver
// the new configuration to the client process.
if (displayChanged) {
- scheduleActivityMovedToDisplay(newDisplayId, newTaskMergedOverrideConfig);
+ scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
- scheduleConfigurationChanged(newTaskMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig);
}
return true;
}
@@ -2088,9 +2199,9 @@
+ Integer.toHexString(changes) + ", handles=0x"
+ Integer.toHexString(info.getRealConfigChanged())
+ ", newGlobalConfig=" + newGlobalConfig
- + ", newTaskMergedOverrideConfig=" + newTaskMergedOverrideConfig);
+ + ", newMergedOverrideConfig=" + newMergedOverrideConfig);
- if (shouldRelaunchLocked(changes, newGlobalConfig, newTaskMergedOverrideConfig)
+ if (shouldRelaunchLocked(changes, newGlobalConfig, newMergedOverrideConfig)
|| forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
@@ -2133,13 +2244,13 @@
}
// Default case: the activity can handle this new configuration, so hand it over.
- // NOTE: We only forward the task override configuration as the system level configuration
+ // NOTE: We only forward the override configuration as the system level configuration
// changes is always sent to all processes when they happen so it can just use whatever
// system level configuration it last got.
if (displayChanged) {
- scheduleActivityMovedToDisplay(newDisplayId, newTaskMergedOverrideConfig);
+ scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
- scheduleConfigurationChanged(newTaskMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig);
}
stopFreezingScreenLocked(false);
@@ -2167,31 +2278,31 @@
return (changes&(~configChanged)) != 0;
}
- private int getTaskConfigurationChanges(Configuration lastReportedConfig) {
+ private int getConfigurationChanges(Configuration lastReportedConfig) {
// Determine what has changed. May be nothing, if this is a config that has come back from
// the app after going idle. In that case we just want to leave the official config object
// now in the activity and do nothing else.
- final Configuration currentConfig = task.getConfiguration();
- int taskChanges = lastReportedConfig.diff(currentConfig);
+ final Configuration currentConfig = getConfiguration();
+ int changes = lastReportedConfig.diff(currentConfig);
// We don't want to use size changes if they don't cross boundaries that are important to
// the app.
- if ((taskChanges & CONFIG_SCREEN_SIZE) != 0) {
+ if ((changes & CONFIG_SCREEN_SIZE) != 0) {
final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp,
currentConfig.screenWidthDp)
|| crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp,
currentConfig.screenHeightDp);
if (!crosses) {
- taskChanges &= ~CONFIG_SCREEN_SIZE;
+ changes &= ~CONFIG_SCREEN_SIZE;
}
}
- if ((taskChanges & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+ if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
final int oldSmallest = lastReportedConfig.smallestScreenWidthDp;
final int newSmallest = currentConfig.smallestScreenWidthDp;
if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
- taskChanges &= ~CONFIG_SMALLEST_SCREEN_SIZE;
+ changes &= ~CONFIG_SMALLEST_SCREEN_SIZE;
}
}
- return taskChanges;
+ return changes;
}
private static boolean isResizeOnlyChange(int change) {
@@ -2232,7 +2343,7 @@
app.thread.scheduleRelaunchActivity(appToken, pendingResults, pendingNewIntents,
configChangeFlags, !andResume,
new Configuration(service.getGlobalConfiguration()),
- new Configuration(task.getMergedOverrideConfiguration()), preserveWindow);
+ new Configuration(getMergedOverrideConfiguration()), preserveWindow);
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// pass in 'andResume' if this activity is currently resumed, which implies we aren't
// sleeping.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 97d0aa3..27b8e91 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1380,7 +1380,7 @@
new Configuration(mService.getGlobalConfiguration());
r.setLastReportedGlobalConfiguration(globalConfiguration);
final Configuration mergedOverrideConfiguration =
- new Configuration(task.getMergedOverrideConfiguration());
+ new Configuration(r.getMergedOverrideConfiguration());
r.setLastReportedMergedOverrideConfiguration(mergedOverrideConfiguration);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
diff --git a/services/core/java/com/android/server/am/ConfigurationContainer.java b/services/core/java/com/android/server/am/ConfigurationContainer.java
index a3e95b8..3d60681 100644
--- a/services/core/java/com/android/server/am/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/am/ConfigurationContainer.java
@@ -22,6 +22,8 @@
* Contains common logic for classes that have override configurations and are organized in a
* hierarchy.
*/
+// TODO(b/36505427): Move to wm package and have WindowContainer use this instead of having its own
+// implementation for merging configuration.
abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/** Contains override configuration settings applied to this configuration container. */
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a668fea..fd65c10 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -924,12 +924,12 @@
@Override
protected int getChildCount() {
- return 0;
+ return mActivities.size();
}
@Override
protected ConfigurationContainer getChildAt(int index) {
- return null;
+ return mActivities.get(index);
}
@Override
@@ -944,7 +944,7 @@
}
// Close up recents linked list.
- void closeRecentsChain() {
+ private void closeRecentsChain() {
if (mPrevAffiliate != null) {
mPrevAffiliate.setNextAffiliate(mNextAffiliate);
}
@@ -1188,7 +1188,10 @@
throw new IllegalArgumentException("Can not add r=" + " to task=" + this
+ " current parent=" + r.task);
}
+ // TODO(b/36505427): Maybe make task private to ActivityRecord so we can also do
+ // onParentChanged() within the setter?
r.task = this;
+ r.onParentChanged();
// Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
if (!mActivities.remove(r) && r.fullscreen) {
@@ -1995,7 +1998,7 @@
if (mStack == null || StackId.persistTaskBounds(mStack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
- calculateOverrideConfig(newConfig, mTmpRect, insetBounds,
+ computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
onOverrideConfigurationChanged(newConfig);
@@ -2008,7 +2011,10 @@
}
/** Clears passed config and fills it with new override values. */
- private void calculateOverrideConfig(Configuration config, Rect bounds, Rect insetBounds,
+ // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
+ // depend on task or stacks, but uses those object to get the display to base the calculation
+ // on. Probably best to centralize calculations like this in ConfigurationContainer.
+ void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
boolean overrideWidth, boolean overrideHeight) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
@@ -2027,7 +2033,7 @@
config.smallestScreenWidthDp =
mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
config.screenWidthDp = config.screenHeightDp = config.smallestScreenWidthDp;
- Slog.wtf(TAG, "Expected stack when caclulating override config");
+ Slog.wtf(TAG, "Expected stack when calculating override config");
}
config.orientation = (config.screenWidthDp <= config.screenHeightDp)
@@ -2048,23 +2054,6 @@
}
- /**
- * Using the existing configuration {@param config}, creates a new task override config such
- * that all the fields that are usually set in an override config are set to the ones in
- * {@param config}.
- */
- Configuration extractOverrideConfig(Configuration config) {
- final Configuration extracted = new Configuration();
- extracted.screenWidthDp = config.screenWidthDp;
- extracted.screenHeightDp = config.screenHeightDp;
- extracted.smallestScreenWidthDp = config.smallestScreenWidthDp;
- extracted.orientation = config.orientation;
- // We're only overriding LONG, SIZE and COMPAT parts of screenLayout.
- extracted.screenLayout = config.screenLayout & (Configuration.SCREENLAYOUT_LONG_MASK
- | Configuration.SCREENLAYOUT_SIZE_MASK | Configuration.SCREENLAYOUT_COMPAT_NEEDED);
- return extracted;
- }
-
Rect updateOverrideConfigurationFromLaunchBounds() {
final Rect bounds = validateBounds(getLaunchBounds());
updateOverrideConfiguration(bounds);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index ef3d87c..e60295d 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -32,9 +32,11 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Trace;
import android.util.Slog;
import android.view.IApplicationToken;
@@ -180,12 +182,13 @@
IApplicationToken token, AppWindowContainerListener listener, int index,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
- int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
+ int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
+ Configuration overrideConfig, Rect bounds) {
this(taskController, token, listener, index, requestedOrientation, fullscreen,
showForAllUsers,
configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
- WindowManagerService.getInstance());
+ WindowManagerService.getInstance(), overrideConfig, bounds);
}
public AppWindowContainerController(TaskWindowContainerController taskController,
@@ -193,7 +196,7 @@
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
- WindowManagerService service) {
+ WindowManagerService service, Configuration overrideConfig, Rect bounds) {
super(listener, service);
mHandler = new Handler(service.mH.getLooper());
mToken = token;
@@ -214,7 +217,7 @@
atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
- alwaysFocusable, this);
+ alwaysFocusable, this, overrideConfig, bounds);
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
+ " controller=" + taskController + " at " + index);
task.addChild(atoken, index);
@@ -226,11 +229,12 @@
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
- boolean alwaysFocusable, AppWindowContainerController controller) {
+ boolean alwaysFocusable, AppWindowContainerController controller,
+ Configuration overrideConfig, Rect bounds) {
return new AppWindowToken(service, token, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
+ controller, overrideConfig, bounds);
}
public void removeContainer(int displayId) {
@@ -297,6 +301,17 @@
}
}
+ // TODO(b/36505427): Maybe move to WindowContainerController so other sub-classes can use it as
+ // a generic way to set override config. Need to untangle current ways the override config is
+ // currently set for tasks and displays before we are doing that though.
+ public void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
+ synchronized(mWindowMap) {
+ if (mContainer != null) {
+ mContainer.onOverrideConfigurationChanged(overrideConfiguration, bounds);
+ }
+ }
+ }
+
public void setDisablePreviewScreenshots(boolean disable) {
synchronized (mWindowMap) {
if (mContainer == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d1f1305..72ae90d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -164,6 +164,11 @@
private boolean mLastContainsShowWhenLockedWindow;
private boolean mLastContainsDismissKeyguardWindow;
+ // The bounds of this activity. Mainly used for aspect-ratio compatibility.
+ // TODO(b/36505427): Every level on WindowContainer now has bounds information, which directly
+ // affects the configuration. We should probably move this into that class.
+ private final Rect mBounds = new Rect();
+
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
@@ -173,8 +178,8 @@
DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
int configChanges, boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
- this(service, token, voiceInteraction, dc, fullscreen);
+ AppWindowContainerController controller, Configuration overrideConfig, Rect bounds) {
+ this(service, token, voiceInteraction, dc, fullscreen, overrideConfig, bounds);
setController(controller);
mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
mShowForAllUsers = showForAllUsers;
@@ -191,7 +196,7 @@
}
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
- DisplayContent dc, boolean fillsParent) {
+ DisplayContent dc, boolean fillsParent, Configuration overrideConfig, Rect bounds) {
super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true, dc,
false /* ownerCanManageAppTokens */);
appToken = token;
@@ -199,6 +204,30 @@
mFillsParent = fillsParent;
mInputApplicationHandle = new InputApplicationHandle(this);
mAppAnimator = new AppWindowAnimator(this, service);
+ if (overrideConfig != null) {
+ onOverrideConfigurationChanged(overrideConfig);
+ }
+ if (bounds != null) {
+ mBounds.set(bounds);
+ }
+ }
+
+ void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
+ onOverrideConfigurationChanged(overrideConfiguration);
+ if (mBounds.equals(bounds)) {
+ return;
+ }
+ // TODO(b/36505427): If bounds is in WC, then we can automatically call onResize() when set.
+ mBounds.set(bounds);
+ onResize();
+ }
+
+ void getBounds(Rect outBounds) {
+ outBounds.set(mBounds);
+ }
+
+ boolean hasBounds() {
+ return !mBounds.isEmpty();
}
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e5b00f3..8f391a7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3044,6 +3044,12 @@
mTaskStackContainers.removeExistingAppTokensIfPossible();
}
+ @Override
+ void onDescendantOverrideConfigurationChanged() {
+ setLayoutNeeded();
+ mService.requestTraversal();
+ }
+
static final class TaskForResizePointSearchResult {
boolean searchDone;
Task taskForResize;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6973c3c..2a02359 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -30,6 +30,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.EMPTY;
/**
* Defines common functionality for classes that can hold windows directly or through their
@@ -315,9 +316,22 @@
void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
mOverrideConfiguration.setTo(overrideConfiguration);
// Update full configuration of this container and all its children.
- onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY);
+ onConfigurationChanged(mParent != null ? mParent.getConfiguration() : EMPTY);
// Update merged override config of this container and all its children.
onMergedOverrideConfigurationChanged();
+
+ if (mParent != null) {
+ mParent.onDescendantOverrideConfigurationChanged();
+ }
+ }
+
+ /**
+ * Notify that a descendant's overrideConfiguration has changed.
+ */
+ void onDescendantOverrideConfigurationChanged() {
+ if (mParent != null) {
+ mParent.onDescendantOverrideConfigurationChanged();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d4c8b1f..6fd95a4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -717,16 +717,16 @@
mHaveFrame = true;
final Task task = getTask();
- final boolean fullscreenTask = !isInMultiWindowMode();
+ final boolean inFullscreenContainer = inFullscreenContainer();
final boolean windowsAreFloating = task != null && task.isFloating();
final DisplayContent dc = getDisplayContent();
// If the task has temp inset bounds set, we have to make sure all its windows uses
// the temp inset frame. Otherwise different display frames get applied to the main
// window and the child window, making them misaligned.
- if (fullscreenTask) {
+ if (inFullscreenContainer) {
mInsetFrame.setEmpty();
- } else {
+ } else if (task != null && isInMultiWindowMode()) {
task.getTempInsetBounds(mInsetFrame);
}
@@ -740,7 +740,7 @@
// The offset from the layout containing frame to the actual containing frame.
final int layoutXDiff;
final int layoutYDiff;
- if (fullscreenTask || layoutInParentFrame()) {
+ if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
mContainingFrame.set(parentFrame);
mDisplayFrame.set(displayFrame);
@@ -749,7 +749,7 @@
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- task.getBounds(mContainingFrame);
+ getContainerBounds(mContainingFrame);
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
@@ -883,7 +883,7 @@
Math.min(mStableFrame.bottom, mFrame.bottom));
}
- if (fullscreenTask && !windowsAreFloating) {
+ if (inFullscreenContainer && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
@@ -908,10 +908,10 @@
getDisplayContent().getLogicalDisplayRect(mTmpRect);
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
- boolean overrideRightInset = !windowsAreFloating && !fullscreenTask &&
- mFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !windowsAreFloating && !fullscreenTask &&
- mFrame.bottom > mTmpRect.bottom;
+ boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.right > mTmpRect.right;
+ boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.bottom > mTmpRect.bottom;
mContentInsets.set(mContentFrame.left - mFrame.left,
mContentFrame.top - mFrame.top,
overrideRightInset ? mTmpRect.right - mContentFrame.right
@@ -3122,6 +3122,28 @@
return task != null && !task.isFullscreen();
}
+ /** Is this window in a container that takes up the entire screen space? */
+ private boolean inFullscreenContainer() {
+ if (mAppToken == null) {
+ return true;
+ }
+ if (mAppToken.hasBounds()) {
+ return false;
+ }
+ return !isInMultiWindowMode();
+ }
+
+ /** Returns the appropriate bounds to use for computing frames. */
+ private void getContainerBounds(Rect outBounds) {
+ if (isInMultiWindowMode()) {
+ getTask().getBounds(outBounds);
+ } else if (mAppToken != null){
+ mAppToken.getBounds(outBounds);
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
boolean isDragResizeChanged() {
return mDragResizing != computeDragResizing();
}
@@ -3137,7 +3159,7 @@
/**
* @return Whether we reported a drag resize change to the application or not already.
*/
- boolean isDragResizingChangeReported() {
+ private boolean isDragResizingChangeReported() {
return mDragResizingChangeReported;
}
@@ -3154,7 +3176,7 @@
* Set whether we got resized but drag resizing flag was false.
* @see #isResizedWhileNotDragResizing().
*/
- void setResizedWhileNotDragResizing(boolean resizedWhileNotDragResizing) {
+ private void setResizedWhileNotDragResizing(boolean resizedWhileNotDragResizing) {
mResizedWhileNotDragResizing = resizedWhileNotDragResizing;
mResizedWhileNotDragResizingReported = !resizedWhileNotDragResizing;
}
@@ -3459,17 +3481,17 @@
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
- final boolean nonFullscreenTask = isInMultiWindowMode();
+ final boolean inNonFullscreenContainer = !inFullscreenContainer();
final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
// We need to fit it to the display if either
- // a) The task is fullscreen, or we don't have a task (we assume fullscreen for the taskless
- // windows)
+ // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
+ // for the taskless windows)
// b) If it's a secondary app window, we also need to fit it to the display unless
- // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on screen,
- // but SurfaceViews want to be always at a specific location so we don't fit it to the
- // display.
- final boolean fitToDisplay = (task == null || !nonFullscreenTask)
+ // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
+ // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
+ // the display.
+ final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
|| ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
float x, y;
int w,h;
@@ -3514,7 +3536,7 @@
y = mAttrs.y;
}
- if (nonFullscreenTask && !layoutInParentFrame()) {
+ if (inNonFullscreenContainer && !layoutInParentFrame()) {
// Make sure window fits in containing frame since it is in a non-fullscreen task as
// required by {@link Gravity#apply} call.
w = Math.min(w, pw);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 74557e2..80b2e7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -361,6 +361,18 @@
}
@Test
+ public void testOverrideConfigurationAncestorNotification() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer grandparent = builder.setLayer(0).build();
+
+ final TestWindowContainer parent = grandparent.addChildWindow();
+ final TestWindowContainer child = parent.addChildWindow();
+ child.onOverrideConfigurationChanged(new Configuration());
+
+ assertTrue(grandparent.mOnDescendantOverrideCalled);
+ }
+
+ @Test
public void testRemoveChild() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
final TestWindowContainer root = builder.setLayer(0).build();
@@ -718,6 +730,7 @@
private Integer mOrientation;
private boolean mOnParentSetCalled;
+ private boolean mOnDescendantOverrideCalled;
/**
* Compares 2 window layers and returns -1 if the first is lesser than the second in terms
@@ -776,6 +789,12 @@
}
@Override
+ void onDescendantOverrideConfigurationChanged() {
+ mOnDescendantOverrideCalled = true;
+ super.onDescendantOverrideConfigurationChanged();
+ }
+
+ @Override
boolean isAnimating() {
return mIsAnimating || super.isAnimating();
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 28b6e45..1b1984d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -23,7 +23,6 @@
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
import android.graphics.Rect;
-import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -34,7 +33,6 @@
import android.view.WindowManager;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FILL_PARENT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -47,9 +45,8 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class WindowFrameTests {
+public class WindowFrameTests extends WindowTestsBase {
- private static WindowManagerService sWm = null;
private WindowToken mWindowToken;
private final IWindow mIWindow = new TestIWindow();
@@ -105,8 +102,7 @@
// Just any non zero value.
sWm.mSystemDecorLayer = 10000;
- mWindowToken = new WindowToken(sWm, new Binder(), 0, false,
- sWm.getDefaultDisplayContentLocked(), false /* ownerCanManageAppTokens */);
+ mWindowToken = new TestAppWindowToken(sWm.getDefaultDisplayContentLocked());
mStubStack = new TaskStack(sWm, 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 6f78245..48799d2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -303,17 +303,19 @@
static class TestAppWindowToken extends AppWindowToken {
TestAppWindowToken(DisplayContent dc) {
- super(sWm, null, false, dc, true /* fillsParent */);
+ super(sWm, null, false, dc, true /* fillsParent */, null /* overrideConfig */,
+ null /* bounds */);
}
TestAppWindowToken(WindowManagerService service, IApplicationToken token,
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
- boolean alwaysFocusable, AppWindowContainerController controller) {
+ boolean alwaysFocusable, AppWindowContainerController controller,
+ Configuration overrideConfig, Rect bounds) {
super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
- launchTaskBehind, alwaysFocusable, controller);
+ launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds);
}
int getWindowsCount() {
@@ -428,7 +430,8 @@
true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
false /* launchTaskBehind */, false /* alwaysFocusable */,
0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
- 0 /* inputDispatchingTimeoutNanos */, sWm);
+ 0 /* inputDispatchingTimeoutNanos */, sWm, null /* overrideConfig */,
+ null /* bounds */);
mToken = token;
}
@@ -437,12 +440,13 @@
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
- boolean alwaysFocusable, AppWindowContainerController controller) {
+ boolean alwaysFocusable, AppWindowContainerController controller,
+ Configuration overrideConfig, Rect bounds) {
return new TestAppWindowToken(service, token, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
orientation,
rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
+ controller, overrideConfig, bounds);
}
AppWindowToken getAppWindowToken() {