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
18package com.android.server;
19
20import android.content.Context;
21import android.content.pm.ActivityInfo;
22import android.content.pm.PackageManager;
23import android.content.res.Configuration;
24import android.content.res.Resources;
25import android.content.res.TypedArray;
26import android.os.UserHandle;
27import android.util.ArrayMap;
28import android.util.LruCache;
29import android.util.SparseArray;
30
31import 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 */
37public 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