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();
}
}