/* ** ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ package com.android.server; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.UserHandle; import android.util.ArrayMap; import android.util.LruCache; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; /** * TODO: This should be better integrated into the system so it doesn't need * special calls from the activity manager to clear it. */ public final class AttributeCache { private static final int CACHE_SIZE = 4; private static AttributeCache sInstance = null; private final Context mContext; @GuardedBy("this") private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE); @GuardedBy("this") private final Configuration mConfiguration = new Configuration(); public final static class Package { public final Context context; private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>(); public Package(Context c) { context = c; } } public final static class Entry { public final Context context; public final TypedArray array; public Entry(Context c, TypedArray ta) { context = c; array = ta; } void recycle() { if (array != null) { array.recycle(); } } } public static void init(Context context) { if (sInstance == null) { sInstance = new AttributeCache(context); } } public static AttributeCache instance() { return sInstance; } public AttributeCache(Context context) { mContext = context; } public void removePackage(String packageName) { synchronized (this) { final Package pkg = mPackages.remove(packageName); if (pkg != null) { for (int i = 0; i < pkg.mMap.size(); i++) { final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i); for (int j = 0; j < map.size(); j++) { map.valueAt(j).recycle(); } } final Resources res = pkg.context.getResources(); res.flushLayoutCache(); } } } public void updateConfiguration(Configuration config) { synchronized (this) { int changes = mConfiguration.updateFrom(config); if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_KEYBOARD_HIDDEN | ActivityInfo.CONFIG_ORIENTATION)) != 0) { // The configurations being masked out are ones that commonly // change so we don't want flushing the cache... all others // will flush the cache. mPackages.evictAll(); } } } public Entry get(String packageName, int resId, int[] styleable, int userId) { synchronized (this) { Package pkg = mPackages.get(packageName); ArrayMap<int[], Entry> map = null; Entry ent = null; if (pkg != null) { map = pkg.mMap.get(resId); if (map != null) { ent = map.get(styleable); if (ent != null) { return ent; } } } else { Context context; try { context = mContext.createPackageContextAsUser(packageName, 0, new UserHandle(userId)); if (context == null) { return null; } } catch (PackageManager.NameNotFoundException e) { return null; } pkg = new Package(context); mPackages.put(packageName, pkg); } if (map == null) { map = new ArrayMap<>(); pkg.mMap.put(resId, map); } try { ent = new Entry(pkg.context, pkg.context.obtainStyledAttributes(resId, styleable)); map.put(styleable, ent); } catch (Resources.NotFoundException e) { return null; } return ent; } } }