blob: 5cc064e5d1772b3a34c91b20ca80c8116f7a8f19 [file] [log] [blame]
Craig Mautner88c05892013-06-28 09:47:45 -07001/*
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
Adam Lesinski082614c2016-03-04 14:33:47 -080021import android.annotation.NonNull;
22import android.annotation.Nullable;
Craig Mautner88c05892013-06-28 09:47:45 -070023import android.content.pm.ActivityInfo;
24import android.content.res.AssetManager;
25import android.content.res.CompatibilityInfo;
26import android.content.res.Configuration;
27import android.content.res.Resources;
Adam Lesinskifb302cc2016-02-29 16:50:38 -080028import android.content.res.ResourcesImpl;
Craig Mautner88c05892013-06-28 09:47:45 -070029import android.content.res.ResourcesKey;
30import android.hardware.display.DisplayManagerGlobal;
Adam Lesinski082614c2016-03-04 14:33:47 -080031import android.os.IBinder;
Adam Lesinskib7e1ce02016-04-11 20:03:01 -070032import android.os.Trace;
Craig Mautner88c05892013-06-28 09:47:45 -070033import android.util.ArrayMap;
34import android.util.DisplayMetrics;
Adam Lesinski1dd50c52015-03-16 15:10:56 -070035import android.util.Log;
Wale Ogunwale7c726682015-02-06 17:34:28 -080036import android.util.Pair;
Craig Mautner88c05892013-06-28 09:47:45 -070037import android.util.Slog;
38import android.view.Display;
Wale Ogunwale26698512015-06-05 16:55:33 -070039import android.view.DisplayAdjustments;
Jeff Sharkey98bf12f2016-05-26 11:56:57 -060040
Adam Lesinski082614c2016-03-04 14:33:47 -080041import com.android.internal.annotations.VisibleForTesting;
42import com.android.internal.util.ArrayUtils;
Wale Ogunwale26698512015-06-05 16:55:33 -070043
Craig Mautner88c05892013-06-28 09:47:45 -070044import java.lang.ref.WeakReference;
Adam Lesinski082614c2016-03-04 14:33:47 -080045import java.util.ArrayList;
Adam Lesinski082614c2016-03-04 14:33:47 -080046import java.util.Objects;
47import java.util.WeakHashMap;
48import java.util.function.Predicate;
Craig Mautner88c05892013-06-28 09:47:45 -070049
50/** @hide */
51public class ResourcesManager {
52 static final String TAG = "ResourcesManager";
Wale Ogunwale60454db2015-01-23 16:05:07 -080053 private static final boolean DEBUG = false;
Craig Mautner88c05892013-06-28 09:47:45 -070054
55 private static ResourcesManager sResourcesManager;
Adam Lesinski082614c2016-03-04 14:33:47 -080056
57 /**
58 * Predicate that returns true if a WeakReference is gc'ed.
59 */
60 private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
61 new Predicate<WeakReference<Resources>>() {
62 @Override
63 public boolean test(WeakReference<Resources> weakRef) {
64 return weakRef == null || weakRef.get() == null;
65 }
66 };
Craig Mautner88c05892013-06-28 09:47:45 -070067
Adam Lesinski082614c2016-03-04 14:33:47 -080068 /**
69 * The global compatibility settings.
70 */
71 private CompatibilityInfo mResCompatibilityInfo;
Craig Mautner88c05892013-06-28 09:47:45 -070072
Adam Lesinski082614c2016-03-04 14:33:47 -080073 /**
74 * The global configuration upon which all Resources are based. Multi-window Resources
75 * apply their overrides to this configuration.
76 */
77 private final Configuration mResConfiguration = new Configuration();
78
79 /**
80 * A mapping of ResourceImpls and their configurations. These are heavy weight objects
81 * which should be reused as much as possible.
82 */
83 private final ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> mResourceImpls =
84 new ArrayMap<>();
85
86 /**
87 * A list of Resource references that can be reused.
88 */
89 private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
90
91 /**
Adam Lesinski7f3f4992016-03-30 10:32:15 -070092 * Resources and base configuration override associated with an Activity.
Adam Lesinski082614c2016-03-04 14:33:47 -080093 */
Adam Lesinski7f3f4992016-03-30 10:32:15 -070094 private static class ActivityResources {
95 public final Configuration overrideConfig = new Configuration();
96 public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
97 }
98
99 /**
100 * Each Activity may has a base override configuration that is applied to each Resources object,
101 * which in turn may have their own override configuration specified.
102 */
103 private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
Adam Lesinski082614c2016-03-04 14:33:47 -0800104 new WeakHashMap<>();
105
106 /**
107 * A cache of DisplayId to DisplayAdjustments.
108 */
109 private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> mDisplays =
110 new ArrayMap<>();
Craig Mautner88c05892013-06-28 09:47:45 -0700111
112 public static ResourcesManager getInstance() {
113 synchronized (ResourcesManager.class) {
114 if (sResourcesManager == null) {
115 sResourcesManager = new ResourcesManager();
116 }
117 return sResourcesManager;
118 }
119 }
120
Jeff Sharkey98bf12f2016-05-26 11:56:57 -0600121 /**
122 * Invalidate and destroy any resources that reference content under the
123 * given filesystem path. Typically used when unmounting a storage device to
124 * try as hard as possible to release any open FDs.
125 */
126 public void invalidatePath(String path) {
127 synchronized (this) {
128 int count = 0;
129 for (int i = 0; i < mResourceImpls.size();) {
130 final ResourcesKey key = mResourceImpls.keyAt(i);
131 if (key.isPathReferenced(path)) {
132 final ResourcesImpl res = mResourceImpls.removeAt(i).get();
133 if (res != null) {
134 res.flushLayoutCache();
135 }
136 count++;
137 } else {
138 i++;
139 }
140 }
141 Log.i(TAG, "Invalidated " + count + " asset managers that referenced " + path);
142 }
143 }
144
Craig Mautner88c05892013-06-28 09:47:45 -0700145 public Configuration getConfiguration() {
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700146 synchronized (this) {
147 return mResConfiguration;
148 }
Craig Mautner88c05892013-06-28 09:47:45 -0700149 }
150
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700151 DisplayMetrics getDisplayMetrics() {
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700152 return getDisplayMetrics(Display.DEFAULT_DISPLAY,
153 DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
Craig Mautner88c05892013-06-28 09:47:45 -0700154 }
155
Adam Lesinski082614c2016-03-04 14:33:47 -0800156 /**
157 * Protected so that tests can override and returns something a fixed value.
158 */
159 @VisibleForTesting
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700160 protected @NonNull DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments da) {
Wale Ogunwale7c726682015-02-06 17:34:28 -0800161 DisplayMetrics dm = new DisplayMetrics();
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700162 final Display display = getAdjustedDisplay(displayId, da);
Wale Ogunwale7c726682015-02-06 17:34:28 -0800163 if (display != null) {
164 display.getMetrics(dm);
Craig Mautner88c05892013-06-28 09:47:45 -0700165 } else {
Craig Mautner88c05892013-06-28 09:47:45 -0700166 dm.setToDefaults();
167 }
Craig Mautner88c05892013-06-28 09:47:45 -0700168 return dm;
169 }
170
Adam Lesinski082614c2016-03-04 14:33:47 -0800171 private static void applyNonDefaultDisplayMetricsToConfiguration(
172 @NonNull DisplayMetrics dm, @NonNull Configuration config) {
Craig Mautner88c05892013-06-28 09:47:45 -0700173 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
174 config.densityDpi = dm.densityDpi;
Adam Lesinski082614c2016-03-04 14:33:47 -0800175 config.screenWidthDp = (int) (dm.widthPixels / dm.density);
176 config.screenHeightDp = (int) (dm.heightPixels / dm.density);
Craig Mautner88c05892013-06-28 09:47:45 -0700177 int sl = Configuration.resetScreenLayout(config.screenLayout);
178 if (dm.widthPixels > dm.heightPixels) {
179 config.orientation = Configuration.ORIENTATION_LANDSCAPE;
180 config.screenLayout = Configuration.reduceScreenLayout(sl,
181 config.screenWidthDp, config.screenHeightDp);
182 } else {
183 config.orientation = Configuration.ORIENTATION_PORTRAIT;
184 config.screenLayout = Configuration.reduceScreenLayout(sl,
185 config.screenHeightDp, config.screenWidthDp);
186 }
187 config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
188 config.compatScreenWidthDp = config.screenWidthDp;
189 config.compatScreenHeightDp = config.screenHeightDp;
190 config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
191 }
192
Adam Lesinski082614c2016-03-04 14:33:47 -0800193 public boolean applyCompatConfigurationLocked(int displayDensity,
194 @NonNull Configuration compatConfiguration) {
Craig Mautner88c05892013-06-28 09:47:45 -0700195 if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
196 mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
197 return true;
198 }
199 return false;
200 }
201
202 /**
Wale Ogunwale7c726682015-02-06 17:34:28 -0800203 * Returns an adjusted {@link Display} object based on the inputs or null if display isn't
204 * available.
205 *
206 * @param displayId display Id.
Wale Ogunwale26698512015-06-05 16:55:33 -0700207 * @param displayAdjustments display adjustments.
Wale Ogunwale7c726682015-02-06 17:34:28 -0800208 */
Adam Lesinski082614c2016-03-04 14:33:47 -0800209 public Display getAdjustedDisplay(final int displayId,
210 @Nullable DisplayAdjustments displayAdjustments) {
Wale Ogunwale26698512015-06-05 16:55:33 -0700211 final DisplayAdjustments displayAdjustmentsCopy = (displayAdjustments != null)
212 ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
213 final Pair<Integer, DisplayAdjustments> key =
214 Pair.create(displayId, displayAdjustmentsCopy);
Wale Ogunwale7c726682015-02-06 17:34:28 -0800215 synchronized (this) {
216 WeakReference<Display> wd = mDisplays.get(key);
217 if (wd != null) {
218 final Display display = wd.get();
219 if (display != null) {
220 return display;
221 }
222 }
223 final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
224 if (dm == null) {
225 // may be null early in system startup
226 return null;
227 }
Wale Ogunwale26698512015-06-05 16:55:33 -0700228 final Display display = dm.getCompatibleDisplay(displayId, key.second);
Wale Ogunwale7c726682015-02-06 17:34:28 -0800229 if (display != null) {
230 mDisplays.put(key, new WeakReference<>(display));
231 }
232 return display;
233 }
234 }
235
236 /**
Adam Lesinski082614c2016-03-04 14:33:47 -0800237 * Creates an AssetManager from the paths within the ResourcesKey.
Craig Mautner88c05892013-06-28 09:47:45 -0700238 *
Adam Lesinski082614c2016-03-04 14:33:47 -0800239 * This can be overridden in tests so as to avoid creating a real AssetManager with
240 * real APK paths.
241 * @param key The key containing the resource paths to add to the AssetManager.
242 * @return a new AssetManager.
243 */
244 @VisibleForTesting
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700245 protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
Craig Mautner88c05892013-06-28 09:47:45 -0700246 AssetManager assets = new AssetManager();
Adam Lesinski082614c2016-03-04 14:33:47 -0800247
Adam Lesinski54130de2014-08-20 10:49:13 -0700248 // resDir can be null if the 'android' package is creating a new Resources object.
249 // This is fine, since each AssetManager automatically loads the 'android' package
250 // already.
Adam Lesinski082614c2016-03-04 14:33:47 -0800251 if (key.mResDir != null) {
252 if (assets.addAssetPath(key.mResDir) == 0) {
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700253 Log.e(TAG, "failed to add asset path " + key.mResDir);
254 return null;
Adam Lesinski54130de2014-08-20 10:49:13 -0700255 }
Craig Mautner88c05892013-06-28 09:47:45 -0700256 }
257
Adam Lesinski082614c2016-03-04 14:33:47 -0800258 if (key.mSplitResDirs != null) {
259 for (final String splitResDir : key.mSplitResDirs) {
Jeff Sharkey8a4c9722014-06-16 13:48:42 -0700260 if (assets.addAssetPath(splitResDir) == 0) {
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700261 Log.e(TAG, "failed to add split asset path " + splitResDir);
262 return null;
Jeff Sharkey8a4c9722014-06-16 13:48:42 -0700263 }
264 }
265 }
266
Adam Lesinski082614c2016-03-04 14:33:47 -0800267 if (key.mOverlayDirs != null) {
268 for (final String idmapPath : key.mOverlayDirs) {
MÃ¥rten Kongstad48d22322014-01-31 14:43:27 +0100269 assets.addOverlayPath(idmapPath);
270 }
271 }
272
Adam Lesinski082614c2016-03-04 14:33:47 -0800273 if (key.mLibDirs != null) {
274 for (final String libDir : key.mLibDirs) {
Adam Lesinski1dd50c52015-03-16 15:10:56 -0700275 if (libDir.endsWith(".apk")) {
276 // Avoid opening files we know do not have resources,
277 // like code-only .jar files.
Adam Lesinski25f48882016-06-14 11:05:23 -0700278 if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
Adam Lesinski1dd50c52015-03-16 15:10:56 -0700279 Log.w(TAG, "Asset path '" + libDir +
280 "' does not exist or contains no resources.");
281 }
Adam Lesinskide898ff2014-01-29 18:20:45 -0800282 }
283 }
284 }
Adam Lesinski082614c2016-03-04 14:33:47 -0800285 return assets;
286 }
Adam Lesinskide898ff2014-01-29 18:20:45 -0800287
Adam Lesinski082614c2016-03-04 14:33:47 -0800288 private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
Craig Mautner88c05892013-06-28 09:47:45 -0700289 Configuration config;
Adam Lesinski082614c2016-03-04 14:33:47 -0800290 final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
Craig Mautner88c05892013-06-28 09:47:45 -0700291 final boolean hasOverrideConfig = key.hasOverrideConfiguration();
292 if (!isDefaultDisplay || hasOverrideConfig) {
293 config = new Configuration(getConfiguration());
294 if (!isDefaultDisplay) {
Adam Lesinski082614c2016-03-04 14:33:47 -0800295 applyNonDefaultDisplayMetricsToConfiguration(dm, config);
Craig Mautner88c05892013-06-28 09:47:45 -0700296 }
297 if (hasOverrideConfig) {
298 config.updateFrom(key.mOverrideConfiguration);
Wale Ogunwale60454db2015-01-23 16:05:07 -0800299 if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
Craig Mautner88c05892013-06-28 09:47:45 -0700300 }
301 } else {
302 config = getConfiguration();
303 }
Adam Lesinski082614c2016-03-04 14:33:47 -0800304 return config;
305 }
Craig Mautner88c05892013-06-28 09:47:45 -0700306
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700307 private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700308 final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
309 daj.setCompatibilityInfo(key.mCompatInfo);
310
Adam Lesinski4ece3d62016-06-16 18:05:41 -0700311 final AssetManager assets = createAssetManager(key);
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700312 if (assets == null) {
313 return null;
314 }
315
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700316 final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
Adam Lesinski4ece3d62016-06-16 18:05:41 -0700317 final Configuration config = generateConfig(key, dm);
Adam Lesinski8e8d2322016-06-24 12:29:16 -0700318 final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
Adam Lesinski082614c2016-03-04 14:33:47 -0800319 if (DEBUG) {
320 Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
321 }
322 return impl;
323 }
324
325 /**
326 * Finds a cached ResourcesImpl object that matches the given ResourcesKey.
327 *
328 * @param key The key to match.
329 * @return a ResourcesImpl if the key matches a cache entry, null otherwise.
330 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700331 private @Nullable ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
Adam Lesinski082614c2016-03-04 14:33:47 -0800332 WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
333 ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
334 if (impl != null && impl.getAssets().isUpToDate()) {
335 return impl;
336 }
337 return null;
338 }
339
340 /**
Adam Lesinski25f48882016-06-14 11:05:23 -0700341 * Finds a cached ResourcesImpl object that matches the given ResourcesKey, or
342 * creates a new one and caches it for future use.
343 * @param key The key to match.
344 * @return a ResourcesImpl object matching the key.
345 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700346 private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
Adam Lesinski25f48882016-06-14 11:05:23 -0700347 @NonNull ResourcesKey key) {
348 ResourcesImpl impl = findResourcesImplForKeyLocked(key);
349 if (impl == null) {
350 impl = createResourcesImpl(key);
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700351 if (impl != null) {
352 mResourceImpls.put(key, new WeakReference<>(impl));
353 }
Adam Lesinski25f48882016-06-14 11:05:23 -0700354 }
355 return impl;
356 }
357
358 /**
Adam Lesinski082614c2016-03-04 14:33:47 -0800359 * Find the ResourcesKey that this ResourcesImpl object is associated with.
360 * @return the ResourcesKey or null if none was found.
361 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700362 private @Nullable ResourcesKey findKeyForResourceImplLocked(
363 @NonNull ResourcesImpl resourceImpl) {
Adam Lesinski082614c2016-03-04 14:33:47 -0800364 final int refCount = mResourceImpls.size();
365 for (int i = 0; i < refCount; i++) {
366 WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
367 ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
368 if (impl != null && resourceImpl == impl) {
369 return mResourceImpls.keyAt(i);
370 }
371 }
372 return null;
373 }
374
Andrii Kulian3b3c9142016-07-18 19:15:56 -0700375 /**
376 * Check if activity resources have same override config as the provided on.
377 * @param activityToken The Activity that resources should be associated with.
378 * @param overrideConfig The override configuration to be checked for equality with.
379 * @return true if activity resources override config matches the provided one or they are both
380 * null, false otherwise.
381 */
382 boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
383 @Nullable Configuration overrideConfig) {
384 synchronized (this) {
385 final ActivityResources activityResources
386 = activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
387 if (activityResources == null) {
388 return overrideConfig == null;
389 } else {
390 return Objects.equals(activityResources.overrideConfig, overrideConfig);
391 }
392 }
393 }
394
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700395 private ActivityResources getOrCreateActivityResourcesStructLocked(
396 @NonNull IBinder activityToken) {
397 ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
398 if (activityResources == null) {
399 activityResources = new ActivityResources();
400 mActivityResourceReferences.put(activityToken, activityResources);
401 }
402 return activityResources;
403 }
404
Adam Lesinski082614c2016-03-04 14:33:47 -0800405 /**
406 * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
407 * or the class loader is different.
408 */
Adam Lesinskic82f28a2016-06-08 17:19:09 -0700409 private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
Adam Lesinski082614c2016-03-04 14:33:47 -0800410 @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700411 final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
412 activityToken);
413
414 final int refCount = activityResources.activityResources.size();
415 for (int i = 0; i < refCount; i++) {
416 WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
417 Resources resources = weakResourceRef.get();
418
419 if (resources != null
420 && Objects.equals(resources.getClassLoader(), classLoader)
421 && resources.getImpl() == impl) {
422 if (DEBUG) {
423 Slog.d(TAG, "- using existing ref=" + resources);
424 }
425 return resources;
Adam Lesinski082614c2016-03-04 14:33:47 -0800426 }
427 }
428
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700429 Resources resources = new Resources(classLoader);
430 resources.setImpl(impl);
431 activityResources.activityResources.add(new WeakReference<>(resources));
432 if (DEBUG) {
433 Slog.d(TAG, "- creating new ref=" + resources);
434 Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
Adam Lesinski082614c2016-03-04 14:33:47 -0800435 }
436 return resources;
437 }
438
439 /**
440 * Gets an existing Resources object if the class loader and ResourcesImpl are the same,
441 * otherwise creates a new Resources object.
442 */
Adam Lesinskic82f28a2016-06-08 17:19:09 -0700443 private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
Adam Lesinski082614c2016-03-04 14:33:47 -0800444 @NonNull ResourcesImpl impl) {
445 // Find an existing Resources that has this ResourcesImpl set.
446 final int refCount = mResourceReferences.size();
447 for (int i = 0; i < refCount; i++) {
448 WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700449 Resources resources = weakResourceRef.get();
Adam Lesinski082614c2016-03-04 14:33:47 -0800450 if (resources != null &&
451 Objects.equals(resources.getClassLoader(), classLoader) &&
452 resources.getImpl() == impl) {
453 if (DEBUG) {
454 Slog.d(TAG, "- using existing ref=" + resources);
455 }
456 return resources;
457 }
458 }
459
460 // Create a new Resources reference and use the existing ResourcesImpl object.
461 Resources resources = new Resources(classLoader);
462 resources.setImpl(impl);
463 mResourceReferences.add(new WeakReference<>(resources));
464 if (DEBUG) {
465 Slog.d(TAG, "- creating new ref=" + resources);
466 Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
467 }
468 return resources;
469 }
470
471 /**
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700472 * Creates base resources for an Activity. Calls to
473 * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
474 * CompatibilityInfo, ClassLoader)} with the same activityToken will have their override
475 * configurations merged with the one specified here.
476 *
477 * @param activityToken Represents an Activity.
478 * @param resDir The base resource path. Can be null (only framework resources will be loaded).
479 * @param splitResDirs An array of split resource paths. Can be null.
480 * @param overlayDirs An array of overlay paths. Can be null.
481 * @param libDirs An array of resource library paths. Can be null.
482 * @param displayId The ID of the display for which to create the resources.
483 * @param overrideConfig The configuration to apply on top of the base configuration. Can be
484 * null. This provides the base override for this Activity.
485 * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
486 * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
487 * @param classLoader The class loader to use when inflating Resources. If null, the
488 * {@link ClassLoader#getSystemClassLoader()} is used.
489 * @return a Resources object from which to access resources.
490 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700491 public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700492 @Nullable String resDir,
493 @Nullable String[] splitResDirs,
494 @Nullable String[] overlayDirs,
495 @Nullable String[] libDirs,
496 int displayId,
497 @Nullable Configuration overrideConfig,
498 @NonNull CompatibilityInfo compatInfo,
499 @Nullable ClassLoader classLoader) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700500 try {
501 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
502 "ResourcesManager#createBaseActivityResources");
503 final ResourcesKey key = new ResourcesKey(
504 resDir,
505 splitResDirs,
506 overlayDirs,
507 libDirs,
508 displayId,
509 overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
510 compatInfo);
511 classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700512
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700513 if (DEBUG) {
514 Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
515 + " with key=" + key);
Tim Murray98e80072016-04-14 15:44:35 -0700516 }
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700517
518 synchronized (this) {
Adam Lesinski8ce4e122016-05-02 16:09:16 -0700519 // Force the creation of an ActivityResourcesStruct.
520 getOrCreateActivityResourcesStructLocked(activityToken);
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700521 }
522
523 // Update any existing Activity Resources references.
524 updateResourcesForActivity(activityToken, overrideConfig);
525
526 // Now request an actual Resources object.
527 return getOrCreateResources(activityToken, key, classLoader);
528 } finally {
529 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
Tim Murray98e80072016-04-14 15:44:35 -0700530 }
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700531 }
532
533 /**
534 * Gets an existing Resources object set with a ResourcesImpl object matching the given key,
535 * or creates one if it doesn't exist.
536 *
537 * @param activityToken The Activity this Resources object should be associated with.
538 * @param key The key describing the parameters of the ResourcesImpl object.
539 * @param classLoader The classloader to use for the Resources object.
540 * If null, {@link ClassLoader#getSystemClassLoader()} is used.
541 * @return A Resources object that gets updated when
542 * {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
543 * is called.
544 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700545 private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700546 @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700547 synchronized (this) {
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700548 if (DEBUG) {
549 Throwable here = new Throwable();
550 here.fillInStackTrace();
551 Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
552 }
553
554 if (activityToken != null) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700555 final ActivityResources activityResources =
556 getOrCreateActivityResourcesStructLocked(activityToken);
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700557
558 // Clean up any dead references so they don't pile up.
559 ArrayUtils.unstableRemoveIf(activityResources.activityResources,
560 sEmptyReferencePredicate);
561
562 // Rebase the key's override config on top of the Activity's base override.
563 if (key.hasOverrideConfiguration()
564 && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
565 final Configuration temp = new Configuration(activityResources.overrideConfig);
566 temp.updateFrom(key.mOverrideConfiguration);
567 key.mOverrideConfiguration.setTo(temp);
568 }
569
570 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
571 if (resourcesImpl != null) {
572 if (DEBUG) {
573 Slog.d(TAG, "- using existing impl=" + resourcesImpl);
574 }
575 return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
576 resourcesImpl);
577 }
578
579 // We will create the ResourcesImpl object outside of holding this lock.
580
581 } else {
582 // Clean up any dead references so they don't pile up.
583 ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
584
585 // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
586 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
587 if (resourcesImpl != null) {
588 if (DEBUG) {
589 Slog.d(TAG, "- using existing impl=" + resourcesImpl);
590 }
591 return getOrCreateResourcesLocked(classLoader, resourcesImpl);
592 }
593
594 // We will create the ResourcesImpl object outside of holding this lock.
595 }
596 }
597
598 // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
599 ResourcesImpl resourcesImpl = createResourcesImpl(key);
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700600 if (resourcesImpl == null) {
601 return null;
602 }
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700603
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700604 synchronized (this) {
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700605 ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
606 if (existingResourcesImpl != null) {
607 if (DEBUG) {
608 Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
609 + " new impl=" + resourcesImpl);
610 }
611 resourcesImpl.getAssets().close();
612 resourcesImpl = existingResourcesImpl;
613 } else {
614 // Add this ResourcesImpl to the cache.
615 mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
616 }
617
618 final Resources resources;
619 if (activityToken != null) {
620 resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
621 resourcesImpl);
622 } else {
623 resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
624 }
625 return resources;
626 }
627 }
628
629 /**
Adam Lesinski082614c2016-03-04 14:33:47 -0800630 * Gets or creates a new Resources object associated with the IBinder token. References returned
631 * by this method live as long as the Activity, meaning they can be cached and used by the
632 * Activity even after a configuration change. If any other parameter is changed
633 * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object
634 * is updated and handed back to the caller. However, changing the class loader will result in a
635 * new Resources object.
636 * <p/>
637 * If activityToken is null, a cached Resources object will be returned if it matches the
638 * input parameters. Otherwise a new Resources object that satisfies these parameters is
639 * returned.
640 *
641 * @param activityToken Represents an Activity. If null, global resources are assumed.
642 * @param resDir The base resource path. Can be null (only framework resources will be loaded).
643 * @param splitResDirs An array of split resource paths. Can be null.
644 * @param overlayDirs An array of overlay paths. Can be null.
645 * @param libDirs An array of resource library paths. Can be null.
646 * @param displayId The ID of the display for which to create the resources.
647 * @param overrideConfig The configuration to apply on top of the base configuration. Can be
648 * null. Mostly used with Activities that are in multi-window which may override width and
649 * height properties from the base config.
650 * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
651 * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
652 * @param classLoader The class loader to use when inflating Resources. If null, the
653 * {@link ClassLoader#getSystemClassLoader()} is used.
654 * @return a Resources object from which to access resources.
655 */
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700656 public @Nullable Resources getResources(@Nullable IBinder activityToken,
Adam Lesinski082614c2016-03-04 14:33:47 -0800657 @Nullable String resDir,
658 @Nullable String[] splitResDirs,
659 @Nullable String[] overlayDirs,
660 @Nullable String[] libDirs,
661 int displayId,
662 @Nullable Configuration overrideConfig,
663 @NonNull CompatibilityInfo compatInfo,
664 @Nullable ClassLoader classLoader) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700665 try {
666 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
667 final ResourcesKey key = new ResourcesKey(
668 resDir,
669 splitResDirs,
670 overlayDirs,
671 libDirs,
672 displayId,
673 overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
674 compatInfo);
675 classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
676 return getOrCreateResources(activityToken, key, classLoader);
677 } finally {
678 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
679 }
Craig Mautner88c05892013-06-28 09:47:45 -0700680 }
681
Todd Kennedy39bfee52016-02-24 10:28:21 -0800682 /**
Adam Lesinski082614c2016-03-04 14:33:47 -0800683 * Updates an Activity's Resources object with overrideConfig. The Resources object
684 * that was previously returned by
685 * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
686 * CompatibilityInfo, ClassLoader)} is
687 * still valid and will have the updated configuration.
688 * @param activityToken The Activity token.
689 * @param overrideConfig The configuration override to update.
Todd Kennedy39bfee52016-02-24 10:28:21 -0800690 */
Adam Lesinski082614c2016-03-04 14:33:47 -0800691 public void updateResourcesForActivity(@NonNull IBinder activityToken,
692 @Nullable Configuration overrideConfig) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700693 try {
694 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
695 "ResourcesManager#updateResourcesForActivity");
696 synchronized (this) {
697 final ActivityResources activityResources =
698 getOrCreateActivityResourcesStructLocked(activityToken);
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700699
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700700 if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
701 // They are the same, no work to do.
702 return;
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700703 }
704
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700705 // Grab a copy of the old configuration so we can create the delta's of each
706 // Resources object associated with this Activity.
707 final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700708
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700709 // Update the Activity's base override.
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700710 if (overrideConfig != null) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700711 activityResources.overrideConfig.setTo(overrideConfig);
712 } else {
713 activityResources.overrideConfig.setToDefaults();
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700714 }
715
Adam Lesinski0cd9a7ea2016-04-01 11:48:10 -0700716 if (DEBUG) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700717 Throwable here = new Throwable();
718 here.fillInStackTrace();
719 Slog.d(TAG, "updating resources override for activity=" + activityToken
720 + " from oldConfig="
721 + Configuration.resourceQualifierString(oldConfig)
722 + " to newConfig="
723 + Configuration.resourceQualifierString(
724 activityResources.overrideConfig),
725 here);
Adam Lesinski0cd9a7ea2016-04-01 11:48:10 -0700726 }
727
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700728 final boolean activityHasOverrideConfig =
729 !activityResources.overrideConfig.equals(Configuration.EMPTY);
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700730
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700731 // Rebase each Resources associated with this Activity.
732 final int refCount = activityResources.activityResources.size();
733 for (int i = 0; i < refCount; i++) {
734 WeakReference<Resources> weakResRef = activityResources.activityResources.get(
735 i);
736 Resources resources = weakResRef.get();
737 if (resources == null) {
738 continue;
739 }
740
741 // Extract the ResourcesKey that was last used to create the Resources for this
742 // activity.
743 final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
744 if (oldKey == null) {
745 Slog.e(TAG, "can't find ResourcesKey for resources impl="
746 + resources.getImpl());
747 continue;
748 }
749
750 // Build the new override configuration for this ResourcesKey.
751 final Configuration rebasedOverrideConfig = new Configuration();
752 if (overrideConfig != null) {
753 rebasedOverrideConfig.setTo(overrideConfig);
754 }
755
756 if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
757 // Generate a delta between the old base Activity override configuration and
758 // the actual final override configuration that was used to figure out the
759 // real delta this Resources object wanted.
760 Configuration overrideOverrideConfig = Configuration.generateDelta(
761 oldConfig, oldKey.mOverrideConfiguration);
762 rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
763 }
764
765 // Create the new ResourcesKey with the rebased override config.
766 final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
767 oldKey.mSplitResDirs,
768 oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
769 rebasedOverrideConfig, oldKey.mCompatInfo);
770
771 if (DEBUG) {
772 Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
773 + " to newKey=" + newKey);
774 }
775
776 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
777 if (resourcesImpl == null) {
778 resourcesImpl = createResourcesImpl(newKey);
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700779 if (resourcesImpl != null) {
780 mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
781 }
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700782 }
783
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700784 if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700785 // Set the ResourcesImpl, updating it for all users of this Resources
786 // object.
787 resources.setImpl(resourcesImpl);
788 }
Adam Lesinski7f3f4992016-03-30 10:32:15 -0700789 }
Adam Lesinski082614c2016-03-04 14:33:47 -0800790 }
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700791 } finally {
792 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
Adam Lesinski082614c2016-03-04 14:33:47 -0800793 }
Todd Kennedy39bfee52016-02-24 10:28:21 -0800794 }
795
Adam Lesinski082614c2016-03-04 14:33:47 -0800796 public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
797 @Nullable CompatibilityInfo compat) {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700798 try {
799 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
800 "ResourcesManager#applyConfigurationToResourcesLocked");
Tim Murray98e80072016-04-14 15:44:35 -0700801
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700802 if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
803 if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
804 + mResConfiguration.seq + ", newSeq=" + config.seq);
805 return false;
806 }
807 int changes = mResConfiguration.updateFrom(config);
808 // Things might have changed in display manager, so clear the cached displays.
809 mDisplays.clear();
810 DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
Tim Murray98e80072016-04-14 15:44:35 -0700811
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700812 if (compat != null && (mResCompatibilityInfo == null ||
813 !mResCompatibilityInfo.equals(compat))) {
814 mResCompatibilityInfo = compat;
815 changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
816 | ActivityInfo.CONFIG_SCREEN_SIZE
817 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
818 }
819
Adam Lesinskib61e4052016-05-19 18:23:05 -0700820 Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
Tim Murray98e80072016-04-14 15:44:35 -0700821
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700822 ApplicationPackageManager.configurationChanged();
823 //Slog.i(TAG, "Configuration changed in " + currentPackageName());
Tim Murray98e80072016-04-14 15:44:35 -0700824
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700825 Configuration tmpConfig = null;
Tim Murray98e80072016-04-14 15:44:35 -0700826
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700827 for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
828 ResourcesKey key = mResourceImpls.keyAt(i);
829 ResourcesImpl r = mResourceImpls.valueAt(i).get();
830 if (r != null) {
831 if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
Adam Lesinskib61e4052016-05-19 18:23:05 -0700832 + r + " config to: " + config);
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700833 int displayId = key.mDisplayId;
834 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
835 DisplayMetrics dm = defaultDisplayMetrics;
836 final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
837 if (!isDefaultDisplay || hasOverrideConfiguration) {
838 if (tmpConfig == null) {
839 tmpConfig = new Configuration();
840 }
Adam Lesinskib61e4052016-05-19 18:23:05 -0700841 tmpConfig.setTo(config);
Adam Lesinski43097212016-10-14 11:10:29 -0700842
843 // Get new DisplayMetrics based on the DisplayAdjustments given
844 // to the ResourcesImpl. Update a copy if the CompatibilityInfo
845 // changed, because the ResourcesImpl object will handle the
846 // update internally.
847 DisplayAdjustments daj = r.getDisplayAdjustments();
848 if (compat != null) {
849 daj = new DisplayAdjustments(daj);
850 daj.setCompatibilityInfo(compat);
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700851 }
Adam Lesinski43097212016-10-14 11:10:29 -0700852 dm = getDisplayMetrics(displayId, daj);
Adam Lesinskif02523b2016-10-18 12:58:27 -0700853
854 if (!isDefaultDisplay) {
855 applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
856 }
Adam Lesinski43097212016-10-14 11:10:29 -0700857
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700858 if (hasOverrideConfiguration) {
859 tmpConfig.updateFrom(key.mOverrideConfiguration);
860 }
861 r.updateConfiguration(tmpConfig, dm, compat);
862 } else {
Adam Lesinskib61e4052016-05-19 18:23:05 -0700863 r.updateConfiguration(config, dm, compat);
Tim Murray98e80072016-04-14 15:44:35 -0700864 }
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700865 //Slog.i(TAG, "Updated app resources " + v.getKey()
866 // + " " + r + ": " + r.getConfiguration());
Tim Murray98e80072016-04-14 15:44:35 -0700867 } else {
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700868 //Slog.i(TAG, "Removing old resources " + v.getKey());
869 mResourceImpls.removeAt(i);
Tim Murray98e80072016-04-14 15:44:35 -0700870 }
Tim Murray98e80072016-04-14 15:44:35 -0700871 }
Tim Murray98e80072016-04-14 15:44:35 -0700872
Adam Lesinskib7e1ce02016-04-11 20:03:01 -0700873 return changes != 0;
874 } finally {
875 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
876 }
Craig Mautner88c05892013-06-28 09:47:45 -0700877 }
Adam Lesinski25f48882016-06-14 11:05:23 -0700878
879 /**
880 * Appends the library asset path to any ResourcesImpl object that contains the main
881 * assetPath.
882 * @param assetPath The main asset path for which to add the library asset path.
883 * @param libAsset The library asset path to add.
884 */
885 public void appendLibAssetForMainAssetPath(String assetPath, String libAsset) {
886 synchronized (this) {
887 // Record which ResourcesImpl need updating
888 // (and what ResourcesKey they should update to).
889 final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
890
891 final int implCount = mResourceImpls.size();
892 for (int i = 0; i < implCount; i++) {
893 final ResourcesImpl impl = mResourceImpls.valueAt(i).get();
894 final ResourcesKey key = mResourceImpls.keyAt(i);
895 if (impl != null && key.mResDir.equals(assetPath)) {
896 if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
897 final int newLibAssetCount = 1 +
898 (key.mLibDirs != null ? key.mLibDirs.length : 0);
899 final String[] newLibAssets = new String[newLibAssetCount];
900 if (key.mLibDirs != null) {
901 System.arraycopy(key.mLibDirs, 0, newLibAssets, 0, key.mLibDirs.length);
902 }
903 newLibAssets[newLibAssetCount - 1] = libAsset;
904
905 updatedResourceKeys.put(impl, new ResourcesKey(
906 key.mResDir,
907 key.mSplitResDirs,
908 key.mOverlayDirs,
909 newLibAssets,
910 key.mDisplayId,
911 key.mOverrideConfiguration,
912 key.mCompatInfo));
913 }
914 }
915 }
916
917 // Bail early if there is no work to do.
918 if (updatedResourceKeys.isEmpty()) {
919 return;
920 }
921
922 // Update any references to ResourcesImpl that require reloading.
923 final int resourcesCount = mResourceReferences.size();
924 for (int i = 0; i < resourcesCount; i++) {
925 final Resources r = mResourceReferences.get(i).get();
926 if (r != null) {
927 final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
928 if (key != null) {
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700929 final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
930 if (impl == null) {
931 throw new Resources.NotFoundException("failed to load " + libAsset);
932 }
933 r.setImpl(impl);
Adam Lesinski25f48882016-06-14 11:05:23 -0700934 }
935 }
936 }
937
938 // Update any references to ResourcesImpl that require reloading for each Activity.
939 for (ActivityResources activityResources : mActivityResourceReferences.values()) {
940 final int resCount = activityResources.activityResources.size();
941 for (int i = 0; i < resCount; i++) {
942 final Resources r = activityResources.activityResources.get(i).get();
943 if (r != null) {
944 final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
945 if (key != null) {
Adam Lesinski53fafdf2016-08-03 13:36:39 -0700946 final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
947 if (impl == null) {
948 throw new Resources.NotFoundException("failed to load " + libAsset);
949 }
950 r.setImpl(impl);
Adam Lesinski25f48882016-06-14 11:05:23 -0700951 }
952 }
953 }
954 }
955 }
956 }
Seigo Nonakac14dd782016-03-30 23:09:16 +0900957}