Update capability related api in Shortcut

Create dedicated classes for capability to decouple related apis from
native types for future extensibility.

Bug: 216590966
Test: atest CtsShortcutManagerTestCases
Change-Id: I6d7b9a00141ad8ad55ababb4a8446ed28e55031b
diff --git a/core/api/current.txt b/core/api/current.txt
index b5493a3..1d811f1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11230,6 +11230,33 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Attribution> CREATOR;
   }
 
+  public final class Capability implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public String getName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Capability> CREATOR;
+  }
+
+  public static final class Capability.Builder {
+    ctor public Capability.Builder(@NonNull String);
+    method @NonNull public android.content.pm.Capability build();
+  }
+
+  public final class CapabilityParams implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getAliases();
+    method @NonNull public String getName();
+    method @NonNull public String getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.CapabilityParams> CREATOR;
+  }
+
+  public static final class CapabilityParams.Builder {
+    ctor public CapabilityParams.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.content.pm.CapabilityParams.Builder addAlias(@NonNull String);
+    method @NonNull public android.content.pm.CapabilityParams build();
+  }
+
   public final class ChangedPackages implements android.os.Parcelable {
     ctor public ChangedPackages(int, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
@@ -12291,7 +12318,8 @@
     method @NonNull public static android.content.pm.ShortcutInfo createFromGenericDocument(@NonNull android.content.Context, @NonNull android.app.appsearch.GenericDocument);
     method public int describeContents();
     method @Nullable public android.content.ComponentName getActivity();
-    method @NonNull public java.util.List<java.lang.String> getCapabilityParameterValues(@NonNull String, @NonNull String);
+    method @NonNull public java.util.List<android.content.pm.Capability> getCapabilities();
+    method @NonNull public java.util.List<android.content.pm.CapabilityParams> getCapabilityParams(@NonNull android.content.pm.Capability);
     method @Nullable public java.util.Set<java.lang.String> getCategories();
     method @Nullable public CharSequence getDisabledMessage();
     method public int getDisabledReason();
@@ -12306,7 +12334,6 @@
     method public int getRank();
     method @Nullable public CharSequence getShortLabel();
     method public android.os.UserHandle getUserHandle();
-    method public boolean hasCapability(@NonNull String);
     method public boolean hasKeyFieldsOnly();
     method public boolean isCached();
     method public boolean isDeclaredInManifest();
@@ -12331,7 +12358,7 @@
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context, String);
-    method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull String, @Nullable String, @Nullable java.util.List<java.lang.String>);
+    method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull android.content.pm.Capability, @Nullable android.content.pm.CapabilityParams);
     method @NonNull public android.content.pm.ShortcutInfo build();
     method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName);
     method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index f20d1e6..1b84686b 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -349,7 +349,7 @@
                 .setDisabledReason(shortcutInfo.getDisabledReason())
                 .setPersons(shortcutInfo.getPersons())
                 .setLocusId(shortcutInfo.getLocusId())
-                .setCapabilityBindings(shortcutInfo.getCapabilityBindings())
+                .setCapabilityBindings(shortcutInfo.getCapabilityBindingsInternal())
                 .setTtlMillis(SHORTCUT_TTL)
                 .build();
     }
diff --git a/core/java/android/content/pm/Capability.aidl b/core/java/android/content/pm/Capability.aidl
new file mode 100644
index 0000000..df3b1be
--- /dev/null
+++ b/core/java/android/content/pm/Capability.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable Capability;
\ No newline at end of file
diff --git a/core/java/android/content/pm/Capability.java b/core/java/android/content/pm/Capability.java
new file mode 100644
index 0000000..1597d31
--- /dev/null
+++ b/core/java/android/content/pm/Capability.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents a capability that can be performed by an app, also known as App Action.
+ * Capabilities can be associated with a {@link ShortcutInfo}.
+ *
+ * @see ShortcutInfo.Builder#addCapabilityBinding(Capability, CapabilityParams)
+ */
+public final class Capability implements Parcelable {
+
+    @NonNull
+    private final String mName;
+
+    /**
+     * Constructor.
+     * @param name Name of the capability, usually maps to a built-in intent,
+     *            e.g. actions.intent.GET_MESSAGE. Note the character "/" is not permitted.
+     * @throws IllegalArgumentException If specified capability name contains the character "/".
+     *
+     * @hide
+     */
+    Capability(@NonNull final String name) {
+        Objects.requireNonNull(name);
+        if (name.contains("/")) {
+            throw new IllegalArgumentException("'/' is not permitted in the capability name");
+        }
+        mName = name;
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @hide
+     */
+    Capability(@NonNull final Capability orig) {
+        this(orig.mName);
+    }
+
+    private Capability(@NonNull final Builder builder) {
+        this(builder.mName);
+    }
+
+    private Capability(@NonNull final Parcel in) {
+        mName = in.readString();
+    }
+
+    /**
+     * Returns the name of the capability. e.g. actions.intent.GET_MESSAGE.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Capability)) {
+            return false;
+        }
+        return mName.equals(((Capability) obj).mName);
+    }
+
+    @Override
+    public int hashCode() {
+        return mName.hashCode();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mName);
+    }
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<Capability> CREATOR =
+            new Parcelable.Creator<Capability>() {
+        @Override
+        public Capability[] newArray(int size) {
+            return new Capability[size];
+        }
+
+        @Override
+        public Capability createFromParcel(@NonNull Parcel in) {
+            return new Capability(in);
+        }
+    };
+
+    /**
+     * Builder class for {@link Capability}.
+     */
+    public static final class Builder {
+
+        @NonNull
+        private final String mName;
+
+        /**
+         * Constructor.
+         * @param name Name of the capability, usually maps to a built-in intent,
+         *            e.g. actions.intent.GET_MESSAGE. Note the character "/" is not permitted.
+         * @throws IllegalArgumentException If specified capability name contains the character "/".
+         */
+        public Builder(@NonNull final String name) {
+            Objects.requireNonNull(name);
+            if (name.contains("/")) {
+                throw new IllegalArgumentException("'/' is not permitted in the capability name");
+            }
+            mName = name;
+        }
+
+        /**
+         * Creates an instance of {@link Capability}
+         */
+        @NonNull
+        public Capability build() {
+            return new Capability(this);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/CapabilityParams.aidl b/core/java/android/content/pm/CapabilityParams.aidl
new file mode 100644
index 0000000..39f1238
--- /dev/null
+++ b/core/java/android/content/pm/CapabilityParams.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable CapabilityParams;
diff --git a/core/java/android/content/pm/CapabilityParams.java b/core/java/android/content/pm/CapabilityParams.java
new file mode 100644
index 0000000..7239bac
--- /dev/null
+++ b/core/java/android/content/pm/CapabilityParams.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents the parameters and its matching names which can be associated with a
+ * {@link Capability}.
+ *
+ * @see ShortcutInfo.Builder#addCapabilityBinding(Capability, CapabilityParams)
+ */
+public final class CapabilityParams implements Parcelable {
+
+    @NonNull
+    private final String mName;
+    @NonNull
+    private final String mPrimaryValue;
+    @NonNull
+    private final List<String> mAliases;
+
+    /**
+     * Constructor.
+     * @param name Name of the capability parameter.
+     *           Note the character "/" is not permitted.
+     * @param primaryValue The primary value of the parameter.
+     * @param aliases Alternative values of the parameter.
+     */
+    private CapabilityParams(@NonNull final String name,
+            @NonNull final String primaryValue, @Nullable final Collection<String> aliases) {
+        Objects.requireNonNull(name);
+        Objects.requireNonNull(primaryValue);
+        mName = name;
+        mPrimaryValue = primaryValue;
+        mAliases = aliases == null ? Collections.emptyList()
+                : Collections.unmodifiableList(new ArrayList<>(aliases));
+    }
+
+    /**
+     * Copy constructor.
+     * @hide
+     */
+    CapabilityParams(@NonNull final CapabilityParams orig) {
+        this(orig.mName, orig.mPrimaryValue, orig.mAliases);
+    }
+
+    private CapabilityParams(@NonNull final Builder builder) {
+        this(builder.mKey, builder.mPrimaryValue, builder.mAliases);
+    }
+
+    private CapabilityParams(@NonNull final Parcel in) {
+        mName = in.readString();
+        mPrimaryValue = in.readString();
+        final List<String> values = new ArrayList<>();
+        in.readStringList(values);
+        mAliases = Collections.unmodifiableList(values);
+    }
+
+    /**
+     * Name of the parameter.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the primary name of values in this parameter.
+     */
+    @NonNull
+    public String getValue() {
+        return mPrimaryValue;
+    }
+
+    /**
+     * Returns the aliases of the values in ths parameter. Returns an empty list if there are no
+     * aliases.
+     */
+    @NonNull
+    public List<String> getAliases() {
+        return new ArrayList<>(mAliases);
+    }
+
+    /**
+     * A list of values for this parameter. The first value will be the primary name, while the
+     * rest will be alternative names.
+     * @hide
+     */
+    @NonNull
+    List<String> getValues() {
+        if (mAliases == null) {
+            return new ArrayList<>(Collections.singletonList(mPrimaryValue));
+        }
+        final List<String> ret = new ArrayList<>(mAliases.size() + 1);
+        ret.add(mPrimaryValue);
+        ret.addAll(mAliases);
+        return ret;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CapabilityParams)) {
+            return false;
+        }
+        final CapabilityParams target = (CapabilityParams) obj;
+        return mName.equals(target.mName) && mPrimaryValue.equals(target.mPrimaryValue)
+                && mAliases.equals(target.mAliases);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mPrimaryValue, mAliases);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mName);
+        dest.writeString(mPrimaryValue);
+        dest.writeStringList(mAliases);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<CapabilityParams> CREATOR =
+            new Parcelable.Creator<CapabilityParams>() {
+        @Override
+        public CapabilityParams[] newArray(int size) {
+            return new CapabilityParams[size];
+        }
+
+        @Override
+        public CapabilityParams createFromParcel(@NonNull Parcel in) {
+            return new CapabilityParams(in);
+        }
+    };
+
+    /**
+     * Builder class for {@link CapabilityParams}.
+     */
+    public static final class Builder {
+
+        @NonNull
+        private final String mKey;
+        @NonNull
+        private String mPrimaryValue;
+        @NonNull
+        private Set<String> mAliases;
+
+        /**
+         * Constructor.
+         * @param key key of the capability parameter.
+         *           Note the character "/" is not permitted.
+         * @param value The primary name of value in the {@link CapabilityParams}, cannot be empty.
+         */
+        public Builder(@NonNull final String key, @NonNull final String value) {
+            Objects.requireNonNull(key);
+            if (TextUtils.isEmpty(value)) {
+                throw new IllegalArgumentException("Primary value cannot be empty or null");
+            }
+            mPrimaryValue = value;
+            mKey = key;
+        }
+
+        /**
+         * Add an alias in the {@link CapabilityParams}.
+         */
+        @NonNull
+        public Builder addAlias(@NonNull final String alias) {
+            if (mAliases == null) {
+                mAliases = new ArraySet<>(1);
+            }
+            mAliases.add(alias);
+            return this;
+        }
+
+        /**
+         * Creates an instance of {@link CapabilityParams}
+         * @throws IllegalArgumentException If the specified value is empty.
+         */
+        @NonNull
+        public CapabilityParams build() {
+            return new CapabilityParams(this);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 41dd5bb3..56d092d 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -52,7 +52,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -60,7 +60,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * Represents a shortcut that can be published via {@link ShortcutManager}.
@@ -501,7 +500,8 @@
         mRank = b.mRank;
         mExtras = b.mExtras;
         mLocusId = b.mLocusId;
-        mCapabilityBindings = b.mCapabilityBindings;
+        mCapabilityBindings =
+                cloneCapabilityBindings(b.mCapabilityBindings);
         mStartingThemeResName = b.mStartingThemeResId != 0
                 ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
         updateTimestamp();
@@ -652,7 +652,8 @@
             // Set this bit.
             mFlags |= FLAG_KEY_FIELDS_ONLY;
         }
-        mCapabilityBindings = source.mCapabilityBindings;
+        mCapabilityBindings = cloneCapabilityBindings(
+                source.mCapabilityBindings);
         mStartingThemeResName = source.mStartingThemeResName;
     }
 
@@ -1003,7 +1004,8 @@
             mStartingThemeResName = source.mStartingThemeResName;
         }
         if (source.mCapabilityBindings != null) {
-            mCapabilityBindings = source.mCapabilityBindings;
+            mCapabilityBindings =
+                    cloneCapabilityBindings(source.mCapabilityBindings);
         }
     }
 
@@ -1447,43 +1449,25 @@
          * <P>This method can be called multiple times to add multiple parameters to the same
          * capability.
          *
-         * @param capability capability associated with the shortcut. e.g. actions.intent
-         *                   .START_EXERCISE.
-         * @param parameterName name of the parameter associated with given capability.
-         *                      e.g. exercise.name.
-         * @param parameterValues a list of values for that parameters. The first value will be
-         *                        the primary name, while the rest will be alternative names. If
-         *                        the values are empty, then the parameter will not be saved in
-         *                        the shortcut.
+         * @param capability {@link Capability} associated with the shortcut.
+         * @param capabilityParams Optional {@link CapabilityParams} associated with given
+         *                        capability.
          */
         @NonNull
-        public Builder addCapabilityBinding(@NonNull String capability,
-                @Nullable String parameterName, @Nullable List<String> parameterValues) {
+        public Builder addCapabilityBinding(@NonNull final Capability capability,
+                @Nullable final CapabilityParams capabilityParams) {
             Objects.requireNonNull(capability);
-            if (capability.contains("/")) {
-                throw new IllegalArgumentException("Illegal character '/' is found in capability");
-            }
             if (mCapabilityBindings == null) {
                 mCapabilityBindings = new ArrayMap<>(1);
             }
-            if (!mCapabilityBindings.containsKey(capability)) {
-                mCapabilityBindings.put(capability, new ArrayMap<>(0));
+            if (!mCapabilityBindings.containsKey(capability.getName())) {
+                mCapabilityBindings.put(capability.getName(), new ArrayMap<>(0));
             }
-            if (parameterName == null || parameterValues == null || parameterValues.isEmpty()) {
+            if (capabilityParams == null) {
                 return this;
             }
-            if (parameterName.contains("/")) {
-                throw new IllegalArgumentException(
-                        "Illegal character '/' is found in parameter name");
-            }
-            final Map<String, List<String>> params = mCapabilityBindings.get(capability);
-            if (!params.containsKey(parameterName)) {
-                params.put(parameterName, parameterValues);
-                return this;
-            }
-            params.put(parameterName,
-                    Stream.of(params.get(parameterName), parameterValues)
-                            .flatMap(Collection::stream).collect(Collectors.toList()));
+            final Map<String, List<String>> params = mCapabilityBindings.get(capability.getName());
+            params.put(capabilityParams.getName(), capabilityParams.getValues());
             return this;
         }
 
@@ -2264,41 +2248,78 @@
     }
 
     /**
+     * Returns an immutable copy of the capability bindings using internal data structure.
      * @hide
      */
-    public Map<String, Map<String, List<String>>> getCapabilityBindings() {
-        return mCapabilityBindings;
+    @Nullable
+    public Map<String, Map<String, List<String>>> getCapabilityBindingsInternal() {
+        return cloneCapabilityBindings(mCapabilityBindings);
+    }
+
+    @Nullable
+    private static Map<String, Map<String, List<String>>> cloneCapabilityBindings(
+            @Nullable final Map<String, Map<String, List<String>>> orig) {
+        if (orig == null) {
+            return null;
+        }
+        final Map<String, Map<String, List<String>>> ret = new ArrayMap<>();
+        for (String capability : orig.keySet()) {
+            final Map<String, List<String>> params = orig.get(capability);
+            final Map<String, List<String>> clone;
+            if (params == null) {
+                clone = null;
+            } else {
+                clone = new ArrayMap<>(params.size());
+                for (String paramName : params.keySet()) {
+                    final List<String> paramValues = params.get(paramName);
+                    clone.put(paramName, Collections.unmodifiableList(paramValues));
+                }
+            }
+            ret.put(capability, Collections.unmodifiableMap(clone));
+        }
+        return Collections.unmodifiableMap(ret);
     }
 
     /**
-     * Return true if the shortcut is or can be used in specified capability.
-     */
-    public boolean hasCapability(@NonNull String capability) {
-        Objects.requireNonNull(capability);
-        return mCapabilityBindings != null && mCapabilityBindings.containsKey(capability);
-    }
-
-    /**
-     *  Returns the values of specified parameter in associated with given capability.
-     *
-     *  @param capability capability associated with the shortcut. e.g. actions.intent
-     *                   .START_EXERCISE.
-     *  @param parameterName name of the parameter associated with given capability.
-     *                       e.g. exercise.name.
+     * Return a list of {@link Capability} associated with the shortcut.
      */
     @NonNull
-    public List<String> getCapabilityParameterValues(
-            @NonNull String capability, @NonNull String parameterName) {
-        Objects.requireNonNull(capability);
-        Objects.requireNonNull(parameterName);
+    public List<Capability> getCapabilities() {
         if (mCapabilityBindings == null) {
-            return Collections.emptyList();
+            return new ArrayList<>(0);
         }
-        final Map<String, List<String>> param = mCapabilityBindings.get(capability);
-        if (param == null || !param.containsKey(parameterName)) {
-            return Collections.emptyList();
+        return mCapabilityBindings.keySet().stream().map(Capability::new)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     *  Returns the {@link CapabilityParams} in associated with given capability.
+     *
+     *  @param capability {@link Capability} associated with the shortcut.
+     */
+    @NonNull
+    public List<CapabilityParams> getCapabilityParams(@NonNull final Capability capability) {
+        Objects.requireNonNull(capability);
+        if (mCapabilityBindings == null) {
+            return new ArrayList<>(0);
         }
-        return param.get(parameterName);
+        final Map<String, List<String>> param = mCapabilityBindings.get(capability.getName());
+        if (param == null) {
+            return new ArrayList<>(0);
+        }
+        final List<CapabilityParams> ret = new ArrayList<>(param.size());
+        for (String key : param.keySet()) {
+            final List<String> values = param.get(key);
+            final String primaryValue = values.get(0);
+            final List<String> aliases = values.size() == 1
+                    ? Collections.emptyList() : values.subList(1, values.size());
+            CapabilityParams.Builder builder = new CapabilityParams.Builder(key, primaryValue);
+            for (String alias : aliases) {
+                builder = builder.addAlias(alias);
+            }
+            ret.add(builder.build());
+        }
+        return ret;
     }
 
     private ShortcutInfo(Parcel source) {
@@ -2357,7 +2378,7 @@
             final Map<String, Map<String, List<String>>> capabilityBindings =
                     new ArrayMap<>(rawCapabilityBindings.size());
             rawCapabilityBindings.forEach(capabilityBindings::put);
-            mCapabilityBindings = capabilityBindings;
+            mCapabilityBindings = cloneCapabilityBindings(capabilityBindings);
         }
     }
 
@@ -2695,6 +2716,6 @@
         mPersons = persons;
         mLocusId = locusId;
         mStartingThemeResName = startingThemeResName;
-        mCapabilityBindings = capabilityBindings;
+        mCapabilityBindings = cloneCapabilityBindings(capabilityBindings);
     }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 8921fee..c8f809b6 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1827,9 +1827,9 @@
             ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
 
             final Map<String, Map<String, List<String>>> capabilityBindings =
-                    si.getCapabilityBindings();
+                    si.getCapabilityBindingsInternal();
             if (capabilityBindings != null && !capabilityBindings.isEmpty()) {
-                XmlUtils.writeMapXml(si.getCapabilityBindings(), NAME_CAPABILITY, out);
+                XmlUtils.writeMapXml(capabilityBindings, NAME_CAPABILITY, out);
             }
         }
 
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 99edecf..c786784 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -16,7 +16,6 @@
 package com.android.server.pm;
 
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual;
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -37,6 +36,8 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.LocusId;
+import android.content.pm.Capability;
+import android.content.pm.CapabilityParams;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.BitmapFactory;
@@ -258,10 +259,15 @@
                 .setLongLived(true)
                 .setExtras(pb)
                 .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
-                .addCapabilityBinding("action.intent.START_EXERCISE",
-                        "exercise.type", list("running", "jogging"))
-                .addCapabilityBinding("action.intent.START_EXERCISE",
-                        "exercise.duration", list("10m"))
+                .addCapabilityBinding(
+                        new Capability.Builder("action.intent.START_EXERCISE").build(),
+                        new CapabilityParams.Builder("exercise.type", "running")
+                                .addAlias("jogging")
+                                .build())
+                .addCapabilityBinding(
+                        new Capability.Builder("action.intent.START_EXERCISE").build(),
+                        new CapabilityParams.Builder("exercise.duration", "10m")
+                                .build())
                 .build();
         si.addFlags(ShortcutInfo.FLAG_PINNED);
         si.setBitmapPath("abc");
@@ -299,13 +305,14 @@
         assertEquals(null, si.getDisabledMessageResName());
         assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
                 si.getStartingThemeResName());
-        assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
-        assertFalse(si.hasCapability(""));
-        assertFalse(si.hasCapability("random"));
-        assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
-                "action.intent.START_EXERCISE", "exercise.type"));
-        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
-        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
+        assertEquals(list(new Capability.Builder("action.intent.START_EXERCISE").build()),
+                si.getCapabilities());
+        assertEquals(list(
+                        new CapabilityParams.Builder("exercise.type", "running")
+                                .addAlias("jogging").build(),
+                        new CapabilityParams.Builder("exercise.duration", "10m").build()),
+                si.getCapabilityParams(
+                        new Capability.Builder("action.intent.START_EXERCISE").build()));
     }
 
     public void testShortcutInfoParcel_resId() {
@@ -959,10 +966,15 @@
                 .setRank(123)
                 .setExtras(pb)
                 .setLocusId(new LocusId("1.2.3.4.5"))
-                .addCapabilityBinding("action.intent.START_EXERCISE",
-                        "exercise.type", list("running", "jogging"))
-                .addCapabilityBinding("action.intent.START_EXERCISE",
-                        "exercise.duration", list("10m"))
+                .addCapabilityBinding(
+                        new Capability.Builder("action.intent.START_EXERCISE").build(),
+                        new CapabilityParams.Builder("exercise.type", "running")
+                                .addAlias("jogging")
+                                .build())
+                .addCapabilityBinding(
+                        new Capability.Builder("action.intent.START_EXERCISE").build(),
+                        new CapabilityParams.Builder("exercise.duration", "10m")
+                                .build())
                 .build();
         sorig.setTimestamp(mInjectedCurrentTimeMillis);
 
@@ -1024,13 +1036,14 @@
         assertNull(si.getIconUri());
         assertTrue(si.getLastChangedTimestamp() < now);
 
-        assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
-        assertFalse(si.hasCapability(""));
-        assertFalse(si.hasCapability("random"));
-        assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
-                "action.intent.START_EXERCISE", "exercise.type"));
-        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
-        assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
+        assertEquals(list(new Capability.Builder("action.intent.START_EXERCISE").build()),
+                si.getCapabilities());
+        assertEquals(list(
+                        new CapabilityParams.Builder("exercise.type", "running")
+                                .addAlias("jogging").build(),
+                        new CapabilityParams.Builder("exercise.duration", "10m").build()),
+                si.getCapabilityParams(
+                        new Capability.Builder("action.intent.START_EXERCISE").build()));
 
         // Make sure ranks are saved too.  Because of the auto-adjusting, we need two shortcuts
         // to test it.