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