diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 52d57cf0a7f64..8e149a98af0eb 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -33,6 +33,7 @@ import android.os.Bundle; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; import android.view.DisplayCutout; +import android.view.DisplayInfo; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.ICrossWindowBlurEnabledListener; @@ -735,6 +736,17 @@ interface IWindowManager boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId, out InsetsState outInsetsState); + /** + * Returns a list of {@link android.view.DisplayInfo} for the logical display. This is not + * guaranteed to include all possible device states. The list items are unique. + * + * If invoked through a package other than a launcher app, returns an empty list. + * + * @param displayId the id of the logical display + * @param packageName the name of the calling package + */ + List getPossibleDisplayInfo(int displayId, String packageName); + /** * Called to show global actions. */ diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 75b69cb12d32e..39172794f6028 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -202,7 +202,7 @@ public class InsetsState implements Parcelable { @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { Insets[] typeInsetsMap = new Insets[Type.SIZE]; Insets[] typeMaxInsetsMap = new Insets[Type.SIZE]; - boolean[] typeVisibilityMap = new boolean[SIZE]; + boolean[] typeVisibilityMap = new boolean[Type.SIZE]; final Rect relativeFrame = new Rect(frame); final Rect relativeFrameMax = new Rect(frame); for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 0f1a9d9c0a986..cde1cc704f929 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -903,6 +903,16 @@ public final class WindowInsets { result.append(mPrivacyIndicatorBounds != null ? "privacyIndicatorBounds=" + mPrivacyIndicatorBounds : ""); result.append("\n "); + result.append("compatInsetsTypes=" + mCompatInsetsTypes); + result.append("\n "); + result.append("compatIgnoreVisibility=" + mCompatIgnoreVisibility); + result.append("\n "); + result.append("systemWindowInsetsConsumed=" + mSystemWindowInsetsConsumed); + result.append("\n "); + result.append("stableInsetsConsumed=" + mStableInsetsConsumed); + result.append("\n "); + result.append("displayCutoutConsumed=" + mDisplayCutoutConsumed); + result.append("\n "); result.append(isRound() ? "round" : ""); result.append("}"); return result.toString(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 3ce3fabbd6fea..51cd95e427428 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -123,6 +123,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -713,6 +714,20 @@ public interface WindowManager extends ViewManager { throw new UnsupportedOperationException(); } + /** + * Returns a set of {@link WindowMetrics} for the given display. Each WindowMetrics instance + * is the maximum WindowMetrics for a device state, including rotations. This is not guaranteed + * to include all possible device states. + * + * This API can only be used by Launcher. + * + * @param displayId the id of the logical display + * @hide + */ + default @NonNull Set getPossibleMaximumWindowMetrics(int displayId) { + throw new UnsupportedOperationException(); + } + /** * Used to asynchronously request Keyboard Shortcuts from the focused window. * diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index a2d3e3447354a..0fc6b08ae02bb 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -16,12 +16,9 @@ package android.view; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.window.WindowProviderService.isWindowProviderService; @@ -46,7 +43,9 @@ import android.window.WindowProvider; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -304,33 +303,88 @@ public final class WindowManagerImpl implements WindowManager { private WindowInsets computeWindowInsets(Rect bounds) { // Initialize params which used for obtaining all system insets. final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); - params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; final Context context = (mParentWindow != null) ? mParentWindow.getContext() : mContext; params.token = Context.getToken(context); - params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - params.setFitInsetsTypes(0); - params.setFitInsetsSides(0); - - return getWindowInsetsFromServer(params, bounds); + return getWindowInsetsFromServerForCurrentDisplay(params, bounds); } - private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) { + private WindowInsets getWindowInsetsFromServerForCurrentDisplay( + WindowManager.LayoutParams attrs, Rect bounds) { + final Configuration config = mContext.getResources().getConfiguration(); + return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), attrs, bounds, + config.isScreenRound(), config.windowConfiguration.getWindowingMode()); + } + + /** + * Retrieves WindowInsets for the given context and display, given the window bounds. + * + * @param displayId the ID of the logical display to calculate insets for + * @param attrs the LayoutParams for the calling app + * @param bounds the window bounds to calculate insets for + * @param isScreenRound if the display identified by displayId is round + * @param windowingMode the windowing mode of the window to calculate insets for + * @return WindowInsets calculated for the given window bounds, on the given display + */ + private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId, + WindowManager.LayoutParams attrs, Rect bounds, boolean isScreenRound, + int windowingMode) { try { final InsetsState insetsState = new InsetsState(); final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService() - .getWindowInsets(attrs, mContext.getDisplayId(), insetsState); - final Configuration config = mContext.getResources().getConfiguration(); - final boolean isScreenRound = config.isScreenRound(); - final int windowingMode = config.windowConfiguration.getWindowingMode(); + .getWindowInsets(attrs, displayId, insetsState); return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/, isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags, - SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, null /* typeSideMap */); + SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, + null /* typeSideMap */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + @Override + @NonNull + public Set getPossibleMaximumWindowMetrics(int displayId) { + List possibleDisplayInfos; + try { + possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService() + .getPossibleDisplayInfo(displayId, mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + int size = possibleDisplayInfos.size(); + DisplayInfo currentDisplayInfo; + WindowInsets windowInsets = null; + if (size > 0) { + currentDisplayInfo = possibleDisplayInfos.get(0); + + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0; + // TODO(181127261) not computing insets correctly - need to have underlying + // frame reflect the faked orientation. + windowInsets = getWindowInsetsFromServerForDisplay( + currentDisplayInfo.displayId, params, + new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), + currentDisplayInfo.getNaturalHeight()), isScreenRound, + WINDOWING_MODE_FULLSCREEN); + } + + Set maxMetrics = new HashSet<>(); + for (int i = 0; i < size; i++) { + currentDisplayInfo = possibleDisplayInfos.get(i); + + // Calculate max bounds for this rotation and state. + Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), + currentDisplayInfo.getNaturalHeight()); + + // Calculate insets for the rotated max bounds. + // TODO(181127261) calculate insets for each display rotation and state. + + maxMetrics.add(new WindowMetrics(maxBounds, windowInsets)); + } + return maxMetrics; + } + @Override public void holdLock(IBinder token, int durationMs) { try { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4340a365775a5..232c283699551 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -46,6 +46,8 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; @@ -152,6 +154,7 @@ import android.app.IActivityTaskManager; import android.app.IAssistDataReceiver; import android.app.WindowConfiguration; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -161,6 +164,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.TestUtilityService; import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Bitmap; @@ -322,11 +326,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; @@ -8450,6 +8456,65 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public List getPossibleDisplayInfo(int displayId, String packageName) { + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (packageName == null || !isRecentsComponent(packageName, callingUid)) { + Slog.e(TAG, "Unable to verify uid for package " + packageName + + " for getPossibleMaximumWindowMetrics"); + return new ArrayList<>(); + } + // TODO(181127261) DisplayInfo should be pushed from DisplayManager. + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + Slog.e(TAG, "Invalid displayId " + displayId + + " for getPossibleMaximumWindowMetrics"); + return new ArrayList<>(); + } + + // TODO(181127261) DisplayManager should provide a DisplayInfo for each rotation + DisplayInfo currentDisplayInfo = dc.getDisplayInfo(); + Set displayInfoSet = new HashSet<>(); + for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) { + currentDisplayInfo.rotation = rotation; + // TODO(181127261) Retrieve the device state from display stack. + displayInfoSet.add(new DisplayInfo(currentDisplayInfo)); + } + return new ArrayList(displayInfoSet); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Returns {@code true} when the calling package is the recents component. + */ + boolean isRecentsComponent(@NonNull String callingPackageName, int callingUid) { + String recentsPackage; + try { + String recentsComponent = mContext.getResources().getString( + R.string.config_recentsComponentName); + if (recentsComponent == null) { + return false; + } + recentsPackage = ComponentName.unflattenFromString(recentsComponent).getPackageName(); + } catch (Resources.NotFoundException e) { + Slog.e(TAG, "Unable to verify if recents component", e); + return false; + } + try { + return callingUid == mContext.getPackageManager().getPackageUid(callingPackageName, 0) + && callingPackageName.equals(recentsPackage); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Unable to verify if recents component", e); + return false; + } + } + void grantEmbeddedWindowFocus(Session session, IBinder inputToken, boolean grantFocus) { synchronized (mGlobalLock) { final EmbeddedWindowController.EmbeddedWindow embeddedWindow =