Merging  ub-launcher3-master, build 5945220

Test: Manual
Bug:123904290 P2 Remove magic constants from TAPL
Bug:130294785 P3 Can no longer long press to open settings in hotseat area
Bug:131115553 P2 Enable CacheDataUpdatedTaskTest tests
Bug:134712476 P4 Add Duo contact to home screen, icon froze and vanished while over home screen
Bug:136278866 P2 Temporary workarounds to make tests pass on Cuttlefish
Bug:136829198 P2 Scrolling Overview During Quickswitch causes switch with weird animation
Bug:137568159 P4 Refactor LauncherCallback to support multiple clients
Bug:137777105 P1 Make clearcut launcher logging feature parity with westworld logging
Bug:137851409 P4 Jank during swipe up due to inflation
Bug:138620399 P1 Quick switch flicker / artifacts
Bug:139016518 P2 [PinnedStackController] Move shelf offset into sysui
Bug:139137636 P2 Create memory tests for Launcher
Bug:139258979 P2 Switch to the screenshot mode when then system passes ThumbnailData back upon RecentsAnimation cancelation
Bug:139259253 P2 Transform multiple app surfaces during app open animation from overview
Bug:139439373 P2 Live tile should switch to screenshot before finishing recents animation
Bug:139828243 P2 Create a prototype for Overview screenshot actions with a SystemUI plugin
Bug:139888225 P2 Convert custom widget into plugins
Bug:139913027 P2 [a11y] App title in Widget list shouldn't be actionable by a11y methods. (It would cause Pixel launcher crash.)
Bug:139917483 P1 [B1C1][Dec19_QPR][CTS_Verifier_10_r1]Pixel launcher crash observed in Device owner test-> Lock Task UI
Bug:139941530 P2 Lab-only Flake: Launcher switches to All Apps after pressing Recents button
Bug:140212732 P2 [PO Cable] Strange launcher behavior after restore
Bug:140242324 P2 Cache shortcutInfo icons in Launcher
Bug:140246642 P4 Add binder tests for launcher interactions
Bug:140311911 P2 Flake in Launcher tests: java.lang.AssertionError: Stable state != state: OverviewState, LauncherState
Bug:140406263 P2 [a11y] Unable to scroll to the left of the main Home screen to Display google app by Voice access or Switch access.
Bug:140539007 P3 After apply the wallpaper on wallpaper picker, the screen will stay on wallpaper picker about 2 second
Bug:140626334 P4 Pass wallpaper SC to launcher for animation during launch and swipe up
Bug:140635319 P1 [Flaky test] testPromiseIcon_addedFromEligibleSession failing due to NPE on Launcher#getStateManager
Bug:140786694 P2 [A11y]No talkback feedback when long pressing on an app at home screen
Bug:140935140 P1 Launcher force close observed while accessing app info shortcuts.
Bug:141260670 P3 Drag and drop preview doesn't match the destination grid size
Bug:141262820 P3 [Grid] icon badge size should scale as the grid size changes
Bug:141265063 P2 Long press on homescreen in between icons doesn't bring up home settings
Bug:141275518 P2 Test WellbeingTests.testPauseAppFromOverview flakes on Cuttlefish
Bug:141315387 P1 [Failing test] 3P launchers + 2 button mode: failed goToOverviewFromHome and goToOverviewFromApp
Bug:141376165 P2 Remove static initializations in Launcher
Bug:141390432 P1 [Failing test] TaplTestsLauncher3 in 2-button mode
Bug:141517004 P1 FallbackRecentsTest.goToOverviewFromHome Failure
Bug:141522764 P1 DefaultLayoutProviderTest failures
Bug:141523101 P1 TaplTestsQuickstep#testAllAppsFromHome,testAllAppsFromOverview failures
Bug:141524555 P1 FallbackRecentsTest.testOverview flake
Bug:141576561 P2 Fix FlagOverrideSampleTest inside robolectric test
Bug:141576665 P2 AddWorkspaceItemsTaskTest broken
Bug:141577881 P2 FileLogTest broken
Bug:141579810 P1 ViewInflationDuringSwipeUp test failures
Bug:141580748 P2 FallbackRecentsTest#testOverview failure
Bug:141697444 P1 TaplTestsLauncher3#testWidgets test failure
Bug:141770616 P2 Flake: Can't find a launcher object; selector: BySelector [RES='\Qcom.google.android.apps.nexuslauncher:id/deep_shortcuts_container\E'] (visible state: AllApps),
Bug:141772190 P2 Flake: Context menu is still visible afterswiping up to home
Bug:141864547 P2 calendar-stable sometimes fails dialog dismissal on cuttlefish
Bug:141886704 P2 Use app targets to determine input consumer instead of launcher state
Bug:141934188 P2 Automation Test for adding widget automatically
Bug:141939911 P3 Clean up SwipeDetector
Bug:141986013 P2 Update all apps fade interpolators
Bug:142068081 P1 Quick Switch touch swipe dropped, flicker ensues
Bug:142120338 P2 Gmail app (work profile) icon not drawing
Bug:142148773 P2 grid size should not affect widget padding in WidgetsRecyclerView
Bug:142351228 P1 public void testSwipeUp_with_list_widgets() is failing for merge CL
Bug:79868152 P3 Fade out caret with workspace and back in when in Overview
Bug:136282913 P1 Swipe up from Assistant Fulfillment Card Jank
Bug:138473688 P4 Home settings can't be dismissed by swipe up
Bug:140252765 P2 Audit Gesture Nav tests (including Back and Quick Switch)
Bug:141568904 P1 NPE: Pixel launcher crash is observed when adding Directions widget
Bug:142514365 P1 Failing test AddWidgetTest.testDragIcon
Bug:142803200 P1 Broken binder tests

Change-Id: I811537cc8d406f0acd9fa45daddae4da79ffff12
diff --git a/tests/Android.mk b/tests/Android.mk
index b5c1dae..31a9960 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,7 +30,6 @@
     LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
 
     LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
-        ../quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java \
         ../src/com/android/launcher3/ResourceUtils.java \
         ../src/com/android/launcher3/util/SecureSettingsObserver.java \
         ../src/com/android/launcher3/testing/TestProtocol.java
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index c6f55a7..56eca6d 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -62,6 +62,12 @@
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
             </intent-filter>
         </activity>
+        <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
         <activity
             android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
             android:icon="@drawable/test_drawable_pin_item"
@@ -73,8 +79,12 @@
             </intent-filter>
         </activity>
 
+        <service
+            android:name="com.android.launcher3.testcomponent.ListViewService"
+            android:permission="android.permission.BIND_REMOTEVIEWS" />
+
         <provider
-            android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
+            android:name="com.android.launcher3.testcomponent.TestCommandProvider"
             android:authorities="${packageName}.commands"
             android:exported="true"/>
 
diff --git a/tests/res/layout/test_layout_widget_list.xml b/tests/res/layout/test_layout_widget_list.xml
new file mode 100644
index 0000000..0152040
--- /dev/null
+++ b/tests/res/layout/test_layout_widget_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#FFFFFF">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#FF0000FF"
+        android:id="@android:id/text1"
+        android:padding="10dp" />
+
+    <ListView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:id="@android:id/list" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index 7ef946d..efbd9c9 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -48,6 +48,7 @@
     public void setUp() throws Exception {
         super.setUp();
         mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
         waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
         mSessionId = -1;
     }
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 64df8e0..7029ad5 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -1,29 +1,5 @@
 package com.android.launcher3.model;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.database.MatrixCursor;
-import android.graphics.Bitmap;
-import android.os.Process;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
 import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
 import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
@@ -32,6 +8,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.ICON;
 import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
 import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
+import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -40,15 +17,41 @@
 import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
 import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
 import static com.android.launcher3.LauncherSettings.Favorites._ID;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.os.Process;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for {@link LoaderCursor}
  */
@@ -62,7 +65,7 @@
     private MatrixCursor mCursor;
     private InvariantDeviceProfile mIDP;
     private Context mContext;
-    private LauncherAppsCompat mLauncherApps;
+    private LauncherApps mLauncherApps;
 
     private LoaderCursor mLoaderCursor;
 
@@ -81,7 +84,7 @@
         when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
         when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
         when(mMockApp.getContext()).thenReturn(mContext);
-        mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+        mLauncherApps = mContext.getSystemService(LauncherApps.class);
 
         mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
         mLoaderCursor.allUsers.put(0, Process.myUserHandle());
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 6fa8d62..27990f4 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -1,8 +1,11 @@
 package com.android.launcher3.provider;
 
+import static org.junit.Assert.assertEquals;
+
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -13,8 +16,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-
 /**
  * Tests for {@link RestoreDbTask}
  */
@@ -82,7 +83,7 @@
         private final long mProfileId;
 
         MyDatabaseHelper(long profileId) {
-            super(InstrumentationRegistry.getContext(), null, null);
+            super(InstrumentationRegistry.getContext(), null);
             mProfileId = profileId;
         }
 
diff --git a/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java b/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java
new file mode 100644
index 0000000..b673faa
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.testcomponent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import com.android.launcher3.R;
+
+import java.util.UUID;
+
+/**
+ * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
+ * Custom shortcuts are replaced by deep shortcuts after api 25.
+ */
+public class CustomShortcutConfigActivity extends BaseTestingActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent launchIntent = new Intent(this, BaseTestingActivity.class)
+                .setAction("com.android.launcher3.intent.action.test_shortcut");
+        Intent shortcutIntent = createShortcutResultIntent(
+                this, UUID.randomUUID().toString(), "Shortcut",
+                R.drawable.ic_widget, launchIntent);
+        setResult(RESULT_OK, shortcutIntent);
+        finish();
+    }
+
+    private static Intent createShortcutResultIntent(
+            Context context, String uniqueId, String name, int iconId, Intent launchIntent) {
+        ShortcutInfo shortcutInfo =
+                createShortcutInfo(context, uniqueId, name, iconId, launchIntent);
+        ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+        return sm.createShortcutResultIntent(shortcutInfo);
+    }
+
+    private static ShortcutInfo createShortcutInfo(
+            Context context, String uniqueId, String name, int iconId, Intent launchIntent) {
+        return new ShortcutInfo.Builder(context, uniqueId)
+                .setShortLabel(name)
+                .setLongLabel(name)
+                .setIcon(Icon.createWithResource(context, iconId))
+                .setIntent(launchIntent)
+                .build();
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/ListViewService.java b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
new file mode 100644
index 0000000..3da20e0
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+public class ListViewService extends RemoteViewsService {
+
+    public static IBinder sBinderForTest;
+
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        return new SimpleViewsFactory();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return sBinderForTest != null ? sBinderForTest : super.onBind(intent);
+    }
+
+    public static class SimpleViewsFactory implements RemoteViewsFactory {
+
+        public int viewCount = 0;
+
+        @Override
+        public void onCreate() { }
+
+        @Override
+        public void onDataSetChanged() { }
+
+        @Override
+        public void onDestroy() { }
+
+        @Override
+        public int getCount() {
+            return viewCount;
+        }
+
+        @Override
+        public RemoteViews getViewAt(int i) {
+            RemoteViews views = new RemoteViews("android", android.R.layout.simple_list_item_1);
+            views.setTextViewText(android.R.id.text1, getLabel(i));
+            return views;
+        }
+
+        public String getLabel(int i) {
+            return "Item " + i;
+        }
+
+        @Override
+        public RemoteViews getLoadingView() {
+            return null;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+
+        @Override
+        public long getItemId(int i) {
+            return i;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return false;
+        }
+
+        public IBinder toBinder() {
+            return new RemoteViewsService() {
+                @Override
+                public RemoteViewsFactory onGetViewFactory(Intent intent) {
+                    return SimpleViewsFactory.this;
+                }
+            }.onBind(new Intent("dummy_intent"));
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java
new file mode 100644
index 0000000..f9981a9
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+
+import static com.android.launcher3.testcomponent.TestCommandReceiver.DISABLE_TEST_LAUNCHER;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.ENABLE_TEST_LAUNCHER;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.KILL_PROCESS;
+import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Base64;
+
+import com.android.launcher3.tapl.TestHelpers;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class TestCommandProvider extends ContentProvider {
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        switch (method) {
+            case ENABLE_TEST_LAUNCHER: {
+                getContext().getPackageManager().setComponentEnabledSetting(
+                        new ComponentName(getContext(), TestLauncherActivity.class),
+                        COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+                return null;
+            }
+            case DISABLE_TEST_LAUNCHER: {
+                getContext().getPackageManager().setComponentEnabledSetting(
+                        new ComponentName(getContext(), TestLauncherActivity.class),
+                        COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+                return null;
+            }
+            case KILL_PROCESS: {
+                ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE))
+                        .killBackgroundProcesses(arg);
+                return null;
+            }
+
+            case GET_SYSTEM_HEALTH_MESSAGE: {
+                final Bundle response = new Bundle();
+                response.putString("result",
+                        TestHelpers.getSystemHealthMessage(getContext(), Long.parseLong(arg)));
+                return response;
+            }
+
+            case SET_LIST_VIEW_SERVICE_BINDER: {
+                ListViewService.sBinderForTest = extras.getBinder(EXTRA_VALUE);
+                return null;
+            }
+        }
+        return super.call(method, arg, extras);
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        String path = Base64.encodeToString(uri.getPath().getBytes(),
+                Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+        File file = new File(getContext().getCacheDir(), path);
+        if (!file.exists()) {
+            // Create an empty file so that we can pass its descriptor
+            try {
+                file.createNewFile();
+            } catch (IOException e) {
+            }
+        }
+
+        return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
index 6a6916e..eb6c3ed 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -15,125 +15,36 @@
  */
 package com.android.launcher3.testcomponent;
 
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.DONT_KILL_APP;
-import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
-
-import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.util.Base64;
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.launcher3.tapl.TestHelpers;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
 /**
  * Content provider to receive commands from tests
  */
-public class TestCommandReceiver extends ContentProvider {
+public class TestCommandReceiver {
 
     public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher";
     public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher";
     public static final String KILL_PROCESS = "kill-process";
     public static final String GET_SYSTEM_HEALTH_MESSAGE = "get-system-health-message";
+    public static final String SET_LIST_VIEW_SERVICE_BINDER = "set-list-view-service-binder";
 
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Bundle call(String method, String arg, Bundle extras) {
-        switch (method) {
-            case ENABLE_TEST_LAUNCHER: {
-                getContext().getPackageManager().setComponentEnabledSetting(
-                        new ComponentName(getContext(), TestLauncherActivity.class),
-                        COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
-                return null;
-            }
-            case DISABLE_TEST_LAUNCHER: {
-                getContext().getPackageManager().setComponentEnabledSetting(
-                        new ComponentName(getContext(), TestLauncherActivity.class),
-                        COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
-                return null;
-            }
-            case KILL_PROCESS: {
-                ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)).
-                        killBackgroundProcesses(arg);
-                return null;
-            }
-
-            case GET_SYSTEM_HEALTH_MESSAGE: {
-                final Bundle response = new Bundle();
-                response.putString("result", TestHelpers.getSystemHealthMessage(getContext()));
-                return response;
-            }
-        }
-        return super.call(method, arg, extras);
-    }
+    public static final String EXTRA_VALUE = "value";
 
     public static Bundle callCommand(String command) {
         return callCommand(command, null);
     }
 
     public static Bundle callCommand(String command, String arg) {
-        Instrumentation inst = InstrumentationRegistry.getInstrumentation();
-        Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
-        return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
+        return callCommand(command, arg, null);
     }
 
-    @Override
-    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
-        String path = Base64.encodeToString(uri.getPath().getBytes(),
-                Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
-        File file = new File(getContext().getCacheDir(), path);
-        if (!file.exists()) {
-            // Create an empty file so that we can pass its descriptor
-            try {
-                file.createNewFile();
-            } catch (IOException e) {
-            }
-        }
-
-        return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+    public static Bundle callCommand(String command, String arg, Bundle extras) {
+        Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
+        return inst.getTargetContext().getContentResolver().call(uri, command, arg, extras);
     }
 }
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index e042357..f209fae 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -25,6 +25,10 @@
 import android.util.Log;
 import android.view.ViewConfiguration;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.launcher3.testcomponent.TouchEventGenerator;
 
 import org.junit.Before;
@@ -33,10 +37,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SwipeDetectorTest {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1fac708..62989a3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,6 +17,7 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
 import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -27,11 +28,13 @@
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -43,19 +46,19 @@
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Wait;
@@ -94,8 +97,7 @@
 
     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
     protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
-    protected final LauncherInstrumentation mLauncher =
-            new LauncherInstrumentation(getInstrumentation());
+    protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation();
     protected Context mTargetContext;
     protected String mTargetPackage;
 
@@ -107,8 +109,9 @@
         }
         if (TestHelpers.isInLauncherProcess()) {
             Utilities.enableRunningInTestHarnessForTests();
-            mLauncher.setSystemHealthSupplier(() -> TestCommandReceiver.callCommand(
-                    TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE).getString("result"));
+            mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
+                    TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
+                    getString("result"));
             mLauncher.setOnSettledStateAction(
                     containerType -> executeOnLauncher(
                             launcher ->
@@ -173,15 +176,13 @@
 
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
-        // Unlock the phone
-        mDevice.executeShellCommand("input keyevent 82");
     }
 
     @After
     public void verifyLauncherState() {
         try {
             // Limits UI tests affecting tests running after them.
-            waitForModelLoaded();
+            mLauncher.waitForLauncherInitialized();
         } catch (Throwable t) {
             Log.e(TAG,
                     "Couldn't deinit after a test, exiting tests, see logs for failures that "
@@ -220,14 +221,36 @@
         } catch (Throwable t) {
             throw new IllegalArgumentException(t);
         }
-        waitForModelLoaded();
+        mLauncher.waitForLauncherInitialized();
     }
 
-    protected void waitForModelLoaded() {
-        waitForLauncherCondition("Launcher model didn't load", launcher -> {
-            final LauncherModel model = LauncherAppState.getInstance(mTargetContext).getModel();
-            return model.getCallback() == null || model.isModelLoaded();
-        });
+    /**
+     * Adds {@param item} on the homescreen on the 0th screen
+     */
+    protected void addItemToScreen(ItemInfo item) {
+        ContentResolver resolver = mTargetContext.getContentResolver();
+        int screenId = FIRST_SCREEN_ID;
+        // Update the screen id counter for the provider.
+        LauncherSettings.Settings.call(resolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+        if (screenId > FIRST_SCREEN_ID) {
+            screenId = FIRST_SCREEN_ID;
+        }
+
+        // Insert the item
+        ContentWriter writer = new ContentWriter(mTargetContext);
+        item.id = LauncherSettings.Settings.call(
+                resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+        item.screenId = screenId;
+        item.onAddToDatabase(writer);
+        writer.put(LauncherSettings.Favorites._ID, item.id);
+        resolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
+        resetLoaderState();
+
+        // Launch the home activity
+        mDevice.pressHome();
+        mLauncher.waitForLauncherInitialized();
     }
 
     /**
@@ -312,9 +335,8 @@
     }
 
     protected LauncherActivityInfo getSettingsApp() {
-        return LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.settings",
-                        Process.myUserHandle()).get(0);
+        return mTargetContext.getSystemService(LauncherApps.class)
+                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
     }
 
     /**
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index a76b4a4..1d89d6e 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -30,7 +30,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.testcomponent.TestCommandProvider;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.rule.ShellCommandRule;
 
@@ -63,7 +63,7 @@
 
         PackageManager pm = mTargetContext.getPackageManager();
         ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
-                TestCommandReceiver.class), 0);
+                TestCommandProvider.class), 0);
         mAuthority = pi.authority;
     }
 
@@ -73,7 +73,6 @@
 
         // Launch the home activity
         mDevice.pressHome();
-        waitForModelLoaded();
 
         mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
     }
@@ -89,7 +88,6 @@
 
         // Launch the home activity
         mDevice.pressHome();
-        waitForModelLoaded();
 
         // Verify widget present
         assertTrue("Widget is not present",
@@ -106,7 +104,6 @@
 
         // Launch the home activity
         mDevice.pressHome();
-        waitForModelLoaded();
 
         mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
     }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 2cf6c2b..5e87612 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -24,8 +24,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import android.util.Log;
-
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -38,7 +36,6 @@
 import com.android.launcher3.tapl.AppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
 import com.android.launcher3.views.OptionsPopupView;
 import com.android.launcher3.widget.WidgetsFullSheet;
 import com.android.launcher3.widget.WidgetsRecyclerView;
@@ -342,13 +339,24 @@
         }
     }
 
-    public static String getAppPackageName() {
-        return getInstrumentation().getContext().getPackageName();
+    /**
+     * Test dragging a custom shortcut to the workspace and launch it.
+     *
+     * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
+     * Custom shortcuts are replaced by deep shortcuts after api 25.
+     */
+    @Test
+    @Ignore("Temporarily disabled to unblock merging to master")
+    @PortraitLandscape
+    public void testDragCustomShortcut() {
+        mLauncher.getWorkspace().openAllWidgets()
+                .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
+                .dragToWorkspace();
+        mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
+                .launch(getAppPackageName());
     }
 
-    @Test
-    @Stability
-    public void testTestStabilityAttribute() {
-        Log.d("TestStabilityRule", "Hello world!");
+    public static String getAppPackageName() {
+        return getInstrumentation().getContext().getPackageName();
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 3f35a3a..e1b3ede 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,7 +52,8 @@
 @RunWith(AndroidJUnit4.class)
 public class AddConfigWidgetTest extends AbstractLauncherUiTest {
 
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+    @Rule
+    public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     private LauncherAppWidgetProviderInfo mWidgetInfo;
     private AppWidgetManager mAppWidgetManager;
@@ -70,14 +70,12 @@
 
     @Test
     @PortraitLandscape
-    @org.junit.Ignore
     public void testWidgetConfig() throws Throwable {
         runTest(true);
     }
 
     @Test
     @PortraitLandscape
-    @org.junit.Ignore
     public void testConfigCancelled() throws Throwable {
         runTest(false);
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 1edce22..b8ca5de 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -30,7 +30,6 @@
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,11 +41,11 @@
 @RunWith(AndroidJUnit4.class)
 public class AddWidgetTest extends AbstractLauncherUiTest {
 
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+    @Rule
+    public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     @Test
     @PortraitLandscape
-    @org.junit.Ignore
     public void testDragIcon() throws Throwable {
         clearHomescreen();
         mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index e6348d9..3d691da 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -15,7 +15,9 @@
  */
 package com.android.launcher3.ui.widget;
 
-import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -25,6 +27,7 @@
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
@@ -39,15 +42,12 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.pm.PackageInstallerCompat;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.WidgetHostViewLoader;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,7 +57,6 @@
 
 import java.util.HashSet;
 import java.util.Set;
-import java.util.function.Consumer;
 
 /**
  * Tests for bind widget flow.
@@ -72,7 +71,6 @@
     public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     private ContentResolver mResolver;
-    private AppWidgetManagerCompat mWidgetManager;
 
     // Objects created during test, which should be cleaned up in the end.
     private Cursor mCursor;
@@ -85,7 +83,6 @@
         super.setUp();
 
         mResolver = mTargetContext.getContentResolver();
-        mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
 
         // Clear all existing data
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
@@ -108,7 +105,7 @@
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyWidgetPresent(info);
     }
 
@@ -117,7 +114,7 @@
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyWidgetPresent(info);
     }
 
@@ -127,7 +124,7 @@
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.appWidgetId = -33;
 
-        setupContents(item);
+        addItemToScreen(item);
 
         final Workspace workspace = mLauncher.getWorkspace();
         // Item deleted from db
@@ -148,7 +145,7 @@
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyWidgetPresent(info);
     }
 
@@ -161,7 +158,7 @@
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyPendingWidgetPresent();
 
         // Item deleted from db
@@ -183,7 +180,7 @@
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
                 | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
 
-        setupContents(item);
+        addItemToScreen(item);
 
         assertTrue("Pending widget exists",
                 mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
@@ -202,7 +199,7 @@
                 | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
                 | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyPendingWidgetPresent();
 
         // Verify item still exists in db
@@ -230,7 +227,7 @@
         PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
         mSessionId = installer.createSession(params);
 
-        setupContents(item);
+        addItemToScreen(item);
         verifyPendingWidgetPresent();
 
         // Verify item still exists in db
@@ -245,35 +242,6 @@
                         & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
     }
 
-    /**
-     * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
-     * widget class is displayed on the homescreen.
-     */
-    private void setupContents(LauncherAppWidgetInfo item) {
-        int screenId = FIRST_SCREEN_ID;
-        // Update the screen id counter for the provider.
-        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
-        if (screenId > FIRST_SCREEN_ID) {
-            screenId = FIRST_SCREEN_ID;
-        }
-
-        // Insert the item
-        ContentWriter writer = new ContentWriter(mTargetContext);
-        item.id = LauncherSettings.Settings.call(
-                mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
-                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-        item.screenId = screenId;
-        item.onAddToDatabase(writer);
-        writer.put(LauncherSettings.Favorites._ID, item.id);
-        mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
-        resetLoaderState();
-
-        // Launch the home activity
-        mDevice.pressHome();
-        waitForModelLoaded();
-    }
-
     private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
         assertTrue("Widget is not present",
                 mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
@@ -289,8 +257,10 @@
      * @param bindWidget if true the info is bound and a valid widgetId is assigned to
      *                   the LauncherAppWidgetInfo
      */
-    private LauncherAppWidgetInfo createWidgetInfo(
+    public static LauncherAppWidgetInfo createWidgetInfo(
             LauncherAppWidgetProviderInfo info, boolean bindWidget) {
+        Context targetContext = getTargetContext();
+
         LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
                 LauncherAppWidgetInfo.NO_ID, info.provider);
         item.spanX = info.minSpanX;
@@ -308,11 +278,12 @@
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
             pendingInfo.minSpanY = item.minSpanY;
-            Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
+            Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
 
-            AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
+            AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
             int widgetId = host.allocateAppWidgetId();
-            if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+            if (!AppWidgetManagerCompat.getInstance(targetContext)
+                    .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
                 host.deleteAppWidgetId(widgetId);
                 throw new IllegalArgumentException("Unable to bind widget id");
             }
@@ -331,7 +302,7 @@
 
         Set<String> activePackage = getOnUiThread(() -> {
             Set<String> packages = new HashSet<>();
-            PackageInstallerCompat.getInstance(mTargetContext).updateAndGetActiveSessionCache()
+            PackageInstallerCompat.getInstance(mTargetContext).getActiveSessions()
                     .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
             return packages;
         });
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index 8f89173..ed2ec7b 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.util;
 
 import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -64,15 +62,30 @@
  *
  * When we register event XXX:enter, we hold all other events until we register XXX:exit.
  */
-public class RaceConditionReproducer implements RaceConditionTracker.EventProcessor {
+public class RaceConditionReproducer {
     private static final String TAG = "RaceConditionReproducer";
+
+    private static final boolean ENTER = true;
+    private static final boolean EXIT = false;
+    private static final String ENTER_POSTFIX = "enter";
+    private static final String EXIT_POSTFIX = "exit";
+
     private static final long SHORT_TIMEOUT_MS = 2000;
     private static final long LONG_TIMEOUT_MS = 60000;
     // Handler used to resume postponed events.
-    private static final Handler POSTPONED_EVENT_RESUME_HANDLER = createEventResumeHandler();
+    private static final Handler POSTPONED_EVENT_RESUME_HANDLER =
+            new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
 
-    private static Handler createEventResumeHandler() {
-        return new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
+    public static String enterExitEvt(String eventName, boolean isEnter) {
+        return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
+    }
+
+    public static String enterEvt(String eventName) {
+        return enterExitEvt(eventName, ENTER);
+    }
+
+    public static String exitEvt(String eventName) {
+        return enterExitEvt(eventName, EXIT);
     }
 
     /**
@@ -209,7 +222,8 @@
                 parseReproString(mReproString) : generateSequenceToFollowLocked();
         Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
         checkIfCompletedSequenceToFollowLocked();
-        RaceConditionTracker.setEventProcessor(this);
+
+        TraceHelperForTest.setRaceConditionReproducer(this);
     }
 
     /**
@@ -218,7 +232,8 @@
      * Returns whether we need more iterations.
      */
     public synchronized boolean finishIteration() {
-        RaceConditionTracker.setEventProcessor(null);
+        TraceHelperForTest.setRaceConditionReproducer(null);
+
         runResumeAllEventsCallbackLocked();
         assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
         assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
@@ -243,7 +258,6 @@
     /**
      * Called when the app issues an event.
      */
-    @Override
     public void onEvent(String event) {
         final Semaphore waitObject = tryRegisterEvent(event);
         if (waitObject != null) {
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
index 3fc268e..59f2173 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
@@ -22,6 +22,8 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,17 +39,29 @@
         return res;
     }
 
-    private static void run3_3_TestAction() throws InterruptedException {
+    RaceConditionReproducer eventProcessor;
+
+    @Before
+    public void setup() {
+        eventProcessor = new RaceConditionReproducer();
+    }
+
+    @After
+    public void tearDown() {
+        TraceHelperForTest.cleanup();
+    }
+
+    private void run3_3_TestAction() throws InterruptedException {
         Thread tb = new Thread(() -> {
-            RaceConditionTracker.onEvent("B1");
-            RaceConditionTracker.onEvent("B2");
-            RaceConditionTracker.onEvent("B3");
+            eventProcessor.onEvent("B1");
+            eventProcessor.onEvent("B2");
+            eventProcessor.onEvent("B3");
         });
         tb.start();
 
-        RaceConditionTracker.onEvent("A1");
-        RaceConditionTracker.onEvent("A2");
-        RaceConditionTracker.onEvent("A3");
+        eventProcessor.onEvent("A1");
+        eventProcessor.onEvent("A2");
+        eventProcessor.onEvent("A3");
 
         tb.join();
     }
@@ -56,7 +70,6 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads, 3 events each.
     public void test3_3() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
         boolean sawTheValidSequence = false;
 
         for (; ; ) {
@@ -80,25 +93,24 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads, 3 events, including enter-exit pairs each.
     public void test3_3_enter_exit() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
         boolean sawTheValidSequence = false;
 
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1:enter");
-                RaceConditionTracker.onEvent("B1:exit");
-                RaceConditionTracker.onEvent("B2");
-                RaceConditionTracker.onEvent("B3:enter");
-                RaceConditionTracker.onEvent("B3:exit");
+                eventProcessor.onEvent("B1:enter");
+                eventProcessor.onEvent("B1:exit");
+                eventProcessor.onEvent("B2");
+                eventProcessor.onEvent("B3:enter");
+                eventProcessor.onEvent("B3:exit");
             });
             tb.start();
 
-            RaceConditionTracker.onEvent("A1");
-            RaceConditionTracker.onEvent("A2:enter");
-            RaceConditionTracker.onEvent("A2:exit");
-            RaceConditionTracker.onEvent("A3:enter");
-            RaceConditionTracker.onEvent("A3:exit");
+            eventProcessor.onEvent("A1");
+            eventProcessor.onEvent("A2:enter");
+            eventProcessor.onEvent("A2:exit");
+            eventProcessor.onEvent("A3:enter");
+            eventProcessor.onEvent("A3:exit");
 
             tb.join();
             final boolean needMoreIterations = eventProcessor.finishIteration();
@@ -119,9 +131,7 @@
     @Test
     // 2 threads, 3 events each; reproducing a particular event sequence.
     public void test3_3_ReproMode() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(
-                SOME_VALID_SEQUENCE_3_3);
-
+        eventProcessor = new RaceConditionReproducer(SOME_VALID_SEQUENCE_3_3);
         eventProcessor.startIteration();
         run3_3_TestAction();
         assertTrue(!eventProcessor.finishIteration());
@@ -134,23 +144,21 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads with 2 events; 1 thread with 1 event.
     public void test2_1_2() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1");
-                RaceConditionTracker.onEvent("B2");
+                eventProcessor.onEvent("B1");
+                eventProcessor.onEvent("B2");
             });
             tb.start();
 
             Thread tc = new Thread(() -> {
-                RaceConditionTracker.onEvent("C1");
+                eventProcessor.onEvent("C1");
             });
             tc.start();
 
-            RaceConditionTracker.onEvent("A1");
-            RaceConditionTracker.onEvent("A2");
+            eventProcessor.onEvent("A1");
+            eventProcessor.onEvent("A2");
 
             tb.join();
             tc.join();
@@ -167,28 +175,26 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
     public void test2_1_2_enter_exit() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1:enter");
-                RaceConditionTracker.onEvent("B1:exit");
-                RaceConditionTracker.onEvent("B2:enter");
-                RaceConditionTracker.onEvent("B2:exit");
+                eventProcessor.onEvent("B1:enter");
+                eventProcessor.onEvent("B1:exit");
+                eventProcessor.onEvent("B2:enter");
+                eventProcessor.onEvent("B2:exit");
             });
             tb.start();
 
             Thread tc = new Thread(() -> {
-                RaceConditionTracker.onEvent("C1:enter");
-                RaceConditionTracker.onEvent("C1:exit");
+                eventProcessor.onEvent("C1:enter");
+                eventProcessor.onEvent("C1:exit");
             });
             tc.start();
 
-            RaceConditionTracker.onEvent("A1:enter");
-            RaceConditionTracker.onEvent("A1:exit");
-            RaceConditionTracker.onEvent("A2:enter");
-            RaceConditionTracker.onEvent("A2:exit");
+            eventProcessor.onEvent("A1:enter");
+            eventProcessor.onEvent("A1:exit");
+            eventProcessor.onEvent("A2:enter");
+            eventProcessor.onEvent("A2:exit");
 
             tb.join();
             tc.join();
diff --git a/tests/src/com/android/launcher3/util/TraceHelperForTest.java b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
new file mode 100644
index 0000000..f1c8a67
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import java.util.LinkedList;
+import java.util.function.IntConsumer;
+
+public class TraceHelperForTest extends TraceHelper {
+
+    private static final TraceHelperForTest INSTANCE_FOR_TEST = new TraceHelperForTest();
+
+    private final ThreadLocal<LinkedList<TraceInfo>> mStack =
+            ThreadLocal.withInitial(LinkedList::new);
+
+    private RaceConditionReproducer mRaceConditionReproducer;
+    private IntConsumer mFlagsChangeListener;
+
+    public static void setRaceConditionReproducer(RaceConditionReproducer reproducer) {
+        TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+        INSTANCE_FOR_TEST.mRaceConditionReproducer = reproducer;
+    }
+
+    public static void cleanup() {
+        INSTANCE_FOR_TEST.mRaceConditionReproducer = null;
+        INSTANCE_FOR_TEST.mFlagsChangeListener = null;
+    }
+
+    public static void setFlagsChangeListener(IntConsumer listener) {
+        TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+        INSTANCE_FOR_TEST.mFlagsChangeListener = listener;
+    }
+
+    private TraceHelperForTest() { }
+
+    @Override
+    public Object beginSection(String sectionName, int flags) {
+        LinkedList<TraceInfo> stack = mStack.get();
+        TraceInfo info = new TraceInfo(sectionName, flags);
+        stack.add(info);
+
+        if ((flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+                 && mRaceConditionReproducer != null) {
+            mRaceConditionReproducer.onEvent(RaceConditionReproducer.enterEvt(sectionName));
+        }
+        updateBinderTracking(stack);
+
+        super.beginSection(sectionName, flags);
+        return info;
+    }
+
+    @Override
+    public void endSection(Object token) {
+        LinkedList<TraceInfo> stack = mStack.get();
+        if (stack.size() == 0) {
+            new Throwable().printStackTrace();
+        }
+        TraceInfo info = (TraceInfo) token;
+        stack.remove(info);
+        if ((info.flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+                && mRaceConditionReproducer != null) {
+            mRaceConditionReproducer.onEvent(RaceConditionReproducer.exitEvt(info.sectionName));
+        }
+        updateBinderTracking(stack);
+
+        super.endSection(token);
+    }
+
+    @Override
+    public Object beginFlagsOverride(int flags) {
+        LinkedList<TraceInfo> stack = mStack.get();
+        TraceInfo info = new TraceInfo(null, flags);
+        stack.add(info);
+        updateBinderTracking(stack);
+        super.beginFlagsOverride(flags);
+        return info;
+    }
+
+    @Override
+    public void endFlagsOverride(Object token) {
+        super.endFlagsOverride(token);
+        LinkedList<TraceInfo> stack = mStack.get();
+        TraceInfo info = (TraceInfo) token;
+        stack.remove(info);
+        updateBinderTracking(stack);
+    }
+
+    private void updateBinderTracking(LinkedList<TraceInfo> stack) {
+        if (mFlagsChangeListener != null) {
+            mFlagsChangeListener.accept(stack.stream()
+                    .mapToInt(info -> info.flags).reduce(0, (a, b) -> a | b));
+        }
+    }
+
+    private static class TraceInfo {
+        public final String sectionName;
+        public final int flags;
+
+        TraceInfo(String sectionName, int flags) {
+            this.sectionName = sectionName;
+            this.flags = flags;
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 8391ae7..69bf01d 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -17,6 +17,7 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.util.Log;
 
@@ -37,11 +38,11 @@
     private static final String TAG = "TestStabilityRule";
     private static final Pattern LAUNCHER_BUILD =
             Pattern.compile("^("
-                    + "(?<androidStudio>BuildFromAndroidStudio)|"
-                    + "(?<commandLine>[0-9]+-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+)|"
-                    + "(?<presubmit>[0-9]+-P[0-9]+)|"
-                    + "(?<postsubmit>[0-9]+-[0-9]+|"
-                    + "(?<platform>[0-9]+))"
+                    + "(?<local>(BuildFromAndroidStudio|"
+                    + "([0-9]+|[A-Z])-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+))|"
+                    + "(?<presubmit>([0-9]+|[A-Z])-P[0-9]+)|"
+                    + "(?<postsubmit>([0-9]+|[A-Z])-[0-9]+)|"
+                    + "(?<platform>[0-9]+|[A-Z])"
                     + ")$");
     private static final Pattern PLATFORM_BUILD =
             Pattern.compile("^("
@@ -50,88 +51,97 @@
                     + "(?<postsubmit>[0-9]+)"
                     + ")$");
 
+    public static final int LOCAL = 0x1;
+    public static final int UNBUNDLED_PRESUBMIT = 0x2;
+    public static final int UNBUNDLED_POSTSUBMIT = 0x4;
+    public static final int PLATFORM_PRESUBMIT = 0x8;
+    public static final int PLATFORM_POSTSUBMIT = 0x10;
+
+    private static final int RUN_FLAFOR = getRunFlavor();
+
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface Stability {
+        int flavors();
     }
 
     @Override
     public Statement apply(Statement base, Description description) {
-        if (description.getAnnotation(Stability.class) != null) {
+        final Stability stability = description.getAnnotation(Stability.class);
+        if (stability != null) {
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    final String launcherVersion =
-                            getInstrumentation().
-                                    getContext().
-                                    getPackageManager().
-                                    getPackageInfo(
-                                            UiDevice.getInstance(getInstrumentation()).
-                                                    getLauncherPackageName(),
-                                            0).
-                                    versionName;
-
-                    final Matcher launcherBuildMatcher = LAUNCHER_BUILD.matcher(launcherVersion);
-
-                    boolean launcherLocalBuild = false;
-                    boolean launcherUnbundledPresubmit = false;
-                    boolean launcherUnbundledPostsubmit = false;
-                    boolean launcherPlatform = false;
-
-                    if (!launcherBuildMatcher.find()) {
-                        Log.e(TAG, "Match not found");
-                    } else if (launcherBuildMatcher.group("androidStudio") != null
-                            || launcherBuildMatcher.group("commandLine") != null) {
-                        launcherLocalBuild = true;
-                    } else if (launcherBuildMatcher.group("presubmit") != null) {
-                        launcherUnbundledPresubmit = true;
-                    } else if (launcherBuildMatcher.group("postsubmit") != null) {
-                        launcherUnbundledPostsubmit = true;
-                    } else if (launcherBuildMatcher.group("platform") != null) {
-                        launcherPlatform = true;
+                    if ((stability.flavors() & RUN_FLAFOR) != 0) {
+                        Log.d(TAG, "Running " + description.getDisplayName());
+                        base.evaluate();
                     } else {
-                        Log.e(TAG, "ERROR1");
+                        Log.d(TAG, "Skipping " + description.getDisplayName());
                     }
-
-                    boolean platformLocalBuild = false;
-                    boolean platformPresubmit = false;
-                    boolean platformPostsubmit = false;
-
-                    final String platformVersion = Build.VERSION.INCREMENTAL;
-                    final Matcher platformBuildMatcher = PLATFORM_BUILD.matcher(platformVersion);
-                    if (!platformBuildMatcher.find()) {
-                        Log.e(TAG, "Match not found");
-                    } else if (platformBuildMatcher.group("commandLine") != null) {
-                        platformLocalBuild = true;
-                    } else if (platformBuildMatcher.group("presubmit") != null) {
-                        platformPresubmit = true;
-                    } else if (platformBuildMatcher.group("postsubmit") != null) {
-                        platformPostsubmit = true;
-                    } else {
-                        Log.e(TAG, "ERROR2");
-                    }
-
-                    Log.d(TAG, "Launcher: " + launcherVersion + ", platform: " + platformVersion);
-
-                    if (launcherLocalBuild && (platformLocalBuild || platformPostsubmit)) {
-                        Log.d(TAG, "LOCAL RUN");
-                    } else if (launcherUnbundledPresubmit && platformPostsubmit) {
-                        Log.d(TAG, "UNBUNDLED PRESUBMIT");
-                    } else if (launcherUnbundledPostsubmit && platformPostsubmit) {
-                        Log.d(TAG, "UNBUNDLED POSTSUBMIT");
-                    } else if (launcherPlatform && platformPresubmit) {
-                        Log.d(TAG, "PLATFORM PRESUBMIT");
-                    } else if (launcherPlatform && platformPostsubmit) {
-                        Log.d(TAG, "PLATFORM POSTSUBMIT");
-                    } else {
-                        Log.e(TAG, "ERROR3");
-                    }
-
-                    base.evaluate();
                 }
             };
         } else {
             return base;
         }
     }
+
+    private static int getRunFlavor() {
+        final String launcherVersion;
+        try {
+            launcherVersion = getInstrumentation().
+                    getContext().
+                    getPackageManager().
+                    getPackageInfo(
+                            UiDevice.getInstance(getInstrumentation()).
+                                    getLauncherPackageName(),
+                            0).
+                    versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        final Matcher launcherBuildMatcher = LAUNCHER_BUILD.matcher(launcherVersion);
+
+        if (!launcherBuildMatcher.find()) {
+            throw new AssertionError("Launcher build match not found");
+        }
+
+        final String platformVersion = Build.VERSION.INCREMENTAL;
+        final Matcher platformBuildMatcher = PLATFORM_BUILD.matcher(platformVersion);
+
+        if (!platformBuildMatcher.find()) {
+            throw new AssertionError("Platform build match not found");
+        }
+
+        Log.d(TAG, "Launcher: " + launcherVersion + ", platform: " + platformVersion);
+
+        final int runFlavor;
+
+        if (launcherBuildMatcher.group("local") != null && (
+                platformBuildMatcher.group("commandLine") != null ||
+                        platformBuildMatcher.group("postsubmit") != null)) {
+            Log.d(TAG, "LOCAL RUN");
+            runFlavor = LOCAL;
+        } else if (launcherBuildMatcher.group("presubmit") != null
+                && platformBuildMatcher.group("postsubmit") != null) {
+            Log.d(TAG, "UNBUNDLED PRESUBMIT");
+            runFlavor = UNBUNDLED_PRESUBMIT;
+        } else if (launcherBuildMatcher.group("postsubmit") != null
+                && platformBuildMatcher.group("postsubmit") != null) {
+            Log.d(TAG, "UNBUNDLED POSTSUBMIT");
+            runFlavor = UNBUNDLED_POSTSUBMIT;
+        } else if (launcherBuildMatcher.group("platform") != null
+                && platformBuildMatcher.group("presubmit") != null) {
+            Log.d(TAG, "PLATFORM PRESUBMIT");
+            runFlavor = PLATFORM_PRESUBMIT;
+        } else if (launcherBuildMatcher.group("platform") != null
+                && platformBuildMatcher.group("postsubmit") != null) {
+            Log.d(TAG, "PLATFORM POSTSUBMIT");
+            runFlavor = PLATFORM_POSTSUBMIT;
+        } else {
+            throw new AssertionError("Unrecognized run flavor");
+        }
+
+        return runFlavor;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index f070280..e1e9b8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -53,8 +53,8 @@
         return LauncherInstrumentation.ContainerType.ALL_APPS;
     }
 
-    private boolean hasClickableIcon(
-            UiObject2 allAppsContainer, UiObject2 appListRecycler, BySelector appIconSelector) {
+    private boolean hasClickableIcon(UiObject2 allAppsContainer, UiObject2 appListRecycler,
+            BySelector appIconSelector, int displayBottom) {
         final UiObject2 icon = appListRecycler.findObject(appIconSelector);
         if (icon == null) {
             LauncherInstrumentation.log("hasClickableIcon: icon not visible");
@@ -66,6 +66,10 @@
             LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
             return false;
         }
+        if (iconBounds.bottom > displayBottom) {
+            LauncherInstrumentation.log("hasClickableIcon: icon center bellow bottom offset");
+            return false;
+        }
         LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
         return true;
     }
@@ -90,21 +94,32 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
                     "apps_list_view");
+            final UiObject2 searchBox = getSearchBox(allAppsContainer);
+
+            int bottomGestureMargin = ResourceUtils.getNavbarSize(
+                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+            int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+            int displayBottom = deviceHeight - bottomGestureMargin;
             allAppsContainer.setGestureMargins(
                     0,
                     getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
                     0,
-                    ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
-                            mLauncher.getResources()) + 1);
+                    bottomGestureMargin);
             final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
-            if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+            if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+                    displayBottom)) {
                 scrollBackToBeginning();
                 int attempts = 0;
-                int scroll = getScroll(allAppsContainer);
+                int scroll = getAllAppsScroll();
                 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
-                    while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
-                        mLauncher.scroll(allAppsContainer, Direction.DOWN, 0.8f, null, 50);
-                        final int newScroll = getScroll(allAppsContainer);
+                    while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+                            displayBottom)) {
+                        mLauncher.scrollToLastVisibleRow(
+                                allAppsContainer,
+                                mLauncher.getObjectsInContainer(allAppsContainer, "icon"),
+                                searchBox.getVisibleBounds().bottom
+                                        - allAppsContainer.getVisibleBounds().top);
+                        final int newScroll = getAllAppsScroll();
                         if (newScroll == scroll) break;
 
                         mLauncher.assertTrue(
@@ -117,8 +132,11 @@
                 verifyActiveContainer();
             }
 
+            // Ignore bottom offset selection here as there might not be any scroll more scroll
+            // region available.
             mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
-                    hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
+                    hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+                            deviceHeight));
 
             final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
                     appIconSelector);
@@ -136,16 +154,16 @@
             int attempts = 0;
             final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
 
-            for (int scroll = getScroll(allAppsContainer);
+            for (int scroll = getAllAppsScroll();
                     scroll != 0;
-                    scroll = getScroll(allAppsContainer)) {
+                    scroll = getAllAppsScroll()) {
                 mLauncher.assertTrue("Negative scroll position", scroll > 0);
 
                 mLauncher.assertTrue(
                         "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
                         ++attempts <= MAX_SCROLL_ATTEMPTS);
 
-                mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50);
+                mLauncher.scroll(allAppsContainer, Direction.UP, margins, 50);
             }
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -154,9 +172,10 @@
         }
     }
 
-    private int getScroll(UiObject2 allAppsContainer) {
-        return mLauncher.getAnswerFromLauncher(allAppsContainer, TestProtocol.GET_SCROLL_MESSAGE).
-                getInt(TestProtocol.SCROLL_Y_FIELD, -1);
+    private int getAllAppsScroll() {
+        return mLauncher.getTestInfo(
+                TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+                .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
     private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
@@ -172,7 +191,7 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             // Start the gesture in the center to avoid starting at elements near the top.
             mLauncher.scroll(
-                    allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10);
+                    allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10);
             verifyActiveContainer();
         }
     }
@@ -186,7 +205,7 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             // Start the gesture in the center, for symmetry with forward.
             mLauncher.scroll(
-                    allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
+                    allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10);
             verifyActiveContainer();
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index bcce8ef..0d9038f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 
 import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
 
 import com.android.launcher3.testing.TestProtocol;
 
@@ -104,7 +105,12 @@
                     startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
                 }
 
-                mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+                if (mLauncher.isFallbackOverview()) {
+                    mLauncher.linearGesture(startX, startY, endX, endY, 10);
+                    new BaseOverview(mLauncher);
+                } else {
+                    mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+                }
                 break;
             }
 
@@ -114,6 +120,60 @@
         }
     }
 
+    /**
+     * Swipes right or double presses the square button to switch to the previous app.
+     */
+    public Background quickSwitchToPreviousApp() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to quick switch to the previous app")) {
+            verifyActiveContainer();
+            quickSwitchToPreviousApp(getExpectedStateForQuickSwitch());
+            return new Background(mLauncher);
+        }
+    }
+
+    protected int getExpectedStateForQuickSwitch() {
+        return BACKGROUND_APP_STATE_ORDINAL;
+    }
+
+    protected void quickSwitchToPreviousApp(int expectedState) {
+        boolean transposeInLandscape = false;
+        switch (mLauncher.getNavigationModel()) {
+            case TWO_BUTTON:
+                transposeInLandscape = true;
+                // Fall through, zero button and two button modes behave the same.
+            case ZERO_BUTTON: {
+                final int startX;
+                final int startY;
+                final int endX;
+                final int endY;
+                if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+                    // Swipe from the bottom left to the bottom right of the screen.
+                    startX = 0;
+                    startY = getSwipeStartY();
+                    endX = mLauncher.getDevice().getDisplayWidth();
+                    endY = startY;
+                } else {
+                    // Swipe from the bottom right to the top right of the screen.
+                    startX = getSwipeStartX();
+                    startY = mLauncher.getRealDisplaySize().y - 1;
+                    endX = startX;
+                    endY = 0;
+                }
+                mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState);
+                break;
+            }
+
+            case THREE_BUTTON:
+                // Double press the recents button.
+                UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+                recentsButton.click();
+                mLauncher.getOverview();
+                recentsButton.click();
+                break;
+        }
+    }
+
     protected String getSwipeHeightRequestName() {
         return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index eaa21ae..8ccfc05 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -55,7 +55,7 @@
             final int leftMargin = mLauncher.getTestInfo(
                     TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
                     getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-            mLauncher.scroll(overview, Direction.LEFT, 1, new Rect(leftMargin, 0, 0, 0), 20);
+            mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20);
             verifyActiveContainer();
         }
     }
@@ -89,7 +89,7 @@
             final int rightMargin = mLauncher.getTestInfo(
                     TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
                     getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-            mLauncher.scroll(overview, Direction.RIGHT, 1, new Rect(0, 0, rightMargin, 0), 20);
+            mLauncher.scroll(overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20);
             verifyActiveContainer();
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index cfc4374..e0fe933 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.tapl;
 
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 
 import androidx.annotation.NonNull;
 
@@ -58,4 +59,9 @@
             }
         }
     }
+
+    @Override
+    protected int getExpectedStateForQuickSwitch() {
+        return QUICK_SWITCH_STATE_ORDINAL;
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 383c3ce..0879404 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -37,7 +37,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -52,6 +51,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.Configurator;
@@ -60,20 +60,24 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.systemui.shared.system.QuickStepContract;
 
-import org.junit.Assert;
-
+import java.util.ArrayList;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
+import java.util.function.Function;
+
+import org.junit.Assert;
 
 /**
  * The main tapl object. The only object that can be explicitly constructed by the using code. It
@@ -84,6 +88,7 @@
     private static final String TAG = "Tapl";
     private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
     private static final int GESTURE_STEP_MS = 16;
+    private static long START_TIME = System.currentTimeMillis();
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -134,13 +139,22 @@
     private int mExpectedRotation = Surface.ROTATION_0;
     private final Uri mTestProviderUri;
     private final Deque<String> mDiagnosticContext = new LinkedList<>();
-    private Supplier<String> mSystemHealthSupplier;
+    private Function<Long, String> mSystemHealthSupplier;
 
     private Consumer<ContainerType> mOnSettledStateAction;
 
     /**
      * Constructs the root of TAPL hierarchy. You get all other objects from it.
      */
+    public LauncherInstrumentation() {
+        this(InstrumentationRegistry.getInstrumentation());
+    }
+
+    /**
+     * Constructs the root of TAPL hierarchy. You get all other objects from it.
+     * Deprecated: use the constructor without parameters instead.
+     */
+    @Deprecated
     public LauncherInstrumentation(Instrumentation instrumentation) {
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
@@ -238,10 +252,6 @@
         return null;
     }
 
-    public static boolean isAvd() {
-        return Build.MODEL.contains("Cuttlefish");
-    }
-
     static void log(String message) {
         Log.d(TAG, message);
     }
@@ -296,7 +306,7 @@
         return "Background";
     }
 
-    public void setSystemHealthSupplier(Supplier<String> supplier) {
+    public void setSystemHealthSupplier(Function<Long, String> supplier) {
         this.mSystemHealthSupplier = supplier;
     }
 
@@ -316,8 +326,8 @@
         }
 
         return mSystemHealthSupplier != null
-                ? mSystemHealthSupplier.get()
-                : TestHelpers.getSystemHealthMessage(getContext());
+                ? mSystemHealthSupplier.apply(START_TIME)
+                : TestHelpers.getSystemHealthMessage(getContext(), START_TIME);
     }
 
     private void fail(String message) {
@@ -486,8 +496,8 @@
         }
     }
 
-    private void waitForLauncherInitialized() {
-        for (int i = 0; i < 100; ++i) {
+    public void waitForLauncherInitialized() {
+        for (int i = 0; i < 600; ++i) {
             if (getTestInfo(
                     TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
                     getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
@@ -512,15 +522,6 @@
         }
     }
 
-    Bundle getAnswerFromLauncher(UiObject2 view, String requestTag) {
-        // Send a fake set-text request to Launcher to initiate a response with requested data.
-        final String responseTag = requestTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX;
-        return (Bundle) executeAndWaitForEvent(
-                () -> view.setText(requestTag),
-                event -> responseTag.equals(event.getClassName()),
-                "Launcher didn't respond to request: " + requestTag);
-    }
-
     /**
      * Presses nav bar home button.
      *
@@ -534,6 +535,9 @@
         // accessibility events prior to pressing Home.
         final String action;
         if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+            final String anomaly = getAnomalyMessage();
+            if (anomaly != null) fail("Can't swipe up to Home: " + anomaly);
+
             final Point displaySize = getRealDisplaySize();
 
             if (hasLauncherObject("deep_shortcuts_container")) {
@@ -541,13 +545,17 @@
                         displaySize.x / 2, displaySize.y - 1,
                         displaySize.x / 2, 0,
                         ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
-                assertTrue("Context menu is still visible afterswiping up to home",
-                        !hasLauncherObject("deep_shortcuts_container"));
+                try (LauncherInstrumentation.Closable c = addContextLayer(
+                        "Swiped up from context menu to home")) {
+                    waitUntilGone("deep_shortcuts_container");
+                }
             }
             if (hasLauncherObject(WORKSPACE_RES_ID)) {
                 log(action = "already at home");
             } else {
-                log(action = "swiping up to home");
+                log("Hierarchy before swiping up to home");
+                dumpViewHierarchy();
+                log(action = "swiping up to home from " + getVisibleStateMessage());
                 final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
                         ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
 
@@ -769,7 +777,36 @@
                 TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
     }
 
-    void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) {
+    int getBottomGestureSize() {
+        return ResourceUtils.getNavbarSize(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
+    }
+
+    int getBottomGestureMargin(UiObject2 container) {
+        return container.getVisibleBounds().bottom - getRealDisplaySize().y
+                + getBottomGestureSize();
+    }
+
+    void scrollToLastVisibleRow(UiObject2 container, Collection<UiObject2> items, int topPadding) {
+        final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
+                Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top));
+
+        final int gestureStart = lowestItem.getVisibleBounds().top + getTouchSlop();
+        final int distance = gestureStart - container.getVisibleBounds().top - topPadding;
+        final int bottomMargin = container.getVisibleBounds().height() - distance;
+
+        scroll(
+                container,
+                Direction.DOWN,
+                new Rect(
+                        0,
+                        0,
+                        0,
+                        Math.max(bottomMargin, getBottomGestureMargin(container))),
+                150);
+    }
+
+    void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
         final Rect rect = container.getVisibleBounds();
         if (margins != null) {
             rect.left += margins.left;
@@ -786,34 +823,26 @@
         switch (direction) {
             case UP: {
                 startX = endX = rect.centerX();
-                final int vertCenter = rect.centerY();
-                final float halfGestureHeight = rect.height() * percent / 2.0f;
-                startY = (int) (vertCenter - halfGestureHeight) + 1;
-                endY = (int) (vertCenter + halfGestureHeight);
+                startY = rect.top;
+                endY = rect.bottom - 1;
             }
             break;
             case DOWN: {
                 startX = endX = rect.centerX();
-                final int vertCenter = rect.centerY();
-                final float halfGestureHeight = rect.height() * percent / 2.0f;
-                startY = (int) (vertCenter + halfGestureHeight) - 1;
-                endY = (int) (vertCenter - halfGestureHeight);
+                startY = rect.bottom - 1;
+                endY = rect.top;
             }
             break;
             case LEFT: {
                 startY = endY = rect.centerY();
-                final int horizCenter = rect.centerX();
-                final float halfGestureWidth = rect.width() * percent / 2.0f;
-                startX = (int) (horizCenter - halfGestureWidth) + 1;
-                endX = (int) (horizCenter + halfGestureWidth);
+                startX = rect.left;
+                endX = rect.right - 1;
             }
             break;
             case RIGHT: {
                 startY = endY = rect.centerY();
-                final int horizCenter = rect.centerX();
-                final float halfGestureWidth = rect.width() * percent / 2.0f;
-                startX = (int) (horizCenter + halfGestureWidth) - 1;
-                endX = (int) (horizCenter - halfGestureWidth);
+                startX = rect.right - 1;
+                endX = rect.left;
             }
             break;
             default:
@@ -844,10 +873,6 @@
         mDevice.waitForIdle();
     }
 
-    float getDisplayDensity() {
-        return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
-    }
-
     int getTouchSlop() {
         return ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
@@ -969,4 +994,18 @@
     public void produceNativeLeak() {
         getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK);
     }
-}
\ No newline at end of file
+
+    public void produceViewLeak() {
+        getTestInfo(TestProtocol.REQUEST_VIEW_LEAK);
+    }
+
+    public ArrayList<ComponentName> getRecentTasks() {
+        ArrayList<ComponentName> tasks = new ArrayList<>();
+        ArrayList<String> components = getTestInfo(TestProtocol.REQUEST_RECENT_TASKS_LIST)
+                .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+        for (String s : components) {
+            tasks.add(ComponentName.unflattenFromString(s));
+        }
+        return tasks;
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index a089a52..e882171 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -77,13 +77,18 @@
         return launchers.get(0).activityInfo;
     }
 
-    public static String getOverviewPackageName() {
+    public static ComponentName getOverviewComponentName() {
         Resources res = Resources.getSystem();
         int id = res.getIdentifier("config_recentsComponentName", "string", "android");
         if (id != 0) {
-            return ComponentName.unflattenFromString(res.getString(id)).getPackageName();
+            return ComponentName.unflattenFromString(res.getString(id));
         }
-        return "com.android.systemui";
+        return new ComponentName("com.android.systemui",
+                "com.android.systemui.recents.RecentsActivity");
+    }
+
+    public static String getOverviewPackageName() {
+        return getOverviewComponentName().getPackageName();
     }
 
     private static String truncateCrash(String text, int maxLines) {
@@ -101,11 +106,11 @@
         return ret.toString();
     }
 
-    private static String checkCrash(Context context, String label) {
+    private static String checkCrash(Context context, String label, long startTime) {
         DropBoxManager dropbox = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
         Assert.assertNotNull("Unable access the DropBoxManager service", dropbox);
 
-        long timestamp = System.currentTimeMillis() - 5 * 60000;
+        long timestamp = startTime;
         DropBoxManager.Entry entry;
         StringBuilder errorDetails = new StringBuilder();
         while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
@@ -123,7 +128,7 @@
         return errorDetails.length() != 0 ? errorDetails.toString() : null;
     }
 
-    public static String getSystemHealthMessage(Context context) {
+    public static String getSystemHealthMessage(Context context, long startTime) {
         try {
             StringBuilder errors = new StringBuilder();
 
@@ -131,7 +136,6 @@
                     "system_app_anr",
                     "system_app_crash",
                     "system_app_native_crash",
-                    "system_app_wtf",
                     "system_server_anr",
                     "system_server_crash",
                     "system_server_native_crash",
@@ -139,7 +143,7 @@
             };
 
             for (String label : labels) {
-                final String crash = checkCrash(context, label);
+                final String crash = checkCrash(context, label, startTime);
                 if (crash != null) errors.append(crash);
             }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 7d308af..5fcaa55 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.tapl;
 
-import static org.junit.Assert.fail;
-
 import android.graphics.Point;
 import android.graphics.Rect;
 
@@ -26,13 +24,12 @@
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.ResourceUtils;
+import java.util.Collection;
 
 /**
  * All widgets container.
  */
 public final class Widgets extends LauncherInstrumentation.VisibleContainer {
-    private static final Rect MARGINS = new Rect(100, 100, 100, 100);
     private static final int FLING_STEPS = 10;
 
     Widgets(LauncherInstrumentation launcher) {
@@ -48,7 +45,11 @@
                 "want to fling forward in widgets")) {
             LauncherInstrumentation.log("Widgets.flingForward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
-            mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS);
+            mLauncher.scroll(
+                    widgetsContainer,
+                    Direction.DOWN,
+                    new Rect(0, 0, 0, mLauncher.getBottomGestureMargin(widgetsContainer) + 1),
+                    FLING_STEPS);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
                 verifyActiveContainer();
             }
@@ -64,7 +65,11 @@
                 "want to fling backwards in widgets")) {
             LauncherInstrumentation.log("Widgets.flingBackward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
-            mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS);
+            mLauncher.scroll(
+                    widgetsContainer,
+                    Direction.UP,
+                    new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0),
+                    FLING_STEPS);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();
             }
@@ -78,32 +83,33 @@
     }
 
     public Widget getWidget(String labelText) {
-        final int margin = ResourceUtils.getNavbarSize(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
         final UiObject2 widgetsContainer = verifyActiveContainer();
-        widgetsContainer.setGestureMargins(0, 0, 0, margin);
-
         final Point displaySize = mLauncher.getRealDisplaySize();
+        final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
 
         int i = 0;
-        final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
-
         for (; ; ) {
-            final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
-            if (label != null) {
+            final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
+                    widgetsContainer, "widgets_cell_list_container");
+            mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
+            for (UiObject2 cell : cells) {
+                final UiObject2 label = cell.findObject(labelSelector);
+                if (label == null) continue;
+
                 final UiObject2 widget = label.getParent().getParent();
                 mLauncher.assertEquals(
                         "View is not WidgetCell",
                         "com.android.launcher3.widget.WidgetCell",
                         widget.getClassName());
 
-                if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+                if (widget.getVisibleBounds().bottom
+                        <= displaySize.y - mLauncher.getBottomGestureSize()) {
                     return new Widget(mLauncher, widget);
                 }
             }
 
-            if (++i > 40) fail("Too many attempts");
-            mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
+            mLauncher.assertTrue("Too many attempts", ++i <= 40);
+            mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
         }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 510ea14..d1261e0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -38,8 +38,6 @@
  * Operations on the workspace screen.
  */
 public final class Workspace extends Home {
-    private static final float FLING_SPEED =
-            LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
     private static final int DRAG_DURACTION = 2000;
     private static final int FLING_STEPS = 10;
     private final UiObject2 mHotseat;
@@ -142,7 +140,7 @@
     }
 
     private boolean isWorkspaceScrollable(UiObject2 workspace) {
-        return workspace.isScrollable();
+        return workspace.getChildCount() > 1;
     }
 
     @NonNull
@@ -182,8 +180,8 @@
      */
     public void flingForward() {
         final UiObject2 workspace = verifyActiveContainer();
-        mLauncher.scroll(workspace, Direction.RIGHT, 1f,
-                new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
+        mLauncher.scroll(workspace, Direction.RIGHT,
+                new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
                 FLING_STEPS);
         verifyActiveContainer();
     }
@@ -194,8 +192,8 @@
      */
     public void flingBackward() {
         final UiObject2 workspace = verifyActiveContainer();
-        mLauncher.scroll(workspace, Direction.LEFT, 1f,
-                new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
+        mLauncher.scroll(workspace, Direction.LEFT,
+                new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
                 FLING_STEPS);
         verifyActiveContainer();
     }