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

Commit 4abe48ea authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Improve window style cache" into main

parents f7ece445 47112c62
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -39,6 +39,17 @@ flag {
  }
}

flag {
  name: "cache_window_style"
  namespace: "windowing_frontend"
  description: "Cache common window styles"
  bug: "350394503"
  is_fixed_read_only: true
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
  name: "edge_to_edge_by_default"
  namespace: "windowing_frontend"
+15 −7
Original line number Diff line number Diff line
@@ -470,6 +470,20 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                                : info.isChangeEnabled(ENFORCE_EDGE_TO_EDGE))));
    }

    /**
     * This is similar to {@link #isOptingOutEdgeToEdgeEnforcement} but the caller needs to check
     * whether the app declares style to opt out.
     */
    public static boolean isOptOutEdgeToEdgeEnabled(ApplicationInfo info, boolean local) {
        final boolean disabled = Flags.disableOptOutEdgeToEdge()
                && (local
                        // Calling this doesn't require a permission.
                        ? CompatChanges.isChangeEnabled(DISABLE_OPT_OUT_EDGE_TO_EDGE)
                        // Calling this requires permissions.
                        : info.isChangeEnabled(DISABLE_OPT_OUT_EDGE_TO_EDGE));
        return !disabled;
    }

    /**
     * Returns whether the given application is opting out edge-to-edge enforcement.
     *
@@ -480,13 +494,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
     */
    public static boolean isOptingOutEdgeToEdgeEnforcement(ApplicationInfo info, boolean local,
            TypedArray windowStyle) {
        final boolean disabled = Flags.disableOptOutEdgeToEdge()
                && (local
                        // Calling this doesn't require a permission.
                        ? CompatChanges.isChangeEnabled(DISABLE_OPT_OUT_EDGE_TO_EDGE)
                        // Calling this requires permissions.
                        : info.isChangeEnabled(DISABLE_OPT_OUT_EDGE_TO_EDGE));
        return !disabled && windowStyle.getBoolean(
        return isOptOutEdgeToEdgeEnabled(info, local) && windowStyle.getBoolean(
                R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false /* default */);

    }
+84 −33
Original line number Diff line number Diff line
@@ -289,6 +289,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -352,7 +353,6 @@ import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.XmlUtils;
@@ -479,6 +479,8 @@ final class ActivityRecord extends WindowToken {
    final String processName; // process where this component wants to run
    final String taskAffinity; // as per ActivityInfo.taskAffinity
    final boolean stateNotNeeded; // As per ActivityInfo.flags
    @Nullable
    final WindowStyle mWindowStyle;
    @VisibleForTesting
    int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
    @VisibleForTesting
@@ -1926,22 +1928,17 @@ final class ActivityRecord extends WindowToken {
                    ? android.R.style.Theme : android.R.style.Theme_Holo;
        }

        final AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
                realTheme, com.android.internal.R.styleable.Window, mUserId);

        if (ent != null) {
            final boolean styleTranslucent = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
            final boolean styleFloating = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsFloating, false);
            mOccludesParent = !(styleTranslucent || styleFloating)
        final WindowStyle style = mAtmService.getWindowStyle(packageName, realTheme, mUserId);
        mWindowStyle = style;
        if (style != null) {
            mOccludesParent = !(style.isTranslucent() || style.isFloating())
                    // This style is propagated to the main window attributes with
                    // FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout.
                    || ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
                    || style.showWallpaper();
            mStyleFillsParent = mOccludesParent;
            mNoDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
            mOptOutEdgeToEdge = PhoneWindow.isOptingOutEdgeToEdgeEnforcement(
                    aInfo.applicationInfo, false /* local */, ent.array);
            mNoDisplay = style.noDisplay();
            mOptOutEdgeToEdge = style.optOutEdgeToEdge() && PhoneWindow.isOptOutEdgeToEdgeEnabled(
                    aInfo.applicationInfo, false /* local */);
        } else {
            mStyleFillsParent = mOccludesParent = true;
            mNoDisplay = false;
@@ -2272,21 +2269,17 @@ final class ActivityRecord extends WindowToken {
            return false;
        }

        final AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
                com.android.internal.R.styleable.Window, mWmService.mCurrentUserId);
        if (ent == null) {
        final WindowStyle style = theme == this.theme
                ? mWindowStyle : mAtmService.getWindowStyle(pkg, theme, mUserId);
        if (style == null) {
            // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
            // see that.
            return false;
        }
        final boolean windowIsTranslucent = ent.array.getBoolean(
                com.android.internal.R.styleable.Window_windowIsTranslucent, false);
        final boolean windowIsFloating = ent.array.getBoolean(
                com.android.internal.R.styleable.Window_windowIsFloating, false);
        final boolean windowShowWallpaper = ent.array.getBoolean(
                com.android.internal.R.styleable.Window_windowShowWallpaper, false);
        final boolean windowDisableStarting = ent.array.getBoolean(
                com.android.internal.R.styleable.Window_windowDisablePreview, false);
        final boolean windowIsTranslucent = style.isTranslucent();
        final boolean windowIsFloating = style.isFloating();
        final boolean windowShowWallpaper = style.showWallpaper();
        final boolean windowDisableStarting = style.disablePreview();
        ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
                windowIsTranslucent, windowIsFloating, windowShowWallpaper,
@@ -7135,14 +7128,10 @@ final class ActivityRecord extends WindowToken {
        if (theme == 0) {
            return false;
        }
        final AttributeCache.Entry ent = AttributeCache.instance().get(packageName, theme,
                R.styleable.Window, mWmService.mCurrentUserId);
        if (ent != null) {
            if (ent.array.hasValue(R.styleable.Window_windowSplashScreenBehavior)) {
                return ent.array.getInt(R.styleable.Window_windowSplashScreenBehavior,
                        SPLASH_SCREEN_BEHAVIOR_DEFAULT)
                        == SPLASH_SCREEN_BEHAVIOR_ICON_PREFERRED;
            }
        final WindowStyle style = theme == this.theme
                ? mWindowStyle : mAtmService.getWindowStyle(packageName, theme, mUserId);
        if (style != null) {
            return style.mSplashScreenBehavior == SPLASH_SCREEN_BEHAVIOR_ICON_PREFERRED;
        }
        return false;
    }
@@ -9814,6 +9803,68 @@ final class ActivityRecord extends WindowToken {
        int mBackgroundColor;
    }

    static class WindowStyle {
        private static final int FLAG_IS_TRANSLUCENT = 1;
        private static final int FLAG_IS_FLOATING = 1 << 1;
        private static final int FLAG_SHOW_WALLPAPER = 1 << 2;
        private static final int FLAG_NO_DISPLAY = 1 << 3;
        private static final int FLAG_DISABLE_PREVIEW = 1 << 4;
        private static final int FLAG_OPT_OUT_EDGE_TO_EDGE = 1 << 5;

        final int mFlags;

        @SplashScreenBehavior
        final int mSplashScreenBehavior;

        WindowStyle(TypedArray array) {
            int flags = 0;
            if (array.getBoolean(R.styleable.Window_windowIsTranslucent, false)) {
                flags |= FLAG_IS_TRANSLUCENT;
            }
            if (array.getBoolean(R.styleable.Window_windowIsFloating, false)) {
                flags |= FLAG_IS_FLOATING;
            }
            if (array.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
                flags |= FLAG_SHOW_WALLPAPER;
            }
            if (array.getBoolean(R.styleable.Window_windowNoDisplay, false)) {
                flags |= FLAG_NO_DISPLAY;
            }
            if (array.getBoolean(R.styleable.Window_windowDisablePreview, false)) {
                flags |= FLAG_DISABLE_PREVIEW;
            }
            if (array.getBoolean(R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false)) {
                flags |= FLAG_OPT_OUT_EDGE_TO_EDGE;
            }
            mFlags = flags;
            mSplashScreenBehavior = array.getInt(R.styleable.Window_windowSplashScreenBehavior, 0);
        }

        boolean isTranslucent() {
            return (mFlags & FLAG_IS_TRANSLUCENT) != 0;
        }

        boolean isFloating() {
            return (mFlags & FLAG_IS_FLOATING) != 0;
        }

        boolean showWallpaper() {
            return (mFlags & FLAG_SHOW_WALLPAPER) != 0;
        }

        boolean noDisplay() {
            return (mFlags & FLAG_NO_DISPLAY) != 0;
        }

        boolean disablePreview() {
            return (mFlags & FLAG_DISABLE_PREVIEW) != 0;
        }

        boolean optOutEdgeToEdge() {
            return (mFlags & FLAG_OPT_OUT_EDGE_TO_EDGE) != 0;
        }
    }

    static class Builder {
        private final ActivityTaskManagerService mAtmService;
        private WindowProcessController mCallerApp;
+15 −0
Original line number Diff line number Diff line
@@ -286,6 +286,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.utils.WindowStyleCache;
import com.android.wm.shell.Flags;

import java.io.BufferedReader;
@@ -500,6 +501,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

    boolean mSuppressResizeConfigChanges;

    private final WindowStyleCache<ActivityRecord.WindowStyle> mWindowStyleCache =
            new WindowStyleCache<>(ActivityRecord.WindowStyle::new);
    final UpdateConfigurationResult mTmpUpdateConfigurationResult =
            new UpdateConfigurationResult();

@@ -5570,6 +5573,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        return mUserManagerInternal;
    }

    @Nullable
    ActivityRecord.WindowStyle getWindowStyle(String packageName, int theme, int userId) {
        if (!com.android.window.flags.Flags.cacheWindowStyle()) {
            final AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
                    theme, com.android.internal.R.styleable.Window, userId);
            return ent != null ? new ActivityRecord.WindowStyle(ent.array) : null;
        }
        return mWindowStyleCache.get(packageName, theme, userId);
    }

    AppWarnings getAppWarningsLocked() {
        return mAppWarnings;
    }
@@ -6518,6 +6531,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                mCompatModePackages.handlePackageUninstalledLocked(name);
                mPackageConfigPersister.onPackageUninstall(name, userId);
            }
            mWindowStyleCache.invalidatePackage(name);
        }

        @Override
@@ -6534,6 +6548,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                if (mRootWindowContainer == null) return;
                mRootWindowContainer.updateActivityApplicationInfo(aInfo);
            }
            mWindowStyleCache.invalidatePackage(aInfo.packageName);
        }

        @Override
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.wm.utils;

import android.content.res.TypedArray;
import android.util.ArrayMap;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.policy.AttributeCache;

import java.util.function.Function;

/**
 * A wrapper of AttributeCache to preserve more dedicated style caches.
 * @param <T> The type of style cache.
 */
public class WindowStyleCache<T> {
    @GuardedBy("itself")
    private final ArrayMap<String, SparseArray<T>> mCache = new ArrayMap<>();
    private final Function<TypedArray, T> mEntryFactory;

    public WindowStyleCache(Function<TypedArray, T> entryFactory) {
        mEntryFactory = entryFactory;
    }

    /** Returns the cached entry. */
    public T get(String packageName, int theme, int userId) {
        SparseArray<T> themeMap;
        synchronized (mCache) {
            themeMap = mCache.get(packageName);
            if (themeMap != null) {
                T style = themeMap.get(theme);
                if (style != null) {
                    return style;
                }
            }
        }

        final AttributeCache attributeCache = AttributeCache.instance();
        if (attributeCache == null) {
            return null;
        }
        final AttributeCache.Entry ent = attributeCache.get(packageName, theme,
                R.styleable.Window, userId);
        if (ent == null) {
            return null;
        }

        final T style = mEntryFactory.apply(ent.array);
        synchronized (mCache) {
            if (themeMap == null) {
                mCache.put(packageName, themeMap = new SparseArray<>());
            }
            themeMap.put(theme, style);
        }
        return style;
    }

    /** Called when the package is updated or removed. */
    public void invalidatePackage(String packageName) {
        synchronized (mCache) {
            mCache.remove(packageName);
        }
    }
}
Loading