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

Commit c554bbfa authored by Bryce Lee's avatar Bryce Lee Committed by gitbuildkicker
Browse files

Add app bounds to configuration.

The system previously overrode the display size for a specific scope
(task/activity/etc.) by setting the associated Configuration's
screenWidthDp/screenHeightDp. This leads to two issues. First, the
conversion of screen size from pixels to display independent pixels
and then upconverting later on leads to rounding errors. Secondly,
the screenWidthDp and screenHeightDp values account for insets, such
as the status bar. These however are not reflected in the display
size when returned from Display#getMetrics/getSize.

This changelist addresses the issue by adding a Rect value to
Configuration which stores the app display bounds. This is always set
at the display level and overridden as appropriate. As the proper
app insets are accounted for at the root configuration, all overrides
(outside of specific exceptions) are the result of the intersection
between the requested bound and the parent bound.

Change-Id: I2c4fcd0bee92af12aabbca258de05b4ec061d0e1
Fixes: 34338931
Bug: 36812336
Bug: 36676979
Test: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsAppTestCases android.app.cts.AspectRatioTests
Test: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests
Test: bit FrameworksServicesTests:com.android.server.wm.AppBoundsTests
(cherry picked from commit 7566d76c)
parent 7f40e39a
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@

package android.content.res;

import android.graphics.Point;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
@@ -293,6 +298,16 @@ public final class Configuration implements Parcelable, Comparable<Configuration
     */
    public int screenLayout;

    /**
     * @hide
     * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
     * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
     * the display level. Lower levels can override these values to provide custom bounds to enforce
     * features such as a max aspect ratio.
     * TODO(b/36812336): Move appBounds out of {@link Configuration}.
     */
    public Rect appBounds;

    /** @hide */
    static public int resetScreenLayout(int curLayout) {
        return (curLayout&~(SCREENLAYOUT_LONG_MASK | SCREENLAYOUT_SIZE_MASK
@@ -882,6 +897,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        compatScreenWidthDp = o.compatScreenWidthDp;
        compatScreenHeightDp = o.compatScreenHeightDp;
        compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
        setAppBounds(o.appBounds);
        assetsSeq = o.assetsSeq;
        seq = o.seq;
    }
@@ -1032,6 +1048,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
            case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
            default: sb.append("/"); sb.append(navigationHidden); break;
        }
        if (appBounds != null) {
            sb.append(" appBounds="); sb.append(appBounds);
        }
        if (assetsSeq != 0) {
            sb.append(" as.").append(assetsSeq);
        }
@@ -1066,6 +1085,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
        densityDpi = DENSITY_DPI_UNDEFINED;
        assetsSeq = ASSETS_SEQ_UNDEFINED;
        appBounds = null;
        seq = 0;
    }

@@ -1253,6 +1273,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
            compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
        }
        if (delta.appBounds != null && !delta.appBounds.equals(appBounds)) {
            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
            setAppBounds(delta.appBounds);
        }
        if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED) {
            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
            assetsSeq = delta.assetsSeq;
@@ -1399,6 +1423,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration
            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
        }

        // Make sure that one of the values is not null and that they are not equal.
        if ((compareUndefined || delta.appBounds != null)
                && appBounds != delta.appBounds
                && (appBounds == null || !appBounds.equals(delta.appBounds))) {
            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
        }

        return changed;
    }

@@ -1494,6 +1525,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        dest.writeInt(compatScreenWidthDp);
        dest.writeInt(compatScreenHeightDp);
        dest.writeInt(compatSmallestScreenWidthDp);
        dest.writeValue(appBounds);
        dest.writeInt(assetsSeq);
        dest.writeInt(seq);
    }
@@ -1529,6 +1561,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        compatScreenWidthDp = source.readInt();
        compatScreenHeightDp = source.readInt();
        compatSmallestScreenWidthDp = source.readInt();
        appBounds = (Rect) source.readValue(null);
        assetsSeq = source.readInt();
        seq = source.readInt();
    }
@@ -1703,6 +1736,33 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        setLocales(loc == null ? LocaleList.getEmptyLocaleList() : new LocaleList(loc));
    }

    /**
     * @hide
     *
     * Helper method for setting the app bounds.
     */
    public void setAppBounds(Rect rect) {
        if (rect == null) {
            appBounds = null;
            return;
        }

        setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
    }

    /**
     * @hide
     *
     * Helper method for setting the app bounds.
     */
    public void setAppBounds(int left, int top, int right, int bottom) {
        if (appBounds == null) {
            appBounds = new Rect();
        }

        appBounds.set(left, top, right, bottom);
    }

    /**
     * @hide
     *
@@ -2212,6 +2272,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
    private static final String XML_ATTR_SCREEN_HEIGHT = "height";
    private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
    private static final String XML_ATTR_DENSITY = "density";
    private static final String XML_ATTR_APP_BOUNDS = "app_bounds";

    /**
     * Reads the attributes corresponding to Configuration member fields from the Xml parser.
@@ -2261,6 +2322,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
                        SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
        configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
                DENSITY_DPI_UNDEFINED);
        configOut.appBounds =
            Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS));

        // For persistence, we don't care about assetsSeq, so do not read it out.
    }
@@ -2332,6 +2395,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration
            XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
        }

        if (config.appBounds != null) {
            XmlUtils.writeStringAttribute(xml, XML_ATTR_APP_BOUNDS,
                config.appBounds.flattenToString());
        }

        // For persistence, we do not care about assetsSeq, so do not write it out.
    }
}
+4 −6
Original line number Diff line number Diff line
@@ -562,12 +562,10 @@ public final class DisplayInfo implements Parcelable {
        outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi;
        outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi;

        width = (configuration != null
                && configuration.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED)
                ? (int)((configuration.screenWidthDp * outMetrics.density) + 0.5f) : width;
        height = (configuration != null
                && configuration.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED)
                ? (int)((configuration.screenHeightDp * outMetrics.density) + 0.5f) : height;
        width = configuration != null && configuration.appBounds != null
                ? configuration.appBounds.width() : width;
        height = configuration != null && configuration.appBounds != null
                ? configuration.appBounds.height() : height;

        outMetrics.noncompatWidthPixels  = outMetrics.widthPixels = width;
        outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
+6 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.CheckResult;
import android.os.Parcel;
import android.os.Parcelable;

import android.text.TextUtils;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -170,6 +171,10 @@ public final class Rect implements Parcelable {
     * or null if the string is not of that form.
     */
    public static Rect unflattenFromString(String str) {
        if (TextUtils.isEmpty(str)) {
            return null;
        }

        Matcher matcher = UnflattenHelper.getMatcher(str);
        if (!matcher.matches()) {
            return null;
+22 −8
Original line number Diff line number Diff line
@@ -2122,23 +2122,31 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
        return true;
    }

    /** Computes the override configuration for this activity */
    /**
     * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
     */
    // 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) {
        if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0) {
            // 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.
            // the activity. This is indicated by an empty {@link outBounds}.
            return;
        }

        stack.getDisplaySize(mTmpPoint);
        int maxActivityWidth = mTmpPoint.x;
        int maxActivityHeight = mTmpPoint.y;
        if (mTmpPoint.x < mTmpPoint.y) {
        // We must base this on the parent configuration, because we set our override
        // configuration's appBounds based on the result of this method. If we used our own
        // configuration, it would be influenced by past invocations.
        final Configuration configuration = getParent().getConfiguration();
        final int containingAppWidth = configuration.appBounds.width();
        final int containingAppHeight = configuration.appBounds.height();
        int maxActivityWidth = containingAppWidth;
        int maxActivityHeight = containingAppHeight;

        if (containingAppWidth < containingAppHeight) {
            // 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);
@@ -2148,8 +2156,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
            maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
        }

        if (mTmpPoint.x <= maxActivityWidth && mTmpPoint.y <= maxActivityHeight) {
        if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
            // The display matches or is less than the activity aspect ratio, so nothing else to do.
            // Return the existing bounds. If this method is running for the first time,
            // {@link mBounds} will be empty (representing no override). If the method has run
            // before, then effect of {@link mBounds} will already have been applied to the
            // value returned from {@link getConfiguration}. Refer to
            // {@link TaskRecord#computeOverrideConfiguration}.
            outBounds.set(mBounds);
            return;
        }

+2 −5
Original line number Diff line number Diff line
@@ -2044,6 +2044,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta

        config.unset();
        final Configuration parentConfig = getParent().getConfiguration();

        final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;

        if (mStack != null) {
@@ -2052,11 +2053,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
                    mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
                    config, parentConfig);
        } else {
            // No stack, give some default values
            config.smallestScreenWidthDp =
                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
            config.screenWidthDp = config.screenHeightDp = config.smallestScreenWidthDp;
            Slog.wtf(TAG, "Expected stack when calculating override config");
            throw new IllegalArgumentException("Expected stack when calculating override config");
        }

        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
Loading