Adds logging for Launcher restore metrics in Launcher and LoaderTask

- Adds flag for indicating when we are loading after a restore, separate from the pending restore flag
- Adds many success/failure scenario metrics when loading and binding items

Flag: ACONFIG enable_launcher_br_metrics DEVELOPMENT
Test: locally verified
Bug: 307527314
Change-Id: I9d2660f6d9d7cd5813072201e96b1a64aec4ea4b
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e41a8a5..11dc6e2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -52,8 +52,10 @@
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_NEW_INTENT_EVT;
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_RESUME_EVT;
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_START_EVT;
+import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
@@ -63,6 +65,8 @@
 import static com.android.launcher3.LauncherState.NO_SCALE;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_BIND_FAILURE;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_INVALID_LOCATION;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
@@ -162,6 +166,7 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.apppairs.AppPairIcon;
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
 import com.android.launcher3.celllayout.CellPosMapper;
 import com.android.launcher3.celllayout.CellPosMapper.CellPos;
 import com.android.launcher3.celllayout.CellPosMapper.TwoPanelCellPosMapper;
@@ -2158,9 +2163,15 @@
         int newItemsScreenId = -1;
         int end = items.size();
         View newView = null;
+        LauncherRestoreEventLogger restoreEventLogger = null;
+        Boolean isRestoreFromBackup = (Boolean) LauncherPrefs.get(getApplicationContext())
+                .get(IS_FIRST_LOAD_AFTER_RESTORE);
+        if (isRestoreFromBackup) {
+            restoreEventLogger = LauncherRestoreEventLogger.Companion
+                    .newInstance(getApplicationContext());
+        }
         for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
-
             // Short circuit if we are loading dock items for a configuration which has no dock
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                     mHotseat == null) {
@@ -2187,12 +2198,17 @@
                             (FolderInfo) item);
                     break;
                 }
-                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                case ITEM_TYPE_APPWIDGET:
                 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
-                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
+                    view = inflateAppWidget((LauncherAppWidgetInfo) item, restoreEventLogger);
                     if (view == null) {
                         continue;
                     }
+                    // Widgets have more checks when inflating, so we have to wait until here
+                    // to mark restored, instead of logging in LoaderTask.
+                    if (restoreEventLogger != null) {
+                        restoreEventLogger.logSingleFavoritesItemRestored(item.itemType);
+                    }
                     break;
                 }
                 default:
@@ -2216,6 +2232,10 @@
                     if (FeatureFlags.IS_STUDIO_BUILD) {
                         throw (new RuntimeException(desc));
                     } else {
+                        if (restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(item.itemType,
+                                    RESTORE_ERROR_INVALID_LOCATION);
+                        }
                         getModelWriter().deleteItemFromDatabase(item, desc);
                         continue;
                     }
@@ -2274,6 +2294,9 @@
         } else if (focusFirstItemForAccessibility && viewToFocus != null) {
             viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         }
+        if (restoreEventLogger != null) {
+            restoreEventLogger.reportLauncherRestoreResults();
+        }
         workspace.requestLayout();
     }
 
@@ -2281,14 +2304,15 @@
      * Add the views for a widget to the workspace.
      */
     public void bindAppWidget(LauncherAppWidgetInfo item) {
-        View view = inflateAppWidget(item);
+        View view = inflateAppWidget(item, null);
         if (view != null) {
             mWorkspace.addInScreen(view, item);
             mWorkspace.requestLayout();
         }
     }
 
-    private View inflateAppWidget(LauncherAppWidgetInfo item) {
+    private View inflateAppWidget(LauncherAppWidgetInfo item,
+            @Nullable LauncherRestoreEventLogger restoreEventLogger) {
         if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) {
             item.providerName = QsbContainerView.getSearchComponentName(this);
             if (item.providerName == null) {
@@ -2305,11 +2329,9 @@
         }
 
         TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
-
         try {
             final LauncherAppWidgetProviderInfo appWidgetInfo;
             String removalReason = "";
-
             if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
                 // If the provider is not ready, bind as a pending widget.
                 appWidgetInfo = null;
@@ -2349,6 +2371,10 @@
                             "Removing restored widget: id=" + item.appWidgetId
                             + " belongs to component " + item.providerName + " user " + item.user
                             + ", as the provider is null and " + removalReason);
+                    if (restoreEventLogger != null) {
+                        restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE);
+                    }
                     return null;
                 }
 
@@ -2419,6 +2445,10 @@
                 if (appWidgetInfo == null) {
                     FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                     getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason);
+                    if (restoreEventLogger != null) {
+                        restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                ITEM_TYPE_APPWIDGET, RESTORE_ERROR_BIND_FAILURE);
+                    }
                     return null;
                 }
 
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 78056e6..51ba5c6 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -34,6 +34,7 @@
 import com.android.launcher3.model.DeviceGridState
 import com.android.launcher3.pm.InstallSessionHelper
 import com.android.launcher3.provider.RestoreDbTask
+import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
 import com.android.launcher3.states.RotationHelper
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.MainThreadInitializedObject
@@ -417,6 +418,13 @@
                 InvariantDeviceProfile.TYPE_PHONE,
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
+        @JvmField
+        val IS_FIRST_LOAD_AFTER_RESTORE =
+            backedUpItem(
+                FIRST_LOAD_AFTER_RESTORE_KEY,
+                false,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
         @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
         @JvmField
diff --git a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
index 16b1854..bdac05d 100644
--- a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
+++ b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
@@ -14,6 +14,15 @@
     companion object {
         const val TAG = "LauncherRestoreEventLogger"
 
+        // Restore Errors
+        const val RESTORE_ERROR_PROFILE_DELETED = "user_profile_deleted"
+        const val RESTORE_ERROR_MISSING_INFO = "missing_information_when_loading"
+        const val RESTORE_ERROR_BIND_FAILURE = "binding_to_view_failed"
+        const val RESTORE_ERROR_INVALID_LOCATION = "invalid_size_or_location"
+        const val RESTORE_ERROR_SHORTCUT_NOT_FOUND = "shortcut_not_found"
+        const val RESTORE_ERROR_APP_NOT_INSTALLED = "app_not_installed"
+        const val RESTORE_ERROR_WIDGETS_DISABLED = "widgets_disabled"
+
         fun newInstance(context: Context?): LauncherRestoreEventLogger {
             return ResourceBasedOverride.Overrides.getObject(
                 LauncherRestoreEventLogger::class.java,
@@ -54,6 +63,16 @@
     }
 
     /**
+     * Helper to log successfully restoring multiple items from the Favorites table.
+     *
+     * @param favoritesId The id of the item type from [Favorites] that was restored.
+     * @param count number of items that restored.
+     */
+    open fun logFavoritesItemsRestored(favoritesId: Int, count: Int) {
+        // no-op
+    }
+
+    /**
      * Helper to log a failure to restore a single item from the Favorites table.
      *
      * @param favoritesId The id of the item type from [Favorites] that was not restored.
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 683354b..ec6b94d 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -259,7 +259,7 @@
                         query += " or " + LauncherSettings.Favorites.SCREEN + " = "
                                 + Workspace.SECOND_SCREEN_ID;
                     }
-                    loadWorkspace(new ArrayList<>(), query, null);
+                    loadWorkspace(new ArrayList<>(), query, null, null);
 
                     final SparseArray<Size> spanInfo =
                             getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index a98ec64..8dc2ab3 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -17,9 +17,16 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
+import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_APP_NOT_INSTALLED;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_INVALID_LOCATION;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_MISSING_INFO;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_DELETED;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_SHORTCUT_NOT_FOUND;
+import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
@@ -69,6 +76,7 @@
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
@@ -134,6 +142,7 @@
     private final AllAppsList mBgAllAppsList;
     protected final BgDataModel mBgDataModel;
     private final ModelDelegate mModelDelegate;
+    private boolean mIsRestoreFromBackup;
 
     private FirstScreenBroadcast mFirstScreenBroadcast;
 
@@ -148,7 +157,6 @@
     private final IconCache mIconCache;
 
     private final UserManagerState mUserManagerState;
-
     protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
     private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
 
@@ -172,7 +180,6 @@
         mBgDataModel = bgModel;
         mModelDelegate = modelDelegate;
         mLauncherBinder = launcherBinder;
-
         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
         mUserManager = mApp.getContext().getSystemService(UserManager.class);
         mUserCache = UserCache.getInstance(mApp.getContext());
@@ -221,9 +228,14 @@
 
         TraceHelper.INSTANCE.beginSection(TAG);
         LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
+        mIsRestoreFromBackup =
+                (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
+        LauncherRestoreEventLogger restoreEventLogger = LauncherRestoreEventLogger
+                .Companion.newInstance(mApp.getContext());
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
+
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
-            loadWorkspace(allShortcuts, "", memoryLogger);
+            loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);
 
             // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
             // sanitizeData should not be invoked if the workspace is loaded from a db different
@@ -314,8 +326,8 @@
             mLauncherBinder.bindWidgets();
             logASplit("bindWidgets");
             verifyNotStopped();
-
             LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+
             if (SMARTSPACE_AS_A_WIDGET.get() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
                 mLauncherBinder.bindSmartspaceWidget();
                 // Turn off pref.
@@ -349,6 +361,11 @@
             mModelDelegate.modelLoadComplete();
             transaction.commit();
             memoryLogger.clearLogs();
+            if (mIsRestoreFromBackup) {
+                restoreEventLogger.reportLauncherRestoreResults();
+                mIsRestoreFromBackup = false;
+                LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
+            }
         } catch (CancellationException e) {
             // Loader stopped, ignore
             logASplit("Cancelled");
@@ -367,10 +384,12 @@
     protected void loadWorkspace(
             List<ShortcutInfo> allDeepShortcuts,
             String selection,
-            LoaderMemoryLogger memoryLogger) {
+            LoaderMemoryLogger memoryLogger,
+            @Nullable LauncherRestoreEventLogger restoreEventLogger
+    ) {
         Trace.beginSection("LoadWorkspace");
         try {
-            loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger);
+            loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger);
         } finally {
             Trace.endSection();
         }
@@ -391,7 +410,8 @@
     private void loadWorkspaceImpl(
             List<ShortcutInfo> allDeepShortcuts,
             String selection,
-            @Nullable LoaderMemoryLogger memoryLogger) {
+            @Nullable LoaderMemoryLogger memoryLogger,
+            @Nullable LauncherRestoreEventLogger restoreEventLogger) {
         final Context context = mApp.getContext();
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
         final boolean isSafeMode = pmHelper.isSafeMode();
@@ -458,8 +478,8 @@
                 List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
 
                 while (!mStopped && c.moveToNext()) {
-                    processWorkspaceItem(c, memoryLogger, installingPkgs, isSdCardReady,
-                            tempPackageKey, widgetHelper, pmHelper,
+                    processWorkspaceItem(c, memoryLogger, restoreEventLogger, installingPkgs,
+                            isSdCardReady, tempPackageKey, widgetHelper, pmHelper,
                             iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts);
                 }
                 tryLoadWorkspaceIconsInBulk(iconRequestInfos);
@@ -518,6 +538,7 @@
 
     private void processWorkspaceItem(LoaderCursor c,
             LoaderMemoryLogger memoryLogger,
+            @Nullable LauncherRestoreEventLogger restoreEventLogger,
             HashMap<PackageUserKey, SessionInfo> installingPkgs,
             boolean isSdCardReady,
             PackageUserKey tempPackageKey,
@@ -531,7 +552,11 @@
         try {
             if (c.user == null) {
                 // User has been deleted, remove the item.
-                c.markDeleted("User has been deleted");
+                c.markDeleted("User of this item has been deleted");
+                if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                            c.itemType, RESTORE_ERROR_PROFILE_DELETED);
+                }
                 return;
             }
 
@@ -542,6 +567,10 @@
                     Intent intent = c.parseIntent();
                     if (intent == null) {
                         c.markDeleted("Invalid or null intent");
+                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                    c.itemType, RESTORE_ERROR_MISSING_INFO);
+                        }
                         return;
                     }
 
@@ -552,6 +581,10 @@
 
                     if (TextUtils.isEmpty(targetPkg)) {
                         c.markDeleted("Shortcuts can't have null package");
+                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                    c.itemType, RESTORE_ERROR_MISSING_INFO);
+                        }
                         return;
                     }
 
@@ -569,6 +602,9 @@
                         if (mLauncherApps.isActivityEnabled(cn, c.user)) {
                             // no special handling necessary for this item
                             c.markRestored();
+                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
+                            }
                         } else {
                             // Gracefully try to find a fallback activity.
                             intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
@@ -579,7 +615,11 @@
                                         intent.toUri(0)).commit();
                                 cn = intent.getComponent();
                             } else {
-                                c.markDeleted("Unable to find a launch target");
+                                c.markDeleted("Intent null, unable to find a launch target");
+                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                            c.itemType, RESTORE_ERROR_MISSING_INFO);
+                                }
                                 return;
                             }
                         }
@@ -606,6 +646,10 @@
                             } else {
                                 c.markDeleted("removing app that is not restored and not "
                                         + "installing. package: " + targetPkg);
+                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                            c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
+                                }
                                 return;
                             }
                         } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
@@ -623,6 +667,10 @@
                         } else {
                             // Do not wait for external media load anymore.
                             c.markDeleted("Invalid package removed: " + targetPkg);
+                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                        c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
+                            }
                             return;
                         }
                     }
@@ -652,8 +700,12 @@
                             ShortcutInfo pinnedShortcut = mShortcutKeyToPinnedShortcuts.get(key);
                             if (pinnedShortcut == null) {
                                 // The shortcut is no longer valid.
-                                c.markDeleted("Pinned shortcut not found for package: "
-                                        + key.getPackageName());
+                                c.markDeleted("Pinned shortcut not found from request."
+                                        + " package=" + key.getPackageName() + ", user=" + c.user);
+                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                            c.itemType, RESTORE_ERROR_SHORTCUT_NOT_FOUND);
+                                }
                                 return;
                             }
                             info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext());
@@ -672,6 +724,9 @@
                             info = c.loadSimpleWorkspaceItem();
                             info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                         }
+                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
+                        }
                     } else { // item type == ITEM_TYPE_SHORTCUT
                         info = c.loadSimpleWorkspaceItem();
 
@@ -751,13 +806,19 @@
 
                     // no special handling required for restored folders
                     c.markRestored();
-
+                    if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                        restoreEventLogger.logSingleFavoritesItemRestored(c.itemType);
+                    }
                     c.checkAndAddItem(folderInfo, mBgDataModel, memoryLogger);
                     break;
 
                 case Favorites.ITEM_TYPE_APPWIDGET:
                     if (WidgetsModel.GO_DISABLE_WIDGETS) {
                         c.markDeleted("Only legacy shortcuts can have null package");
+                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                    c.itemType, RESTORE_ERROR_WIDGETS_DISABLED);
+                        }
                         return;
                     }
                     // Follow through
@@ -774,6 +835,10 @@
                         component  = QsbContainerView.getSearchComponentName(mApp.getContext());
                         if (component == null) {
                             c.markDeleted("Discarding SearchWidget without packagename ");
+                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                        c.itemType, RESTORE_ERROR_MISSING_INFO);
+                            }
                             return;
                         }
                     } else {
@@ -799,6 +864,10 @@
                     final boolean isProviderReady = isValidProvider(provider);
                     if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) {
                         c.markDeleted("Deleting widget that isn't installed anymore: " + provider);
+                        if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                            restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                    c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
+                        }
                     } else {
                         LauncherAppWidgetInfo appWidgetInfo;
                         if (isProviderReady) {
@@ -841,6 +910,10 @@
                                         |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
                             } else if (!isSafeMode) {
                                 c.markDeleted("Unrestored widget removed: " + component);
+                                if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                    restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                            c.itemType, RESTORE_ERROR_APP_NOT_INSTALLED);
+                                }
                                 return;
                             }
 
@@ -862,6 +935,10 @@
                         if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
                             c.markDeleted("Widget has invalid size: "
                                     + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
+                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                        c.itemType, RESTORE_ERROR_INVALID_LOCATION);
+                            }
                             return;
                         }
                         LauncherAppWidgetProviderInfo widgetProviderInfo =
@@ -875,12 +952,15 @@
                                     + "x" + appWidgetInfo.spanY + " minSpan="
                                     + widgetProviderInfo.minSpanX + "x"
                                     + widgetProviderInfo.minSpanY);
-                            logWidgetInfo(mApp.getInvariantDeviceProfile(),
-                                    widgetProviderInfo);
+                            logWidgetInfo(mApp.getInvariantDeviceProfile(), widgetProviderInfo);
                         }
                         if (!c.isOnWorkspaceOrHotseat()) {
                             c.markDeleted("Widget found where container != CONTAINER_DESKTOP"
                                     + "nor CONTAINER_HOTSEAT - ignoring!");
+                            if (mIsRestoreFromBackup && restoreEventLogger != null) {
+                                restoreEventLogger.logSingleFavoritesItemRestoreFailed(
+                                        c.itemType, RESTORE_ERROR_INVALID_LOCATION);
+                            }
                             return;
                         }
 
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index dc8cd3a..20b2971 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -20,6 +20,7 @@
 
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
 import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
 import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
@@ -83,6 +84,7 @@
 
     private static final String TAG = "RestoreDbTask";
     public static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
+    public static final String FIRST_LOAD_AFTER_RESTORE_KEY = "first_load_after_restore";
 
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
@@ -340,9 +342,10 @@
      * Marks the DB state as pending restoration
      */
     public static void setPending(Context context) {
-        FileLog.d(TAG, "Restore data received through full backup");
-        LauncherPrefs.get(context)
-                .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
+        DeviceGridState deviceGridState = new DeviceGridState(context);
+        FileLog.d(TAG, "restore initiated from backup: DeviceGridState=" + deviceGridState);
+        LauncherPrefs.get(context).putSync(RESTORE_DEVICE.to(deviceGridState.getDeviceType()));
+        LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
     }
 
     @WorkerThread