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

Commit 91498abf authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Moving install queue updates to worker thread.

This avoids acquiring a lock for upating the sharedPrefs during onResume
as all the logic runs on a single thread.

Bug: 67305604
Change-Id: I1bbea382da9fafb403b4e9508f393f78db28478d
parent 22b92df3
Loading
Loading
Loading
Loading
+87 −100
Original line number Diff line number Diff line
@@ -27,7 +27,9 @@ import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -61,6 +63,9 @@ import java.util.Set;

public class InstallShortcutReceiver extends BroadcastReceiver {

    private static final int MSG_ADD_TO_QUEUE = 1;
    private static final int MSG_FLUSH_QUEUE = 2;

    public static final int FLAG_ACTIVITY_PAUSED = 1;
    public static final int FLAG_LOADER_RUNNING = 2;
    public static final int FLAG_DRAG_AND_DROP = 4;
@@ -93,28 +98,74 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
    public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
    public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;

    private static final Object sLock = new Object();
    private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) {

    private static void addToInstallQueue(
            SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) {
        synchronized(sLock) {
            String encoded = info.encodeToString();
            if (encoded != null) {
                Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ADD_TO_QUEUE: {
                    Pair<Context, PendingInstallShortcutInfo> pair =
                            (Pair<Context, PendingInstallShortcutInfo>) msg.obj;
                    String encoded = pair.second.encodeToString();
                    SharedPreferences prefs = Utilities.getPrefs(pair.first);
                    Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
                    strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
                    strings.add(encoded);
                sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
                    prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
                    return;
                }
                case MSG_FLUSH_QUEUE: {
                    Context context = (Context) msg.obj;
                    LauncherModel model = LauncherAppState.getInstance(context).getModel();
                    if (model.getCallback() == null) {
                        // Launcher not loaded
                        return;
                    }

                    ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
                    SharedPreferences prefs = Utilities.getPrefs(context);
                    Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
                    if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
                    if (strings == null) {
                        return;
                    }

                    LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
                    for (String encoded : strings) {
                        PendingInstallShortcutInfo info = decode(encoded, context);
                        if (info == null) {
                            continue;
                        }

                        String pkg = getIntentPackage(info.launchIntent);
                        if (!TextUtils.isEmpty(pkg)
                                && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
                            if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
                                    + info.launchIntent);
                            continue;
                        }

                        // Generate a shortcut info to add into the model
                        installQueue.add(info.getItemInfo());
                    }
                    prefs.edit().remove(APPS_PENDING_INSTALL).apply();
                    if (!installQueue.isEmpty()) {
                        model.addAndBindAddedWorkspaceItems(installQueue);
                    }
                    return;
                }
            }
        }
    };

    public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
            UserHandle user) {
        if (packageNames.isEmpty()) {
            return;
        }
        Preconditions.assertWorkerThread();

        SharedPreferences sp = Utilities.getPrefs(context);
        synchronized(sLock) {
        Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
        if (DBG) {
            Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
@@ -140,27 +191,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
        }
        sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
    }
    }

    private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(Context context) {
        SharedPreferences sharedPrefs = Utilities.getPrefs(context);
        synchronized(sLock) {
            ArrayList<PendingInstallShortcutInfo> infos = new ArrayList<>();
            Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
            if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
            if (strings == null) {
                return infos;
            }
            for (String encoded : strings) {
                PendingInstallShortcutInfo info = decode(encoded, context);
                if (info != null) {
                    infos.add(info);
                }
            }
            sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).apply();
            return infos;
        }
    }

    public void onReceive(Context context, Intent data) {
        if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
@@ -256,7 +286,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {

    private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
        // Queue the item up for adding if launcher has not loaded properly yet
        addToInstallQueue(Utilities.getPrefs(context), info);
        Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget();
        flushInstallQueue(context);
    }

@@ -269,17 +299,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
    }

    static void flushInstallQueue(Context context) {
        LauncherModel model = LauncherAppState.getInstance(context).getModel();
        boolean launcherNotLoaded = model.getCallback() == null;
        if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
        if (sInstallQueueDisabledFlags != 0) {
            return;
        }

        ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
        if (!items.isEmpty()) {
            model.addAndBindAddedWorkspaceItems(
                    new LazyShortcutsProvider(context.getApplicationContext(), items));
        }
        Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget();
    }

    /**
@@ -601,42 +624,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
        return new PendingInstallShortcutInfo(info, original.mContext);
    }

    private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> {

        private final Context mContext;
        private final ArrayList<PendingInstallShortcutInfo> mPendingItems;

        public LazyShortcutsProvider(Context context, ArrayList<PendingInstallShortcutInfo> items) {
            mContext = context;
            mPendingItems = items;
        }

        /**
         * This must be called on the background thread as this requires multiple calls to
         * packageManager and icon cache.
         */
        @Override
        public ArrayList<Pair<ItemInfo, Object>> get() {
            Preconditions.assertNonUiThread();
            ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
            for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
                // If the intent specifies a package, make sure the package exists
                String packageName = getIntentPackage(pendingInfo.launchIntent);
                if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile(
                        packageName, pendingInfo.user)) {
                    if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
                            + pendingInfo.launchIntent);
                    continue;
                }

                // Generate a shortcut info to add into the model
                installQueue.add(pendingInfo.getItemInfo());
            }
            return installQueue;
        }
    }

    private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
        Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+2 −3
Original line number Diff line number Diff line
@@ -192,9 +192,8 @@ public class LauncherModel extends BroadcastReceiver
    /**
     * Adds the provided items to the workspace.
     */
    public void addAndBindAddedWorkspaceItems(
            Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
        enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
    public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
        enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
    }

    public ModelWriter getWriter(boolean hasVerticalHotseat) {
+6 −8
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.List;

@@ -47,19 +46,18 @@ import java.util.List;
 */
public class AddWorkspaceItemsTask extends BaseModelUpdateTask {

    private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider;
    private final List<Pair<ItemInfo, Object>> mItemList;

    /**
     * @param appsProvider items to add on the workspace
     * @param itemList items to add on the workspace
     */
    public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
        mAppsProvider = appsProvider;
    public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
        mItemList = itemList;
    }

    @Override
    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
        List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get();
        if (workspaceApps.isEmpty()) {
        if (mItemList.isEmpty()) {
            return;
        }
        Context context = app.getContext();
@@ -75,7 +73,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
        synchronized(dataModel) {

            List<ItemInfo> filteredItems = new ArrayList<>();
            for (Pair<ItemInfo, Object> entry : workspaceApps) {
            for (Pair<ItemInfo, Object> entry : mItemList) {
                ItemInfo item = entry.first;
                if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                        item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+1 −1
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
        for (ItemInfo item : items) {
            list.add(Pair.create(item, null));
        }
        return new AddWorkspaceItemsTask(Provider.of(list)) {
        return new AddWorkspaceItemsTask(list) {

            @Override
            protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }