Fix ephemeral post-install launching

Provide the ephemeral installer with some additional pieces of information:
1) instead of de-referencing the URL a second time, give the installer the
   exact package name
2) instead of relying on ephemeral apps to define verified links, give the
   installer a pending intent to launch when the ephemeral is installed
3) give the installer a pending intent to launch if the installer fails,
   for whatever reason, to install the ephemeral app

Bug: 25119046
Change-Id: I45f50481caee09d5d09451e4b2492e64b0faae82
diff --git a/api/system-current.txt b/api/system-current.txt
index ee975d1..3b58514 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8701,6 +8701,8 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
+    field public static final java.lang.String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
     field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
@@ -9449,6 +9451,18 @@
     field protected static final java.lang.String TAG = "ContainerEncryptionParams";
   }
 
+  public final class EphemeralResolveInfo implements android.os.Parcelable {
+    ctor public EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+    method public int describeContents();
+    method public byte[] getDigestBytes();
+    method public int getDigestPrefix();
+    method public java.util.List<android.content.IntentFilter> getFilters();
+    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 final class FeatureGroupInfo implements android.os.Parcelable {
     ctor public FeatureGroupInfo();
     ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a27d1cb..a958953 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3687,6 +3687,20 @@
     public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
 
     /**
+     * A {@link IntentSender} to start after ephemeral installation success.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
+
+    /**
+     * A {@link IntentSender} to start after ephemeral installation failure.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
+
+    /**
      * A Bundle forming a mapping of potential target package names to different extras Bundles
      * to add to the default intent extras in {@link #EXTRA_INTENT} when used with
      * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
new file mode 100644
index 0000000..afb4c30
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 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.SystemApi;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about an ephemeral application.
+ * @hide
+ */
+@SystemApi
+public final class EphemeralResolveInfo implements Parcelable {
+    /** Algorithm that will be used to generate the domain digest */
+    public static final String SHA_ALGORITHM = "SHA-256";
+
+    /** Full digest of the domain hash */
+    private final byte[] mDigestBytes;
+    /** The first 4 bytes of the domain hash */
+    private final int mDigestPrefix;
+    private final String mPackageName;
+    /** The filters used to match domain */
+    private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+    public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
+            @NonNull List<IntentFilter> filters) {
+        // validate arguments
+        if (uri == null
+                || packageName == null
+                || filters == null
+                || filters.size() == 0) {
+            throw new IllegalArgumentException();
+        }
+
+        mDigestBytes = generateDigest(uri);
+        mDigestPrefix =
+                mDigestBytes[0] << 24
+                | mDigestBytes[1] << 16
+                | mDigestBytes[2] << 8
+                | mDigestBytes[3] << 0;
+        mFilters.addAll(filters);
+        mPackageName = packageName;
+    }
+
+    EphemeralResolveInfo(Parcel in) {
+        mDigestBytes = in.createByteArray();
+        mDigestPrefix = in.readInt();
+        mPackageName = in.readString();
+        in.readList(mFilters, null /*loader*/);
+    }
+
+    public byte[] getDigestBytes() {
+        return mDigestBytes;
+    }
+
+    public int getDigestPrefix() {
+        return mDigestPrefix;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public List<IntentFilter> getFilters() {
+        return mFilters;
+    }
+
+    private static byte[] generateDigest(Uri uri) {
+        try {
+            final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
+            final byte[] hostBytes = uri.getHost().getBytes();
+            return digest.digest(hostBytes);
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("could not find digest algorithm");
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(mDigestBytes);
+        out.writeInt(mDigestPrefix);
+        out.writeString(mPackageName);
+        out.writeList(mFilters);
+    }
+
+    public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
+            = new Parcelable.Creator<EphemeralResolveInfo>() {
+        public EphemeralResolveInfo createFromParcel(Parcel in) {
+            return new EphemeralResolveInfo(in);
+        }
+
+        public EphemeralResolveInfo[] newArray(int size) {
+            return new EphemeralResolveInfo[size];
+        }
+    };
+
+    /** @hide */
+    public static final class EphemeralResolveIntentInfo extends IntentFilter {
+        private final EphemeralResolveInfo mResolveInfo;
+
+        public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
+                @NonNull EphemeralResolveInfo resolveInfo) {
+            super(orig);
+            this.mResolveInfo = resolveInfo;
+        }
+
+        public EphemeralResolveInfo getEphemeralResolveInfo() {
+            return mResolveInfo;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index a5fb451..d5d3007 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -61,6 +61,19 @@
     public ProviderInfo providerInfo;
 
     /**
+     * The ephemeral application that corresponds to this resolution match. This will
+     * only be set in specific circumstances.
+     * @hide
+     */
+    public EphemeralResolveInfo ephemeralResolveInfo;
+
+    /**
+     * A ResolveInfo that points at the ephemeral installer.
+     * @hide
+     */
+    public ResolveInfo ephemeralInstaller;
+
+    /**
      * The IntentFilter that was matched for this ResolveInfo.
      */
     public IntentFilter filter;
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.java b/core/java/com/android/internal/app/EphemeralResolveInfo.java
deleted file mode 100644
index 0e7ef05..0000000
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.app;
-
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information that is returned when resolving ephemeral
- * applications.
- */
-public final class EphemeralResolveInfo implements Parcelable {
-    public static final String SHA_ALGORITHM = "SHA-256";
-    private byte[] mDigestBytes;
-    private int mDigestPrefix;
-    private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
-
-    public EphemeralResolveInfo(Uri uri, List<IntentFilter> filters) {
-        generateDigest(uri);
-        mFilters.addAll(filters);
-    }
-
-    private EphemeralResolveInfo(Parcel in) {
-        readFromParcel(in);
-    }
-
-    public byte[] getDigestBytes() {
-        return mDigestBytes;
-    }
-
-    public int getDigestPrefix() {
-        return mDigestPrefix;
-    }
-
-    public List<IntentFilter> getFilters() {
-        return mFilters;
-    }
-
-    private void generateDigest(Uri uri) {
-        try {
-            final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
-            final byte[] hostBytes = uri.getHost().getBytes();
-            final byte[] digestBytes = digest.digest(hostBytes);
-            mDigestBytes = digestBytes;
-            mDigestPrefix =
-                    digestBytes[0] << 24
-                    | digestBytes[1] << 16
-                    | digestBytes[2] << 8
-                    | digestBytes[3] << 0;
-        } catch (NoSuchAlgorithmException e) {
-            throw new IllegalStateException("could not find digest algorithm");
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        if (mDigestBytes == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(mDigestBytes.length);
-            out.writeByteArray(mDigestBytes);
-        }
-        out.writeInt(mDigestPrefix);
-        out.writeList(mFilters);
-    }
-
-    private void readFromParcel(Parcel in) {
-        int digestBytesSize = in.readInt();
-        if (digestBytesSize > 0) {
-            mDigestBytes = new byte[digestBytesSize];
-            in.readByteArray(mDigestBytes);
-        }
-        mDigestPrefix = in.readInt();
-        in.readList(mFilters, null /*loader*/);
-    }
-
-    public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
-            = new Parcelable.Creator<EphemeralResolveInfo>() {
-        public EphemeralResolveInfo createFromParcel(Parcel in) {
-            return new EphemeralResolveInfo(in);
-        }
-
-        public EphemeralResolveInfo[] newArray(int size) {
-            return new EphemeralResolveInfo[size];
-        }
-    };
-}
diff --git a/core/java/com/android/internal/app/EphemeralResolverService.java b/core/java/com/android/internal/app/EphemeralResolverService.java
index 65530f2..6ba04a9 100644
--- a/core/java/com/android/internal/app/EphemeralResolverService.java
+++ b/core/java/com/android/internal/app/EphemeralResolverService.java
@@ -16,9 +16,11 @@
 
 package com.android.internal.app;
 
+import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.EphemeralResolveInfo;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -33,6 +35,7 @@
  * Base class for implementing the resolver service.
  * @hide
  */
+@SystemApi
 public abstract class EphemeralResolverService extends Service {
     public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO";
     public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90a5d3f..d2fc602 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3674,9 +3674,9 @@
                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
-                    mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
-                            null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, false,
-                            null, null, null);
+                    mStackSupervisor.startActivityLocked(null, intent, null /*ephemeralIntent*/,
+                            null, ri.activityInfo, null /*rInfo*/, null, null, null, null, 0, 0, 0,
+                            null, 0, 0, 0, null, false, false, null, null, null);
                 }
             }
         }
@@ -4285,9 +4285,10 @@
 
             final long origId = Binder.clearCallingIdentity();
             int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
-                    r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
-                    resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage,
-                    -1, r.launchedFromUid, 0, options, false, false, null, null, null);
+                    null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
+                    null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
+                    r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
+                    false, false, null, null, null);
             Binder.restoreCallingIdentity(origId);
 
             r.finishing = wasFinishing;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ffa6a4d..3f62cbc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3349,9 +3349,10 @@
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
                     int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
-                            null, aInfo, null, null, parent.appToken, null,
-                            0, -1, parent.launchedFromUid, parent.launchedFromPackage,
-                            -1, parent.launchedFromUid, 0, null, false, true, null, null, null);
+                            null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
+                            parent.appToken, null, 0, -1, parent.launchedFromUid,
+                            parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
+                            false, true, null, null, null);
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
                 } catch (RemoteException e) {
                     foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e1bc580..f615613 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -920,21 +920,9 @@
         }
     }
 
-    ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
-            ProfilerInfo profilerInfo, int userId) {
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo;
-        try {
-            ResolveInfo rInfo =
-                AppGlobals.getPackageManager().resolveIntent(
-                        intent, resolvedType,
-                        PackageManager.MATCH_DEFAULT_ONLY
-                                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
-            aInfo = rInfo != null ? rInfo.activityInfo : null;
-        } catch (RemoteException e) {
-            aInfo = null;
-        }
-
+    ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
+            ProfilerInfo profilerInfo) {
+        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
         if (aInfo != null) {
             // Store the found target back into the intent, because now that
             // we have it we never want to do this again.  For example, if the
@@ -961,15 +949,31 @@
         return aInfo;
     }
 
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+        try {
+            return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
+                            PackageManager.MATCH_DEFAULT_ONLY
+                            | ActivityManagerService.STOCK_PM_FLAGS, userId);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
+            ProfilerInfo profilerInfo, int userId) {
+        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
+        return resolveActivity(intent, rInfo, startFlags, profilerInfo);
+    }
+
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
         moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
-        startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,
-                null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,
-                null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,
-                null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,
-                0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,
-                false /* componentSpecified */,
-                null /* outActivity */, null /* container */,  null /* inTask */);
+        startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
+                null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
+                null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
+                0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
+                0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
+                false /*ignoreTargetSecurity*/, false /*componentSpecified*/,  null /*outActivity*/,
+                null /*container*/, null /*inTask*/);
         if (inResumeTopActivity) {
             // If we are in resume section already, home activity will be initialized, but not
             // resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -991,12 +995,14 @@
         }
         boolean componentSpecified = intent.getComponent() != null;
 
+        // Save a copy in case ephemeral needs it
+        final Intent ephemeralIntent = new Intent(intent);
         // Don't modify the client's object!
         intent = new Intent(intent);
 
+        ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
         // Collect information about the target of the Intent.
-        ActivityInfo aInfo =
-                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
+        ActivityInfo aInfo = resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
         ActivityOptions options = ActivityOptions.fromBundle(bOptions);
         ActivityContainer container = (ActivityContainer)iContainer;
@@ -1084,26 +1090,20 @@
                         callingUid = Binder.getCallingUid();
                         callingPid = Binder.getCallingPid();
                         componentSpecified = true;
-                        try {
-                            ResolveInfo rInfo =
-                                AppGlobals.getPackageManager().resolveIntent(
-                                        intent, null,
-                                        PackageManager.MATCH_DEFAULT_ONLY
-                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
-                            aInfo = rInfo != null ? rInfo.activityInfo : null;
+                        rInfo = resolveIntent(intent, null /*resolvedType*/, userId);
+                        aInfo = rInfo != null ? rInfo.activityInfo : null;
+                        if (aInfo != null) {
                             aInfo = mService.getActivityInfoForUser(aInfo, userId);
-                        } catch (RemoteException e) {
-                            aInfo = null;
                         }
                     }
                 }
             }
 
-            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
-                    voiceSession, voiceInteractor, resultTo, resultWho,
-                    requestCode, callingPid, callingUid, callingPackage,
-                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
-                    componentSpecified, null, container, inTask);
+            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+                    aInfo, rInfo, voiceSession, voiceInteractor,
+                    resultTo, resultWho, requestCode, callingPid,
+                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+                    options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -1211,10 +1211,10 @@
 
                     ActivityOptions options = ActivityOptions.fromBundle(
                             i == intents.length - 1 ? bOptions : null);
-                    int res = startActivityLocked(caller, intent, resolvedTypes[i],
-                            aInfo, null, null, resultTo, null, -1, callingPid, callingUid,
-                            callingPackage, callingPid, callingUid,
-                            0, options, false, componentSpecified, outActivity, null, null);
+                    int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
+                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
+                            callingPid, callingUid, callingPackage, callingPid, callingUid, 0,
+                            options, false, componentSpecified, outActivity, null, null);
                     if (res < 0) {
                         return res;
                     }
@@ -1448,14 +1448,13 @@
                 "activity", r.intent.getComponent(), false, false, true);
     }
 
-    final int startActivityLocked(IApplicationThread caller,
-            Intent intent, String resolvedType, ActivityInfo aInfo,
+    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode,
-            int callingPid, int callingUid, String callingPackage,
-            int realCallingPid, int realCallingUid, int startFlags, ActivityOptions options,
-            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
-            ActivityContainer container, TaskRecord inTask) {
+            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+            ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) {
         int err = ActivityManager.START_SUCCESS;
 
         ProcessRecord callerApp = null;
@@ -1625,23 +1624,23 @@
                     new String[]{ resolvedType },
                     PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
                             | PendingIntent.FLAG_IMMUTABLE, null);
-            int flags = intent.getFlags();
-            Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
+            final int flags = intent.getFlags();
+            final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
             if (newIntent != null) {
                 intent = newIntent;
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                intent.setFlags(flags
+                        | Intent.FLAG_ACTIVITY_NEW_TASK
                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                 intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-                intent.setFlags(flags);
 
                 resolvedType = null;
                 callingUid = realCallingUid;
                 callingPid = realCallingPid;
 
                 UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
-                aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY
-                        | ActivityManagerService.STOCK_PM_FLAGS, null, parent.id);
+                rInfo = resolveIntent(intent, resolvedType, parent.id);
+                aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
             }
         }
 
@@ -1668,22 +1667,23 @@
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
 
+                final int flags = intent.getFlags();
                 Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                newIntent.setFlags(flags
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                 newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                 if (resultRecord != null) {
                     newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
                 }
-                newIntent.setFlags(intent.getFlags());
                 intent = newIntent;
 
                 resolvedType = null;
                 callingUid = realCallingUid;
                 callingPid = realCallingPid;
 
-                aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY
-                        | ActivityManagerService.STOCK_PM_FLAGS, null, userId);
+                rInfo = resolveIntent(intent, resolvedType, userId);
+                aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
@@ -1696,6 +1696,47 @@
             }
         }
 
+        // If we have an ephemeral app, abort the process of launching the resolved intent.
+        // 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) {
+            // Create a pending intent to start the intent resolved here.
+            final IIntentSender failureTarget = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+                    new String[]{ resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+
+            // Create a pending intent to start the ephemeral application; force it to be
+            // directed to the ephemeral package.
+            ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
+            final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
+                    new String[]{ resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                    | PendingIntent.FLAG_IMMUTABLE, null);
+
+            int flags = intent.getFlags();
+            intent = new Intent();
+            intent.setFlags(flags
+                    | Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
+                    rInfo.ephemeralResolveInfo.getPackageName());
+            intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
+            intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
+
+            resolvedType = null;
+            callingUid = realCallingUid;
+            callingPid = realCallingPid;
+
+            rInfo = rInfo.ephemeralInstaller;
+            aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+        }
+
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                 intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                 requestCode, componentSpecified, voiceSession != null, this, container, options);
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 628ad0e..fe6fb1f 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.EphemeralResolveInfo;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -30,7 +31,6 @@
 import android.util.TimedRemoteCaller;
 
 import com.android.internal.app.EphemeralResolverService;
-import com.android.internal.app.EphemeralResolveInfo;
 import com.android.internal.app.IEphemeralResolver;
 
 import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0f85c5..cad3b3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,6 +105,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AppsQueryHelper;
+import android.content.pm.EphemeralResolveInfo;
 import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
@@ -144,6 +145,7 @@
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
+import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
@@ -210,7 +212,7 @@
 import libcore.util.EmptyArray;
 
 import com.android.internal.R;
-import com.android.internal.app.EphemeralResolveInfo;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.NativeLibraryHelper;
@@ -4418,7 +4420,21 @@
         flags = augmentFlagsForUser(flags, userId);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
         List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
-        return chooseBestActivity(intent, resolvedType, flags, query, userId);
+        final ResolveInfo bestChoice =
+                chooseBestActivity(intent, resolvedType, flags, query, userId);
+
+        if (isEphemeralAllowed(intent, query, userId)) {
+            final EphemeralResolveInfo ai =
+                    getEphemeralResolveInfo(intent, resolvedType, userId);
+            if (ai != null) {
+                if (DEBUG_EPHEMERAL) {
+                    Slog.v(TAG, "Returning an EphemeralResolveInfo");
+                }
+                bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
+                bestChoice.ephemeralResolveInfo = ai;
+            }
+        }
+        return bestChoice;
     }
 
     @Override
@@ -4453,13 +4469,61 @@
                 false, false, false, userId);
     }
 
-    private boolean isEphemeralAvailable(Intent intent, String resolvedType, int userId) {
+
+    private boolean isEphemeralAllowed(
+            Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
+        // Short circuit and return early if possible.
+        final int callingUser = UserHandle.getCallingUserId();
+        if (callingUser != UserHandle.USER_SYSTEM) {
+            return false;
+        }
+        if (mEphemeralResolverConnection == null) {
+            return false;
+        }
+        if (intent.getComponent() != null) {
+            return false;
+        }
+        if (intent.getPackage() != null) {
+            return false;
+        }
+        final boolean isWebUri = hasWebURI(intent);
+        if (!isWebUri) {
+            return false;
+        }
+        // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
+        synchronized (mPackages) {
+            final int count = resolvedActivites.size();
+            for (int n = 0; n < count; n++) {
+                ResolveInfo info = resolvedActivites.get(n);
+                String packageName = info.activityInfo.packageName;
+                PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (ps != null) {
+                    // Try to get the status from User settings first
+                    long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+                    int status = (int) (packedStatus >> 32);
+                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+                            || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+                        if (DEBUG_EPHEMERAL) {
+                            Slog.v(TAG, "DENY ephemeral apps;"
+                                + " pkg: " + packageName + ", status: " + status);
+                        }
+                        return false;
+                    }
+                }
+            }
+        }
+        // We've exhausted all ways to deny ephemeral application; let the system look for them.
+        return true;
+    }
+
+    private EphemeralResolveInfo getEphemeralResolveInfo(Intent intent, String resolvedType,
+            int userId) {
         MessageDigest digest = null;
         try {
             digest = MessageDigest.getInstance(EphemeralResolveInfo.SHA_ALGORITHM);
         } catch (NoSuchAlgorithmException e) {
             // If we can't create a digest, ignore ephemeral apps.
-            return false;
+            return null;
         }
 
         final byte[] hostBytes = intent.getData().getHost().getBytes();
@@ -4473,7 +4537,7 @@
                 mEphemeralResolverConnection.getEphemeralResolveInfoList(shaPrefix);
         if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
             // No hash prefix match; there are no ephemeral apps for this domain.
-            return false;
+            return null;
         }
         for (int i = ephemeralResolveInfoList.size() - 1; i >= 0; --i) {
             EphemeralResolveInfo ephemeralApplication = ephemeralResolveInfoList.get(i);
@@ -4488,62 +4552,22 @@
             // 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) {
-                ephemeralResolver.addFilter(filters.get(j));
+                final EphemeralResolveIntentInfo intentInfo =
+                        new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
+                ephemeralResolver.addFilter(intentInfo);
             }
-            List<ResolveInfo> ephemeralResolveList = ephemeralResolver.queryIntent(
+            List<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
                     intent, resolvedType, false /*defaultOnly*/, userId);
-            return !ephemeralResolveList.isEmpty();
+            if (!matchedResolveInfoList.isEmpty()) {
+                return matchedResolveInfoList.get(0);
+            }
         }
         // Hash or filter mis-match; no ephemeral apps for this domain.
-        return false;
+        return null;
     }
 
     private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
             int flags, List<ResolveInfo> query, int userId) {
-        final boolean isWebUri = hasWebURI(intent);
-        // Check whether or not an ephemeral app exists to handle the URI.
-        if (isWebUri && mEphemeralResolverConnection != null) {
-            // Deny ephemeral apps if the user choose _ALWAYS or _ALWAYS_ASK for intent resolution.
-            boolean hasAlwaysHandler = false;
-            synchronized (mPackages) {
-                final int count = query.size();
-                for (int n=0; n<count; n++) {
-                    ResolveInfo info = query.get(n);
-                    String packageName = info.activityInfo.packageName;
-                    PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps != null) {
-                        // Try to get the status from User settings first
-                        long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                        int status = (int) (packedStatus >> 32);
-                        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
-                                || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
-                            hasAlwaysHandler = true;
-                            break;
-                        }
-                    }
-                }
-            }
-
-            // Only consider installing an ephemeral app if there isn't already a verified handler.
-            // We've determined that there's an ephemeral app available for the URI, ignore any
-            // ResolveInfo's and just return the ephemeral installer
-            if (!hasAlwaysHandler && isEphemeralAvailable(intent, resolvedType, userId)) {
-                if (DEBUG_EPHEMERAL) {
-                    Slog.v(TAG, "Resolving to the ephemeral installer");
-                }
-                // ditch the result and return a ResolveInfo to launch the ephemeral installer
-                ResolveInfo ri = new ResolveInfo(mEphemeralInstallerInfo);
-                ri.activityInfo = new ActivityInfo(ri.activityInfo);
-                // make a deep copy of the applicationInfo
-                ri.activityInfo.applicationInfo = new ApplicationInfo(
-                        ri.activityInfo.applicationInfo);
-                if (userId != 0) {
-                    ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
-                            UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
-                }
-                return ri;
-            }
-        }
         if (query != null) {
             final int N = query.size();
             if (N == 1) {
@@ -4559,7 +4583,7 @@
                             + r1.activityInfo.name + "=" + r1.priority);
                 }
                 // If the first activity has a higher priority, or a different
-                // default, then it is always desireable to pick it.
+                // default, then it is always desirable to pick it.
                 if (r0.priority != r1.priority
                         || r0.preferredOrder != r1.preferredOrder
                         || r0.isDefault != r1.isDefault) {
@@ -9737,23 +9761,24 @@
     }
 
     private static final class EphemeralIntentResolver
-            extends IntentResolver<IntentFilter, ResolveInfo> {
+            extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
         @Override
-        protected IntentFilter[] newArray(int size) {
-            return new IntentFilter[size];
+        protected EphemeralResolveIntentInfo[] newArray(int size) {
+            return new EphemeralResolveIntentInfo[size];
         }
 
         @Override
-        protected boolean isPackageForFilter(String packageName, IntentFilter info) {
+        protected boolean isPackageForFilter(String packageName, EphemeralResolveIntentInfo info) {
             return true;
         }
 
         @Override
-        protected ResolveInfo newResult(IntentFilter info, int match, int userId) {
-            if (!sUserManager.exists(userId)) return null;
-            final ResolveInfo res = new ResolveInfo();
-            res.filter = info;
-            return res;
+        protected EphemeralResolveInfo newResult(EphemeralResolveIntentInfo info, int match,
+                int userId) {
+            if (!sUserManager.exists(userId)) {
+                return null;
+            }
+            return info.getEphemeralResolveInfo();
         }
     }