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

Commit 7f3f4998 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Allow multiple Resources associated with an Activity

Previously it was assumed that one Resources object per Activity would be fine.
This proved to be incorrect, as methods like Context#createConfigurationContext()
expect to create a new Resources object that gets updated whenever the Activity Resources
get updated.

To fix this issue, there exists a master override configuration for each Activity. Subsequent
Resources created for an Activity will be based off that master override configuration, and
will be updated accordingly.

Bug:27644297
Bug:27604953
Change-Id: Idb3d851f55ee7981eec4e3fe28e2ea8d55f1192d
parent 0ade7ff3
Loading
Loading
Loading
Loading
+29 −11
Original line number Original line Diff line number Diff line
@@ -1993,7 +1993,7 @@ class ContextImpl extends Context {
        ContextImpl context = new ContextImpl(null, mainThread,
        ContextImpl context = new ContextImpl(null, mainThread,
                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
        context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
        context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                context.mResourcesManager.getDisplayMetricsLocked());
                context.mResourcesManager.getDisplayMetrics());
        return context;
        return context;
    }
    }


@@ -2065,6 +2065,10 @@ class ContextImpl extends Context {
                    || overrideConfiguration != null
                    || overrideConfiguration != null
                    || (compatInfo != null && compatInfo.applicationScale
                    || (compatInfo != null && compatInfo.applicationScale
                            != resources.getCompatibilityInfo().applicationScale)) {
                            != resources.getCompatibilityInfo().applicationScale)) {

                if (container != null) {
                    // This is a nested Context, so it can't be a base Activity context.
                    // Just create a regular Resources object associated with the Activity.
                    resources = mResourcesManager.getResources(
                    resources = mResourcesManager.getResources(
                            activityToken,
                            activityToken,
                            packageInfo.getResDir(),
                            packageInfo.getResDir(),
@@ -2075,6 +2079,20 @@ class ContextImpl extends Context {
                            overrideConfiguration,
                            overrideConfiguration,
                            compatInfo,
                            compatInfo,
                            packageInfo.getClassLoader());
                            packageInfo.getClassLoader());
                } else {
                    // This is not a nested Context, so it must be the root Activity context.
                    // All other nested Contexts will inherit the configuration set here.
                    resources = mResourcesManager.createBaseActivityResources(
                            activityToken,
                            packageInfo.getResDir(),
                            packageInfo.getSplitResDirs(),
                            packageInfo.getOverlayDirs(),
                            packageInfo.getApplicationInfo().sharedLibraryFiles,
                            displayId,
                            overrideConfiguration,
                            compatInfo,
                            packageInfo.getClassLoader());
                }
            }
            }
        }
        }
        mResources = resources;
        mResources = resources;
+227 −73
Original line number Original line Diff line number Diff line
@@ -94,9 +94,18 @@ public class ResourcesManager {
    private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
    private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();


    /**
    /**
     * Each Activity may have only one Resources object.
     * Resources and base configuration override associated with an Activity.
     */
     */
    private final WeakHashMap<IBinder, WeakReference<Resources>> mActivityResourceReferences =
    private static class ActivityResources {
        public final Configuration overrideConfig = new Configuration();
        public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
    }

    /**
     * Each Activity may has a base override configuration that is applied to each Resources object,
     * which in turn may have their own override configuration specified.
     */
    private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
            new WeakHashMap<>();
            new WeakHashMap<>();


    /**
    /**
@@ -115,18 +124,20 @@ public class ResourcesManager {
    }
    }


    public Configuration getConfiguration() {
    public Configuration getConfiguration() {
        synchronized (this) {
            return mResConfiguration;
            return mResConfiguration;
        }
        }
    }


    DisplayMetrics getDisplayMetricsLocked() {
    DisplayMetrics getDisplayMetrics() {
        return getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
        return getDisplayMetrics(Display.DEFAULT_DISPLAY);
    }
    }


    /**
    /**
     * Protected so that tests can override and returns something a fixed value.
     * Protected so that tests can override and returns something a fixed value.
     */
     */
    @VisibleForTesting
    @VisibleForTesting
    protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
    protected DisplayMetrics getDisplayMetrics(int displayId) {
        DisplayMetrics dm = new DisplayMetrics();
        DisplayMetrics dm = new DisplayMetrics();
        final Display display =
        final Display display =
                getAdjustedDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
                getAdjustedDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -272,10 +283,9 @@ public class ResourcesManager {
        return config;
        return config;
    }
    }



    private ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
    private ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
        AssetManager assets = createAssetManager(key);
        AssetManager assets = createAssetManager(key);
        DisplayMetrics dm = getDisplayMetricsLocked(key.mDisplayId);
        DisplayMetrics dm = getDisplayMetrics(key.mDisplayId);
        Configuration config = generateConfig(key, dm);
        Configuration config = generateConfig(key, dm);
        ResourcesImpl impl = new ResourcesImpl(assets, dm, config, key.mCompatInfo);
        ResourcesImpl impl = new ResourcesImpl(assets, dm, config, key.mCompatInfo);
        if (DEBUG) {
        if (DEBUG) {
@@ -290,7 +300,7 @@ public class ResourcesManager {
     * @param key The key to match.
     * @param key The key to match.
     * @return a ResourcesImpl if the key matches a cache entry, null otherwise.
     * @return a ResourcesImpl if the key matches a cache entry, null otherwise.
     */
     */
    private ResourcesImpl findResourcesImplForKey(@NonNull ResourcesKey key) {
    private ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
        WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
        WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
        ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
        ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
        if (impl != null && impl.getAssets().isUpToDate()) {
        if (impl != null && impl.getAssets().isUpToDate()) {
@@ -303,7 +313,7 @@ public class ResourcesManager {
     * Find the ResourcesKey that this ResourcesImpl object is associated with.
     * Find the ResourcesKey that this ResourcesImpl object is associated with.
     * @return the ResourcesKey or null if none was found.
     * @return the ResourcesKey or null if none was found.
     */
     */
    private ResourcesKey findKeyForResourceImpl(@NonNull ResourcesImpl resourceImpl) {
    private ResourcesKey findKeyForResourceImplLocked(@NonNull ResourcesImpl resourceImpl) {
        final int refCount = mResourceImpls.size();
        final int refCount = mResourceImpls.size();
        for (int i = 0; i < refCount; i++) {
        for (int i = 0; i < refCount; i++) {
            WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
            WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
@@ -315,36 +325,46 @@ public class ResourcesManager {
        return null;
        return null;
    }
    }


    private ActivityResources getOrCreateActivityResourcesStructLocked(
            @NonNull IBinder activityToken) {
        ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
        if (activityResources == null) {
            activityResources = new ActivityResources();
            mActivityResourceReferences.put(activityToken, activityResources);
        }
        return activityResources;
    }

    /**
    /**
     * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
     * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
     * or the class loader is different.
     * or the class loader is different.
     */
     */
    private Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
    private Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
            @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
            @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
        // This is a request tied to an Activity, meaning we will need to update all
        final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
        // Activity related Resources to match this configuration.
                activityToken);
        WeakReference<Resources> weakResourceRef = mActivityResourceReferences.get(activityToken);

        Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
        final int refCount = activityResources.activityResources.size();
        if (resources == null || !Objects.equals(resources.getClassLoader(), classLoader)) {
        for (int i = 0; i < refCount; i++) {
            resources = new Resources(classLoader);
            WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
            mActivityResourceReferences.put(activityToken, new WeakReference<>(resources));
            Resources resources = weakResourceRef.get();
            if (DEBUG) {

                Slog.d(TAG, "- creating new ref=" + resources);
            if (resources != null
            }
                    && Objects.equals(resources.getClassLoader(), classLoader)
        } else {
                    && resources.getImpl() == impl) {
                if (DEBUG) {
                if (DEBUG) {
                    Slog.d(TAG, "- using existing ref=" + resources);
                    Slog.d(TAG, "- using existing ref=" + resources);
                }
                }
                return resources;
            }
            }

        if (resources.getImpl() != impl) {
            if (DEBUG) {
                Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
        }
        }


            // Setting an impl is expensive because we update all ThemeImpl references.
        Resources resources = new Resources(classLoader);
            // too.
        resources.setImpl(impl);
        resources.setImpl(impl);
        activityResources.activityResources.add(new WeakReference<>(resources));
        if (DEBUG) {
            Slog.d(TAG, "- creating new ref=" + resources);
            Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
        }
        }
        return resources;
        return resources;
    }
    }
@@ -359,7 +379,7 @@ public class ResourcesManager {
        final int refCount = mResourceReferences.size();
        final int refCount = mResourceReferences.size();
        for (int i = 0; i < refCount; i++) {
        for (int i = 0; i < refCount; i++) {
            WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
            WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
            Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
            Resources resources = weakResourceRef.get();
            if (resources != null &&
            if (resources != null &&
                    Objects.equals(resources.getClassLoader(), classLoader) &&
                    Objects.equals(resources.getClassLoader(), classLoader) &&
                    resources.getImpl() == impl) {
                    resources.getImpl() == impl) {
@@ -382,33 +402,26 @@ public class ResourcesManager {
    }
    }


    /**
    /**
     * Gets or creates a new Resources object associated with the IBinder token. References returned
     * Creates base resources for an Activity. Calls to
     * by this method live as long as the Activity, meaning they can be cached and used by the
     * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
     * Activity even after a configuration change. If any other parameter is changed
     * CompatibilityInfo, ClassLoader)} with the same activityToken will have their override
     * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object
     * configurations merged with the one specified here.
     * is updated and handed back to the caller. However, changing the class loader will result in a
     * new Resources object.
     * <p/>
     * If activityToken is null, a cached Resources object will be returned if it matches the
     * input parameters. Otherwise a new Resources object that satisfies these parameters is
     * returned.
     *
     *
     * @param activityToken Represents an Activity. If null, global resources are assumed.
     * @param activityToken Represents an Activity.
     * @param resDir The base resource path. Can be null (only framework resources will be loaded).
     * @param resDir The base resource path. Can be null (only framework resources will be loaded).
     * @param splitResDirs An array of split resource paths. Can be null.
     * @param splitResDirs An array of split resource paths. Can be null.
     * @param overlayDirs An array of overlay paths. Can be null.
     * @param overlayDirs An array of overlay paths. Can be null.
     * @param libDirs An array of resource library paths. Can be null.
     * @param libDirs An array of resource library paths. Can be null.
     * @param displayId The ID of the display for which to create the resources.
     * @param displayId The ID of the display for which to create the resources.
     * @param overrideConfig The configuration to apply on top of the base configuration. Can be
     * @param overrideConfig The configuration to apply on top of the base configuration. Can be
     * null. Mostly used with Activities that are in multi-window which may override width and
     *                       null. This provides the base override for this Activity.
     * height properties from the base config.
     * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
     * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
     *                   {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
     *                   {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
     * @param classLoader The class loader to use when inflating Resources. If null, the
     * @param classLoader The class loader to use when inflating Resources. If null, the
     *                    {@link ClassLoader#getSystemClassLoader()} is used.
     *                    {@link ClassLoader#getSystemClassLoader()} is used.
     * @return a Resources object from which to access resources.
     * @return a Resources object from which to access resources.
     */
     */
    public Resources getResources(@Nullable IBinder activityToken,
    public Resources createBaseActivityResources(@NonNull IBinder activityToken,
            @Nullable String resDir,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] overlayDirs,
@@ -425,9 +438,40 @@ public class ResourcesManager {
                displayId,
                displayId,
                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                compatInfo);
                compatInfo);

        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();


        synchronized (this) {
            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
                    activityToken);

            if (overrideConfig != null) {
                activityResources.overrideConfig.setTo(overrideConfig);
            } else {
                activityResources.overrideConfig.setToDefaults();
            }
        }

        // Update any existing Activity Resources references.
        updateResourcesForActivity(activityToken, overrideConfig);

        // Now request an actual Resources object.
        return getOrCreateResources(activityToken, key, classLoader);
    }

    /**
     * Gets an existing Resources object set with a ResourcesImpl object matching the given key,
     * or creates one if it doesn't exist.
     *
     * @param activityToken The Activity this Resources object should be associated with.
     * @param key The key describing the parameters of the ResourcesImpl object.
     * @param classLoader The classloader to use for the Resources object.
     *                    If null, {@link ClassLoader#getSystemClassLoader()} is used.
     * @return A Resources object that gets updated when
     *         {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
     *         is called.
     */
    private Resources getOrCreateResources(@Nullable IBinder activityToken,
            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
        final boolean findSystemLocales;
        final boolean findSystemLocales;
        final boolean hasNonSystemLocales;
        final boolean hasNonSystemLocales;
        synchronized (this) {
        synchronized (this) {
@@ -441,7 +485,22 @@ public class ResourcesManager {
            }
            }


            if (activityToken != null) {
            if (activityToken != null) {
                ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
                final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
                        activityToken);

                // Clean up any dead references so they don't pile up.
                ArrayUtils.unstableRemoveIf(activityResources.activityResources,
                        sEmptyReferencePredicate);

                // Rebase the key's override config on top of the Activity's base override.
                if (key.hasOverrideConfiguration()
                        && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
                    final Configuration temp = new Configuration(activityResources.overrideConfig);
                    temp.updateFrom(key.mOverrideConfiguration);
                    key.mOverrideConfiguration.setTo(temp);
                }

                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
                if (resourcesImpl != null) {
                if (resourcesImpl != null) {
                    if (DEBUG) {
                    if (DEBUG) {
                        Slog.d(TAG, "- using existing impl=" + resourcesImpl);
                        Slog.d(TAG, "- using existing impl=" + resourcesImpl);
@@ -457,7 +516,7 @@ public class ResourcesManager {
                ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
                ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);


                // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
                // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
                ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
                if (resourcesImpl != null) {
                if (resourcesImpl != null) {
                    if (DEBUG) {
                    if (DEBUG) {
                        Slog.d(TAG, "- using existing impl=" + resourcesImpl);
                        Slog.d(TAG, "- using existing impl=" + resourcesImpl);
@@ -489,7 +548,7 @@ public class ResourcesManager {
            mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
            mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
            mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
            mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;


            ResourcesImpl existingResourcesImpl = findResourcesImplForKey(key);
            ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
            if (existingResourcesImpl != null) {
            if (existingResourcesImpl != null) {
                if (DEBUG) {
                if (DEBUG) {
                    Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
                    Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
@@ -513,6 +572,54 @@ public class ResourcesManager {
        }
        }
    }
    }


    /**
     * Gets or creates a new Resources object associated with the IBinder token. References returned
     * by this method live as long as the Activity, meaning they can be cached and used by the
     * Activity even after a configuration change. If any other parameter is changed
     * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object
     * is updated and handed back to the caller. However, changing the class loader will result in a
     * new Resources object.
     * <p/>
     * If activityToken is null, a cached Resources object will be returned if it matches the
     * input parameters. Otherwise a new Resources object that satisfies these parameters is
     * returned.
     *
     * @param activityToken Represents an Activity. If null, global resources are assumed.
     * @param resDir The base resource path. Can be null (only framework resources will be loaded).
     * @param splitResDirs An array of split resource paths. Can be null.
     * @param overlayDirs An array of overlay paths. Can be null.
     * @param libDirs An array of resource library paths. Can be null.
     * @param displayId The ID of the display for which to create the resources.
     * @param overrideConfig The configuration to apply on top of the base configuration. Can be
     * null. Mostly used with Activities that are in multi-window which may override width and
     * height properties from the base config.
     * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
     * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
     * @param classLoader The class loader to use when inflating Resources. If null, the
     * {@link ClassLoader#getSystemClassLoader()} is used.
     * @return a Resources object from which to access resources.
     */
    public Resources getResources(@Nullable IBinder activityToken,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] libDirs,
            int displayId,
            @Nullable Configuration overrideConfig,
            @NonNull CompatibilityInfo compatInfo,
            @Nullable ClassLoader classLoader) {
        final ResourcesKey key = new ResourcesKey(
                resDir,
                splitResDirs,
                overlayDirs,
                libDirs,
                displayId,
                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                compatInfo);
        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
        return getOrCreateResources(activityToken, key, classLoader);
    }

    /**
    /**
     * Updates an Activity's Resources object with overrideConfig. The Resources object
     * Updates an Activity's Resources object with overrideConfig. The Resources object
     * that was previously returned by
     * that was previously returned by
@@ -524,31 +631,78 @@ public class ResourcesManager {
     */
     */
    public void updateResourcesForActivity(@NonNull IBinder activityToken,
    public void updateResourcesForActivity(@NonNull IBinder activityToken,
            @Nullable Configuration overrideConfig) {
            @Nullable Configuration overrideConfig) {
        final ClassLoader classLoader;
        final ResourcesKey oldKey;
        synchronized (this) {
        synchronized (this) {
            // Extract the ResourcesKey that was last used to create the Resources for this
            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
            // activity.
                    activityToken);
            WeakReference<Resources> weakResRef = mActivityResourceReferences.get(activityToken);

            final Resources resources = weakResRef != null ? weakResRef.get() : null;
            if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
            if (resources == null) {
                // They are the same, no work to do.
                Slog.e(TAG, "can't update resources for uncached activity " + activityToken);
                return;
                return;
            }
            }


            classLoader = resources.getClassLoader();
            // Grab a copy of the old configuration so we can create the delta's of each
            oldKey = findKeyForResourceImpl(resources.getImpl());
            // Resources object associated with this Activity.
            final Configuration oldConfig = new Configuration(activityResources.overrideConfig);

            // Update the Activity's base override.
            if (overrideConfig != null) {
                activityResources.overrideConfig.setTo(overrideConfig);
            } else {
                activityResources.overrideConfig.setToDefaults();
            }

            final boolean activityHasOverrideConfig =
                    !activityResources.overrideConfig.equals(Configuration.EMPTY);

            // Rebase each Resources associated with this Activity.
            final int refCount = activityResources.activityResources.size();
            for (int i = 0; i < refCount; i++) {
                WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
                Resources resources = weakResRef.get();
                if (resources == null) {
                    continue;
                }

                // Extract the ResourcesKey that was last used to create the Resources for this
                // activity.
                final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
                if (oldKey == null) {
                if (oldKey == null) {
                Slog.e(TAG, "can't find ResourcesKey for resources impl=" + resources.getImpl());
                    Slog.e(TAG, "can't find ResourcesKey for resources impl="
                return;
                            + resources.getImpl());
                    continue;
                }

                // Build the new override configuration for this ResourcesKey.
                final Configuration rebasedOverrideConfig = new Configuration();
                if (overrideConfig != null) {
                    rebasedOverrideConfig.setTo(overrideConfig);
                }

                if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
                    // Generate a delta between the old base Activity override configuration and
                    // the actual final override configuration that was used to figure out the real
                    // delta this Resources object wanted.
                    Configuration overrideOverrideConfig = Configuration.generateDelta(
                            oldConfig, oldKey.mOverrideConfiguration);
                    rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
                }
                }

                // Create the new ResourcesKey with the rebased override config.
                final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
                        oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
                        rebasedOverrideConfig, oldKey.mCompatInfo);

                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
                if (resourcesImpl == null) {
                    resourcesImpl = createResourcesImpl(newKey);
                }
                }


        // Update the Resources object with the new override config and all of the existing
                if (resourcesImpl != resources.getImpl()) {
        // settings.
                    // Set the ResourcesImpl, updating it for all users of this Resources object.
        getResources(activityToken, oldKey.mResDir, oldKey.mSplitResDirs, oldKey.mOverlayDirs,
                    resources.setImpl(resourcesImpl);
                oldKey.mLibDirs, oldKey.mDisplayId, overrideConfig, oldKey.mCompatInfo,
                }
                classLoader);
            }
        }
    }
    }


    /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
    /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
@@ -578,7 +732,7 @@ public class ResourcesManager {
        int changes = mResConfiguration.updateFrom(config);
        int changes = mResConfiguration.updateFrom(config);
        // Things might have changed in display manager, so clear the cached displays.
        // Things might have changed in display manager, so clear the cached displays.
        mDisplays.clear();
        mDisplays.clear();
        DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked();
        DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();


        if (compat != null && (mResCompatibilityInfo == null ||
        if (compat != null && (mResCompatibilityInfo == null ||
                !mResCompatibilityInfo.equals(compat))) {
                !mResCompatibilityInfo.equals(compat))) {
@@ -632,7 +786,7 @@ public class ResourcesManager {
                    }
                    }
                    tmpConfig.setTo(localeAdjustedConfig);
                    tmpConfig.setTo(localeAdjustedConfig);
                    if (!isDefaultDisplay) {
                    if (!isDefaultDisplay) {
                        dm = getDisplayMetricsLocked(displayId);
                        dm = getDisplayMetrics(displayId);
                        applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
                        applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
                    }
                    }
                    if (hasOverrideConfiguration) {
                    if (hasOverrideConfiguration) {
+9 −0
Original line number Original line Diff line number Diff line
@@ -1251,6 +1251,15 @@
        <service android:name="android.os.BinderThreadPriorityService"
        <service android:name="android.os.BinderThreadPriorityService"
                android:process=":BinderThreadPriorityService" />
                android:process=":BinderThreadPriorityService" />


        <!-- Used by ApplyOverrideConfigurationTest -->
        <activity android:name="android.app.activity.ApplyOverrideConfigurationActivity"
                  android:configChanges="orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
            </intent-filter>
        </activity>

        <!-- Application components used for search manager tests -->
        <!-- Application components used for search manager tests -->


        <activity android:name="android.app.activity.SearchableActivity"
        <activity android:name="android.app.activity.SearchableActivity"
Loading