Home | History | Annotate | Download | only in app
      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