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

Commit 2a40a813 authored by Mady Mellor's avatar Mady Mellor Committed by Automerger Merge Worker
Browse files

Merge "When a user is removed make sure we remove their persisted bubbles"...

Merge "When a user is removed make sure we remove their persisted bubbles" into tm-dev am: f0f1105d am: dd7397e3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18153123



Change-Id: Ia9cb0ff9d31de4ba8d5fbc1d2227319ad45eeb4b
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 2a82df99 dd7397e3
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
@@ -146,6 +147,7 @@ public class BubbleController {
    private final FloatingContentCoordinator mFloatingContentCoordinator;
    private final BubbleDataRepository mDataRepository;
    private final WindowManagerShellWrapper mWindowManagerShellWrapper;
    private final UserManager mUserManager;
    private final LauncherApps mLauncherApps;
    private final IStatusBarService mBarService;
    private final WindowManager mWindowManager;
@@ -230,6 +232,7 @@ public class BubbleController {
            @Nullable IStatusBarService statusBarService,
            WindowManager windowManager,
            WindowManagerShellWrapper windowManagerShellWrapper,
            UserManager userManager,
            LauncherApps launcherApps,
            TaskStackListenerImpl taskStackListener,
            UiEventLogger uiEventLogger,
@@ -247,8 +250,8 @@ public class BubbleController {
        BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
        return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
                new BubbleDataRepository(context, launcherApps, mainExecutor),
                statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                logger, taskStackListener, organizer, positioner, displayController,
                statusBarService, windowManager, windowManagerShellWrapper, userManager,
                launcherApps, logger, taskStackListener, organizer, positioner, displayController,
                oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
                taskViewTransitions, syncQueue);
    }
@@ -265,6 +268,7 @@ public class BubbleController {
            @Nullable IStatusBarService statusBarService,
            WindowManager windowManager,
            WindowManagerShellWrapper windowManagerShellWrapper,
            UserManager userManager,
            LauncherApps launcherApps,
            BubbleLogger bubbleLogger,
            TaskStackListenerImpl taskStackListener,
@@ -286,6 +290,7 @@ public class BubbleController {
                : statusBarService;
        mWindowManager = windowManager;
        mWindowManagerShellWrapper = windowManagerShellWrapper;
        mUserManager = userManager;
        mFloatingContentCoordinator = floatingContentCoordinator;
        mDataRepository = dataRepository;
        mLogger = bubbleLogger;
@@ -442,6 +447,10 @@ public class BubbleController {

        mOneHandedOptional.ifPresent(this::registerOneHandedState);
        mDragAndDropController.addListener(this::collapseStack);

        // Clear out any persisted bubbles on disk that no longer have a valid user.
        List<UserInfo> users = mUserManager.getAliveUsers();
        mDataRepository.sanitizeBubbles(users);
    }

    @VisibleForTesting
@@ -585,6 +594,17 @@ public class BubbleController {
        mCurrentProfiles = currentProfiles;
    }

    /** Called when a user is removed from the device, including work profiles. */
    public void onUserRemoved(int removedUserId) {
        UserInfo parent = mUserManager.getProfileParent(removedUserId);
        int parentUserId = parent != null ? parent.getUserHandle().getIdentifier() : -1;
        mBubbleData.removeBubblesForUser(removedUserId);
        // Typically calls from BubbleData would remove bubbles from the DataRepository as well,
        // however, this gets complicated when users are removed (mCurrentUserId won't necessarily
        // be correct for this) so we update the repo directly.
        mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
    }

    /** Whether this userId belongs to the current user. */
    private boolean isCurrentProfile(int userId) {
        return userId == UserHandle.USER_ALL
@@ -1828,6 +1848,13 @@ public class BubbleController {
            });
        }

        @Override
        public void onUserRemoved(int removedUserId) {
            mMainExecutor.execute(() -> {
                BubbleController.this.onUserRemoved(removedUserId);
            });
        }

        @Override
        public void onConfigChanged(Configuration newConfig) {
            mMainExecutor.execute(() -> {
+44 −2
Original line number Diff line number Diff line
@@ -465,7 +465,7 @@ public class BubbleData {
                getOverflowBubbles(), invalidBubblesFromPackage, removeBubble);
    }

    /** Dismisses all bubbles from the given package. */
    /** Removes all bubbles from the given package. */
    public void removeBubblesWithPackageName(String packageName, int reason) {
        final Predicate<Bubble> bubbleMatchesPackage = bubble ->
                bubble.getPackageName().equals(packageName);
@@ -477,6 +477,18 @@ public class BubbleData {
        performActionOnBubblesMatching(getOverflowBubbles(), bubbleMatchesPackage, removeBubble);
    }

    /** Removes all bubbles for the given user. */
    public void removeBubblesForUser(int userId) {
        List<Bubble> removedBubbles = filterAllBubbles(bubble ->
                userId == bubble.getUser().getIdentifier());
        for (Bubble b : removedBubbles) {
            doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED);
        }
        if (!removedBubbles.isEmpty()) {
            dispatchPendingChanges();
        }
    }

    private void doAdd(Bubble bubble) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "doAdd: " + bubble);
@@ -552,7 +564,8 @@ public class BubbleData {
                || reason == Bubbles.DISMISS_BLOCKED
                || reason == Bubbles.DISMISS_SHORTCUT_REMOVED
                || reason == Bubbles.DISMISS_PACKAGE_REMOVED
                || reason == Bubbles.DISMISS_USER_CHANGED;
                || reason == Bubbles.DISMISS_USER_CHANGED
                || reason == Bubbles.DISMISS_USER_REMOVED;

        int indexToRemove = indexForKey(key);
        if (indexToRemove == -1) {
@@ -1073,6 +1086,35 @@ public class BubbleData {
        return null;
    }

    /**
     * Returns a list of bubbles that match the provided predicate. This checks all types of
     * bubbles (i.e. pending, suppressed, active, and overflowed).
     */
    private List<Bubble> filterAllBubbles(Predicate<Bubble> predicate) {
        ArrayList<Bubble> matchingBubbles = new ArrayList<>();
        for (Bubble b : mPendingBubbles.values()) {
            if (predicate.test(b)) {
                matchingBubbles.add(b);
            }
        }
        for (Bubble b : mSuppressedBubbles.values()) {
            if (predicate.test(b)) {
                matchingBubbles.add(b);
            }
        }
        for (Bubble b : mBubbles) {
            if (predicate.test(b)) {
                matchingBubbles.add(b);
            }
        }
        for (Bubble b : mOverflowBubbles) {
            if (predicate.test(b)) {
                matchingBubbles.add(b);
            }
        }
        return matchingBubbles;
    }

    @VisibleForTesting(visibility = PRIVATE)
    void setTimeSource(TimeSource timeSource) {
        mTimeSource = timeSource;
+17 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
import com.android.wm.shell.bubbles.storage.BubbleEntity
@@ -73,6 +74,22 @@ internal class BubbleDataRepository(
        if (entities.isNotEmpty()) persistToDisk()
    }

    /**
     * Removes all the bubbles associated with the provided user from memory. Then persists the
     * snapshot to disk asynchronously.
     */
    fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentId: Int) {
        if (volatileRepository.removeBubblesForUser(userId, parentId)) persistToDisk()
    }

    /**
     * Remove any bubbles that don't have a user id from the provided list of users.
     */
    fun sanitizeBubbles(users: List<UserInfo>) {
        val userIds = users.map { u -> u.id }
        if (volatileRepository.sanitizeBubbles(userIds)) persistToDisk()
    }

    private fun transform(bubbles: List<Bubble>): List<BubbleEntity> {
        return bubbles.mapNotNull { b ->
            BubbleEntity(
+9 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public interface Bubbles {
            DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
            DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
            DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
            DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK})
            DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED})
    @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
    @interface DismissReason {}

@@ -76,6 +76,7 @@ public interface Bubbles {
    int DISMISS_PACKAGE_REMOVED = 13;
    int DISMISS_NO_BUBBLE_UP = 14;
    int DISMISS_RELOAD_FROM_DISK = 15;
    int DISMISS_USER_REMOVED = 16;

    /**
     * @return {@code true} if there is a bubble associated with the provided key and if its
@@ -247,6 +248,13 @@ public interface Bubbles {
     */
    void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles);

    /**
     * Called when a user is removed.
     *
     * @param removedUserId the id of the removed user.
     */
    void onUserRemoved(int removedUserId);

    /**
     * Called when config changed.
     *
+55 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.wm.shell.bubbles.storage

import android.annotation.UserIdInt
import android.content.pm.LauncherApps
import android.os.UserHandle
import android.util.SparseArray
@@ -95,10 +96,63 @@ class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
    }

    @Synchronized
    fun removeBubbles(userId: Int, bubbles: List<BubbleEntity>) =
    fun removeBubbles(@UserIdInt userId: Int, bubbles: List<BubbleEntity>) =
            uncache(bubbles.filter { b: BubbleEntity ->
                getEntities(userId).removeIf { e: BubbleEntity -> b.key == e.key } })

    /**
     * Removes all the bubbles associated with the provided userId.
     * @return whether bubbles were removed or not.
     */
    @Synchronized
    fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentUserId: Int): Boolean {
        if (parentUserId != -1) {
            return removeBubblesForUserWithParent(userId, parentUserId)
        } else {
            val entities = entitiesByUser.get(userId)
            entitiesByUser.remove(userId)
            return entities != null
        }
    }

    /**
     * Removes all the bubbles associated with the provided userId when that userId is part of
     * a profile (e.g. managed account).
     *
     * @return whether bubbles were removed or not.
     */
    @Synchronized
    private fun removeBubblesForUserWithParent(
        @UserIdInt userId: Int,
        @UserIdInt parentUserId: Int
    ): Boolean {
        return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity -> b.userId == userId }
    }

    /**
     * Goes through all the persisted bubbles and removes them if the user is not in the active
     * list of users.
     *
     * @return whether the list of bubbles changed or not (i.e. was a removal made).
     */
    @Synchronized
    fun sanitizeBubbles(activeUsers: List<Int>): Boolean {
        for (i in 0 until entitiesByUser.size()) {
            // First check if the user is a parent / top-level user
            val parentUserId = entitiesByUser.keyAt(i)
            if (!activeUsers.contains(parentUserId)) {
                return removeBubblesForUser(parentUserId, -1)
            } else {
                // Then check if each of the bubbles in the top-level user, still has a valid user
                // as it could belong to a profile and have a different id from the parent.
                return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity ->
                    !activeUsers.contains(b.userId)
                }
            }
        }
        return false
    }

    private fun cache(bubbles: List<BubbleEntity>) {
        bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
            launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
Loading