ResourcesManager.java revision 08c7116ab9cd04ad6dd3c04aa1017237e7f409ac
1/*
2 * Copyright (C) 2013 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
17package android.app;
18
19import static android.app.ActivityThread.DEBUG_CONFIGURATION;
20
21import android.content.pm.ActivityInfo;
22import android.content.res.AssetManager;
23import android.content.res.CompatibilityInfo;
24import android.content.res.Configuration;
25import android.content.res.Resources;
26import android.content.res.ResourcesKey;
27import android.hardware.display.DisplayManagerGlobal;
28import android.util.ArrayMap;
29import android.util.DisplayMetrics;
30import android.util.Pair;
31import android.util.Slog;
32import android.view.Display;
33import java.lang.ref.WeakReference;
34import java.util.Locale;
35
36/** @hide */
37public class ResourcesManager {
38    static final String TAG = "ResourcesManager";
39    private static final boolean DEBUG = false;
40
41    private static ResourcesManager sResourcesManager;
42    private final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources =
43            new ArrayMap<>();
44    private final ArrayMap<Pair<Integer, Configuration>, WeakReference<Display>> mDisplays =
45            new ArrayMap<>();
46
47    CompatibilityInfo mResCompatibilityInfo;
48
49    Configuration mResConfiguration;
50
51    public static ResourcesManager getInstance() {
52        synchronized (ResourcesManager.class) {
53            if (sResourcesManager == null) {
54                sResourcesManager = new ResourcesManager();
55            }
56            return sResourcesManager;
57        }
58    }
59
60    public Configuration getConfiguration() {
61        return mResConfiguration;
62    }
63
64    DisplayMetrics getDisplayMetricsLocked() {
65        return getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
66    }
67
68    DisplayMetrics getDisplayMetricsLocked(int displayId) {
69        DisplayMetrics dm = new DisplayMetrics();
70        final Display display = getAdjustedDisplay(displayId, Configuration.EMPTY);
71        if (display != null) {
72            display.getMetrics(dm);
73        } else {
74            dm.setToDefaults();
75        }
76        return dm;
77    }
78
79    final void applyNonDefaultDisplayMetricsToConfigurationLocked(
80            DisplayMetrics dm, Configuration config) {
81        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
82        config.densityDpi = dm.densityDpi;
83        config.screenWidthDp = (int)(dm.widthPixels / dm.density);
84        config.screenHeightDp = (int)(dm.heightPixels / dm.density);
85        int sl = Configuration.resetScreenLayout(config.screenLayout);
86        if (dm.widthPixels > dm.heightPixels) {
87            config.orientation = Configuration.ORIENTATION_LANDSCAPE;
88            config.screenLayout = Configuration.reduceScreenLayout(sl,
89                    config.screenWidthDp, config.screenHeightDp);
90        } else {
91            config.orientation = Configuration.ORIENTATION_PORTRAIT;
92            config.screenLayout = Configuration.reduceScreenLayout(sl,
93                    config.screenHeightDp, config.screenWidthDp);
94        }
95        config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
96        config.compatScreenWidthDp = config.screenWidthDp;
97        config.compatScreenHeightDp = config.screenHeightDp;
98        config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
99    }
100
101    public boolean applyCompatConfiguration(int displayDensity,
102            Configuration compatConfiguration) {
103        if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
104            mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
105            return true;
106        }
107        return false;
108    }
109
110    /**
111     * Returns an adjusted {@link Display} object based on the inputs or null if display isn't
112     * available.
113     *
114     * @param displayId display Id.
115     * @param overrideConfiguration override configurations.
116     */
117    public Display getAdjustedDisplay(final int displayId, Configuration overrideConfiguration) {
118        final Configuration configCopy = (overrideConfiguration != null)
119                ? new Configuration(overrideConfiguration) : new Configuration();
120        final Pair<Integer, Configuration> key = Pair.create(displayId, configCopy);
121        synchronized (this) {
122            WeakReference<Display> wd = mDisplays.get(key);
123            if (wd != null) {
124                final Display display = wd.get();
125                if (display != null) {
126                    return display;
127                }
128            }
129            final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
130            if (dm == null) {
131                // may be null early in system startup
132                return null;
133            }
134            final Display display = dm.getRealDisplay(displayId, key.second);
135            if (display != null) {
136                mDisplays.put(key, new WeakReference<>(display));
137            }
138            return display;
139        }
140    }
141
142    /**
143     * Creates the top level Resources for applications with the given compatibility info.
144     *
145     * @param resDir the resource directory.
146     * @param splitResDirs split resource directories.
147     * @param overlayDirs the resource overlay directories.
148     * @param libDirs the shared library resource dirs this app references.
149     * @param displayId display Id.
150     * @param overrideConfiguration override configurations.
151     * @param compatInfo the compatibility info. Must not be null.
152     */
153    Resources getTopLevelResources(String resDir, String[] splitResDirs,
154            String[] overlayDirs, String[] libDirs, int displayId,
155            Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
156        final float scale = compatInfo.applicationScale;
157        Configuration overrideConfigCopy = (overrideConfiguration != null)
158                ? new Configuration(overrideConfiguration) : null;
159        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
160        Resources r;
161        synchronized (this) {
162            // Resources is app scale dependent.
163            if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
164
165            WeakReference<Resources> wr = mActiveResources.get(key);
166            r = wr != null ? wr.get() : null;
167            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
168            if (r != null && r.getAssets().isUpToDate()) {
169                if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
170                        + ": appScale=" + r.getCompatibilityInfo().applicationScale
171                        + " key=" + key + " overrideConfig=" + overrideConfiguration);
172                return r;
173            }
174        }
175
176        //if (r != null) {
177        //    Slog.w(TAG, "Throwing away out-of-date resources!!!! "
178        //            + r + " " + resDir);
179        //}
180
181        AssetManager assets = new AssetManager();
182        // resDir can be null if the 'android' package is creating a new Resources object.
183        // This is fine, since each AssetManager automatically loads the 'android' package
184        // already.
185        if (resDir != null) {
186            if (assets.addAssetPath(resDir) == 0) {
187                return null;
188            }
189        }
190
191        if (splitResDirs != null) {
192            for (String splitResDir : splitResDirs) {
193                if (assets.addAssetPath(splitResDir) == 0) {
194                    return null;
195                }
196            }
197        }
198
199        if (overlayDirs != null) {
200            for (String idmapPath : overlayDirs) {
201                assets.addOverlayPath(idmapPath);
202            }
203        }
204
205        if (libDirs != null) {
206            for (String libDir : libDirs) {
207                if (assets.addAssetPath(libDir) == 0) {
208                    Slog.w(TAG, "Asset path '" + libDir +
209                            "' does not exist or contains no resources.");
210                }
211            }
212        }
213
214        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
215        DisplayMetrics dm = getDisplayMetricsLocked(displayId);
216        Configuration config;
217        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
218        final boolean hasOverrideConfig = key.hasOverrideConfiguration();
219        if (!isDefaultDisplay || hasOverrideConfig) {
220            config = new Configuration(getConfiguration());
221            if (!isDefaultDisplay) {
222                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
223            }
224            if (hasOverrideConfig) {
225                config.updateFrom(key.mOverrideConfiguration);
226                if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
227            }
228        } else {
229            config = getConfiguration();
230        }
231        r = new Resources(assets, dm, config, compatInfo);
232        if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
233                + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
234
235        synchronized (this) {
236            WeakReference<Resources> wr = mActiveResources.get(key);
237            Resources existing = wr != null ? wr.get() : null;
238            if (existing != null && existing.getAssets().isUpToDate()) {
239                // Someone else already created the resources while we were
240                // unlocked; go ahead and use theirs.
241                r.getAssets().close();
242                return existing;
243            }
244
245            // XXX need to remove entries when weak references go away
246            mActiveResources.put(key, new WeakReference<>(r));
247            if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
248            return r;
249        }
250    }
251
252    final boolean applyConfigurationToResourcesLocked(Configuration config,
253            CompatibilityInfo compat) {
254        if (mResConfiguration == null) {
255            mResConfiguration = new Configuration();
256        }
257        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
258            if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
259                    + mResConfiguration.seq + ", newSeq=" + config.seq);
260            return false;
261        }
262        int changes = mResConfiguration.updateFrom(config);
263        // Things might have changed in display manager, so clear the cached displays.
264        mDisplays.clear();
265        DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked();
266
267        if (compat != null && (mResCompatibilityInfo == null ||
268                !mResCompatibilityInfo.equals(compat))) {
269            mResCompatibilityInfo = compat;
270            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
271                    | ActivityInfo.CONFIG_SCREEN_SIZE
272                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
273        }
274
275        // set it for java, this also affects newly created Resources
276        if (config.locale != null) {
277            Locale.setDefault(config.locale);
278        }
279
280        Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
281
282        ApplicationPackageManager.configurationChanged();
283        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
284
285        Configuration tmpConfig = null;
286
287        for (int i = mActiveResources.size() - 1; i >= 0; i--) {
288            ResourcesKey key = mActiveResources.keyAt(i);
289            Resources r = mActiveResources.valueAt(i).get();
290            if (r != null) {
291                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
292                        + r + " config to: " + config);
293                int displayId = key.mDisplayId;
294                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
295                DisplayMetrics dm = defaultDisplayMetrics;
296                final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
297                if (!isDefaultDisplay || hasOverrideConfiguration) {
298                    if (tmpConfig == null) {
299                        tmpConfig = new Configuration();
300                    }
301                    tmpConfig.setTo(config);
302                    if (!isDefaultDisplay) {
303                        dm = getDisplayMetricsLocked(displayId);
304                        applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
305                    }
306                    if (hasOverrideConfiguration) {
307                        tmpConfig.updateFrom(key.mOverrideConfiguration);
308                    }
309                    r.updateConfiguration(tmpConfig, dm, compat);
310                } else {
311                    r.updateConfiguration(config, dm, compat);
312                }
313                //Slog.i(TAG, "Updated app resources " + v.getKey()
314                //        + " " + r + ": " + r.getConfiguration());
315            } else {
316                //Slog.i(TAG, "Removing old resources " + v.getKey());
317                mActiveResources.removeAt(i);
318            }
319        }
320
321        return changes != 0;
322    }
323
324}
325