diff options
| -rw-r--r-- | api/system-current.txt | 14 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 14 | ||||
| -rw-r--r-- | core/java/android/content/pm/EphemeralResolveInfo.java (renamed from core/java/com/android/internal/app/EphemeralResolveInfo.java) | 92 | ||||
| -rw-r--r-- | core/java/android/content/pm/ResolveInfo.java | 13 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/EphemeralResolverService.java | 3 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 13 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStack.java | 7 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStackSupervisor.java | 159 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/EphemeralResolverConnection.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 151 |
10 files changed, 303 insertions, 165 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index ee975d1b2fe2..3b585146074e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8701,6 +8701,8 @@ package android.content { 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 @@ package android.content.pm { 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 a27d1cb845c8..a9589537193b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3687,6 +3687,20 @@ public class Intent implements Parcelable, Cloneable { 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/com/android/internal/app/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java index 0e7ef05dd150..afb4c30d6a99 100644 --- a/core/java/com/android/internal/app/EphemeralResolveInfo.java +++ b/core/java/android/content/pm/EphemeralResolveInfo.java @@ -14,8 +14,10 @@ * limitations under the License. */ -package com.android.internal.app; +package android.content.pm; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.content.IntentFilter; import android.net.Uri; import android.os.Parcel; @@ -27,22 +29,47 @@ import java.util.ArrayList; import java.util.List; /** - * Information that is returned when resolving ephemeral - * applications. + * 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"; - private byte[] mDigestBytes; - private int mDigestPrefix; + + /** 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(Uri uri, List<IntentFilter> filters) { - generateDigest(uri); + 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; } - private EphemeralResolveInfo(Parcel in) { - readFromParcel(in); + EphemeralResolveInfo(Parcel in) { + mDigestBytes = in.createByteArray(); + mDigestPrefix = in.readInt(); + mPackageName = in.readString(); + in.readList(mFilters, null /*loader*/); } public byte[] getDigestBytes() { @@ -53,21 +80,19 @@ public final class EphemeralResolveInfo implements Parcelable { return mDigestPrefix; } + public String getPackageName() { + return mPackageName; + } + public List<IntentFilter> getFilters() { return mFilters; } - private void generateDigest(Uri uri) { + private static byte[] 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; + return digest.digest(hostBytes); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("could not find digest algorithm"); } @@ -80,26 +105,12 @@ public final class EphemeralResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - if (mDigestBytes == null) { - out.writeInt(0); - } else { - out.writeInt(mDigestBytes.length); - out.writeByteArray(mDigestBytes); - } + out.writeByteArray(mDigestBytes); out.writeInt(mDigestPrefix); + out.writeString(mPackageName); 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) { @@ -110,4 +121,19 @@ public final class EphemeralResolveInfo implements Parcelable { 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 a5fb451db61d..d5d300760c8a 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -61,6 +61,19 @@ public class ResolveInfo implements Parcelable { 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/EphemeralResolverService.java b/core/java/com/android/internal/app/EphemeralResolverService.java index 65530f270128..6ba04a98aa2a 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 @@ import java.util.List; * 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 67596f7f4ad9..1381dfd20994 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3676,9 +3676,9 @@ public final class ActivityManagerService extends ActivityManagerNative 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); } } } @@ -4287,9 +4287,10 @@ public final class ActivityManagerService extends ActivityManagerNative 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 0d645d0469fa..bfd17b2e1bf9 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 @@ final class ActivityStack { 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 e1bc580ab354..f6156139af6b 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { "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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + // 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 628ad0ebea41..fe6fb1fd7792 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.ComponentName; 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.os.UserHandle; 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 e0f85c5e956b..cad3b3f8b15d 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.ServiceConnection; 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.UserInfo; 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.io.IoUtils; 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { + 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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(); } } |