Implement new API

This is the first swack at the new, 2-phase API. Adds the new methods
to the resolver service and makes the split name explicit on the
installer intent.

The 2nd phase will not yet be invoked; that's coming in a follow-on
change.

Bug: 25119046
Test: build & install the sample resolver and run 'adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://www.tripadvisor.com/Tourism-g33020-San_Jose_California-Vacations.html"'
Change-Id: I2df6fa64d46f17a86a2e32b19417632c594fb10f
diff --git a/api/system-current.txt b/api/system-current.txt
index 6622ea5..9aebe8f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4545,7 +4545,9 @@
     ctor public EphemeralResolverService();
     method public final void attachBaseContext(android.content.Context);
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
+    method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
+    method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralIntentFilter(int[]);
+    method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]);
     field public static final java.lang.String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
     field public static final java.lang.String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
   }
@@ -9189,6 +9191,7 @@
     field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
     field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
+    field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
     field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
@@ -9893,18 +9896,39 @@
     field public int reqTouchScreen;
   }
 
+  public final class EphemeralIntentFilter implements android.os.Parcelable {
+    ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
+    method public int describeContents();
+    method public java.util.List<android.content.IntentFilter> getFilters();
+    method public java.lang.String getSplitName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR;
+  }
+
   public final class EphemeralResolveInfo implements android.os.Parcelable {
-    ctor public EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+    ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+    ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
+    ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
     method public int describeContents();
     method public byte[] getDigestBytes();
     method public int getDigestPrefix();
-    method public java.util.List<android.content.IntentFilter> getFilters();
+    method public deprecated java.util.List<android.content.IntentFilter> getFilters();
+    method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
     method public java.lang.String getPackageName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
     field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
   }
 
+  public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable {
+    ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String);
+    method public int describeContents();
+    method public byte[][] getDigestBytes();
+    method public int[] getDigestPrefix();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR;
+  }
+
   public final class FeatureGroupInfo implements android.os.Parcelable {
     ctor public FeatureGroupInfo();
     ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
index ba79108..d8d7340 100644
--- a/core/java/android/app/EphemeralResolverService.java
+++ b/core/java/android/app/EphemeralResolverService.java
@@ -40,6 +40,7 @@
     public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
     public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
     private static final String EXTRA_PREFIX = "android.app.PREFIX";
+    private static final String EXTRA_HOSTNAME = "android.app.HOSTNAME";
     private Handler mHandler;
 
     /**
@@ -49,9 +50,29 @@
      * @param prefixMask A mask that was applied to each digest prefix. This should
      *      be used when comparing against the digest prefixes as all bits might
      *      not be set.
+     * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead
      */
+    @Deprecated
     public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
-            int digestPrefix[], int prefixMask);
+            int digestPrefix[], int prefix);
+
+    /**
+     * Called to retrieve resolve info for ephemeral applications.
+     *
+     * @param digestPrefix The hash prefix of the ephemeral's domain.
+     */
+    public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) {
+        return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000);
+    }
+
+    /**
+     * Called to retrieve intent filters for ephemeral applications.
+     *
+     * @param digestPrefix The hash prefix of the ephemeral's domain.
+     */
+    public List<EphemeralResolveInfo> onGetEphemeralIntentFilter(int digestPrefix[]) {
+        throw new IllegalStateException("Must define");
+    }
 
     @Override
     public final void attachBaseContext(Context base) {
@@ -64,9 +85,20 @@
         return new IEphemeralResolver.Stub() {
             @Override
             public void getEphemeralResolveInfoList(
-                    IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) {
+                    IRemoteCallback callback, int digestPrefix[], int sequence) {
                 final Message msg = mHandler.obtainMessage(
-                        ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback);
+                        ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, sequence, 0, callback);
+                final Bundle data = new Bundle();
+                data.putIntArray(EXTRA_PREFIX, digestPrefix);
+                msg.setData(data);
+                msg.sendToTarget();
+            }
+
+            @Override
+            public void getEphemeralIntentFilterList(
+                    IRemoteCallback callback, int digestPrefix[], int sequence) {
+                final Message msg = mHandler.obtainMessage(
+                        ServiceHandler.MSG_GET_EPHEMERAL_INTENT_FILTER, sequence, 0, callback);
                 final Bundle data = new Bundle();
                 data.putIntArray(EXTRA_PREFIX, digestPrefix);
                 msg.setData(data);
@@ -77,6 +109,7 @@
 
     private final class ServiceHandler extends Handler {
         public static final int MSG_GET_EPHEMERAL_RESOLVE_INFO = 1;
+        public static final int MSG_GET_EPHEMERAL_INTENT_FILTER = 2;
 
         public ServiceHandler(Looper looper) {
             super(looper, null /*callback*/, true /*async*/);
@@ -91,9 +124,23 @@
                     final IRemoteCallback callback = (IRemoteCallback) message.obj;
                     final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX);
                     final List<EphemeralResolveInfo> resolveInfo =
-                            onEphemeralResolveInfoList(digestPrefix, message.arg1);
+                            onGetEphemeralResolveInfo(digestPrefix);
                     final Bundle data = new Bundle();
-                    data.putInt(EXTRA_SEQUENCE, message.arg2);
+                    data.putInt(EXTRA_SEQUENCE, message.arg1);
+                    data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
+                    try {
+                        callback.sendResult(data);
+                    } catch (RemoteException e) {
+                    }
+                } break;
+
+                case MSG_GET_EPHEMERAL_INTENT_FILTER: {
+                    final IRemoteCallback callback = (IRemoteCallback) message.obj;
+                    final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX);
+                    final List<EphemeralResolveInfo> resolveInfo =
+                            onGetEphemeralIntentFilter(digestPrefix);
+                    final Bundle data = new Bundle();
+                    data.putInt(EXTRA_SEQUENCE, message.arg1);
                     data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
                     try {
                         callback.sendResult(data);
diff --git a/core/java/android/app/IEphemeralResolver.aidl b/core/java/android/app/IEphemeralResolver.aidl
index ee869ea..e0cef83 100644
--- a/core/java/android/app/IEphemeralResolver.aidl
+++ b/core/java/android/app/IEphemeralResolver.aidl
@@ -21,5 +21,8 @@
 /** @hide */
 oneway interface IEphemeralResolver {
     void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix,
-            int prefixMask, int sequence);
+            int sequence);
+
+    void getEphemeralIntentFilterList(IRemoteCallback callback, in int[] digestPrefix,
+            int sequence);
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9015c4b..a9e987a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1713,6 +1713,16 @@
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
+     * Intent extra: An app split name.
+     * <p>
+     * Type: String
+     * </p>
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+
+    /**
      * Intent extra: An extra for specifying whether a result is needed.
      * <p>
      * Type: boolean
diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java
new file mode 100644
index 0000000..0674e7c
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralIntentFilter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 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.annotation.SystemApi;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about an ephemeral application intent filter.
+ * @hide
+ */
+@SystemApi
+public final class EphemeralIntentFilter implements Parcelable {
+    private final String mSplitName;
+    /** The filters used to match domain */
+    private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+    public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
+        if (filters == null || filters.size() == 0) {
+            throw new IllegalArgumentException();
+        }
+        mSplitName = splitName;
+        mFilters.addAll(filters);
+    }
+
+    EphemeralIntentFilter(Parcel in) {
+        mSplitName = in.readString();
+        in.readList(mFilters, null /*loader*/);
+    }
+
+    public String getSplitName() {
+        return mSplitName;
+    }
+
+    public List<IntentFilter> getFilters() {
+        return mFilters;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSplitName);
+        out.writeList(mFilters);
+    }
+
+    public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
+            = new Parcelable.Creator<EphemeralIntentFilter>() {
+        public EphemeralIntentFilter createFromParcel(Parcel in) {
+            return new EphemeralIntentFilter(in);
+        }
+
+        public EphemeralIntentFilter[] newArray(int size) {
+            return new EphemeralIntentFilter[size];
+        }
+    };
+
+    /** @hide */
+    public static final class EphemeralResolveIntentInfo extends IntentFilter {
+        private final EphemeralIntentFilter mResolveInfo;
+
+        public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
+                @NonNull EphemeralIntentFilter resolveInfo) {
+            super(orig);
+            this.mResolveInfo = resolveInfo;
+        }
+
+        public EphemeralIntentFilter getEphemeralResolveInfo() {
+            return mResolveInfo;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
index fc3b958..3bed06b 100644
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.IntentFilter;
 import android.net.Uri;
@@ -41,27 +42,54 @@
     private final EphemeralDigest mDigest;
     private final String mPackageName;
     /** The filters used to match domain */
-    private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+    private final List<EphemeralIntentFilter> mFilters;
+    /** Filters only for legacy clients */
+    @Deprecated
+    private List<IntentFilter> mLegacyFilters;
 
+    @Deprecated
     public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
             @NonNull List<IntentFilter> filters) {
-        // validate arguments
-        if (uri == null
-                || packageName == null
-                || filters == null
-                || filters.size() == 0) {
+        if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
             throw new IllegalArgumentException();
         }
-
-        mDigest = new EphemeralDigest(uri, 0xFFFFFFFF, -1);
-        mFilters.addAll(filters);
+        mDigest = new EphemeralDigest(uri.getHost());
         mPackageName = packageName;
+        mFilters = new ArrayList<EphemeralIntentFilter>();
+        mFilters.add(new EphemeralIntentFilter(packageName, filters));
+        mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
+        mLegacyFilters.addAll(filters);
+    }
+
+    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
+            @Nullable List<EphemeralIntentFilter> filters) {
+        // validate arguments
+        if ((packageName == null && (filters != null && filters.size() != 0))
+                || (packageName != null && (filters == null || filters.size() == 0))) {
+            throw new IllegalArgumentException();
+        }
+        mDigest = digest;
+        if (filters != null) {
+            mFilters = new ArrayList<EphemeralIntentFilter>(filters.size());
+            mFilters.addAll(filters);
+        } else {
+            mFilters = null;
+        }
+        mPackageName = packageName;
+    }
+
+    public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
+            @Nullable List<EphemeralIntentFilter> filters) {
+        this(new EphemeralDigest(hostName), packageName, filters);
     }
 
     EphemeralResolveInfo(Parcel in) {
         mDigest = in.readParcelable(null /*loader*/);
         mPackageName = in.readString();
+        mFilters = new ArrayList<EphemeralIntentFilter>();
         in.readList(mFilters, null /*loader*/);
+        mLegacyFilters = new ArrayList<IntentFilter>();
+        in.readList(mLegacyFilters, null /*loader*/);
     }
 
     public byte[] getDigestBytes() {
@@ -76,7 +104,12 @@
         return mPackageName;
     }
 
+    @Deprecated
     public List<IntentFilter> getFilters() {
+        return mLegacyFilters;
+    }
+
+    public List<EphemeralIntentFilter> getIntentFilters() {
         return mFilters;
     }
 
@@ -90,6 +123,7 @@
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
+        out.writeList(mLegacyFilters);
     }
 
     public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
@@ -106,16 +140,23 @@
     /** @hide */
     public static final class EphemeralResolveIntentInfo extends IntentFilter {
         private final EphemeralResolveInfo mResolveInfo;
+        private final String mSplitName;
 
         public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
-                @NonNull EphemeralResolveInfo resolveInfo) {
+                @NonNull EphemeralResolveInfo resolveInfo,
+                @Nullable String splitName) {
             super(orig);
-            this.mResolveInfo = resolveInfo;
+            mResolveInfo = resolveInfo;
+            mSplitName = splitName;
         }
 
         public EphemeralResolveInfo getEphemeralResolveInfo() {
             return mResolveInfo;
         }
+
+        public String getSplitName() {
+            return mSplitName;
+        }
     }
 
     /**
@@ -129,17 +170,25 @@
      *
      * @hide
      */
+    @SystemApi
     public static final class EphemeralDigest implements Parcelable {
+        private static final int DIGEST_MASK = 0xfffff000;
+        private static final int DIGEST_PREFIX_COUNT = 5;
         /** Full digest of the domain hashes */
         private final byte[][] mDigestBytes;
         /** The first 4 bytes of the domain hashes */
         private final int[] mDigestPrefix;
 
-        public EphemeralDigest(@NonNull Uri uri, int digestMask, int maxDigests) {
-            if (uri == null) {
+        public EphemeralDigest(@NonNull String hostName) {
+            this(hostName, -1 /*maxDigests*/);
+        }
+
+        /** @hide */
+        public EphemeralDigest(@NonNull String hostName, int maxDigests) {
+            if (hostName == null) {
                 throw new IllegalArgumentException();
             }
-            mDigestBytes = generateDigest(uri, maxDigests);
+            mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), maxDigests);
             mDigestPrefix = new int[mDigestBytes.length];
             for (int i = 0; i < mDigestBytes.length; i++) {
                 mDigestPrefix[i] =
@@ -147,31 +196,32 @@
                                 | (mDigestBytes[i][1] & 0xFF) << 16
                                 | (mDigestBytes[i][2] & 0xFF) << 8
                                 | (mDigestBytes[i][3] & 0xFF) << 0)
-                        & digestMask;
+                        & DIGEST_MASK;
             }
         }
 
-        private static byte[][] generateDigest(Uri uri, int maxDigests) {
+        private static byte[][] generateDigest(String hostName, int maxDigests) {
             ArrayList<byte[]> digests = new ArrayList<>();
             try {
-                final String host = uri.getHost().toLowerCase(Locale.ENGLISH);
                 final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
                 if (maxDigests <= 0) {
-                    final byte[] hostBytes = host.getBytes();
+                    final byte[] hostBytes = hostName.getBytes();
                     digests.add(digest.digest(hostBytes));
                 } else {
-                    int prevDot = host.lastIndexOf('.');
-                    prevDot = host.lastIndexOf('.', prevDot - 1);
+                    int prevDot = hostName.lastIndexOf('.');
+                    prevDot = hostName.lastIndexOf('.', prevDot - 1);
                     // shortcut for short URLs
                     if (prevDot < 0) {
-                        digests.add(digest.digest(host.getBytes()));
+                        digests.add(digest.digest(hostName.getBytes()));
                     } else {
-                        byte[] hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+                        byte[] hostBytes =
+                                hostName.substring(prevDot + 1, hostName.length()).getBytes();
                         digests.add(digest.digest(hostBytes));
                         int digestCount = 1;
                         while (prevDot >= 0 && digestCount < maxDigests) {
-                            prevDot = host.lastIndexOf('.', prevDot - 1);
-                            hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+                            prevDot = hostName.lastIndexOf('.', prevDot - 1);
+                            hostBytes =
+                                    hostName.substring(prevDot + 1, hostName.length()).getBytes();
                             digests.add(digest.digest(hostBytes));
                             digestCount++;
                         }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index b5df4d7..86dbe8a 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.IntentFilter;
+import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -65,7 +66,7 @@
      * only be set in specific circumstances.
      * @hide
      */
-    public EphemeralResolveInfo ephemeralResolveInfo;
+    public EphemeralResolveIntentInfo ephemeralIntentInfo;
 
     /**
      * The IntentFilter that was matched for this ResolveInfo.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b905a0..22336c6 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8956,24 +8956,6 @@
         public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
 
         /**
-         * A mask applied to the ephemeral hash to generate the hash prefix.
-         * <p>
-         * Type: int
-         *
-         * @hide
-         */
-        public static final String EPHEMERAL_HASH_PREFIX_MASK = "ephemeral_hash_prefix_mask";
-
-        /**
-         * Number of hash prefixes to send during ephemeral resolution.
-         * <p>
-         * Type: int
-         *
-         * @hide
-         */
-        public static final String EPHEMERAL_HASH_PREFIX_COUNT = "ephemeral_hash_prefix_count";
-
-        /**
          * The duration for caching uninstalled ephemeral apps.
          * <p>
          * Type: long
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index e4ec169..2a0c6d0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -458,9 +458,12 @@
         // Instead, launch the ephemeral installer. Once the installer is finished, it
         // starts either the intent we resolved here [on install error] or the ephemeral
         // app [on install success].
-        if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
+        if (rInfo != null && rInfo.ephemeralIntentInfo != null) {
+            final String packageName =
+                    rInfo.ephemeralIntentInfo.getEphemeralResolveInfo().getPackageName();
+            final String splitName = rInfo.ephemeralIntentInfo.getSplitName();
             intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
-                    rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
+                    packageName, splitName, callingPackage, resolvedType,
                     userId);
             resolvedType = null;
             callingUid = realCallingUid;
@@ -524,7 +527,8 @@
      * Builds and returns an intent to launch the ephemeral installer.
      */
     private Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
-            String ephemeralPackage, String callingPackage, String resolvedType, int userId) {
+            String ephemeralPackageName, String ephemeralSplitName, String callingPackage,
+            String resolvedType, int userId) {
         final Intent nonEphemeralIntent = new Intent(origIntent);
         nonEphemeralIntent.setFlags(nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
         // Intent that is launched if the ephemeral package couldn't be installed
@@ -540,7 +544,7 @@
         if (USE_DEFAULT_EPHEMERAL_LAUNCHER) {
             // Force the intent to be directed to the ephemeral package
             ephemeralIntent = new Intent(origIntent);
-            ephemeralIntent.setPackage(ephemeralPackage);
+            ephemeralIntent.setPackage(ephemeralPackageName);
         } else {
             // Success intent goes back to the installer
             ephemeralIntent = new Intent(launchIntent);
@@ -564,7 +568,8 @@
                 | Intent.FLAG_ACTIVITY_CLEAR_TASK
                 | Intent.FLAG_ACTIVITY_NO_HISTORY
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackage);
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackageName);
+        intent.putExtra(Intent.EXTRA_SPLIT_NAME, ephemeralSplitName);
         intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureIntentTarget));
         intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(successIntentTarget));
         // TODO: Remove when the platform has fully implemented ephemeral apps
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index a6a3774..9778919 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -51,6 +51,8 @@
     private final Object mLock = new Object();
     private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
             new GetEphemeralResolveInfoCaller();
+    private final GetEphemeralIntentFilterCaller mGetEphemeralIntentFilterCaller =
+            new GetEphemeralIntentFilterCaller();
     private final ServiceConnection mServiceConnection = new MyServiceConnection();
     private final Context mContext;
     /** Intent used to bind to the service */
@@ -64,12 +66,26 @@
         mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName);
     }
 
-    public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
-            int hashPrefix[], int prefixMask) {
+    public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix[]) {
         throwIfCalledOnMainThread();
         try {
             return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
-                    getRemoteInstanceLazy(), hashPrefix, prefixMask);
+                    getRemoteInstanceLazy(), hashPrefix);
+        } catch (RemoteException re) {
+        } catch (TimeoutException te) {
+        } finally {
+            synchronized (mLock) {
+                mLock.notifyAll();
+            }
+        }
+        return null;
+    }
+
+    public final List<EphemeralResolveInfo> getEphemeralIntentFilterList(int digestPrefix[]) {
+        throwIfCalledOnMainThread();
+        try {
+            return mGetEphemeralIntentFilterCaller.getEphemeralIntentFilterList(
+                    getRemoteInstanceLazy(), digestPrefix);
         } catch (RemoteException re) {
         } catch (TimeoutException te) {
         } finally {
@@ -181,10 +197,38 @@
         }
 
         public List<EphemeralResolveInfo> getEphemeralResolveInfoList(
-                IEphemeralResolver target, int hashPrefix[], int prefixMask)
+                IEphemeralResolver target, int hashPrefix[])
                         throws RemoteException, TimeoutException {
             final int sequence = onBeforeRemoteCall();
-            target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence);
+            target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    private static final class GetEphemeralIntentFilterCaller
+            extends TimedRemoteCaller<List<EphemeralResolveInfo>> {
+        private final IRemoteCallback mCallback;
+
+        public GetEphemeralIntentFilterCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new IRemoteCallback.Stub() {
+                @Override
+                public void sendResult(Bundle data) throws RemoteException {
+                    final ArrayList<EphemeralResolveInfo> resolveList =
+                            data.getParcelableArrayList(
+                                    EphemeralResolverService.EXTRA_RESOLVE_INFO);
+                    int sequence =
+                            data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1);
+                    onRemoteMethodResult(resolveList, sequence);
+                }
+            };
+        }
+
+        public List<EphemeralResolveInfo> getEphemeralIntentFilterList(
+                IEphemeralResolver target, int digestPrefix[])
+                        throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.getEphemeralIntentFilterList(mCallback, digestPrefix, sequence);
             return getResultTimed(sequence);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b1f2a24..21b6964 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -122,6 +122,7 @@
 import android.content.pm.AppsQueryHelper;
 import android.content.pm.ComponentInfo;
 import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.EphemeralIntentFilter;
 import android.content.pm.EphemeralResolveInfo;
 import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
 import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
@@ -478,9 +479,6 @@
     private static final String VENDOR_OVERLAY_THEME_PERSIST_PROPERTY
             = "persist.vendor.overlay.theme";
 
-    private static int DEFAULT_EPHEMERAL_HASH_PREFIX_MASK = 0xFFFFF000;
-    private static int DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT = 5;
-
     /** Permission grant: not grant the permission. */
     private static final int GRANT_DENIED = 1;
 
@@ -4945,19 +4943,15 @@
         return true;
     }
 
-    private static EphemeralResolveInfo getEphemeralResolveInfo(
+    private static EphemeralResolveIntentInfo getEphemeralIntentInfo(
             Context context, EphemeralResolverConnection resolverConnection, Intent intent,
             String resolvedType, int userId, String packageName) {
-        final int ephemeralPrefixMask = Global.getInt(context.getContentResolver(),
-                Global.EPHEMERAL_HASH_PREFIX_MASK, DEFAULT_EPHEMERAL_HASH_PREFIX_MASK);
-        final int ephemeralPrefixCount = Global.getInt(context.getContentResolver(),
-                Global.EPHEMERAL_HASH_PREFIX_COUNT, DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT);
-        final EphemeralDigest digest = new EphemeralDigest(intent.getData(), ephemeralPrefixMask,
-                ephemeralPrefixCount);
+        final EphemeralDigest digest =
+                new EphemeralDigest(intent.getData().getHost(), 5 /*maxDigests*/);
         final int[] shaPrefix = digest.getDigestPrefix();
         final byte[][] digestBytes = digest.getDigestBytes();
         final List<EphemeralResolveInfo> ephemeralResolveInfoList =
-                resolverConnection.getEphemeralResolveInfoList(shaPrefix, ephemeralPrefixMask);
+                resolverConnection.getEphemeralResolveInfoList(shaPrefix);
         if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
             // No hash prefix match; there are no ephemeral apps for this domain.
             return null;
@@ -4969,9 +4963,10 @@
                 if (!Arrays.equals(digestBytes[i], ephemeralApplication.getDigestBytes())) {
                     continue;
                 }
-                final List<IntentFilter> filters = ephemeralApplication.getFilters();
+                final List<EphemeralIntentFilter> ephemeralFilters =
+                        ephemeralApplication.getIntentFilters();
                 // No filters; this should never happen.
-                if (filters.isEmpty()) {
+                if (ephemeralFilters.isEmpty()) {
                     continue;
                 }
                 if (packageName != null
@@ -4980,13 +4975,21 @@
                 }
                 // We have a domain match; resolve the filters to see if anything matches.
                 final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
-                for (int j = filters.size() - 1; j >= 0; --j) {
-                    final EphemeralResolveIntentInfo intentInfo =
-                            new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
-                    ephemeralResolver.addFilter(intentInfo);
+                for (int j = ephemeralFilters.size() - 1; j >= 0; --j) {
+                    final EphemeralIntentFilter ephemeralFilter = ephemeralFilters.get(j);
+                    final List<IntentFilter> splitFilters = ephemeralFilter.getFilters();
+                    if (splitFilters == null || splitFilters.isEmpty()) {
+                        continue;
+                    }
+                    for (int k = splitFilters.size() - 1; k >= 0; --k) {
+                        final EphemeralResolveIntentInfo intentInfo =
+                                new EphemeralResolveIntentInfo(splitFilters.get(k),
+                                        ephemeralApplication, ephemeralFilter.getSplitName());
+                        ephemeralResolver.addFilter(intentInfo);
+                    }
                 }
-                List<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
-                        intent, resolvedType, false /*defaultOnly*/, userId);
+                List<EphemeralResolveIntentInfo> matchedResolveInfoList = ephemeralResolver
+                        .queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
                 if (!matchedResolveInfoList.isEmpty()) {
                     return matchedResolveInfoList.get(0);
                 }
@@ -5469,15 +5472,15 @@
         }
         if (addEphemeral) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
-            final EphemeralResolveInfo ai = getEphemeralResolveInfo(
+            final EphemeralResolveIntentInfo intentInfo = getEphemeralIntentInfo(
                     mContext, mEphemeralResolverConnection, intent, resolvedType, userId,
                     matchEphemeralPackage ? pkgName : null);
-            if (ai != null) {
+            if (intentInfo != null) {
                 if (DEBUG_EPHEMERAL) {
                     Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                 }
                 final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
-                ephemeralInstaller.ephemeralResolveInfo = ai;
+                ephemeralInstaller.ephemeralIntentInfo = intentInfo;
                 // make sure this resolver is the default
                 ephemeralInstaller.isDefault = true;
                 ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -11451,7 +11454,7 @@
     }
 
     private static final class EphemeralIntentResolver
-            extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
+            extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveIntentInfo> {
         /**
          * The result that has the highest defined order. Ordering applies on a
          * per-package basis. Mapping is from package name to Pair of order and
@@ -11476,7 +11479,7 @@
         }
 
         @Override
-        protected EphemeralResolveInfo newResult(EphemeralResolveIntentInfo info, int match,
+        protected EphemeralResolveIntentInfo newResult(EphemeralResolveIntentInfo info, int match,
                 int userId) {
             if (!sUserManager.exists(userId)) {
                 return null;
@@ -11494,18 +11497,18 @@
                 // non-zero order, enable ordering
                 mOrderResult.put(packageName, new Pair<>(order, res));
             }
-            return res;
+            return info;
         }
 
         @Override
-        protected void filterResults(List<EphemeralResolveInfo> results) {
+        protected void filterResults(List<EphemeralResolveIntentInfo> results) {
             // only do work if ordering is enabled [most of the time it won't be]
             if (mOrderResult.size() == 0) {
                 return;
             }
             int resultSize = results.size();
             for (int i = 0; i < resultSize; i++) {
-                final EphemeralResolveInfo info = results.get(i);
+                final EphemeralResolveInfo info = results.get(i).getEphemeralResolveInfo();
                 final String packageName = info.getPackageName();
                 final Pair<Integer, EphemeralResolveInfo> savedInfo = mOrderResult.get(packageName);
                 if (savedInfo == null) {