Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c745f421 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

OverlayManagerService: Make broadcasts/updates explicit

Previously there was a listener attached to the Settings object
which would fire any time a single settings change was made.
This made it very inefficient to do batch updates.

This change makes the Settings object return its mutated status
on each call to a mutating method, allowing the caller to keep track
of whether or not to notify the listener of any changes.

This allows for the implementation of setEnabledExclusive, where
all but the target overlay are disabled and only a single notification
/ update is sent out.

Bug: 36099320
Test: manual (with OverlayManagerService.DEBUG = true), observe logcat
Test: when Going to Settings -> Display -> Advanced -> Themes and
Test: selecting a theme.
Change-Id: Ic8b8ca3ba0cf5d2d682bf6dac5a6c82e4f0f2502
parent ba2e284a
Loading
Loading
Loading
Loading
+0 −23
Original line number Diff line number Diff line
@@ -3188,13 +3188,6 @@ public class Intent implements Parcelable, Cloneable {
    public static final String ACTION_MEDIA_RESOURCE_GRANTED =
            "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
@@ -3206,22 +3199,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
+52 −90
Original line number Diff line number Diff line
@@ -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();
            FgThread.getHandler().post(() -> {
                synchronized (mLock) {
                    updateAssetsLocked(userId, targetPackageName);
                }

        @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);
                }
                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,10 +723,11 @@ public final class OverlayManagerService extends SystemService {
        if (mPersistSettingsScheduled.getAndSet(true)) {
            return;
        }
        IoThread.getHandler().post(new Runnable() {
            @Override
            public void run() {
        IoThread.getHandler().post(() -> {
            mPersistSettingsScheduled.set(false);
            if (DEBUG) {
                Slog.d(TAG, "Writing overlay settings");
            }
            synchronized (mLock) {
                FileOutputStream stream = null;
                try {
@@ -777,7 +739,6 @@ public final class OverlayManagerService extends SystemService {
                    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
            }
+122 −33
Original line number Diff line number Diff line
@@ -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;
                }

            setEnabled(packageName, enable, userId);
                // Disable the overlay.
                modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
                modified |= updateState(targetPackage, disabledOverlayPackageInfo, 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,7 +462,10 @@ 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 {
        // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
@@ -388,7 +474,7 @@ final class OverlayManagerServiceImpl {
            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,
+173 −319

File changed.

Preview size limit exceeded, changes collapsed.