Merge "Close floating panels on Launcher when using Escape" into udc-dev
diff --git a/quickstep/res/layout/taskbar_edu_features.xml b/quickstep/res/layout/taskbar_edu_features.xml
index 5cd7aaf..efbe7f8 100644
--- a/quickstep/res/layout/taskbar_edu_features.xml
+++ b/quickstep/res/layout/taskbar_edu_features.xml
@@ -13,29 +13,36 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
 
     <TextView
         android:id="@+id/title"
         style="@style/TextAppearance.TaskbarEduTooltip.Title"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
+        android:paddingBottom="@dimen/taskbar_edu_tooltip_vertical_margin"
         android:text="@string/taskbar_edu_features"
+
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/splitscreen_animation"/>
 
     <com.airbnb.lottie.LottieAnimationView
         android:id="@+id/splitscreen_animation"
         android:layout_width="@dimen/taskbar_edu_features_lottie_width"
         android:layout_height="@dimen/taskbar_edu_features_lottie_height"
-        android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/title"
+
         app:lottie_autoPlay="true"
-        app:lottie_loop="true" />
+        app:lottie_loop="true"
+
+        app:layout_constraintEnd_toEndOf="@id/splitscreen_text"
+        app:layout_constraintStart_toStartOf="@id/splitscreen_text"
+        app:layout_constraintTop_toBottomOf="@id/title" />
 
     <TextView
         android:id="@+id/splitscreen_text"
@@ -43,8 +50,9 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:text="@string/taskbar_edu_splitscreen"
-        app:layout_constraintEnd_toEndOf="@id/splitscreen_animation"
-        app:layout_constraintStart_toStartOf="@id/splitscreen_animation"
+
+        app:layout_constraintEnd_toStartOf="@id/settings_text"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/splitscreen_animation" />
 
     <androidx.constraintlayout.widget.Group
@@ -57,13 +65,14 @@
         android:id="@+id/settings_animation"
         android:layout_width="@dimen/taskbar_edu_features_lottie_width"
         android:layout_height="@dimen/taskbar_edu_features_lottie_height"
-        android:layout_marginStart="@dimen/taskbar_edu_features_horizontal_spacing"
-        android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
-        app:layout_constraintStart_toEndOf="@id/splitscreen_animation"
-        app:layout_constraintTop_toBottomOf="@id/title"
+
         app:lottie_autoPlay="true"
         app:lottie_loop="true"
-        app:lottie_rawRes="@raw/taskbar_edu_settings" />
+        app:lottie_rawRes="@raw/taskbar_edu_settings"
+
+        app:layout_constraintEnd_toEndOf="@id/settings_text"
+        app:layout_constraintStart_toStartOf="@id/settings_text"
+        app:layout_constraintTop_toBottomOf="@id/title" />
 
     <TextView
         android:id="@+id/settings_text"
@@ -71,21 +80,23 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:text="@string/taskbar_edu_settings_persistent"
-        app:layout_constraintEnd_toEndOf="@id/settings_animation"
-        app:layout_constraintStart_toStartOf="@id/settings_animation"
+        android:layout_marginStart="@dimen/taskbar_edu_features_horizontal_spacing"
+
+        app:layout_constraintEnd_toStartOf="@id/suggestions_text"
+        app:layout_constraintStart_toEndOf="@id/splitscreen_text"
         app:layout_constraintTop_toBottomOf="@id/settings_animation" />
 
     <com.airbnb.lottie.LottieAnimationView
         android:id="@+id/suggestions_animation"
         android:layout_width="@dimen/taskbar_edu_features_lottie_width"
         android:layout_height="@dimen/taskbar_edu_features_lottie_height"
-        android:layout_marginStart="@dimen/taskbar_edu_features_horizontal_spacing"
-        android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@id/settings_animation"
-        app:layout_constraintTop_toBottomOf="@id/title"
+
         app:lottie_autoPlay="true"
-        app:lottie_loop="true" />
+        app:lottie_loop="true"
+
+        app:layout_constraintEnd_toEndOf="@id/suggestions_text"
+        app:layout_constraintStart_toStartOf="@id/suggestions_text"
+        app:layout_constraintTop_toBottomOf="@id/title" />
 
     <TextView
         android:id="@+id/suggestions_text"
@@ -93,8 +104,10 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:text="@string/taskbar_edu_suggestions"
-        app:layout_constraintEnd_toEndOf="@id/suggestions_animation"
-        app:layout_constraintStart_toStartOf="@id/suggestions_animation"
+        android:layout_marginStart="@dimen/taskbar_edu_features_horizontal_spacing"
+
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@id/settings_text"
         app:layout_constraintTop_toBottomOf="@id/suggestions_animation" />
 
     <androidx.constraintlayout.widget.Barrier
@@ -108,11 +121,12 @@
         android:id="@+id/done_button"
         style="@style/TaskbarEdu.Button.Done"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
+        android:layout_height="wrap_content"
         android:layout_marginTop="32dp"
         android:text="@string/taskbar_edu_done"
         android:textColor="?androidprv:attr/textColorOnAccent"
+
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toBottomOf="@id/edu_barrier_bottom" />
-</merge>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_swipe.xml b/quickstep/res/layout/taskbar_edu_swipe.xml
index ebdfbb1..3f5e819 100644
--- a/quickstep/res/layout/taskbar_edu_swipe.xml
+++ b/quickstep/res/layout/taskbar_edu_swipe.xml
@@ -13,8 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
 
     <TextView
         android:id="@+id/title"
@@ -39,4 +41,4 @@
         app:lottie_loop="true"
         app:lottie_rawRes="@raw/taskbar_edu_stashing" />
 
-</merge>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_tooltip.xml b/quickstep/res/layout/taskbar_edu_tooltip.xml
index 657066c..f3da8b0 100644
--- a/quickstep/res/layout/taskbar_edu_tooltip.xml
+++ b/quickstep/res/layout/taskbar_edu_tooltip.xml
@@ -17,7 +17,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="bottom|center"
-    android:layout_marginBottom="16dp"
+    android:layout_marginHorizontal="24dp"
+    android:paddingBottom="16dp"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:focusable="true"
@@ -25,7 +26,7 @@
     android:gravity="center"
     android:orientation="vertical">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <FrameLayout
         android:id="@+id/content"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 959fea7..6c7decd 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -340,6 +340,8 @@
     <dimen name="taskbar_edu_features_lottie_width">170dp</dimen>
     <dimen name="taskbar_edu_features_lottie_height">106dp</dimen>
     <dimen name="taskbar_edu_features_horizontal_spacing">24dp</dimen>
+    <dimen name="taskbar_edu_features_tooltip_width_persistent">624dp</dimen>
+    <dimen name="taskbar_edu_features_tooltip_width_transient">428dp</dimen>
 
     <!--- Taskbar Pinning -->
     <dimen name="taskbar_pinning_popup_menu_width">300dp</dimen>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 6c12f11..e1afb26 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -200,7 +200,7 @@
         <item name="android:background">@drawable/button_taskbar_edu_colored</item>
         <item name="android:stateListAnimator">@null</item>
         <item name="android:textSize">16sp</item>
-        <item name="android:padding">4dp</item>
+        <item name="android:minHeight">36dp</item>
     </style>
 
     <style name="TextAppearance.TaskbarEduTooltip.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
index 7f65e41..bcae06c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
@@ -25,7 +25,9 @@
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.View
 import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.view.animation.Interpolator
+import androidx.core.view.updateLayoutParams
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.app.animation.Interpolators.STANDARD
@@ -77,6 +79,18 @@
         }
         mIsOpen = true
         activityContext.dragLayer.addView(this)
+
+        // Make sure we have enough height to display all of the content, which can be an issue on
+        // large text and display scaling configurations. If we run out of height, remove the width
+        // constraint to reduce the number of lines of text and hopefully free up some height.
+        activityContext.dragLayer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+        if (
+            measuredHeight + activityContext.deviceProfile.taskbarHeight >=
+                activityContext.deviceProfile.availableHeightPx
+        ) {
+            updateLayoutParams { width = MATCH_PARENT }
+        }
+
         openCloseAnimator = createOpenCloseAnimator(isOpening = true).apply { start() }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 2c686b8..e99fa50 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -19,11 +19,13 @@
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
-import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.MarginLayoutParams
 import android.view.accessibility.AccessibilityEvent
 import android.view.accessibility.AccessibilityNodeInfo
 import androidx.annotation.IntDef
 import androidx.annotation.LayoutRes
+import androidx.core.view.updateLayoutParams
 import com.airbnb.lottie.LottieAnimationView
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
@@ -127,11 +129,24 @@
                 settingsEdu.visibility = VISIBLE
             }
 
-            findViewById<View>(R.id.done_button)?.setOnClickListener { hide() }
-            if (DisplayController.isTransientTaskbar(activityContext)) {
-                (layoutParams as ViewGroup.MarginLayoutParams).bottomMargin +=
-                    activityContext.deviceProfile.taskbarHeight
+            // Set up layout parameters.
+            content.updateLayoutParams { width = MATCH_PARENT }
+            updateLayoutParams<MarginLayoutParams> {
+                if (DisplayController.isTransientTaskbar(activityContext)) {
+                    width =
+                        resources.getDimensionPixelSize(
+                            R.dimen.taskbar_edu_features_tooltip_width_transient
+                        )
+                    bottomMargin += activityContext.deviceProfile.taskbarHeight
+                } else {
+                    width =
+                        resources.getDimensionPixelSize(
+                            R.dimen.taskbar_edu_features_tooltip_width_persistent
+                        )
+                }
             }
+
+            findViewById<View>(R.id.done_button)?.setOnClickListener { hide() }
             show()
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 476e0a8..8de0e40 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.quickstep.views.DesktopTaskView;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
@@ -65,7 +66,9 @@
 
         @Override
         public void onTaskMovedToFront(int taskId) {
-            mProxyView.close(false);
+            if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+                mProxyView.close(false);
+            }
         }
     };
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 4540eee..6243471 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -27,7 +27,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.Taskbar;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.LauncherLayoutBuilder;
@@ -64,10 +63,6 @@
                 "com.android.launcher3.testcomponent.BaseTestingActivity");
         mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
         TaplTestsLauncher3.initialize(this);
-        Overview overview = mLauncher.getWorkspace().switchToOverview();
-        if (overview.hasTasks()) {
-            overview.dismissAllTasks();
-        }
 
         startAppFast(CALCULATOR_APP_PACKAGE);
         mLauncher.enableBlockTimeout(true);
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1aa1871..786088e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -32,12 +32,6 @@
     <dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
     <dimen name="spring_loaded_hotseat_top_margin">76dp</dimen>
 
-    <!-- Qsb -->
-    <!-- Used for adjusting the position of QSB when placed in hotseat. This is a ratio and a higher
-     number signifies that the QSB is close to the hotseat icons and a lower number signifies that
-      it is close to the bottom of the screen -->
-    <item name="qsb_center_factor" format="float" type="dimen">0.325</item>
-
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
     <!-- Scalable Grid -->
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index bc4a5c3..7131452 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,7 +1,5 @@
 package com.android.launcher3;
 
-import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
-
 import static com.android.launcher3.CellLayout.SPRING_LOADED_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
@@ -77,8 +75,6 @@
     private DragLayer mDragLayer;
     private ImageButton mReconfigureButton;
 
-    private Rect mWidgetPadding;
-
     private final int mBackgroundPadding;
     private final int mTouchTargetWidth;
 
@@ -218,9 +214,6 @@
         mMaxHSpan = info.maxSpanX;
         mMaxVSpan = info.maxSpanY;
 
-        mWidgetPadding = getDefaultPaddingForWidget(getContext(),
-                widgetView.getAppWidgetInfo().provider, null);
-
         // Only show resize handles for the directions in which resizing is possible.
         InvariantDeviceProfile idp = LauncherAppState.getIDP(cellLayout.getContext());
         mVerticalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0
@@ -517,16 +510,12 @@
      */
     private void getSnappedRectRelativeToDragLayer(Rect out) {
         float scale = mWidgetView.getScaleToFit();
-
         mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
 
-        int width = 2 * mBackgroundPadding
-                + (int) (scale * (out.width() - mWidgetPadding.left - mWidgetPadding.right));
-        int height = 2 * mBackgroundPadding
-                + (int) (scale * (out.height() - mWidgetPadding.top - mWidgetPadding.bottom));
-
-        int x = (int) (out.left - mBackgroundPadding + scale * mWidgetPadding.left);
-        int y = (int) (out.top - mBackgroundPadding + scale * mWidgetPadding.top);
+        int width = 2 * mBackgroundPadding + Math.round(scale * out.width());
+        int height = 2 * mBackgroundPadding + Math.round(scale * out.height());
+        int x = out.left - mBackgroundPadding;
+        int y = out.top - mBackgroundPadding;
 
         out.left = x;
         out.top = y;
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 5fbd48c..f041ffb 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -16,8 +16,8 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.model.LoaderTask;
-import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
@@ -52,7 +52,7 @@
      * Updates the app widgets whose id has changed during the restore process.
      */
     @WorkerThread
-    public static void restoreAppWidgetIds(Context context, ModelDbController controller,
+    public static void restoreAppWidgetIds(Context context, DatabaseHelper helper,
             int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             Log.e(TAG, "Skipping widget ID remap as widgets not supported");
@@ -92,12 +92,12 @@
             final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
             final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
             int result = new ContentWriter(context,
-                            new ContentWriter.CommitParams(controller, where, args))
+                            new ContentWriter.CommitParams(helper, where, args))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
             if (result == 0) {
-                Cursor cursor = controller.getDb().query(
+                Cursor cursor = helper.getWritableDatabase().query(
                         Favorites.TABLE_NAME,
                         new String[] {Favorites.APPWIDGET_ID},
                         "appWidgetId=?", new String[] { oldWidgetId }, null, null, null);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 86c9f16..8675226 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -67,14 +67,12 @@
     private static final int DEFAULT_DOT_SIZE = 100;
     private static final float ALL_APPS_TABLET_MAX_ROWS = 5.5f;
     private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
+    private static final float MIN_WIDGET_PADDING_DP = 6f;
 
     public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
     public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
     public static final Consumer<DeviceProfile> DEFAULT_DIMENSION_PROVIDER = dp -> {};
 
-    // Ratio of empty space, qsb should take up to appear visually centered.
-    private final float mQsbCenterFactor;
-
     public final InvariantDeviceProfile inv;
     private final Info mInfo;
     private final DisplayMetrics mMetrics;
@@ -252,6 +250,10 @@
     // Insets
     private final Rect mInsets = new Rect();
     public final Rect workspacePadding = new Rect();
+    // Additional padding added to the widget inside its cellSpace. It is applied outside
+    // the widgetView, such that the actual view size is same as the widget size.
+    public final Rect widgetPadding = new Rect();
+
     // When true, nav bar is on the left side of the screen.
     private boolean mIsSeascape;
 
@@ -314,9 +316,6 @@
         availableHeightPx = windowBounds.availableSize.y;
 
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
-        boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
-        mQsbCenterFactor = res.getFloat(R.dimen.qsb_center_factor);
-
         if (isTwoPanels) {
             if (isLandscape) {
                 mTypeIndex = INDEX_TWO_PANEL_LANDSCAPE;
@@ -730,22 +729,6 @@
         return mInfo;
     }
 
-    /**
-     * We inset the widget padding added by the system and instead rely on the border spacing
-     * between cells to create reliable consistency between widgets
-     */
-    public boolean shouldInsetWidgets() {
-        Rect widgetPadding = inv.defaultWidgetPadding;
-
-        // Check all sides to ensure that the widget won't overlap into another cell, or into
-        // status bar.
-        return workspaceTopPadding > widgetPadding.top
-                && cellLayoutBorderSpacePx.x > widgetPadding.left
-                && cellLayoutBorderSpacePx.y > widgetPadding.top
-                && cellLayoutBorderSpacePx.x > widgetPadding.right
-                && cellLayoutBorderSpacePx.y > widgetPadding.bottom;
-    }
-
     public Builder toBuilder(Context context) {
         WindowBounds bounds = new WindowBounds(
                 widthPx, heightPx, availableWidthPx, availableHeightPx, rotationHint);
@@ -999,6 +982,18 @@
         // Folder icon
         folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
         folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
+
+        // Update widget padding:
+        float minSpacing = pxFromDp(MIN_WIDGET_PADDING_DP, mMetrics);
+        if (cellLayoutBorderSpacePx.x < minSpacing
+                || cellLayoutBorderSpacePx.y < minSpacing) {
+            widgetPadding.left = widgetPadding.right =
+                    Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.x));
+            widgetPadding.top = widgetPadding.bottom =
+                    Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.y));
+        } else {
+            widgetPadding.setEmpty();
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5e07a3c..3aa582d 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -26,8 +26,6 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.annotation.TargetApi;
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -35,7 +33,6 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -192,7 +189,6 @@
     public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
 
     public Point defaultWallpaperSize;
-    public Rect defaultWidgetPadding;
 
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
 
@@ -443,9 +439,6 @@
                     deviceProfile.numShownHotseatIcons = numMinShownHotseatIconsForTablet;
                     deviceProfile.recalculateHotseatWidthAndBorderSpace();
                 });
-
-        ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
-        defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
     }
 
     public void addOnChangeListener(OnIDPChangeListener listener) {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 9abec50..0df4bd4 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -263,6 +263,18 @@
                 getModelDbController().refreshHotseatRestoreTable();
                 return null;
             }
+            case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        getModelDbController().updateCurrentOpenHelper(arg /* dbFile */));
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        getModelDbController().prepareForPreview(arg /* dbFile */));
+                return result;
+            }
         }
         return null;
     }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 7fda326..b65e96b 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -148,6 +148,11 @@
         public static final String HYBRID_HOTSEAT_BACKUP_TABLE = "hotseat_restore_backup";
 
         /**
+         * Temporary table used specifically for grid migrations during wallpaper preview
+         */
+        public static final String PREVIEW_TABLE_NAME = "favorites_preview";
+
+        /**
          * Temporary table used specifically for multi-db grid migrations
          */
         public static final String TMP_TABLE = "favorites_tmp";
@@ -159,6 +164,18 @@
                 + LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
 
         /**
+         * The content:// style URL for "favorites_preview" table
+         */
+        public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
+
+        /**
+         * The content:// style URL for "favorites_tmp" table
+         */
+        public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
+
+        /**
          * The content:// style URL for a given row, identified by its id.
          *
          * @param id The row id.
@@ -359,6 +376,10 @@
 
         public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
 
+        public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
+
+        public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
+
         public static final String EXTRA_VALUE = "value";
 
         public static final String EXTRA_DB_NAME = "db_name";
@@ -372,8 +393,11 @@
         }
 
         public static Bundle call(ContentResolver cr, String method, String arg) {
-            return cr.call(CONTENT_URI, method, arg, null);
+            return call(cr, method, arg, null /* extras */);
         }
 
+        public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) {
+            return cr.call(CONTENT_URI, method, arg, extras);
+        }
     }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index b00199f..a0ceefb 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -46,8 +46,6 @@
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private final int[] mTmpCellXY = new int[2];
 
-    private final Rect mTempRect = new Rect();
-
     @ContainerType
     private final int mContainerType;
     private final WallpaperManager mWallpaperManager;
@@ -124,13 +122,12 @@
         CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
         if (child instanceof NavigableAppWidgetHostView) {
             DeviceProfile profile = mActivity.getDeviceProfile();
-            ((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
             final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
+                    appWidgetScale.x, appWidgetScale.y, mBorderSpace, profile.widgetPadding);
         } else {
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    mBorderSpace, null);
+                    mBorderSpace);
         }
     }
 
@@ -149,13 +146,12 @@
         final DeviceProfile dp = mActivity.getDeviceProfile();
 
         if (child instanceof NavigableAppWidgetHostView) {
-            ((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
             final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag());
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
+                    appWidgetScale.x, appWidgetScale.y, mBorderSpace, dp.widgetPadding);
         } else {
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
-                    mBorderSpace, null);
+                    mBorderSpace);
             // Center the icon/folder
             int cHeight = getCellContentHeight();
             int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6e6f1ac..98016f6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2912,9 +2912,8 @@
         Rect r = estimateItemPosition(layout, targetCell[0], targetCell[1], spanX, spanY);
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
             DeviceProfile profile = mLauncher.getDeviceProfile();
-            if (profile.shouldInsetWidgets() && finalView instanceof NavigableAppWidgetHostView) {
-                Rect widgetPadding = new Rect();
-                ((NavigableAppWidgetHostView) finalView).getWidgetInset(profile, widgetPadding);
+            if (finalView instanceof NavigableAppWidgetHostView) {
+                Rect widgetPadding = profile.widgetPadding;
                 r.left -= widgetPadding.left;
                 r.right += widgetPadding.right;
                 r.top -= widgetPadding.top;
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
index 4b6a062..bdf7643 100644
--- a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -113,13 +113,13 @@
      * full/invariant device profile sizes.
      */
     public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
-            int rowCount, Point borderSpace, @Nullable Rect inset) {
+            int rowCount, Point borderSpace) {
         setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
-                borderSpace, inset);
+                borderSpace, null);
     }
 
     /**
-     * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
+     * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point)},
      * if the view needs to be scaled.
      *
      * ie. In multi-window mode, we setup widgets so that they are measured and laid out
@@ -150,10 +150,10 @@
             y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
 
             if (inset != null) {
-                x -= inset.left;
-                y -= inset.top;
-                width += inset.left + inset.right;
-                height += inset.top + inset.bottom;
+                x += inset.left;
+                y += inset.top;
+                width -= inset.left + inset.right;
+                height -= inset.top + inset.bottom;
             }
         }
     }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 7f49aa9..b438e86 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -451,26 +451,8 @@
         final Size origSize = WidgetSizes.getWidgetSizePx(mDpOrig,
                 launcherWidgetSize.getWidth(), launcherWidgetSize.getHeight());
         final Size newSize = WidgetSizes.getWidgetSizePx(mDp, info.spanX, info.spanY);
-        final Rect previewInset = new Rect();
-        final Rect origInset = new Rect();
-        // When the setup() is called for the LayoutParams, insets are added to the width
-        // and height of the view. This is not accounted for in WidgetSizes and is handled
-        // here.
-        if (mDp.shouldInsetWidgets()) {
-            previewInset.set(mDp.inv.defaultWidgetPadding);
-        } else {
-            previewInset.setEmpty();
-        }
-        if (mDpOrig.shouldInsetWidgets()) {
-            origInset.set(mDpOrig.inv.defaultWidgetPadding);
-        } else {
-            origInset.setEmpty();
-        }
-
-        return new PointF((float) newSize.getWidth() / (origSize.getWidth()
-                + origInset.left + origInset.right),
-                (float) newSize.getHeight() / (origSize.getHeight()
-                + origInset.top + origInset.bottom));
+        return new PointF((float) newSize.getWidth() / origSize.getWidth(),
+                (float) newSize.getHeight() / origSize.getHeight());
     }
 
     private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 8f0b8ec..372e9bf 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.graphics;
 
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
@@ -53,8 +52,6 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.GridSizeMigrationUtil;
 import com.android.launcher3.model.LoaderTask;
-import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
@@ -148,9 +145,7 @@
         final String query = LauncherSettings.Favorites.ITEM_TYPE + " = "
                 + LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 
-        ModelDbController mainController =
-                LauncherAppState.getInstance(mContext).getModel().getModelDbController();
-        try (Cursor c = mainController.query(TABLE_NAME,
+        try (Cursor c = context.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[] {
                         LauncherSettings.Favorites.APPWIDGET_ID,
                         LauncherSettings.Favorites.SPANX,
@@ -195,6 +190,8 @@
 
     @WorkerThread
     private void loadModelData() {
+        final boolean migrated = doGridMigrationIfNecessary();
+
         final Context inflationContext;
         if (mWallpaperColors != null) {
             // Create a themed context, without affecting the main application context
@@ -212,20 +209,8 @@
                     Themes.getActivityThemeRes(mContext));
         }
 
-        if (GridSizeMigrationUtil.needsToMigrate(inflationContext, mIdp)) {
-            // Start the migration
+        if (migrated) {
             PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
-            // Copy existing data to preview DB
-            LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
-                    .getModel().getModelDbController().getDb(),
-                    TABLE_NAME,
-                    LauncherAppState.getInstance(previewContext)
-                            .getModel().getModelDbController().getDb(),
-                    TABLE_NAME,
-                    mContext);
-            LauncherAppState.getInstance(previewContext)
-                    .getModel().getModelDbController().clearEmptyDbFlag();
-
             new LoaderTask(
                     LauncherAppState.getInstance(previewContext),
                     /* bgAllAppsList= */ null,
@@ -244,7 +229,8 @@
                         query += " or " + LauncherSettings.Favorites.SCREEN + " = "
                                 + Workspace.SECOND_SCREEN_ID;
                     }
-                    loadWorkspace(new ArrayList<>(), query, null);
+                    loadWorkspaceForPreviewSurfaceRenderer(new ArrayList<>(),
+                            LauncherSettings.Favorites.PREVIEW_CONTENT_URI, query);
 
                     final SparseArray<Size> spanInfo =
                             getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
@@ -267,6 +253,14 @@
         }
     }
 
+    @WorkerThread
+    private boolean doGridMigrationIfNecessary() {
+        if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
+            return false;
+        }
+        return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
+    }
+
     @UiThread
     private void renderView(Context inflationContext, BgDataModel dataModel,
             Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index ecf5f67..dc5fcf7 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -36,6 +36,9 @@
 
 import com.android.launcher3.AutoInstallsLayout;
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
@@ -55,7 +58,6 @@
 import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.Locale;
-import java.util.function.ToLongFunction;
 import java.util.stream.Collectors;
 
 /**
@@ -74,23 +76,45 @@
     private static final boolean LOGD = false;
 
     private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
+    public static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
     private final Context mContext;
-    private final ToLongFunction<UserHandle> mUserSerialProvider;
-    private final Runnable mOnEmptyDbCreateCallback;
-
+    private final boolean mForMigration;
     private int mMaxItemId = -1;
     public boolean mHotseatRestoreTableExists;
 
+    public static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) {
+        return createDatabaseHelper(context, null, forMigration);
+    }
+
+    public static DatabaseHelper createDatabaseHelper(Context context, String dbName,
+            boolean forMigration) {
+        if (dbName == null) {
+            dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile;
+        }
+        DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration);
+        // Table creation sometimes fails silently, which leads to a crash loop.
+        // This way, we will try to create a table every time after crash, so the device
+        // would eventually be able to recover.
+        if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
+            Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
+            // This operation is a no-op if the table already exists.
+            databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
+        }
+        databaseHelper.mHotseatRestoreTableExists = tableExists(
+                databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
+
+        databaseHelper.initIds();
+        return databaseHelper;
+    }
+
     /**
      * Constructor used in tests and for restore.
      */
-    public DatabaseHelper(Context context, String dbName,
-            ToLongFunction<UserHandle> userSerialProvider, Runnable onEmptyDbCreateCallback) {
+    public DatabaseHelper(Context context, String dbName, boolean forMigration) {
         super(context, dbName, SCHEMA_VERSION);
         mContext = context;
-        mUserSerialProvider = userSerialProvider;
-        mOnEmptyDbCreateCallback = onEmptyDbCreateCallback;
+        mForMigration = forMigration;
     }
 
     protected void initIds() {
@@ -107,11 +131,13 @@
 
         mMaxItemId = 1;
 
-        addTableToDb(db, getDefaultUserSerial(), false /* optional */);
+        addFavoritesTable(db, false);
 
         // Fresh and clean launcher DB.
         mMaxItemId = initializeMaxItemId(db);
-        mOnEmptyDbCreateCallback.run();
+        if (!mForMigration) {
+            onEmptyDbCreated();
+        }
     }
 
     public void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -121,8 +147,38 @@
         }
     }
 
-    private long getDefaultUserSerial() {
-        return mUserSerialProvider.applyAsLong(Process.myUserHandle());
+    /**
+     * Re-composite given key in respect to database. If the current db is
+     * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
+     * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
+     * string will be "EMPTY_DATABASE_CREATED@minimal.db".
+     */
+    public String getKey(final String key) {
+        if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) {
+            return key;
+        }
+        return key + "@" + getDatabaseName();
+    }
+
+    /**
+     * Overridden in tests.
+     */
+    protected void onEmptyDbCreated() {
+        // Set the flag for empty DB
+        LauncherPrefs.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
+                .commit();
+    }
+
+    public long getSerialNumberForUser(UserHandle user) {
+        return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
+    }
+
+    public long getDefaultUserSerial() {
+        return getSerialNumberForUser(Process.myUserHandle());
+    }
+
+    private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
+        Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 9a6cde6..eded5ea 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -16,9 +16,6 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
-import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.content.ComponentName;
@@ -37,15 +34,16 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
@@ -91,38 +89,81 @@
         return needsToMigrate;
     }
 
+    /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
+    public static boolean migrateGridIfNeeded(Context context) {
+        if (context instanceof LauncherPreviewRenderer.PreviewContext) {
+            return true;
+        }
+        return migrateGridIfNeeded(context, null);
+    }
+
     /**
-     * When migrating the grid, we copy the table
-     * {@link LauncherSettings.Favorites#TABLE_NAME} from {@code source} into
-     * {@link LauncherSettings.Favorites#TMP_TABLE}, run the grid size migration algorithm
+     * When migrating the grid for preview, we copy the table
+     * {@link LauncherSettings.Favorites#TABLE_NAME} into
+     * {@link LauncherSettings.Favorites#PREVIEW_TABLE_NAME}, run grid size migration from the
+     * former to the later, then use the later table for preview.
+     *
+     * Similarly when doing the actual grid migration, the former grid option's table
+     * {@link LauncherSettings.Favorites#TABLE_NAME} is copied into the new grid option's
+     * {@link LauncherSettings.Favorites#TMP_TABLE}, we then run the grid size migration algorithm
      * to migrate the later to the former, and load the workspace from the default
      * {@link LauncherSettings.Favorites#TABLE_NAME}.
      *
      * @return false if the migration failed.
      */
-    public static boolean migrateGridIfNeeded(
-            @NonNull Context context,
-            @NonNull InvariantDeviceProfile idp,
-            @NonNull DatabaseHelper target,
-            @NonNull SQLiteDatabase source) {
+    public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
+        boolean migrateForPreview = idp != null;
+        if (!migrateForPreview) {
+            idp = LauncherAppState.getIDP(context);
+        }
 
         DeviceGridState srcDeviceState = new DeviceGridState(context);
         DeviceGridState destDeviceState = new DeviceGridState(idp);
         if (!needsToMigrate(srcDeviceState, destDeviceState)) {
             return true;
         }
-        copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
 
         HashSet<String> validPackages = getValidPackages(context);
+
+        if (migrateForPreview) {
+            if (!LauncherSettings.Settings.call(
+                    context.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW,
+                    destDeviceState.getDbFile()).getBoolean(
+                    LauncherSettings.Settings.EXTRA_VALUE)) {
+                return false;
+            }
+        } else if (!LauncherSettings.Settings.call(
+                context.getContentResolver(),
+                LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER,
+                destDeviceState.getDbFile()).getBoolean(
+                LauncherSettings.Settings.EXTRA_VALUE)) {
+            return false;
+        }
+
         long migrationStartTime = System.currentTimeMillis();
-        try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
-            DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context, validPackages);
-            DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context, validPackages);
+        try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
+                context.getContentResolver(),
+                LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
+                LauncherSettings.Settings.EXTRA_VALUE)) {
+
+            DbReader srcReader = new DbReader(t.getDb(),
+                    migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
+                            : LauncherSettings.Favorites.TMP_TABLE,
+                    context, validPackages);
+            DbReader destReader = new DbReader(t.getDb(),
+                    migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
+                            : LauncherSettings.Favorites.TABLE_NAME,
+                    context, validPackages);
 
             Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
-            migrate(target, srcReader, destReader, destDeviceState.getNumHotseat(),
+            migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
                     targetSize, srcDeviceState, destDeviceState);
-            dropTable(t.getDb(), TMP_TABLE);
+
+            if (!migrateForPreview) {
+                dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
+            }
+
             t.commit();
             return true;
         } catch (Exception e) {
@@ -133,7 +174,7 @@
             Log.v(TAG, "Workspace migration completed in "
                     + (System.currentTimeMillis() - migrationStartTime));
 
-            if (!(context instanceof SandboxContext)) {
+            if (!migrateForPreview) {
                 // Save current configuration, so that the migration does not run again.
                 destDeviceState.writeToPrefs(context);
             }
@@ -141,7 +182,7 @@
     }
 
     public static boolean migrate(
-            @NonNull DatabaseHelper helper,
+            @NonNull final Context context, @NonNull final SQLiteDatabase db,
             @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
             final int destHotseatSize, @NonNull final Point targetSize,
             @NonNull final DeviceGridState srcDeviceState,
@@ -193,8 +234,8 @@
         Collections.sort(workspaceToBeAdded);
 
         // Migrate hotseat
-        solveHotseatPlacement(helper, destHotseatSize,
-                srcReader, destReader, dstHotseatItems, hotseatToBeAdded);
+        solveHotseatPlacement(db, srcReader,
+                destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded);
 
         // Migrate workspace.
         // First we create a collection of the screens
@@ -214,8 +255,8 @@
             if (DEBUG) {
                 Log.d(TAG, "Migrating " + screenId);
             }
-            solveGridPlacement(helper, srcReader,
-                    destReader, screenId, trgX, trgY, workspaceToBeAdded, false);
+            solveGridPlacement(db, srcReader,
+                    destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false);
             if (workspaceToBeAdded.isEmpty()) {
                 break;
             }
@@ -225,8 +266,8 @@
         // any of the screens, in this case we add them to new screens until all of them are placed.
         int screenId = destReader.mLastScreenId + 1;
         while (!workspaceToBeAdded.isEmpty()) {
-            solveGridPlacement(helper, srcReader,
-                    destReader, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
+            solveGridPlacement(db, srcReader,
+                    destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
             screenId++;
         }
 
@@ -257,33 +298,33 @@
         });
     }
 
-    private static void insertEntryInDb(DatabaseHelper helper, DbEntry entry,
+    private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
             String srcTableName, String destTableName) {
-        int id = copyEntryAndUpdate(helper, entry, srcTableName, destTableName);
+        int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
 
         if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             for (Set<Integer> itemIds : entry.mFolderItems.values()) {
                 for (int itemId : itemIds) {
-                    copyEntryAndUpdate(helper, itemId, id, srcTableName, destTableName);
+                    copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
                 }
             }
         }
     }
 
-    private static int copyEntryAndUpdate(DatabaseHelper helper,
+    private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
             DbEntry entry, String srcTableName, String destTableName) {
-        return copyEntryAndUpdate(helper, entry, -1, -1, srcTableName, destTableName);
+        return copyEntryAndUpdate(db, context, entry, -1, -1, srcTableName, destTableName);
     }
 
-    private static int copyEntryAndUpdate(DatabaseHelper helper,
+    private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
             int id, int folderId, String srcTableName, String destTableName) {
-        return copyEntryAndUpdate(helper, null, id, folderId, srcTableName, destTableName);
+        return copyEntryAndUpdate(db, context, null, id, folderId, srcTableName, destTableName);
     }
 
-    private static int copyEntryAndUpdate(DatabaseHelper helper, DbEntry entry,
-            int id, int folderId, String srcTableName, String destTableName) {
+    private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+            DbEntry entry, int id, int folderId, String srcTableName, String destTableName) {
         int newId = -1;
-        Cursor c = helper.getWritableDatabase().query(srcTableName, null,
+        Cursor c = db.query(srcTableName, null,
                 LauncherSettings.Favorites._ID + " = '" + (entry != null ? entry.id : id) + "'",
                 null, null, null, null);
         while (c.moveToNext()) {
@@ -294,9 +335,11 @@
             } else {
                 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
             }
-            newId = helper.generateNewItemId();
+            newId = LauncherSettings.Settings.call(context.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
+                    LauncherSettings.Settings.EXTRA_VALUE);
             values.put(LauncherSettings.Favorites._ID, newId);
-            helper.getWritableDatabase().insert(destTableName, null, values);
+            db.insert(destTableName, null, values);
         }
         c.close();
         return newId;
@@ -324,9 +367,9 @@
         return validPackages;
     }
 
-    private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
+    private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
             @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
-            final int screenId, final int trgX, final int trgY,
+            @NonNull final Context context, final int screenId, final int trgX, final int trgY,
             @NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
         final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
         final Point trg = new Point(trgX, trgY);
@@ -348,7 +391,7 @@
                 continue;
             }
             if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
-                insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
+                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                 iterator.remove();
             }
         }
@@ -385,9 +428,9 @@
         return false;
     }
 
-    private static void solveHotseatPlacement(
-            @NonNull final DatabaseHelper helper, final int hotseatSize,
+    private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
             @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
+            @NonNull final Context context, final int hotseatSize,
             @NonNull final  List<DbEntry> placedHotseatItems,
             @NonNull final List<DbEntry> itemsToPlace) {
 
@@ -404,7 +447,7 @@
                 // to something other than -1.
                 entry.cellX = i;
                 entry.cellY = 0;
-                insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
+                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                 occupied[entry.screenId] = true;
             }
         }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 2054d93..a5dccc1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,16 +16,16 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.CursorWrapper;
+import android.net.Uri;
 import android.os.UserHandle;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
@@ -66,7 +66,9 @@
     private final LongSparseArray<UserHandle> allUsers;
 
     private final LauncherAppState mApp;
+    private final Uri mContentUri;
     private final Context mContext;
+    private final PackageManager mPM;
     private final IconCache mIconCache;
     private final InvariantDeviceProfile mIDP;
 
@@ -106,14 +108,17 @@
     public int itemType;
     public int restoreFlag;
 
-    public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState) {
+    public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app,
+            UserManagerState userManagerState) {
         super(cursor);
 
         mApp = app;
         allUsers = userManagerState.allUsers;
+        mContentUri = contentUri;
         mContext = app.getContext();
         mIconCache = app.getIconCache();
         mIDP = app.getInvariantDeviceProfile();
+        mPM = mContext.getPackageManager();
 
         // Init column indices
         mIconIndex = getColumnIndexOrThrow(Favorites.ICON);
@@ -385,7 +390,7 @@
      */
     public ContentWriter updater() {
        return new ContentWriter(mContext, new ContentWriter.CommitParams(
-               mApp.getModel().getModelDbController(),
+               mApp.getModel().getModelDbController().getDatabaseHelper(),
                BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
     }
 
@@ -404,8 +409,8 @@
     public boolean commitDeleted() {
         if (mItemsToRemove.size() > 0) {
             // Remove dead items
-            mApp.getModel().getModelDbController().delete(TABLE_NAME,
-                    Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
+            mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery(
+                    Favorites._ID, mItemsToRemove), null);
             return true;
         }
         return false;
@@ -430,8 +435,9 @@
             // Update restored items that no longer require special handling
             ContentValues values = new ContentValues();
             values.put(Favorites.RESTORED, 0);
-            mApp.getModel().getModelDbController().update(TABLE_NAME, values,
-                    Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
+            mContext.getContentResolver().update(mContentUri, values,
+                    Utilities.createDbSelectionQuery(
+                            Favorites._ID, mRestoredRows), null);
         }
     }
 
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d4eded5..9053d19 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -42,6 +41,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -50,6 +50,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.TimingLogger;
 
 import androidx.annotation.Nullable;
 
@@ -199,10 +200,25 @@
         }
 
         Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
+        TimingLogger timingLogger = new TimingLogger(TAG, "run");
         LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
-            loadWorkspace(allShortcuts, "", memoryLogger);
+            Trace.beginSection("LoadWorkspace");
+            try {
+                loadWorkspace(allShortcuts, memoryLogger);
+            } finally {
+                Trace.endSection();
+            }
+            logASplit(timingLogger, "loadWorkspace");
+
+            if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
+                verifyNotStopped();
+                mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
+                        mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
+                mModelDelegate.markActive();
+                logASplit(timingLogger, "workspaceDelegateItems");
+            }
 
             // 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
@@ -212,21 +228,21 @@
                 verifyNotStopped();
                 sanitizeFolders(mItemsDeleted);
                 sanitizeWidgetsShortcutsAndPackages();
-                logASplit("sanitizeData");
+                logASplit(timingLogger, "sanitizeData");
             }
 
             verifyNotStopped();
             mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
-            logASplit("bindWorkspace");
+            logASplit(timingLogger, "bindWorkspace");
 
             mModelDelegate.workspaceLoadComplete();
             // Notify the installer packages of packages with active installs on the first screen.
             sendFirstScreenActiveInstallsBroadcast();
-            logASplit("sendFirstScreenActiveInstallsBroadcast");
+            logASplit(timingLogger, "sendFirstScreenActiveInstallsBroadcast");
 
             // Take a break
             waitForIdle();
-            logASplit("step 1 complete");
+            logASplit(timingLogger, "step 1 complete");
             verifyNotStopped();
 
             // second step
@@ -237,16 +253,16 @@
             } finally {
                 Trace.endSection();
             }
-            logASplit("loadAllApps");
+            logASplit(timingLogger, "loadAllApps");
 
             if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
                 mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
                         mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
-                logASplit("allAppsDelegateItems");
+                logASplit(timingLogger, "allAppsDelegateItems");
             }
             verifyNotStopped();
             mLauncherBinder.bindAllApps();
-            logASplit("bindAllApps");
+            logASplit(timingLogger, "bindAllApps");
 
             verifyNotStopped();
             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
@@ -254,73 +270,75 @@
             updateHandler.updateIcons(allActivityList,
                     LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                     mApp.getModel()::onPackageIconsUpdated);
-            logASplit("update icon cache");
+            logASplit(timingLogger, "update icon cache");
 
             verifyNotStopped();
-            logASplit("save shortcuts in icon cache");
+            logASplit(timingLogger, "save shortcuts in icon cache");
             updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                     mApp.getModel()::onPackageIconsUpdated);
 
             // Take a break
             waitForIdle();
-            logASplit("step 2 complete");
+            logASplit(timingLogger, "step 2 complete");
             verifyNotStopped();
 
             // third step
             List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
-            logASplit("loadDeepShortcuts");
+            logASplit(timingLogger, "loadDeepShortcuts");
 
             verifyNotStopped();
             mLauncherBinder.bindDeepShortcuts();
-            logASplit("bindDeepShortcuts");
+            logASplit(timingLogger, "bindDeepShortcuts");
 
             verifyNotStopped();
-            logASplit("save deep shortcuts in icon cache");
+            logASplit(timingLogger, "save deep shortcuts in icon cache");
             updateHandler.updateIcons(allDeepShortcuts,
                     new ShortcutCachingLogic(), (pkgs, user) -> { });
 
             // Take a break
             waitForIdle();
-            logASplit("step 3 complete");
+            logASplit(timingLogger, "step 3 complete");
             verifyNotStopped();
 
             // fourth step
             List<ComponentWithLabelAndIcon> allWidgetsList =
                     mBgDataModel.widgetsModel.update(mApp, null);
-            logASplit("load widgets");
+            logASplit(timingLogger, "load widgets");
 
             verifyNotStopped();
             mLauncherBinder.bindWidgets();
-            logASplit("bindWidgets");
+            logASplit(timingLogger, "bindWidgets");
             verifyNotStopped();
 
             if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
                 mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
-                logASplit("otherDelegateItems");
+                logASplit(timingLogger, "otherDelegateItems");
                 verifyNotStopped();
             }
 
             updateHandler.updateIcons(allWidgetsList,
                     new ComponentWithIconCachingLogic(mApp.getContext(), true),
                     mApp.getModel()::onWidgetLabelsUpdated);
-            logASplit("save widgets in icon cache");
+            logASplit(timingLogger, "save widgets in icon cache");
 
             // fifth step
             loadFolderNames();
 
             verifyNotStopped();
             updateHandler.finish();
-            logASplit("finish icon update");
+            logASplit(timingLogger, "finish icon update");
 
             mModelDelegate.modelLoadComplete();
             transaction.commit();
             memoryLogger.clearLogs();
         } catch (CancellationException e) {
             // Loader stopped, ignore
-            logASplit("Cancelled");
+            logASplit(timingLogger, "Cancelled");
         } catch (Exception e) {
             memoryLogger.printLogs();
             throw e;
+        } finally {
+            timingLogger.dumpToLog();
         }
         TraceHelper.INSTANCE.endSection(traceToken);
     }
@@ -330,29 +348,25 @@
         this.notify();
     }
 
-    protected void loadWorkspace(
-            List<ShortcutInfo> allDeepShortcuts,
-            String selection,
-            LoaderMemoryLogger memoryLogger) {
-        Trace.beginSection("LoadWorkspace");
-        try {
-            loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger);
-        } finally {
-            Trace.endSection();
-        }
-        logASplit("loadWorkspace");
+    private void loadWorkspace(
+            List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger memoryLogger) {
+        loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI,
+                null /* selection */, memoryLogger);
+    }
 
+    protected void loadWorkspaceForPreviewSurfaceRenderer(
+            List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection) {
+        loadWorkspace(allDeepShortcuts, contentUri, selection, null);
         if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
-            verifyNotStopped();
             mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
                     mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
             mModelDelegate.markActive();
-            logASplit("workspaceDelegateItems");
         }
     }
 
-    private void loadWorkspaceImpl(
+    protected void loadWorkspace(
             List<ShortcutInfo> allDeepShortcuts,
+            Uri contentUri,
             String selection,
             @Nullable LoaderMemoryLogger memoryLogger) {
         final Context context = mApp.getContext();
@@ -363,7 +377,7 @@
         final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
 
         boolean clearDb = false;
-        if (!mApp.getModel().getModelDbController().migrateGridIfNeeded()) {
+        if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
             // Migration failed. Clear workspace.
             clearDb = true;
         }
@@ -388,9 +402,8 @@
             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
 
             mShortcutKeyToPinnedShortcuts = new HashMap<>();
-            ModelDbController dbController = mApp.getModel().getModelDbController();
             final LoaderCursor c = new LoaderCursor(
-                    dbController.query(TABLE_NAME, null, selection, null, null),
+                    contentResolver.query(contentUri, null, selection, null, null), contentUri,
                     mApp, mUserManagerState);
             final Bundle extras = c.getExtras();
             mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME);
@@ -1099,9 +1112,12 @@
         FileLog.d(TAG, widgetDimension.toString());
     }
 
-    private static void logASplit(String label) {
-        if (DEBUG) {
-            Log.d(TAG, label);
+    private static void logASplit(@Nullable TimingLogger timingLogger, String label) {
+        if (timingLogger != null) {
+            timingLogger.addSplit(label);
+            if (DEBUG) {
+                Log.d(TAG, label);
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 1274c08..97bce8c 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -19,10 +19,11 @@
 import static android.util.Base64.NO_WRAP;
 
 import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
-import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
 import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
 import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
 import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
+import static com.android.launcher3.model.DatabaseHelper.EMPTY_DATABASE_CREATED;
+import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 
 import android.app.blob.BlobHandle;
@@ -30,6 +31,7 @@
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.res.Resources;
@@ -41,7 +43,6 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -53,22 +54,18 @@
 
 import com.android.launcher3.AutoInstallsLayout;
 import com.android.launcher3.AutoInstallsLayout.SourceResources;
-import com.android.launcher3.ConstantItem;
 import com.android.launcher3.DefaultLayoutParser;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.IOUtils;
 import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
 import com.android.launcher3.util.Partner;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 
@@ -76,6 +73,7 @@
 
 import java.io.InputStream;
 import java.io.StringReader;
+import java.util.function.Supplier;
 
 /**
  * Utility class which maintains an instance of Launcher database and provides utility methods
@@ -84,8 +82,6 @@
 public class ModelDbController {
     private static final String TAG = "LauncherProvider";
 
-    private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
-
     protected DatabaseHelper mOpenHelper;
 
     private final Context mContext;
@@ -96,36 +92,26 @@
 
     private synchronized void createDbIfNotExists() {
         if (mOpenHelper == null) {
-            mOpenHelper = createDatabaseHelper(false /* forMigration */);
-            RestoreDbTask.restoreIfNeeded(mContext, this);
+            mOpenHelper = DatabaseHelper.createDatabaseHelper(
+                    mContext, false /* forMigration */);
+
+            RestoreDbTask.restoreIfNeeded(mContext, mOpenHelper);
         }
     }
 
-    protected DatabaseHelper createDatabaseHelper(boolean forMigration) {
-        boolean isSandbox = mContext instanceof SandboxContext;
-        String dbName = isSandbox ? null : InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
-
-        // Set the flag for empty DB
-        Runnable onEmptyDbCreateCallback = forMigration ? () -> { }
-                : () -> LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey(dbName).to(true));
-
-        DatabaseHelper databaseHelper = new DatabaseHelper(mContext, dbName,
-                this::getSerialNumberForUser, onEmptyDbCreateCallback);
-        // Table creation sometimes fails silently, which leads to a crash loop.
-        // This way, we will try to create a table every time after crash, so the device
-        // would eventually be able to recover.
-        if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
-            Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
-            // This operation is a no-op if the table already exists.
-            addTableToDb(databaseHelper.getWritableDatabase(),
-                    getSerialNumberForUser(Process.myUserHandle()),
-                    true /* optional */);
+    private synchronized boolean prepForMigration(String dbFile, String targetTableName,
+            Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
+        if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
+            Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
+            return false;
         }
-        databaseHelper.mHotseatRestoreTableExists = tableExists(
-                databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
 
-        databaseHelper.initIds();
-        return databaseHelper;
+        final DatabaseHelper helper = src.get();
+        mOpenHelper = dst.get();
+        copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
+                mOpenHelper.getWritableDatabase(), targetTableName, mContext);
+        helper.close();
+        return true;
     }
 
     /**
@@ -281,38 +267,42 @@
     }
 
     /**
-     * Migrates the DB if needed, and returns false if the migration failed
-     * and DB needs to be cleared.
+     * Updates the current DB and copies all the existing data to the temp table
+     * @param dbFile name of the target db file name
      */
-    public boolean migrateGridIfNeeded() {
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
-        if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) {
-            return true;
-        }
-        String targetDbName = new DeviceGridState(idp).getDbFile();
-        if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
-            // DB is same, ignore
-            return true;
-        }
-        DatabaseHelper oldHelper = mOpenHelper;
-        mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
-                : createDatabaseHelper(true /* forMigration */);
-        try {
-            return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper,
-                   oldHelper.getWritableDatabase());
-        } finally {
-            if (mOpenHelper != oldHelper) {
-                oldHelper.close();
-            }
-        }
+    @WorkerThread
+    public boolean updateCurrentOpenHelper(String dbFile) {
+        createDbIfNotExists();
+        return prepForMigration(
+                dbFile,
+                Favorites.TMP_TABLE,
+                () -> mOpenHelper,
+                () -> DatabaseHelper.createDatabaseHelper(
+                        mContext, true /* forMigration */));
     }
 
     /**
-     * Returns the underlying model database
+     * Returns the current DatabaseHelper.
+     * Only for tests
      */
-    public SQLiteDatabase getDb() {
+    @WorkerThread
+    public DatabaseHelper getDatabaseHelper() {
         createDbIfNotExists();
-        return mOpenHelper.getWritableDatabase();
+        return mOpenHelper;
+    }
+
+    /**
+     * Prepares the DB for preview by copying all existing data to preview table
+     */
+    @WorkerThread
+    public boolean prepareForPreview(String dbFile) {
+        createDbIfNotExists();
+        return prepForMigration(
+                dbFile,
+                Favorites.PREVIEW_TABLE_NAME,
+                () -> DatabaseHelper.createDatabaseHelper(
+                        mContext, dbFile, true /* forMigration */),
+                () -> mOpenHelper);
     }
 
     private void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -355,7 +345,8 @@
     }
 
     private void clearFlagEmptyDbCreated() {
-        LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()));
+        LauncherPrefs.getPrefs(mContext).edit()
+                .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
     }
 
     /**
@@ -368,8 +359,9 @@
     @WorkerThread
     public synchronized void loadDefaultFavoritesIfNecessary() {
         createDbIfNotExists();
+        SharedPreferences sp = LauncherPrefs.getPrefs(mContext);
 
-        if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) {
+        if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
             Log.d(TAG, "loading default workspace");
 
             LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
@@ -487,27 +479,4 @@
         return new DefaultLayoutParser(mContext, widgetHolder,
                 mOpenHelper, mContext.getResources(), defaultLayout);
     }
-
-    /**
-     * Re-composite given key in respect to database. If the current db is
-     * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
-     * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
-     * string will be "EMPTY_DATABASE_CREATED@minimal.db".
-     */
-    private ConstantItem<Boolean> getEmptyDbCreatedKey(String dbName) {
-        if (mContext instanceof SandboxContext) {
-            return LauncherPrefs.nonRestorableItem(EMPTY_DATABASE_CREATED,
-                    false /* default value */, false /* boot aware */);
-        }
-        String key = TextUtils.equals(dbName, LauncherFiles.LAUNCHER_DB)
-                ? EMPTY_DATABASE_CREATED : EMPTY_DATABASE_CREATED + "@" + dbName;
-        return LauncherPrefs.backedUpItem(key, false /* default value */, false /* boot aware */);
-    }
-
-    /**
-     * Returns the serial number for the provided user
-     */
-    public long getSerialNumberForUser(UserHandle user) {
-        return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
-    }
 }
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index c718dcc..48969fc 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -101,7 +101,7 @@
         UserManagerState ums = new UserManagerState();
         ums.init(UserCache.INSTANCE.get(context),
                 context.getSystemService(UserManager.class));
-        LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums);
+        LoaderCursor lc = new LoaderCursor(c, null, LauncherAppState.getInstance(context), ums);
         IntSet deletedShortcuts = new IntSet();
 
         while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index a6e064a..ac72164 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.provider;
 
-import static android.os.Process.myUserHandle;
-
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
 import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
 import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
@@ -49,8 +47,8 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.model.DeviceGridState;
-import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -85,12 +83,12 @@
     /**
      * Tries to restore the backup DB if needed
      */
-    public static void restoreIfNeeded(Context context, ModelDbController dbController) {
+    public static void restoreIfNeeded(Context context, DatabaseHelper helper) {
         if (!isPending(context)) {
             return;
         }
-        if (!performRestore(context, dbController)) {
-            dbController.createEmptyDB();
+        if (!performRestore(context, helper)) {
+            helper.createEmptyDB(helper.getWritableDatabase());
         }
 
         // Obtain InvariantDeviceProfile first before setting pending to false, so
@@ -104,12 +102,12 @@
         idp.reinitializeAfterRestore(context);
     }
 
-    private static boolean performRestore(Context context, ModelDbController controller) {
-        SQLiteDatabase db = controller.getDb();
+    private static boolean performRestore(Context context, DatabaseHelper helper) {
+        SQLiteDatabase db = helper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             RestoreDbTask task = new RestoreDbTask();
-            task.sanitizeDB(context, controller, db, new BackupManager(context));
-            task.restoreAppWidgetIdsIfExists(context, controller);
+            task.sanitizeDB(context, helper, db, new BackupManager(context));
+            task.restoreAppWidgetIdsIfExists(context, helper);
             t.commit();
             return true;
         } catch (Exception e) {
@@ -131,10 +129,10 @@
      * @return number of items deleted.
      */
     @VisibleForTesting
-    protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db,
+    protected int sanitizeDB(Context context, DatabaseHelper helper, SQLiteDatabase db,
             BackupManager backupManager) throws Exception {
         // Primary user ids
-        long myProfileId = controller.getSerialNumberForUser(myUserHandle());
+        long myProfileId = helper.getDefaultUserSerial();
         long oldProfileId = getDefaultProfileId(db);
         LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
         LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
@@ -146,7 +144,7 @@
             long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
             UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
             if (user != null) {
-                long newManagedProfileId = controller.getSerialNumberForUser(user);
+                long newManagedProfileId = helper.getSerialNumberForUser(user);
                 profileMapping.put(oldManagedProfileId, newManagedProfileId);
             }
         }
@@ -215,7 +213,7 @@
         }
 
         // Override shortcuts
-        maybeOverrideShortcuts(context, controller, db, myProfileId);
+        maybeOverrideShortcuts(context, helper, db, myProfileId);
 
         return itemsDeleted;
     }
@@ -323,11 +321,11 @@
                 .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
     }
 
-    private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
+    private void restoreAppWidgetIdsIfExists(Context context, DatabaseHelper helper) {
         LauncherPrefs lp = LauncherPrefs.get(context);
         if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
             AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
-            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller,
+            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, helper,
                     IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
                     IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
                     host);
@@ -345,7 +343,7 @@
                 APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
     }
 
-    protected static void maybeOverrideShortcuts(Context context, ModelDbController controller,
+    protected static void maybeOverrideShortcuts(Context context, DatabaseHelper helper,
             SQLiteDatabase db, long currentUser) {
         Map<String, LauncherActivityInfo> activityOverrides = ApiWrapper.getActivityOverrides(
                 context);
@@ -369,7 +367,7 @@
                 if (override != null) {
                     ContentValues values = new ContentValues();
                     values.put(Favorites.PROFILE_ID,
-                            controller.getSerialNumberForUser(override.getUser()));
+                            helper.getSerialNumberForUser(override.getUser()));
                     values.put(Favorites.INTENT, AppInfo.makeLaunchIntent(override).toUri(0));
                     db.update(Favorites.TABLE_NAME, values, String.format("%s=?", Favorites._ID),
                             new String[]{String.valueOf(c.getInt(idIndex))});
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 7c5ef4d..e509235 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -26,7 +26,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.pm.UserCache;
 
 /**
@@ -106,7 +106,7 @@
 
     public int commit() {
         if (mCommitParams != null) {
-            mCommitParams.mDbController.update(
+            mCommitParams.mDatabaseHelper.getWritableDatabase().update(
                     Favorites.TABLE_NAME, getValues(mContext),
                     mCommitParams.mWhere, mCommitParams.mSelectionArgs);
         }
@@ -115,12 +115,12 @@
 
     public static final class CommitParams {
 
-        final ModelDbController mDbController;
+        final DatabaseHelper mDatabaseHelper;
         final String mWhere;
         final String[] mSelectionArgs;
 
-        public CommitParams(ModelDbController controller, String where, String[] selectionArgs) {
-            mDbController = controller;
+        public CommitParams(DatabaseHelper helper, String where, String[] selectionArgs) {
+            mDatabaseHelper = helper;
             mWhere = where;
             mSelectionArgs = selectionArgs;
         }
diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index 7030f6d..6f74fd9 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -146,8 +146,7 @@
             previewWidth = drawable.getIntrinsicWidth();
             previewHeight = drawable.getIntrinsicHeight();
         } else {
-            Size widgetSize = WidgetSizes.getWidgetPaddedSizePx(mContext, info.provider, dp, spanX,
-                    spanY);
+            Size widgetSize = WidgetSizes.getWidgetSizePx(dp, spanX, spanY);
             previewWidth = widgetSize.getWidth();
             previewHeight = widgetSize.getHeight();
         }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index bba1016..10aef9a 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -2,7 +2,6 @@
 
 import static com.android.launcher3.Utilities.ATLEAST_S;
 
-import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -105,44 +104,35 @@
         int spanX = 0;
         int spanY = 0;
 
-        Rect widgetPadding = new Rect();
-        Rect localPadding = new Rect();
-        AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
 
         Point cellSize = new Point();
         for (DeviceProfile dp : idp.supportedProfiles) {
             dp.getCellSize(cellSize);
-            // We want to account for the extra amount of padding that we are adding to the widget
-            // to ensure that it gets the full amount of space that it has requested.
-            // If grids supports insetting widgets, we do not account for widget padding.
-            if (dp.shouldInsetWidgets()) {
-                localPadding.setEmpty();
-            } else {
-                localPadding.set(widgetPadding);
-            }
+            Rect widgetPadding = dp.widgetPadding;
+
             minSpanX = Math.max(minSpanX,
-                    getSpanX(localPadding, minResizeWidth, dp.cellLayoutBorderSpacePx.x,
+                    getSpanX(widgetPadding, minResizeWidth, dp.cellLayoutBorderSpacePx.x,
                             cellSize.x));
             minSpanY = Math.max(minSpanY,
-                    getSpanY(localPadding, minResizeHeight, dp.cellLayoutBorderSpacePx.y,
+                    getSpanY(widgetPadding, minResizeHeight, dp.cellLayoutBorderSpacePx.y,
                             cellSize.y));
 
             if (ATLEAST_S) {
                 if (maxResizeWidth > 0) {
-                    maxSpanX = Math.min(maxSpanX, getSpanX(localPadding, maxResizeWidth,
+                    maxSpanX = Math.min(maxSpanX, getSpanX(widgetPadding, maxResizeWidth,
                             dp.cellLayoutBorderSpacePx.x, cellSize.x));
                 }
                 if (maxResizeHeight > 0) {
-                    maxSpanY = Math.min(maxSpanY, getSpanY(localPadding, maxResizeHeight,
+                    maxSpanY = Math.min(maxSpanY, getSpanY(widgetPadding, maxResizeHeight,
                             dp.cellLayoutBorderSpacePx.y, cellSize.y));
                 }
             }
 
             spanX = Math.max(spanX,
-                    getSpanX(localPadding, minWidth, dp.cellLayoutBorderSpacePx.x,
+                    getSpanX(widgetPadding, minWidth, dp.cellLayoutBorderSpacePx.x,
                             cellSize.x));
             spanY = Math.max(spanY,
-                    getSpanY(localPadding, minHeight, dp.cellLayoutBorderSpacePx.y,
+                    getSpanY(widgetPadding, minHeight, dp.cellLayoutBorderSpacePx.y,
                             cellSize.y));
         }
 
@@ -184,15 +174,22 @@
     }
 
     private int getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth) {
-        return Math.max(1, (int) Math.ceil(
-                (widgetWidth + widgetPadding.left + widgetPadding.right + cellSpacing) / (cellWidth
-                        + cellSpacing)));
+        return getSpan(widgetPadding.left + widgetPadding.right,
+                widgetWidth, cellSpacing, cellWidth);
     }
 
     private int getSpanY(Rect widgetPadding, int widgetHeight, int cellSpacing, float cellHeight) {
+        return getSpan(widgetPadding.top + widgetPadding.bottom, widgetHeight,
+                cellSpacing, cellHeight);
+    }
+
+    /**
+     * Solving the equation:
+     *   n * cellSize + (n - 1) * cellSpacing - widgetPadding = widgetSize
+     */
+    private int getSpan(int widgetPadding, int widgetSize, int cellSpacing, float cellSize) {
         return Math.max(1, (int) Math.ceil(
-                (widgetHeight + widgetPadding.top + widgetPadding.bottom + cellSpacing) / (
-                        cellHeight + cellSpacing)));
+                (widgetSize + widgetPadding + cellSpacing) / (cellSize + cellSpacing)));
     }
 
     public String getLabel(PackageManager packageManager) {
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index 3389fb1..f46b214 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -25,7 +25,6 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.util.MultiTranslateDelegate;
@@ -48,13 +47,13 @@
 
     private float mScaleForReorderBounce = 1f;
 
-    private final Rect mTempRect = new Rect();
-
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mChildrenFocused;
 
     protected final ActivityContext mActivity;
 
+    private boolean mDisableSetPadding = false;
+
     public NavigableAppWidgetHostView(Context context) {
         super(context);
         mActivity = ActivityContext.lookupContext(context);
@@ -147,6 +146,22 @@
     }
 
     @Override
+    public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
+        // Prevent default padding being set on the view based on provider info. Launcher manages
+        // its own widget spacing
+        mDisableSetPadding = true;
+        super.setAppWidget(appWidgetId, info);
+        mDisableSetPadding = false;
+    }
+
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        if (!mDisableSetPadding) {
+            super.setPadding(left, top, right, bottom);
+        }
+    }
+
+    @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
         return mChildrenFocused;
     }
@@ -195,26 +210,6 @@
     public void getWorkspaceVisualDragBounds(Rect bounds) {
         int width = (int) (getMeasuredWidth() * mScaleToFit);
         int height = (int) (getMeasuredHeight() * mScaleToFit);
-
-        getWidgetInset(mActivity.getDeviceProfile(), mTempRect);
-        bounds.set(mTempRect.left, mTempRect.top, width - mTempRect.right,
-                height - mTempRect.bottom);
-    }
-
-    /**
-     * Widgets have padding added by the system. We may choose to inset this padding if the grid
-     * supports it.
-     */
-    public void getWidgetInset(DeviceProfile grid, Rect out) {
-        if (!grid.shouldInsetWidgets()) {
-            out.setEmpty();
-            return;
-        }
-        AppWidgetProviderInfo info = getAppWidgetInfo();
-        if (info == null) {
-            out.set(grid.inv.defaultWidgetPadding);
-        } else {
-            AppWidgetHostView.getDefaultPaddingForWidget(getContext(), info.provider, out);
-        }
+        bounds.set(0, 0, width, height);
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 410a555..67d21f7 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -43,7 +45,6 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.RoundDrawableWrapper;
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
-import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -121,13 +122,8 @@
                 mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1,
                         ((PendingAddWidgetInfo) mAddInfo).info);
                 DeviceProfile deviceProfile = launcher.getDeviceProfile();
-                Rect padding = new Rect();
-                mAppWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
-                mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
-                        padding.bottom);
                 mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview);
-                Size widgetSizes = WidgetSizes.getWidgetPaddedSizePx(launcher,
-                        mAddInfo.componentName, deviceProfile, mAddInfo.spanX, mAddInfo.spanY);
+                Size widgetSizes = getWidgetSizePx(deviceProfile, mAddInfo.spanX, mAddInfo.spanY);
                 mAppWidgetHostViewPreview.measure(
                         MeasureSpec.makeMeasureSpec(widgetSizes.getWidth(), MeasureSpec.EXACTLY),
                         MeasureSpec.makeMeasureSpec(widgetSizes.getHeight(), MeasureSpec.EXACTLY));
diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java
index 601c1b5..7049509 100644
--- a/src/com/android/launcher3/widget/util/WidgetSizes.java
+++ b/src/com/android/launcher3/widget/util/WidgetSizes.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.widget.util;
 
-import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
-
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
@@ -28,8 +26,6 @@
 import android.util.Size;
 import android.util.SizeF;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
@@ -43,24 +39,13 @@
 
     /**
      * Returns the list of all possible sizes, in dp, for a widget of given spans on this device.
-     *
-     * <p>The returned sizes already take into account the system padding, and whether it is applied
-     * or not in that specific configuration.
      */
-    public static ArrayList<SizeF> getWidgetPaddedSizes(Context context, ComponentName provider,
-            int spanX, int spanY) {
-        Rect padding = getDefaultPaddingForWidget(context, provider, /* padding= */ null);
-
+    public static ArrayList<SizeF> getWidgetSizesDp(Context context, int spanX, int spanY) {
         ArrayList<SizeF> sizes = new ArrayList<>(2);
         final float density = context.getResources().getDisplayMetrics().density;
-        final Point cellSize = new Point();
 
         for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) {
-            Size widgetSizePx = getWidgetSizePx(profile, spanX, spanY, cellSize);
-            if (!profile.shouldInsetWidgets()) {
-                widgetSizePx = new Size(widgetSizePx.getWidth() - padding.left - padding.right,
-                        widgetSizePx.getHeight() - padding.top - padding.bottom);
-            }
+            Size widgetSizePx = getWidgetSizePx(profile, spanX, spanY);
             sizes.add(new SizeF(widgetSizePx.getWidth() / density,
                     widgetSizePx.getHeight() / density));
         }
@@ -69,21 +54,15 @@
 
     /** Returns the size, in pixels, a widget of given spans & {@code profile}. */
     public static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY) {
-        return getWidgetSizePx(profile, spanX, spanY, /* recycledCellSize= */ null);
-    }
+        final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacePx.x;
+        final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacePx.y;
 
-    /**
-     * Returns the size, in pixels and removing padding, a widget of given spans & {@code profile}.
-     */
-    public static Size getWidgetPaddedSizePx(Context context, ComponentName component,
-            DeviceProfile profile, int spanX, int spanY) {
-        Size size = getWidgetSizePx(profile, spanX, spanY);
-        if (profile.shouldInsetWidgets()) {
-            return size;
-        }
-        Rect padding = getDefaultPaddingForWidget(context, component, /* padding= */ null);
-        return new Size(size.getWidth() - padding.left - padding.right,
-                size.getHeight() - padding.top - padding.bottom);
+        Point cellSize = profile.getCellSize();
+        Rect padding = profile.widgetPadding;
+
+        return new Size(
+                (spanX * cellSize.x) + hBorderSpacing - padding.left - padding.right,
+                (spanY * cellSize.y) + vBorderSpacing - padding.top - padding.bottom);
     }
 
     /**
@@ -92,8 +71,7 @@
      * <p>This size is used by the widget picker. It should NEVER be shared with app widgets.
      *
      * <p>For sizes shared with app widgets, please refer to
-     * {@link #getWidgetPaddedSizes(Context, ComponentName, int, int)} &
-     * {@link #getWidgetPaddedSizePx(Context, ComponentName, DeviceProfile, int, int)}.
+     * {@link #getWidgetSizesDp(Context, int, int)} &
      */
     public static Size getWidgetItemSizePx(Context context, DeviceProfile profile,
             WidgetItem widgetItem) {
@@ -102,27 +80,7 @@
                     .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
             return new Size(dimension, dimension);
         }
-        Size widgetItemSize = getWidgetSizePx(profile, widgetItem.spanX,
-                widgetItem.spanY, /* recycledCellSize= */ null);
-        if (profile.shouldInsetWidgets()) {
-            Rect inset = new Rect();
-            AppWidgetHostView.getDefaultPaddingForWidget(context, widgetItem.componentName, inset);
-            return new Size(widgetItemSize.getWidth() + inset.left + inset.right,
-                    widgetItemSize.getHeight() + inset.top + inset.bottom);
-        }
-        return widgetItemSize;
-    }
-
-    private static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY,
-            @Nullable Point recycledCellSize) {
-        final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacePx.x;
-        final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacePx.y;
-        if (recycledCellSize == null) {
-            recycledCellSize = new Point();
-        }
-        profile.getCellSize(recycledCellSize);
-        return new Size(((spanX * recycledCellSize.x) + hBorderSpacing),
-                ((spanY * recycledCellSize.y) + vBorderSpacing));
+        return getWidgetSizePx(profile, widgetItem.spanX, widgetItem.spanY);
     }
 
     /**
@@ -154,7 +112,7 @@
      */
     public static Bundle getWidgetSizeOptions(Context context, ComponentName provider, int spanX,
             int spanY) {
-        ArrayList<SizeF> paddedSizes = getWidgetPaddedSizes(context, provider, spanX, spanY);
+        ArrayList<SizeF> paddedSizes = getWidgetSizesDp(context, spanX, spanY);
 
         Rect rect = getMinMaxSizes(paddedSizes);
         Bundle options = new Bundle();
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index cea95e5..0a1a9ba 100644
--- a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -39,7 +39,6 @@
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.pm.UserCache;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -223,9 +222,7 @@
     private class MyDatabaseHelper extends DatabaseHelper {
 
         MyDatabaseHelper() {
-            super(mContext, DB_FILE,
-                    UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
-                    () -> { });
+            super(mContext, DB_FILE, false);
         }
 
         @Override
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 63dbaa7..f24f0da 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.model
 
-import android.content.ContentValues
 import android.content.Context
 import android.content.Intent
 import android.database.Cursor
@@ -24,7 +23,6 @@
 import android.os.Process
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.InvariantDeviceProfile
 import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
@@ -33,7 +31,10 @@
 import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.provider.LauncherDbUtils
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.*
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -42,12 +43,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class GridSizeMigrationUtilTest {
-
+    private lateinit var modelHelper: LauncherModelHelper
     private lateinit var context: Context
+    private lateinit var db: SQLiteDatabase
     private lateinit var validPackages: Set<String>
     private lateinit var idp: InvariantDeviceProfile
-    private lateinit var dbHelper: DatabaseHelper
-    private lateinit var db: SQLiteDatabase
     private val testPackage1 = "com.android.launcher3.validpackage1"
     private val testPackage2 = "com.android.launcher3.validpackage2"
     private val testPackage3 = "com.android.launcher3.validpackage3"
@@ -61,17 +61,13 @@
 
     @Before
     fun setUp() {
-        context = InstrumentationRegistry.getInstrumentation().targetContext
-        dbHelper =
-            DatabaseHelper(
-                context,
-                null,
-                UserCache.INSTANCE.get(context)::getSerialNumberForUser
-            ) {}
-        db = dbHelper.writableDatabase
+        modelHelper = LauncherModelHelper()
+        context = modelHelper.sandboxContext
+        db = modelHelper.provider.db
 
         validPackages =
             setOf(
+                TEST_PACKAGE,
                 testPackage1,
                 testPackage2,
                 testPackage3,
@@ -90,6 +86,11 @@
         addTableToDb(db, userSerial, false, TMP_TABLE)
     }
 
+    @After
+    fun tearDown() {
+        modelHelper.destroy()
+    }
+
     /**
      * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
      * needed anymore
@@ -98,26 +99,26 @@
     @Throws(Exception::class)
     fun testMigration() {
         // Src Hotseat icons
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
         // Src grid icons
         // _ _ _ _ _
         // _ _ _ _ 5
         // _ _ 6 _ 7
         // _ _ 8 _ 9
         // _ _ _ _ _
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
 
         // Dest hotseat icons
-        addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+        modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
         // Dest grid icons
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 4
@@ -125,7 +126,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -136,13 +138,12 @@
 
         // Check hotseat items
         var c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -167,13 +168,12 @@
 
         // Check workspace items
         c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -209,30 +209,30 @@
     fun testMigrationBackAndForth() {
         // Hotseat items in grid A
         // 1 2 _ 3 4
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
         // Workspace items in grid A
         // _ _ _ _ _
         // _ _ _ _ 5
         // _ _ 6 _ 7
         // _ _ 8 _ _
         // _ _ _ _ _
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
 
         // Hotseat items in grid B
         // 2 _ _ _
-        addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+        modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2)
         // Workspace items in grid B
         // _ _ _ _
         // _ _ _ 10
         // _ _ _ _
         // _ _ _ _
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 3, testPackage10)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 4
@@ -241,7 +241,8 @@
         val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
         // migrate from A -> B
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             readerGridA,
             readerGridB,
             idp.numDatabaseHotseatIcons,
@@ -252,13 +253,12 @@
 
         // Check hotseat items in grid B
         var c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -272,13 +272,12 @@
 
         // Check workspace items in grid B
         c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -295,11 +294,12 @@
         assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
 
         // add item in B
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 2, testPackage9)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9)
 
         // migrate from B -> A
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             readerGridB,
             readerGridA,
             5,
@@ -309,13 +309,12 @@
         )
         // Check hotseat items in grid A
         c =
-            db.query(
-                TMP_TABLE,
+            context.contentResolver.query(
+                TMP_CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -329,13 +328,12 @@
 
         // Check workspace items in grid A
         c =
-            db.query(
-                TMP_TABLE,
+            context.contentResolver.query(
+                TMP_CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -356,11 +354,12 @@
         assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
 
         // remove item from B
-        db.delete(TMP_TABLE, "$_ID=7", null)
+        modelHelper.deleteItem(7, TMP_TABLE)
 
         // migrate from A -> B
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             readerGridA,
             readerGridB,
             idp.numDatabaseHotseatIcons,
@@ -371,13 +370,12 @@
 
         // Check hotseat items in grid B
         c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -391,13 +389,12 @@
 
         // Check workspace items in grid B
         c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -446,28 +443,10 @@
     fun migrateToLargerHotseat() {
         val srcHotseatItems =
             intArrayOf(
-                addItem(
-                    ITEM_TYPE_APPLICATION,
-                    0,
-                    CONTAINER_HOTSEAT,
-                    0,
-                    0,
-                    testPackage1,
-                    1,
-                    TMP_TABLE
-                ),
-                addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
-                addItem(
-                    ITEM_TYPE_APPLICATION,
-                    2,
-                    CONTAINER_HOTSEAT,
-                    0,
-                    0,
-                    testPackage3,
-                    3,
-                    TMP_TABLE
-                ),
-                addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+                modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
             )
         val numSrcDatabaseHotseatIcons = srcHotseatItems.size
         idp.numDatabaseHotseatIcons = 6
@@ -476,7 +455,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -487,13 +467,12 @@
 
         // Check hotseat items
         val c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -522,11 +501,11 @@
 
     @Test
     fun migrateFromLargerHotseat() {
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
-        addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 4
@@ -534,7 +513,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -545,13 +525,12 @@
 
         // Check hotseat items
         val c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -589,11 +568,11 @@
         enableNewMigrationLogic("4,4")
 
         // Setup src grid
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage1, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage2, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 1, testPackage3, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 2, testPackage4, 8, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 3, 3, testPackage5, 9, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 6
@@ -602,7 +581,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -613,13 +593,12 @@
 
         // Get workspace items
         val c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(INTENT, SCREEN),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -651,11 +630,11 @@
         enableNewMigrationLogic("2,2")
 
         // Setup src grid
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 5
@@ -663,7 +642,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -674,13 +654,12 @@
 
         // Get workspace items
         val c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(INTENT, SCREEN),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -712,11 +691,11 @@
         enableNewMigrationLogic("5,5")
 
         // Setup src grid
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 4
@@ -724,7 +703,8 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-            dbHelper,
+            context,
+            db,
             srcReader,
             destReader,
             idp.numDatabaseHotseatIcons,
@@ -735,13 +715,12 @@
 
         // Get workspace items
         val c =
-            db.query(
-                TABLE_NAME,
+            context.contentResolver.query(
+                CONTENT_URI,
                 arrayOf(INTENT, SCREEN),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
-                null,
                 null
             )
                 ?: throw IllegalStateException()
@@ -768,48 +747,4 @@
     private fun enableNewMigrationLogic(srcGridSize: String) {
         LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
     }
-
-    private fun addItem(
-        type: Int,
-        screen: Int,
-        container: Int,
-        x: Int,
-        y: Int,
-        packageName: String?
-    ): Int {
-        return addItem(
-            type,
-            screen,
-            container,
-            x,
-            y,
-            packageName,
-            dbHelper.generateNewItemId(),
-            TABLE_NAME
-        )
-    }
-
-    private fun addItem(
-        type: Int,
-        screen: Int,
-        container: Int,
-        x: Int,
-        y: Int,
-        packageName: String?,
-        id: Int,
-        tableName: String
-    ): Int {
-        val values = ContentValues()
-        values.put(_ID, id)
-        values.put(CONTAINER, container)
-        values.put(SCREEN, screen)
-        values.put(CELLX, x)
-        values.put(CELLY, y)
-        values.put(SPANX, 1)
-        values.put(SPANY, 1)
-        values.put(ITEM_TYPE, type)
-        values.put(INTENT, Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0))
-        db.insert(tableName, null, values)
-        return id
-    }
 }
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 78812c0..d192be4 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -59,6 +59,7 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
@@ -101,7 +102,7 @@
         });
 
         UserManagerState ums = new UserManagerState();
-        mLoaderCursor = new LoaderCursor(mCursor, mApp, ums);
+        mLoaderCursor = new LoaderCursor(mCursor, Favorites.CONTENT_URI, mApp, ums);
         ums.allUsers.put(0, Process.myUserHandle());
     }
 
diff --git a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
index 54b8489..2b6f9ff 100644
--- a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
+++ b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
@@ -45,7 +45,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.model.DbDowngradeHelper;
-import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.settings.SettingsActivity;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.IOUtils;
@@ -166,11 +165,12 @@
     private class MyDatabaseHelper extends DatabaseHelper {
 
         MyDatabaseHelper() {
-            super(mContext, null, UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
-                    () -> { });
+            super(mContext, null, false);
         }
 
         @Override
         protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
+
+        protected void onEmptyDbCreated() { }
     }
 }
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 1b1b294..67de1f5 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -46,7 +46,6 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.DatabaseHelper;
-import com.android.launcher3.model.ModelDbController;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,13 +63,13 @@
 
     @Test
     public void testGetProfileId() throws Exception {
-        SQLiteDatabase db = new MyModelDbController(23).getDb();
+        SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
         assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
     }
 
     @Test
     public void testMigrateProfileId() throws Exception {
-        SQLiteDatabase db = new MyModelDbController(42).getDb();
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
         // Add some mock data
         for (int i = 0; i < 5; i++) {
             ContentValues values = new ContentValues();
@@ -90,7 +89,7 @@
 
     @Test
     public void testChangeDefaultColumn() throws Exception {
-        SQLiteDatabase db = new MyModelDbController(42).getDb();
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
         // Add some mock data
         for (int i = 0; i < 5; i++) {
             ContentValues values = new ContentValues();
@@ -121,20 +120,20 @@
         long workProfileId = myProfileId + 2;
         long workProfileId_old = myProfileId + 3;
 
-        MyModelDbController controller = new MyModelDbController(myProfileId);
-        SQLiteDatabase db = controller.getDb();
+        MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
+        SQLiteDatabase db = helper.getWritableDatabase();
         BackupManager bm = spy(new BackupManager(context));
         doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
         doReturn(mWorkUser).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
-        controller.users.put(workProfileId, mWorkUser);
+        helper.users.put(workProfileId, mWorkUser);
 
-        addIconsBulk(controller, 10, 1, myProfileId_old);
-        addIconsBulk(controller, 6, 2, workProfileId_old);
+        addIconsBulk(helper, 10, 1, myProfileId_old);
+        addIconsBulk(helper, 6, 2, workProfileId_old);
         assertEquals(10, getItemCountForProfile(db, myProfileId_old));
         assertEquals(6, getItemCountForProfile(db, workProfileId_old));
 
         RestoreDbTask task = new RestoreDbTask();
-        task.sanitizeDB(context, controller, controller.getDb(), bm);
+        task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
 
         // All the data has been migrated to the new user ids
         assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -152,20 +151,20 @@
         long myProfileId_old = myProfileId + 1;
         long workProfileId_old = myProfileId + 3;
 
-        MyModelDbController controller = new MyModelDbController(myProfileId);
-        SQLiteDatabase db = controller.getDb();
+        MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
+        SQLiteDatabase db = helper.getWritableDatabase();
         BackupManager bm = spy(new BackupManager(context));
         doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
         // Work profile is not migrated
         doReturn(null).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
 
-        addIconsBulk(controller, 10, 1, myProfileId_old);
-        addIconsBulk(controller, 6, 2, workProfileId_old);
+        addIconsBulk(helper, 10, 1, myProfileId_old);
+        addIconsBulk(helper, 6, 2, workProfileId_old);
         assertEquals(10, getItemCountForProfile(db, myProfileId_old));
         assertEquals(6, getItemCountForProfile(db, workProfileId_old));
 
         RestoreDbTask task = new RestoreDbTask();
-        task.sanitizeDB(context, controller, controller.getDb(), bm);
+        task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
 
         // All the data has been migrated to the new user ids
         assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -174,13 +173,12 @@
         assertEquals(10, getCount(db, "select * from favorites"));
     }
 
-    private void addIconsBulk(MyModelDbController controller,
-            int count, int screen, long profileId) {
+    private void addIconsBulk(DatabaseHelper helper, int count, int screen, long profileId) {
         int columns = LauncherAppState.getIDP(getInstrumentation().getTargetContext()).numColumns;
         String packageName = getInstrumentation().getContext().getPackageName();
         for (int i = 0; i < count; i++) {
             ContentValues values = new ContentValues();
-            values.put(LauncherSettings.Favorites._ID, controller.generateNewItemId());
+            values.put(LauncherSettings.Favorites._ID, helper.generateNewItemId());
             values.put(LauncherSettings.Favorites.CONTAINER, CONTAINER_DESKTOP);
             values.put(LauncherSettings.Favorites.SCREEN, screen);
             values.put(LauncherSettings.Favorites.CELLX, i % columns);
@@ -191,11 +189,11 @@
             values.put(LauncherSettings.Favorites.ITEM_TYPE, ITEM_TYPE_APPLICATION);
             values.put(LauncherSettings.Favorites.INTENT,
                     new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
-
-            controller.insert(TABLE_NAME, values);
+            helper.getWritableDatabase().insert(TABLE_NAME, null, values);
         }
     }
 
+
     @Test
     public void testRemoveScreenIdGaps_firstScreenEmpty() {
         runRemoveScreenIdGapsTest(
@@ -218,7 +216,7 @@
     }
 
     private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
-        SQLiteDatabase db = new MyModelDbController(42).getDb();
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
         // Add some mock data
         for (int i = 0; i < screenIds.length; i++) {
             ContentValues values = new ContentValues();
@@ -256,15 +254,14 @@
         }
     }
 
-    private class MyModelDbController extends ModelDbController {
+    private class MyDatabaseHelper extends DatabaseHelper {
 
-        public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
+        public final LongSparseArray<UserHandle> users;
 
-        MyModelDbController(long profileId) {
-            super(getInstrumentation().getTargetContext());
+        MyDatabaseHelper(long profileId) {
+            super(getInstrumentation().getTargetContext(), null, false);
+            users = new LongSparseArray<>();
             users.put(profileId, myUserHandle());
-            mOpenHelper = new DatabaseHelper(getInstrumentation().getTargetContext(), null,
-                    this::getSerialNumberForUser, () -> { });
         }
 
         @Override
@@ -272,5 +269,10 @@
             int index = users.indexOfValue(user);
             return index >= 0 ? users.keyAt(index) : -1;
         }
+
+        @Override
+        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
+
+        protected void onEmptyDbCreated() { }
     }
 }
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 6fca965..bf31e39 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -363,6 +363,12 @@
         sandboxContext.getContentResolver().insert(contentUri, values);
     }
 
+    public void deleteItem(int itemId, @NonNull final String tableName) {
+        final Uri uri = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId);
+        sandboxContext.getContentResolver().delete(uri, null, null);
+    }
+
     /**
      * Sets up a mock provider to load the provided layout by default, next time the layout loads
      */
@@ -420,7 +426,7 @@
         }
 
         public SQLiteDatabase getDb() {
-            return getModelDbController().getDb();
+            return getModelDbController().getDatabaseHelper().getWritableDatabase();
         }
     }
 
diff --git a/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index b534a41..48cf3df 100644
--- a/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -32,6 +32,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -161,7 +162,6 @@
         int maxPadding = Math.max(Math.max(padding.left, padding.right),
                 Math.max(padding.top, padding.bottom));
         dp.cellLayoutBorderSpacePx.x = dp.cellLayoutBorderSpacePx.y = maxPadding + 1;
-        Mockito.when(dp.shouldInsetWidgets()).thenReturn(true);
 
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
         info.minWidth = CELL_SIZE * 3;
@@ -184,7 +184,6 @@
         int maxPadding = Math.max(Math.max(padding.left, padding.right),
                 Math.max(padding.top, padding.bottom));
         dp.cellLayoutBorderSpacePx.x = dp.cellLayoutBorderSpacePx.y = maxPadding - 1;
-        Mockito.when(dp.shouldInsetWidgets()).thenReturn(false);
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
         info.minWidth = CELL_SIZE * 3;
         info.minHeight = CELL_SIZE * 3;
@@ -257,14 +256,16 @@
     }
 
     private InvariantDeviceProfile createIDP() {
-        DeviceProfile profile = Mockito.mock(DeviceProfile.class);
+        DeviceProfile dp = LauncherAppState.getIDP(mContext)
+                .getDeviceProfile(mContext).copy(mContext);
+        DeviceProfile profile = Mockito.spy(dp);
         doAnswer(i -> {
             ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
             return null;
         }).when(profile).getCellSize(any(Point.class));
         Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
         profile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE);
-        Mockito.when(profile.shouldInsetWidgets()).thenReturn(true);
+        profile.widgetPadding.setEmpty();
 
         InvariantDeviceProfile idp = new InvariantDeviceProfile();
         List<DeviceProfile> supportedProfiles = new ArrayList<>(idp.supportedProfiles);
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index d2c2fd7..834f851 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -38,6 +38,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
@@ -50,6 +51,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -68,7 +70,6 @@
     @Mock
     private IconCache mIconCache;
 
-    @Mock
     private DeviceProfile mTestDeviceProfile;
 
     private Context mContext;
@@ -198,13 +199,18 @@
     }
 
     private void initDP() {
+        DeviceProfile dp = LauncherAppState.getIDP(mContext)
+                .getDeviceProfile(mContext).copy(mContext);
+        mTestDeviceProfile = Mockito.spy(dp);
+
         doAnswer(i -> {
             ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
             return null;
         }).when(mTestDeviceProfile).getCellSize(any(Point.class));
         when(mTestDeviceProfile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
         mTestDeviceProfile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE);
-        when(mTestDeviceProfile.shouldInsetWidgets()).thenReturn(false);
+        mTestDeviceProfile.widgetPadding.setEmpty();
+        mTestDeviceProfile.allAppsIconSizePx = 0;
     }
 
     private void initTestWidgets() {