Merge "Changing the widget loading strategy" into ub-launcher3-calgary
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 752567b..c8eb9cc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1560,8 +1560,7 @@
 
         ItemInfo info = mPendingAddInfo;
         if (appWidgetInfo == null) {
-            appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
-                    mAppWidgetManager.getAppWidgetInfo(appWidgetId));
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
         }
 
         if (appWidgetInfo.isCustomWidget) {
@@ -2558,10 +2557,10 @@
         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
         if (v.isReadyForClickSetup()) {
             int widgetId = info.appWidgetId;
-            AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+            LauncherAppWidgetProviderInfo appWidgetInfo =
+                    mAppWidgetManager.getLauncherAppWidgetInfo(widgetId);
             if (appWidgetInfo != null) {
-                mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
-                        this, appWidgetInfo);
+                mPendingAddWidgetInfo = appWidgetInfo;
                 mPendingAddInfo.copyFrom(info);
                 mPendingAddWidgetId = widgetId;
 
@@ -3538,10 +3537,6 @@
         // TODO
     }
 
-    protected void disableVoiceButtonProxy(boolean disable) {
-        // NO-OP
-    }
-
     public boolean launcherCallbacksProvidesSearch() {
         return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
     }
@@ -3955,6 +3950,16 @@
         sFolders = folders.clone();
     }
 
+    private void bindSafeModeWidget(LauncherAppWidgetInfo item) {
+        PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, true);
+        view.updateIcon(mIconCache);
+        item.hostView = view;
+        item.hostView.updateAppWidget(null);
+        item.hostView.setOnClickListener(this);
+        addAppWidgetToWorkspace(item, null, false);
+        mWorkspace.requestLayout();
+    }
+
     /**
      * Add the views for a widget to the workspace.
      *
@@ -3970,19 +3975,31 @@
             return;
         }
 
+        if (mIsSafeModeEnabled) {
+            bindSafeModeWidget(item);
+            return;
+        }
+
         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bindAppWidget: " + item);
         }
-        final Workspace workspace = mWorkspace;
 
-        LauncherAppWidgetProviderInfo appWidgetInfo =
-                LauncherModel.getProviderInfo(this, item.providerName, item.user);
+        final LauncherAppWidgetProviderInfo appWidgetInfo;
 
-        if (!mIsSafeModeEnabled
-                && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
-                && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+        if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+            // If the provider is not ready, bind as a pending widget.
+            appWidgetInfo = null;
+        } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+            // The widget id is not valid. Try to find the widget based on the provider info.
+            appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+        } else {
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+        }
 
+        // If the provider is ready, but the width is not yet restored, try to restore it.
+        if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
+                (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
             if (appWidgetInfo == null) {
                 if (DEBUG_WIDGETS) {
                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -3994,7 +4011,7 @@
             }
 
             // If we do not have a valid id, try to bind an id.
-            if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
                 // Note: This assumes that the id remap broadcast is received before this step.
                 // If that is not the case, the id remap will be ignored and user may see the
                 // click to setup view.
@@ -4030,46 +4047,42 @@
                         : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
 
                 LauncherModel.updateItemInDatabase(this, item);
-            } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
                     && (appWidgetInfo.configure == null)) {
-                // If the ID is already valid, verify if we need to configure or not.
+                // The widget was marked as UI not ready, but there is no configure activity to
+                // update the UI.
                 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                 LauncherModel.updateItemInDatabase(this, item);
             }
         }
 
-        if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-            final int appWidgetId = item.appWidgetId;
+        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
             if (DEBUG_WIDGETS) {
                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
                         + appWidgetInfo.provider);
             }
 
             // Verify that we own the widget
-            AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-            if (info == null || appWidgetInfo == null ||
-                    !info.provider.equals(appWidgetInfo.provider)) {
-                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId + " info=" + info
-                        + " appWidgetInfo=" + appWidgetInfo);
+            if (appWidgetInfo == null) {
+                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                 deleteWidgetInfo(item);
                 return;
             }
 
-            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            item.hostView = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             item.minSpanX = appWidgetInfo.minSpanX;
             item.minSpanY = appWidgetInfo.minSpanY;
+            addAppWidgetToWorkspace(item, appWidgetInfo, false);
         } else {
-            appWidgetInfo = null;
             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
                     mIsSafeModeEnabled);
             view.updateIcon(mIconCache);
             item.hostView = view;
             item.hostView.updateAppWidget(null);
             item.hostView.setOnClickListener(this);
+            addAppWidgetToWorkspace(item, null, false);
         }
-
-        addAppWidgetToWorkspace(item, appWidgetInfo, false);
-        workspace.requestLayout();
+        mWorkspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
@@ -4407,15 +4420,15 @@
         }
     }
 
-    private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
+    private Runnable mBindWidgetModelRunnable = new Runnable() {
             public void run() {
-                bindAllPackages(mWidgetsModel);
+                bindWidgetsModel(mWidgetsModel);
             }
         };
 
     @Override
-    public void bindAllPackages(final WidgetsModel model) {
-        if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
+    public void bindWidgetsModel(WidgetsModel model) {
+        if (waitUntilResume(mBindWidgetModelRunnable, true)) {
             mWidgetsModel = model;
             return;
         }
@@ -4426,6 +4439,13 @@
         }
     }
 
+    @Override
+    public void notifyWidgetProvidersChanged() {
+        if (mWorkspace.getState().shouldUpdateWidget) {
+            mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
+        }
+    }
+
     private int mapConfigurationOriActivityInfoOri(int configOri) {
         final Display d = getWindowManager().getDefaultDisplay();
         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index b07ccc3..8c23ff3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -102,6 +102,10 @@
                 callback.run();
             }
         }
+
+        if (Utilities.ATLEAST_MARSHMALLOW) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
     }
 
     public AppWidgetHostView createView(Context context, int appWidgetId,
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 05d729e..cad0f2c 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.backup.nano.BackupProtos.Resource;
 import com.android.launcher3.backup.nano.BackupProtos.Screen;
 import com.android.launcher3.backup.nano.BackupProtos.Widget;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.model.GridSizeMigrationTask;
@@ -660,12 +661,14 @@
                 + getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
+        AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
                 final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX);
                 final ComponentName provider = ComponentName.unflattenFromString(providerName);
+
                 Key key = null;
                 String backupKey = null;
                 if (provider != null) {
@@ -684,11 +687,14 @@
                 } else if (backupKey != null) {
                     if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
                     if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
-                        if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
-                        UserHandleCompat user = UserHandleCompat.myUserHandle();
-                        writeRowToBackup(key, packWidget(dpi, provider, user), data);
-                        mKeys.add(key);
-                        backupWidgetCount ++;
+                        LauncherAppWidgetProviderInfo widgetInfo = widgetManager
+                                .getLauncherAppWidgetInfo(cursor.getInt(APPWIDGET_ID_INDEX));
+                        if (widgetInfo != null) {
+                            if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
+                            writeRowToBackup(key, packWidget(dpi, widgetInfo), data);
+                            mKeys.add(key);
+                            backupWidgetCount ++;
+                        }
                     } else {
                         if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey);
                         // too many widgets for this pass, request another.
@@ -1004,16 +1010,14 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) {
-        final LauncherAppWidgetProviderInfo info =
-                LauncherModel.getProviderInfo(mContext, provider, user);
+    private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) {
         Widget widget = new Widget();
-        widget.provider = provider.flattenToShortString();
+        widget.provider = info.provider.flattenToShortString();
         widget.label = info.label;
         widget.configure = info.configure != null;
         if (info.icon != 0) {
             widget.icon = new Resource();
-            Drawable fullResIcon = mIconCache.getFullResIcon(provider.getPackageName(), info.icon);
+            Drawable fullResIcon = mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
             Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
             widget.icon.data = Utilities.flattenBitmap(icon);
             widget.icon.dpi = dpi;
@@ -1022,7 +1026,6 @@
         Point spans = info.getMinSpans(mIdp, mContext);
         widget.minSpanX = spans.x;
         widget.minSpanY = spans.y;
-
         return widget;
     }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f707ec5..3877b94 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.app.SearchManager;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -34,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.DeadObjectException;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -42,7 +40,6 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.TransactionTooLargeException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
@@ -73,7 +70,6 @@
 import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -137,11 +133,9 @@
     @Thunk WeakReference<Callbacks> mCallbacks;
 
     // < only access in worker thread >
-    AllAppsList mBgAllAppsList;
+    private final AllAppsList mBgAllAppsList;
     // Entire list of widgets.
-    WidgetsModel mBgWidgetsModel;
-    // Keep a clone of widgets that can be accessed from non-worker thread.
-    WidgetsModel mFgWidgetsModel;
+    private final WidgetsModel mBgWidgetsModel;
 
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
@@ -168,12 +162,6 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
-    // sBgWidgetProviders is the set of widget providers including custom internal widgets
-    public static HashMap<ComponentKey, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
-
-    // sBgShortcutProviders is the set of custom shortcut providers
-    public static List<ResolveInfo> sBgShortcutProviders;
-
     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
             new HashMap<UserHandleCompat, HashSet<String>>();
@@ -209,7 +197,8 @@
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
-        public void bindAllPackages(WidgetsModel model);
+        public void notifyWidgetProvidersChanged();
+        public void bindWidgetsModel(WidgetsModel model);
         public void bindSearchProviderChanged();
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
@@ -245,7 +234,6 @@
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
         mIconCache = iconCache;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
@@ -1701,8 +1689,8 @@
                         .getInstance(mContext).updateAndGetActiveSessionCache();
                 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
 
-                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
-                final ArrayList<Long> restoredRows = new ArrayList<Long>();
+                final ArrayList<Long> itemsToRemove = new ArrayList<>();
+                final ArrayList<Long> restoredRows = new ArrayList<>();
                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1711,6 +1699,7 @@
                 // Load workspace in reverse order to ensure that latest items are loaded first (and
                 // before any earlier duplicates)
                 final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
+                HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
 
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -2070,10 +2059,14 @@
                                 final boolean wasProviderReady = (restoreStatus &
                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
 
-                                final LauncherAppWidgetProviderInfo provider =
-                                        LauncherModel.getProviderInfo(context,
+                                if (widgetProvidersMap == null) {
+                                    widgetProvidersMap = AppWidgetManagerCompat
+                                            .getInstance(mContext).getAllProvidersMap();
+                                }
+                                final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+                                        new ComponentKey(
                                                 ComponentName.unflattenFromString(savedProvider),
-                                                user);
+                                                user));
 
                                 final boolean isProviderReady = isValidProvider(provider);
                                 if (!isSafeMode && !customWidget &&
@@ -2695,7 +2688,6 @@
                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(list);
-                        callbacks.bindAllPackages(mFgWidgetsModel);
                     }
                     if (DEBUG_LOADERS) {
                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
@@ -2787,7 +2779,7 @@
                         callbacks.bindAllApplications(added);
                         if (DEBUG_LOADERS) {
                             Log.d(TAG, "bound " + added.size() + " apps in "
-                                + (SystemClock.uptimeMillis() - bindTime) + "ms");
+                                    + (SystemClock.uptimeMillis() - bindTime) + "ms");
                         }
                     } else {
                         Log.i(TAG, "not binding apps: no Launcher activity");
@@ -2796,8 +2788,6 @@
             });
             // Cleanup any data stored for a deleted user.
             ManagedProfileHeuristic.processAllUsers(profiles, mContext);
-
-            loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
             if (DEBUG_LOADERS) {
                 Log.d(TAG, "Icons processed in "
                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2864,9 +2854,6 @@
                 }
             });
         }
-
-        // Reload widget list. No need to refresh, as we only want to update the icons and labels.
-        loadAndBindWidgetsAndShortcuts(callbacks, false);
     }
 
     void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -3230,167 +3217,49 @@
                 });
             }
 
-            // Update widgets
-            if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) {
-                // Always refresh for a package event on secondary user
-                boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle());
-
-                // Refresh widget list, if the package already had a widget.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        HashSet<String> pkgSet = new HashSet<>();
-                        Collections.addAll(pkgSet, mPackages);
-
-                        for (ComponentKey key : sBgWidgetProviders.keySet()) {
-                            needToRefresh |= key.user.equals(mUser) &&
-                                    pkgSet.contains(key.componentName.getPackageName());
-                        }
-                    }
-                }
-
-                if (!needToRefresh && mOp != OP_REMOVE) {
-                    // Refresh widget list, if there is any newly added widget
-                    PackageManager pm = context.getPackageManager();
-                    for (String pkg : mPackages) {
-                        try {
-                            List<ResolveInfo> widgets = pm.queryBroadcastReceivers(
-                                    new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
-                                            .setPackage(pkg), 0);
-                            needToRefresh |= widgets != null && !widgets.isEmpty();
-                        } catch (RuntimeException e) {
-                            if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                                throw e;
-                            }
-                            // Ignore the crash. We can live with a state widget list.
-                            Log.e(TAG, "PM call failed for " + pkg, e);
-                        }
-                    }
-                }
-
-                loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
-            }
-        }
-    }
-
-    public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
-            boolean refresh) {
-        ArrayList<LauncherAppWidgetProviderInfo> results =
-                new ArrayList<LauncherAppWidgetProviderInfo>();
-        try {
-            synchronized (sBgLock) {
-                if (sBgWidgetProviders == null || refresh) {
-                    HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
-                            = new HashMap<>();
-                    AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
-                    LauncherAppWidgetProviderInfo info;
-
-                    List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
-                    for (AppWidgetProviderInfo pInfo : widgets) {
-                        info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-
-                    Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
-                    for (CustomAppWidget widget : customWidgets) {
-                        info = new LauncherAppWidgetProviderInfo(context, widget);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-                    // Replace the global list at the very end, so that if there is an exception,
-                    // previously loaded provider list is used.
-                    sBgWidgetProviders = tmpWidgetProviders;
-                }
-                results.addAll(sBgWidgetProviders.values());
-                return results;
-            }
-        } catch (Exception e) {
-            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
-                    (e.getCause() instanceof TransactionTooLargeException ||
-                    e.getCause() instanceof DeadObjectException)) {
-                // the returned value may be incomplete and will not be refreshed until the next
-                // time Launcher starts.
-                // TODO: after figuring out a repro step, introduce a dirty bit to check when
-                // onResume is called to refresh the widget provider list.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        results.addAll(sBgWidgetProviders.values());
-                    }
-                    return results;
-                }
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name,
-            UserHandleCompat user) {
-        synchronized (sBgLock) {
-            if (sBgWidgetProviders == null) {
-                getWidgetProviders(ctx, false /* refresh */);
-            }
-            return sBgWidgetProviders.get(new ComponentKey(name, user));
-        }
-    }
-
-    public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
-
-        runOnWorkerThread(new Runnable() {
-            @Override
-            public void run() {
-                updateWidgetsModel(refresh);
+            // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
+            // get widget update signals.
+            if (!Utilities.ATLEAST_MARSHMALLOW &&
+                    (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
                 mHandler.post(new Runnable() {
-                    @Override
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindAllPackages(mFgWidgetsModel);
+                            callbacks.notifyWidgetProvidersChanged();
                         }
                     }
                 });
-                // update the Widget entries inside DB on the worker thread.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
-                        mFgWidgetsModel.getRawList());
+            }
+        }
+    }
+
+    private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Callbacks cb = getCallback();
+                if (callbacks == cb && cb != null) {
+                    callbacks.bindWidgetsModel(model);
+                }
             }
         });
     }
 
-    /**
-     * Returns a list of ResolveInfos/AppWidgetInfos.
-     *
-     * @see #loadAndBindWidgetsAndShortcuts
-     */
-    @Thunk void updateWidgetsModel(boolean refresh) {
-        Utilities.assertWorkerThread();
-        PackageManager packageManager = mApp.getContext().getPackageManager();
-        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
-        widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
-
-        // Update shortcut providers
-        synchronized (sBgLock) {
-            try {
-                Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                List<ResolveInfo> providers = packageManager.queryIntentActivities(shortcutsIntent, 0);
-                sBgShortcutProviders = providers;
-            } catch (RuntimeException e) {
-                if (!ProviderConfig.IS_DOGFOOD_BUILD &&
-                        (e.getCause() instanceof TransactionTooLargeException ||
-                                e.getCause() instanceof DeadObjectException)) {
-                    /**
-                     * Ignore exception and use the cached list if available.
-                     * Refer to {@link #getWidgetProviders(Context, boolean}} for more info.
-                     */
-                } else {
-                    throw e;
+    public void refreshAndBindWidgetsAndShortcuts(
+            final Callbacks callbacks, final boolean bindFirst) {
+        runOnWorkerThread(new Runnable() {
+            @Override
+            public void run() {
+                if (bindFirst && !mBgWidgetsModel.isEmpty()) {
+                    bindWidgetsModel(callbacks, mBgWidgetsModel.clone());
                 }
+                final WidgetsModel model = mBgWidgetsModel.updateAndClone(mApp.getContext());
+                bindWidgetsModel(callbacks, model);
+                // update the Widget entries inside DB on the worker thread.
+                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
+                        model.getRawList());
             }
-            if (sBgShortcutProviders != null) {
-                widgetsAndShortcuts.addAll(sBgShortcutProviders);
-            }
-        }
-        mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
+        });
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 303785d..8855cf0 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -403,7 +403,7 @@
 
         // Animate the search bar
         final SearchDropTargetBar.State toSearchBarState =
-                toWorkspaceState.getSearchDropTargetBarState();
+                toWorkspaceState.searchDropTargetBarState;
         mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
                 animated ? revealDuration : 0, animation);
 
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index fdb2ff4..10a26ad 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -145,6 +145,8 @@
         LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras);
         broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras);
         mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
-        mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras);
+        if (intent.getComponent() != null) {
+            mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras);
+        }
     }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a74fa0d..0b72ef7 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,11 +52,11 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
@@ -179,22 +179,20 @@
     // in all apps or customize mode)
 
     enum State {
-        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED),
-        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET),
-        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE),
-        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
+        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR, false),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false),
+        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET, false),
+        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE, true),
+        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true);
 
-        private final SearchDropTargetBar.State mBarState;
+        public final SearchDropTargetBar.State searchDropTargetBarState;
+        public final boolean shouldUpdateWidget;
 
-        State(SearchDropTargetBar.State searchBarState) {
-            mBarState = searchBarState;
+        State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget) {
+            searchDropTargetBarState = searchBarState;
+            this.shouldUpdateWidget = shouldUpdateWidget;
         }
-
-        public SearchDropTargetBar.State getSearchDropTargetBarState() {
-            return mBarState;
-        }
-    };
+    }
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.NORMAL;
@@ -1914,6 +1912,8 @@
         Animator workspaceAnim =  mStateTransitionAnimation.getAnimationToState(mState,
                 toState, animated, layerViews);
 
+        boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
+                && toState.shouldUpdateWidget;
         // Update the current state
         mState = toState;
         updateAccessibilityFlags();
@@ -1923,6 +1923,10 @@
             invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
         }
 
+        if (shouldNotifyWidgetChange) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
+
         return workspaceAnim;
     }
 
@@ -4148,13 +4152,22 @@
         });
     }
 
-    void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
+    public void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
                     mLauncher.getAppWidgetHost());
-            if (LauncherModel.getProviderInfo(getContext(),
-                    changedInfo.get(0).providerName,
-                    changedInfo.get(0).user) != null) {
+
+            LauncherAppWidgetInfo item = changedInfo.get(0);
+            final AppWidgetProviderInfo widgetInfo;
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                widgetInfo = AppWidgetManagerCompat
+                        .getInstance(mLauncher).findProvider(item.providerName, item.user);
+            } else {
+                widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
+                        .getAppWidgetInfo(item.appWidgetId);
+            }
+
+            if (widgetInfo != null) {
                 // Re-inflate the widgets which have changed status
                 widgetRefresh.run();
             } else {
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index f0221bc..811cacf 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -28,7 +29,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 public abstract class AppWidgetManagerCompat {
@@ -62,6 +65,11 @@
         return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
     }
 
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
+        AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId);
+        return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+    }
+
     public abstract List<AppWidgetProviderInfo> getAllProviders();
 
     public abstract String loadLabel(LauncherAppWidgetProviderInfo info);
@@ -81,4 +89,8 @@
     public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
             int imageWidth, int imageHeight);
 
+    public abstract LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user);
+
+    public abstract HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap();
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index e9d2510..de9414e 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -31,7 +32,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
@@ -91,4 +94,25 @@
             int imageWidth, int imageHeight) {
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        UserHandleCompat user = UserHandleCompat.myUserHandle();
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            result.put(new ComponentKey(info.provider, user), info);
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 3bc3d0d..a1570e6 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -40,8 +41,10 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.util.ComponentKey;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -145,4 +148,28 @@
         c.setBitmap(null);
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager
+                .getInstalledProvidersForProfile(user.getUser())) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        for (UserHandle user : mUserManager.getUserProfiles()) {
+            UserHandleCompat userHandle = UserHandleCompat.fromUser(user);
+            for (AppWidgetProviderInfo info :
+                    mAppWidgetManager.getInstalledProvidersForProfile(user)) {
+                result.put(new ComponentKey(info.provider, userHandle), info);
+            }
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 19ec3ed..931466c 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -23,8 +23,8 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.backup.nano.BackupProtos;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.LongArrayMap;
 
 import java.util.ArrayList;
@@ -696,7 +696,8 @@
                         Favorites.SPANX,                // 4
                         Favorites.SPANY,                // 5
                         Favorites.INTENT,               // 6
-                        Favorites.APPWIDGET_PROVIDER},  // 7
+                        Favorites.APPWIDGET_PROVIDER,   // 7
+                        Favorites.APPWIDGET_ID},        // 8
                 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
                         + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
 
@@ -708,6 +709,7 @@
         final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
         final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
         final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
+        final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
 
         ArrayList<DbEntry> entries = new ArrayList<>();
         while (c.moveToNext()) {
@@ -737,9 +739,9 @@
                         entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
                                 * entry.spanX * entry.spanY);
 
-                        // Migration happens for current user only.
-                        LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo(
-                                mContext, cn, UserHandleCompat.myUserHandle());
+                        int widgetId = c.getInt(indexAppWidgetId);
+                        LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
+                                mContext).getLauncherAppWidgetInfo(widgetId);
                         Point spans = pInfo == null ?
                                 mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
                         if (spans != null) {
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 99a53ff..e043c94 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,9 +1,13 @@
 
 package com.android.launcher3.model;
 
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.DeadObjectException;
+import android.os.TransactionTooLargeException;
 import android.util.Log;
 
 import com.android.launcher3.AppFilter;
@@ -16,6 +20,7 @@
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -95,8 +100,41 @@
         return mRawList;
     }
 
-    public void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+    public boolean isEmpty() {
+        return mRawList.isEmpty();
+    }
+
+    public WidgetsModel updateAndClone(Context context) {
         Utilities.assertWorkerThread();
+
+        try {
+            final ArrayList<Object> widgetsAndShortcuts = new ArrayList<>();
+            // Widgets
+            for (AppWidgetProviderInfo widgetInfo :
+                    AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
+                widgetsAndShortcuts.add(LauncherAppWidgetProviderInfo
+                        .fromProviderInfo(context, widgetInfo));
+            }
+            // Shortcuts
+            widgetsAndShortcuts.addAll(context.getPackageManager().queryIntentActivities(
+                    new Intent(Intent.ACTION_CREATE_SHORTCUT), 0));
+            setWidgetsAndShortcuts(widgetsAndShortcuts);
+        } catch (Exception e) {
+            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
+                    (e.getCause() instanceof TransactionTooLargeException ||
+                            e.getCause() instanceof DeadObjectException)) {
+                // the returned value may be incomplete and will not be refreshed until the next
+                // time Launcher starts.
+                // TODO: after figuring out a repro step, introduce a dirty bit to check when
+                // onResume is called to refresh the widget provider list.
+            } else {
+                throw e;
+            }
+        }
+        return clone();
+    }
+
+    private void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
         mRawList = rawWidgetsShortcuts;
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index d7ed9ed..2a08d2d 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -327,6 +327,10 @@
         mAdapter.notifyDataSetChanged();
     }
 
+    public boolean isEmpty() {
+        return mAdapter.getItemCount() == 0;
+    }
+
     private WidgetPreviewLoader getWidgetPreviewLoader() {
         if (mWidgetPreviewLoader == null) {
             mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();