Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1f3375e authored by Charles Chen's avatar Charles Chen
Browse files

Extract WindowMetrics logic to WindowMetricsController

We would add some mechanisms to improve WindowMetrics performance,
which will introduce more WindowMetrics logic.
Extracting it to a separate module will make the WindowMetrics logic
easier to maintain. No logic changes.

Bug: 187712731
Test: atest WindowMetricsActivityTests WindowMetricsWindowContextTests
Change-Id: Ic35501efc4e2b73af80593f3b1911be35c2f0e48
parent 68cfa172
Loading
Loading
Loading
Loading
+9 −104
Original line number Diff line number Diff line
@@ -16,11 +16,8 @@

package android.view;

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.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.window.WindowProviderService.isWindowProviderService;

import android.annotation.CallbackExecutor;
@@ -28,12 +25,9 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
@@ -42,6 +36,7 @@ import android.os.StrictMode;
import android.window.ITaskFpsCallback;
import android.window.TaskFpsCallback;
import android.window.WindowContext;
import android.window.WindowMetricsController;
import android.window.WindowProvider;

import com.android.internal.annotations.GuardedBy;
@@ -49,7 +44,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -107,6 +101,10 @@ public final class WindowManagerImpl implements WindowManager {
    private final ArrayList<OnFpsCallbackListenerProxy> mOnFpsCallbackListenerProxies =
            new ArrayList<>();

    /** A controller to handle {@link WindowMetrics} related APIs */
    @NonNull
    private final WindowMetricsController mWindowMetricsController;

    public WindowManagerImpl(Context context) {
        this(context, null /* parentWindow */, null /* clientToken */);
    }
@@ -116,6 +114,7 @@ public final class WindowManagerImpl implements WindowManager {
        mContext = context;
        mParentWindow = parentWindow;
        mWindowContextToken = windowContextToken;
        mWindowMetricsController = new WindowMetricsController(mContext);
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
@@ -293,112 +292,18 @@ public final class WindowManagerImpl implements WindowManager {

    @Override
    public WindowMetrics getCurrentWindowMetrics() {
        final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
        final Rect bounds = getCurrentBounds(context);

        return new WindowMetrics(bounds, computeWindowInsets(bounds));
    }

    private static Rect getCurrentBounds(Context context) {
        synchronized (ResourcesManager.getInstance()) {
            return context.getResources().getConfiguration().windowConfiguration.getBounds();
        }
        return mWindowMetricsController.getCurrentWindowMetrics();
    }

    @Override
    public WindowMetrics getMaximumWindowMetrics() {
        final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
        final Rect maxBounds = getMaximumBounds(context);

        return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
    }

    private static Rect getMaximumBounds(Context context) {
        synchronized (ResourcesManager.getInstance()) {
            return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
        }
    }

    private WindowInsets computeWindowInsets(Rect bounds) {
        // Initialize params which used for obtaining all system insets.
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        final Context context = (mParentWindow != null) ? mParentWindow.getContext() : mContext;
        params.token = Context.getToken(context);
        return getWindowInsetsFromServerForCurrentDisplay(params, 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, 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 */);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return mWindowMetricsController.getMaximumWindowMetrics();
    }

    @Override
    @NonNull
    public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
        List<DisplayInfo> possibleDisplayInfos;
        try {
            possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
                    .getPossibleDisplayInfo(displayId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        Set<WindowMetrics> maxMetrics = new HashSet<>();
        WindowInsets windowInsets;
        DisplayInfo currentDisplayInfo;
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        for (int i = 0; i < possibleDisplayInfos.size(); i++) {
            currentDisplayInfo = possibleDisplayInfos.get(i);

            // Calculate max bounds for this rotation and state.
            Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
                    currentDisplayInfo.logicalHeight);

            // Calculate insets for the rotated max bounds.
            final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
            // Initialize insets based upon display rotation. Note any window-provided insets
            // will not be set.
            windowInsets = getWindowInsetsFromServerForDisplay(
                    currentDisplayInfo.displayId, params,
                    new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
                            currentDisplayInfo.getNaturalHeight()), isScreenRound,
                    WINDOWING_MODE_FULLSCREEN);
            // Set the hardware-provided insets.
            windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
                    currentDisplayInfo.roundedCorners)
                    .setDisplayCutout(currentDisplayInfo.displayCutout).build();

            maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
        }
        return maxMetrics;
        return mWindowMetricsController.getPossibleMaximumWindowMetrics(displayId);
    }

    @Override
+172 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.window;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;

import android.annotation.NonNull;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A controller to handle {@link android.view.WindowMetrics} related APIs, which are
 * <ol>
 *     <li>{@link WindowManager#getCurrentWindowMetrics()}</li>
 *     <li>{@link WindowManager#getMaximumWindowMetrics()}</li>
 *     <li>{@link WindowManager#getPossibleMaximumWindowMetrics(int)}</li>
 * </ol>
 *
 * @hide
 */
public final class WindowMetricsController {
    private final Context mContext;

    public WindowMetricsController(@NonNull Context context) {
        mContext = context;
    }

    /** @see WindowManager#getCurrentWindowMetrics() */
    public WindowMetrics getCurrentWindowMetrics() {
        final Rect bounds = getCurrentBounds(mContext);

        // TODO(b/187712731): Provide density for WindowMetrics.
        return new WindowMetrics(bounds, computeWindowInsets(bounds));
    }

    private static Rect getCurrentBounds(Context context) {
        synchronized (ResourcesManager.getInstance()) {
            return context.getResources().getConfiguration().windowConfiguration.getBounds();
        }
    }

    /** @see WindowManager#getMaximumWindowMetrics() */
    public WindowMetrics getMaximumWindowMetrics() {
        final Rect maxBounds = getMaximumBounds(mContext);

        // TODO(b/187712731): Provide density for WindowMetrics.
        return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
    }

    private static Rect getMaximumBounds(Context context) {
        synchronized (ResourcesManager.getInstance()) {
            return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
        }
    }

    private WindowInsets computeWindowInsets(Rect bounds) {
        // Initialize params which used for obtaining all system insets.
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.token = Context.getToken(mContext);
        return getWindowInsetsFromServerForCurrentDisplay(params, bounds);
    }

    private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
            WindowManager.LayoutParams attrs, Rect bounds) {
        final boolean isScreenRound;
        final int windowingMode;
        synchronized (ResourcesManager.getInstance()) {
            final Configuration config = mContext.getResources().getConfiguration();
            isScreenRound = config.isScreenRound();
            windowingMode = config.windowConfiguration.getWindowingMode();
        }
        return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), attrs, bounds,
                isScreenRound, windowingMode);
    }

    /**
     * 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, 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 */);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** @see WindowManager#getPossibleMaximumWindowMetrics(int) */
    @NonNull
    public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
        List<DisplayInfo> possibleDisplayInfos;
        try {
            possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
                    .getPossibleDisplayInfo(displayId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        Set<WindowMetrics> maxMetrics = new HashSet<>();
        WindowInsets windowInsets;
        DisplayInfo currentDisplayInfo;
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        for (int i = 0; i < possibleDisplayInfos.size(); i++) {
            currentDisplayInfo = possibleDisplayInfos.get(i);

            // Calculate max bounds for this rotation and state.
            Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
                    currentDisplayInfo.logicalHeight);

            // Calculate insets for the rotated max bounds.
            final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
            // Initialize insets based upon display rotation. Note any window-provided insets
            // will not be set.
            windowInsets = getWindowInsetsFromServerForDisplay(
                    currentDisplayInfo.displayId, params,
                    new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
                            currentDisplayInfo.getNaturalHeight()), isScreenRound,
                    WINDOWING_MODE_FULLSCREEN);
            // Set the hardware-provided insets.
            windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
                            currentDisplayInfo.roundedCorners)
                    .setDisplayCutout(currentDisplayInfo.displayCutout).build();

            maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
        }
        return maxMetrics;
    }
}