Home | History | Annotate | Download | only in om
      1 /*
      2  * Copyright (C) 2016 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 com.android.server.om;
     18 
     19 import static android.content.om.OverlayInfo.STATE_DISABLED;
     20 import static android.content.om.OverlayInfo.STATE_ENABLED;
     21 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
     22 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
     23 
     24 import static com.android.server.om.OverlayManagerService.DEBUG;
     25 import static com.android.server.om.OverlayManagerService.TAG;
     26 
     27 import android.annotation.NonNull;
     28 import android.annotation.Nullable;
     29 import android.content.om.OverlayInfo;
     30 import android.content.pm.PackageInfo;
     31 import android.text.TextUtils;
     32 import android.util.ArrayMap;
     33 import android.util.ArraySet;
     34 import android.util.Slog;
     35 
     36 import java.io.PrintWriter;
     37 import java.util.ArrayList;
     38 import java.util.Iterator;
     39 import java.util.List;
     40 import java.util.Map;
     41 import java.util.Set;
     42 
     43 /**
     44  * Internal implementation of OverlayManagerService.
     45  *
     46  * Methods in this class should only be called by the OverlayManagerService.
     47  * This class is not thread-safe; the caller is expected to ensure the
     48  * necessary thread synchronization.
     49  *
     50  * @see OverlayManagerService
     51  */
     52 final class OverlayManagerServiceImpl {
     53     private final PackageManagerHelper mPackageManager;
     54     private final IdmapManager mIdmapManager;
     55     private final OverlayManagerSettings mSettings;
     56     private final Set<String> mDefaultOverlays;
     57     private final OverlayChangeListener mListener;
     58 
     59     OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
     60             @NonNull final IdmapManager idmapManager,
     61             @NonNull final OverlayManagerSettings settings,
     62             @NonNull final Set<String> defaultOverlays,
     63             @NonNull final OverlayChangeListener listener) {
     64         mPackageManager = packageManager;
     65         mIdmapManager = idmapManager;
     66         mSettings = settings;
     67         mDefaultOverlays = defaultOverlays;
     68         mListener = listener;
     69     }
     70 
     71     /**
     72      * Call this to synchronize the Settings for a user with what PackageManager knows about a user.
     73      * Returns a list of target packages that must refresh their overlays. This list is the union
     74      * of two sets: the set of targets with currently active overlays, and the
     75      * set of targets that had, but no longer have, active overlays.
     76      */
     77     ArrayList<String> updateOverlaysForUser(final int newUserId) {
     78         if (DEBUG) {
     79             Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId);
     80         }
     81 
     82         final Set<String> packagesToUpdateAssets = new ArraySet<>();
     83         final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
     84         final int tmpSize = tmp.size();
     85         final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize);
     86         for (int i = 0; i < tmpSize; i++) {
     87             final List<OverlayInfo> chunk = tmp.valueAt(i);
     88             final int chunkSize = chunk.size();
     89             for (int j = 0; j < chunkSize; j++) {
     90                 final OverlayInfo oi = chunk.get(j);
     91                 storedOverlayInfos.put(oi.packageName, oi);
     92             }
     93         }
     94 
     95         List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId);
     96         final int overlayPackagesSize = overlayPackages.size();
     97         for (int i = 0; i < overlayPackagesSize; i++) {
     98             final PackageInfo overlayPackage = overlayPackages.get(i);
     99             final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName);
    100             if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) {
    101                 // Update the overlay if it didn't exist or had the wrong target package.
    102                 mSettings.init(overlayPackage.packageName, newUserId,
    103                         overlayPackage.overlayTarget,
    104                         overlayPackage.applicationInfo.getBaseCodePath(),
    105                         overlayPackage.isStaticOverlay, overlayPackage.overlayPriority);
    106 
    107                 if (oi == null) {
    108                     // This overlay does not exist in our settings.
    109                     if (overlayPackage.isStaticOverlay ||
    110                             mDefaultOverlays.contains(overlayPackage.packageName)) {
    111                         // Enable this overlay by default.
    112                         if (DEBUG) {
    113                             Slog.d(TAG, "Enabling overlay " + overlayPackage.packageName
    114                                     + " for user " + newUserId + " by default");
    115                         }
    116                         mSettings.setEnabled(overlayPackage.packageName, newUserId, true);
    117                     }
    118                 } else {
    119                     // The targetPackageName we have stored doesn't match the overlay's target.
    120                     // Queue the old target for an update as well.
    121                     packagesToUpdateAssets.add(oi.targetPackageName);
    122                 }
    123             }
    124 
    125             try {
    126                 final PackageInfo targetPackage =
    127                         mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId);
    128                 updateState(targetPackage, overlayPackage, newUserId);
    129             } catch (OverlayManagerSettings.BadKeyException e) {
    130                 Slog.e(TAG, "failed to update settings", e);
    131                 mSettings.remove(overlayPackage.packageName, newUserId);
    132             }
    133 
    134             packagesToUpdateAssets.add(overlayPackage.overlayTarget);
    135             storedOverlayInfos.remove(overlayPackage.packageName);
    136         }
    137 
    138         // any OverlayInfo left in storedOverlayInfos is no longer
    139         // installed and should be removed
    140         final int storedOverlayInfosSize = storedOverlayInfos.size();
    141         for (int i = 0; i < storedOverlayInfosSize; i++) {
    142             final OverlayInfo oi = storedOverlayInfos.valueAt(i);
    143             mSettings.remove(oi.packageName, oi.userId);
    144             removeIdmapIfPossible(oi);
    145             packagesToUpdateAssets.add(oi.targetPackageName);
    146         }
    147 
    148         // remove target packages that are not installed
    149         final Iterator<String> iter = packagesToUpdateAssets.iterator();
    150         while (iter.hasNext()) {
    151             String targetPackageName = iter.next();
    152             if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
    153                 iter.remove();
    154             }
    155         }
    156         return new ArrayList<>(packagesToUpdateAssets);
    157     }
    158 
    159     void onUserRemoved(final int userId) {
    160         if (DEBUG) {
    161             Slog.d(TAG, "onUserRemoved userId=" + userId);
    162         }
    163         mSettings.removeUser(userId);
    164     }
    165 
    166     void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
    167         if (DEBUG) {
    168             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
    169         }
    170 
    171         final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
    172         updateAllOverlaysForTarget(packageName, userId, targetPackage);
    173         mListener.onOverlaysChanged(packageName, userId);
    174     }
    175 
    176     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
    177         if (DEBUG) {
    178             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
    179         }
    180 
    181         final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
    182         if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
    183             mListener.onOverlaysChanged(packageName, userId);
    184         }
    185     }
    186 
    187     void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
    188         if (DEBUG) {
    189             Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
    190         }
    191 
    192         if (updateAllOverlaysForTarget(packageName, userId, null)) {
    193             mListener.onOverlaysChanged(packageName, userId);
    194         }
    195     }
    196 
    197     void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
    198         if (DEBUG) {
    199             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
    200         }
    201 
    202         final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
    203         if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
    204             mListener.onOverlaysChanged(packageName, userId);
    205         }
    206     }
    207 
    208     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
    209         if (DEBUG) {
    210             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
    211         }
    212 
    213         updateAllOverlaysForTarget(packageName, userId, null);
    214     }
    215 
    216     /**
    217      * Returns true if the settings were modified for this target.
    218      */
    219     private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
    220             @Nullable final PackageInfo targetPackage) {
    221         boolean modified = false;
    222         final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
    223         final int N = ois.size();
    224         for (int i = 0; i < N; i++) {
    225             final OverlayInfo oi = ois.get(i);
    226             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
    227             if (overlayPackage == null) {
    228                 modified |= mSettings.remove(oi.packageName, oi.userId);
    229                 removeIdmapIfPossible(oi);
    230             } else {
    231                 try {
    232                     modified |= updateState(targetPackage, overlayPackage, userId);
    233                 } catch (OverlayManagerSettings.BadKeyException e) {
    234                     Slog.e(TAG, "failed to update settings", e);
    235                     modified |= mSettings.remove(oi.packageName, userId);
    236                 }
    237             }
    238         }
    239         return modified;
    240     }
    241 
    242     void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
    243         if (DEBUG) {
    244             Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
    245         }
    246 
    247         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    248         if (overlayPackage == null) {
    249             Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
    250             onOverlayPackageRemoved(packageName, userId);
    251             return;
    252         }
    253 
    254         final PackageInfo targetPackage =
    255                 mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId);
    256 
    257         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
    258                 overlayPackage.applicationInfo.getBaseCodePath(), overlayPackage.isStaticOverlay,
    259                 overlayPackage.overlayPriority);
    260         try {
    261             if (updateState(targetPackage, overlayPackage, userId)) {
    262                 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
    263             }
    264         } catch (OverlayManagerSettings.BadKeyException e) {
    265             Slog.e(TAG, "failed to update settings", e);
    266             mSettings.remove(packageName, userId);
    267         }
    268     }
    269 
    270     void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
    271         Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported");
    272     }
    273 
    274     void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
    275         Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported");
    276     }
    277 
    278     void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
    279         Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported");
    280     }
    281 
    282     void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
    283         Slog.wtf(TAG, "onOverlayPackageRemoved called, but only pre-installed overlays supported");
    284     }
    285 
    286     OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) {
    287         try {
    288             return mSettings.getOverlayInfo(packageName, userId);
    289         } catch (OverlayManagerSettings.BadKeyException e) {
    290             return null;
    291         }
    292     }
    293 
    294     List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
    295             final int userId) {
    296         return mSettings.getOverlaysForTarget(targetPackageName, userId);
    297     }
    298 
    299     Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
    300         return mSettings.getOverlaysForUser(userId);
    301     }
    302 
    303     boolean setEnabled(@NonNull final String packageName, final boolean enable,
    304             final int userId) {
    305         if (DEBUG) {
    306             Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
    307                         packageName, enable, userId));
    308         }
    309 
    310         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    311         if (overlayPackage == null) {
    312             return false;
    313         }
    314 
    315         // Ignore static overlays.
    316         if (overlayPackage.isStaticOverlay) {
    317             return false;
    318         }
    319 
    320         try {
    321             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
    322             final PackageInfo targetPackage =
    323                     mPackageManager.getPackageInfo(oi.targetPackageName, userId);
    324             boolean modified = mSettings.setEnabled(packageName, userId, enable);
    325             modified |= updateState(targetPackage, overlayPackage, userId);
    326 
    327             if (modified) {
    328                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
    329             }
    330             return true;
    331         } catch (OverlayManagerSettings.BadKeyException e) {
    332             return false;
    333         }
    334     }
    335 
    336     boolean setEnabledExclusive(@NonNull final String packageName, final int userId) {
    337         if (DEBUG) {
    338             Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId));
    339         }
    340 
    341         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    342         if (overlayPackage == null) {
    343             return false;
    344         }
    345 
    346         try {
    347             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
    348             final PackageInfo targetPackage =
    349                     mPackageManager.getPackageInfo(oi.targetPackageName, userId);
    350 
    351             List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
    352 
    353             boolean modified = false;
    354 
    355             // Disable all other overlays.
    356             allOverlays.remove(oi);
    357             for (int i = 0; i < allOverlays.size(); i++) {
    358                 final String disabledOverlayPackageName = allOverlays.get(i).packageName;
    359                 final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
    360                         disabledOverlayPackageName, userId);
    361                 if (disabledOverlayPackageInfo == null) {
    362                     modified |= mSettings.remove(disabledOverlayPackageName, userId);
    363                     continue;
    364                 }
    365 
    366                 if (disabledOverlayPackageInfo.isStaticOverlay) {
    367                     // Don't touch static overlays.
    368                     continue;
    369                 }
    370 
    371                 // Disable the overlay.
    372                 modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
    373                 modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId);
    374             }
    375 
    376             // Enable the selected overlay.
    377             modified |= mSettings.setEnabled(packageName, userId, true);
    378             modified |= updateState(targetPackage, overlayPackage, userId);
    379 
    380             if (modified) {
    381                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
    382             }
    383             return true;
    384         } catch (OverlayManagerSettings.BadKeyException e) {
    385             return false;
    386         }
    387     }
    388 
    389     private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
    390         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    391         if (overlayPackage == null || overlayPackage.isStaticOverlay) {
    392             return false;
    393         }
    394         return true;
    395     }
    396 
    397     boolean setPriority(@NonNull final String packageName,
    398             @NonNull final String newParentPackageName, final int userId) {
    399         if (DEBUG) {
    400             Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
    401                     + newParentPackageName + " userId=" + userId);
    402         }
    403 
    404         if (!isPackageUpdatableOverlay(packageName, userId)) {
    405             return false;
    406         }
    407 
    408         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    409         if (overlayPackage == null) {
    410             return false;
    411         }
    412 
    413         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
    414             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
    415         }
    416         return true;
    417     }
    418 
    419     boolean setHighestPriority(@NonNull final String packageName, final int userId) {
    420         if (DEBUG) {
    421             Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
    422         }
    423 
    424         if (!isPackageUpdatableOverlay(packageName, userId)) {
    425             return false;
    426         }
    427 
    428         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    429         if (overlayPackage == null) {
    430             return false;
    431         }
    432 
    433         if (mSettings.setHighestPriority(packageName, userId)) {
    434             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
    435         }
    436         return true;
    437     }
    438 
    439     boolean setLowestPriority(@NonNull final String packageName, final int userId) {
    440         if (DEBUG) {
    441             Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
    442         }
    443 
    444         if (!isPackageUpdatableOverlay(packageName, userId)) {
    445             return false;
    446         }
    447 
    448         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
    449         if (overlayPackage == null) {
    450             return false;
    451         }
    452 
    453         if (mSettings.setLowestPriority(packageName, userId)) {
    454             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
    455         }
    456         return true;
    457     }
    458 
    459     void onDump(@NonNull final PrintWriter pw) {
    460         mSettings.dump(pw);
    461         pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
    462     }
    463 
    464     List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
    465             final int userId) {
    466         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
    467         final List<String> paths = new ArrayList<>(overlays.size());
    468         final int N = overlays.size();
    469         for (int i = 0; i < N; i++) {
    470             final OverlayInfo oi = overlays.get(i);
    471             if (oi.isEnabled()) {
    472                 paths.add(oi.packageName);
    473             }
    474         }
    475         return paths;
    476     }
    477 
    478     /**
    479      * Returns true if the settings/state was modified, false otherwise.
    480      */
    481     private boolean updateState(@Nullable final PackageInfo targetPackage,
    482             @NonNull final PackageInfo overlayPackage, final int userId)
    483             throws OverlayManagerSettings.BadKeyException {
    484         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
    485         if (targetPackage != null &&
    486                 !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) {
    487             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
    488         }
    489 
    490         boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId,
    491                 overlayPackage.applicationInfo.getBaseCodePath());
    492 
    493         final int currentState = mSettings.getState(overlayPackage.packageName, userId);
    494         final int newState = calculateNewState(targetPackage, overlayPackage, userId);
    495         if (currentState != newState) {
    496             if (DEBUG) {
    497                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
    498                             overlayPackage.packageName, userId,
    499                             OverlayInfo.stateToString(currentState),
    500                             OverlayInfo.stateToString(newState)));
    501             }
    502             modified |= mSettings.setState(overlayPackage.packageName, userId, newState);
    503         }
    504         return modified;
    505     }
    506 
    507     private int calculateNewState(@Nullable final PackageInfo targetPackage,
    508             @NonNull final PackageInfo overlayPackage, final int userId)
    509         throws OverlayManagerSettings.BadKeyException {
    510         if (targetPackage == null) {
    511             return STATE_MISSING_TARGET;
    512         }
    513 
    514         if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
    515             return STATE_NO_IDMAP;
    516         }
    517 
    518         final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
    519         return enabled ? STATE_ENABLED : STATE_DISABLED;
    520     }
    521 
    522     private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) {
    523         // For a given package, all Android users share the same idmap file.
    524         // This works because Android currently does not support users to
    525         // install different versions of the same package. It also means we
    526         // cannot remove an idmap file if any user still needs it.
    527         //
    528         // When/if the Android framework allows different versions of the same
    529         // package to be installed for different users, idmap file handling
    530         // should be revised:
    531         //
    532         // - an idmap file should be unique for each {user, package} pair
    533         //
    534         // - the path to the idmap file should be passed to the native Asset
    535         //   Manager layers, just like the path to the apk is passed today
    536         //
    537         // As part of that change, calls to this method should be replaced by
    538         // direct calls to IdmapManager.removeIdmap, without looping over all
    539         // users.
    540 
    541         if (!mIdmapManager.idmapExists(oi)) {
    542             return;
    543         }
    544         final int[] userIds = mSettings.getUsers();
    545         for (int userId : userIds) {
    546             try {
    547                 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
    548                 if (tmp != null && tmp.isEnabled()) {
    549                     // someone is still using the idmap file -> we cannot remove it
    550                     return;
    551                 }
    552             } catch (OverlayManagerSettings.BadKeyException e) {
    553                 // intentionally left empty
    554             }
    555         }
    556         mIdmapManager.removeIdmap(oi, oi.userId);
    557     }
    558 
    559     interface OverlayChangeListener {
    560         void onOverlaysChanged(@NonNull String targetPackage, int userId);
    561     }
    562 
    563     interface PackageManagerHelper {
    564         PackageInfo getPackageInfo(@NonNull String packageName, int userId);
    565         boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
    566                                    int userId);
    567         List<PackageInfo> getOverlayPackages(int userId);
    568     }
    569 }
    570