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 17 package android.app; 18 19 import static android.app.ActivityThread.DEBUG_CONFIGURATION; 20 21 import android.content.pm.ActivityInfo; 22 import android.content.res.AssetManager; 23 import android.content.res.CompatibilityInfo; 24 import android.content.res.Configuration; 25 import android.content.res.Resources; 26 import android.content.res.ResourcesKey; 27 import android.hardware.display.DisplayManagerGlobal; 28 import android.os.IBinder; 29 import android.util.ArrayMap; 30 import android.util.DisplayMetrics; 31 import android.util.Slog; 32 import android.view.Display; 33 import android.view.DisplayAdjustments; 34 35 import java.lang.ref.WeakReference; 36 import java.util.Locale; 37 38 /** @hide */ 39 public class ResourcesManager { 40 static final String TAG = "ResourcesManager"; 41 static final boolean DEBUG_CACHE = false; 42 static final boolean DEBUG_STATS = true; 43 44 private static ResourcesManager sResourcesManager; 45 final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources 46 = new ArrayMap<ResourcesKey, WeakReference<Resources> >(); 47 48 final ArrayMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics 49 = new ArrayMap<DisplayAdjustments, DisplayMetrics>(); 50 51 CompatibilityInfo mResCompatibilityInfo; 52 53 Configuration mResConfiguration; 54 final Configuration mTmpConfig = new Configuration(); 55 56 public static ResourcesManager getInstance() { 57 synchronized (ResourcesManager.class) { 58 if (sResourcesManager == null) { 59 sResourcesManager = new ResourcesManager(); 60 } 61 return sResourcesManager; 62 } 63 } 64 65 public Configuration getConfiguration() { 66 return mResConfiguration; 67 } 68 69 public void flushDisplayMetricsLocked() { 70 mDefaultDisplayMetrics.clear(); 71 } 72 73 public DisplayMetrics getDisplayMetricsLocked(int displayId) { 74 return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); 75 } 76 77 public DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) { 78 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); 79 DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null; 80 if (dm != null) { 81 return dm; 82 } 83 dm = new DisplayMetrics(); 84 85 DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); 86 if (displayManager == null) { 87 // may be null early in system startup 88 dm.setToDefaults(); 89 return dm; 90 } 91 92 if (isDefaultDisplay) { 93 mDefaultDisplayMetrics.put(daj, dm); 94 } 95 96 Display d = displayManager.getCompatibleDisplay(displayId, daj); 97 if (d != null) { 98 d.getMetrics(dm); 99 } else { 100 // Display no longer exists 101 // FIXME: This would not be a problem if we kept the Display object around 102 // instead of using the raw display id everywhere. The Display object caches 103 // its information even after the display has been removed. 104 dm.setToDefaults(); 105 } 106 //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" 107 // + metrics.heightPixels + " den=" + metrics.density 108 // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); 109 return dm; 110 } 111 112 final void applyNonDefaultDisplayMetricsToConfigurationLocked( 113 DisplayMetrics dm, Configuration config) { 114 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; 115 config.densityDpi = dm.densityDpi; 116 config.screenWidthDp = (int)(dm.widthPixels / dm.density); 117 config.screenHeightDp = (int)(dm.heightPixels / dm.density); 118 int sl = Configuration.resetScreenLayout(config.screenLayout); 119 if (dm.widthPixels > dm.heightPixels) { 120 config.orientation = Configuration.ORIENTATION_LANDSCAPE; 121 config.screenLayout = Configuration.reduceScreenLayout(sl, 122 config.screenWidthDp, config.screenHeightDp); 123 } else { 124 config.orientation = Configuration.ORIENTATION_PORTRAIT; 125 config.screenLayout = Configuration.reduceScreenLayout(sl, 126 config.screenHeightDp, config.screenWidthDp); 127 } 128 config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate 129 config.compatScreenWidthDp = config.screenWidthDp; 130 config.compatScreenHeightDp = config.screenHeightDp; 131 config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp; 132 } 133 134 public boolean applyCompatConfiguration(int displayDensity, 135 Configuration compatConfiguration) { 136 if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) { 137 mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration); 138 return true; 139 } 140 return false; 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 compatInfo the compability info. Must not be null. 148 * @param token the application token for determining stack bounds. 149 */ 150 public Resources getTopLevelResources(String resDir, int displayId, 151 Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { 152 final float scale = compatInfo.applicationScale; 153 ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, 154 token); 155 Resources r; 156 synchronized (this) { 157 // Resources is app scale dependent. 158 if (false) { 159 Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); 160 } 161 WeakReference<Resources> wr = mActiveResources.get(key); 162 r = wr != null ? wr.get() : null; 163 //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); 164 if (r != null && r.getAssets().isUpToDate()) { 165 if (false) { 166 Slog.w(TAG, "Returning cached resources " + r + " " + resDir 167 + ": appScale=" + r.getCompatibilityInfo().applicationScale); 168 } 169 return r; 170 } 171 } 172 173 //if (r != null) { 174 // Slog.w(TAG, "Throwing away out-of-date resources!!!! " 175 // + r + " " + resDir); 176 //} 177 178 AssetManager assets = new AssetManager(); 179 if (assets.addAssetPath(resDir) == 0) { 180 return null; 181 } 182 183 //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); 184 DisplayMetrics dm = getDisplayMetricsLocked(displayId); 185 Configuration config; 186 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); 187 final boolean hasOverrideConfig = key.hasOverrideConfiguration(); 188 if (!isDefaultDisplay || hasOverrideConfig) { 189 config = new Configuration(getConfiguration()); 190 if (!isDefaultDisplay) { 191 applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); 192 } 193 if (hasOverrideConfig) { 194 config.updateFrom(key.mOverrideConfiguration); 195 } 196 } else { 197 config = getConfiguration(); 198 } 199 r = new Resources(assets, dm, config, compatInfo, token); 200 if (false) { 201 Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " 202 + r.getConfiguration() + " appScale=" 203 + r.getCompatibilityInfo().applicationScale); 204 } 205 206 synchronized (this) { 207 WeakReference<Resources> wr = mActiveResources.get(key); 208 Resources existing = wr != null ? wr.get() : null; 209 if (existing != null && existing.getAssets().isUpToDate()) { 210 // Someone else already created the resources while we were 211 // unlocked; go ahead and use theirs. 212 r.getAssets().close(); 213 return existing; 214 } 215 216 // XXX need to remove entries when weak references go away 217 mActiveResources.put(key, new WeakReference<Resources>(r)); 218 return r; 219 } 220 } 221 222 public final boolean applyConfigurationToResourcesLocked(Configuration config, 223 CompatibilityInfo compat) { 224 if (mResConfiguration == null) { 225 mResConfiguration = new Configuration(); 226 } 227 if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) { 228 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" 229 + mResConfiguration.seq + ", newSeq=" + config.seq); 230 return false; 231 } 232 int changes = mResConfiguration.updateFrom(config); 233 flushDisplayMetricsLocked(); 234 DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY); 235 236 if (compat != null && (mResCompatibilityInfo == null || 237 !mResCompatibilityInfo.equals(compat))) { 238 mResCompatibilityInfo = compat; 239 changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT 240 | ActivityInfo.CONFIG_SCREEN_SIZE 241 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 242 } 243 244 // set it for java, this also affects newly created Resources 245 if (config.locale != null) { 246 Locale.setDefault(config.locale); 247 } 248 249 Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); 250 251 ApplicationPackageManager.configurationChanged(); 252 //Slog.i(TAG, "Configuration changed in " + currentPackageName()); 253 254 Configuration tmpConfig = null; 255 256 for (int i=mActiveResources.size()-1; i>=0; i--) { 257 ResourcesKey key = mActiveResources.keyAt(i); 258 Resources r = mActiveResources.valueAt(i).get(); 259 if (r != null) { 260 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " 261 + r + " config to: " + config); 262 int displayId = key.mDisplayId; 263 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); 264 DisplayMetrics dm = defaultDisplayMetrics; 265 final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); 266 if (!isDefaultDisplay || hasOverrideConfiguration) { 267 if (tmpConfig == null) { 268 tmpConfig = new Configuration(); 269 } 270 tmpConfig.setTo(config); 271 if (!isDefaultDisplay) { 272 dm = getDisplayMetricsLocked(displayId); 273 applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig); 274 } 275 if (hasOverrideConfiguration) { 276 tmpConfig.updateFrom(key.mOverrideConfiguration); 277 } 278 r.updateConfiguration(tmpConfig, dm, compat); 279 } else { 280 r.updateConfiguration(config, dm, compat); 281 } 282 //Slog.i(TAG, "Updated app resources " + v.getKey() 283 // + " " + r + ": " + r.getConfiguration()); 284 } else { 285 //Slog.i(TAG, "Removing old resources " + v.getKey()); 286 mActiveResources.removeAt(i); 287 } 288 } 289 290 return changes != 0; 291 } 292 293 } 294