| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.om; |
| |
| import static android.content.om.OverlayInfo.STATE_DISABLED; |
| import static android.content.om.OverlayInfo.STATE_ENABLED; |
| import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; |
| import static android.content.om.OverlayInfo.STATE_NO_IDMAP; |
| import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING; |
| import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING; |
| |
| import static com.android.server.om.OverlayManagerService.DEBUG; |
| import static com.android.server.om.OverlayManagerService.TAG; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.om.OverlayInfo; |
| import android.content.pm.PackageInfo; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.Slog; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Internal implementation of OverlayManagerService. |
| * |
| * Methods in this class should only be called by the OverlayManagerService. |
| * This class is not thread-safe; the caller is expected to ensure the |
| * necessary thread synchronization. |
| * |
| * @see OverlayManagerService |
| */ |
| final class OverlayManagerServiceImpl { |
| // Flags to use in conjunction with updateState. |
| private static final int FLAG_TARGET_IS_UPGRADING = 1<<0; |
| private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1; |
| |
| private final PackageManagerHelper mPackageManager; |
| private final IdmapManager mIdmapManager; |
| private final OverlayManagerSettings mSettings; |
| private final Set<String> mDefaultOverlays; |
| private final OverlayChangeListener mListener; |
| |
| OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager, |
| @NonNull final IdmapManager idmapManager, |
| @NonNull final OverlayManagerSettings settings, |
| @NonNull final Set<String> defaultOverlays, |
| @NonNull final OverlayChangeListener listener) { |
| mPackageManager = packageManager; |
| mIdmapManager = idmapManager; |
| mSettings = settings; |
| mDefaultOverlays = defaultOverlays; |
| mListener = listener; |
| } |
| |
| /** |
| * Call this to synchronize the Settings for a user with what PackageManager knows about a user. |
| * Returns a list of target packages that must refresh their overlays. This list is the union |
| * of two sets: the set of targets with currently active overlays, and the |
| * set of targets that had, but no longer have, active overlays. |
| */ |
| ArrayList<String> updateOverlaysForUser(final int newUserId) { |
| if (DEBUG) { |
| Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId); |
| } |
| |
| final Set<String> packagesToUpdateAssets = new ArraySet<>(); |
| final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId); |
| final int tmpSize = tmp.size(); |
| final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize); |
| for (int i = 0; i < tmpSize; i++) { |
| final List<OverlayInfo> chunk = tmp.valueAt(i); |
| final int chunkSize = chunk.size(); |
| for (int j = 0; j < chunkSize; j++) { |
| final OverlayInfo oi = chunk.get(j); |
| storedOverlayInfos.put(oi.packageName, oi); |
| } |
| } |
| |
| List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId); |
| final int overlayPackagesSize = overlayPackages.size(); |
| for (int i = 0; i < overlayPackagesSize; i++) { |
| final PackageInfo overlayPackage = overlayPackages.get(i); |
| final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); |
| if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) { |
| // Update the overlay if it didn't exist or had the wrong target package. |
| mSettings.init(overlayPackage.packageName, newUserId, |
| overlayPackage.overlayTarget, |
| overlayPackage.applicationInfo.getBaseCodePath(), |
| overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); |
| |
| if (oi == null) { |
| // This overlay does not exist in our settings. |
| if (overlayPackage.isStaticOverlayPackage() || |
| mDefaultOverlays.contains(overlayPackage.packageName)) { |
| // Enable this overlay by default. |
| if (DEBUG) { |
| Slog.d(TAG, "Enabling overlay " + overlayPackage.packageName |
| + " for user " + newUserId + " by default"); |
| } |
| mSettings.setEnabled(overlayPackage.packageName, newUserId, true); |
| } |
| } else { |
| // The targetPackageName we have stored doesn't match the overlay's target. |
| // Queue the old target for an update as well. |
| packagesToUpdateAssets.add(oi.targetPackageName); |
| } |
| } |
| |
| try { |
| updateState(overlayPackage.overlayTarget, overlayPackage.packageName, newUserId, 0); |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| mSettings.remove(overlayPackage.packageName, newUserId); |
| } |
| |
| packagesToUpdateAssets.add(overlayPackage.overlayTarget); |
| storedOverlayInfos.remove(overlayPackage.packageName); |
| } |
| |
| // any OverlayInfo left in storedOverlayInfos is no longer |
| // installed and should be removed |
| final int storedOverlayInfosSize = storedOverlayInfos.size(); |
| for (int i = 0; i < storedOverlayInfosSize; i++) { |
| final OverlayInfo oi = storedOverlayInfos.valueAt(i); |
| mSettings.remove(oi.packageName, oi.userId); |
| removeIdmapIfPossible(oi); |
| packagesToUpdateAssets.add(oi.targetPackageName); |
| } |
| |
| // remove target packages that are not installed |
| final Iterator<String> iter = packagesToUpdateAssets.iterator(); |
| while (iter.hasNext()) { |
| String targetPackageName = iter.next(); |
| if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { |
| iter.remove(); |
| } |
| } |
| return new ArrayList<>(packagesToUpdateAssets); |
| } |
| |
| void onUserRemoved(final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onUserRemoved userId=" + userId); |
| } |
| mSettings.removeUser(userId); |
| } |
| |
| void onTargetPackageAdded(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (updateAllOverlaysForTarget(packageName, userId, 0)) { |
| mListener.onOverlaysChanged(packageName, userId); |
| } |
| } |
| |
| void onTargetPackageChanged(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (updateAllOverlaysForTarget(packageName, userId, 0)) { |
| mListener.onOverlaysChanged(packageName, userId); |
| } |
| } |
| |
| void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" |
| + userId); |
| } |
| |
| if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) { |
| mListener.onOverlaysChanged(packageName, userId); |
| } |
| } |
| |
| void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (updateAllOverlaysForTarget(packageName, userId, 0)) { |
| mListener.onOverlaysChanged(packageName, userId); |
| } |
| } |
| |
| void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (updateAllOverlaysForTarget(packageName, userId, 0)) { |
| mListener.onOverlaysChanged(packageName, userId); |
| } |
| } |
| |
| /** |
| * Returns true if the settings were modified for this target. |
| */ |
| private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName, |
| final int userId, final int flags) { |
| boolean modified = false; |
| final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId); |
| final int N = ois.size(); |
| for (int i = 0; i < N; i++) { |
| final OverlayInfo oi = ois.get(i); |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, |
| userId); |
| if (overlayPackage == null) { |
| modified |= mSettings.remove(oi.packageName, oi.userId); |
| removeIdmapIfPossible(oi); |
| } else { |
| try { |
| modified |= updateState(targetPackageName, oi.packageName, userId, flags); |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| modified |= mSettings.remove(oi.packageName, userId); |
| } |
| } |
| } |
| return modified; |
| } |
| |
| void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); |
| onOverlayPackageRemoved(packageName, userId); |
| return; |
| } |
| |
| mSettings.init(packageName, userId, overlayPackage.overlayTarget, |
| overlayPackage.applicationInfo.getBaseCodePath(), |
| overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); |
| try { |
| if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { |
| mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| mSettings.remove(packageName, userId); |
| } |
| } |
| |
| void onOverlayPackageChanged(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); |
| } |
| |
| try { |
| final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); |
| if (updateState(oi.targetPackageName, packageName, userId, 0)) { |
| mListener.onOverlaysChanged(oi.targetPackageName, userId); |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| } |
| } |
| |
| void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onOverlayPackageUpgrading packageName=" + packageName + " userId=" |
| + userId); |
| } |
| |
| try { |
| final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); |
| if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) { |
| removeIdmapIfPossible(oi); |
| mListener.onOverlaysChanged(oi.targetPackageName, userId); |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| } |
| } |
| |
| void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "onOverlayPackageUpgraded packageName=" + packageName + " userId=" |
| + userId); |
| } |
| |
| final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId); |
| if (pkg == null) { |
| Slog.w(TAG, "overlay package " + packageName + " was upgraded, but couldn't be found"); |
| onOverlayPackageRemoved(packageName, userId); |
| return; |
| } |
| |
| try { |
| final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId); |
| if (!oldOi.targetPackageName.equals(pkg.overlayTarget)) { |
| mSettings.init(packageName, userId, pkg.overlayTarget, |
| pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(), |
| pkg.overlayPriority); |
| } |
| |
| if (updateState(pkg.overlayTarget, packageName, userId, 0)) { |
| mListener.onOverlaysChanged(pkg.overlayTarget, userId); |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to update settings", e); |
| } |
| } |
| |
| void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) { |
| try { |
| final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId); |
| if (mSettings.remove(packageName, userId)) { |
| removeIdmapIfPossible(overlayInfo); |
| if (overlayInfo.isEnabled()) { |
| // Only trigger updates if the overlay was enabled. |
| mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId); |
| } |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| Slog.e(TAG, "failed to remove overlay", e); |
| } |
| } |
| |
| OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) { |
| try { |
| return mSettings.getOverlayInfo(packageName, userId); |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| return null; |
| } |
| } |
| |
| List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName, |
| final int userId) { |
| return mSettings.getOverlaysForTarget(targetPackageName, userId); |
| } |
| |
| Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { |
| return mSettings.getOverlaysForUser(userId); |
| } |
| |
| boolean setEnabled(@NonNull final String packageName, final boolean enable, |
| final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", |
| packageName, enable, userId)); |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| return false; |
| } |
| |
| // Ignore static overlays. |
| if (overlayPackage.isStaticOverlayPackage()) { |
| return false; |
| } |
| |
| try { |
| final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); |
| boolean modified = mSettings.setEnabled(packageName, userId, enable); |
| modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); |
| |
| if (modified) { |
| mListener.onOverlaysChanged(oi.targetPackageName, userId); |
| } |
| return true; |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| return false; |
| } |
| } |
| |
| boolean setEnabledExclusive(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, |
| userId)); |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| return false; |
| } |
| |
| try { |
| final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); |
| final String targetPackageName = oi.targetPackageName; |
| |
| List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId); |
| |
| boolean modified = false; |
| |
| // Disable all other overlays. |
| allOverlays.remove(oi); |
| for (int i = 0; i < allOverlays.size(); i++) { |
| final String disabledOverlayPackageName = allOverlays.get(i).packageName; |
| final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( |
| disabledOverlayPackageName, userId); |
| if (disabledOverlayPackageInfo == null) { |
| modified |= mSettings.remove(disabledOverlayPackageName, userId); |
| continue; |
| } |
| |
| if (disabledOverlayPackageInfo.isStaticOverlayPackage()) { |
| // Don't touch static overlays. |
| continue; |
| } |
| |
| // Disable the overlay. |
| modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); |
| modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0); |
| } |
| |
| // Enable the selected overlay. |
| modified |= mSettings.setEnabled(packageName, userId, true); |
| modified |= updateState(targetPackageName, packageName, userId, 0); |
| |
| if (modified) { |
| mListener.onOverlaysChanged(targetPackageName, userId); |
| } |
| return true; |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| return false; |
| } |
| } |
| |
| private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) { |
| return false; |
| } |
| return true; |
| } |
| |
| boolean setPriority(@NonNull final String packageName, |
| @NonNull final String newParentPackageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" |
| + newParentPackageName + " userId=" + userId); |
| } |
| |
| if (!isPackageUpdatableOverlay(packageName, userId)) { |
| return false; |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| return false; |
| } |
| |
| if (mSettings.setPriority(packageName, newParentPackageName, userId)) { |
| mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); |
| } |
| return true; |
| } |
| |
| boolean setHighestPriority(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (!isPackageUpdatableOverlay(packageName, userId)) { |
| return false; |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| return false; |
| } |
| |
| if (mSettings.setHighestPriority(packageName, userId)) { |
| mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); |
| } |
| return true; |
| } |
| |
| boolean setLowestPriority(@NonNull final String packageName, final int userId) { |
| if (DEBUG) { |
| Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); |
| } |
| |
| if (!isPackageUpdatableOverlay(packageName, userId)) { |
| return false; |
| } |
| |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); |
| if (overlayPackage == null) { |
| return false; |
| } |
| |
| if (mSettings.setLowestPriority(packageName, userId)) { |
| mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); |
| } |
| return true; |
| } |
| |
| void onDump(@NonNull final PrintWriter pw) { |
| mSettings.dump(pw); |
| pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); |
| } |
| |
| List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, |
| final int userId) { |
| final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, |
| userId); |
| final List<String> paths = new ArrayList<>(overlays.size()); |
| final int N = overlays.size(); |
| for (int i = 0; i < N; i++) { |
| final OverlayInfo oi = overlays.get(i); |
| if (oi.isEnabled()) { |
| paths.add(oi.packageName); |
| } |
| } |
| return paths; |
| } |
| |
| /** |
| * Returns true if the settings/state was modified, false otherwise. |
| */ |
| private boolean updateState(@NonNull final String targetPackageName, |
| @NonNull final String overlayPackageName, final int userId, final int flags) |
| throws OverlayManagerSettings.BadKeyException { |
| |
| final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId); |
| final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName, |
| userId); |
| |
| // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. |
| if (targetPackage != null && overlayPackage != null && |
| !("android".equals(targetPackageName) |
| && overlayPackage.isStaticOverlayPackage())) { |
| mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); |
| } |
| |
| boolean modified = false; |
| if (overlayPackage != null) { |
| modified |= mSettings.setBaseCodePath(overlayPackageName, userId, |
| overlayPackage.applicationInfo.getBaseCodePath()); |
| } |
| |
| final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId); |
| final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage, |
| userId, flags); |
| if (currentState != newState) { |
| if (DEBUG) { |
| Slog.d(TAG, String.format("%s:%d: %s -> %s", |
| overlayPackageName, userId, |
| OverlayInfo.stateToString(currentState), |
| OverlayInfo.stateToString(newState))); |
| } |
| modified |= mSettings.setState(overlayPackageName, userId, newState); |
| } |
| return modified; |
| } |
| |
| private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage, |
| @Nullable final PackageInfo overlayPackage, final int userId, final int flags) |
| throws OverlayManagerSettings.BadKeyException { |
| |
| if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) { |
| return STATE_TARGET_UPGRADING; |
| } |
| |
| if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) { |
| return STATE_OVERLAY_UPGRADING; |
| } |
| |
| // assert expectation on overlay package: can only be null if the flags are used |
| if (DEBUG && overlayPackage == null) { |
| throw new IllegalArgumentException("null overlay package not compatible with no flags"); |
| } |
| |
| if (targetPackage == null) { |
| return STATE_MISSING_TARGET; |
| } |
| |
| if (!mIdmapManager.idmapExists(overlayPackage, userId)) { |
| return STATE_NO_IDMAP; |
| } |
| |
| final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); |
| return enabled ? STATE_ENABLED : STATE_DISABLED; |
| } |
| |
| private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) { |
| // For a given package, all Android users share the same idmap file. |
| // This works because Android currently does not support users to |
| // install different versions of the same package. It also means we |
| // cannot remove an idmap file if any user still needs it. |
| // |
| // When/if the Android framework allows different versions of the same |
| // package to be installed for different users, idmap file handling |
| // should be revised: |
| // |
| // - an idmap file should be unique for each {user, package} pair |
| // |
| // - the path to the idmap file should be passed to the native Asset |
| // Manager layers, just like the path to the apk is passed today |
| // |
| // As part of that change, calls to this method should be replaced by |
| // direct calls to IdmapManager.removeIdmap, without looping over all |
| // users. |
| |
| if (!mIdmapManager.idmapExists(oi)) { |
| return; |
| } |
| final int[] userIds = mSettings.getUsers(); |
| for (int userId : userIds) { |
| try { |
| final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); |
| if (tmp != null && tmp.isEnabled()) { |
| // someone is still using the idmap file -> we cannot remove it |
| return; |
| } |
| } catch (OverlayManagerSettings.BadKeyException e) { |
| // intentionally left empty |
| } |
| } |
| mIdmapManager.removeIdmap(oi, oi.userId); |
| } |
| |
| interface OverlayChangeListener { |
| void onOverlaysChanged(@NonNull String targetPackage, int userId); |
| } |
| |
| interface PackageManagerHelper { |
| PackageInfo getPackageInfo(@NonNull String packageName, int userId); |
| boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, |
| int userId); |
| List<PackageInfo> getOverlayPackages(int userId); |
| } |
| } |