diff options
| author | 2020-05-12 09:05:17 +0000 | |
|---|---|---|
| committer | 2020-05-12 09:05:17 +0000 | |
| commit | 8e726a2ef72965430f514b0d8fa17a01798e51dc (patch) | |
| tree | 89e98a59b3571079dbe40836edc46d59f0eea904 | |
| parent | 80dcec801deb24186ce375da85c5b96bc2437038 (diff) | |
| parent | d490c57905bc47a4d583d69049864e6348171c61 (diff) | |
Merge changes from topic "b147213487" into rvc-dev
* changes:
Send fixed rotation adjustments to the associated client
Add support to override display adjustments by token
Add fixed rotation display adjustments
17 files changed, 598 insertions, 23 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b2976eca9590..af5fafbc93d4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7151,6 +7151,11 @@ public class Activity extends ContextThemeWrapper writer.println(mChangingConfigurations); writer.print(innerPrefix); writer.print("mCurrentConfig="); writer.println(mCurrentConfig); + if (getResources().hasOverrideDisplayAdjustments()) { + writer.print(innerPrefix); + writer.print("FixedRotationAdjustments="); + writer.println(getResources().getDisplayAdjustments().getFixedRotationAdjustments()); + } mFragments.dumpLoaders(innerPrefix, fd, writer, args); mFragments.getFragmentManager().dump(innerPrefix, fd, writer, args); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b45705924910..c75870e933f8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -156,6 +156,8 @@ import android.util.proto.ProtoOutputStream; import android.view.Choreographer; import android.view.ContextThemeWrapper; import android.view.Display; +import android.view.DisplayAdjustments; +import android.view.DisplayAdjustments.FixedRotationAdjustments; import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewDebug; @@ -215,6 +217,7 @@ import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; final class RemoteServiceException extends AndroidRuntimeException { public RemoteServiceException(String msg) { @@ -405,6 +408,9 @@ public final class ActivityThread extends ClientTransactionHandler { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final ResourcesManager mResourcesManager; + /** The active adjustments that override the {@link DisplayAdjustments} in resources. */ + private ArrayList<Pair<IBinder, Consumer<DisplayAdjustments>>> mActiveRotationAdjustments; + // Registry of remote cancellation transports pending a reply with reply handles. @GuardedBy("this") private @Nullable Map<SafeCancellationTransport, CancellationSignal> mRemoteCancellations; @@ -541,6 +547,12 @@ public final class ActivityThread extends ClientTransactionHandler { @UnsupportedAppUsage boolean mPreserveWindow; + /** + * If non-null, the activity is launching with a specified rotation, the adjustments should + * be consumed before activity creation. + */ + FixedRotationAdjustments mPendingFixedRotationAdjustments; + @LifecycleState private int mLifecycleState = PRE_ON_CREATE; @@ -557,7 +569,7 @@ public final class ActivityThread extends ClientTransactionHandler { PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, - IBinder assistToken) { + IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) { this.token = token; this.assistToken = assistToken; this.ident = ident; @@ -575,6 +587,7 @@ public final class ActivityThread extends ClientTransactionHandler { this.overrideConfig = overrideConfig; this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo, compatInfo); + mPendingFixedRotationAdjustments = fixedRotationAdjustments; init(); } @@ -3233,6 +3246,44 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.CLEAN_UP_CONTEXT, cci); } + @Override + public void handleFixedRotationAdjustments(@NonNull IBinder token, + @Nullable FixedRotationAdjustments fixedRotationAdjustments) { + final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null + ? displayAdjustments -> displayAdjustments.setFixedRotationAdjustments( + fixedRotationAdjustments) + : null; + if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) { + // No resources are associated with the token. + return; + } + if (mActivities.get(token) == null) { + // Only apply the override to application for activity token because the appearance of + // activity is usually more sensitive to the application resources. + return; + } + + // Apply the last override to application resources for compatibility. Because the Resources + // of Display can be from application, e.g. + // applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId) + // and the deprecated usage: + // applicationContext.getSystemService(WindowManager.class).getDefaultDisplay(); + final Consumer<DisplayAdjustments> appOverride; + if (mActiveRotationAdjustments == null) { + mActiveRotationAdjustments = new ArrayList<>(2); + } + if (override != null) { + mActiveRotationAdjustments.add(Pair.create(token, override)); + appOverride = override; + } else { + mActiveRotationAdjustments.removeIf(adjustmentsPair -> adjustmentsPair.first == token); + appOverride = mActiveRotationAdjustments.isEmpty() + ? null + : mActiveRotationAdjustments.get(mActiveRotationAdjustments.size() - 1).second; + } + mInitialApplication.getResources().overrideDisplayAdjustments(appOverride); + } + /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; @@ -3446,6 +3497,13 @@ public final class ActivityThread extends ClientTransactionHandler { ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); + // The rotation adjustments must be applied before creating the activity, so the activity + // can get the adjusted display info during creation. + if (r.mPendingFixedRotationAdjustments != null) { + handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments); + r.mPendingFixedRotationAdjustments = null; + } + final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); // For debugging purposes, if the activity's package name contains the value of // the "debug.use-second-display" system property as a substring, then show diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 83465b0f8d36..2df756e80fde 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -25,6 +25,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; import android.util.MergedConfiguration; +import android.view.DisplayAdjustments.FixedRotationAdjustments; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; @@ -167,6 +168,10 @@ public abstract class ClientTransactionHandler { /** Deliver app configuration change notification. */ public abstract void handleConfigurationChanged(Configuration config); + /** Apply addition adjustments to override display information. */ + public abstract void handleFixedRotationAdjustments(IBinder token, + FixedRotationAdjustments fixedRotationAdjustments); + /** * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the * provided token. diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 106f8ac92d05..1aae04db32d0 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -59,6 +59,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.WeakHashMap; +import java.util.function.Consumer; import java.util.function.Predicate; /** @hide */ @@ -1296,6 +1297,35 @@ public class ResourcesManager { } } + /** + * Overrides the display adjustments of all resources which are associated with the given token. + * + * @param token The token that owns the resources. + * @param override The operation to override the existing display adjustments. If it is null, + * the override adjustments will be cleared. + * @return {@code true} if the override takes effect. + */ + public boolean overrideTokenDisplayAdjustments(IBinder token, + @Nullable Consumer<DisplayAdjustments> override) { + boolean handled = false; + synchronized (this) { + final ActivityResources tokenResources = mActivityResourceReferences.get(token); + if (tokenResources == null) { + return false; + } + final ArrayList<WeakReference<Resources>> resourcesRefs = + tokenResources.activityResources; + for (int i = resourcesRefs.size() - 1; i >= 0; i--) { + final Resources res = resourcesRefs.get(i).get(); + if (res != null) { + res.overrideDisplayAdjustments(override); + handled = true; + } + } + } + return handled; + } + private class UpdateHandler implements Resources.UpdateCallbacks { /** diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java new file mode 100644 index 000000000000..6183d5f28fdb --- /dev/null +++ b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java @@ -0,0 +1,112 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.servertransaction; + +import android.app.ClientTransactionHandler; +import android.os.IBinder; +import android.os.Parcel; +import android.view.DisplayAdjustments.FixedRotationAdjustments; + +import java.util.Objects; + +/** + * The request to update display adjustments for a rotated activity or window token. + * @hide + */ +public class FixedRotationAdjustmentsItem extends ClientTransactionItem { + + /** The token who may have {@link android.content.res.Resources}. */ + private IBinder mToken; + + /** + * The adjustments for the display adjustments of resources. If it is null, the existing + * rotation adjustments will be dropped to restore natural state. + */ + private FixedRotationAdjustments mFixedRotationAdjustments; + + private FixedRotationAdjustmentsItem() {} + + /** Obtain an instance initialized with provided params. */ + public static FixedRotationAdjustmentsItem obtain(IBinder token, + FixedRotationAdjustments fixedRotationAdjustments) { + FixedRotationAdjustmentsItem instance = + ObjectPool.obtain(FixedRotationAdjustmentsItem.class); + if (instance == null) { + instance = new FixedRotationAdjustmentsItem(); + } + instance.mToken = token; + instance.mFixedRotationAdjustments = fixedRotationAdjustments; + + return instance; + } + + @Override + public void execute(ClientTransactionHandler client, IBinder token, + PendingTransactionActions pendingActions) { + client.handleFixedRotationAdjustments(mToken, mFixedRotationAdjustments); + } + + @Override + public void recycle() { + mToken = null; + mFixedRotationAdjustments = null; + ObjectPool.recycle(this); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeTypedObject(mFixedRotationAdjustments, flags); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final FixedRotationAdjustmentsItem other = (FixedRotationAdjustmentsItem) o; + return Objects.equals(mToken, other.mToken) + && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + Objects.hashCode(mToken); + result = 31 * result + Objects.hashCode(mFixedRotationAdjustments); + return result; + } + + private FixedRotationAdjustmentsItem(Parcel in) { + mToken = in.readStrongBinder(); + mFixedRotationAdjustments = in.readTypedObject(FixedRotationAdjustments.CREATOR); + } + + public static final Creator<FixedRotationAdjustmentsItem> CREATOR = + new Creator<FixedRotationAdjustmentsItem>() { + public FixedRotationAdjustmentsItem createFromParcel(Parcel in) { + return new FixedRotationAdjustmentsItem(in); + } + + public FixedRotationAdjustmentsItem[] newArray(int size) { + return new FixedRotationAdjustmentsItem[size]; + } + }; +} diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 9ab6e7fc9f58..2e7b6262c785 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -33,6 +33,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.PersistableBundle; import android.os.Trace; +import android.view.DisplayAdjustments.FixedRotationAdjustments; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -64,6 +65,7 @@ public class LaunchActivityItem extends ClientTransactionItem { private boolean mIsForward; private ProfilerInfo mProfilerInfo; private IBinder mAssistToken; + private FixedRotationAdjustments mFixedRotationAdjustments; @Override public void preExecute(ClientTransactionHandler client, IBinder token) { @@ -79,7 +81,7 @@ public class LaunchActivityItem extends ClientTransactionItem { ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mIsForward, - mProfilerInfo, client, mAssistToken); + mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -101,14 +103,14 @@ public class LaunchActivityItem extends ClientTransactionItem { String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo, - IBinder assistToken) { + IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); } setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer, voiceInteractor, procState, state, persistentState, pendingResults, - pendingNewIntents, isForward, profilerInfo, assistToken); + pendingNewIntents, isForward, profilerInfo, assistToken, fixedRotationAdjustments); return instance; } @@ -116,7 +118,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void recycle() { setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, - false, null, null); + false, null, null, null); ObjectPool.recycle(this); } @@ -142,6 +144,7 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeBoolean(mIsForward); dest.writeTypedObject(mProfilerInfo, flags); dest.writeStrongBinder(mAssistToken); + dest.writeTypedObject(mFixedRotationAdjustments, flags); } /** Read from Parcel. */ @@ -156,7 +159,8 @@ public class LaunchActivityItem extends ClientTransactionItem { in.createTypedArrayList(ResultInfo.CREATOR), in.createTypedArrayList(ReferrerIntent.CREATOR), in.readBoolean(), in.readTypedObject(ProfilerInfo.CREATOR), - in.readStrongBinder()); + in.readStrongBinder(), + in.readTypedObject(FixedRotationAdjustments.CREATOR)); } public static final @android.annotation.NonNull Creator<LaunchActivityItem> CREATOR = @@ -192,7 +196,8 @@ public class LaunchActivityItem extends ClientTransactionItem { && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) && mIsForward == other.mIsForward && Objects.equals(mProfilerInfo, other.mProfilerInfo) - && Objects.equals(mAssistToken, other.mAssistToken); + && Objects.equals(mAssistToken, other.mAssistToken) + && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments); } @Override @@ -212,6 +217,7 @@ public class LaunchActivityItem extends ClientTransactionItem { result = 31 * result + (mIsForward ? 1 : 0); result = 31 * result + Objects.hashCode(mProfilerInfo); result = 31 * result + Objects.hashCode(mAssistToken); + result = 31 * result + Objects.hashCode(mFixedRotationAdjustments); return result; } @@ -247,7 +253,7 @@ public class LaunchActivityItem extends ClientTransactionItem { + ",referrer=" + mReferrer + ",procState=" + mProcState + ",state=" + mState + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo - + " assistToken=" + mAssistToken + + ",assistToken=" + mAssistToken + ",rotationAdj=" + mFixedRotationAdjustments + "}"; } @@ -257,7 +263,8 @@ public class LaunchActivityItem extends ClientTransactionItem { CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken) { + boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, + FixedRotationAdjustments fixedRotationAdjustments) { instance.mIntent = intent; instance.mIdent = ident; instance.mInfo = info; @@ -274,5 +281,6 @@ public class LaunchActivityItem extends ClientTransactionItem { instance.mIsForward = isForward; instance.mProfilerInfo = profilerInfo; instance.mAssistToken = assistToken; + instance.mFixedRotationAdjustments = fixedRotationAdjustments; } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c399bc72e438..0f1c876a1133 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -81,6 +81,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; /** * Class for accessing an application's resources. This sits on top of the @@ -140,6 +141,9 @@ public class Resources { @UnsupportedAppUsage private DrawableInflater mDrawableInflater; + /** Used to override the returned adjustments of {@link #getDisplayAdjustments}. */ + private DisplayAdjustments mOverrideDisplayAdjustments; + /** Lock object used to protect access to {@link #mTmpValue}. */ private final Object mTmpValueLock = new Object(); @@ -2055,10 +2059,41 @@ public class Resources { /** @hide */ @UnsupportedAppUsage public DisplayAdjustments getDisplayAdjustments() { + final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments; + if (overrideDisplayAdjustments != null) { + return overrideDisplayAdjustments; + } return mResourcesImpl.getDisplayAdjustments(); } /** + * Customize the display adjustments based on the current one in {@link #mResourcesImpl}, in + * order to isolate the effect with other instances of {@link Resource} that may share the same + * instance of {@link ResourcesImpl}. + * + * @param override The operation to override the existing display adjustments. If it is null, + * the override adjustments will be cleared. + * @hide + */ + public void overrideDisplayAdjustments(@Nullable Consumer<DisplayAdjustments> override) { + if (override != null) { + mOverrideDisplayAdjustments = new DisplayAdjustments( + mResourcesImpl.getDisplayAdjustments()); + override.accept(mOverrideDisplayAdjustments); + } else { + mOverrideDisplayAdjustments = null; + } + } + + /** + * Return {@code true} if the override display adjustments have been set. + * @hide + */ + public boolean hasOverrideDisplayAdjustments() { + return mOverrideDisplayAdjustments != null; + } + + /** * Return the current configuration that is in effect for this resource * object. The returned object should be treated as read-only. * diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 4469fdbb12ec..8db1703a627f 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -104,6 +104,14 @@ public final class Display { private int mCachedAppHeightCompat; /** + * Indicates that the application is started in a different rotation than the real display, so + * the display information may be adjusted. That ensures the methods {@link #getRotation}, + * {@link #getRealSize}, {@link #getRealMetrics}, and {@link #getCutout} are consistent with how + * the application window is laid out. + */ + private boolean mMayAdjustByFixedRotation; + + /** * The default Display id, which is the id of the primary display assuming there is one. */ public static final int DEFAULT_DISPLAY = 0; @@ -804,7 +812,9 @@ public final class Display { public int getRotation() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.rotation; + return mMayAdjustByFixedRotation + ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation) + : mDisplayInfo.rotation; } } @@ -828,7 +838,9 @@ public final class Display { public DisplayCutout getCutout() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.displayCutout; + return mMayAdjustByFixedRotation + ? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout) + : mDisplayInfo.displayCutout; } } @@ -1140,6 +1152,9 @@ public final class Display { updateDisplayInfoLocked(); outSize.x = mDisplayInfo.logicalWidth; outSize.y = mDisplayInfo.logicalHeight; + if (mMayAdjustByFixedRotation) { + getDisplayAdjustments().adjustSize(outSize, mDisplayInfo.rotation); + } } } @@ -1159,6 +1174,9 @@ public final class Display { updateDisplayInfoLocked(); mDisplayInfo.getLogicalMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + if (mMayAdjustByFixedRotation) { + getDisplayAdjustments().adjustMetrics(outMetrics, mDisplayInfo.rotation); + } } } @@ -1225,6 +1243,9 @@ public final class Display { } } } + + mMayAdjustByFixedRotation = mResources != null + && mResources.hasOverrideDisplayAdjustments(); } private void updateCachedAppSizeIfNeededLocked() { @@ -1243,9 +1264,12 @@ public final class Display { public String toString() { synchronized (this) { updateDisplayInfoLocked(); - mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments()); + final DisplayAdjustments adjustments = getDisplayAdjustments(); + mDisplayInfo.getAppMetrics(mTempMetrics, adjustments); return "Display id " + mDisplayId + ": " + mDisplayInfo - + ", " + mTempMetrics + ", isValid=" + mIsValid; + + (mMayAdjustByFixedRotation + ? (", " + adjustments.getFixedRotationAdjustments() + ", ") : ", ") + + mTempMetrics + ", isValid=" + mIsValid; } } diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java index 27c2d5c5cdc3..c726bee9f402 100644 --- a/core/java/android/view/DisplayAdjustments.java +++ b/core/java/android/view/DisplayAdjustments.java @@ -21,6 +21,10 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DisplayMetrics; import java.util.Objects; @@ -30,6 +34,7 @@ public class DisplayAdjustments { private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; private final Configuration mConfiguration = new Configuration(Configuration.EMPTY); + private FixedRotationAdjustments mFixedRotationAdjustments; @UnsupportedAppUsage public DisplayAdjustments() { @@ -44,6 +49,7 @@ public class DisplayAdjustments { public DisplayAdjustments(@NonNull DisplayAdjustments daj) { setCompatibilityInfo(daj.mCompatInfo); mConfiguration.setTo(daj.getConfiguration()); + mFixedRotationAdjustments = daj.mFixedRotationAdjustments; } @UnsupportedAppUsage @@ -84,11 +90,78 @@ public class DisplayAdjustments { return mConfiguration; } + public void setFixedRotationAdjustments(FixedRotationAdjustments fixedRotationAdjustments) { + mFixedRotationAdjustments = fixedRotationAdjustments; + } + + public FixedRotationAdjustments getFixedRotationAdjustments() { + return mFixedRotationAdjustments; + } + + /** Returns {@code false} if the width and height of display should swap. */ + private boolean noFlip(@Surface.Rotation int realRotation) { + final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments; + if (rotationAdjustments == null) { + return true; + } + // Check if the delta is rotated by 90 degrees. + return (realRotation - rotationAdjustments.mRotation + 4) % 2 == 0; + } + + /** Adjusts the given size if possible. */ + public void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation) { + if (noFlip(realRotation)) { + return; + } + final int w = size.x; + size.x = size.y; + size.y = w; + } + + /** Adjusts the given metrics if possible. */ + public void adjustMetrics(@NonNull DisplayMetrics metrics, @Surface.Rotation int realRotation) { + if (noFlip(realRotation)) { + return; + } + int w = metrics.widthPixels; + metrics.widthPixels = metrics.heightPixels; + metrics.heightPixels = w; + + w = metrics.noncompatWidthPixels; + metrics.noncompatWidthPixels = metrics.noncompatHeightPixels; + metrics.noncompatHeightPixels = w; + + float x = metrics.xdpi; + metrics.xdpi = metrics.ydpi; + metrics.ydpi = x; + + x = metrics.noncompatXdpi; + metrics.noncompatXdpi = metrics.noncompatYdpi; + metrics.noncompatYdpi = x; + } + + /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */ + @Nullable + public DisplayCutout getDisplayCutout(@Nullable DisplayCutout realCutout) { + final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments; + return rotationAdjustments != null && rotationAdjustments.mRotatedDisplayCutout != null + ? rotationAdjustments.mRotatedDisplayCutout + : realCutout; + } + + /** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */ + @Surface.Rotation + public int getRotation(@Surface.Rotation int realRotation) { + final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments; + return rotationAdjustments != null ? rotationAdjustments.mRotation : realRotation; + } + @Override public int hashCode() { int hash = 17; hash = hash * 31 + Objects.hashCode(mCompatInfo); hash = hash * 31 + Objects.hashCode(mConfiguration); + hash = hash * 31 + Objects.hashCode(mFixedRotationAdjustments); return hash; } @@ -98,7 +171,82 @@ public class DisplayAdjustments { return false; } DisplayAdjustments daj = (DisplayAdjustments)o; - return Objects.equals(daj.mCompatInfo, mCompatInfo) && - Objects.equals(daj.mConfiguration, mConfiguration); + return Objects.equals(daj.mCompatInfo, mCompatInfo) + && Objects.equals(daj.mConfiguration, mConfiguration) + && Objects.equals(daj.mFixedRotationAdjustments, mFixedRotationAdjustments); + } + + /** + * An application can be launched in different rotation than the real display. This class + * provides the information to adjust the values returned by {@link #Display}. + * @hide + */ + public static class FixedRotationAdjustments implements Parcelable { + /** The application-based rotation. */ + @Surface.Rotation + final int mRotation; + + /** Non-null if the device has cutout. */ + @Nullable + final DisplayCutout mRotatedDisplayCutout; + + public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) { + mRotation = rotation; + mRotatedDisplayCutout = cutout; + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + mRotation; + hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout); + return hash; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof FixedRotationAdjustments)) { + return false; + } + final FixedRotationAdjustments other = (FixedRotationAdjustments) o; + return mRotation == other.mRotation + && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout); + } + + @Override + public String toString() { + return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation) + + " cutout=" + mRotatedDisplayCutout + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRotation); + dest.writeTypedObject( + new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags); + } + + private FixedRotationAdjustments(Parcel in) { + mRotation = in.readInt(); + final DisplayCutout.ParcelableWrapper cutoutWrapper = + in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR); + mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null; + } + + public static final Creator<FixedRotationAdjustments> CREATOR = + new Creator<FixedRotationAdjustments>() { + public FixedRotationAdjustments createFromParcel(Parcel in) { + return new FixedRotationAdjustments(in); + } + + public FixedRotationAdjustments[] newArray(int size) { + return new FixedRotationAdjustments[size]; + } + }; } } diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 107fe3f3ced5..6c23125aaf13 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -144,11 +144,12 @@ public class ObjectPoolTests { IBinder assistToken = new Binder(); LaunchActivityItem emptyItem = LaunchActivityItem.obtain(null, 0, null, null, null, null, - null, null, 0, null, null, null, null, false, null, null); + null, null, 0, null, null, null, null, false, null, null, null); LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), - true /* isForward */, null /* profilerInfo */, assistToken); + true /* isForward */, null /* profilerInfo */, assistToken, + null /* fixedRotationAdjustments */); assertNotSame(item, emptyItem); assertFalse(item.equals(emptyItem)); @@ -158,7 +159,8 @@ public class ObjectPoolTests { LaunchActivityItem item2 = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), - true /* isForward */, null /* profilerInfo */, assistToken); + true /* isForward */, null /* profilerInfo */, assistToken, + null /* fixedRotationAdjustments */); assertSame(item, item2); assertFalse(item2.equals(emptyItem)); } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index 09ea1b1865c0..3c32c71cf961 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -267,7 +267,7 @@ public class TransactionExecutorTests { null /* voiceInteractor */, 0 /* procState */, null /* state */, null /* persistentState */, null /* pendingResults */, null /* pendingNewIntents */, false /* isForward */, null /* profilerInfo */, - null /* assistToken*/)); + null /* assistToken */, null /* fixedRotationAdjustments */)); launchTransaction.addCallback(launchItem); mExecutor.execute(launchTransaction); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 47f9323a95f9..3f8d9ef964db 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -52,6 +52,9 @@ import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.DisplayAdjustments.FixedRotationAdjustments; +import android.view.DisplayCutout; +import android.view.Surface; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -187,11 +190,14 @@ public class TransactionParcelTests { bundle.putParcelable("data", new ParcelableData(1)); PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putInt("k", 4); + FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments( + Surface.ROTATION_90, DisplayCutout.NO_CUTOUT); LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), - true /* isForward */, null /* profilerInfo */, new Binder()); + true /* isForward */, null /* profilerInfo */, new Binder(), + fixedRotationAdjustments); writeAndPrepareForReading(item); // Read from parcel and assert @@ -340,6 +346,22 @@ public class TransactionParcelTests { assertTrue(transaction.equals(result)); } + @Test + public void testFixedRotationAdjustments() { + ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(), + null /* activityToken */); + transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(), + new FixedRotationAdjustments(Surface.ROTATION_270, DisplayCutout.NO_CUTOUT))); + + writeAndPrepareForReading(transaction); + + // Read from parcel and assert + ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); + + assertEquals(transaction.hashCode(), result.hashCode()); + assertTrue(transaction.equals(result)); + } + /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */ private void writeAndPrepareForReading(Parcelable parcelable) { parcelable.writeToParcel(mParcel, 0 /* flags */); diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 4114b28a7252..efcd458e19cc 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -259,4 +259,35 @@ public class ResourcesManagerTest extends TestCase { expectedConfig2.orientation = Configuration.ORIENTATION_LANDSCAPE; assertEquals(expectedConfig2, resources2.getConfiguration()); } + + @SmallTest + public void testOverrideDisplayAdjustments() { + final int originalOverrideDensity = 200; + final int overrideDisplayDensity = 400; + final Binder token = new Binder(); + final Configuration overrideConfig = new Configuration(); + overrideConfig.densityDpi = originalOverrideDensity; + final Resources resources = mResourcesManager.createBaseTokenResources( + token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */, + null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */, + null /* loaders */); + + // Update the override. + boolean handled = mResourcesManager.overrideTokenDisplayAdjustments(token, + adjustments -> adjustments.getConfiguration().densityDpi = overrideDisplayDensity); + + assertTrue(handled); + assertTrue(resources.hasOverrideDisplayAdjustments()); + assertEquals(overrideDisplayDensity, + resources.getDisplayAdjustments().getConfiguration().densityDpi); + + // Clear the override. + handled = mResourcesManager.overrideTokenDisplayAdjustments(token, null /* override */); + + assertTrue(handled); + assertFalse(resources.hasOverrideDisplayAdjustments()); + assertEquals(originalOverrideDensity, + resources.getDisplayAdjustments().getConfiguration().densityDpi); + } } diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java index afbf8db3cd2d..2fc42e91a8cc 100644 --- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java +++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java @@ -19,6 +19,9 @@ package android.view; import static org.junit.Assert.assertEquals; import android.content.res.Configuration; +import android.graphics.Point; +import android.util.DisplayMetrics; +import android.view.DisplayAdjustments.FixedRotationAdjustments; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -67,4 +70,38 @@ public class DisplayAdjustmentsTests { assertEquals(configuration, newAdjustments.getConfiguration()); } + + @Test + public void testFixedRotationAdjustments() { + final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); + final int realRotation = Surface.ROTATION_0; + final int fixedRotation = Surface.ROTATION_90; + + mDisplayAdjustments.setFixedRotationAdjustments( + new FixedRotationAdjustments(fixedRotation, null /* cutout */)); + + final int w = 1000; + final int h = 2000; + final Point size = new Point(w, h); + mDisplayAdjustments.adjustSize(size, realRotation); + + assertEquals(fixedRotation, mDisplayAdjustments.getRotation(realRotation)); + assertEquals(new Point(h, w), size); + + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.xdpi = metrics.noncompatXdpi = w; + metrics.widthPixels = metrics.noncompatWidthPixels = w; + metrics.ydpi = metrics.noncompatYdpi = h; + metrics.heightPixels = metrics.noncompatHeightPixels = h; + + final DisplayMetrics flippedMetrics = new DisplayMetrics(); + flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h; + flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h; + flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w; + flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w; + + mDisplayAdjustments.adjustMetrics(metrics, realRotation); + + assertEquals(flippedMetrics, metrics); + } } diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 0390ac6b8e9c..1cdc75aa1f40 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -225,7 +225,8 @@ public class ActivityThreadClientTest { CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */, null /* voiceInteractor */, null /* state */, null /* persistentState */, null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */, - null /* profilerInfo */, mThread /* client */, null /* asssitToken */); + null /* profilerInfo */, mThread /* client */, null /* asssitToken */, + null /* fixedRotationAdjustments */); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index fb7ba62b5fd2..afd92c74eda3 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -842,7 +842,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(), - r.assistToken)); + r.assistToken, r.createFixedRotationAdjustmentsIfNeeded())); // Set desired final state. final ActivityLifecycleItem lifecycleItem; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index cf585df87f24..768f89eff774 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.os.Process.INVALID_UID; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -40,6 +41,7 @@ import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.app.IWindowToken; +import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; @@ -47,6 +49,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.DisplayAdjustments.FixedRotationAdjustments; import android.view.DisplayInfo; import android.view.InsetsState; import android.view.SurfaceControl; @@ -529,6 +532,7 @@ class WindowToken extends WindowContainer<WindowState> { mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, insetsState, new Configuration(config), mDisplayContent.getRotation()); onConfigurationChanged(getParent().getConfiguration()); + notifyFixedRotationTransform(true /* enabled */); } /** @@ -546,6 +550,7 @@ class WindowToken extends WindowContainer<WindowState> { mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); onConfigurationChanged(getParent().getConfiguration()); + notifyFixedRotationTransform(true /* enabled */); } void finishFixedRotationTransform() { @@ -578,9 +583,52 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).mFixedRotationTransformState = null; + state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); } + cleanUpFixedRotationTransformState(); + } + + private void cleanUpFixedRotationTransformState() { mFixedRotationTransformState = null; + notifyFixedRotationTransform(false /* enabled */); + } + + /** Notifies application side to enable or disable the rotation adjustment of display info. */ + private void notifyFixedRotationTransform(boolean enabled) { + FixedRotationAdjustments adjustments = null; + // A token may contain windows of the same processes or different processes. The list is + // used to avoid sending the same adjustments to a process multiple times. + ArrayList<WindowProcessController> notifiedProcesses = null; + for (int i = mChildren.size() - 1; i >= 0; i--) { + final WindowState w = mChildren.get(i); + final WindowProcessController app; + if (w.mAttrs.type == TYPE_APPLICATION_STARTING) { + // Use the host activity because starting window is controlled by window manager. + final ActivityRecord r = asActivityRecord(); + if (r == null) { + continue; + } + app = r.app; + } else { + app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid); + } + if (app == null || !app.hasThread()) { + continue; + } + if (notifiedProcesses == null) { + notifiedProcesses = new ArrayList<>(2); + adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null; + } else if (notifiedProcesses.contains(app)) { + continue; + } + notifiedProcesses.add(app); + try { + mWmService.mAtmService.getLifecycleManager().scheduleTransaction( + app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e); + } + } } /** Restores the changes that applies to this container. */ @@ -590,6 +638,7 @@ class WindowToken extends WindowContainer<WindowState> { // The window may be detached or detaching. return; } + notifyFixedRotationTransform(false /* enabled */); final int originalRotation = getWindowConfiguration().getRotation(); onConfigurationChanged(parent.getConfiguration()); onCancelFixedRotationTransform(originalRotation); @@ -603,6 +652,14 @@ class WindowToken extends WindowContainer<WindowState> { void onCancelFixedRotationTransform(int originalDisplayRotation) { } + FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() { + if (!isFixedRotationTransforming()) { + return null; + } + return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation, + mFixedRotationTransformState.mDisplayInfo.displayCutout); + } + @Override void resolveOverrideConfiguration(Configuration newParentConfig) { super.resolveOverrideConfiguration(newParentConfig); |