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

Commit 3bbbabc5 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Fixing model being updated on UI thread

> When package gets unavailable or suspended, the disabled flag was getting
updated on the UI thread. This could lead to inconsistent model if launcher
activity didn't exist.
> Fixing: When unsuspending one work profile apps, all work profile apps get
unsuspended

Bug: 27673573,27673373,27403236
Change-Id: I7fde3f79c36204e73ca66ccf8431fa0f0cce3d08
parent b8f00da8
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.FlagOp;

import java.util.ArrayList;
import java.util.HashSet;
@@ -119,19 +120,15 @@ class AllAppsList {
    }

    /**
     * Suspend the apps for the given apk identified by packageName.
     * Updates the apps for the given packageName and user based on {@param op}.
     */
    public void suspendPackage(String packageName, UserHandleCompat user, boolean suspend) {
    public void updatePackageFlags(String packageName, UserHandleCompat user, FlagOp op) {
        final List<AppInfo> data = this.data;
        for (int i = data.size() - 1; i >= 0; i--) {
            AppInfo info = data.get(i);
            final ComponentName component = info.intent.getComponent();
            if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
                if (suspend) {
                    info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                } else {
                    info.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                }
                info.isDisabled = op.apply(info.isDisabled);
                modified.add(info);
            }
        }
+1 −1
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
        }
    }

    public static void removeFromInstallQueue(Context context, ArrayList<String> packageNames,
    public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
            UserHandleCompat user) {
        if (packageNames.isEmpty()) {
            return;
+25 −24
Original line number Diff line number Diff line
@@ -4322,7 +4322,7 @@ public class Launcher extends Activity
            }
            mWorkspace.removeItemsByComponentName(removedComponents, user);
            // Notify the drag controller
            mDragController.onAppsRemoved(new ArrayList<String>(), removedComponents);
            mDragController.onAppsRemoved(new HashSet<String>(), removedComponents);
        }
    }

@@ -4346,43 +4346,44 @@ public class Launcher extends Activity
    }

    /**
     * A package was uninstalled.  We take both the super set of packageNames
     * A package was uninstalled/updated.  We take both the super set of packageNames
     * in addition to specific applications to remove, the reason being that
     * this can be called when a package is updated as well.  In that scenario,
     * we only remove specific components from the workspace, where as
     * we only remove specific components from the workspace and hotseat, where as
     * package-removal should clear all items by package name.
     *
     * @param reason if non-zero, the icons are not permanently removed, rather marked as disabled.
     * Implementation of the method from LauncherModel.Callbacks.
     */
    @Override
    public void bindComponentsRemoved(final ArrayList<String> packageNames,
            final ArrayList<AppInfo> appInfos, final UserHandleCompat user, final int reason) {
    public void bindWorkspaceComponentsRemoved(
            final HashSet<String> packageNames, final HashSet<ComponentName> components,
            final UserHandleCompat user) {
        Runnable r = new Runnable() {
            public void run() {
                bindComponentsRemoved(packageNames, appInfos, user, reason);
                bindWorkspaceComponentsRemoved(packageNames, components, user);
            }
        };
        if (waitUntilResume(r)) {
            return;
        }

        if (reason == 0) {
            HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
            for (AppInfo info : appInfos) {
                removedComponents.add(info.componentName);
            }
        if (!packageNames.isEmpty()) {
            mWorkspace.removeItemsByPackageName(packageNames, user);
        }
            if (!removedComponents.isEmpty()) {
                mWorkspace.removeItemsByComponentName(removedComponents, user);
        if (!components.isEmpty()) {
            mWorkspace.removeItemsByComponentName(components, user);
        }
        // Notify the drag controller
            mDragController.onAppsRemoved(packageNames, removedComponents);
        mDragController.onAppsRemoved(packageNames, components);

        } else {
            mWorkspace.disableShortcutsByPackageName(packageNames, user, reason);
    }

    @Override
    public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
        Runnable r = new Runnable() {
            public void run() {
                bindAppInfosRemoved(appInfos);
            }
        };
        if (waitUntilResume(r)) {
            return;
        }

        // Update AllApps
+62 −50
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.CursorIconInfo;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
@@ -195,8 +196,10 @@ public class LauncherModel extends BroadcastReceiver
                ArrayList<ShortcutInfo> removed, UserHandleCompat user);
        public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
        public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
        public void bindComponentsRemoved(ArrayList<String> packageNames,
                        ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
        public void bindWorkspaceComponentsRemoved(
                HashSet<String> packageNames, HashSet<ComponentName> components,
                UserHandleCompat user);
        public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
        public void notifyWidgetProvidersChanged();
        public void bindWidgetsModel(WidgetsModel model);
        public void bindSearchProviderChanged();
@@ -2928,6 +2931,7 @@ public class LauncherModel extends BroadcastReceiver

            final String[] packages = mPackages;
            final int N = packages.length;
            FlagOp flagOp = FlagOp.NO_OP;
            switch (mOp) {
                case OP_ADD: {
                    for (int i=0; i<N; i++) {
@@ -2949,6 +2953,8 @@ public class LauncherModel extends BroadcastReceiver
                        mBgAllAppsList.updatePackage(context, packages[i], mUser);
                        mApp.getWidgetCache().removePackage(packages[i], mUser);
                    }
                    // Since package was just updated, the target must be available now.
                    flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                    break;
                case OP_REMOVE: {
                    ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
@@ -2967,14 +2973,16 @@ public class LauncherModel extends BroadcastReceiver
                        mBgAllAppsList.removePackage(packages[i], mUser);
                        mApp.getWidgetCache().removePackage(packages[i], mUser);
                    }
                    flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                    break;
                case OP_SUSPEND:
                case OP_UNSUSPEND:
                    boolean suspend = mOp == OP_SUSPEND;
                    flagOp = mOp == OP_SUSPEND ?
                            FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
                                    FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
                    for (int i=0; i<N; i++) {
                        if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.suspendPackage "
                                + suspend + " " + packages[i]);
                        mBgAllAppsList.suspendPackage(packages[i], mUser, suspend);
                        if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.(un)suspend " + packages[i]);
                        mBgAllAppsList.updatePackageFlags(packages[i], mUser, flagOp);
                    }
                    break;
            }
@@ -2984,11 +2992,11 @@ public class LauncherModel extends BroadcastReceiver
            final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();

            if (mBgAllAppsList.added.size() > 0) {
                added = new ArrayList<AppInfo>(mBgAllAppsList.added);
                added = new ArrayList<>(mBgAllAppsList.added);
                mBgAllAppsList.added.clear();
            }
            if (mBgAllAppsList.modified.size() > 0) {
                modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
                modified = new ArrayList<>(mBgAllAppsList.modified);
                mBgAllAppsList.modified.clear();
            }
            if (mBgAllAppsList.removed.size() > 0) {
@@ -2996,14 +3004,7 @@ public class LauncherModel extends BroadcastReceiver
                mBgAllAppsList.removed.clear();
            }

            final Callbacks callbacks = getCallback();
            if (callbacks == null) {
                Log.w(TAG, "Nobody to tell about the new app.  Launcher is probably loading.");
                return;
            }

            final HashMap<ComponentName, AppInfo> addedOrUpdatedApps =
                    new HashMap<ComponentName, AppInfo>();
            final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();

            if (added != null) {
                addAppsToAllApps(context, added);
@@ -3013,6 +3014,7 @@ public class LauncherModel extends BroadcastReceiver
            }

            if (modified != null) {
                final Callbacks callbacks = getCallback();
                final ArrayList<AppInfo> modifiedFinal = modified;
                for (AppInfo ai : modified) {
                    addedOrUpdatedApps.put(ai.componentName, ai);
@@ -3029,7 +3031,7 @@ public class LauncherModel extends BroadcastReceiver
            }

            // Update shortcut infos
            if (mOp == OP_ADD || mOp == OP_UPDATE || mOp == OP_UNSUSPEND) {
            if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
                final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
                final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
                final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
@@ -3042,11 +3044,6 @@ public class LauncherModel extends BroadcastReceiver
                            boolean infoUpdated = false;
                            boolean shortcutUpdated = false;

                            if (mOp == OP_UNSUSPEND) {
                                si.isDisabled &= ~ ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                                infoUpdated = true;
                            }

                            // Update shortcuts which use iconResource.
                            if ((si.iconResource != null)
                                    && packageSet.contains(si.iconResource.packageName)) {
@@ -3109,9 +3106,9 @@ public class LauncherModel extends BroadcastReceiver
                                    infoUpdated = true;
                                }

                                if ((si.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
                                    // Since package was just updated, the target must be available now.
                                    si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
                                int oldDisabledFlags = si.isDisabled;
                                si.isDisabled = flagOp.apply(si.isDisabled);
                                if (si.isDisabled != oldDisabledFlags) {
                                    shortcutUpdated = true;
                                }
                            }
@@ -3144,6 +3141,7 @@ public class LauncherModel extends BroadcastReceiver
                }

                if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
                    final Callbacks callbacks = getCallback();
                    mHandler.post(new Runnable() {

                        public void run() {
@@ -3159,6 +3157,7 @@ public class LauncherModel extends BroadcastReceiver
                    }
                }
                if (!widgets.isEmpty()) {
                    final Callbacks callbacks = getCallback();
                    mHandler.post(new Runnable() {
                        public void run() {
                            Callbacks cb = getCallback();
@@ -3170,48 +3169,60 @@ public class LauncherModel extends BroadcastReceiver
                }
            }

            final ArrayList<String> removedPackageNames =
                    new ArrayList<String>();
            if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE || mOp == OP_SUSPEND) {
            final HashSet<String> removedPackages = new HashSet<>();
            final HashSet<ComponentName> removedComponents = new HashSet<>();
            if (mOp == OP_REMOVE) {
                // Mark all packages in the broadcast to be removed
                removedPackageNames.addAll(Arrays.asList(packages));
                Collections.addAll(removedPackages, packages);

                // No need to update the removedComponents as
                // removedPackages is a super-set of removedComponents
            } else if (mOp == OP_UPDATE) {
                // Mark disabled packages in the broadcast to be removed
                for (int i=0; i<N; i++) {
                    if (isPackageDisabled(context, packages[i], mUser)) {
                        removedPackageNames.add(packages[i]);
                        removedPackages.add(packages[i]);
                    }
                }

                // Update removedComponents as some components can get removed during package update
                for (AppInfo info : removedApps) {
                    removedComponents.add(info.componentName);
                }
            }

            if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
                final int removeReason;
                if (mOp == OP_UNAVAILABLE) {
                    removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
                } else if (mOp == OP_SUSPEND) {
                    removeReason = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                } else {
                    // Remove all the components associated with this package
                    for (String pn : removedPackageNames) {
            if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
                for (String pn : removedPackages) {
                    deletePackageFromDatabase(context, pn, mUser);
                }
                    // Remove all the specific components
                    for (AppInfo a : removedApps) {
                        ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
                        deleteItemsFromDatabase(context, infos);
                    }
                    removeReason = 0;
                for (ComponentName cn : removedComponents) {
                    deleteItemsFromDatabase(context, getItemInfoForComponentName(cn, mUser));
                }

                // Remove any queued items from the install queue
                InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
                InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);

                // Call the components-removed callback
                final Callbacks callbacks = getCallback();
                mHandler.post(new Runnable() {
                    public void run() {
                        Callbacks cb = getCallback();
                        if (callbacks == cb && cb != null) {
                            callbacks.bindComponentsRemoved(
                                    removedPackageNames, removedApps, mUser, removeReason);
                            callbacks.bindWorkspaceComponentsRemoved(
                                    removedPackages, removedComponents, mUser);
                        }
                    }
                });
            }

            if (!removedApps.isEmpty()) {
                // Remove corresponding apps from All-Apps
                final Callbacks callbacks = getCallback();
                mHandler.post(new Runnable() {
                    public void run() {
                        Callbacks cb = getCallback();
                        if (callbacks == cb && cb != null) {
                            callbacks.bindAppInfosRemoved(removedApps);
                        }
                    }
                });
@@ -3221,6 +3232,7 @@ public class LauncherModel extends BroadcastReceiver
            // get widget update signals.
            if (!Utilities.ATLEAST_MARSHMALLOW &&
                    (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
                final Callbacks callbacks = getCallback();
                mHandler.post(new Runnable() {
                    public void run() {
                        Callbacks cb = getCallback();
+2 −33
Original line number Diff line number Diff line
@@ -3922,41 +3922,10 @@ public class Workspace extends PagedView
        });
    }

    public void disableShortcutsByPackageName(final ArrayList<String> packages,
            final UserHandleCompat user, final int reason) {
        final HashSet<String> packageNames = new HashSet<String>();
        packageNames.addAll(packages);

        mapOverItems(MAP_RECURSE, new ItemOperator() {
            @Override
            public boolean evaluate(ItemInfo info, View v, View parent) {
                if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
                    ShortcutInfo shortcutInfo = (ShortcutInfo) info;
                    ComponentName cn = shortcutInfo.getTargetComponent();
                    if (user.equals(shortcutInfo.user) && cn != null
                            && packageNames.contains(cn.getPackageName())) {
                        shortcutInfo.isDisabled |= reason;
                        BubbleTextView shortcut = (BubbleTextView) v;
                        shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);

                        if (parent != null) {
                            parent.invalidate();
                        }
                    }
                }
                // process all the shortcuts
                return false;
            }
        });
    }

    // Removes ALL items that match a given package name, this is usually called when a package
    // has been removed and we want to remove all components (widgets, shortcuts, apps) that
    // belong to that package.
    void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
        final HashSet<String> packageNames = new HashSet<String>();
        packageNames.addAll(packages);

    void removeItemsByPackageName(final HashSet<String> packageNames, final UserHandleCompat user) {
        // Filter out all the ItemInfos that this is going to affect
        final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
        final HashSet<ComponentName> cns = new HashSet<ComponentName>();
@@ -4138,7 +4107,7 @@ public class Workspace extends PagedView
    }

    public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
        ArrayList<String> packages = new ArrayList<String>(1);
        HashSet<String> packages = new HashSet<>(1);
        packages.add(packageName);
        LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
        removeItemsByPackageName(packages, user);
Loading