[automerger skipped] Merge "DO NOT MERGE - Merge Android 10 into master" am: 636e58424b -s ours am: 2fe921d058
am: 957b8a1d35

Change-Id: Ib035785fc518dc3d10bc584f512762b0a8193f7b
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 5318a12..8db875b 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,6 +43,7 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
 
 
     <!--
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
index 1ccd7d7..66aec40 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
@@ -64,7 +64,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.WORKSPACE;
     }
 }
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 1e44910..ee113df 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -25,6 +25,7 @@
 import android.os.UserHandle;
 
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.notification.NotificationKeyData;
 
 import java.util.Collections;
 import java.util.List;
@@ -48,10 +49,6 @@
     private DeepShortcutManager(Context context) {
     }
 
-    public static boolean supportsShortcuts(ItemInfo info) {
-        return false;
-    }
-
     public boolean wasLastCallSuccess() {
         return false;
     }
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index d84633d..36d1c3e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -36,12 +36,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -57,8 +61,6 @@
 import java.util.Set;
 import java.util.function.Supplier;
 
-import androidx.annotation.NonNull;
-
 public abstract class BaseIconCache {
 
     private static final String TAG = "BaseIconCache";
@@ -84,6 +86,7 @@
 
     protected int mIconDpi;
     protected IconDB mIconDb;
+    protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
     protected String mSystemState = "";
 
     private final String mDbFileName;
@@ -227,12 +230,12 @@
 
     /**
      * Refreshes the system state definition used to check the validity of the cache. It
-     * incorporates all the properties that can affect the cache like locale and system-version.
+     * incorporates all the properties that can affect the cache like the list of enabled locale
+     * and system-version.
      */
     private void updateSystemState() {
-        final String locale =
-                mContext.getResources().getConfiguration().getLocales().toLanguageTags();
-        mSystemState = locale + "," + Build.VERSION.SDK_INT;
+        mLocaleList = mContext.getResources().getConfiguration().getLocales();
+        mSystemState = mLocaleList.toLanguageTags() + "," + Build.VERSION.SDK_INT;
     }
 
     protected String getIconSystemState(String packageName) {
@@ -269,7 +272,7 @@
         mCache.put(key, entry);
 
         ContentValues values = newContentValues(entry, entry.title.toString(),
-                componentName.getPackageName());
+                componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
         addIconToDB(values, componentName, info, userSerial);
     }
 
@@ -445,7 +448,7 @@
                     // Add the icon in the DB here, since these do not get written during
                     // package updates.
                     ContentValues values = newContentValues(
-                            iconInfo, entry.title.toString(), packageName);
+                            iconInfo, entry.title.toString(), packageName, null);
                     addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
 
                 } catch (NameNotFoundException e) {
@@ -504,23 +507,35 @@
         return false;
     }
 
-    static final class IconDB extends SQLiteCacheHelper {
-        private final static int RELEASE_VERSION = 26;
+    /**
+     * Returns a cursor for an arbitrary query to the cache db
+     */
+    public synchronized Cursor queryCacheDb(String[] columns, String selection,
+            String[] selectionArgs) {
+        return mIconDb.query(columns, selection, selectionArgs);
+    }
 
-        public final static String TABLE_NAME = "icons";
-        public final static String COLUMN_ROWID = "rowid";
-        public final static String COLUMN_COMPONENT = "componentName";
-        public final static String COLUMN_USER = "profileId";
-        public final static String COLUMN_LAST_UPDATED = "lastUpdated";
-        public final static String COLUMN_VERSION = "version";
-        public final static String COLUMN_ICON = "icon";
-        public final static String COLUMN_ICON_COLOR = "icon_color";
-        public final static String COLUMN_LABEL = "label";
-        public final static String COLUMN_SYSTEM_STATE = "system_state";
+    /**
+     * Cache class to store the actual entries on disk
+     */
+    public static final class IconDB extends SQLiteCacheHelper {
+        private static final int RELEASE_VERSION = 27;
 
-        public final static String[] COLUMNS_HIGH_RES = new String[] {
+        public static final String TABLE_NAME = "icons";
+        public static final String COLUMN_ROWID = "rowid";
+        public static final String COLUMN_COMPONENT = "componentName";
+        public static final String COLUMN_USER = "profileId";
+        public static final String COLUMN_LAST_UPDATED = "lastUpdated";
+        public static final String COLUMN_VERSION = "version";
+        public static final String COLUMN_ICON = "icon";
+        public static final String COLUMN_ICON_COLOR = "icon_color";
+        public static final String COLUMN_LABEL = "label";
+        public static final String COLUMN_SYSTEM_STATE = "system_state";
+        public static final String COLUMN_KEYWORDS = "keywords";
+
+        public static final String[] COLUMNS_HIGH_RES = new String[] {
                 IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
-        public final static String[] COLUMNS_LOW_RES = new String[] {
+        public static final String[] COLUMNS_LOW_RES = new String[] {
                 IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
 
         public IconDB(Context context, String dbFileName, int iconPixelSize) {
@@ -529,21 +544,23 @@
 
         @Override
         protected void onCreateTable(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
-                    COLUMN_COMPONENT + " TEXT NOT NULL, " +
-                    COLUMN_USER + " INTEGER NOT NULL, " +
-                    COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_ICON + " BLOB, " +
-                    COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_LABEL + " TEXT, " +
-                    COLUMN_SYSTEM_STATE + " TEXT, " +
-                    "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
-                    ");");
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+                    + COLUMN_COMPONENT + " TEXT NOT NULL, "
+                    + COLUMN_USER + " INTEGER NOT NULL, "
+                    + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_ICON + " BLOB, "
+                    + COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_LABEL + " TEXT, "
+                    + COLUMN_SYSTEM_STATE + " TEXT, "
+                    + COLUMN_KEYWORDS + " TEXT, "
+                    + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") "
+                    + ");");
         }
     }
 
-    private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName) {
+    private ContentValues newContentValues(BitmapInfo bitmapInfo, String label,
+            String packageName, @Nullable String keywords) {
         ContentValues values = new ContentValues();
         values.put(IconDB.COLUMN_ICON,
                 bitmapInfo.isLowRes() ? null : GraphicsUtils.flattenBitmap(bitmapInfo.icon));
@@ -551,7 +568,7 @@
 
         values.put(IconDB.COLUMN_LABEL, label);
         values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName));
-
+        values.put(IconDB.COLUMN_KEYWORDS, keywords);
         return values;
     }
 
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index addb51f..09f59b8 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -17,8 +17,11 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.LocaleList;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BitmapInfo;
 
 public interface CachingLogic<T> {
@@ -30,4 +33,12 @@
     CharSequence getLabel(T object);
 
     void loadIcon(Context context, T object, BitmapInfo target);
+
+    /**
+     * Provides a option list of keywords to associate with this object
+     */
+    @Nullable
+    default String getKeywords(T object, LocaleList localeList) {
+        return null;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index 8f1282d..24fc61b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -95,6 +95,10 @@
     private AppPredictor createPredictor(Client client, int count) {
         AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
 
+        if (apm == null) {
+          return null;
+        }
+
         AppPredictor predictor = apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(mContext)
                         .setUiSurface(client.id)
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
index 73f328b..9091168 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.WORKSPACE;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 18b8af4..eb571f6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -154,7 +154,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.NAVBAR;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 5e77e0a..5ebefa3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -81,6 +81,7 @@
                 });
         factory.onRemoteAnimationReceived(null);
         factory.createActivityController(RECENTS_LAUNCH_DURATION);
+        factory.setRecentsAttachedToAppWindow(true, false);
         mActivity = activity;
         mRecentsView = mActivity.getOverviewPanel();
         return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 6533c63..a94f25d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -99,6 +99,7 @@
 
         @Override
         protected void onTransitionComplete() {
+            // TODO(b/138729100) This doesn't execute first time launcher is run
             if (mTriggeredFromAltTab) {
                 RecentsView rv = (RecentsView) mHelper.getVisibleRecentsView();
                 if (rv == null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index b90f6c2..17457aa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
 import android.graphics.Matrix;
 import android.view.View;
 
@@ -47,8 +49,7 @@
     };
 
     public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
-                    c, R.string.task_overlay_factory_class));
+            forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
 
     public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
         final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
index 83601e6..14083dd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RecentsAnimationListener;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -39,7 +40,7 @@
 public class RecentsAnimationListenerSet implements RecentsAnimationListener {
 
     // The actual app surface is replaced by a screenshot upon recents animation cancelation when
-    // deferredWithScreenshot is true. Launcher takes the responsibility to clean up this screenshot
+    // the thumbnailData exists. Launcher takes the responsibility to clean up this screenshot
     // after app transition is finished. This delay is introduced to cover the app transition
     // period of time.
     private final int TRANSITION_DELAY = 100;
@@ -90,14 +91,14 @@
     }
 
     @Override
-    public final void onAnimationCanceled(boolean deferredWithScreenshot) {
+    public final void onAnimationCanceled(ThumbnailData thumbnailData) {
         Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
             for (SwipeAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationCanceled();
             }
         });
         // TODO: handle the transition better instead of simply using a transition delay.
-        if (deferredWithScreenshot) {
+        if (thumbnailData != null) {
             MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
                     TRANSITION_DELAY);
         }
@@ -109,6 +110,6 @@
 
     public void cancelListener() {
         mCancelled = true;
-        onAnimationCanceled(false);
+        onAnimationCanceled(null);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 9b157d1..1bf77f5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -97,6 +97,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.Themes;
@@ -1051,9 +1052,10 @@
         if (task != null) {
             ActivityManagerWrapper.getInstance().removeTask(task.key.id);
             if (shouldLog) {
+                ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
                 mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                        onEndListener.logAction, Direction.UP, index,
-                        TaskUtils.getLaunchComponentKeyForTask(task.key));
+                        onEndListener.logAction, Direction.UP, index, componentKey);
+                mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
new file mode 100644
index 0000000..e425088
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -0,0 +1,32 @@
+/*
+ * 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.uioverrides;
+
+import android.provider.DeviceConfig;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+public class TogglableFlag extends BaseTogglableFlag {
+
+    public TogglableFlag(String key, boolean defaultValue, String description) {
+        super(key, defaultValue, description);
+    }
+
+    @Override
+    public boolean getInitialValue(boolean value) {
+        return DeviceConfig.getBoolean("launcher", getKey(), value);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 97cd38a..c02df93 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -32,9 +32,11 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.Activity;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.util.Base64;
@@ -244,4 +246,9 @@
         }
         return new ScaleAndTranslation(1.1f, 0f, 0f);
     }
+
+    public static Person[] getPersons(ShortcutInfo si) {
+        Person[] persons = si.getPersons();
+        return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 0605953..bb72315 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.NAVBAR;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index a55f36b..b81edfa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -147,8 +147,8 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
-        return ContainerType.HOTSEAT;
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
+        return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
     }
 
     private AnimatorSetBuilder getNormalToOverviewAnimation() {
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 9f12484..dfab434 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,15 +16,12 @@
 package com.android.quickstep;
 
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
@@ -35,7 +32,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
@@ -52,7 +48,7 @@
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
-            new MainThreadInitializedObject<>(c -> new RecentsModel(c));
+            new MainThreadInitializedObject<>(RecentsModel::new);
 
     private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2f411ef..13a8dc2 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -25,6 +25,7 @@
 import static android.stats.launcher.nano.Launcher.HOME;
 import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
 import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
+import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
 import static android.stats.launcher.nano.Launcher.BACKGROUND;
 import static android.stats.launcher.nano.Launcher.OVERVIEW;
 
@@ -74,6 +75,16 @@
                 MessageNano.toByteArray(ext), true);
     }
 
+    @Override
+    public void logTaskDismiss(View v, ComponentKey componentKey) {
+        LauncherExtension ext = new LauncherExtension();
+        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
+        int srcState = OVERVIEW;
+        fillInLauncherExtension(v, ext);
+        StatsLogCompat.write(DISMISS_TASK, srcState, BACKGROUND /* dstState */,
+                MessageNano.toByteArray(ext), true);
+    }
+
     public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
         StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
         if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
diff --git a/quickstep/tests/OWNERS b/quickstep/tests/OWNERS
index 046d871..02e8ebc 100644
--- a/quickstep/tests/OWNERS
+++ b/quickstep/tests/OWNERS
@@ -1 +1,4 @@
 vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/res/values/config.xml b/res/values/config.xml
index 638a411..0387184 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -72,6 +72,7 @@
     <string name="system_shortcut_factory_class" translatable="false"></string>
     <string name="app_launch_tracker_class" translatable="false"></string>
     <string name="test_information_handler_class" translatable="false"></string>
+    <string name="launcher_activity_logic_class" translatable="false"></string>
 
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
index 92bcc64..a3d1216 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
@@ -1,6 +1,8 @@
 package com.android.launcher3.config;
 
 
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -70,7 +72,7 @@
         };
     }
 
-    private void override(BaseFlags.TogglableFlag flag, boolean newValue) {
+    private void override(BaseTogglableFlag flag, boolean newValue) {
         if (!ruleInProgress) {
             throw new IllegalStateException(
                     "Rule isn't in progress. Did you remember to mark it with @Rule?");
@@ -93,7 +95,7 @@
 
     private void applyAnnotation(FlagOverride flagOverride) {
         boolean found = false;
-        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             if (flag.getKey().equals(flagOverride.key())) {
                 override(flag, flagOverride.value());
                 found = true;
@@ -109,7 +111,7 @@
      * Resets all flags to their default values.
      */
     private void clearOverrides() {
-        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             flag.setForTests(flag.getDefaultValue());
         }
     }
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index e1ef954..0f006f7 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,16 +1,17 @@
 package com.android.launcher3;
 
-import android.content.Context;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
 
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 public class IconProvider implements ResourceBasedOverride {
 
-    public static IconProvider newInstance(Context context) {
-        return Overrides.getObject(IconProvider.class, context, R.string.icon_provider_class);
-    }
+    public static MainThreadInitializedObject<IconProvider> INSTANCE =
+            forOverride(IconProvider.class, R.string.icon_provider_class);
 
     public IconProvider() { }
 
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 134e116..3f723d1 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -22,6 +22,8 @@
 import android.os.Process;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.util.ContentWriter;
 
 /**
@@ -134,6 +136,7 @@
         return null;
     }
 
+    @Nullable
     public ComponentName getTargetComponent() {
         Intent intent = getIntent();
         if (intent != null) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5b38261..d79230f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -127,6 +127,7 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.RaceConditionTracker;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
@@ -873,9 +874,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
-
-        getUserEventDispatcher().logActionCommand(Action.Command.STOP,
-                mStateManager.getState().containerType, -1);
+        logStopAndResume(Action.Command.STOP);
 
         mAppWidgetHost.setListenIfResumed(false);
 
@@ -901,8 +900,7 @@
 
     private void handleDeferredResume() {
         if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
-            getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
-                    mStateManager.getState().containerType, -1);
+            logStopAndResume(Action.Command.RESUME);
             getUserEventDispatcher().startSession();
 
             UiFactory.onLauncherStateOrResumeChanged(this);
@@ -929,6 +927,17 @@
         }
     }
 
+    private void logStopAndResume(int command) {
+        int containerType = mStateManager.getState().containerType;
+        if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
+            getUserEventDispatcher().logActionCommand(command,
+                containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
+        } else {
+            getUserEventDispatcher().logActionCommand(command, containerType, -1);
+        }
+
+    }
+
     protected void onStateSet(LauncherState state) {
         getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
         if (mDeferredResumePending) {
@@ -2494,7 +2503,7 @@
                         KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
             }
             if (currentFocus.getTag() instanceof ItemInfo
-                    && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
+                    && ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
                 shortcutInfos.add(new KeyboardShortcutInfo(
                         getString(R.string.shortcuts_menu_with_notifications_description),
                         KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d07638a..2a801d6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -19,14 +19,12 @@
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
 import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
 
-import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
-import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -46,7 +44,7 @@
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
-            new MainThreadInitializedObject<>((c) -> new LauncherAppState(c));
+            new MainThreadInitializedObject<>(LauncherAppState::new);
 
     private final Context mContext;
     private final LauncherModel mModel;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index d2b8d4e..bbb3915 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1562,12 +1562,20 @@
         final boolean pagesFlipped = isPageOrderFlipped();
         info.setScrollable(getPageCount() > 1);
         if (getCurrentPage() < getPageCount() - 1) {
-            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
-                    : AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            info.addAction(pagesFlipped ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+            info.addAction(mIsRtl ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
         }
         if (getCurrentPage() > 0) {
-            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
-                    : AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            info.addAction(pagesFlipped ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+            info.addAction(mIsRtl ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
         }
 
         // Accessibility-wise, PagedView doesn't support long click, so disabling it.
@@ -1607,8 +1615,21 @@
                 if (pagesFlipped ? scrollRight() : scrollLeft()) {
                     return true;
                 }
+            } break;
+            case android.R.id.accessibilityActionPageRight: {
+                if (!mIsRtl) {
+                  return scrollRight();
+                } else {
+                  return scrollLeft();
+                }
             }
-            break;
+            case android.R.id.accessibilityActionPageLeft: {
+                if (!mIsRtl) {
+                  return scrollLeft();
+                } else {
+                  return scrollRight();
+                }
+            }
         }
         return false;
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index fc5cd8a..3bef598 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
+import android.app.Person;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -109,6 +110,9 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
+    public static final String[] EMPTY_STRING_ARRAY = new String[0];
+    public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
+
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
     public static final boolean ATLEAST_P =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 269a591..3be91d4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -85,6 +85,7 @@
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.WorkspaceTouchListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -420,6 +421,9 @@
         }
 
         // Always enter the spring loaded mode
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Switching to SPRING_LOADED");
+        }
         mLauncher.getStateManager().goToState(SPRING_LOADED);
     }
 
@@ -1741,6 +1745,9 @@
     public void prepareAccessibilityDrop() { }
 
     public void onDrop(final DragObject d, DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDrop");
+        }
         mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
         CellLayout dropTargetLayout = mDropToLayout;
 
@@ -2418,6 +2425,9 @@
      * to add an item to one of the workspace screens.
      */
     private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDropExternal");
+        }
         if (d.dragInfo instanceof PendingAddShortcutInfo) {
             WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
                     .activityInfo.createWorkspaceItemInfo();
@@ -3251,6 +3261,10 @@
         }
     }
 
+    public boolean isOverlayShown() {
+        return mOverlayShown;
+    }
+
     void moveToDefaultScreen() {
         int page = DEFAULT_PAGE;
         if (!workspaceInModalState() && getNextPage() != page) {
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 5a2373b..b72866c 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -16,17 +16,23 @@
 
 package com.android.launcher3;
 
+import android.app.Person;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.ContentWriter;
 
+import java.util.Arrays;
+
 /**
  * Represents a launchable icon on the workspaces and in folders.
  */
@@ -83,10 +89,17 @@
     public int status;
 
     /**
+     * A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
+     * represents a deep shortcut.
+     */
+    @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
+
+    /**
      * The installation progress [0-100] of the package that this shortcut represents.
      */
     private int mInstallProgress;
 
+
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
@@ -98,6 +111,7 @@
         iconResource = info.iconResource;
         status = info.status;
         mInstallProgress = info.mInstallProgress;
+        personKeys = info.personKeys.clone();
     }
 
     /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
@@ -175,6 +189,10 @@
             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
         }
         disabledMessage = shortcutInfo.getDisabledMessage();
+
+        Person[] persons = UiFactory.getPersons(shortcutInfo);
+        personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
+            : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
     }
 
     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
@@ -183,6 +201,11 @@
                 getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
     }
 
+    @NonNull
+    public String[] getPersonKeys() {
+        return personKeys;
+    }
+
     @Override
     public ComponentName getTargetComponent() {
         ComponentName cn = super.getTargetComponent();
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index fd4df52..0c1303b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
@@ -115,7 +116,7 @@
 
         // If the request came from keyboard, do not add custom shortcuts as that is already
         // exposed as a direct shortcut
-        if (!fromKeyboard && DeepShortcutManager.supportsShortcuts(item)) {
+        if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
             info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
                     ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
         }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 54efcb7..23edcd2 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -18,20 +18,16 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.Keep;
-import androidx.annotation.VisibleForTesting;
 
+import androidx.annotation.VisibleForTesting;
 import com.android.launcher3.Utilities;
 
+import com.android.launcher3.uioverrides.TogglableFlag;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.SortedMap;
@@ -41,11 +37,9 @@
  * Defines a set of flags used to control various launcher behaviors.
  *
  * <p>All the flags should be defined here with appropriate default values.
- *
- * <p>This class is kept package-private to prevent direct access.
  */
 @Keep
-abstract class BaseFlags {
+public abstract class BaseFlags {
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
@@ -112,11 +106,15 @@
             "FAKE_LANDSCAPE_UI", false,
             "Rotate launcher UI instead of using transposed layout");
 
+    public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
+            "APP_SEARCH_IMPROVEMENTS", false,
+            "Adds localized title and keyword search and ranking");
+
     public static void initialize(Context context) {
         // Avoid the disk read for user builds
         if (Utilities.IS_DEBUG_DEVICE) {
             synchronized (sLock) {
-                for (TogglableFlag flag : sFlags) {
+                for (BaseTogglableFlag flag : sFlags) {
                     flag.initialize(context);
                 }
             }
@@ -132,27 +130,27 @@
         SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
         synchronized (sLock) {
             for (TogglableFlag flag : sFlags) {
-                flagsByKey.put(flag.key, flag);
+                flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag);
             }
         }
         return new ArrayList<>(flagsByKey.values());
     }
 
-    public static class TogglableFlag {
+    public static abstract class BaseTogglableFlag {
         private final String key;
         private final boolean defaultValue;
         private final String description;
         private boolean currentValue;
 
-        TogglableFlag(
+        public BaseTogglableFlag(
                 String key,
                 boolean defaultValue,
                 String description) {
             this.key = checkNotNull(key);
-            this.currentValue = this.defaultValue = defaultValue;
+            this.currentValue = this.defaultValue = getInitialValue(defaultValue);
             this.description = checkNotNull(description);
             synchronized (sLock) {
-                sFlags.add(this);
+                sFlags.add((TogglableFlag)this);
             }
         }
 
@@ -162,14 +160,16 @@
             currentValue = value;
         }
 
-        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
         public String getKey() {
             return key;
         }
+
         void initialize(Context context) {
             currentValue = getFromStorage(context, defaultValue);
         }
 
+        protected abstract boolean getInitialValue(boolean value);
+
         public void updateStorage(Context context, boolean value) {
             SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
                     Context.MODE_PRIVATE).edit();
@@ -213,7 +213,7 @@
                 return true;
             }
             if (o instanceof TogglableFlag) {
-                TogglableFlag that = (TogglableFlag) o;
+                BaseTogglableFlag that = (BaseTogglableFlag) o;
                 return (this.key.equals(that.getKey()))
                         && (this.defaultValue == that.getDefaultValue())
                         && (this.description.equals(that.getDescription()));
@@ -233,48 +233,4 @@
             return h$;
         }
     }
-
-    /**
-     * Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
-     * This is useful if we want to be able to control this flag from another process.
-     */
-    public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
-        private ContentResolver contentResolver;
-
-        ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
-            super(key, defaultValue, description);
-        }
-
-        @Override
-        public void initialize(Context context) {
-            contentResolver = context.getContentResolver();
-            contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
-                    new ContentObserver(new Handler(Looper.getMainLooper())) {
-                        @Override
-                        public void onChange(boolean selfChange) {
-                            superInitialize(context);
-                    }});
-            superInitialize(context);
-        }
-
-        private void superInitialize(Context context) {
-            super.initialize(context);
-        }
-
-        @Override
-        public void updateStorage(Context context, boolean value) {
-            if (contentResolver == null) {
-                return;
-            }
-            Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
-        }
-
-        @Override
-        boolean getFromStorage(Context context, boolean defaultValue) {
-            if (contentResolver == null) {
-                return defaultValue;
-            }
-            return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 5ecb186..54e5322 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -26,12 +26,13 @@
 import android.widget.Toast;
 
 import com.android.launcher3.R;
-import com.android.launcher3.config.BaseFlags.TogglableFlag;
 
 import androidx.preference.PreferenceDataStore;
 import androidx.preference.PreferenceFragment;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.SwitchPreference;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
 
 /**
  * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
@@ -62,7 +63,7 @@
 
         @Override
         public boolean getBoolean(String key, boolean defaultValue) {
-            for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
                 if (flag.getKey().equals(key)) {
                     return flag.getFromStorage(mContext, defaultValue);
                 }
@@ -83,7 +84,7 @@
         // flag with a different value than the default. That way, when we flip flags in
         // future, engineers will pick up the new value immediately. To accomplish this, we use a
         // custom preference data store.
-        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             SwitchPreference switchPreference = new SwitchPreference(mContext);
             switchPreference.setKey(flag.getKey());
             switchPreference.setDefaultValue(flag.getDefaultValue());
@@ -99,7 +100,7 @@
     /**
      * Updates the summary to show the description and whether the flag overrides the default value.
      */
-    private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
+    private void updateSummary(SwitchPreference switchPreference, BaseTogglableFlag flag) {
         String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
         String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
         switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
@@ -134,7 +135,7 @@
         }
     }
 
-    private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
+    private boolean getFlagStateFromSharedPrefs(BaseTogglableFlag flag) {
         return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d32dd2e..b72fd98 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -579,6 +579,9 @@
     }
 
     private void drop(DropTarget dropTarget, Runnable flingAnimation) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragController.drop");
+        }
         final int[] coordinates = mCoordinatesTemp;
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 84fc94d..bd2a03b 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -17,10 +17,12 @@
 package com.android.launcher3.dragndrop;
 
 import android.content.Context;
+import android.util.Log;
 import android.view.DragEvent;
 import android.view.MotionEvent;
 
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Base class for driving a drag/drop operation.
@@ -52,6 +54,9 @@
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_UP:
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_UP");
+                }
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
                 mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
                 break;
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index c9566cb..288749f 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.graphics;
 
 import static com.android.launcher3.graphics.IconShape.getShapePath;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -31,6 +32,8 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.R;
@@ -38,16 +41,13 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import androidx.annotation.UiThread;
-
 /**
  * Factory for creating new drawables.
  */
 public class DrawableFactory implements ResourceBasedOverride {
 
     public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
-                        c.getApplicationContext(), R.string.drawable_factory_class));
+            forOverride(DrawableFactory.class, R.string.drawable_factory_class);
 
     protected final UserHandle mMyUser = Process.myUserHandle();
     protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 648445e..55d58b9 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -29,6 +29,8 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconProvider;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -36,8 +38,8 @@
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -50,8 +52,6 @@
 
 import java.util.function.Supplier;
 
-import androidx.annotation.NonNull;
-
 /**
  * Cache of application icons.  Icons can be made from any thread.
  */
@@ -75,11 +75,11 @@
         super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
                 inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
         mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
-        mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this);
+        mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mUserManager = UserManagerCompat.getInstance(mContext);
         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconProvider = IconProvider.newInstance(context);
+        mIconProvider = IconProvider.INSTANCE.get(context);
     }
 
     @Override
diff --git a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
similarity index 67%
rename from src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
rename to src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 7c99633..f9a94da 100644
--- a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,14 +20,23 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
+import com.android.launcher3.IconProvider;
+import com.android.launcher3.R;
 import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.util.ResourceBasedOverride;
 
-public class LauncherActivtiyCachingLogic implements CachingLogic<LauncherActivityInfo> {
+/**
+ * Caching logic for LauncherActivityInfo.
+ */
+public class LauncherActivityCachingLogic
+        implements CachingLogic<LauncherActivityInfo>, ResourceBasedOverride {
 
-    private final IconCache mCache;
-
-    public LauncherActivtiyCachingLogic(IconCache cache) {
-        mCache = cache;
+    /**
+     * Creates and returns a new instance
+     */
+    public static LauncherActivityCachingLogic newInstance(Context context) {
+        return Overrides.getObject(LauncherActivityCachingLogic.class, context,
+                R.string.launcher_activity_logic_class);
     }
 
     @Override
@@ -49,8 +58,10 @@
     public void loadIcon(Context context, LauncherActivityInfo object,
             BitmapInfo target) {
         LauncherIcons li = LauncherIcons.obtain(context);
-        li.createBadgedIconBitmap(mCache.getFullResIcon(object),
+        li.createBadgedIconBitmap(
+                IconProvider.INSTANCE.get(context)
+                        .getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
                 object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
         li.recycle();
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 9b9543e..d11d434 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -40,5 +40,6 @@
 
     public void logAppLaunch(View v, Intent intent) { }
     public void logTaskLaunch(View v, ComponentKey key) { }
+    public void logTaskDismiss(View v, ComponentKey key) { }
     public void verify() {}     // TODO: should move into robo tests
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index d81020e..c72b07a 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -187,6 +187,14 @@
                 dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
     }
 
+    public void logActionCommand(int command, int srcContainerType, int dstContainerType,
+                                 int pageIndex) {
+        Target srcTarget = newContainerTarget(srcContainerType);
+        srcTarget.pageIndex = pageIndex;
+        logActionCommand(command, srcTarget,
+                dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+    }
+
     public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
         LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
         if (command == Action.Command.STOP) {
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 1613d47..29a46cf 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -15,18 +15,18 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 
 import android.content.ComponentName;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import androidx.annotation.Nullable;
-
 /**
  * Callback for receiving various app launch events
  */
@@ -43,8 +43,7 @@
 
 
     public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
-            new MainThreadInitializedObject<>(c ->
-                    getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
+            forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
 
     public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
             @Nullable String container) { }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 0138572..4b01b5e 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -49,8 +49,8 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
@@ -61,7 +61,7 @@
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
+import com.android.launcher3.icons.LauncherActivityCachingLogic;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
@@ -196,7 +196,7 @@
             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
             setIgnorePackages(updateHandler);
             updateHandler.updateIcons(allActivityList,
-                    new LauncherActivtiyCachingLogic(mApp.getIconCache()),
+                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                     mApp.getModel()::onPackageIconsUpdated);
 
             // Take a break
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index 5050457..a1917ec 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -17,13 +17,17 @@
 package com.android.launcher3.notification;
 
 import android.app.Notification;
+import android.app.Person;
 import android.service.notification.StatusBarNotification;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-
 /**
  * The key data associated with the notification, used to determine what to include
  * in dots and dummy popup views before they are populated.
@@ -33,20 +37,27 @@
 public class NotificationKeyData {
     public final String notificationKey;
     public final String shortcutId;
+    @NonNull
+    public final String[] personKeysFromNotification;
     public int count;
 
-    private NotificationKeyData(String notificationKey, String shortcutId, int count) {
+    private NotificationKeyData(String notificationKey, String shortcutId, int count,
+            String[] personKeysFromNotification) {
         this.notificationKey = notificationKey;
         this.shortcutId = shortcutId;
         this.count = Math.max(1, count);
+        this.personKeysFromNotification = personKeysFromNotification;
     }
 
     public static NotificationKeyData fromNotification(StatusBarNotification sbn) {
         Notification notif = sbn.getNotification();
-        return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number);
+        return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number,
+                extractPersonKeyOnly(notif.extras.getParcelableArrayList(
+                        Notification.EXTRA_PEOPLE_LIST)));
     }
 
-    public static List<String> extractKeysOnly(@NonNull List<NotificationKeyData> notificationKeys) {
+    public static List<String> extractKeysOnly(
+            @NonNull List<NotificationKeyData> notificationKeys) {
         List<String> keysOnly = new ArrayList<>(notificationKeys.size());
         for (NotificationKeyData notificationKeyData : notificationKeys) {
             keysOnly.add(notificationKeyData.notificationKey);
@@ -54,6 +65,14 @@
         return keysOnly;
     }
 
+    private static String[] extractPersonKeyOnly(@Nullable ArrayList<Person> people) {
+        if (people == null || people.isEmpty()) {
+            return Utilities.EMPTY_STRING_ARRAY;
+        }
+        return people.stream().filter(person -> person.getKey() != null)
+                .map(Person::getKey).sorted().toArray(String[]::new);
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof NotificationKeyData)) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 25d9f79..baaad65 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -72,6 +72,7 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
@@ -201,7 +202,7 @@
             return null;
         }
         ItemInfo itemInfo = (ItemInfo) icon.getTag();
-        if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
+        if (!ShortcutUtil.supportsShortcuts(itemInfo)) {
             return null;
         }
 
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 2d301ac..4612b2a 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -29,17 +29,22 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * Provides data for the popup menu that appears after long-clicking on apps.
@@ -129,7 +134,8 @@
         for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
             DotInfo prevDot = updatedDots.get(packageUserKey);
             DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
-            if (prevDot == null) {
+            if (prevDot == null
+                    || prevDot.getNotificationCount() != newDot.getNotificationCount()) {
                 updatedDots.put(packageUserKey, newDot);
             } else {
                 // No need to update the dot if it already existed (no visual change).
@@ -155,7 +161,7 @@
     }
 
     public int getShortcutCountForItem(ItemInfo info) {
-        if (!DeepShortcutManager.supportsShortcuts(info)) {
+        if (!ShortcutUtil.supportsDeepShortcuts(info)) {
             return 0;
         }
         ComponentName component = info.getTargetComponent();
@@ -167,17 +173,26 @@
         return count == null ? 0 : count;
     }
 
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        if (!DeepShortcutManager.supportsShortcuts(info)) {
+    public @Nullable DotInfo getDotInfoForItem(@NonNull ItemInfo info) {
+        if (!ShortcutUtil.supportsShortcuts(info)) {
             return null;
         }
-
-        return mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+        DotInfo dotInfo = mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+        if (dotInfo == null) {
+            return null;
+        }
+        List<NotificationKeyData> notifications = getNotificationsForItem(
+                info, dotInfo.getNotificationKeys());
+        if (notifications.isEmpty()) {
+            return null;
+        }
+        return dotInfo;
     }
 
     public @NonNull List<NotificationKeyData> getNotificationKeysForItem(ItemInfo info) {
         DotInfo dotInfo = getDotInfoForItem(info);
-        return dotInfo == null ? Collections.EMPTY_LIST : dotInfo.getNotificationKeys();
+        return dotInfo == null ? Collections.EMPTY_LIST
+                : getNotificationsForItem(info, dotInfo.getNotificationKeys());
     }
 
     /** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -226,6 +241,27 @@
         return null;
     }
 
+    /**
+     * Returns a list of notifications that are relevant to given ItemInfo.
+     */
+    public static @NonNull List<NotificationKeyData> getNotificationsForItem(
+            @NonNull ItemInfo info, @NonNull List<NotificationKeyData> notifications) {
+        String shortcutId = ShortcutUtil.getShortcutIdIfPinnedShortcut(info);
+        if (shortcutId == null) {
+            return notifications;
+        }
+        String[] personKeys = ShortcutUtil.getPersonKeysIfPinnedShortcut(info);
+        return notifications.stream().filter((NotificationKeyData notification) -> {
+                    if (notification.shortcutId != null) {
+                        return notification.shortcutId.equals(shortcutId);
+                    }
+                    if (notification.personKeysFromNotification.length != 0) {
+                        return Arrays.equals(notification.personKeysFromNotification, personKeys);
+                    }
+                    return false;
+                }).collect(Collectors.toList());
+    }
+
     public interface PopupDataChangeListener {
 
         PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 41ab4df..5a5fbab 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -29,11 +29,12 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
     private static final String TAG = "RemoteActionShortcut";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
 
     private final RemoteAction mAction;
 
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 563f3b3..78bd81b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -131,6 +131,7 @@
         @Override
         public View.OnClickListener getOnClickListener(final Launcher launcher,
                 final ItemInfo itemInfo) {
+            if (itemInfo.getTargetComponent() == null) return null;
             final List<WidgetItem> widgets =
                     launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
                             itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
index 516fafa..37a2092 100644
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -24,13 +28,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-
 public class SystemShortcutFactory implements ResourceBasedOverride {
 
     public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(
-                    SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class));
+            forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
 
     /** Note that these are in order of priority. */
     private final SystemShortcut[] mAllShortcuts;
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index bab454f..4fd0f88 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -112,6 +112,13 @@
                 }
                 break;
             }
+
+            case TestProtocol.REQUEST_ALLOCATED_MEMORY: {
+                final Runtime runtime = Runtime.getRuntime();
+                response.putLong(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        runtime.totalMemory() - runtime.freeMemory());
+                break;
+            }
         }
         return response;
     }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 9846a04..f9f5dc4 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -73,10 +73,12 @@
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
     public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
+    public static final String REQUEST_ALLOCATED_MEMORY = "allocated-memory";
 
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
     public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
 
     public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
+    public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 0545344..c5ba5ba 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -69,6 +69,7 @@
     protected final SwipeDetector.Direction mSwipeDirection;
 
     private boolean mNoIntercept;
+    private boolean mIsLogContainerSet;
     protected int mStartContainerType;
 
     protected LauncherState mStartState;
@@ -180,7 +181,7 @@
     /**
      * Returns the container that the touch started from when leaving NORMAL state.
      */
-    protected abstract int getLogContainerTypeForNormalState();
+    protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
 
     private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
         LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -231,13 +232,7 @@
     @Override
     public void onDragStart(boolean start) {
         mStartState = mLauncher.getStateManager().getState();
-        if (mStartState == ALL_APPS) {
-            mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
-        } else if (mStartState == NORMAL) {
-            mStartContainerType = getLogContainerTypeForNormalState();
-        } else if (mStartState == OVERVIEW){
-            mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
-        }
+        mIsLogContainerSet = false;
         if (mCurrentAnimation == null) {
             mFromState = mStartState;
             mToState = null;
@@ -285,6 +280,21 @@
         return true;
     }
 
+    @Override
+    public boolean onDrag(float displacement, MotionEvent ev) {
+        if (!mIsLogContainerSet) {
+            if (mStartState == ALL_APPS) {
+                mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
+            } else if (mStartState == NORMAL) {
+                mStartContainerType = getLogContainerTypeForNormalState(ev);
+            } else if (mStartState == OVERVIEW) {
+                mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+            }
+            mIsLogContainerSet = true;
+        }
+        return onDrag(displacement);
+    }
+
     protected void updateProgress(float fraction) {
         mCurrentAnimation.setPlayFraction(fraction);
         if (mAtomicComponentsController != null) {
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 2ee0328..e185a31 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -18,12 +18,13 @@
 import android.content.Context;
 import android.os.Looper;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 
 import java.util.concurrent.ExecutionException;
 
-import androidx.annotation.VisibleForTesting;
-
 /**
  * Utility class for defining singletons which are initiated on main thread.
  */
@@ -60,6 +61,14 @@
         mValue = value;
     }
 
+    /**
+     * Initializes a provider based on resource overrides
+     */
+    public static <T extends ResourceBasedOverride> MainThreadInitializedObject<T> forOverride(
+            Class<T> clazz, int resourceId) {
+        return new MainThreadInitializedObject<>(c -> Overrides.getObject(clazz, c, resourceId));
+    }
+
     public interface ObjectProvider<T> {
 
         T get(Context context);
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index 1ce2822..8dc45f5 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -3,6 +3,8 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 
@@ -15,7 +17,9 @@
     public UserHandle mUser;
     private int mHashCode;
 
+    @Nullable
     public static PackageUserKey fromItemInfo(ItemInfo info) {
+        if (info.getTargetComponent() == null) return null;
         return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user);
     }
 
@@ -38,7 +42,8 @@
      * @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
      */
     public boolean updateFromItemInfo(ItemInfo info) {
-        if (DeepShortcutManager.supportsShortcuts(info)) {
+        if (info.getTargetComponent() == null) return false;
+        if (ShortcutUtil.supportsShortcuts(info)) {
             update(info.getTargetComponent().getPackageName(), info.user);
             return true;
         }
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
new file mode 100644
index 0000000..792d69f
--- /dev/null
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+public class ShortcutUtil {
+  public static boolean supportsShortcuts(ItemInfo info) {
+    return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+  }
+
+  public static boolean supportsDeepShortcuts(ItemInfo info) {
+    return isActive(info) && isApp(info);
+  }
+
+  public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
+    return isActive(info) && isPinnedShortcut(info) ?
+        ShortcutKey.fromItemInfo(info).getId() : null;
+  }
+
+  public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
+    return isActive(info) && isPinnedShortcut(info) ?
+        ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
+  }
+
+  private static boolean isActive(ItemInfo info) {
+    boolean isLoading = info instanceof WorkspaceItemInfo
+        && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+    return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+  }
+
+  private static boolean isApp(ItemInfo info) {
+    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+  }
+
+  private static boolean isPinnedShortcut(ItemInfo info) {
+    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+        && info.container != ItemInfo.NO_ID
+        && info instanceof WorkspaceItemInfo;
+  }
+}
\ No newline at end of file
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 6b6f70d..09b1890 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,9 +27,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import androidx.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -63,13 +61,6 @@
         mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
     }
 
-    public static boolean supportsShortcuts(ItemInfo info) {
-        boolean isItemPromise = info instanceof WorkspaceItemInfo
-                && ((WorkspaceItemInfo) info).hasPromiseIconUi();
-        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && !info.isDisabled() && !isItemPromise;
-    }
-
     public boolean wasLastCallSuccess() {
         return mWasLastCallSuccess;
     }
@@ -89,8 +80,9 @@
      * Gets all the manifest and dynamic shortcuts associated with the given package and user,
      * to be displayed in the shortcuts container on long press.
      */
-    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+    public List<ShortcutInfo> queryForShortcutsContainer(@Nullable ComponentName activity,
             UserHandle user) {
+        if (activity == null) return Collections.EMPTY_LIST;
         return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
                 activity.getPackageName(), activity, null, user);
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index e9dc800..bd6ea50 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
                 ContainerType.HOTSEAT : ContainerType.WORKSPACE;
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
new file mode 100644
index 0000000..e875a3c
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -0,0 +1,31 @@
+/*
+ * 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.uioverrides;
+
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+public class TogglableFlag extends BaseTogglableFlag {
+
+    public TogglableFlag(String key, boolean defaultValue, String description) {
+        super(key, defaultValue, description);
+    }
+
+    @Override
+    public boolean getInitialValue(boolean value) {
+        return value;
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 5cc64dc..467ae02 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -17,9 +17,11 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Activity;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 
@@ -27,6 +29,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.util.TouchController;
 
@@ -95,4 +98,7 @@
 
     public static void clearSwipeSharedState(boolean finishAnimation) {}
 
+    public static Person[] getPersons(ShortcutInfo si) {
+        return Utilities.EMPTY_PERSON_ARRAY;
+    }
 }
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 61c7306..c6f55a7 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -20,6 +20,9 @@
 
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
 
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner"/>
 
diff --git a/tests/OWNERS b/tests/OWNERS
index 046d871..02e8ebc 100644
--- a/tests/OWNERS
+++ b/tests/OWNERS
@@ -1 +1,4 @@
 vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 8dc8cea..fc19baa 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -38,6 +38,7 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
@@ -330,30 +331,27 @@
     }
 
     protected void startAppFast(String packageName) {
-        final Instrumentation instrumentation = getInstrumentation();
-        final Intent intent = instrumentation.getContext().getPackageManager().
-                getLaunchIntentForPackage(packageName);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        instrumentation.getTargetContext().startActivity(intent);
-        assertTrue(packageName + " didn't start",
-                mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
+        startIntent(
+                getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
+                        packageName),
+                By.pkg(packageName).depth(0));
     }
 
     protected void startTestActivity(int activityNumber) {
         final String packageName = getAppPackageName();
-        final Instrumentation instrumentation = getInstrumentation();
-        final Intent intent = instrumentation.getContext().getPackageManager().
+        final Intent intent = getInstrumentation().getContext().getPackageManager().
                 getLaunchIntentForPackage(packageName);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setComponent(new ComponentName(packageName,
                 "com.android.launcher3.tests.Activity" + activityNumber));
-        instrumentation.getTargetContext().startActivity(intent);
-        assertTrue(packageName + " didn't start",
-                mDevice.wait(
-                        Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
-                        DEFAULT_UI_TIMEOUT));
+        startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
+    }
+
+    private void startIntent(Intent intent, BySelector selector) {
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        getInstrumentation().getTargetContext().startActivity(intent);
+        assertTrue("App didn't start: " + selector,
+                mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
     }
 
     public static String resolveSystemApp(String category) {
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4dab44f..0c87ab9 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -173,6 +173,7 @@
 
     @Test
     public void testWorkspace() throws Exception {
+        mLauncher.enableDebugTracing();
         final Workspace workspace = mLauncher.getWorkspace();
 
         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
@@ -190,7 +191,7 @@
                 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
                         isWorkspaceScrollable(launcher)));
         assertNotNull("ensureScrollable didn't add Chrome app",
-                workspace.tryGetWorkspaceAppIcon("Chrome"));
+                workspace.getWorkspaceAppIcon("Chrome"));
 
         // Test flinging workspace.
         workspace.flingBackward();
@@ -206,8 +207,9 @@
         assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
 
         // Test starting a workspace app.
-        final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
+        final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
         assertNotNull("No Chrome app in workspace", app);
+        mLauncher.disableDebugTracing();
     }
 
     public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -298,6 +300,7 @@
     @Test
     @PortraitLandscape
     public void testDragAppIcon() throws Throwable {
+        mLauncher.enableDebugTracing();
         // 1. Open all apps and wait for load complete.
         // 2. Drag icon to homescreen.
         // 3. Verify that the icon works on homescreen.
@@ -314,11 +317,13 @@
                 "Launcher activity is the top activity; expecting another activity to be the top "
                         + "one",
                 isInBackground(launcher)));
+        mLauncher.disableDebugTracing();
     }
 
     @Test
     @PortraitLandscape
     public void testDragShortcut() throws Throwable {
+        mLauncher.enableDebugTracing();
         // 1. Open all apps and wait for load complete.
         // 2. Find the app and long press it to show shortcuts.
         // 3. Press icon center until shortcuts appear
@@ -338,6 +343,7 @@
         } finally {
             allApps.unfreeze();
         }
+        mLauncher.disableDebugTracing();
     }
 
     public static String getAppPackageName() {
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 593cce8..899686b 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.util;
 
 import android.os.SystemClock;
+import android.util.Log;
 
 import org.junit.Assert;
 
@@ -16,7 +17,9 @@
     }
 
     public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
-        long endTime = SystemClock.uptimeMillis() + timeout;
+        final long startTime = SystemClock.uptimeMillis();
+        long endTime = startTime + timeout;
+        Log.d("Wait", "atMost: " + startTime + " - " + endTime);
         while (SystemClock.uptimeMillis() < endTime) {
             try {
                 if (condition.isTrue()) {
@@ -36,6 +39,7 @@
         } catch (Throwable t) {
             throw new RuntimeException(t);
         }
+        Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
         Assert.fail(message);
     }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index eef2f24..e7a2bca 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -15,7 +15,6 @@
 
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
-    private static int sScreenshotCount = 0;
     final private UiDevice mDevice;
 
     public FailureWatcher(UiDevice device) {
@@ -40,7 +39,8 @@
     protected void failed(Throwable e, Description description) {
         if (mDevice == null) return;
         final String pathname = getInstrumentation().getTargetContext().
-                getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
+                getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
+                + ".png";
         Log.e(TAG, "Failed test " + description.getMethodName() +
                 ", screenshot will be saved to " + pathname +
                 ", track trace is below, UI object dump is further below:\n" +
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 2042403..62fe26d 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -19,6 +19,7 @@
 import android.app.Application;
 import android.app.Application.ActivityLifecycleCallbacks;
 import android.os.Bundle;
+
 import androidx.test.InstrumentationRegistry;
 
 import com.android.launcher3.Launcher;
@@ -84,19 +85,27 @@
         }
 
         @Override
-        public void onActivityStarted(Activity activity) { }
+        public void onActivityStarted(Activity activity) {
+            if (activity instanceof Launcher) {
+                mActivity.getRotationHelper().forceAllowRotationForTesting(true);
+            }
+        }
 
         @Override
-        public void onActivityResumed(Activity activity) { }
+        public void onActivityResumed(Activity activity) {
+        }
 
         @Override
-        public void onActivityPaused(Activity activity) { }
+        public void onActivityPaused(Activity activity) {
+        }
 
         @Override
-        public void onActivityStopped(Activity activity) { }
+        public void onActivityStopped(Activity activity) {
+        }
 
         @Override
-        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+        }
 
         @Override
         public void onActivityDestroyed(Activity activity) {
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 9ff354a..f070280 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -120,7 +120,7 @@
             mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
                     hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
 
-            final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
+            final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
                     appIconSelector);
             return new AppIcon(mLauncher, appIcon);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index bbd2c29..25e6e8c 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -74,7 +74,7 @@
                 flingForward();
             }
 
-            mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
+            mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "dismissed all tasks")) {
                 return new Workspace(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 82af7b0..df80a51 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import android.graphics.Point;
 
 import androidx.test.uiautomator.By;
@@ -23,13 +25,10 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
-
 /**
  * Ancestor for AppIcon and AppMenuItem.
  */
 abstract class Launchable {
-    private static final int WAIT_TIME_MS = 60000;
     protected final LauncherInstrumentation mLauncher;
 
     protected final UiObject2 mObject;
@@ -53,9 +52,12 @@
     private Background launch(BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
-        mLauncher.assertTrue(
-                "Launching an app didn't open a new window: " + mObject.getText(),
-                mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+
+        mLauncher.executeAndWaitForEvent(
+                () -> mObject.click(),
+                event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                "Launching an app didn't open a new window: " + mObject.getText());
+
         mLauncher.assertTrue(
                 "App didn't start: " + selector,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fe7401c..9d43352 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -124,7 +124,7 @@
     private static final String APPS_RES_ID = "apps_view";
     private static final String OVERVIEW_RES_ID = "overview_panel";
     private static final String WIDGETS_RES_ID = "widgets_list_view";
-    public static final int WAIT_TIME_MS = 60000;
+    public static final int WAIT_TIME_MS = 10000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
 
     private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -178,6 +178,7 @@
         PackageManager pm = getContext().getPackageManager();
         ProviderInfo pi = pm.resolveContentProvider(
                 testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
+        assertNotNull("Cannot find content provider for " + testProviderAuthority, pi);
         ComponentName cn = new ComponentName(pi.packageName, pi.name);
 
         if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
@@ -678,13 +679,6 @@
     }
 
     @NonNull
-    UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
-        final UiObject2 object = container.findObject(selector);
-        assertNotNull("Can't find an object with selector: " + selector, object);
-        return object;
-    }
-
-    @NonNull
     List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
         return container.findObjects(getLauncherObjectSelector(resName));
     }
@@ -888,6 +882,7 @@
     }
 
     long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
+        log("movePointer: " + from + " to " + to);
         final Point point = new Point();
         long steps = duration / GESTURE_STEP_MS;
         long currentTime = startTime;
@@ -962,4 +957,9 @@
     public void disableDebugTracing() {
         getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
     }
+
+    public long getAllocatedMemory() {
+        return getTestInfo(TestProtocol.REQUEST_ALLOCATED_MEMORY).
+                getLong(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 6e33322..91f0fc4 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,18 +16,16 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import android.graphics.Rect;
 
 import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * A recent task in the overview panel carousel.
  */
 public final class OverviewTask {
-    private static final long WAIT_TIME_MS = 60000;
     private final LauncherInstrumentation mLauncher;
     private final UiObject2 mTask;
     private final BaseOverview mOverview;
@@ -66,9 +64,11 @@
         verifyActiveContainer();
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "clicking an overview task")) {
-            mLauncher.assertTrue("Launching task didn't open a new window: " +
-                            mTask.getParent().getContentDescription(),
-                    mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+            mLauncher.executeAndWaitForEvent(
+                    () -> mTask.click(),
+                    event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    "Launching task didn't open a new window: " +
+                            mTask.getParent().getContentDescription());
         }
         return new Background(mLauncher);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index 399c59d..a089a52 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -109,7 +109,7 @@
         DropBoxManager.Entry entry;
         StringBuilder errorDetails = new StringBuilder();
         while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
-            if (errorDetails.length() != 0) errorDetails.append("------------------------------");
+            errorDetails.append("------------------------------\n");
             timestamp = entry.getTimeMillis();
             errorDetails.append(new Date(timestamp));
             errorDetails.append(": ");
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 07f8b64..639902f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -108,10 +108,13 @@
      */
     @NonNull
     public AppIcon getWorkspaceAppIcon(String appName) {
-        return new AppIcon(mLauncher,
-                mLauncher.getObjectInContainer(
-                        verifyActiveContainer(),
-                        AppIcon.getAppIconSelector(appName, mLauncher)));
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get a workspace icon")) {
+            return new AppIcon(mLauncher,
+                    mLauncher.waitForObjectInContainer(
+                            verifyActiveContainer(),
+                            AppIcon.getAppIconSelector(appName, mLauncher)));
+        }
     }
 
     /**
@@ -142,13 +145,13 @@
 
     @NonNull
     public AppIcon getHotseatAppIcon(String appName) {
-        return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
+        return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
     }
 
     @NonNull
     public Folder getHotseatFolder(String appName) {
-        return new Folder(mLauncher, mLauncher.getObjectInContainer(
+        return new Folder(mLauncher, mLauncher.waitForObjectInContainer(
                 mHotseat, Folder.getSelector(appName, mLauncher)));
     }