Moving LoaderTask static

Will move it to a separate file in a followup cl.
This simplifies dependencies between LauncherModel and LoaderTask which
and making it easier to start the loader before Launcher activity is
created (as the Callbacks in LauncherModel can change while loader is running).

Bug: 34112546
Bug: 37616877
Change-Id: Ie9619c6b0de0e3eb60657c04ae1b58d946c829e9
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 ff7ca81..c8c5661 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