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

Commit 7b262506 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Distinguish window configuration for different tokens

If a ResourcesImpl is only used by one activity, it can be reused for
a pure WindowConfiguration change. But if there are other activities
sharing the ResourcesImpl, a new ResourcesImpl should be created for
the change.

For example, 2 activities have the same size and position:
  Activity1 -> ResourcesImplA bounds(0,0,100,200)
  Activity2 -> ResourcesImplA bounds(0,0,100,200)
If Activity2 changed position, it should have its own resources:
  Activity1 -> ResourcesImplA bounds(0,0,100,200)
  Activity2 -> ResourcesImplB bounds(10,10,110,210)

Also update all Configuration of reused ResourcesImpl to make its
values the same as using ResourcesManager#createResourcesImpl.

Bug: 413273054
Flag: android.content.res.ignore_non_public_config_diff_for_resources_key
Test: atest FrameworksCoreTests:ResourcesManagerTest
Change-Id: I96520a3b31b696cc4d2808866e7ab757beededda
parent 0d7a87f8
Loading
Loading
Loading
Loading
+80 −21
Original line number Diff line number Diff line
@@ -795,13 +795,19 @@ public class ResourcesManager {
     * Finds a cached ResourcesImpl object that matches the given ResourcesKey.
     *
     * @param key The key to match.
     * @return a ResourcesImpl if the key matches a cache entry, null otherwise.
     * @return a pair of key and ResourcesImpl if the key matches a cache entry, null otherwise.
     */
    private @Nullable ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
        WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
        ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
    @Nullable
    private Pair<ResourcesKey, ResourcesImpl> findResourcesImplPairForKeyLocked(
            @NonNull ResourcesKey key) {
        final int index = mResourceImpls.indexOfKey(key);
        if (index < 0) {
            return null;
        }
        final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(index);
        final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
        if (impl != null && impl.getAssets().isUpToDate()) {
            return impl;
            return new Pair<>(mResourceImpls.keyAt(index), impl);
        }
        return null;
    }
@@ -823,15 +829,29 @@ public class ResourcesManager {
     */
    private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
            @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
        ResourcesImpl impl = findResourcesImplForKeyLocked(key);
        final Pair<ResourcesKey, ResourcesImpl> pair =
                findOrCreateResourcesImplPairForKeyLocked(key, apkSupplier);
        return pair != null ? pair.second : null;
    }

    /**
     * Returns a pair consisting of the key (the instance may be different from the given one if
     * it matches an existing ResourcesImpl) and ResourcesImpl object matching the key.
     */
    @Nullable
    private Pair<ResourcesKey, ResourcesImpl> findOrCreateResourcesImplPairForKeyLocked(
            @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
        final Pair<ResourcesKey, ResourcesImpl> pair = findResourcesImplPairForKeyLocked(key);
        ResourcesImpl impl = pair != null ? pair.second : null;
        // ResourcesImpl also need to be recreated if its shared library hash is not up-to-date.
        if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) {
            impl = createResourcesImpl(key, apkSupplier);
            if (impl != null) {
                mResourceImpls.put(key, new WeakReference<>(impl));
                return new Pair<>(key, impl);
            }
        }
        return impl;
        return pair;
    }

    /**
@@ -990,6 +1010,7 @@ public class ResourcesManager {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                    "ResourcesManager#createBaseActivityResources");
            final ResourcesKey key = new ResourcesKey(
                    System.identityHashCode(token),
                    resDir,
                    splitResDirs,
                    combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
@@ -1248,6 +1269,7 @@ public class ResourcesManager {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
            final ResourcesKey key = new ResourcesKey(
                    System.identityHashCode(activityToken),
                    resDir,
                    splitResDirs,
                    combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
@@ -1352,16 +1374,18 @@ public class ResourcesManager {
                        continue;
                    }

                    final ResourcesKey newKey = rebaseActivityOverrideConfig(activityResource,
                            overrideConfig, displayId);
                    final ResourcesKey newKey = rebaseActivityOverrideConfig(activityToken,
                            activityResource, overrideConfig, displayId);
                    if (newKey == null) {
                        continue;
                    }

                    // TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl
                    // constructions.
                    final ResourcesImpl resourcesImpl =
                            findOrCreateResourcesImplForKeyLocked(newKey);
                    final Pair<ResourcesKey, ResourcesImpl> implPair =
                            findOrCreateResourcesImplPairForKeyLocked(
                                    newKey, null /* apkSupplier */);
                    final ResourcesImpl resourcesImpl = implPair != null ? implPair.second : null;
                    if (resourcesImpl == null) {
                        continue;
                    }
@@ -1369,15 +1393,26 @@ public class ResourcesManager {
                        // Set the ResourcesImpl, updating it for all users of this Resources
                        // object.
                        resources.setImpl(resourcesImpl);
                    } else if (android.content.res.Flags
                            .ignoreNonPublicConfigDiffForResourcesKey()) {
                    }
                    // Even if the new key matches an existing ResourcesImpl, the window
                    // configuration of the new key and the existing key can be different, e.g.
                    // only position change. So retrieve the existing key to check.
                    final ResourcesKey currentKey = implPair.first;
                    final boolean isReusedResImpl =
                            android.content.res.Flags.ignoreNonPublicConfigDiffForResourcesKey()
                                    && currentKey != null && currentKey != newKey;
                    if (isReusedResImpl) {
                        // If the ResourcesImpl is reused, also update fields not related to
                        // resources in case the app accesses WindowConfiguration, e.g. rotation.
                        final Configuration resConfig = resourcesImpl.getConfiguration();
                        resConfig.windowConfiguration.updateFrom(
                                newKey.mOverrideConfiguration.windowConfiguration);
                        if (newKey.mOverrideConfiguration.seq != 0) {
                            resConfig.seq = newKey.mOverrideConfiguration.seq;
                        // Note that the content of window configuration won't affect the result of
                        // ResourcesKey#equals/hashCode for the reused case.
                        if (updateWindowConfiguration(currentKey.mOverrideConfiguration,
                                newKey.mOverrideConfiguration)) {
                            updateWindowConfiguration(resourcesImpl.getConfiguration(),
                                    newKey.mOverrideConfiguration);
                            updateWindowConfiguration(
                                    resourcesImpl.getDisplayAdjustments().getConfiguration(),
                                    newKey.mOverrideConfiguration);
                        }
                    }
                }
@@ -1392,7 +1427,8 @@ public class ResourcesManager {
     * that an Activity's Resources should be set to.
     */
    @Nullable
    private ResourcesKey rebaseActivityOverrideConfig(@NonNull ActivityResource activityResource,
    private ResourcesKey rebaseActivityOverrideConfig(@NonNull IBinder activityToken,
            @NonNull ActivityResource activityResource,
            @Nullable Configuration newOverrideConfig, int displayId) {
        final Resources resources = activityResource.resources.get();
        if (resources == null) {
@@ -1442,8 +1478,8 @@ public class ResourcesManager {
        displayId = overrideDisplayId != null ? overrideDisplayId : displayId;

        // Create the new ResourcesKey with the rebased override config.
        final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
                oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
        final ResourcesKey newKey = new ResourcesKey(System.identityHashCode(activityToken),
                oldKey.mResDir, oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
                displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders);

        if (DEBUG) {
@@ -1454,6 +1490,25 @@ public class ResourcesManager {
        return newKey;
    }

    /**
     * Updates the window configuration of the destination configuration from the source
     * configuration.
     *
     * @param destConfig The destination configuration to update.
     * @param srcConfig The source configuration which will update to the destination configuration.
     * @return true if the destination configuration is changed.
     */
    private static boolean updateWindowConfiguration(@NonNull Configuration destConfig,
            @NonNull Configuration srcConfig) {
        final int changes = destConfig.windowConfiguration.updateFrom(
                srcConfig.windowConfiguration);
        if (changes != 0 && srcConfig.seq != 0) {
            destConfig.seq = srcConfig.seq;
        }
        return changes != 0;
    }


    @RavenwoodThrow(reason = "AppInfo update not supported")
    public void appendPendingAppInfoUpdate(@NonNull String[] oldSourceDirs,
            @NonNull ApplicationInfo appInfo) {
@@ -1608,6 +1663,7 @@ public class ResourcesManager {

                    if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
                        updatedResourceKeys.put(impl, new ResourcesKey(
                                key.mTokenIdentity,
                                key.mResDir,
                                key.mSplitResDirs,
                                key.mOverlayPaths,
@@ -1680,6 +1736,7 @@ public class ResourcesManager {

        @NonNull ResourcesKey collectedKey() {
            return new ResourcesKey(
                    originalKey == null ? 0 : originalKey.mTokenIdentity,
                    originalKey == null ? null : originalKey.mResDir,
                    originalKey == null ? null : originalKey.mSplitResDirs,
                    orderedOverlays.toArray(new String[0]), orderedLibs.toArray(new String[0]),
@@ -1764,6 +1821,7 @@ public class ResourcesManager {
                        || key.mResDir.equals(baseCodePath)
                        || ArrayUtils.contains(oldSourceDirs, key.mResDir)) {
                    updatedResourceKeys.put(impl, new ResourcesKey(
                            key.mTokenIdentity,
                            baseCodePath,
                            copiedSplitDirs,
                            copiedResourceDirs,
@@ -1927,6 +1985,7 @@ public class ResourcesManager {
                }

                final ResourcesKey newKey = new ResourcesKey(
                        oldKey.mTokenIdentity,
                        oldKey.mResDir,
                        oldKey.mSplitResDirs,
                        oldKey.mOverlayPaths,
+24 −11
Original line number Diff line number Diff line
@@ -31,6 +31,14 @@ import java.util.Objects;
/** @hide */
@RavenwoodKeepWholeClass
public final class ResourcesKey {
    /**
     * The identity hash of owner Activity/WindowTokenClient's token. It can be zero for global
     * resources. If the token identity is set, it means that different
     * {@link Configuration#windowConfiguration} of {@link #mOverrideConfiguration} cannot be
     * shared for different tokens.
     */
    public final int mTokenIdentity;

    @Nullable
    @UnsupportedAppUsage
    public final String mResDir;
@@ -67,7 +75,8 @@ public final class ResourcesKey {

    private final int mHash;

    public ResourcesKey(@Nullable String resDir,
    public ResourcesKey(int tokenIdentity,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayPaths,
            @Nullable String[] libDirs,
@@ -75,6 +84,7 @@ public final class ResourcesKey {
            @Nullable Configuration overrideConfig,
            @Nullable CompatibilityInfo compatInfo,
            @Nullable ResourcesLoader[] loader) {
        mTokenIdentity = tokenIdentity;
        mResDir = resDir;
        mSplitResDirs = splitResDirs;
        mOverlayPaths = overlayPaths;
@@ -105,8 +115,8 @@ public final class ResourcesKey {
            int displayId,
            @Nullable Configuration overrideConfig,
            @Nullable CompatibilityInfo compatInfo) {
        this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo,
                null);
        this(0 /* tokenIdentity */, resDir, splitResDirs, overlayPaths, libDirs, displayId,
                overrideConfig, compatInfo, null /* loader */);
    }

    public boolean hasOverrideConfiguration() {
@@ -166,9 +176,12 @@ public final class ResourcesKey {
            return false;
        }
        if (android.content.res.Flags.ignoreNonPublicConfigDiffForResourcesKey()) {
            // Different tokens need to have their own ResourcesImpl instances to store different
            // window configurations.
            final boolean ignoreWindowConfig = mTokenIdentity == peer.mTokenIdentity;
            // Do not compare the configuration fields that won't affect resources.
            if (mOverrideConfiguration.diff(peer.mOverrideConfiguration,
                    true /* compareUndefined */, true /* publicOnly */) != 0) {
                    true /* compareUndefined */, ignoreWindowConfig /* publicOnly */) != 0) {
                return false;
            }
        } else if (!Objects.equals(mOverrideConfiguration, peer.mOverrideConfiguration)) {
+18 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.content.res;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
@@ -377,16 +378,33 @@ public class ResourcesManagerTest {
    @SmallTest
    public void testUpdateResourcesForActivityUpdateWindowConfiguration() {
        final Binder activity = new Binder();
        final Binder activity2 = new Binder();
        final Configuration overrideConfig = new Configuration();
        final Resources resources = mResourcesManager.getResources(
                activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY,
                overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
        final Resources resources2 = mResourcesManager.getResources(
                activity2, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY,
                overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
        overrideConfig.windowConfiguration.getBounds().set(100, 100, 600, 1200);
        mResourcesManager.updateResourcesForActivity(activity, overrideConfig,
                Display.DEFAULT_DISPLAY);

        assertEquals(overrideConfig.windowConfiguration,
                resources.getConfiguration().windowConfiguration);
        assertEquals(overrideConfig.windowConfiguration,
                resources.getDisplayAdjustments().getConfiguration().windowConfiguration);
        assertNotEquals(resources.getConfiguration().windowConfiguration,
                resources2.getConfiguration().windowConfiguration);

        overrideConfig.windowConfiguration.getBounds().offset(10, 10);
        mResourcesManager.updateResourcesForActivity(activity2, overrideConfig,
                Display.DEFAULT_DISPLAY);

        assertEquals(overrideConfig.windowConfiguration,
                resources2.getConfiguration().windowConfiguration);
        assertNotEquals(resources.getConfiguration().windowConfiguration,
                resources2.getConfiguration().windowConfiguration);
    }

    @Test