diff options
| -rw-r--r-- | core/java/android/animation/AnimatorInflater.java | 4 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 10 | ||||
| -rw-r--r-- | core/java/android/content/res/AssetManager.java | 1 | ||||
| -rw-r--r-- | core/java/android/content/res/ConfigurationBoundResourceCache.java | 146 | ||||
| -rw-r--r-- | core/java/android/content/res/DrawableCache.java | 57 | ||||
| -rw-r--r-- | core/java/android/content/res/Resources.java | 296 | ||||
| -rw-r--r-- | core/java/android/content/res/ThemedResourceCache.java | 233 | ||||
| -rw-r--r-- | core/jni/android_util_AssetManager.cpp | 8 | ||||
| -rw-r--r-- | include/androidfw/ResourceTypes.h | 1 | ||||
| -rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 24 | ||||
| -rw-r--r-- | services/core/java/com/android/server/policy/GlobalActions.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 9 | 
12 files changed, 501 insertions, 290 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 427ecce6fec4..435d5ab96949 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -108,7 +108,7 @@ public class AnimatorInflater {              float pathErrorScale) throws NotFoundException {          final ConfigurationBoundResourceCache<Animator> animatorCache = resources                  .getAnimatorCache(); -        Animator animator = animatorCache.get(id, theme); +        Animator animator = animatorCache.getInstance(id, theme);          if (animator != null) {              if (DBG_ANIMATOR_INFLATER) {                  Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); @@ -157,7 +157,7 @@ public class AnimatorInflater {          final ConfigurationBoundResourceCache<StateListAnimator> cache = resources                  .getStateListAnimatorCache();          final Theme theme = context.getTheme(); -        StateListAnimator animator = cache.get(id, theme); +        StateListAnimator animator = cache.getInstance(id, theme);          if (animator != null) {              return animator;          } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index da6d8c5599af..f16406acc68f 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -38,6 +38,7 @@ import android.content.res.AssetManager;  import android.content.res.CompatibilityInfo;  import android.content.res.Configuration;  import android.content.res.Resources; +import android.content.res.Resources.Theme;  import android.database.sqlite.SQLiteDatabase;  import android.database.sqlite.SQLiteDebug;  import android.database.sqlite.SQLiteDebug.DbStats; @@ -4186,9 +4187,14 @@ public final class ActivityThread {              if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {                  return;              } -            configDiff = mConfiguration.diff(config); -            mConfiguration.updateFrom(config); + +            configDiff = mConfiguration.updateFrom(config);              config = applyCompatConfiguration(mCurDefaultDisplayDpi); + +            final Theme systemTheme = getSystemContext().getTheme(); +            if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { +                systemTheme.rebase(); +            }          }          ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 525059f19287..8d96f5c2a9ce 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -785,6 +785,7 @@ public final class AssetManager implements AutoCloseable {      private native final void deleteTheme(long theme);      /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);      /*package*/ native static final void copyTheme(long dest, long source); +    /*package*/ native static final void clearTheme(long theme);      /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,                                                                  TypedValue outValue,                                                                  boolean resolve); diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java index cde7e84b70ce..fecda8798250 100644 --- a/core/java/android/content/res/ConfigurationBoundResourceCache.java +++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java @@ -1,138 +1,58 @@  /* -* Copyright (C) 2014 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.content.res; + * Copyright (C) 2014 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. + */ -import android.util.ArrayMap; -import android.util.LongSparseArray; -import java.lang.ref.WeakReference; +package android.content.res;  /**   * A Cache class which can be used to cache resource objects that are easy to clone but more   * expensive to inflate. - * @hide + * + * @hide For internal use only.   */ -public class ConfigurationBoundResourceCache<T> { - -    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache = -            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>(); - -    final Resources mResources; +public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> { +    private final Resources mResources;      /** -     * Creates a Resource cache for the given Resources instance. +     * Creates a cache for the given Resources instance.       * -     * @param resources The Resource which can be used when creating new instances. +     * @param resources the resources to use when creating new instances       */      public ConfigurationBoundResourceCache(Resources resources) {          mResources = resources;      }      /** -     * Adds a new item to the cache. -     * -     * @param key A custom key that uniquely identifies the resource. -     * @param theme The Theme instance where this resource was loaded. -     * @param constantState The constant state that can create new instances of the resource. +     * If the resource is cached, creates and returns a new instance of it.       * +     * @param key a key that uniquely identifies the drawable resource +     * @param theme the theme where the resource will be used +     * @return a new instance of the resource, or {@code null} if not in +     *         the cache       */ -    public void put(long key, Resources.Theme theme, ConstantState<T> constantState) { -        if (constantState == null) { -            return; -        } -        final String themeKey = theme == null ? "" : theme.getKey(); -        LongSparseArray<WeakReference<ConstantState<T>>> themedCache; -        synchronized (this) { -            themedCache = mCache.get(themeKey); -            if (themedCache == null) { -                themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1); -                mCache.put(themeKey, themedCache); -            } -            themedCache.put(key, new WeakReference<ConstantState<T>>(constantState)); -        } -    } - -    /** -     * If the resource is cached, creates a new instance of it and returns. -     * -     * @param key The long key which can be used to uniquely identify the resource. -     * @param theme The The Theme instance where we want to load this resource. -     * -     * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns -     *         null. -     */ -    public T get(long key, Resources.Theme theme) { -        final String themeKey = theme != null ? theme.getKey() : ""; -        final LongSparseArray<WeakReference<ConstantState<T>>> themedCache; -        final WeakReference<ConstantState<T>> wr; -        synchronized (this) { -            themedCache = mCache.get(themeKey); -            if (themedCache == null) { -                return null; -            } -            wr = themedCache.get(key); -        } -        if (wr == null) { -            return null; -        } -        final ConstantState entry = wr.get(); +    public T getInstance(long key, Resources.Theme theme) { +        final ConstantState<T> entry = get(key, theme);          if (entry != null) { -            return  (T) entry.newInstance(mResources, theme); -        } else {  // our entry has been purged -            synchronized (this) { -                // there is a potential race condition here where this entry may be put in -                // another thread. But we prefer it to minimize lock duration -                themedCache.delete(key); -            } +            return entry.newInstance(mResources, theme);          } -        return null; -    } -    /** -     * Users of ConfigurationBoundResourceCache must call this method whenever a configuration -     * change happens. On this callback, the cache invalidates all resources that are not valid -     * anymore. -     * -     * @param configChanges The configuration changes -     */ -    public void onConfigurationChange(final int configChanges) { -        synchronized (this) { -            final int size = mCache.size(); -            for (int i = size - 1; i >= 0; i--) { -                final LongSparseArray<WeakReference<ConstantState<T>>> -                        themeCache = mCache.valueAt(i); -                onConfigurationChangeInt(themeCache, configChanges); -                if (themeCache.size() == 0) { -                    mCache.removeAt(i); -                } -            } -        } +        return null;      } -    private void onConfigurationChangeInt( -            final LongSparseArray<WeakReference<ConstantState<T>>> themeCache, -            final int configChanges) { -        final int size = themeCache.size(); -        for (int i = size - 1; i >= 0; i--) { -            final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i); -            final ConstantState<T> constantState = wr.get(); -            if (constantState == null || Configuration.needNewResources( -                    configChanges, constantState.getChangingConfigurations())) { -                themeCache.removeAt(i); -            } -        } +    @Override +    public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) { +        return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());      } -  } diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java new file mode 100644 index 000000000000..fc70bc60b5c2 --- /dev/null +++ b/core/java/android/content/res/DrawableCache.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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.content.res; + +import android.graphics.drawable.Drawable; + +/** + * Class which can be used to cache Drawable resources against a theme. + */ +class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { +    private final Resources mResources; + +    /** +     * Creates a cache for the given Resources instance. +     * +     * @param resources the resources to use when creating new instances +     */ +    public DrawableCache(Resources resources) { +        mResources = resources; +    } + +    /** +     * If the resource is cached, creates and returns a new instance of it. +     * +     * @param key a key that uniquely identifies the drawable resource +     * @param theme the theme where the resource will be used +     * @return a new instance of the resource, or {@code null} if not in +     *         the cache +     */ +    public Drawable getInstance(long key, Resources.Theme theme) { +        final Drawable.ConstantState entry = get(key, theme); +        if (entry != null) { +            return entry.newDrawable(mResources, theme); +        } + +        return null; +    } + +    @Override +    public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { +        return false; +    } +} diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ae41b69427e1..1d108a2b206b 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -20,6 +20,8 @@ import android.annotation.AttrRes;  import android.annotation.ColorInt;  import android.annotation.StyleRes;  import android.annotation.StyleableRes; + +import com.android.internal.util.GrowingArrayUtils;  import com.android.internal.util.XmlUtils;  import org.xmlpull.v1.XmlPullParser; @@ -132,10 +134,8 @@ public class Resources {      // These are protected by mAccessLock.      private final Object mAccessLock = new Object();      private final Configuration mTmpConfig = new Configuration(); -    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache = -            new ArrayMap<>(); -    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache = -            new ArrayMap<>(); +    private final DrawableCache mDrawableCache = new DrawableCache(this); +    private final DrawableCache mColorDrawableCache = new DrawableCache(this);      private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =              new ConfigurationBoundResourceCache<>(this);      private final ConfigurationBoundResourceCache<Animator> mAnimatorCache = @@ -1441,7 +1441,7 @@ public class Resources {              AssetManager.applyThemeStyle(mTheme, resId, force);              mThemeResId = resId; -            mKey += Integer.toHexString(resId) + (force ? "! " : " "); +            mKey.append(resId, force);          }          /** @@ -1457,7 +1457,7 @@ public class Resources {              AssetManager.copyTheme(mTheme, other.mTheme);              mThemeResId = other.mThemeResId; -            mKey = other.mKey; +            mKey.setTo(other.getKey());          }          /** @@ -1765,6 +1765,9 @@ public class Resources {              mTheme = mAssets.createTheme();          } +        /** Unique key for the series of styles applied to this theme. */ +        private final ThemeKey mKey = new ThemeKey(); +          @SuppressWarnings("hiding")          private final AssetManager mAssets;          private final long mTheme; @@ -1772,9 +1775,6 @@ public class Resources {          /** Resource identifier for the theme. */          private int mThemeResId = 0; -        /** Unique key for the series of styles applied to this theme. */ -        private String mKey = ""; -          // Needed by layoutlib.          /*package*/ long getNativeTheme() {              return mTheme; @@ -1784,7 +1784,7 @@ public class Resources {              return mThemeResId;          } -        /*package*/ String getKey() { +        /*package*/ ThemeKey getKey() {              return mKey;          } @@ -1793,29 +1793,119 @@ public class Resources {          }          /** -         * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data: -         * resource name followed by whether or not it was forced, as specified by -         * {@link #applyStyle(int, boolean)}. +         * Parses {@link #mKey} and returns a String array that holds pairs of +         * adjacent Theme data: resource name followed by whether or not it was +         * forced, as specified by {@link #applyStyle(int, boolean)}.           *           * @hide           */          @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)          public String[] getTheme() { -            String[] themeData = mKey.split(" "); -            String[] themes = new String[themeData.length * 2]; -            String theme; -            boolean forced; - -            for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) { -                theme = themeData[j]; -                forced = theme.endsWith("!"); -                themes[i] = forced ? -                        getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) : -                        getResourceNameFromHexString(theme); +            final int N = mKey.mCount; +            final String[] themes = new String[N * 2]; +            for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { +                final int resId = mKey.mResId[i]; +                final boolean forced = mKey.mForce[i]; +                themes[i] = getResourceName(resId);                  themes[i + 1] = forced ? "forced" : "not forced";              }              return themes;          } + +        /** +         * Rebases the theme against the parent Resource object's current +         * configuration by re-applying the styles passed to +         * {@link #applyStyle(int, boolean)}. +         * +         * @hide +         */ +        public void rebase() { +            AssetManager.clearTheme(mTheme); + +            // Reapply the same styles in the same order. +            for (int i = 0; i < mKey.mCount; i++) { +                final int resId = mKey.mResId[i]; +                final boolean force = mKey.mForce[i]; +                AssetManager.applyThemeStyle(mTheme, resId, force); +            } +        } +    } + +    static class ThemeKey implements Cloneable { +        int[] mResId; +        boolean[] mForce; +        int mCount; + +        private int mHashCode = 0; + +        public void append(int resId, boolean force) { +            if (mResId == null) { +                mResId = new int[4]; +            } + +            if (mForce == null) { +                mForce = new boolean[4]; +            } + +            mResId = GrowingArrayUtils.append(mResId, mCount, resId); +            mForce = GrowingArrayUtils.append(mForce, mCount, force); +            mCount++; + +            mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0); +        } + +        /** +         * Sets up this key as a deep copy of another key. +         * +         * @param other the key to deep copy into this key +         */ +        public void setTo(ThemeKey other) { +            mResId = other.mResId == null ? null : other.mResId.clone(); +            mForce = other.mForce == null ? null : other.mForce.clone(); +            mCount = other.mCount; +        } + +        @Override +        public int hashCode() { +            return mHashCode; +        } + +        @Override +        public boolean equals(Object o) { +            if (this == o) { +                return true; +            } + +            if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) { +                return false; +            } + +            final ThemeKey t = (ThemeKey) o; +            if (mCount != t.mCount) { +                return false; +            } + +            final int N = mCount; +            for (int i = 0; i < N; i++) { +                if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) { +                    return false; +                } +            } + +            return true; +        } + +        /** +         * @return a shallow copy of this key +         */ +        @Override +        public ThemeKey clone() { +            final ThemeKey other = new ThemeKey(); +            other.mResId = mResId; +            other.mForce = mForce; +            other.mCount = mCount; +            return other; +        }      }      /** @@ -1944,8 +2034,8 @@ public class Resources {                          + " final compat is " + mCompatibilityInfo);              } -            clearDrawableCachesLocked(mDrawableCache, configChanges); -            clearDrawableCachesLocked(mColorDrawableCache, configChanges); +            mDrawableCache.onConfigurationChange(configChanges); +            mColorDrawableCache.onConfigurationChange(configChanges);              mColorStateListCache.onConfigurationChange(configChanges);              mAnimatorCache.onConfigurationChange(configChanges);              mStateListAnimatorCache.onConfigurationChange(configChanges); @@ -1983,48 +2073,6 @@ public class Resources {          return configChanges;      } -    private void clearDrawableCachesLocked( -            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, -            int configChanges) { -        final int N = caches.size(); -        for (int i = 0; i < N; i++) { -            clearDrawableCacheLocked(caches.valueAt(i), configChanges); -        } -    } - -    private void clearDrawableCacheLocked( -            LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) { -        if (DEBUG_CONFIG) { -            Log.d(TAG, "Cleaning up drawables config changes: 0x" -                    + Integer.toHexString(configChanges)); -        } -        final int N = cache.size(); -        for (int i = 0; i < N; i++) { -            final WeakReference<ConstantState> ref = cache.valueAt(i); -            if (ref != null) { -                final ConstantState cs = ref.get(); -                if (cs != null) { -                    if (Configuration.needNewResources( -                            configChanges, cs.getChangingConfigurations())) { -                        if (DEBUG_CONFIG) { -                            Log.d(TAG, "FLUSHING #0x" -                                    + Long.toHexString(cache.keyAt(i)) -                                    + " / " + cs + " with changes: 0x" -                                    + Integer.toHexString(cs.getChangingConfigurations())); -                        } -                        cache.setValueAt(i, null); -                    } else if (DEBUG_CONFIG) { -                        Log.d(TAG, "(Keeping #0x" -                                + Long.toHexString(cache.keyAt(i)) -                                + " / " + cs + " with changes: 0x" -                                + Integer.toHexString(cs.getChangingConfigurations()) -                                + ")"); -                    } -                } -            } -        } -    } -      /**       * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)       * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively. @@ -2436,7 +2484,7 @@ public class Resources {          }          final boolean isColorDrawable; -        final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches; +        final DrawableCache caches;          final long key;          if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT                  && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { @@ -2452,7 +2500,7 @@ public class Resources {          // First, check whether we have a cached version of this drawable          // that was inflated against the specified theme.          if (!mPreloading) { -            final Drawable cachedDrawable = getCachedDrawable(caches, key, theme); +            final Drawable cachedDrawable = caches.getInstance(key, theme);              if (cachedDrawable != null) {                  return cachedDrawable;              } @@ -2479,13 +2527,8 @@ public class Resources {          // Determine if the drawable has unresolved theme attributes. If it          // does, we'll need to apply a theme and store it in a theme-specific          // cache. -        final String cacheKey; -        if (!dr.canApplyTheme()) { -            cacheKey = CACHE_NOT_THEMED; -        } else if (theme == null) { -            cacheKey = CACHE_NULL_THEME; -        } else { -            cacheKey = theme.getKey(); +        final boolean canApplyTheme = dr.canApplyTheme(); +        if (canApplyTheme && theme != null) {              dr = dr.mutate();              dr.applyTheme(theme);              dr.clearMutated(); @@ -2495,15 +2538,14 @@ public class Resources {          // cache: preload, not themed, null theme, or theme-specific.          if (dr != null) {              dr.setChangingConfigurations(value.changingConfigurations); -            cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr); +            cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);          }          return dr;      } -    private void cacheDrawable(TypedValue value, boolean isColorDrawable, -            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, -            String cacheKey, long key, Drawable dr) { +    private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, +            Theme theme, boolean usesTheme, long key, Drawable dr) {          final ConstantState cs = dr.getConstantState();          if (cs == null) {              return; @@ -2531,51 +2573,9 @@ public class Resources {              }          } else {              synchronized (mAccessLock) { -                LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey); -                if (themedCache == null) { -                    // Clean out the caches before we add more. This shouldn't -                    // happen very often. -                    pruneCaches(caches); -                    themedCache = new LongSparseArray<>(1); -                    caches.put(cacheKey, themedCache); -                } -                themedCache.put(key, new WeakReference<>(cs)); -            } -        } -    } - -    /** -     * Prunes empty caches from the cache map. -     * -     * @param caches The map of caches to prune. -     */ -    private void pruneCaches(ArrayMap<String, -            LongSparseArray<WeakReference<ConstantState>>> caches) { -        final int N = caches.size(); -        for (int i = N - 1; i >= 0; i--) { -            final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i); -            if (pruneCache(cache)) { -                caches.removeAt(i); -            } -        } -    } - -    /** -     * Prunes obsolete weak references from a cache, returning {@code true} if -     * the cache is empty and should be removed. -     * -     * @param cache The cache of weak references to prune. -     * @return {@code true} if the cache is empty and should be removed. -     */ -    private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) { -        final int N = cache.size(); -        for (int i = N - 1; i >= 0; i--) { -            final WeakReference entry = cache.valueAt(i); -            if (entry == null || entry.get() == null) { -                cache.removeAt(i); +                caches.put(key, theme, cs, usesTheme);              }          } -        return cache.size() == 0;      }      /** @@ -2631,51 +2631,6 @@ public class Resources {          return dr;      } -    private Drawable getCachedDrawable( -            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, -            long key, Theme theme) { -        synchronized (mAccessLock) { -            // First search theme-agnostic cache. -            final Drawable unthemedDrawable = getCachedDrawableLocked( -                    caches, key, CACHE_NOT_THEMED); -            if (unthemedDrawable != null) { -                return unthemedDrawable; -            } - -            // Next search theme-specific cache. -            final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME; -            return getCachedDrawableLocked(caches, key, themeKey); -        } -    } - -    private Drawable getCachedDrawableLocked( -            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, -            long key, String themeKey) { -        final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey); -        if (cache != null) { -            final ConstantState entry = getConstantStateLocked(cache, key); -            if (entry != null) { -                return entry.newDrawable(this); -            } -        } -        return null; -    } - -    private ConstantState getConstantStateLocked( -            LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { -        final WeakReference<ConstantState> wr = drawableCache.get(key); -        if (wr != null) { -            final ConstantState entry = wr.get(); -            if (entry != null) { -                return entry; -            } else { -                // Our entry has been purged. -                drawableCache.delete(key); -            } -        } -        return null; -    } -      @Nullable      ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)              throws NotFoundException { @@ -2713,8 +2668,7 @@ public class Resources {          }          final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache; - -        csl = cache.get(key, theme); +        csl = cache.getInstance(key, theme);          if (csl != null) {              return csl;          } diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java new file mode 100644 index 000000000000..9a2d061474e6 --- /dev/null +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2015 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.content.res; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Resources.Theme; +import android.content.res.Resources.ThemeKey; +import android.util.LongSparseArray; +import android.util.ArrayMap; + +import java.lang.ref.WeakReference; + +/** + * Data structure used for caching data against themes. + * + * @param <T> type of data to cache + */ +abstract class ThemedResourceCache<T> { +    private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries; +    private LongSparseArray<WeakReference<T>> mUnthemedEntries; +    private LongSparseArray<WeakReference<T>> mNullThemedEntries; + +    /** +     * Adds a new theme-dependent entry to the cache. +     * +     * @param key a key that uniquely identifies the entry +     * @param theme the theme against which this entry was inflated, or +     *              {@code null} if the entry has no theme applied +     * @param entry the entry to cache +     */ +    public void put(long key, @Nullable Theme theme, @NonNull T entry) { +        put(key, theme, entry, true); +    } + +    /** +     * Adds a new entry to the cache. +     * +     * @param key a key that uniquely identifies the entry +     * @param theme the theme against which this entry was inflated, or +     *              {@code null} if the entry has no theme applied +     * @param entry the entry to cache +     * @param usesTheme {@code true} if the entry is affected theme changes, +     *                  {@code false} otherwise +     */ +    public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) { +        if (entry == null) { +            return; +        } + +        synchronized (this) { +            final LongSparseArray<WeakReference<T>> entries; +            if (!usesTheme) { +                entries = getUnthemedLocked(true); +            } else { +                entries = getThemedLocked(theme, true); +            } +            if (entries != null) { +                entries.put(key, new WeakReference<>(entry)); +            } +        } +    } + +    /** +     * Returns an entry from the cache. +     * +     * @param key a key that uniquely identifies the entry +     * @param theme the theme where the entry will be used +     * @return a cached entry, or {@code null} if not in the cache +     */ +    @Nullable +    public T get(long key, @Nullable Theme theme) { +        // The themed (includes null-themed) and unthemed caches are mutually +        // exclusive, so we'll give priority to whichever one we think we'll +        // hit first. Since most of the framework drawables are themed, that's +        // probably going to be the themed cache. +        synchronized (this) { +            final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false); +            if (themedEntries != null) { +                final WeakReference<T> themedEntry = themedEntries.get(key); +                if (themedEntry != null) { +                    return themedEntry.get(); +                } +            } + +            final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false); +            if (unthemedEntries != null) { +                final WeakReference<T> unthemedEntry = unthemedEntries.get(key); +                if (unthemedEntry != null) { +                    return unthemedEntry.get(); +                } +            } +        } + +        return null; +    } + +    /** +     * Prunes cache entries that have been invalidated by a configuration +     * change. +     * +     * @param configChanges a bitmask of configuration changes +     */ +    public void onConfigurationChange(int configChanges) { +        prune(configChanges); +    } + +    /** +     * Returns whether a cached entry has been invalidated by a configuration +     * change. +     * +     * @param entry a cached entry +     * @param configChanges a non-zero bitmask of configuration changes +     * @return {@code true} if the entry is invalid, {@code false} otherwise +     */ +    protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges); + +    /** +     * Returns the cached data for the specified theme, optionally creating a +     * new entry if one does not already exist. +     * +     * @param t the theme for which to return cached data +     * @param create {@code true} to create an entry if one does not already +     *               exist, {@code false} otherwise +     * @return the cached data for the theme, or {@code null} if the cache is +     *         empty and {@code create} was {@code false} +     */ +    @Nullable +    private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) { +        if (t == null) { +            if (mNullThemedEntries == null && create) { +                mNullThemedEntries = new LongSparseArray<>(1); +            } +            return mNullThemedEntries; +        } + +        if (mThemedEntries == null) { +            if (create) { +                mThemedEntries = new ArrayMap<>(1); +            } else { +                return null; +            } +        } + +        final ThemeKey key = t.getKey(); +        LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key); +        if (cache == null && create) { +            cache = new LongSparseArray<>(1); + +            final ThemeKey keyClone = key.clone(); +            mThemedEntries.put(keyClone, cache); +        } + +        return cache; +    } + +    /** +     * Returns the theme-agnostic cached data. +     * +     * @param create {@code true} to create an entry if one does not already +     *               exist, {@code false} otherwise +     * @return the theme-agnostic cached data, or {@code null} if the cache is +     *         empty and {@code create} was {@code false} +     */ +    @Nullable +    private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) { +        if (mUnthemedEntries == null && create) { +            mUnthemedEntries = new LongSparseArray<>(1); +        } +        return mUnthemedEntries; +    } + +    /** +     * Prunes cache entries affected by configuration changes or where weak +     * references have expired. +     * +     * @param configChanges a bitmask of configuration changes, or {@code 0} to +     *                      simply prune missing weak references +     * @return {@code true} if the cache is completely empty after pruning +     */ +    private boolean prune(int configChanges) { +        synchronized (this) { +            if (mThemedEntries != null) { +                for (int i = mThemedEntries.size() - 1; i >= 0; i--) { +                    if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) { +                        mThemedEntries.removeAt(i); +                    } +                } +            } + +            pruneEntriesLocked(mNullThemedEntries, configChanges); +            pruneEntriesLocked(mUnthemedEntries, configChanges); + +            return mThemedEntries == null && mNullThemedEntries == null +                    && mUnthemedEntries == null; +        } +    } + +    private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries, +            int configChanges) { +        if (entries == null) { +            return true; +        } + +        for (int i = entries.size() - 1; i >= 0; i--) { +            final WeakReference<T> ref = entries.valueAt(i); +            if (ref == null || pruneEntryLocked(ref.get(), configChanges)) { +                entries.removeAt(i); +            } +        } + +        return entries.size() == 0; +    } + +    private boolean pruneEntryLocked(@Nullable T entry, int configChanges) { +        return entry == null || (configChanges != 0 +                && shouldInvalidateEntry(entry, configChanges)); +    } +} diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index db495dd4709f..74a9e4e2d162 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -976,6 +976,12 @@ static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,      dest->setTo(*src);  } +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ +    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); +    theme->clear(); +} +  static jint android_content_AssetManager_loadThemeAttributeValue(      JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)  { @@ -2108,6 +2114,8 @@ static JNINativeMethod gAssetManagerMethods[] = {          (void*) android_content_AssetManager_applyThemeStyle },      { "copyTheme", "(JJ)V",          (void*) android_content_AssetManager_copyTheme }, +    { "clearTheme", "(J)V", +        (void*) android_content_AssetManager_clearTheme },      { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",          (void*) android_content_AssetManager_loadThemeAttributeValue },      { "getThemeChangingConfigurations", "(J)I", diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 587e7fa761d6..b15caeb1b4ae 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1631,6 +1631,7 @@ public:          status_t applyStyle(uint32_t resID, bool force=false);          status_t setTo(const Theme& other); +        status_t clear();          /**           * Retrieve a value in the theme.  If the theme defines this diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 19a5bebfc14d..2ae7b0856e74 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3336,6 +3336,30 @@ status_t ResTable::Theme::setTo(const Theme& other)      return NO_ERROR;  } +status_t ResTable::Theme::clear() +{ +    if (kDebugTableTheme) { +        ALOGI("Clearing theme %p...\n", this); +        dumpToLog(); +    } + +    for (size_t i = 0; i < Res_MAXPACKAGE; i++) { +        if (mPackages[i] != NULL) { +            free_package(mPackages[i]); +            mPackages[i] = NULL; +        } +    } + +    mTypeSpecFlags = 0; + +    if (kDebugTableTheme) { +        ALOGI("Final theme:"); +        dumpToLog(); +    } + +    return NO_ERROR; +} +  ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,          uint32_t* outTypeSpecFlags) const  { diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java index b431b33598f4..3cee9279c018 100644 --- a/services/core/java/com/android/server/policy/GlobalActions.java +++ b/services/core/java/com/android/server/policy/GlobalActions.java @@ -1138,7 +1138,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac          public GlobalActionsDialog(Context context, AlertParams params) {              super(context, getDialogTheme(context)); -            mContext = context; +            mContext = getContext();              mAlert = new AlertController(mContext, this, getWindow());              mAdapter = (MyAdapter) params.mAdapter;              mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 2d265e2446f9..925a6091354a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -28,6 +28,7 @@ import android.content.Context;  import android.content.Intent;  import android.content.pm.PackageManager;  import android.content.res.Configuration; +import android.content.res.Resources.Theme;  import android.os.Build;  import android.os.Environment;  import android.os.FactoryTest; @@ -291,7 +292,7 @@ public final class SystemServer {      private void createSystemContext() {          ActivityThread activityThread = ActivityThread.systemMain();          mSystemContext = activityThread.getSystemContext(); -        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar); +        mSystemContext.setTheme(android.R.style.Theme_Material_DayNight_DarkActionBar);      }      /** @@ -1026,6 +1027,12 @@ public final class SystemServer {          w.getDefaultDisplay().getMetrics(metrics);          context.getResources().updateConfiguration(config, metrics); +        // The system context's theme may be configuration-dependent. +        final Theme systemTheme = context.getTheme(); +        if (systemTheme.getChangingConfigurations() != 0) { +            systemTheme.rebase(); +        } +          try {              // TODO: use boot phase              mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());  |