From 39791594560b2326625b663ed6796882900c220f Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 26 Apr 2017 09:29:12 -0700 Subject: [PATCH] Prevent non-fullscreen activities from influencing orientation This changelist enforces that activities targeting O and beyond can only specify an orientation if they are fullscreen. The change ignores the orientation on the server side and throws an exception when the client has an orientation set in onCreate or invokes Activity#setRequestedOrientation. Fixes: 33483680 Test: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests#testNonFullscreenActivityProhibited Test: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests#testLegacyNonFullscreenActivityPermitted Change-Id: I4f7f79744918fad6465a537633dfadec8d05c6df --- core/java/android/app/Activity.java | 13 ++++++++ .../java/android/content/pm/ActivityInfo.java | 31 +++++++++++++++++-- .../com/android/server/am/ActivityRecord.java | 16 +++++----- .../com/android/server/wm/AppWindowToken.java | 9 +++++- .../server/wm/AppWindowTokenTests.java | 4 +-- 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 37c287eebf18e..1175267e37a77 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -131,6 +131,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import static android.os.Build.VERSION_CODES.O; import static java.lang.Character.MIN_VALUE; /** @@ -974,6 +975,18 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState); + + if (getApplicationInfo().targetSdkVersion >= O && mActivityInfo.isFixedOrientation()) { + final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window); + final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta); + ta.recycle(); + + if (isTranslucentOrFloating) { + throw new IllegalStateException( + "Only fullscreen opaque activities can request orientation"); + } + } + if (mLastNonConfigurationInstances != null) { mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 587ab3b3b7434..9a014766c2067 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Configuration.NativeConfig; +import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.Printer; @@ -440,7 +441,6 @@ public class ActivityInfo extends ComponentInfo * @hide */ public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x400000; - /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -978,11 +978,19 @@ public class ActivityInfo extends ComponentInfo * Returns true if the activity's orientation is fixed. * @hide */ - boolean isFixedOrientation() { + public boolean isFixedOrientation() { return isFixedOrientationLandscape() || isFixedOrientationPortrait() || screenOrientation == SCREEN_ORIENTATION_LOCKED; } + /** + * Returns true if the specified orientation is considered fixed. + * @hide + */ + static public boolean isFixedOrientation(int orientation) { + return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation); + } + /** * Returns true if the activity's orientation is fixed to landscape. * @hide @@ -1162,6 +1170,25 @@ public class ActivityInfo extends ComponentInfo dest.writeFloat(maxAspectRatio); } + /** + * Determines whether the {@link Activity} is considered translucent or floating. + * @hide + */ + public static boolean isTranslucentOrFloating(TypedArray attributes) { + final boolean isTranslucent = + attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, + false); + final boolean isSwipeToDismiss = !attributes.hasValue( + com.android.internal.R.styleable.Window_windowIsTranslucent) + && attributes.getBoolean( + com.android.internal.R.styleable.Window_windowSwipeToDismiss, false); + final boolean isFloating = + attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, + false); + + return isFloating || isTranslucent || isSwipeToDismiss; + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public ActivityInfo createFromParcel(Parcel source) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 01fbdd454b900..ed1687ab41a70 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -131,6 +131,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; @@ -892,15 +893,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, userId); - final boolean translucent = ent != null && (ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsTranslucent, false) - || (!ent.array.hasValue( - com.android.internal.R.styleable.Window_windowIsTranslucent) - && ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowSwipeToDismiss, - false))); - fullscreen = ent != null && !ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsFloating, false) && !translucent; + fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array); noDisplay = ent != null && ent.array.getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); @@ -2188,6 +2181,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void setRequestedOrientation(int requestedOrientation) { + if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen + && appInfo.targetSdkVersion >= O) { + throw new IllegalStateException("Only fullscreen activities can request orientation"); + } + final int displayId = getDisplayId(); final Configuration displayConfig = mStackSupervisor.getDisplayOverrideConfiguration(displayId); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 640bac28efdf2..982561c989fff 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -53,6 +53,7 @@ import android.app.Activity; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; +import android.os.Build; import android.os.Debug; import android.os.IBinder; import android.os.SystemClock; @@ -70,6 +71,8 @@ import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import static android.os.Build.VERSION_CODES.O; + class AppTokenList extends ArrayList { } @@ -1245,7 +1248,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree */ @Override int getOrientation(int candidate) { - if (!fillsParent()) { + // We do not allow non-fullscreen apps to influence orientation at and beyond O. While we do + // throw an exception in {@link Activity#onCreate} and + // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that + // other calculations aren't affected. + if (!fillsParent() && mTargetSdk >= O) { // Can't specify orientation if app doesn't fill parent. return SCREEN_ORIENTATION_UNSET; } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 876008b7412b3..46f10c1959ff6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -173,8 +173,8 @@ public class AppWindowTokenTests extends WindowTestsBase { token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); - // Can not specify orientation if app doesn't fill parent. - assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation()); + // Can specify orientation if app doesn't fill parent. Allowed for SDK <= 25. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation()); token.setFillsParent(true); token.hidden = true;