Merge "Moving LoaderTask static" into ub-launcher3-dorval-polish
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index db8f5dd..923835a 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -1,9 +1,14 @@
 package com.android.launcher3;
 
 import android.content.ComponentName;
+import android.content.Context;
 
 public class AppFilter {
 
+    public static AppFilter newInstance(Context context) {
+        return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+    }
+
     public boolean shouldShowApp(ComponentName app) {
         return true;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0075887..909853e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3970,7 +3970,7 @@
      *                    refreshes the widgets and shortcuts associated with the given package/user
      */
     public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
-        mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
+        mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
     }
 
     public void lockScreenOrientation() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 27ccabe..cf20feb 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -93,9 +93,7 @@
         mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-
-        mModel = new LauncherModel(this, mIconCache,
-                Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
+        mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
 
         LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ee06d9e..265c7e3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -69,7 +69,6 @@
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.provider.ImportDataTask;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -116,7 +115,6 @@
     @Thunk final Object mLock = new Object();
     @Thunk LoaderTask mLoaderTask;
     @Thunk boolean mIsLoaderTaskRunning;
-    @Thunk boolean mHasLoaderCompletedOnce;
 
     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
     static {
@@ -139,23 +137,6 @@
 
     // < only access in worker thread >
     private final AllAppsList mBgAllAppsList;
-    // Entire list of widgets.
-    private final WidgetsModel mBgWidgetsModel;
-
-    private boolean mHasShortcutHostPermission;
-    // Runnable to check if the shortcuts permission has changed.
-    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mModelLoaded) {
-                boolean hasShortcutHostPermission =
-                        DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
-                if (hasShortcutHostPermission != mHasShortcutHostPermission) {
-                    forceReload();
-                }
-            }
-        }
-    };
 
     /**
      * All the static data should be accessed on the background thread, A lock should be acquired
@@ -163,12 +144,19 @@
      */
     static final BgDataModel sBgDataModel = new BgDataModel();
 
-    // </ only access in worker thread >
-
-    private final IconCache mIconCache;
-
-    private final LauncherAppsCompat mLauncherApps;
-    private final UserManagerCompat mUserManager;
+    // Runnable to check if the shortcuts permission has changed.
+    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mModelLoaded) {
+                boolean hasShortcutHostPermission =
+                        DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
+                if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) {
+                    forceReload();
+                }
+            }
+        }
+    };
 
     public interface Callbacks {
         public boolean setLoadOnResume();
@@ -204,14 +192,8 @@
     }
 
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
-        Context context = app.getContext();
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
-        mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
-        mIconCache = iconCache;
-
-        mLauncherApps = LauncherAppsCompat.getInstance(context);
-        mUserManager = UserManagerCompat.getInstance(context);
     }
 
     /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
@@ -539,9 +521,10 @@
                     // issues that arise from that.
                     loaderResults.bindAllApps();
                     loaderResults.bindDeepShortcuts();
+                    loaderResults.bindWidgets();
                     return true;
                 } else {
-                    mLoaderTask = new LoaderTask(mApp.getContext(), loaderResults);
+                    mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, loaderResults);
                     sWorker.post(mLoaderTask);
                 }
             }
@@ -588,6 +571,44 @@
         });
     }
 
+    public class LoaderTransaction implements AutoCloseable {
+
+        private final LoaderTask mTask;
+
+        private LoaderTransaction(LoaderTask task) throws CancellationException {
+            synchronized (mLock) {
+                if (mLoaderTask != task) {
+                    throw new CancellationException("Loader already stopped");
+                }
+                mTask = task;
+                mIsLoaderTaskRunning = true;
+                mModelLoaded = false;
+            }
+        }
+
+        public void commit() {
+            synchronized (mLock) {
+                // Everything loaded bind the data.
+                mModelLoaded = true;
+            }
+        }
+
+        @Override
+        public void close() {
+            synchronized (mLock) {
+                // If we are still the last one to be scheduled, remove ourselves.
+                if (mLoaderTask == mTask) {
+                    mLoaderTask = null;
+                }
+                mIsLoaderTaskRunning = false;
+            }
+        }
+    }
+
+    public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+        return new LoaderTransaction(task);
+    }
+
     /**
      * Runnable for the thread that loads the contents of the launcher:
      *   - workspace icons
@@ -595,46 +616,62 @@
      *   - all apps icons
      *   - deep shortcuts within apps
      */
-    private class LoaderTask implements Runnable {
-        private Context mContext;
+    private static class LoaderTask implements Runnable {
+        private final LauncherAppState mApp;
+        private final AllAppsList mBgAllAppsList;
+        private final BgDataModel mBgDataModel;
+
         private final LoaderResults mResults;
 
+        private final LauncherAppsCompat mLauncherApps;
+        private final UserManagerCompat mUserManager;
+        private final DeepShortcutManager mShortcutManager;
+        private final PackageInstallerCompat mPackageInstaller;
+        private final AppWidgetManagerCompat mAppWidgetManager;
+        private final IconCache mIconCache;
+
         private boolean mStopped;
 
-        LoaderTask(Context context, LoaderResults results) {
-            mContext = context;
+        LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
+                LoaderResults results) {
+            mApp = app;
+            mBgAllAppsList = bgAllAppsList;
+            mBgDataModel = dataModel;
             mResults = results;
+
+            mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+            mUserManager = UserManagerCompat.getInstance(mApp.getContext());
+            mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
+            mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
+            mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
+            mIconCache = mApp.getIconCache();
         }
 
-        private void waitForIdle() {
+        private synchronized void waitForIdle() {
             // Wait until the either we're stopped or the other threads are done.
             // This way we don't start loading all apps until the workspace has settled
             // down.
-            synchronized (LoaderTask.this) {
-                LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
-                // Just in case mFlushingWorkerThread changes but we aren't woken up,
-                // wait no longer than 1sec at a time
-                while (!mStopped && idleLock.awaitLocked(1000));
-            }
+            LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
+            // Just in case mFlushingWorkerThread changes but we aren't woken up,
+            // wait no longer than 1sec at a time
+            while (!mStopped && idleLock.awaitLocked(1000));
         }
 
-        private void verifyNotStopped() throws CancellationException {
-            synchronized (LoaderTask.this) {
-                if (mStopped) {
-                    throw new CancellationException("Loader stopped");
-                }
+        private synchronized void verifyNotStopped() throws CancellationException {
+            if (mStopped) {
+                throw new CancellationException("Loader stopped");
             }
         }
 
         public void run() {
-            synchronized (mLock) {
+            synchronized (this) {
+                // Skip fast if we are already stopped.
                 if (mStopped) {
                     return;
                 }
-                mIsLoaderTaskRunning = true;
             }
 
-            try {
+            try (LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
                 long now = 0;
                 if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
                 loadWorkspace();
@@ -688,36 +725,21 @@
 
                 // fourth step
                 if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
-                refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
-                        null /* packageUser */);
+                mBgDataModel.widgetsModel.update(mApp, null);
 
-                synchronized (mLock) {
-                    // Everything loaded bind the data.
-                    mModelLoaded = true;
-                    mHasLoaderCompletedOnce = true;
-                }
+                verifyNotStopped();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+                mResults.bindWidgets();
+
+                transaction.commit();
             } catch (CancellationException e) {
               // Loader stopped, ignore
-            } finally {
-                // Clear out this reference, otherwise we end up holding it until all of the
-                // callback runnables are done.
-                mContext = null;
-
-                synchronized (mLock) {
-                    // If we are still the last one to be scheduled, remove ourselves.
-                    if (mLoaderTask == this) {
-                        mLoaderTask = null;
-                    }
-                    mIsLoaderTaskRunning = false;
-                }
             }
         }
 
-        public void stopLocked() {
-            synchronized (LoaderTask.this) {
-                mStopped = true;
-                this.notify();
-            }
+        public synchronized void stopLocked() {
+            mStopped = true;
+            this.notify();
         }
 
         private void loadWorkspace() {
@@ -725,12 +747,10 @@
                 Trace.beginSection("Loading Workspace");
             }
 
-            final Context context = mContext;
+            final Context context = mApp.getContext();
             final ContentResolver contentResolver = context.getContentResolver();
             final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
             final boolean isSafeMode = pmHelper.isSafeMode();
-            final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-            final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
             final boolean isSdCardReady = Utilities.isBootCompleted();
             final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
 
@@ -743,7 +763,7 @@
             }
 
             if (!clearDb && GridSizeMigrationTask.ENABLED &&
-                    !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
+                    !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
                 // Migration failed. Clear workspace.
                 clearDb = true;
             }
@@ -758,12 +778,12 @@
             LauncherSettings.Settings.call(contentResolver,
                     LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
 
-            synchronized (sBgDataModel) {
-                sBgDataModel.clear();
+            synchronized (mBgDataModel) {
+                mBgDataModel.clear();
 
-                final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
-                        .getInstance(mContext).updateAndGetActiveSessionCache();
-                sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
+                final HashMap<String, Integer> installingPkgs =
+                        mPackageInstaller.updateAndGetActiveSessionCache();
+                mBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(context));
 
                 Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
                 final LoaderCursor c = new LoaderCursor(contentResolver.query(
@@ -798,8 +818,8 @@
                         // We can only query for shortcuts when the user is unlocked.
                         if (userUnlocked) {
                             List<ShortcutInfoCompat> pinnedShortcuts =
-                                    shortcutManager.queryForPinnedShortcuts(null, user);
-                            if (shortcutManager.wasLastCallSuccess()) {
+                                    mShortcutManager.queryForPinnedShortcuts(null, user);
+                            if (mShortcutManager.wasLastCallSuccess()) {
                                 for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
                                     shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
                                             shortcut);
@@ -864,14 +884,14 @@
                                 // If there is no target package, its an implicit intent
                                 // (legacy shortcut) which is always valid
                                 boolean validTarget = TextUtils.isEmpty(targetPkg) ||
-                                        launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
+                                        mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user);
 
                                 if (cn != null && validTarget) {
                                     // If the apk is present and the shortcut points to a specific
                                     // component.
 
                                     // If the component is already present
-                                    if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
+                                    if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
                                         // no special handling necessary for this item
                                         c.markRestored();
                                     } else {
@@ -1022,14 +1042,14 @@
                                         }
                                     }
 
-                                    c.checkAndAddItem(info, sBgDataModel);
+                                    c.checkAndAddItem(info, mBgDataModel);
                                 } else {
                                     throw new RuntimeException("Unexpected null ShortcutInfo");
                                 }
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                                FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
+                                FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
                                 c.applyCommonProperties(folderInfo);
 
                                 // Do not trim the folder label, as is was set by the user.
@@ -1041,7 +1061,7 @@
                                 // no special handling required for restored folders
                                 c.markRestored();
 
-                                c.checkAndAddItem(folderInfo, sBgDataModel);
+                                c.checkAndAddItem(folderInfo, mBgDataModel);
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -1062,8 +1082,7 @@
                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
 
                                 if (widgetProvidersMap == null) {
-                                    widgetProvidersMap = AppWidgetManagerCompat
-                                            .getInstance(mContext).getAllProvidersMap();
+                                    widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
                                 }
                                 final AppWidgetProviderInfo provider = widgetProvidersMap.get(
                                         new ComponentKey(
@@ -1161,7 +1180,7 @@
                                                 appWidgetInfo.pendingItemInfo, false);
                                     }
 
-                                    c.checkAndAddItem(appWidgetInfo, sBgDataModel);
+                                    c.checkAndAddItem(appWidgetInfo, mBgDataModel);
                                 }
                                 break;
                             }
@@ -1175,7 +1194,7 @@
 
                 // Break early if we've stopped loading
                 if (mStopped) {
-                    sBgDataModel.clear();
+                    mBgDataModel.clear();
                     return;
                 }
 
@@ -1187,9 +1206,9 @@
                                     LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
                             .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
                     for (long folderId : deletedFolderIds) {
-                        sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
-                        sBgDataModel.folders.remove(folderId);
-                        sBgDataModel.itemsIdMap.remove(folderId);
+                        mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+                        mBgDataModel.folders.remove(folderId);
+                        mBgDataModel.itemsIdMap.remove(folderId);
                     }
 
                     // Remove any ghost widgets
@@ -1201,18 +1220,18 @@
                 HashSet<ShortcutKey> pendingShortcuts =
                         InstallShortcutReceiver.getPendingShortcuts(context);
                 for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
-                    MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
+                    MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
                     if ((numTimesPinned == null || numTimesPinned.value == 0)
                             && !pendingShortcuts.contains(key)) {
                         // Shortcut is pinned but doesn't exist on the workspace; unpin it.
-                        shortcutManager.unpinShortcut(key);
+                        mShortcutManager.unpinShortcut(key);
                     }
                 }
 
                 FolderIconPreviewVerifier verifier =
                         new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
                 // Sort the folder items and make sure all items in the preview are high resolution.
-                for (FolderInfo folder : sBgDataModel.folders) {
+                for (FolderInfo folder : mBgDataModel.folders) {
                     Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                     verifier.setFolderInfo(folder);
 
@@ -1234,16 +1253,15 @@
                 c.commitRestoredItems();
                 if (!isSdCardReady && !pendingPackages.isEmpty()) {
                     context.registerReceiver(
-                            new SdCardAvailableReceiver(
-                                    LauncherModel.this, mContext, pendingPackages),
+                            new SdCardAvailableReceiver(mApp, pendingPackages),
                             new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
                             null,
                             sWorker);
                 }
 
                 // Remove any empty screens
-                ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
-                for (ItemInfo item: sBgDataModel.itemsIdMap) {
+                ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
+                for (ItemInfo item: mBgDataModel.itemsIdMap) {
                     long screenId = item.screenId;
                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                             unusedScreens.contains(screenId)) {
@@ -1253,8 +1271,8 @@
 
                 // If there are any empty screens remove them, and update.
                 if (unusedScreens.size() != 0) {
-                    sBgDataModel.workspaceScreens.removeAll(unusedScreens);
-                    updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
+                    mBgDataModel.workspaceScreens.removeAll(unusedScreens);
+                    updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
                 }
             }
             if (LauncherAppState.PROFILE_STARTUP) {
@@ -1265,8 +1283,8 @@
         private void updateIconCache() {
             // Ignore packages which have a promise icon.
             HashSet<String> packagesToIgnore = new HashSet<>();
-            synchronized (sBgDataModel) {
-                for (ItemInfo info : sBgDataModel.itemsIdMap) {
+            synchronized (mBgDataModel) {
+                for (ItemInfo info : mBgDataModel.itemsIdMap) {
                     if (info instanceof ShortcutInfo) {
                         ShortcutInfo si = (ShortcutInfo) info;
                         if (si.isPromise() && si.getTargetComponent() != null) {
@@ -1312,14 +1330,14 @@
                     mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
                 }
 
-                ManagedProfileHeuristic.onAllAppsLoaded(mContext, apps, user);
+                ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
             }
 
             if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
                 // get all active sessions and add them to the all apps list
-                PackageInstallerCompat installer = PackageInstallerCompat.getInstance(mContext);
-                for (PackageInstaller.SessionInfo info : installer.getAllVerifiedSessions()) {
-                    mBgAllAppsList.addPromiseApp(mContext,
+                for (PackageInstaller.SessionInfo info :
+                        mPackageInstaller.getAllVerifiedSessions()) {
+                    mBgAllAppsList.addPromiseApp(mApp.getContext(),
                             PackageInstallInfo.fromInstallingState(info));
                 }
             }
@@ -1332,15 +1350,14 @@
         }
 
         private void loadDeepShortcuts() {
-            sBgDataModel.deepShortcutMap.clear();
-            DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
-            mHasShortcutHostPermission = shortcutManager.hasHostPermission();
-            if (mHasShortcutHostPermission) {
+            mBgDataModel.deepShortcutMap.clear();
+            mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
+            if (mBgDataModel.hasShortcutHostPermission) {
                 for (UserHandle user : mUserManager.getUserProfiles()) {
                     if (mUserManager.isUserUnlocked(user)) {
                         List<ShortcutInfoCompat> shortcuts =
-                                shortcutManager.queryForAllShortcuts(user);
-                        sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+                                mShortcutManager.queryForAllShortcuts(user);
+                        mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
                     }
                 }
             }
@@ -1370,12 +1387,6 @@
     }
 
     public void enqueueModelUpdateTask(BaseModelUpdateTask task) {
-        if (!mModelLoaded && mLoaderTask == null) {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
-            }
-            return;
-        }
         task.init(this);
         runOnWorkerThread(task);
     }
@@ -1404,8 +1415,11 @@
         }
 
         @Override
-        public void run() {
-            if (!mModel.mHasLoaderCompletedOnce) {
+        public final void run() {
+            if (!mModel.mModelLoaded) {
+                if (DEBUG_LOADERS) {
+                    Log.d(TAG, "Ignoring model task since loader is pending=" + this);
+                }
                 // Loader has not yet run.
                 return;
             }
@@ -1465,34 +1479,12 @@
         });
     }
 
-    private void bindWidgetsModel(final Callbacks callbacks) {
-        final MultiHashMap<PackageItemInfo, WidgetItem> widgets
-                = mBgWidgetsModel.getWidgetsMap().clone();
-        mUiExecutor.execute(new Runnable() {
+    public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
+        enqueueModelUpdateTask(new ExtendedModelTask() {
             @Override
-            public void run() {
-                Callbacks cb = getCallback();
-                if (callbacks == cb && cb != null) {
-                    callbacks.bindAllWidgets(widgets);
-                }
-            }
-        });
-    }
-
-    public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
-            final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
-        runOnWorkerThread(new Runnable() {
-            @Override
-            public void run() {
-                if (bindFirst && !mBgWidgetsModel.isEmpty()) {
-                    bindWidgetsModel(callbacks);
-                }
-                ArrayList<WidgetItem> widgets = mBgWidgetsModel.update(
-                        mApp.getContext(), packageUser);
-                bindWidgetsModel(callbacks);
-
-                // update the Widget entries inside DB on the worker thread.
-                mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                dataModel.widgetsModel.update(app, packageUser);
+                bindUpdatedWidgets(dataModel);
             }
         });
     }
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index be93be4..d9c5143 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -91,11 +91,21 @@
     public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
 
     /**
+     * True if the launcher has permission to access deep shortcuts.
+     */
+    public boolean hasShortcutHostPermission;
+
+    /**
      * Maps all launcher activities to the id's of their shortcuts (if they have any).
      */
     public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
 
     /**
+     * Entire list of widgets.
+     */
+    public final WidgetsModel widgetsModel = new WidgetsModel();
+
+    /**
      * Clears all the data
      */
     public synchronized void clear() {
diff --git a/src/com/android/launcher3/model/ExtendedModelTask.java b/src/com/android/launcher3/model/ExtendedModelTask.java
index 0541966..080aaf5 100644
--- a/src/com/android/launcher3/model/ExtendedModelTask.java
+++ b/src/com/android/launcher3/model/ExtendedModelTask.java
@@ -59,4 +59,15 @@
             }
         });
     }
+
+    public void bindUpdatedWidgets(BgDataModel dataModel) {
+        final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+                = dataModel.widgetsModel.getWidgetsMap();
+        scheduleCallbackTask(new CallbackTask() {
+            @Override
+            public void execute(Callbacks callbacks) {
+                callbacks.bindAllWidgets(widgets);
+            }
+        });
+    }
 }
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 61fd356..28df64d 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.MainThreadExecutor;
@@ -58,6 +59,7 @@
     private final BgDataModel mBgDataModel;
     private final AllAppsList mBgAllAppsList;
     private final int mPageToBindFirst;
+
     private final WeakReference<Callbacks> mCallbacks;
 
     public LoaderResults(LauncherAppState app, BgDataModel dataModel,
@@ -358,7 +360,6 @@
         mUiExecutor.execute(r);
     }
 
-
     public void bindAllApps() {
         // shallow copy
         @SuppressWarnings("unchecked")
@@ -374,4 +375,18 @@
         };
         mUiExecutor.execute(r);
     }
+
+    public void bindWidgets() {
+        final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+                = mBgDataModel.widgetsModel.getWidgetsMap();
+        Runnable r = new Runnable() {
+            public void run() {
+                Callbacks callbacks = mCallbacks.get();
+                if (callbacks != null) {
+                    callbacks.bindAllWidgets(widgets);
+                }
+            }
+        };
+        mUiExecutor.execute(r);
+    }
 }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 8380f01..46fea21 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -373,11 +374,9 @@
         } else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
             // Load widgets for the new package.
             for (int i = 0; i < N; i++) {
-                LauncherModel model = app.getModel();
-                model.refreshAndBindWidgetsAndShortcuts(
-                        model.getCallback(), false /* bindFirst */,
-                        new PackageUserKey(packages[i], mUser) /* packageUser */);
+                dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
             }
+            bindUpdatedWidgets(dataModel);
         }
     }
 }
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index bae5c73..3aedae6 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.UserHandle;
 
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.MultiHashMap;
@@ -43,10 +44,10 @@
     private final Context mContext;
     private final MultiHashMap<UserHandle, String> mPackages;
 
-    public SdCardAvailableReceiver(LauncherModel model, Context context,
+    public SdCardAvailableReceiver(LauncherAppState app,
             MultiHashMap<UserHandle, String> packages) {
-        mModel = model;
-        mContext = context;
+        mModel = app.getModel();
+        mContext = app.getContext();
         mPackages = packages;
     }
 
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 827675a..ed900bf 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -38,36 +38,26 @@
     private static final boolean DEBUG = false;
 
     /* Map of widgets and shortcuts that are tracked per package. */
-    private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList;
+    private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
 
-    private final IconCache mIconCache;
-    private final AppFilter mAppFilter;
+    private AppFilter mAppFilter;
 
-    public WidgetsModel(IconCache iconCache, AppFilter appFilter) {
-        mIconCache = iconCache;
-        mAppFilter = appFilter;
-        mWidgetsList = new MultiHashMap<>();
-    }
-
-    public MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
-        return mWidgetsList;
-    }
-
-    public boolean isEmpty() {
-        return mWidgetsList.isEmpty();
+    public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
+        return mWidgetsList.clone();
     }
 
     /**
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
      */
-    public ArrayList<WidgetItem> update(Context context, @Nullable PackageUserKey packageUser) {
+    public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
         Preconditions.assertWorkerThread();
 
+        Context context = app.getContext();
         final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
         try {
             PackageManager pm = context.getPackageManager();
-            InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+            InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
 
             // Widgets
             AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
@@ -81,7 +71,7 @@
                     .getCustomShortcutActivityList(packageUser)) {
                 widgetsAndShortcuts.add(new WidgetItem(info));
             }
-            setWidgetsAndShortcuts(widgetsAndShortcuts, context, packageUser);
+            setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
         } catch (Exception e) {
             if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
                 // the returned value may be incomplete and will not be refreshed until the next
@@ -92,11 +82,12 @@
                 throw e;
             }
         }
-        return widgetsAndShortcuts;
+
+        app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
     }
 
-    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
-            Context context, @Nullable PackageUserKey packageUser) {
+    private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+            LauncherAppState app, @Nullable PackageUserKey packageUser) {
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
         }
@@ -133,7 +124,7 @@
             }
         }
 
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+        InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
         UserHandle myUser = Process.myUserHandle();
 
         // add and update.
@@ -152,6 +143,9 @@
                 }
             }
 
+            if (mAppFilter == null) {
+                mAppFilter = AppFilter.newInstance(app.getContext());
+            }
             if (!mAppFilter.shouldShowApp(item.componentName)) {
                 if (DEBUG) {
                     Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
@@ -174,8 +168,9 @@
         }
 
         // Update each package entry
+        IconCache iconCache = app.getIconCache();
         for (PackageItemInfo p : tmpPackageItemInfos.values()) {
-            mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+            iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
         }
     }
 }
\ No newline at end of file