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