diff options
4 files changed, 347 insertions, 465 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5415eb565b90..b382cef39145 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3216,13 +3216,6 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MEDIA_RESOURCE_GRANTED"; /** - * Broadcast Action: An overlay package has been installed. The data - * contains the name of the added overlay package. - * @hide - */ - public static final String ACTION_OVERLAY_ADDED = "android.intent.action.OVERLAY_ADDED"; - - /** * Broadcast Action: An overlay package has changed. The data contains the * name of the overlay package which has changed. This is broadcast on all * changes to the OverlayInfo returned by {@link @@ -3234,22 +3227,6 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"; /** - * Broadcast Action: An overlay package has been removed. The data contains - * the name of the overlay package which has been removed. - * @hide - */ - public static final String ACTION_OVERLAY_REMOVED = "android.intent.action.OVERLAY_REMOVED"; - - /** - * Broadcast Action: The order of a package's list of overlay packages has - * changed. The data contains the package name of the overlay package that - * had its position in the list adjusted. - * @hide - */ - public static final String - ACTION_OVERLAY_PRIORITY_CHANGED = "android.intent.action.OVERLAY_PRIORITY_CHANGED"; - - /** * Activity Action: Allow the user to select and return one or more existing * documents. When invoked, the system will display the various * {@link DocumentsProvider} instances installed on the device, letting the diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 2026c1b176ba..818c3ad9728b 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -193,13 +193,10 @@ import java.util.concurrent.atomic.AtomicBoolean; * </ul> */ public final class OverlayManagerService extends SystemService { - static final String TAG = "OverlayManager"; static final boolean DEBUG = false; - static final String PERMISSION_DENIED = "Operation not permitted for user shell"; - /** * The system property that specifies the default overlays to apply. * This is a semicolon separated list of package names. @@ -234,7 +231,7 @@ public final class OverlayManagerService extends SystemService { IdmapManager im = new IdmapManager(installer); mSettings = new OverlayManagerSettings(); mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, - getDefaultOverlayPackages()); + getDefaultOverlayPackages(), new OverlayChangeListener()); mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> { final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(ACTION_PACKAGE_ADDED); @@ -251,9 +248,6 @@ public final class OverlayManagerService extends SystemService { restoreSettings(); onSwitchUser(UserHandle.USER_SYSTEM); - schedulePersistSettings(); - - mSettings.addChangeListener(new OverlayChangeListener()); publishBinderService(Context.OVERLAY_SERVICE, mService); publishLocalService(OverlayManagerService.class, this); @@ -281,8 +275,9 @@ public final class OverlayManagerService extends SystemService { final List<String> targets; synchronized (mLock) { targets = mImpl.onSwitchUser(newUserId); + updateAssetsLocked(newUserId, targets); } - updateAssets(newUserId, targets); + schedulePersistSettings(); } private static Set<String> getDefaultOverlayPackages() { @@ -348,7 +343,8 @@ public final class OverlayManagerService extends SystemService { @NonNull final int[] userIds) { for (final int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); if (!isOverlayPackage(pi)) { @@ -365,7 +361,8 @@ public final class OverlayManagerService extends SystemService { @NonNull final int[] userIds) { for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); if (!isOverlayPackage(pi)) { @@ -397,7 +394,8 @@ public final class OverlayManagerService extends SystemService { @NonNull final int[] userIds) { for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, + false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); if (!isOverlayPackage(pi)) { @@ -449,8 +447,7 @@ public final class OverlayManagerService extends SystemService { private final IBinder mService = new IOverlayManager.Stub() { @Override - public Map<String, List<OverlayInfo>> getAllOverlays(int userId) - throws RemoteException { + public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException { userId = handleIncomingUser(userId, "getAllOverlays"); synchronized (mLock) { @@ -508,14 +505,14 @@ public final class OverlayManagerService extends SystemService { int userId) throws RemoteException { enforceChangeOverlayPackagesPermission("setEnabled"); userId = handleIncomingUser(userId, "setEnabled"); - if (packageName == null) { + if (packageName == null || !enable) { return false; } final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabledExclusive(packageName, enable, userId); + return mImpl.setEnabledExclusive(packageName, userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -643,68 +640,24 @@ public final class OverlayManagerService extends SystemService { return pi != null && pi.overlayTarget != null; } - private final class OverlayChangeListener implements OverlayManagerSettings.ChangeListener { + private final class OverlayChangeListener + implements OverlayManagerServiceImpl.OverlayChangeListener { @Override - public void onSettingsChanged() { + public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) { schedulePersistSettings(); - } - - @Override - public void onOverlayAdded(@NonNull final OverlayInfo oi) { - scheduleBroadcast(Intent.ACTION_OVERLAY_ADDED, oi, oi.isEnabled()); - } - - @Override - public void onOverlayRemoved(@NonNull final OverlayInfo oi) { - scheduleBroadcast(Intent.ACTION_OVERLAY_REMOVED, oi, oi.isEnabled()); - } - - @Override - public void onOverlayChanged(@NonNull final OverlayInfo oi, - @NonNull final OverlayInfo oldOi) { - scheduleBroadcast(Intent.ACTION_OVERLAY_CHANGED, oi, oi.isEnabled() != oldOi.isEnabled()); - } - - @Override - public void onOverlayPriorityChanged(@NonNull final OverlayInfo oi) { - scheduleBroadcast(Intent.ACTION_OVERLAY_PRIORITY_CHANGED, oi, oi.isEnabled()); - } - - private void scheduleBroadcast(@NonNull final String action, @NonNull final OverlayInfo oi, - final boolean doUpdate) { - FgThread.getHandler().post(new BroadcastRunnable(action, oi, doUpdate)); - } - - private final class BroadcastRunnable implements Runnable { - private final String mAction; - private final OverlayInfo mOverlayInfo; - private final boolean mDoUpdate; - - BroadcastRunnable(@NonNull final String action, @NonNull final OverlayInfo oi, - final boolean doUpdate) { - mAction = action; - mOverlayInfo = oi; - mDoUpdate = doUpdate; - } - - @Override - public void run() { - if (mDoUpdate) { - updateAssets(mOverlayInfo.userId, mOverlayInfo.targetPackageName); + FgThread.getHandler().post(() -> { + synchronized (mLock) { + updateAssetsLocked(userId, targetPackageName); } - sendBroadcast(mAction, mOverlayInfo.targetPackageName, mOverlayInfo.packageName, - mOverlayInfo.userId); - } - private void sendBroadcast(@NonNull final String action, - @NonNull final String targetPackageName, @NonNull final String packageName, - final int userId) { - final Intent intent = new Intent(action, Uri.fromParts("package", - String.format("%s/%s", targetPackageName, packageName), null)); + final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED, + Uri.fromParts("package", targetPackageName, null)); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (DEBUG) { - Slog.d(TAG, String.format("send broadcast %s", intent)); + Slog.d(TAG, "send broadcast " + intent); } + try { ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, @@ -712,18 +665,20 @@ public final class OverlayManagerService extends SystemService { } catch (RemoteException e) { // Intentionally left empty. } - } - + }); } } - private void updateAssets(final int userId, final String targetPackageName) { + private void updateAssetsLocked(final int userId, final String targetPackageName) { final List<String> list = new ArrayList<>(); list.add(targetPackageName); - updateAssets(userId, list); + updateAssetsLocked(userId, list); } - private void updateAssets(final int userId, List<String> targetPackageNames) { + private void updateAssetsLocked(final int userId, List<String> targetPackageNames) { + if (DEBUG) { + Slog.d(TAG, "Updating overlay assets"); + } final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); final boolean updateFrameworkRes = targetPackageNames.contains("android"); if (updateFrameworkRes) { @@ -743,6 +698,12 @@ public final class OverlayManagerService extends SystemService { final int N = targetPackageNames.size(); for (int i = 0; i < N; i++) { final String targetPackageName = targetPackageNames.get(i); + if (DEBUG) { + Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" + + TextUtils.join(",", pendingChanges.get(targetPackageName)) + + "] userId=" + userId); + } + if (!pm.setEnabledOverlayPackages( userId, targetPackageName, pendingChanges.get(targetPackageName))) { Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", @@ -762,20 +723,20 @@ public final class OverlayManagerService extends SystemService { if (mPersistSettingsScheduled.getAndSet(true)) { return; } - IoThread.getHandler().post(new Runnable() { - @Override - public void run() { - mPersistSettingsScheduled.set(false); - synchronized (mLock) { - FileOutputStream stream = null; - try { - stream = mSettingsFile.startWrite(); - mSettings.persist(stream); - mSettingsFile.finishWrite(stream); - } catch (IOException | XmlPullParserException e) { - mSettingsFile.failWrite(stream); - Slog.e(TAG, "failed to persist overlay state", e); - } + IoThread.getHandler().post(() -> { + mPersistSettingsScheduled.set(false); + if (DEBUG) { + Slog.d(TAG, "Writing overlay settings"); + } + synchronized (mLock) { + FileOutputStream stream = null; + try { + stream = mSettingsFile.startWrite(); + mSettings.persist(stream); + mSettingsFile.finishWrite(stream); + } catch (IOException | XmlPullParserException e) { + mSettingsFile.failWrite(stream); + Slog.e(TAG, "failed to persist overlay state", e); } } }); @@ -862,7 +823,8 @@ public final class OverlayManagerService extends SystemService { // The package manager does not support different versions of packages // to be installed for different users: ignore userId for now. try { - return mPackageManager.checkSignatures(packageName1, packageName2) == SIGNATURE_MATCH; + return mPackageManager.checkSignatures( + packageName1, packageName2) == SIGNATURE_MATCH; } catch (RemoteException e) { // Intentionally left blank } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 3705946008f1..c536278d1499 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -54,15 +54,18 @@ final class OverlayManagerServiceImpl { 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 Set<String> defaultOverlays, + @NonNull final OverlayChangeListener listener) { mPackageManager = packageManager; mIdmapManager = idmapManager; mSettings = settings; mDefaultOverlays = defaultOverlays; + mListener = listener; } /* @@ -145,7 +148,6 @@ final class OverlayManagerServiceImpl { iter.remove(); } } - return new ArrayList<>(packagesToUpdateAssets); } @@ -199,25 +201,30 @@ final class OverlayManagerServiceImpl { updateAllOverlaysForTarget(packageName, userId, null); } - private void updateAllOverlaysForTarget(@NonNull final String packageName, final int userId, + /** + * Returns true if the settings were modified for this target. + */ + private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId, @Nullable final PackageInfo targetPackage) { + boolean modified = false; final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, 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) { - mSettings.remove(oi.packageName, oi.userId); + modified |= mSettings.remove(oi.packageName, oi.userId); removeIdmapIfPossible(oi); } else { try { - updateState(targetPackage, overlayPackage, userId); + modified |= updateState(targetPackage, overlayPackage, userId); } catch (OverlayManagerSettings.BadKeyException e) { Slog.e(TAG, "failed to update settings", e); - mSettings.remove(oi.packageName, userId); + modified |= mSettings.remove(oi.packageName, userId); } } } + return modified; } void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { @@ -238,7 +245,9 @@ final class OverlayManagerServiceImpl { mSettings.init(packageName, userId, overlayPackage.overlayTarget, overlayPackage.applicationInfo.getBaseCodePath()); try { - updateState(targetPackage, overlayPackage, userId); + if (updateState(targetPackage, overlayPackage, userId)) { + mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); + } } catch (OverlayManagerSettings.BadKeyException e) { Slog.e(TAG, "failed to update settings", e); mSettings.remove(packageName, userId); @@ -289,8 +298,9 @@ final class OverlayManagerServiceImpl { if (overlayPackage == null) { return false; } - // Static overlay is always being enabled. - if (!enable && overlayPackage.isStaticOverlay) { + + // Ignore static overlays. + if (overlayPackage.isStaticOverlay) { return false; } @@ -298,19 +308,21 @@ final class OverlayManagerServiceImpl { final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); final PackageInfo targetPackage = mPackageManager.getPackageInfo(oi.targetPackageName, userId); - mSettings.setEnabled(packageName, userId, enable); - updateState(targetPackage, overlayPackage, userId); + boolean modified = mSettings.setEnabled(packageName, userId, enable); + modified |= updateState(targetPackage, overlayPackage, userId); + + if (modified) { + mListener.onOverlaysChanged(oi.targetPackageName, userId); + } return true; } catch (OverlayManagerSettings.BadKeyException e) { return false; } } - boolean setEnabledExclusive(@NonNull final String packageName, final boolean enable, - final int userId) { + boolean setEnabledExclusive(@NonNull final String packageName, final int userId) { if (DEBUG) { - Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", - packageName, enable, userId)); + Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId)); } final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); @@ -320,23 +332,48 @@ final class OverlayManagerServiceImpl { try { final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); + final PackageInfo targetPackage = + mPackageManager.getPackageInfo(oi.targetPackageName, userId); + List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId); + boolean modified = false; + // Disable all other overlays. allOverlays.remove(oi); for (int i = 0; i < allOverlays.size(); i++) { - // TODO: Optimize this to only send updates after all changes. - setEnabled(allOverlays.get(i).packageName, false, userId); + 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.isStaticOverlay) { + // Don't touch static overlays. + continue; + } + + // Disable the overlay. + modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); + modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId); } - setEnabled(packageName, enable, userId); + // Enable the selected overlay. + modified |= mSettings.setEnabled(packageName, userId, true); + modified |= updateState(targetPackage, overlayPackage, userId); + + if (modified) { + mListener.onOverlaysChanged(oi.targetPackageName, userId); + } return true; } catch (OverlayManagerSettings.BadKeyException e) { return false; } } - boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { + private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); if (overlayPackage == null || overlayPackage.isStaticOverlay) { return false; @@ -346,18 +383,64 @@ final class OverlayManagerServiceImpl { boolean setPriority(@NonNull final String packageName, @NonNull final String newParentPackageName, final int userId) { - return isPackageUpdatableOverlay(packageName, userId) && - mSettings.setPriority(packageName, newParentPackageName, 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) { - return isPackageUpdatableOverlay(packageName, userId) && - mSettings.setHighestPriority(packageName, 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) { - return isPackageUpdatableOverlay(packageName, userId) && - mSettings.setLowestPriority(packageName, 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) { @@ -379,16 +462,19 @@ final class OverlayManagerServiceImpl { return paths; } - private void updateState(@Nullable final PackageInfo targetPackage, + /** + * Returns true if the settings/state was modified, false otherwise. + */ + private boolean updateState(@Nullable final PackageInfo targetPackage, @NonNull final PackageInfo overlayPackage, final int userId) - throws OverlayManagerSettings.BadKeyException { + throws OverlayManagerSettings.BadKeyException { // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. if (targetPackage != null && !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } - mSettings.setBaseCodePath(overlayPackage.packageName, userId, + boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId, overlayPackage.applicationInfo.getBaseCodePath()); final int currentState = mSettings.getState(overlayPackage.packageName, userId); @@ -400,8 +486,9 @@ final class OverlayManagerServiceImpl { OverlayInfo.stateToString(currentState), OverlayInfo.stateToString(newState))); } - mSettings.setState(overlayPackage.packageName, userId, newState); + modified |= mSettings.setState(overlayPackage.packageName, userId, newState); } + return modified; } private int calculateNewState(@Nullable final PackageInfo targetPackage, @@ -441,10 +528,8 @@ final class OverlayManagerServiceImpl { if (!mIdmapManager.idmapExists(oi)) { return; } - final List<Integer> userIds = mSettings.getUsers(); - final int N = userIds.size(); - for (int i = 0; i < N; i++) { - final int userId = userIds.get(i); + final int[] userIds = mSettings.getUsers(); + for (int userId : userIds) { try { final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); if (tmp != null && tmp.isEnabled()) { @@ -458,6 +543,10 @@ final class OverlayManagerServiceImpl { 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, diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 2262a2e0c208..2cafa39aff6e 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -16,17 +16,11 @@ package com.android.server.om; -import static android.content.om.OverlayInfo.STATE_UNKNOWN; - -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.util.AndroidRuntimeException; import android.util.ArrayMap; -import android.util.Slog; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; @@ -41,23 +35,28 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Data structure representing the current state of all overlay packages in the * system. * - * Modifications to the data are exposed through the ChangeListener interface. + * Modifications to the data are signaled by returning true from any state mutating method. * - * @see ChangeListener * @see OverlayManagerService */ final class OverlayManagerSettings { - private final List<ChangeListener> mListeners = new ArrayList<>(); - + /** + * All overlay data for all users and target packages is stored in this list. + * This keeps memory down, while increasing the cost of running queries or mutating the + * data. This is ok, since changing of overlays is very rare and has larger costs associated + * with it. + * + * The order of the items in the list is important, those with a lower index having a lower + * priority. + */ private final ArrayList<SettingsItem> mItems = new ArrayList<>(); void init(@NonNull final String packageName, final int userId, @@ -68,225 +67,176 @@ final class OverlayManagerSettings { mItems.add(item); } - void remove(@NonNull final String packageName, final int userId) { - final SettingsItem item = select(packageName, userId); - if (item == null) { - return; - } - final OverlayInfo oi = item.getOverlayInfo(); - mItems.remove(item); - if (oi != null) { - notifyOverlayRemoved(oi); + /** + * Returns true if the settings were modified, false if they remain the same. + */ + boolean remove(@NonNull final String packageName, final int userId) { + final int idx = select(packageName, userId); + if (idx < 0) { + return false; } - } - boolean contains(@NonNull final String packageName, final int userId) { - return select(packageName, userId) != null; + mItems.remove(idx); + return true; } OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { - throw new BadKeyException(packageName, userId); - } - return item.getOverlayInfo(); - } - - String getTargetPackageName(@NonNull final String packageName, final int userId) - throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - return item.getTargetPackageName(); + return mItems.get(idx).getOverlayInfo(); } - void setBaseCodePath(@NonNull final String packageName, final int userId, + /** + * Returns true if the settings were modified, false if they remain the same. + */ + boolean setBaseCodePath(@NonNull final String packageName, final int userId, @NonNull final String path) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - item.setBaseCodePath(path); - notifySettingsChanged(); + return mItems.get(idx).setBaseCodePath(path); } boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - return item.isEnabled(); + return mItems.get(idx).isEnabled(); } - void setEnabled(@NonNull final String packageName, final int userId, final boolean enable) + /** + * Returns true if the settings were modified, false if they remain the same. + */ + boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - if (enable == item.isEnabled()) { - return; // nothing to do - } - - item.setEnabled(enable); - notifySettingsChanged(); + return mItems.get(idx).setEnabled(enable); } int getState(@NonNull final String packageName, final int userId) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - return item.getState(); + return mItems.get(idx).getState(); } - void setState(@NonNull final String packageName, final int userId, final int state) + /** + * Returns true if the settings were modified, false if they remain the same. + */ + boolean setState(@NonNull final String packageName, final int userId, final int state) throws BadKeyException { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx < 0) { throw new BadKeyException(packageName, userId); } - final OverlayInfo previous = item.getOverlayInfo(); - item.setState(state); - final OverlayInfo current = item.getOverlayInfo(); - if (previous.state == STATE_UNKNOWN) { - notifyOverlayAdded(current); - notifySettingsChanged(); - } else if (current.state != previous.state) { - notifyOverlayChanged(current, previous); - notifySettingsChanged(); - } + return mItems.get(idx).setState(state); } List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName, final int userId) { - final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId); - if (items.isEmpty()) { - return Collections.emptyList(); - } - final List<OverlayInfo> out = new ArrayList<>(items.size()); - final int N = items.size(); - for (int i = 0; i < N; i++) { - final SettingsItem item = items.get(i); - out.add(item.getOverlayInfo()); - } - return out; + return selectWhereTarget(targetPackageName, userId) + .map(SettingsItem::getOverlayInfo) + .collect(Collectors.toList()); } ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { - final List<SettingsItem> items = selectWhereUser(userId); - if (items.isEmpty()) { - return ArrayMap.EMPTY; - } - final ArrayMap<String, List<OverlayInfo>> out = new ArrayMap<>(items.size()); - final int N = items.size(); - for (int i = 0; i < N; i++) { - final SettingsItem item = items.get(i); - final String targetPackageName = item.getTargetPackageName(); - if (!out.containsKey(targetPackageName)) { - out.put(targetPackageName, new ArrayList<OverlayInfo>()); - } - final List<OverlayInfo> overlays = out.get(targetPackageName); - overlays.add(item.getOverlayInfo()); - } - return out; + return selectWhereUser(userId) + .map(SettingsItem::getOverlayInfo) + .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new, + Collectors.toList())); } - List<String> getTargetPackageNamesForUser(final int userId) { - final List<SettingsItem> items = selectWhereUser(userId); - if (items.isEmpty()) { - return Collections.emptyList(); - } - final List<String> out = new ArrayList<>(); - final int N = items.size(); - for (int i = 0; i < N; i++) { - final SettingsItem item = items.get(i); - final String targetPackageName = item.getTargetPackageName(); - if (!out.contains(targetPackageName)) { - out.add(targetPackageName); - } - } - return out; + int[] getUsers() { + return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray(); } - List<Integer> getUsers() { - final ArrayList<Integer> users = new ArrayList<>(); - final int N = mItems.size(); - for (int i = 0; i < N; i++) { + /** + * Returns true if the settings were modified, false if they remain the same. + */ + boolean removeUser(final int userId) { + boolean removed = false; + for (int i = 0; i < mItems.size(); i++) { final SettingsItem item = mItems.get(i); - if (!users.contains(item.userId)) { - users.add(item.userId); - } - } - return users; - } - - void removeUser(final int userId) { - final Iterator<SettingsItem> iter = mItems.iterator(); - while (iter.hasNext()) { - final SettingsItem item = iter.next(); - if (item.userId == userId) { - iter.remove(); + if (item.getUserId() == userId) { + mItems.remove(i); + removed = true; + i--; } } + return removed; } + /** + * Returns true if the settings were modified, false if they remain the same. + */ boolean setPriority(@NonNull final String packageName, @NonNull final String newParentPackageName, final int userId) { if (packageName.equals(newParentPackageName)) { return false; } - final SettingsItem rowToMove = select(packageName, userId); - if (rowToMove == null) { + final int moveIdx = select(packageName, userId); + if (moveIdx < 0) { return false; } - final SettingsItem newParentRow = select(newParentPackageName, userId); - if (newParentRow == null) { - return false; - } - if (!rowToMove.getTargetPackageName().equals(newParentRow.getTargetPackageName())) { + + final int parentIdx = select(newParentPackageName, userId); + if (parentIdx < 0) { return false; } - mItems.remove(rowToMove); - final ListIterator<SettingsItem> iter = mItems.listIterator(); - while (iter.hasNext()) { - final SettingsItem item = iter.next(); - if (item.userId == userId && item.packageName.equals(newParentPackageName)) { - iter.add(rowToMove); - notifyOverlayPriorityChanged(rowToMove.getOverlayInfo()); - notifySettingsChanged(); - return true; - } + final SettingsItem itemToMove = mItems.get(moveIdx); + + // Make sure both packages are targeting the same package. + if (!itemToMove.getTargetPackageName().equals( + mItems.get(parentIdx).getTargetPackageName())) { + return false; } - Slog.wtf(TAG, "failed to find the parent item a second time"); - return false; + mItems.remove(moveIdx); + final int newParentIdx = select(newParentPackageName, userId); + mItems.add(newParentIdx, itemToMove); + return moveIdx != newParentIdx; } + /** + * Returns true if the settings were modified, false if they remain the same. + */ boolean setLowestPriority(@NonNull final String packageName, final int userId) { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + if (idx <= 0) { + // If the item doesn't exist or is already the lowest, don't change anything. return false; } + + final SettingsItem item = mItems.get(idx); mItems.remove(item); mItems.add(0, item); - notifyOverlayPriorityChanged(item.getOverlayInfo()); - notifySettingsChanged(); return true; } + /** + * Returns true if the settings were modified, false if they remain the same. + */ boolean setHighestPriority(@NonNull final String packageName, final int userId) { - final SettingsItem item = select(packageName, userId); - if (item == null) { + final int idx = select(packageName, userId); + + // If the item doesn't exist or is already the highest, don't change anything. + if (idx < 0 || idx == mItems.size() - 1) { return false; } - mItems.remove(item); + + final SettingsItem item = mItems.get(idx); + mItems.remove(idx); mItems.add(item); - notifyOverlayPriorityChanged(item.getOverlayInfo()); - notifySettingsChanged(); return true; } @@ -296,11 +246,6 @@ final class OverlayManagerSettings { void dump(@NonNull final PrintWriter pw) { pw.println("Settings"); - dumpItems(pw); - dumpListeners(pw); - } - - private void dumpItems(@NonNull final PrintWriter pw) { pw.println(TAB1 + "Items"); if (mItems.isEmpty()) { @@ -312,34 +257,18 @@ final class OverlayManagerSettings { for (int i = 0; i < N; i++) { final SettingsItem item = mItems.get(i); final StringBuilder sb = new StringBuilder(); - sb.append(TAB2 + item.packageName + ":" + item.userId + " {\n"); - sb.append(TAB3 + "packageName.......: " + item.packageName + "\n"); - sb.append(TAB3 + "userId............: " + item.userId + "\n"); - sb.append(TAB3 + "targetPackageName.: " + item.getTargetPackageName() + "\n"); - sb.append(TAB3 + "baseCodePath......: " + item.getBaseCodePath() + "\n"); - sb.append(TAB3 + "state.............: " + OverlayInfo.stateToString(item.getState()) + "\n"); - sb.append(TAB3 + "isEnabled.........: " + item.isEnabled() + "\n"); + sb.append(TAB2 + item.mPackageName + ":" + item.getUserId() + " {\n"); + sb.append(TAB3 + "mPackageName.......: " + item.mPackageName + "\n"); + sb.append(TAB3 + "mUserId............: " + item.getUserId() + "\n"); + sb.append(TAB3 + "mTargetPackageName.: " + item.getTargetPackageName() + "\n"); + sb.append(TAB3 + "mBaseCodePath......: " + item.getBaseCodePath() + "\n"); + sb.append(TAB3 + "mState.............: " + OverlayInfo.stateToString(item.getState()) + "\n"); + sb.append(TAB3 + "mIsEnabled.........: " + item.isEnabled() + "\n"); sb.append(TAB2 + "}"); pw.println(sb.toString()); } } - private void dumpListeners(@NonNull final PrintWriter pw) { - pw.println(TAB1 + "Change listeners"); - - if (mListeners.isEmpty()) { - pw.println(TAB2 + "<none>"); - return; - } - - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener ch = mListeners.get(i); - pw.println(TAB2 + ch); - } - - } - void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException { Serializer.restore(mItems, is); } @@ -434,127 +363,122 @@ final class OverlayManagerSettings { private static void persistRow(@NonNull final FastXmlSerializer xml, @NonNull final SettingsItem item) throws IOException { xml.startTag(null, TAG_ITEM); - XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.packageName); - XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.userId); - XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.targetPackageName); - XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.baseCodePath); - XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.state); - XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.isEnabled); + XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName); + XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId); + XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); + XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); + XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState); + XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); xml.endTag(null, TAG_ITEM); } } private static final class SettingsItem { - private final int userId; - private final String packageName; - private final String targetPackageName; - private String baseCodePath; - private int state; - private boolean isEnabled; - private OverlayInfo cache; + private final int mUserId; + private final String mPackageName; + private final String mTargetPackageName; + private String mBaseCodePath; + private int mState; + private boolean mIsEnabled; + private OverlayInfo mCache; SettingsItem(@NonNull final String packageName, final int userId, @NonNull final String targetPackageName, @NonNull final String baseCodePath, final int state, final boolean isEnabled) { - this.packageName = packageName; - this.userId = userId; - this.targetPackageName = targetPackageName; - this.baseCodePath = baseCodePath; - this.state = state; - this.isEnabled = isEnabled; - cache = null; + mPackageName = packageName; + mUserId = userId; + mTargetPackageName = targetPackageName; + mBaseCodePath = baseCodePath; + mState = state; + mIsEnabled = isEnabled; + mCache = null; } SettingsItem(@NonNull final String packageName, final int userId, @NonNull final String targetPackageName, @NonNull final String baseCodePath) { - this(packageName, userId, targetPackageName, baseCodePath, STATE_UNKNOWN, + this(packageName, userId, targetPackageName, baseCodePath, OverlayInfo.STATE_UNKNOWN, false); } private String getTargetPackageName() { - return targetPackageName; + return mTargetPackageName; + } + + private int getUserId() { + return mUserId; } private String getBaseCodePath() { - return baseCodePath; + return mBaseCodePath; } - private void setBaseCodePath(@NonNull final String path) { - if (!baseCodePath.equals(path)) { - baseCodePath = path; + private boolean setBaseCodePath(@NonNull final String path) { + if (!mBaseCodePath.equals(path)) { + mBaseCodePath = path; invalidateCache(); + return true; } + return false; } private int getState() { - return state; + return mState; } - private void setState(final int state) { - if (this.state != state) { - this.state = state; + private boolean setState(final int state) { + if (mState != state) { + mState = state; invalidateCache(); + return true; } + return false; } private boolean isEnabled() { - return isEnabled; + return mIsEnabled; } - private void setEnabled(final boolean enable) { - if (isEnabled != enable) { - isEnabled = enable; + private boolean setEnabled(final boolean enable) { + if (mIsEnabled != enable) { + mIsEnabled = enable; invalidateCache(); + return true; } + return false; } private OverlayInfo getOverlayInfo() { - if (cache == null) { - cache = new OverlayInfo(packageName, targetPackageName, baseCodePath, - state, userId); + if (mCache == null) { + mCache = new OverlayInfo(mPackageName, mTargetPackageName, mBaseCodePath, mState, + mUserId); } - return cache; + return mCache; } private void invalidateCache() { - cache = null; + mCache = null; } } - private SettingsItem select(@NonNull final String packageName, final int userId) { + private int select(@NonNull final String packageName, final int userId) { final int N = mItems.size(); for (int i = 0; i < N; i++) { final SettingsItem item = mItems.get(i); - if (item.userId == userId && item.packageName.equals(packageName)) { - return item; + if (item.mUserId == userId && item.mPackageName.equals(packageName)) { + return i; } } - return null; + return -1; } - private List<SettingsItem> selectWhereUser(final int userId) { - final ArrayList<SettingsItem> items = new ArrayList<>(); - final int N = mItems.size(); - for (int i = 0; i < N; i++) { - final SettingsItem item = mItems.get(i); - if (item.userId == userId) { - items.add(item); - } - } - return items; + private Stream<SettingsItem> selectWhereUser(final int userId) { + return mItems.stream().filter(item -> item.mUserId == userId); } - private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, + private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, final int userId) { - final ArrayList<SettingsItem> items = new ArrayList<>(); - final int N = mItems.size(); - for (int i = 0; i < N; i++) { - final SettingsItem item = mItems.get(i); - if (item.userId == userId && item.getTargetPackageName().equals(targetPackageName)) { - items.add(item); - } - } - return items; + return selectWhereUser(userId) + .filter(item -> item.getTargetPackageName().equals(targetPackageName)); } private void assertNotNull(@Nullable final Object o) { @@ -563,79 +487,9 @@ final class OverlayManagerSettings { } } - void addChangeListener(@NonNull final ChangeListener listener) { - mListeners.add(listener); - } - - void removeChangeListener(@NonNull final ChangeListener listener) { - mListeners.remove(listener); - } - - private void notifySettingsChanged() { - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener listener = mListeners.get(i); - listener.onSettingsChanged(); - } - } - - private void notifyOverlayAdded(@NonNull final OverlayInfo oi) { - if (DEBUG) { - assertNotNull(oi); - } - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener listener = mListeners.get(i); - listener.onOverlayAdded(oi); - } - } - - private void notifyOverlayRemoved(@NonNull final OverlayInfo oi) { - if (DEBUG) { - assertNotNull(oi); - } - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener listener = mListeners.get(i); - listener.onOverlayRemoved(oi); - } - } - - private void notifyOverlayChanged(@NonNull final OverlayInfo oi, - @NonNull final OverlayInfo oldOi) { - if (DEBUG) { - assertNotNull(oi); - assertNotNull(oldOi); - } - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener listener = mListeners.get(i); - listener.onOverlayChanged(oi, oldOi); - } - } - - private void notifyOverlayPriorityChanged(@NonNull final OverlayInfo oi) { - if (DEBUG) { - assertNotNull(oi); - } - final int N = mListeners.size(); - for (int i = 0; i < N; i++) { - final ChangeListener listener = mListeners.get(i); - listener.onOverlayPriorityChanged(oi); - } - } - - interface ChangeListener { - void onSettingsChanged(); - void onOverlayAdded(@NonNull OverlayInfo oi); - void onOverlayRemoved(@NonNull OverlayInfo oi); - void onOverlayChanged(@NonNull OverlayInfo oi, @NonNull OverlayInfo oldOi); - void onOverlayPriorityChanged(@NonNull OverlayInfo oi); - } - static final class BadKeyException extends RuntimeException { BadKeyException(@NonNull final String packageName, final int userId) { - super("Bad key packageName=" + packageName + " userId=" + userId); + super("Bad key mPackageName=" + packageName + " mUserId=" + userId); } } } |