Merge "OverlayManagerService: Make broadcasts/updates explicit" into oc-dev
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5415eb5..b382cef 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3216,13 +3216,6 @@
             "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 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 2026c1b..818c3ad 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 @@
  * </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 @@
         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 @@
 
             restoreSettings();
             onSwitchUser(UserHandle.USER_SYSTEM);
-            schedulePersistSettings();
-
-            mSettings.addChangeListener(new OverlayChangeListener());
 
             publishBinderService(Context.OVERLAY_SERVICE, mService);
             publishLocalService(OverlayManagerService.class, this);
@@ -281,8 +275,9 @@
         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 @@
                 @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 @@
                 @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 @@
                 @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 @@
 
     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 @@
                 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 @@
         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 @@
                 } 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 @@
         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 @@
         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 @@
             // 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 3705946..c536278 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 @@
     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 @@
                 iter.remove();
             }
         }
-
         return new ArrayList<>(packagesToUpdateAssets);
     }
 
@@ -199,25 +201,30 @@
         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 @@
         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 @@
         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 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 @@
 
         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 @@
 
     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 @@
         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 @@
                             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 @@
         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 @@
         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 2262a2e..2cafa39 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.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 @@
         mItems.add(item);
     }
 
-    void remove(@NonNull final String packageName, final int userId) {
-        final SettingsItem item = select(packageName, userId);
-        if (item == null) {
-            return;
+    /**
+     * 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;
         }
-        final OverlayInfo oi = item.getOverlayInfo();
-        mItems.remove(item);
-        if (oi != null) {
-            notifyOverlayRemoved(oi);
-        }
-    }
 
-    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) {
+        final int idx = select(packageName, userId);
+        if (idx < 0) {
             throw new BadKeyException(packageName, userId);
         }
-        return item.getOverlayInfo();
+        return mItems.get(idx).getOverlayInfo();
     }
 
-    String getTargetPackageName(@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.getTargetPackageName();
-    }
-
-    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);
+            if (item.getUserId() == userId) {
+                mItems.remove(i);
+                removed = true;
+                i--;
             }
         }
-        return users;
+        return removed;
     }
 
-    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();
-            }
-        }
-    }
-
+    /**
+     * 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) {
-            return false;
-        }
-        final SettingsItem newParentRow = select(newParentPackageName, userId);
-        if (newParentRow == null) {
-            return false;
-        }
-        if (!rowToMove.getTargetPackageName().equals(newParentRow.getTargetPackageName())) {
+        final int moveIdx = select(packageName, userId);
+        if (moveIdx < 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 int parentIdx = select(newParentPackageName, userId);
+        if (parentIdx < 0) {
+            return false;
         }
 
-        Slog.wtf(TAG, "failed to find the parent item a second time");
-        return false;
+        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;
+        }
+
+        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 @@
 
     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 @@
         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 @@
         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 @@
         }
     }
 
-    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);
         }
     }
 }