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