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.util.ArrayMap;
     29 import android.util.DisplayMetrics;
     30 import android.util.Log;
     31 import android.util.Pair;
     32 import android.util.Slog;
     33 import android.view.Display;
     34 import android.view.DisplayAdjustments;
     35 
     36 import java.lang.ref.WeakReference;
     37 import java.util.Locale;
     38 
     39 /** @hide */
     40 public 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         final float scale = compatInfo.applicationScale;
    162         Configuration overrideConfigCopy = (overrideConfiguration != null)
    163                 ? new Configuration(overrideConfiguration) : null;
    164         ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
    165         Resources r;
    166         synchronized (this) {
    167             // Resources is app scale dependent.
    168             if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
    169 
    170             WeakReference<Resources> wr = mActiveResources.get(key);
    171             r = wr != null ? wr.get() : null;
    172             //if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
    173             if (r != null && r.getAssets().isUpToDate()) {
    174                 if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
    175                         + ": appScale=" + r.getCompatibilityInfo().applicationScale
    176                         + " key=" + key + " overrideConfig=" + overrideConfiguration);
    177                 return r;
    178             }
    179         }
    180 
    181         //if (r != null) {
    182         //    Log.w(TAG, "Throwing away out-of-date resources!!!! "
    183         //            + r + " " + resDir);
    184         //}
    185 
    186         AssetManager assets = new AssetManager();
    187         // resDir can be null if the 'android' package is creating a new Resources object.
    188         // This is fine, since each AssetManager automatically loads the 'android' package
    189         // already.
    190         if (resDir != null) {
    191             if (assets.addAssetPath(resDir) == 0) {
    192                 return null;
    193             }
    194         }
    195 
    196         if (splitResDirs != null) {
    197             for (String splitResDir : splitResDirs) {
    198                 if (assets.addAssetPath(splitResDir) == 0) {
    199                     return null;
    200                 }
    201             }
    202         }
    203 
    204         if (overlayDirs != null) {
    205             for (String idmapPath : overlayDirs) {
    206                 assets.addOverlayPath(idmapPath);
    207             }
    208         }
    209 
    210         if (libDirs != null) {
    211             for (String libDir : libDirs) {
    212                 if (libDir.endsWith(".apk")) {
    213                     // Avoid opening files we know do not have resources,
    214                     // like code-only .jar files.
    215                     if (assets.addAssetPath(libDir) == 0) {
    216                         Log.w(TAG, "Asset path '" + libDir +
    217                                 "' does not exist or contains no resources.");
    218                     }
    219                 }
    220             }
    221         }
    222 
    223         //Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
    224         DisplayMetrics dm = getDisplayMetricsLocked(displayId);
    225         Configuration config;
    226         final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
    227         final boolean hasOverrideConfig = key.hasOverrideConfiguration();
    228         if (!isDefaultDisplay || hasOverrideConfig) {
    229             config = new Configuration(getConfiguration());
    230             if (!isDefaultDisplay) {
    231                 applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
    232             }
    233             if (hasOverrideConfig) {
    234                 config.updateFrom(key.mOverrideConfiguration);
    235                 if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
    236             }
    237         } else {
    238             config = getConfiguration();
    239         }
    240         r = new Resources(assets, dm, config, compatInfo);
    241         if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
    242                 + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
    243 
    244         synchronized (this) {
    245             WeakReference<Resources> wr = mActiveResources.get(key);
    246             Resources existing = wr != null ? wr.get() : null;
    247             if (existing != null && existing.getAssets().isUpToDate()) {
    248                 // Someone else already created the resources while we were
    249                 // unlocked; go ahead and use theirs.
    250                 r.getAssets().close();
    251                 return existing;
    252             }
    253 
    254             // XXX need to remove entries when weak references go away
    255             mActiveResources.put(key, new WeakReference<>(r));
    256             if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
    257             return r;
    258         }
    259     }
    260 
    261     final boolean applyConfigurationToResourcesLocked(Configuration config,
    262             CompatibilityInfo compat) {
    263         if (mResConfiguration == null) {
    264             mResConfiguration = new Configuration();
    265         }
    266         if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
    267             if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
    268                     + mResConfiguration.seq + ", newSeq=" + config.seq);
    269             return false;
    270         }
    271         int changes = mResConfiguration.updateFrom(config);
    272         // Things might have changed in display manager, so clear the cached displays.
    273         mDisplays.clear();
    274         DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked();
    275 
    276         if (compat != null && (mResCompatibilityInfo == null ||
    277                 !mResCompatibilityInfo.equals(compat))) {
    278             mResCompatibilityInfo = compat;
    279             changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
    280                     | ActivityInfo.CONFIG_SCREEN_SIZE
    281                     | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
    282         }
    283 
    284         // set it for java, this also affects newly created Resources
    285         if (config.locale != null) {
    286             Locale.setDefault(config.locale);
    287         }
    288 
    289         Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
    290 
    291         ApplicationPackageManager.configurationChanged();
    292         //Slog.i(TAG, "Configuration changed in " + currentPackageName());
    293 
    294         Configuration tmpConfig = null;
    295 
    296         for (int i = mActiveResources.size() - 1; i >= 0; i--) {
    297             ResourcesKey key = mActiveResources.keyAt(i);
    298             Resources r = mActiveResources.valueAt(i).get();
    299             if (r != null) {
    300                 if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
    301                         + r + " config to: " + config);
    302                 int displayId = key.mDisplayId;
    303                 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
    304                 DisplayMetrics dm = defaultDisplayMetrics;
    305                 final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
    306                 if (!isDefaultDisplay || hasOverrideConfiguration) {
    307                     if (tmpConfig == null) {
    308                         tmpConfig = new Configuration();
    309                     }
    310                     tmpConfig.setTo(config);
    311                     if (!isDefaultDisplay) {
    312                         dm = getDisplayMetricsLocked(displayId);
    313                         applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
    314                     }
    315                     if (hasOverrideConfiguration) {
    316                         tmpConfig.updateFrom(key.mOverrideConfiguration);
    317                     }
    318                     r.updateConfiguration(tmpConfig, dm, compat);
    319                 } else {
    320                     r.updateConfiguration(config, dm, compat);
    321                 }
    322                 //Slog.i(TAG, "Updated app resources " + v.getKey()
    323                 //        + " " + r + ": " + r.getConfiguration());
    324             } else {
    325                 //Slog.i(TAG, "Removing old resources " + v.getKey());
    326                 mActiveResources.removeAt(i);
    327             }
    328         }
    329 
    330         return changes != 0;
    331     }
    332 
    333 }
    334