1 /* 2 ** 3 ** Copyright 2007, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 package com.android.server; 19 20 import android.content.Context; 21 import android.content.pm.ActivityInfo; 22 import android.content.pm.PackageManager; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.content.res.TypedArray; 26 import android.os.UserHandle; 27 import android.util.ArrayMap; 28 import android.util.LruCache; 29 import android.util.SparseArray; 30 31 import com.android.internal.annotations.GuardedBy; 32 33 /** 34 * TODO: This should be better integrated into the system so it doesn't need 35 * special calls from the activity manager to clear it. 36 */ 37 public final class AttributeCache { 38 private static final int CACHE_SIZE = 4; 39 private static AttributeCache sInstance = null; 40 41 private final Context mContext; 42 43 @GuardedBy("this") 44 private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE); 45 46 @GuardedBy("this") 47 private final Configuration mConfiguration = new Configuration(); 48 49 public final static class Package { 50 public final Context context; 51 private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>(); 52 53 public Package(Context c) { 54 context = c; 55 } 56 } 57 58 public final static class Entry { 59 public final Context context; 60 public final TypedArray array; 61 62 public Entry(Context c, TypedArray ta) { 63 context = c; 64 array = ta; 65 } 66 67 void recycle() { 68 if (array != null) { 69 array.recycle(); 70 } 71 } 72 } 73 74 public static void init(Context context) { 75 if (sInstance == null) { 76 sInstance = new AttributeCache(context); 77 } 78 } 79 80 public static AttributeCache instance() { 81 return sInstance; 82 } 83 84 public AttributeCache(Context context) { 85 mContext = context; 86 } 87 88 public void removePackage(String packageName) { 89 synchronized (this) { 90 final Package pkg = mPackages.remove(packageName); 91 if (pkg != null) { 92 for (int i = 0; i < pkg.mMap.size(); i++) { 93 final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i); 94 for (int j = 0; j < map.size(); j++) { 95 map.valueAt(j).recycle(); 96 } 97 } 98 99 final Resources res = pkg.context.getResources(); 100 res.flushLayoutCache(); 101 } 102 } 103 } 104 105 public void updateConfiguration(Configuration config) { 106 synchronized (this) { 107 int changes = mConfiguration.updateFrom(config); 108 if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE | 109 ActivityInfo.CONFIG_KEYBOARD_HIDDEN | 110 ActivityInfo.CONFIG_ORIENTATION)) != 0) { 111 // The configurations being masked out are ones that commonly 112 // change so we don't want flushing the cache... all others 113 // will flush the cache. 114 mPackages.evictAll(); 115 } 116 } 117 } 118 119 public Entry get(String packageName, int resId, int[] styleable, int userId) { 120 synchronized (this) { 121 Package pkg = mPackages.get(packageName); 122 ArrayMap<int[], Entry> map = null; 123 Entry ent = null; 124 if (pkg != null) { 125 map = pkg.mMap.get(resId); 126 if (map != null) { 127 ent = map.get(styleable); 128 if (ent != null) { 129 return ent; 130 } 131 } 132 } else { 133 Context context; 134 try { 135 context = mContext.createPackageContextAsUser(packageName, 0, 136 new UserHandle(userId)); 137 if (context == null) { 138 return null; 139 } 140 } catch (PackageManager.NameNotFoundException e) { 141 return null; 142 } 143 pkg = new Package(context); 144 mPackages.put(packageName, pkg); 145 } 146 147 if (map == null) { 148 map = new ArrayMap<>(); 149 pkg.mMap.put(resId, map); 150 } 151 152 try { 153 ent = new Entry(pkg.context, 154 pkg.context.obtainStyledAttributes(resId, styleable)); 155 map.put(styleable, ent); 156 } catch (Resources.NotFoundException e) { 157 return null; 158 } 159 160 return ent; 161 } 162 } 163 } 164 165