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