Push chooser targets to the shortcut manager.

More info at go/direct-share-push

Test: Some basic testing currently in, more to be added.

Change-Id: I069f9779988c3eca2c4d8b83ec62501983854355
diff --git a/api/current.txt b/api/current.txt
index 3a7d5e5..2d534a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10103,10 +10103,12 @@
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
     method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+    method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent);
     method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
     method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
     method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10
     field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
     field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
     field public static final int FLAG_MATCH_PINNED = 2; // 0x2
@@ -10645,6 +10647,9 @@
     method public int describeContents();
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
+    method public android.content.ComponentName[] getChooserComponentNames();
+    method public android.os.PersistableBundle getChooserExtras();
+    method public android.content.IntentFilter[] getChooserIntentFilters();
     method public java.lang.CharSequence getDisabledMessage();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
@@ -10657,6 +10662,7 @@
     method public java.lang.CharSequence getShortLabel();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
+    method public boolean isChooser();
     method public boolean isDeclaredInManifest();
     method public boolean isDynamic();
     method public boolean isEnabled();
@@ -10669,9 +10675,11 @@
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+    method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
diff --git a/api/system-current.txt b/api/system-current.txt
index ab9dfd7..1f9d8cb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10640,10 +10640,12 @@
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
     method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+    method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent);
     method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
     method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
     method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10
     field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
     field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
     field public static final int FLAG_MATCH_PINNED = 2; // 0x2
@@ -11267,6 +11269,9 @@
     method public int describeContents();
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
+    method public android.content.ComponentName[] getChooserComponentNames();
+    method public android.os.PersistableBundle getChooserExtras();
+    method public android.content.IntentFilter[] getChooserIntentFilters();
     method public java.lang.CharSequence getDisabledMessage();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
@@ -11279,6 +11284,7 @@
     method public java.lang.CharSequence getShortLabel();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
+    method public boolean isChooser();
     method public boolean isDeclaredInManifest();
     method public boolean isDynamic();
     method public boolean isEnabled();
@@ -11291,9 +11297,11 @@
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+    method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
diff --git a/api/test-current.txt b/api/test-current.txt
index ceb9805..590421e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10135,10 +10135,12 @@
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
     method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+    method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent);
     method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
     method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
     method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10
     field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
     field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
     field public static final int FLAG_MATCH_PINNED = 2; // 0x2
@@ -10681,6 +10683,9 @@
     method public int describeContents();
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
+    method public android.content.ComponentName[] getChooserComponentNames();
+    method public android.os.PersistableBundle getChooserExtras();
+    method public android.content.IntentFilter[] getChooserIntentFilters();
     method public java.lang.CharSequence getDisabledMessage();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
@@ -10693,6 +10698,7 @@
     method public java.lang.CharSequence getShortLabel();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
+    method public boolean isChooser();
     method public boolean isDeclaredInManifest();
     method public boolean isDynamic();
     method public boolean isEnabled();
@@ -10705,9 +10711,11 @@
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
+    method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index c08bd1d..41311eb 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -55,7 +55,8 @@
             String callingPackage, String packageName, int flags, in UserHandle user);
 
     ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
-            in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
+            in List shortcutIds, in ComponentName componentName, in Intent intent, int flags,
+            in UserHandle user);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
     boolean startShortcut(String callingPackage, String packageName, String id,
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index d76d824..776492a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -275,7 +275,18 @@
         @Deprecated
         public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
 
-        /** @hide */
+        /**
+         * Include chooser shortcuts in the result.
+         * STOPSHIP TODO: Unless explicitly requesting chooser fields, we should strip out chooser
+         *           relevant fields from the Shortcut. This should also be adequately documented.
+         */
+        public static final int FLAG_MATCH_CHOOSER = 1 << 4;
+
+        /**
+         * Does not retrieve CHOOSER only shortcuts.
+         * TODO: Add another flag for MATCH_ALL_PINNED
+         * @hide
+         */
         public static final int FLAG_MATCH_ALL_KINDS =
                 FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
 
@@ -308,6 +319,7 @@
                         FLAG_MATCH_DYNAMIC,
                         FLAG_MATCH_PINNED,
                         FLAG_MATCH_MANIFEST,
+                        FLAG_MATCH_CHOOSER,
                         FLAG_GET_KEY_FIELDS_ONLY,
                 })
         @Retention(RetentionPolicy.SOURCE)
@@ -324,6 +336,9 @@
         @Nullable
         ComponentName mActivity;
 
+        @Nullable
+        Intent mIntent;
+
         @QueryFlags
         int mQueryFlags;
 
@@ -368,6 +383,14 @@
         }
 
         /**
+         * If non-null, returns only shortcuts with intent filters that match this intent.
+         */
+        public ShortcutQuery setIntent(@Nullable Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
          * Set query options.  At least one of the {@code MATCH} flags should be set.  Otherwise,
          * no shortcuts will be returned.
          *
@@ -681,7 +704,7 @@
         try {
             return mService.getShortcuts(mContext.getPackageName(),
                     query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
-                    query.mQueryFlags, user)
+                    query.mIntent, query.mQueryFlags, user)
                     .getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index f1f2683..d3d3c66 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -21,8 +21,10 @@
 import android.annotation.UserIdInt;
 import android.app.TaskStackBuilder;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -38,10 +40,12 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -95,6 +99,14 @@
     public static final int FLAG_MASKABLE_BITMAP = 1 << 9;
 
     /** @hide */
+    public static final int FLAG_CHOOSER = 1 << 10;
+
+    /**
+     * TODO: Add FLAG_CHOOSER_INFO_OMITTED to reflect that chooser info was omitted in the Shortcut
+     *       due to the context in which it was retrieved.
+     * TODO: Add a FLAG_LAUNCHABLE to reflect whether or not the Shortcut has a launchable intent
+     * @hide
+     */
     @IntDef(flag = true,
             value = {
             FLAG_DYNAMIC,
@@ -107,6 +119,7 @@
             FLAG_STRINGS_RESOLVED,
             FLAG_IMMUTABLE,
             FLAG_MASKABLE_BITMAP,
+            FLAG_CHOOSER,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ShortcutFlags {}
@@ -201,6 +214,24 @@
     @Nullable
     private PersistableBundle[] mIntentPersistableExtrases;
 
+    /**
+     * If used in a chooser, extras that should be added into the intent passed through.
+     */
+    @Nullable
+    private PersistableBundle mChooserExtras;
+
+    /**
+     * Intent filters to be used if the shortcut is to be used in a chooser context.
+     */
+    @Nullable
+    private IntentFilter[] mChooserIntentFilters;
+
+    /**
+     * Component names corresponding to the above intent filters.
+     */
+    @Nullable
+    private ComponentName[] mChooserComponentNames;
+
     private int mRank;
 
     /**
@@ -250,6 +281,13 @@
         mDisabledMessageResId = b.mDisabledMessageResId;
         mCategories = cloneCategories(b.mCategories);
         mIntents = cloneIntents(b.mIntents);
+        if (b.mChooserIntentFilters != null) {
+            mChooserIntentFilters = b.mChooserIntentFilters.toArray(new IntentFilter[0]);
+        }
+        if (b.mChooserComponentNames != null) {
+            mChooserComponentNames = b.mChooserComponentNames.toArray(new ComponentName[0]);
+        }
+        mChooserExtras = b.mChooserExtras;
         fixUpIntentExtras();
         mRank = b.mRank;
         mExtras = b.mExtras;
@@ -330,8 +368,28 @@
         if (mTitle == null && mTitleResId == 0) {
             throw new IllegalArgumentException("Short label must be provided");
         }
-        Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided");
-        Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
+
+        // For a shortcut to be valid, there should either be an Intent, or a non-empty set of
+        // intent filters.
+        if (mIntents == null || mIntents.length == 0) {
+            Preconditions.checkNotNull(mChooserIntentFilters,
+                    "Intent must be provided if not a chooser target");
+            Preconditions.checkNotNull(mChooserComponentNames,
+                    "Intent must be provided if not a chooser target");
+        }
+
+        // If ChooserIntentFilter are provided, they should match the length of the provided
+        // component names.
+        if (mChooserIntentFilters != null) {
+            if (mChooserComponentNames == null
+                    || mChooserIntentFilters.length != mChooserComponentNames.length) {
+                throw new IllegalArgumentException("Inconsistent intent filters and "
+                        + "component names given");
+            }
+            if (mChooserIntentFilters.length == 0 || mChooserComponentNames.length == 0) {
+                throw new IllegalArgumentException("Empty intent filter and component names given");
+            }
+        }
     }
 
     /**
@@ -376,6 +434,10 @@
                 mDisabledMessageResName = source.mDisabledMessageResName;
                 mIconResName = source.mIconResName;
             }
+            // TODO: Omit these by default and add a new clone flag.
+            mChooserIntentFilters = source.mChooserIntentFilters;
+            mChooserComponentNames = source.mChooserComponentNames;
+            mChooserExtras = source.mChooserExtras;
         } else {
             // Set this bit.
             mFlags |= FLAG_KEY_FIELDS_ONLY;
@@ -503,6 +565,25 @@
     }
 
     /**
+     * Whether the shortcut has any intentFilter matching the passed in one.
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean hasMatchingFilter(ContentResolver resolver, Intent intent) {
+        if (mChooserIntentFilters == null) {
+            return false;
+        }
+        for (IntentFilter filter : mChooserIntentFilters) {
+            int match = filter.match(resolver, intent, false, TAG);
+            if (match > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
      * Extract the entry name from a fully-donated resource name.
      * e.g. "com.android.app1:drawable/icon1" -> "icon1"
      * @hide
@@ -685,6 +766,15 @@
         if (source.mExtras != null) {
             mExtras = source.mExtras;
         }
+        if (source.mChooserExtras != null) {
+            mChooserExtras = source.mChooserExtras;
+        }
+        if (source.mChooserIntentFilters != null) {
+            mChooserIntentFilters = source.mChooserIntentFilters;
+        }
+        if (source.mChooserComponentNames != null) {
+            mChooserComponentNames = source.mChooserComponentNames;
+        }
     }
 
     /**
@@ -746,6 +836,12 @@
 
         private PersistableBundle mExtras;
 
+        private PersistableBundle mChooserExtras;
+
+        private List<IntentFilter> mChooserIntentFilters;
+
+        private List<ComponentName> mChooserComponentNames;
+
         /**
          * Old style constructor.
          * @hide
@@ -1032,6 +1128,40 @@
         }
 
         /**
+         * Extras that can be added which will be added to the Intent used to launch the app if
+         * launched from a chooser context.
+         */
+        @NonNull
+        public Builder setChooserExtras(@NonNull PersistableBundle extras) {
+            mChooserExtras = extras;
+            return this;
+        }
+
+        /**
+         * IntentFilters and the components that should resolve a match for a given chooser target.
+         * If multiple matches are found, the component corresponding to the closest match will be
+         * used.
+         *
+         * @param filter IntendFilter that if matched will have the intent forwarded to the given
+         *               component
+         * @param name The component that an intent that passes this filter will resolve to.
+         */
+        public Builder addChooserIntentFilter(@NonNull IntentFilter filter,
+                @NonNull ComponentName name) {
+            Preconditions.checkNotNull(filter, "intent filter cannot be null");
+            Preconditions.checkNotNull(name, "component name cannot be null");
+
+            if (mChooserIntentFilters == null || mChooserComponentNames == null) {
+                mChooserIntentFilters = new ArrayList<>();
+                mChooserComponentNames = new ArrayList<>();
+            }
+
+            mChooserIntentFilters.add(filter);
+            mChooserComponentNames.add(name);
+            return this;
+        }
+
+        /**
          * Creates a {@link ShortcutInfo} instance.
          */
         @NonNull
@@ -1232,6 +1362,30 @@
     }
 
     /**
+     * Retrieve the extras that will be added in to any intent launched through the chooser.
+     */
+    @NonNull
+    public PersistableBundle getChooserExtras() {
+        return mChooserExtras;
+    }
+
+    /**
+     * Retrieve the list of intent filters for chooser targets.
+     */
+    @NonNull
+    public IntentFilter[] getChooserIntentFilters() {
+        return mChooserIntentFilters;
+    }
+
+    /**
+     * Retrieve the list of component names corresponding to the above intent filters.
+     */
+    @NonNull
+    public ComponentName[] getChooserComponentNames() {
+        return mChooserComponentNames;
+    }
+
+    /**
      * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
      * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
      *
@@ -1352,6 +1506,11 @@
         return hasFlags(FLAG_PINNED);
     }
 
+    /** Return whether a shortcut can be shown in the chooser. */
+    public boolean isChooser() {
+        return hasFlags(FLAG_CHOOSER);
+    }
+
     /**
      * Return whether a shortcut is static; that is, whether a shortcut is
      * published from AndroidManifest.xml.  If {@code true}, the shortcut is
@@ -1380,6 +1539,14 @@
         return isPinned() && !(isDynamic() || isManifestShortcut());
     }
 
+    /**
+     * @return true if pinned but neither static nor dynamic.
+     * @hide
+     */
+    public boolean isDynamicOrChooser() {
+        return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_CHOOSER);
+    }
+
     /** @hide */
     public boolean isOriginallyFromManifest() {
         return hasFlags(FLAG_IMMUTABLE);
@@ -1661,6 +1828,19 @@
                 mCategories.add(source.readString().intern());
             }
         }
+
+        // We put a placeholder empty array in to keep the parcelable order, but can do away with
+        // them at this point if they're empty.
+        mChooserComponentNames = source.readParcelableArray(cl, ComponentName.class);
+        if (mChooserComponentNames.length == 0) {
+            mChooserComponentNames = null;
+        }
+
+        mChooserIntentFilters = source.readParcelableArray(cl, IntentFilter.class);
+        if (mChooserIntentFilters.length == 0) {
+            mChooserIntentFilters = null;
+        }
+        mChooserExtras = source.readPersistableBundle(cl);
     }
 
     @Override
@@ -1707,6 +1887,17 @@
         } else {
             dest.writeInt(0);
         }
+        if (mChooserComponentNames != null) {
+            dest.writeParcelableArray(mChooserComponentNames, flags);
+        } else {
+            dest.writeParcelableArray(new ComponentName[0], flags);
+        }
+        if (mChooserIntentFilters != null) {
+            dest.writeParcelableArray(mChooserIntentFilters, flags);
+        } else {
+            dest.writeParcelableArray(new IntentFilter[0], flags);
+        }
+        dest.writePersistableBundle(mChooserExtras);
     }
 
     public static final Creator<ShortcutInfo> CREATOR =
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 87a6d4a..696fe81 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -44,8 +44,8 @@
             getShortcuts(int launcherUserId,
             @NonNull String callingPackage, long changedSince,
             @Nullable String packageName, @Nullable List<String> shortcutIds,
-            @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
-            int userId);
+            @Nullable ComponentName componentName, @Nullable Intent intent,
+            @ShortcutQuery.QueryFlags int flags, int userId);
 
     public abstract boolean
             isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index df65659..79301aa 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -23,15 +23,21 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LabeledIntent;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
@@ -356,6 +362,7 @@
             mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets));
         }
         mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
+        mChooserRowAdapter.updateRowScales();
         mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
         adapterView.setAdapter(mChooserRowAdapter);
         if (listView != null) {
@@ -842,7 +849,9 @@
                 return false;
             }
             intent.setComponent(mChooserTarget.getComponentName());
-            intent.putExtras(mChooserTarget.getIntentExtras());
+            if (mChooserTarget.getIntentExtras() != null) {
+                intent.putExtras(mChooserTarget.getIntentExtras());
+            }
 
             // Important: we will ignore the target security checks in ActivityManager
             // if and only if the ChooserTarget's target package is the same package
@@ -925,6 +934,8 @@
         private static final int MAX_SERVICE_TARGETS = 8;
         private static final int MAX_TARGETS_PER_SERVICE = 4;
 
+        private boolean mAreChooserShortcutsRetrieved;
+
         private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
         private final List<TargetInfo> mCallerTargets = new ArrayList<>();
         private boolean mShowServiceTargets;
@@ -1016,6 +1027,20 @@
             if (mServiceTargets != null) {
                 pruneServiceTargets();
             }
+
+            if (DEBUG) Log.d(TAG, "Adding pushed chooser targets");
+
+            if (!mAreChooserShortcutsRetrieved) {
+                LauncherApps launcherApps = getLauncherApps();
+                LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+                query.setIntent(getTargetIntent());
+                query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_CHOOSER);
+                List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, UserHandle.SYSTEM);
+                if (DEBUG) Log.d(TAG, "Adding " + shortcuts.size() + " chooser shortcuts");
+                addShortcuts(shortcuts);
+                mAreChooserShortcutsRetrieved = true;
+            }
+
             if (DEBUG) Log.d(TAG, "List built querying services");
             queryTargetServices(this);
         }
@@ -1041,6 +1066,7 @@
 
         public int getServiceTargetCount() {
             if (!mShowServiceTargets) {
+                if (DEBUG) Log.d("TAG", "Hiding service targets");
                 return 0;
             }
             return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
@@ -1132,6 +1158,71 @@
             notifyDataSetChanged();
         }
 
+        // TODO: Pushed targets need to be scored correctly
+        public void addShortcuts(List<ShortcutInfo> infos) {
+            for (ShortcutInfo info : infos) {
+                List<ChooserTarget> newTargets = new ArrayList<>();
+                final ComponentName cn = info.getActivity();
+                ActivityInfo ai;
+                ResolveInfo ri = new ResolveInfo();
+                if (cn != null) {
+                    try {
+                        ai = getPackageManager().getActivityInfo(cn, 0);
+                        ri.activityInfo = ai;
+                        UserManager userManager =
+                                (UserManager) getSystemService(Context.USER_SERVICE);
+                        ri.iconResourceId = ai.icon;
+                        ri.labelRes = ai.labelRes;
+                        ri.resolvePackageName = ai.packageName;
+                        ri.activityInfo.applicationInfo = new ApplicationInfo(
+                                ri.activityInfo.applicationInfo);
+                        ri.activityInfo.applicationInfo = ai.applicationInfo;
+                        ri.activityInfo.applicationInfo.uid = getUserId();
+                    } catch (PackageManager.NameNotFoundException ignored) {
+                        if (DEBUG) Log.d(TAG, "Package not found, skipping this shortcut");
+                        continue;
+                    }
+                }
+
+                DisplayResolveInfo resolveInfo = new DisplayResolveInfo(getTargetIntent(),
+                        ri,
+                        info.getShortLabel(),
+                        info.getLongLabel(),
+                        getTargetIntent());
+
+                int bestMatch = 0;
+                ComponentName bestComponent = null;
+                for (int i = 0; i < info.getChooserIntentFilters().length; i++) {
+                    int newMatch = info.getChooserIntentFilters()[i]
+                            .match(getContentResolver(), getTargetIntent(), false, TAG);
+                    if (DEBUG) Log.d(TAG, "A match was found with value: " + newMatch);
+                    if (newMatch > bestMatch) {
+                        bestMatch = newMatch;
+                        bestComponent = info.getChooserComponentNames()[i];
+                    }
+                }
+                if (bestMatch == 0) {
+                    Log.e(TAG, "Unexpectedly, no match was found for the provided chooser intent");
+                    return;
+                }
+
+                Bundle extrasToAdd =
+                        info.getChooserExtras() == null ? null: new Bundle(info.getChooserExtras());
+                if (DEBUG) Log.d(TAG, "Adding service target " + info.getShortLabel());
+                newTargets.add(new ChooserTarget(
+                        info.getShortLabel(),
+                        info.getIcon(),
+                        1,
+                        bestComponent,
+                        extrasToAdd));
+                addServiceResults(resolveInfo, newTargets);
+            }
+            if (mChooserRowAdapter != null) {
+                mChooserRowAdapter.updateRowScales();
+            }
+            setShowServiceTargets(true);
+        }
+
         /**
          * Set to true to reveal all service targets at once.
          */
@@ -1246,37 +1337,7 @@
                 @Override
                 public void onChanged() {
                     super.onChanged();
-                    final int rcount = getServiceTargetRowCount();
-                    if (mServiceTargetScale == null
-                            || mServiceTargetScale.length != rcount) {
-                        RowScale[] old = mServiceTargetScale;
-                        int oldRCount = old != null ? old.length : 0;
-                        mServiceTargetScale = new RowScale[rcount];
-                        if (old != null && rcount > 0) {
-                            System.arraycopy(old, 0, mServiceTargetScale, 0,
-                                    Math.min(old.length, rcount));
-                        }
-
-                        for (int i = rcount; i < oldRCount; i++) {
-                            old[i].cancelAnimation();
-                        }
-
-                        for (int i = oldRCount; i < rcount; i++) {
-                            final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
-                                    .setInterpolator(mInterpolator);
-                            mServiceTargetScale[i] = rs;
-                        }
-
-                        // Start the animations in a separate loop.
-                        // The process of starting animations will result in
-                        // binding views to set up initial values, and we must
-                        // have ALL of the new RowScale objects created above before
-                        // we get started.
-                        for (int i = oldRCount; i < rcount; i++) {
-                            mServiceTargetScale[i].startAnimation();
-                        }
-                    }
-
+                    updateRowScales();
                     notifyDataSetChanged();
                 }
 
@@ -1293,6 +1354,40 @@
             });
         }
 
+         void updateRowScales() {
+            final int rcount = getServiceTargetRowCount();
+            if (mServiceTargetScale == null
+                    || mServiceTargetScale.length != rcount) {
+                if (DEBUG) Log.d(TAG, "Row scales need adjusting to " + rcount + " rows.");
+                RowScale[] old = mServiceTargetScale;
+                int oldRCount = old != null ? old.length : 0;
+                mServiceTargetScale = new RowScale[rcount];
+                if (old != null && rcount > 0) {
+                    System.arraycopy(old, 0, mServiceTargetScale, 0,
+                            Math.min(old.length, rcount));
+                }
+
+                for (int i = rcount; i < oldRCount; i++) {
+                    old[i].cancelAnimation();
+                }
+
+                for (int i = oldRCount; i < rcount; i++) {
+                    final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
+                            .setInterpolator(mInterpolator);
+                    mServiceTargetScale[i] = rs;
+                }
+
+                // Start the animations in a separate loop.
+                // The process of starting animations will result in
+                // binding views to set up initial values, and we must
+                // have ALL of the new RowScale objects created above before
+                // we get started.
+                for (int i = oldRCount; i < rcount; i++) {
+                    mServiceTargetScale[i].startAnimation();
+                }
+            }
+        }
+
         private float getRowScale(int rowPosition) {
             final int start = getCallerTargetRowCount();
             final int end = start + getServiceTargetRowCount();
@@ -1563,6 +1658,10 @@
         }
     }
 
+    public LauncherApps getLauncherApps() {
+        return (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
+    }
+
     static class ServiceResultInfo {
         public final DisplayResolveInfo originalTarget;
         public final List<ChooserTarget> resultTargets;
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 1080a9f..3dfecc6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -16,6 +16,17 @@
 
 package com.android.internal.app;
 
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.SystemClock;
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
@@ -48,25 +59,31 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 /**
  * Chooser activity instrumentation tests
  */
 @RunWith(AndroidJUnit4.class)
 public class ChooserActivityTest {
+    private Instrumentation instrumentation;
+
+    @Before
+    public void setUp() {
+        instrumentation = InstrumentationRegistry.getInstrumentation();
+        sOverrides.reset();
+    }
+
     @Rule
     public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
             new ActivityTestRule<>(ChooserWrapperActivity.class, false,
                     false);
 
-    @Before
-    public void cleanOverrideData() {
-        sOverrides.reset();
-    }
-
     @Test
     public void customTitle() throws InterruptedException {
         Intent sendIntent = createSendImageIntent();
@@ -235,7 +252,6 @@
             chosen[0] = targetInfo.getResolveInfo();
             return true;
         };
-
         // Make a stable copy of the components as the original list may be modified
         List<ResolvedComponentInfo> stableCopy =
                 createResolvedComponentsForTestWithOtherProfile(2);
@@ -324,6 +340,32 @@
         assertThat(chosen[0], is(toChoose));
     }
 
+    public void pushedChooserTarget() {
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        setChooserShortcuts(1);
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        Intent sendIntent = createSendImageIntent();
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+
+        waitForIdle();
+
+        onView(withText("short chooser label 0"))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0].resolvePackageName,
+                is(ResolverDataProvider.createActivityInfo(0).packageName));
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -371,4 +413,48 @@
         }
         return packageStats.mChooserCounts.get(action).getOrDefault(annotation, 0);
     }
+
+    private void setChooserShortcuts(int numShortcuts) {
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        for (int i = 0; i < numShortcuts; i++) {
+           shortcuts.add(makeShortcut(i));
+        }
+        when(sOverrides.launcherApps.getShortcuts(
+                Mockito.isA(LauncherApps.ShortcutQuery.class),
+                Mockito.eq(UserHandle.SYSTEM)))
+                .thenReturn(shortcuts);
+    }
+
+    private ShortcutInfo makeShortcut(int i) {
+        try {
+            IntentFilter filter = new IntentFilter(Intent.ACTION_SEND, "image/jpeg");
+
+            ComponentName component = new ComponentName("foo.bar", "foo.bar" + ".MainActivity");
+            ShortcutInfo.Builder b = new ShortcutInfo.Builder(instrumentation.getContext(), "" + i)
+                    .setActivity(component)
+                    .setShortLabel("short chooser label " + i)
+                    .setLongLabel("long chooser label" + i)
+                    .setRank(i)
+                    .setIntent(createSendImageIntent())
+                    .setIcon(Icon.createWithResource(instrumentation.getContext(),
+                            android.R.drawable.ic_menu_add))
+                    .addChooserIntentFilter(
+                            filter,
+                            component);
+
+            sOverrides.createPackageManager = pm -> {
+                final PackageManager spied = spy(pm);
+                try {
+                    doAnswer(invocation -> ResolverDataProvider.createActivityInfo(i))
+                            .when(spied).getActivityInfo(
+                            Mockito.isA(ComponentName.class), Mockito.anyInt());
+                } catch (Exception e) {
+                    // this is ok, just not found
+                    e.printStackTrace();
+                }
+                return spied;
+            };
+            return b.build();
+        } catch (Exception e) {return null;}
+    }
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index c446f3c..0dac260 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 
 import java.util.function.Function;
@@ -74,6 +75,11 @@
         return super.getPackageManager();
     }
 
+    @Override
+    public LauncherApps getLauncherApps() {
+        return sOverrides.launcherApps;
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -82,6 +88,7 @@
     static class OverrideData {
         @SuppressWarnings("Since15")
         public Function<PackageManager, PackageManager> createPackageManager;
+        public LauncherApps launcherApps;
         public Function<TargetInfo, Boolean> onSafelyStartCallback;
         public ResolverListController resolverListController;
         public Boolean isVoiceInteraction;
@@ -90,6 +97,7 @@
             onSafelyStartCallback = null;
             isVoiceInteraction = null;
             createPackageManager = null;
+            launcherApps = mock(LauncherApps.class);
             resolverListController = mock(ResolverListController.class);
         }
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c11131a..71bfa64 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -442,8 +442,8 @@
 
         @Override
         public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
-                String packageName, List shortcutIds, ComponentName componentName, int flags,
-                UserHandle targetUser) {
+                String packageName, List shortcutIds, ComponentName componentName, Intent intent,
+                int flags, UserHandle targetUser) {
             ensureShortcutPermission(callingPackage);
             if (!canAccessProfile(callingPackage, targetUser, "Cannot get shortcuts")
                     || !isUserEnabled(targetUser)) {
@@ -454,11 +454,17 @@
                         "To query by shortcut ID, package name must also be set");
             }
 
+            if ((flags & ShortcutQuery.FLAG_MATCH_CHOOSER) == 0
+                    && intent != null) {
+                throw new IllegalArgumentException("Supplied an intent in the query, but did "
+                        + "not request chooser targets");
+            }
+
             // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
             return new ParceledListSlice<>((List<ShortcutInfo>)
                     mShortcutServiceInternal.getShortcuts(getCallingUserId(),
                             callingPackage, changedSince, packageName, shortcutIds,
-                            componentName, flags, targetUser.getIdentifier()));
+                            componentName, intent, flags, targetUser.getIdentifier()));
         }
 
         @Override
@@ -906,6 +912,7 @@
                                         cookie.packageName,
                                         /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
                                         /* component= */ null,
+                                        /* intent= */ null,
                                         ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
                                         | ShortcutQuery.FLAG_GET_ALL_KINDS
                                         , userId);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 570259b..ac98ab9 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
@@ -31,6 +32,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.ShortcutService.ShortcutOperation;
@@ -68,6 +70,9 @@
     private static final String TAG_EXTRAS = "extras";
     private static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_CATEGORIES = "categories";
+    private static final String TAG_CHOOSER_EXTRAS = "chooser-extras";
+    private static final String TAG_CHOOSER_INTENT_FILTERS = "chooser-intent-filters";
+    private static final String TAG_CHOOSER_COMPONENT_NAMES = "chooser-component-names";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_CALL_COUNT = "call-count";
@@ -91,6 +96,7 @@
     private static final String ATTR_ICON_RES_ID = "icon-res";
     private static final String ATTR_ICON_RES_NAME = "icon-resname";
     private static final String ATTR_BITMAP_PATH = "bitmap-path";
+    private static final String ATTR_COMPONENT_NAMES = "component-names";
 
     private static final String NAME_CATEGORIES = "categories";
 
@@ -200,7 +206,7 @@
         if (shortcut != null) {
             mShortcutUser.mService.removeIcon(getPackageUserId(), shortcut);
             shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
-                    | ShortcutInfo.FLAG_MANIFEST);
+                    | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CHOOSER);
         }
         return shortcut;
     }
@@ -226,7 +232,7 @@
         Preconditions.checkArgument(newShortcut.isEnabled(),
                 "add/setDynamicShortcuts() cannot publish disabled shortcuts");
 
-        newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+        addCorrectDynamicFlags(newShortcut);
 
         final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
 
@@ -250,6 +256,17 @@
         addShortcutInner(newShortcut);
     }
 
+    // TODO: Sample code & JavaDoc for ShortcutManager needs updating to reflect the fact that
+    //       Chooser shortcuts are not always dynamic.
+    public void addCorrectDynamicFlags(@NonNull ShortcutInfo shortcut) {
+        if (shortcut.getIntent() != null) {
+            shortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+        }
+        if (!ArrayUtils.isEmpty(shortcut.getChooserIntentFilters())) {
+            shortcut.addFlags(ShortcutInfo.FLAG_CHOOSER);
+        }
+    }
+
     /**
      * Remove all shortcuts that aren't pinned nor dynamic.
      */
@@ -282,11 +299,11 @@
         boolean changed = false;
         for (int i = mShortcuts.size() - 1; i >= 0; i--) {
             final ShortcutInfo si = mShortcuts.valueAt(i);
-            if (si.isDynamic()) {
+            if (si.isDynamic() || si.isChooser()) {
                 changed = true;
 
                 si.setTimestamp(now);
-                si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+                si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER);
                 si.setRank(0); // It may still be pinned, so clear the rank.
             }
         }
@@ -355,7 +372,8 @@
         if (oldShortcut.isPinned()) {
 
             oldShortcut.setRank(0);
-            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
+            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST
+                    | ShortcutInfo.FLAG_CHOOSER);
             if (disable) {
                 oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
             }
@@ -1116,8 +1134,8 @@
                     // Don't adjust ranks for manifest shortcuts.
                     continue;
                 }
-                // At this point, it must be dynamic.
-                if (!si.isDynamic()) {
+                // At this point, it must be dynamic or a chooser.
+                if (!si.isDynamicOrChooser()) {
                     s.wtf("Non-dynamic shortcut found.");
                     continue;
                 }
@@ -1294,7 +1312,7 @@
             ShortcutService.writeAttr(out, ATTR_FLAGS,
                     si.getFlags() &
                             ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
-                            | ShortcutInfo.FLAG_DYNAMIC));
+                            | ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER));
         } else {
             // When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored
             // as dynamic.
@@ -1317,15 +1335,36 @@
         }
         final Intent[] intentsNoExtras = si.getIntentsNoExtras();
         final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
-        final int numIntents = intentsNoExtras.length;
-        for (int i = 0; i < numIntents; i++) {
-            out.startTag(null, TAG_INTENT);
-            ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
-            ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
-            out.endTag(null, TAG_INTENT);
+        if (intentsNoExtras != null) {
+            final int numIntents = intentsNoExtras.length;
+            for (int i = 0; i < numIntents; i++) {
+                out.startTag(null, TAG_INTENT);
+                ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
+                ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
+                out.endTag(null, TAG_INTENT);
+            }
+        }
+        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+
+        ShortcutService.writeTagExtra(out, TAG_CHOOSER_EXTRAS, si.getChooserExtras());
+
+        final IntentFilter[] intentFilters = si.getChooserIntentFilters();
+        if (intentFilters != null) {
+            for (int i = 0; i < intentFilters.length; i++) {
+                out.startTag(null, TAG_CHOOSER_INTENT_FILTERS);
+                intentFilters[i].writeToXml(out);
+                out.endTag(null, TAG_CHOOSER_INTENT_FILTERS);
+            }
         }
 
-        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+        final ComponentName[] componentNames = si.getChooserComponentNames();
+        if (componentNames != null) {
+            for (int i = 0; i < componentNames.length; i++) {
+                out.startTag(null, TAG_CHOOSER_COMPONENT_NAMES);
+                ShortcutService.writeAttr(out, ATTR_COMPONENT_NAMES, componentNames[i]);
+                out.endTag(null, TAG_CHOOSER_COMPONENT_NAMES);
+            }
+        }
 
         out.endTag(null, TAG_SHORTCUT);
     }
@@ -1398,6 +1437,9 @@
         String iconResName;
         String bitmapPath;
         ArraySet<String> categories = null;
+        PersistableBundle chooserExtras;
+        List<IntentFilter> chooserIntentFilters = new ArrayList<>();
+        List<ComponentName> chooserComponentNames = new ArrayList<>();
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -1458,6 +1500,18 @@
                         }
                     }
                     continue;
+                case TAG_CHOOSER_EXTRAS:
+                    chooserExtras = PersistableBundle.restoreFromXml(parser);
+                    continue;
+                case TAG_CHOOSER_COMPONENT_NAMES:
+                    chooserComponentNames.add(ShortcutService.parseComponentNameAttribute(parser,
+                            ATTR_ACTIVITY));
+                    continue;
+                case TAG_CHOOSER_INTENT_FILTERS:
+                    IntentFilter toAdd = new IntentFilter();
+                    toAdd.readFromXml(parser);
+                    chooserIntentFilters.add(toAdd);
+                    continue;
             }
             throw ShortcutService.throwForInvalidTag(depth, tag);
         }
@@ -1551,10 +1605,10 @@
         // Verify each shortcut's status.
         for (int i = mShortcuts.size() - 1; i >= 0; i--) {
             final ShortcutInfo si = mShortcuts.valueAt(i);
-            if (!(si.isDeclaredInManifest() || si.isDynamic() || si.isPinned())) {
+            if (!(si.isDeclaredInManifest() || si.isDynamicOrChooser() || si.isPinned())) {
                 failed = true;
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
-                        + " is not manifest, dynamic or pinned.");
+                        + " is not manifest, dynamic, chooser or pinned.");
             }
             if (si.isDeclaredInManifest() && si.isDynamic()) {
                 failed = true;
@@ -1596,6 +1650,11 @@
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                         + " has a dummy target activity");
             }
+            if (si.getIntent() == null && !si.isChooser()) {
+                failed = true;
+                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+                        + " has a null intent, but is not a chooser");
+            }
         }
 
         if (failed) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 057e781..74eb340 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -27,6 +27,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -64,6 +65,7 @@
 import android.os.Handler;
 import android.os.LocaleList;
 import android.os.Looper;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -1750,6 +1752,7 @@
             ps.clearAllImplicitRanks();
             assignImplicitRanks(newShortcuts);
 
+            // TODO: Consider removing Chooser fields. If so, the FLAG_CHOOSER should be removed
             for (int i = 0; i < size; i++) {
                 final ShortcutInfo source = newShortcuts.get(i);
                 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
@@ -1789,6 +1792,13 @@
                 if (replacingIcon || source.hasStringResources()) {
                     fixUpShortcutResourceNamesAndValues(target);
                 }
+
+                // While updating, we keep the dynamic flag as it previously was, but refresh the
+                // chooser flag.
+                // TODO: If we support clearing Chooser fields, we should also remove the flag.
+                if (target.getChooserIntentFilters() != null) {
+                    target.addFlags(ShortcutInfo.FLAG_CHOOSER);
+                }
             }
 
             // Lastly, adjust the ranks.
@@ -1852,6 +1862,7 @@
         return true;
     }
 
+    // TODO: Ensure non-launchable shortcuts can not be pinned
     @Override
     public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
             IntentSender resultIntent, int userId) {
@@ -2007,7 +2018,7 @@
 
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
-                    ShortcutInfo::isDynamic);
+                    ShortcutInfo::isDynamicOrChooser);
         }
     }
 
@@ -2200,6 +2211,14 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
+            // For the chooser, we just check is the system is calling.
+            // STOPSHIP: We need to implement a new permission here rather than this terrible check.
+            //           The packageName check is to try to distinguish between when an actual
+            //           launcher is making the call, and when it's the system.
+            if (isCallerSystem() && packageName.equals("android")) {
+                return true;
+            }
+
             final ShortcutUser user = getUserShortcutsLocked(userId);
 
             // Always trust the cached component.
@@ -2372,7 +2391,7 @@
         public List<ShortcutInfo> getShortcuts(int launcherUserId,
                 @NonNull String callingPackage, long changedSince,
                 @Nullable String packageName, @Nullable List<String> shortcutIds,
-                @Nullable ComponentName componentName,
+                @Nullable ComponentName componentName, @Nullable Intent intent,
                 int queryFlags, int userId) {
             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
 
@@ -2394,13 +2413,13 @@
                 if (packageName != null) {
                     getShortcutsInnerLocked(launcherUserId,
                             callingPackage, packageName, shortcutIds, changedSince,
-                            componentName, queryFlags, userId, ret, cloneFlag);
+                            componentName, intent, queryFlags, userId, ret, cloneFlag);
                 } else {
                     final List<String> shortcutIdsF = shortcutIds;
                     getUserShortcutsLocked(userId).forAllPackages(p -> {
                         getShortcutsInnerLocked(launcherUserId,
                                 callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
-                                componentName, queryFlags, userId, ret, cloneFlag);
+                                componentName, intent, queryFlags, userId, ret, cloneFlag);
                     });
                 }
             }
@@ -2409,7 +2428,7 @@
 
         private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
                 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
-                @Nullable ComponentName componentName, int queryFlags,
+                @Nullable ComponentName componentName, Intent intent, int queryFlags,
                 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
             final ArraySet<String> ids = shortcutIds == null ? null
                     : new ArraySet<>(shortcutIds);
@@ -2434,6 +2453,15 @@
                                 return false;
                             }
                         }
+                        if (intent != null
+                                && !si.hasMatchingFilter(mContext.getContentResolver(), intent)) {
+                            return false;
+                        }
+
+                        if (((queryFlags & ShortcutQuery.FLAG_MATCH_CHOOSER) != 0)
+                                && si.isChooser()) {
+                            return true;
+                        }
                         if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
                                 && si.isDynamic()) {
                             return true;
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 100338e..1b59d72 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -1324,20 +1325,23 @@
     protected ShortcutInfo makeShortcut(String id) {
         return makeShortcut(
                 id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
     }
 
     @Deprecated // Title was renamed to short label.
     protected ShortcutInfo makeShortcutWithTitle(String id, String title) {
         return makeShortcut(
                 id, title, /* activity =*/ null, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
     }
 
     protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) {
         return makeShortcut(
                 id, shortLabel, /* activity =*/ null, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
     }
 
     /**
@@ -1346,7 +1350,8 @@
     protected ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
         final ShortcutInfo s = makeShortcut(
                 id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
         s.setTimestamp(timestamp);
         return s;
     }
@@ -1358,7 +1363,8 @@
             ComponentName activity) {
         final ShortcutInfo s = makeShortcut(
                 id, "Title-" + id, activity, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
         s.setTimestamp(timestamp);
         return s;
     }
@@ -1369,7 +1375,27 @@
     protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
         return makeShortcut(
                 id, "Title-" + id, /* activity =*/ null, icon,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
+    }
+
+    protected ShortcutInfo makeChooserShortcut(String id, int i, boolean includeIntent) {
+        List<IntentFilter> filters = new ArrayList<>();
+        List<ComponentName> componentNames = new ArrayList<>();
+        for(int j = 0; j < i; j++) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction("view");
+            filters.add(filter);
+
+            componentNames.add(new ComponentName("xxxx", "yy" + i));
+        }
+        Intent intent = null;
+        if (includeIntent) {
+            intent = makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class);
+        }
+        return makeShortcut(
+                id, "Title-" + id, /* activity =*/ null, /* icon */ null,
+                intent, /* rank =*/ 0, filters, componentNames);
     }
 
     protected ShortcutInfo makePackageShortcut(String packageName, String id) {
@@ -1378,7 +1404,8 @@
         setCaller(packageName);
         ShortcutInfo s = makeShortcut(
                 id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
         setCaller(origCaller); // restore the caller
 
         return s;
@@ -1402,39 +1429,52 @@
     protected ShortcutInfo makeShortcutWithActivity(String id, ComponentName activity) {
         return makeShortcut(
                 id, "Title-" + id, activity, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilters =*/ null, /* chooserComponentNames =*/ null);
     }
 
     protected ShortcutInfo makeShortcutWithIntent(String id, Intent intent) {
         return makeShortcut(
                 id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
-                intent, /* rank =*/ 0);
+                intent, /* rank =*/ 0, /* chooserFilters =*/ null,
+                /* chooserComponentNames =*/ null);
+
     }
 
     protected ShortcutInfo makeShortcutWithActivityAndTitle(String id, ComponentName activity,
             String title) {
         return makeShortcut(
                 id, title, activity, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+                /* chooserFilters =*/ null, /* chooserComponentNames =*/ null);
     }
 
     protected ShortcutInfo makeShortcutWithActivityAndRank(String id, ComponentName activity,
             int rank) {
         return makeShortcut(
                 id, "Title-" + id, activity, /* icon =*/ null,
-                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank);
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank,
+                /* chooserFilters =*/ null, /* chooserComponentNames =*/ null);
     }
 
     /**
      * Make a shortcut with details.
      */
     protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
-            Icon icon, Intent intent, int rank) {
+            Icon icon, Intent intent, int rank, @Nullable List<IntentFilter> chooserFilters,
+            @Nullable List<ComponentName> chooserComponentNames) {
         final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, id)
                 .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
                 .setShortLabel(title)
-                .setRank(rank)
-                .setIntent(intent);
+                .setRank(rank);
+        if (intent != null) {
+            b.setIntent(intent);
+        }
+        if (chooserFilters != null) {
+            for (int i = 0; i < chooserFilters.size(); i++) {
+                b.addChooserIntentFilter(chooserFilters.get(i), chooserComponentNames.get(i));
+            }
+        }
         if (icon != null) {
             b.setIcon(icon);
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index e4d92ba..94ff07f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllChooser;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned;
@@ -256,7 +257,9 @@
                 icon1,
                 makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
                         "key1", "val1", "nest", makeBundle("key", 123)),
-                /* weight */ 10);
+                /* weight */ 10,
+                /* chooserFilter=*/ null,
+                /* chooserComponentNames=*/ null);
 
         final ShortcutInfo si2 = makeShortcut(
                 "shortcut2",
@@ -264,14 +267,18 @@
                 /* activity */ null,
                 icon2,
                 makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                /* weight */ 12);
+                /* weight */ 12,
+                /* chooserFilter=*/ null,
+                /* chooserComponentNames=*/ null);
         final ShortcutInfo si3 = makeShortcut(
                 "shortcut3",
                 "Title 3",
                 /* activity */ null,
                 icon3,
                 makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                /* weight */ 13);
+                /* weight */ 13,
+                /* chooserFilter=*/ null,
+                /* chooserComponentNames=*/ null);
 
         assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
@@ -982,8 +989,10 @@
                     makeShortcut("s2"),
                     makeShortcut("s3"),
                     makeShortcut("s4"),
-                    makeShortcut("s5")
-            )));
+                    makeShortcut("s5"),
+                    makeChooserShortcut("s6", 2, true),
+                    makeChooserShortcut("s7", 2, true),
+                    makeChooserShortcut("s8", 1, true))));
         });
         runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -991,11 +1000,13 @@
                     makeShortcut("s2"),
                     makeShortcut("s3"),
                     makeShortcut("s4"),
-                    makeShortcut("s5")
-            )));
+                    makeShortcut("s5"),
+                    makeChooserShortcut("s6", 2, true),
+                    makeChooserShortcut("s7", 2, true),
+                    makeChooserShortcut("s8", 1, true))));
         });
         runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"),
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3", "s6"),
                     getCallingUser());
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"),
                     getCallingUser());
@@ -1008,19 +1019,20 @@
             mManager.removeDynamicShortcuts(list("s1"));
             mManager.removeDynamicShortcuts(list("s3"));
             mManager.removeDynamicShortcuts(list("s5"));
+            mManager.removeDynamicShortcuts(list("s7"));
         });
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             assertShortcutIds(assertAllDynamic(
                     mManager.getDynamicShortcuts()),
-                    "s3", "s4", "s5");
+                    "s3", "s4", "s5", "s6", "s7", "s8");
             assertShortcutIds(assertAllPinned(
                     mManager.getPinnedShortcuts()),
-                    "s2", "s3");
+                    "s2", "s3", "s6");
         });
         runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
             assertShortcutIds(assertAllDynamic(
                     mManager.getDynamicShortcuts()),
-                    "s2", "s4");
+                    "s2", "s4", "s6", "s8");
             assertShortcutIds(assertAllPinned(
                     mManager.getPinnedShortcuts()),
                     "s4", "s5");
@@ -1057,10 +1069,10 @@
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             assertShortcutIds(assertAllDynamic(
                     mManager.getDynamicShortcuts()),
-                    "s3", "s4", "s5");
+                    "s3", "s4", "s5", "s6", "s7", "s8");
             assertShortcutIds(assertAllPinned(
                     mManager.getPinnedShortcuts()),
-                    "s2", "s3");
+                    "s2", "s3", "s6");
 
             ShortcutInfo s = getCallerShortcut("s2");
             assertTrue(s.hasIconResource());
@@ -1076,7 +1088,7 @@
         runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
             assertShortcutIds(assertAllDynamic(
                     mManager.getDynamicShortcuts()),
-                    "s2", "s4");
+                    "s2", "s4", "s6", "s8");
             assertShortcutIds(assertAllPinned(
                     mManager.getPinnedShortcuts()),
                     "s4", "s5");
@@ -1173,7 +1185,46 @@
         });
     }
 
-    // === Test for launcher side APIs ===
+    public void testUpdateShortcuts_chooser() {
+        runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"),
+                    makeChooserShortcut("s2", 2, false),
+                    makeChooserShortcut("s3", 2, false)
+            )));
+
+            assertFalse(getCallerShortcut("s1").isChooser());
+            assertTrue(getCallerShortcut("s2").isChooser());
+            assertTrue(getCallerShortcut("s3").isChooser());
+
+            ShortcutInfo s = getCallerShortcut("s1");
+            assertNull(s.getChooserIntentFilters());
+            assertNull(s.getChooserComponentNames());
+
+            assertTrue(getCallerShortcut("s1").isDynamic());
+            assertFalse(getCallerShortcut("s2").isDynamic());
+            assertFalse(getCallerShortcut("s3").isDynamic());
+
+
+            // Replace 2 with a chooser shortcut
+            mManager.updateShortcuts(list(makeChooserShortcut("s1", 2, true)));
+
+            s = getCallerShortcut("s1");
+            assertEquals(2, s.getChooserIntentFilters().length);
+            assertEquals(2, s.getChooserComponentNames().length);
+
+            assertShortcutIds(assertAllChooser(
+                    mManager.getDynamicShortcuts()),
+                    "s1", "s2", "s3");
+
+            assertTrue(getCallerShortcut("s1").isDynamic());
+            assertFalse(getCallerShortcut("s2").isDynamic());
+            assertFalse(getCallerShortcut("s3").isDynamic());
+        });
+    }
+
+
+            // === Test for launcher side APIs ===
 
     public void testGetShortcuts() {
 
@@ -1484,15 +1535,17 @@
                 /* icon =*/ null,
                 makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
                         "key1", "val1", "nest", makeBundle("key", 123)),
-                /* weight */ 10);
+                        /* weight */ 10, /* chooserFilter=*/ null,
+                        /* chooserComponentNames=*/ null);
 
         final ShortcutInfo s1_2 = makeShortcut(
-                "s2",
-                "Title 2",
+                "s2", "Title 2",
                 /* activity */ null,
                 /* icon =*/ null,
                 makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                /* weight */ 12);
+                /* weight */ 12,
+                /* chooserFilter=*/ null,
+                /* chooserComponentNames=*/ null);
 
         assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
         dumpsysOnLogcat();
@@ -1505,7 +1558,9 @@
                 /* icon =*/ null,
                 makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
                         "key1", "val1", "nest", makeBundle("key", 123)),
-                /* weight */ 10);
+                /* weight */ 10,
+                /* chooserFilter=*/ null,
+                /* chooserComponentNames=*/ null);
         assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
         dumpsysOnLogcat();
 
@@ -2674,10 +2729,12 @@
             final ShortcutInfo s1_2 = makeShortcut(
                     "s2",
                     "Title 2",
-            /* activity */ null,
-            /* icon =*/ null,
+                    /* activity */ null,
+                    /* icon =*/ null,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-            /* rank */ 12);
+                    /* rank */ 12,
+                    /* chooserFilter=*/ null,
+                    /* chooserComponentNames=*/ null);
 
             final ShortcutInfo s1_3 = makeShortcut("s3");
 
@@ -2692,7 +2749,9 @@
                     /* icon =*/ null,
                     makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
                             "key1", "val1", "nest", makeBundle("key", 123)),
-                    /* weight */ 10);
+                    /* weight */ 10,
+                    /* chooserFilter=*/ null,
+                    /* chooserComponentNames=*/ null);
             assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
         });
 
@@ -3110,7 +3169,9 @@
                     icon1,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
                             "key1", "val1", "nest", makeBundle("key", 123)),
-                        /* weight */ 10);
+                        /* weight */ 10,
+                    /* chooserFilter=*/ null,
+                    /* chooserComponentNames=*/ null);
 
             final ShortcutInfo si2 = makeShortcut(
                     "s2",
@@ -3118,7 +3179,9 @@
                         /* activity */ null,
                     icon2,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                        /* weight */ 12);
+                        /* weight */ 12,
+                    /* chooserFilter=*/ null,
+                    /* chooserComponentNames=*/ null);
 
             assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
 
@@ -3136,8 +3199,8 @@
                     makeComponent(ShortcutActivity.class),
                     icon1,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
-                            "key1", "val1", "nest", makeBundle("key", 123)),
-                        /* weight */ 10);
+                            "key1", "val1", "nest", makeBundle("key", 123)), /* weight */ 10,
+                            /* chooserFilter=*/ null, /* chooserComponentNames=*/ null);
 
             final ShortcutInfo si2 = makeShortcut(
                     "s2",
@@ -3145,7 +3208,8 @@
                         /* activity */ null,
                     icon2,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                        /* weight */ 12);
+                        /* weight */ 12, /* chooserFilter=*/ null,
+                        /* chooserComponentNames=*/ null);
 
             assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
 
@@ -3167,7 +3231,8 @@
                     icon1,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
                             "key1", "val1", "nest", makeBundle("key", 123)),
-                        /* weight */ 10);
+                            /* weight */ 10, /* chooserFilter=*/ null,
+                            /* chooserComponentNames=*/ null);
 
             final ShortcutInfo si2 = makeShortcut(
                     "s2",
@@ -3175,7 +3240,8 @@
                         /* activity */ null,
                     icon2,
                     makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                        /* weight */ 12);
+                            /* weight */ 12, /* chooserFilter=*/ null,
+                            /* chooserComponentNames=*/ null);
 
             assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
 
@@ -6800,10 +6866,12 @@
             mManager.setDynamicShortcuts(list(
                     makeShortcut("ms1", "title1",
                             new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
-                    /* icon */ null, new Intent("action1"), /* rank */ 0),
+                            /* icon */ null, new Intent("action1"), /* rank */ 0,
+                            /* chooserFilter=*/ null, /* chooserComponentNames=*/ null),
                     makeShortcut("ms2", "title2",
                             new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
-                    /* icon */ null, new Intent("action1"), /* rank */ 0)));
+                            /* icon */ null, new Intent("action1"), /* rank */ 0, /* chooserFilter=*/ null,
+                            /* chooserComponentNames=*/ null)));
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 28ec4fd..c54fa02 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -34,6 +34,7 @@
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.BitmapFactory;
@@ -93,11 +94,6 @@
 
         assertExpectException(
                 RuntimeException.class,
-                "intents cannot contain null",
-                () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null));
-
-        assertExpectException(
-                RuntimeException.class,
                 "action must be set",
                 () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent()));
 
@@ -142,6 +138,19 @@
                 "disabledMessage cannot be empty",
                 () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage(""));
 
+
+        assertExpectException(
+                RuntimeException.class,
+                "component name cannot be null",
+                () -> new ShortcutInfo.Builder(getTestContext(), "id")
+                        .addChooserIntentFilter(new IntentFilter(Intent.ACTION_SEND), null));
+
+        assertExpectException(
+                RuntimeException.class,
+                "intent filter cannot be null",
+                () -> new ShortcutInfo.Builder(getTestContext(), "id")
+                        .addChooserIntentFilter(null, new ComponentName("xxx", "s")));
+
         assertExpectException(NullPointerException.class, "action must be set",
                 () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent()));
 
@@ -240,6 +249,10 @@
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
+        IntentFilter chooserFilter = new IntentFilter();
+        chooserFilter.addAction(Intent.ACTION_VIEW);
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("l", 1);
 
         si = new ShortcutInfo.Builder(getTestContext())
                 .setId("id")
@@ -252,6 +265,8 @@
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setRank(123)
                 .setExtras(pb)
+                .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b"))
+                .setChooserExtras(pb2)
                 .build();
         si.addFlags(ShortcutInfo.FLAG_PINNED);
         si.setBitmapPath("abc");
@@ -282,6 +297,12 @@
         assertEquals(null, si.getTextResName());
         assertEquals(0, si.getDisabledMessageResourceId());
         assertEquals(null, si.getDisabledMessageResName());
+
+        assertEquals(1, si.getChooserIntentFilters().length);
+        assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0));
+        assertEquals(1, si.getChooserComponentNames().length);
+        assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]);
+        assertEquals(1, si.getChooserExtras().getInt("l"));
     }
 
     public void testShortcutInfoParcel_resId() {
@@ -290,6 +311,10 @@
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
+        IntentFilter chooserFilter = new IntentFilter();
+        chooserFilter.addAction(Intent.ACTION_VIEW);
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("l", 1);
 
         si = new ShortcutInfo.Builder(getTestContext())
                 .setId("id")
@@ -302,6 +327,8 @@
                 .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setRank(123)
                 .setExtras(pb)
+                .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b"))
+                .setChooserExtras(pb2)
                 .build();
         si.addFlags(ShortcutInfo.FLAG_PINNED);
         si.setBitmapPath("abc");
@@ -338,6 +365,11 @@
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
+        IntentFilter chooserFilter = new IntentFilter();
+        chooserFilter.addAction(Intent.ACTION_VIEW);
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("l", 1);
+
         ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setActivity(new ComponentName("a", "b"))
@@ -349,6 +381,8 @@
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setRank(123)
                 .setExtras(pb)
+                .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b"))
+                .setChooserExtras(pb2)
                 .build();
         sorig.addFlags(ShortcutInfo.FLAG_PINNED);
         sorig.setBitmapPath("abc");
@@ -378,6 +412,12 @@
         assertEquals(456, si.getIconResourceId());
         assertEquals("string/r456", si.getIconResName());
 
+        assertEquals(1, si.getChooserIntentFilters().length);
+        assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0));
+        assertEquals(1, si.getChooserComponentNames().length);
+        assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]);
+        assertEquals(1, si.getChooserExtras().getInt("l"));
+
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
 
         assertEquals(mClientContext.getPackageName(), si.getPackage());
@@ -445,6 +485,10 @@
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
+        IntentFilter chooserFilter = new IntentFilter();
+        chooserFilter.addAction(Intent.ACTION_VIEW);
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("l", 1);
         ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setActivity(new ComponentName("a", "b"))
@@ -456,6 +500,8 @@
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setRank(123)
                 .setExtras(pb)
+                .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b"))
+                .setChooserExtras(pb2)
                 .build();
         sorig.addFlags(ShortcutInfo.FLAG_PINNED);
         sorig.setBitmapPath("abc");
@@ -488,6 +534,12 @@
         assertEquals(456, si.getIconResourceId());
         assertEquals("string/r456", si.getIconResName());
 
+        assertEquals(1, si.getChooserIntentFilters().length);
+        assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0));
+        assertEquals(1, si.getChooserComponentNames().length);
+        assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]);
+        assertEquals(1, si.getChooserExtras().getInt("l"));
+
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
 
         assertEquals(mClientContext.getPackageName(), si.getPackage());
@@ -603,6 +655,10 @@
     public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
+        IntentFilter chooserFilter = new IntentFilter();
+        chooserFilter.addAction(Intent.ACTION_VIEW);
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("l", 1);
         ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
                 .setId("id")
                 .setActivity(new ComponentName("a", "b"))
@@ -714,12 +770,12 @@
         assertEquals(999, si.getRank());
 
 
-        PersistableBundle pb2 = new PersistableBundle();
-        pb2.putInt("x", 99);
+        PersistableBundle pb3 = new PersistableBundle();
+        pb3.putInt("x", 99);
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
-                .setExtras(pb2).build());
+                .setExtras(pb3).build());
         assertEquals("text", si.getText());
         assertEquals(99, si.getExtras().getInt("x"));
     }
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index ea45bd1..fd335c3 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -508,6 +508,13 @@
         return actualShortcuts;
     }
 
+    public static List<ShortcutInfo> assertAllChooser(List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.isChooser());
+        }
+        return actualShortcuts;
+    }
+
     public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
         for (ShortcutInfo s : actualShortcuts) {
             assertTrue("ID " + s.getId(), s.isPinned());