From e991022423c2e5b4386553af7ef3b54da7c54be1 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Tue, 21 Feb 2017 16:00:11 -0800 Subject: [PATCH] Load splits on-demand A split may be declared in an application's base manifest, but, defined in a feature split. When resolving such a component, invoke the installer to download and install the necessary split(s) At the moment, this only works for instant apps. However, the implementation is generic and could be applied to any application. Bug: 25119046 Test: cts-tradefed run commandAndExit cts-dev -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.EphemeralTest Change-Id: I6598abb34becfd049fc03199813226736e5057b1 --- ...eralResponse.java => AuxiliaryResolveInfo.java} | 33 ++++- core/java/android/content/pm/EphemeralRequest.java | 9 +- .../android/content/pm/PackageManagerInternal.java | 5 +- core/java/android/content/pm/ResolveInfo.java | 7 +- .../com/android/server/am/ActivityStarter.java | 37 ++--- .../com/android/server/pm/EphemeralResolver.java | 71 +++++----- .../server/pm/EphemeralResolverConnection.java | 1 - .../android/server/pm/PackageManagerService.java | 156 ++++++++++++--------- 8 files changed, 187 insertions(+), 132 deletions(-) rename core/java/android/content/pm/{EphemeralResponse.java => AuxiliaryResolveInfo.java} (57%) diff --git a/core/java/android/content/pm/EphemeralResponse.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java similarity index 57% rename from core/java/android/content/pm/EphemeralResponse.java rename to core/java/android/content/pm/AuxiliaryResolveInfo.java index 6e569f7d4667..7d7784a185bf 100644 --- a/core/java/android/content/pm/EphemeralResponse.java +++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java @@ -18,13 +18,19 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Intent; import android.content.IntentFilter; /** - * Ephemeral application resolution response. + * Auxiliary application resolution response. + *

+ * Used when resolution occurs, but, the target is not actually on the device. + * This happens resolving instant apps that haven't been installed yet or if + * the application consists of multiple feature splits and the needed split + * hasn't been installed. * @hide */ -public final class EphemeralResponse extends IntentFilter { +public final class AuxiliaryResolveInfo extends IntentFilter { /** Resolved information returned from the external ephemeral resolver */ public final EphemeralResolveInfo resolveInfo; /** The resolved package. Copied from {@link #resolveInfo}. */ @@ -32,11 +38,14 @@ public final class EphemeralResponse extends IntentFilter { /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */ public final String splitName; /** Whether or not ephemeral resolution needs the second phase */ - public final boolean needsPhase2; + public final boolean needsPhaseTwo; /** Opaque token to track the ephemeral application resolution */ public final String token; + /** The version code of the package */ + public final int versionCode; - public EphemeralResponse(@NonNull EphemeralResolveInfo resolveInfo, + /** Create a response for installing an instant application. */ + public AuxiliaryResolveInfo(@NonNull EphemeralResolveInfo resolveInfo, @NonNull IntentFilter orig, @Nullable String splitName, @NonNull String token, @@ -46,6 +55,20 @@ public final class EphemeralResponse extends IntentFilter { this.packageName = resolveInfo.getPackageName(); this.splitName = splitName; this.token = token; - this.needsPhase2 = needsPhase2; + this.needsPhaseTwo = needsPhase2; + this.versionCode = resolveInfo.getVersionCode(); + } + + /** Create a response for installing a split on demand. */ + public AuxiliaryResolveInfo(@NonNull String packageName, + @Nullable String splitName, + int versionCode) { + super(); + this.packageName = packageName; + this.splitName = splitName; + this.versionCode = versionCode; + this.resolveInfo = null; + this.token = null; + this.needsPhaseTwo = false; } } \ No newline at end of file diff --git a/core/java/android/content/pm/EphemeralRequest.java b/core/java/android/content/pm/EphemeralRequest.java index 7f2b3ee1245b..58099c2b9b6a 100644 --- a/core/java/android/content/pm/EphemeralRequest.java +++ b/core/java/android/content/pm/EphemeralRequest.java @@ -24,24 +24,21 @@ import android.content.Intent; */ public final class EphemeralRequest { /** Response from the first phase of ephemeral application resolution */ - public final EphemeralResponse responseObj; + public final AuxiliaryResolveInfo responseObj; /** The original intent that triggered ephemeral application resolution */ public final Intent origIntent; /** Resolved type of the intent */ public final String resolvedType; - /** The intent that would launch if there were no ephemeral applications */ - public final Intent launchIntent; /** The name of the package requesting the ephemeral application */ public final String callingPackage; /** ID of the user requesting the ephemeral application */ public final int userId; - public EphemeralRequest(EphemeralResponse responseObj, Intent origIntent, - String resolvedType, Intent launchIntent, String callingPackage, int userId) { + public EphemeralRequest(AuxiliaryResolveInfo responseObj, Intent origIntent, + String resolvedType, String callingPackage, int userId) { this.responseObj = responseObj; this.origIntent = origIntent; this.resolvedType = resolvedType; - this.launchIntent = launchIntent; this.callingPackage = callingPackage; this.userId = userId; } diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 7d59bd619498..94fe572360b5 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -213,12 +213,11 @@ public abstract class PackageManagerInternal { * @param responseObj The response of the first phase of ephemeral resolution * @param origIntent The original intent that triggered ephemeral resolution * @param resolvedType The resolved type of the intent - * @param launchIntent The intent that would launch if there was no ephemeral application * @param callingPackage The name of the package requesting the ephemeral application * @param userId The ID of the user that triggered ephemeral resolution */ - public abstract void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj, - Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage, + public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, + Intent origIntent, String resolvedType, String callingPackage, int userId); /** diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index f8b4570be6cc..50f2d5371019 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -61,11 +61,12 @@ 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. + * An auxiliary response that may modify the resolved information. This is + * only set under certain circumstances; such as when resolving instant apps + * or components defined in un-installed splits. * @hide */ - public EphemeralResponse ephemeralResponse; + public AuxiliaryResolveInfo auxiliaryInfo; /** * The IntentFilter that was matched for this ResolveInfo. diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 7605a1e0ffc3..83d43db20671 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -82,6 +82,7 @@ import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.am.EventLogTags.AM_NEW_INTENT; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AppGlobals; @@ -98,7 +99,9 @@ import android.content.Intent; import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; @@ -456,22 +459,9 @@ class ActivityStarter { // 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.ephemeralResponse != null) { - final String packageName = - rInfo.ephemeralResponse.resolveInfo.getPackageName(); - final String splitName = rInfo.ephemeralResponse.splitName; - final boolean needsPhaseTwo = rInfo.ephemeralResponse.needsPhase2; - final String token = rInfo.ephemeralResponse.token; - final int versionCode = rInfo.ephemeralResponse.resolveInfo.getVersionCode(); - if (needsPhaseTwo) { - // request phase two resolution - mService.getPackageManagerInternalLocked().requestEphemeralResolutionPhaseTwo( - rInfo.ephemeralResponse, ephemeralIntent, resolvedType, intent, - callingPackage, userId); - } - intent = EphemeralResolver.buildEphemeralInstallerIntent(intent, ephemeralIntent, - callingPackage, resolvedType, userId, packageName, splitName, versionCode, - token, needsPhaseTwo); + if (rInfo != null && rInfo.auxiliaryInfo != null) { + intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent, + callingPackage, resolvedType, userId); resolvedType = null; callingUid = realCallingUid; callingPid = realCallingPid; @@ -530,6 +520,21 @@ class ActivityStarter { return err; } + /** Creates a launch intent for the given auxiliary resolution data. */ + private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse, + Intent originalIntent, String callingPackage, + String resolvedType, int userId) { + if (auxiliaryResponse.needsPhaseTwo) { + // request phase two resolution + mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo( + auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId); + } + return EphemeralResolver.buildEphemeralInstallerIntent(originalIntent, + callingPackage, resolvedType, userId, auxiliaryResponse.packageName, + auxiliaryResponse.splitName, auxiliaryResponse.versionCode, + auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); + } + void postStartActivityUncheckedProcessing( ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord, ActivityStack targetStack) { diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java index d99a1b685da3..7bc65f91a7ab 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolver.java +++ b/services/core/java/com/android/server/pm/EphemeralResolver.java @@ -16,8 +16,9 @@ package com.android.server.pm; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.ActivityManagerNative; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -29,7 +30,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.EphemeralIntentFilter; import android.content.pm.EphemeralRequest; import android.content.pm.EphemeralResolveInfo; -import android.content.pm.EphemeralResponse; +import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.EphemeralResolveInfo.EphemeralDigest; import android.os.Binder; import android.os.Handler; @@ -46,7 +47,7 @@ import java.util.UUID; /** @hide */ public abstract class EphemeralResolver { - public static EphemeralResponse doEphemeralResolutionPhaseOne(Context context, + public static AuxiliaryResolveInfo doEphemeralResolutionPhaseOne(Context context, EphemeralResolverConnection connection, EphemeralRequest requestObj) { final Intent intent = requestObj.origIntent; final EphemeralDigest digest = @@ -83,7 +84,7 @@ public abstract class EphemeralResolver { final ArrayList ephemeralResolveInfoList = new ArrayList(1); ephemeralResolveInfoList.add(ephemeralResolveInfo); - final EphemeralResponse ephemeralIntentInfo = + final AuxiliaryResolveInfo ephemeralIntentInfo = EphemeralResolver.filterEphemeralIntent( ephemeralResolveInfoList, intent, null /*resolvedType*/, 0 /*userId*/, intent.getPackage(), digest, @@ -104,7 +105,6 @@ public abstract class EphemeralResolver { versionCode = -1; } final Intent installerIntent = buildEphemeralInstallerIntent( - requestObj.launchIntent, requestObj.origIntent, requestObj.callingPackage, requestObj.resolvedType, @@ -126,35 +126,41 @@ public abstract class EphemeralResolver { /** * Builds and returns an intent to launch the ephemeral installer. */ - public static Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent, - String callingPackage, String resolvedType, int userId, String ephemeralPackageName, - String ephemeralSplitName, int versionCode, String token, boolean needsPhaseTwo) { + public static Intent buildEphemeralInstallerIntent(@NonNull Intent origIntent, + @NonNull String callingPackage, + @NonNull String resolvedType, + int userId, + @NonNull String ephemeralPackageName, + @Nullable String ephemeralSplitName, + int versionCode, + @Nullable String token, + boolean needsPhaseTwo) { // Construct the intent that launches the ephemeral installer - int flags = launchIntent.getFlags(); + int flags = origIntent.getFlags(); final Intent intent = new Intent(); intent.setFlags(flags | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - // TODO: Remove when the platform has fully implemented ephemeral apps - intent.setData(origIntent.getData().buildUpon().clearQuery().build()); - intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token); - intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost()); + if (token != null) { + intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token); + } + if (origIntent.getData() != null) { + intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost()); + } + // We have all of the data we need; just start the installer without a second phase if (!needsPhaseTwo) { - // We have all of the data we need; just start the installer without a second phase - final Intent nonEphemeralIntent = new Intent(origIntent); - nonEphemeralIntent.setFlags( - nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); - // Intent that is launched if the ephemeral package couldn't be installed - // for any reason. + // Intent that is launched if the package couldn't be installed for any reason. + final Intent failureIntent = new Intent(origIntent); + failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); try { - final IIntentSender failureIntentTarget = ActivityManagerNative.getDefault() + final IIntentSender failureIntentTarget = ActivityManager.getService() .getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, null /*token*/, null /*resultWho*/, 1 /*requestCode*/, - new Intent[] { nonEphemeralIntent }, + new Intent[] { failureIntent }, new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, @@ -163,19 +169,14 @@ public abstract class EphemeralResolver { new IntentSender(failureIntentTarget)); } catch (RemoteException ignore) { /* ignore; same process */ } - // Success intent goes back to the installer - final Intent ephemeralIntent = new Intent(launchIntent) - .setComponent(null) - .setPackage(ephemeralPackageName); - // Intent that is eventually launched if the ephemeral package was - // installed successfully. This will actually be launched by a platform - // broadcast receiver. + // Intent that is launched if the package was installed successfully. + final Intent successIntent = new Intent(origIntent); try { - final IIntentSender successIntentTarget = ActivityManagerNative.getDefault() + final IIntentSender successIntentTarget = ActivityManager.getService() .getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, null /*token*/, null /*resultWho*/, 0 /*requestCode*/, - new Intent[] { ephemeralIntent }, + new Intent[] { successIntent }, new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, @@ -192,7 +193,7 @@ public abstract class EphemeralResolver { return intent; } - private static EphemeralResponse filterEphemeralIntent( + private static AuxiliaryResolveInfo filterEphemeralIntent( List ephemeralResolveInfoList, Intent intent, String resolvedType, int userId, String packageName, EphemeralDigest digest, String token) { @@ -212,7 +213,7 @@ public abstract class EphemeralResolver { ephemeralInfo.getIntentFilters(); // No filters; we need to start phase two if (ephemeralFilters == null || ephemeralFilters.isEmpty()) { - return new EphemeralResponse(ephemeralInfo, + return new AuxiliaryResolveInfo(ephemeralInfo, new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/, null /*splitName*/, token, true /*needsPhase2*/); } @@ -226,14 +227,14 @@ public abstract class EphemeralResolver { continue; } for (int k = splitFilters.size() - 1; k >= 0; --k) { - final EphemeralResponse intentInfo = - new EphemeralResponse(ephemeralInfo, + final AuxiliaryResolveInfo intentInfo = + new AuxiliaryResolveInfo(ephemeralInfo, splitFilters.get(k), ephemeralFilter.getSplitName(), token, false /*needsPhase2*/); ephemeralResolver.addFilter(intentInfo); } } - List matchedResolveInfoList = ephemeralResolver.queryIntent( + List matchedResolveInfoList = ephemeralResolver.queryIntent( intent, resolvedType, false /*defaultOnly*/, userId); if (!matchedResolveInfoList.isEmpty()) { return matchedResolveInfoList.get(0); diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index 2b6ce10741c4..7f5973e64283 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.EphemeralResolveInfo; -import android.content.pm.EphemeralResponse; import android.os.Build; import android.os.Bundle; import android.os.Handler; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 371a062c0c43..dff78ba68f91 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -131,7 +131,7 @@ import android.content.pm.ComponentInfo; import android.content.pm.InstantAppInfo; import android.content.pm.EphemeralRequest; import android.content.pm.EphemeralResolveInfo; -import android.content.pm.EphemeralResponse; +import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IOnPermissionsChangeListener; @@ -834,12 +834,12 @@ public class PackageManagerService extends IPackageManager.Stub { private int mIntentFilterVerificationToken = 0; /** The service connection to the ephemeral resolver */ - final EphemeralResolverConnection mEphemeralResolverConnection; + final EphemeralResolverConnection mInstantAppResolverConnection; /** Component used to install ephemeral applications */ - ComponentName mEphemeralInstallerComponent; - final ActivityInfo mEphemeralInstallerActivity = new ActivityInfo(); - final ResolveInfo mEphemeralInstallerInfo = new ResolveInfo(); + ComponentName mInstantAppInstallerComponent; + final ActivityInfo mInstantAppInstallerActivity = new ActivityInfo(); + final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo(); final SparseArray mIntentFilterVerificationStates = new SparseArray(); @@ -1159,7 +1159,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int START_INTENT_FILTER_VERIFICATIONS = 17; static final int INTENT_FILTER_VERIFIED = 18; static final int WRITE_PACKAGE_LIST = 19; - static final int EPHEMERAL_RESOLUTION_PHASE_TWO = 20; + static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds @@ -1730,11 +1730,11 @@ public class PackageManagerService extends IPackageManager.Stub { break; } - case EPHEMERAL_RESOLUTION_PHASE_TWO: { + case INSTANT_APP_RESOLUTION_PHASE_TWO: { EphemeralResolver.doEphemeralResolutionPhaseTwo(mContext, - mEphemeralResolverConnection, + mInstantAppResolverConnection, (EphemeralRequest) msg.obj, - mEphemeralInstallerActivity, + mInstantAppInstallerActivity, mHandler); } } @@ -2861,17 +2861,17 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_EPHEMERAL) { Slog.i(TAG, "Ephemeral resolver: " + ephemeralResolverComponent); } - mEphemeralResolverConnection = + mInstantAppResolverConnection = new EphemeralResolverConnection(mContext, ephemeralResolverComponent); } else { - mEphemeralResolverConnection = null; + mInstantAppResolverConnection = null; } - mEphemeralInstallerComponent = getEphemeralInstallerLPr(); - if (mEphemeralInstallerComponent != null) { + mInstantAppInstallerComponent = getEphemeralInstallerLPr(); + if (mInstantAppInstallerComponent != null) { if (DEBUG_EPHEMERAL) { - Slog.i(TAG, "Ephemeral installer: " + mEphemeralInstallerComponent); + Slog.i(TAG, "Ephemeral installer: " + mInstantAppInstallerComponent); } - setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent); + setUpInstantAppInstallerActivityLP(mInstantAppInstallerComponent); } // Read and update the usage of dex files. @@ -3969,7 +3969,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { // Otherwise, prevent leaking ephemeral components flags &= ~PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; - if (callingUid != Process.SYSTEM_UID && callingUid != 0) { + if (callingUid != Process.SYSTEM_UID + && callingUid != Process.SHELL_UID + && callingUid != 0) { // Unless called from the system process flags &= ~PackageManager.MATCH_INSTANT; } @@ -5602,10 +5604,10 @@ public class PackageManagerService extends IPackageManager.Stub { if (callingUser != UserHandle.USER_SYSTEM) { return false; } - if (mEphemeralResolverConnection == null) { + if (mInstantAppResolverConnection == null) { return false; } - if (mEphemeralInstallerComponent == null) { + if (mInstantAppInstallerComponent == null) { return false; } if (intent.getComponent() != null) { @@ -5622,6 +5624,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. + // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { @@ -5640,6 +5643,9 @@ public class PackageManagerService extends IPackageManager.Stub { } return false; } + if (ps.getInstantApp(userId)) { + return false; + } } } } @@ -5647,11 +5653,11 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } - private void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj, - Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage, + private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, + Intent origIntent, String resolvedType, String callingPackage, int userId) { - final Message msg = mHandler.obtainMessage(EPHEMERAL_RESOLUTION_PHASE_TWO, - new EphemeralRequest(responseObj, origIntent, resolvedType, launchIntent, + final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, + new EphemeralRequest(responseObj, origIntent, resolvedType, callingPackage, userId)); mHandler.sendMessage(msg); } @@ -6077,7 +6083,7 @@ public class PackageManagerService extends IPackageManager.Stub { list.add(ri); } } - return list; + return applyPostResolutionFilter(list, instantAppPkgName); } // reader @@ -6095,7 +6101,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (xpResolveInfo != null) { List xpResult = new ArrayList(1); xpResult.add(xpResolveInfo); - return filterForEphemeral( + return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName); } @@ -6136,13 +6142,13 @@ public class PackageManagerService extends IPackageManager.Stub { // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); - return filterForEphemeral(result, instantAppPkgName); + return applyPostResolutionFilter(result, instantAppPkgName); } } else if (result.size() <= 1 && !addEphemeral) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. - return filterForEphemeral(result, instantAppPkgName); + return applyPostResolutionFilter(result, instantAppPkgName); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. @@ -6153,7 +6159,7 @@ public class PackageManagerService extends IPackageManager.Stub { } else { final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - result = filterForEphemeral(filterIfNotSystemUser( + result = applyPostResolutionFilter(filterIfNotSystemUser( mActivities.queryIntentForPackage( intent, resolvedType, flags, pkg.activities, userId), userId), instantAppPkgName); @@ -6170,15 +6176,16 @@ public class PackageManagerService extends IPackageManager.Stub { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final EphemeralRequest requestObject = new EphemeralRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, - null /*launchIntent*/, null /*callingPackage*/, userId); - final EphemeralResponse intentInfo = EphemeralResolver.doEphemeralResolutionPhaseOne( - mContext, mEphemeralResolverConnection, requestObject); - if (intentInfo != null) { + null /*callingPackage*/, userId); + final AuxiliaryResolveInfo auxiliaryResponse = + EphemeralResolver.doEphemeralResolutionPhaseOne( + mContext, mInstantAppResolverConnection, requestObject); + if (auxiliaryResponse != null) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } - final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo); - ephemeralInstaller.ephemeralResponse = intentInfo; + final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); + ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -6194,7 +6201,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (sortResult) { Collections.sort(result, mResolvePrioritySorter); } - return filterForEphemeral(result, instantAppPkgName); + return applyPostResolutionFilter(result, instantAppPkgName); } private static class CrossProfileDomainInfo { @@ -6302,16 +6309,40 @@ public class PackageManagerService extends IPackageManager.Stub { * is performed. * @return A filtered list of resolved activities. */ - private List filterForEphemeral(List resolveInfos, + private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName) { + // TODO: When adding on-demand split support for non-instant apps, remove this check + // and always apply post filtering if (ephemeralPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { - ResolveInfo info = resolveInfos.get(i); + final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); // allow activities that are defined in the provided package if (isEphemeralApp && ephemeralPkgName.equals(info.activityInfo.packageName)) { + if (info.activityInfo.splitName != null + && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, + info.activityInfo.splitName)) { + // requested activity is defined in a split that hasn't been installed yet. + // add the installer to the resolve list + if (DEBUG_EPHEMERAL) { + Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); + } + final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( + info.activityInfo.packageName, info.activityInfo.splitName, + info.activityInfo.applicationInfo.versionCode); + // make sure this resolver is the default + installerInfo.isDefault = true; + installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + // add a non-generic filter + installerInfo.filter = new IntentFilter(); + // load resources from the correct package + installerInfo.resolvePackageName = info.getComponentInfo().packageName; + resolveInfos.set(i, installerInfo); + } continue; } // allow activities that have been explicitly exposed to ephemeral apps @@ -10642,12 +10673,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void setUpEphemeralInstallerActivityLP(ComponentName installerComponent) { + private void setUpInstantAppInstallerActivityLP(ComponentName installerComponent) { if (installerComponent == null) { if (DEBUG_EPHEMERAL) { Slog.d(TAG, "Clear ephemeral installer activity"); } - mEphemeralInstallerActivity.applicationInfo = null; + mInstantAppInstallerActivity.applicationInfo = null; return; } @@ -10656,21 +10687,21 @@ public class PackageManagerService extends IPackageManager.Stub { } final PackageParser.Package pkg = mPackages.get(installerComponent.getPackageName()); // Set up information for ephemeral installer activity - mEphemeralInstallerActivity.applicationInfo = pkg.applicationInfo; - mEphemeralInstallerActivity.name = installerComponent.getClassName(); - mEphemeralInstallerActivity.packageName = pkg.applicationInfo.packageName; - mEphemeralInstallerActivity.processName = pkg.applicationInfo.packageName; - mEphemeralInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; - mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS + mInstantAppInstallerActivity.applicationInfo = pkg.applicationInfo; + mInstantAppInstallerActivity.name = installerComponent.getClassName(); + mInstantAppInstallerActivity.packageName = pkg.applicationInfo.packageName; + mInstantAppInstallerActivity.processName = pkg.applicationInfo.packageName; + mInstantAppInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + mInstantAppInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; - mEphemeralInstallerActivity.theme = 0; - mEphemeralInstallerActivity.exported = true; - mEphemeralInstallerActivity.enabled = true; - mEphemeralInstallerInfo.activityInfo = mEphemeralInstallerActivity; - mEphemeralInstallerInfo.priority = 0; - mEphemeralInstallerInfo.preferredOrder = 1; - mEphemeralInstallerInfo.isDefault = true; - mEphemeralInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + mInstantAppInstallerActivity.theme = 0; + mInstantAppInstallerActivity.exported = true; + mInstantAppInstallerActivity.enabled = true; + mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity; + mInstantAppInstallerInfo.priority = 0; + mInstantAppInstallerInfo.preferredOrder = 1; + mInstantAppInstallerInfo.isDefault = true; + mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; } @@ -12712,7 +12743,7 @@ public class PackageManagerService extends IPackageManager.Stub { } static final class EphemeralIntentResolver - extends IntentResolver { + extends IntentResolver { /** * The result that has the highest defined order. Ordering applies on a * per-package basis. Mapping is from package name to Pair of order and @@ -12727,17 +12758,17 @@ public class PackageManagerService extends IPackageManager.Stub { final ArrayMap> mOrderResult = new ArrayMap<>(); @Override - protected EphemeralResponse[] newArray(int size) { - return new EphemeralResponse[size]; + protected AuxiliaryResolveInfo[] newArray(int size) { + return new AuxiliaryResolveInfo[size]; } @Override - protected boolean isPackageForFilter(String packageName, EphemeralResponse responseObj) { + protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) { return true; } @Override - protected EphemeralResponse newResult(EphemeralResponse responseObj, int match, + protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match, int userId) { if (!sUserManager.exists(userId)) { return null; @@ -12759,7 +12790,7 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - protected void filterResults(List results) { + protected void filterResults(List results) { // only do work if ordering is enabled [most of the time it won't be] if (mOrderResult.size() == 0) { return; @@ -22908,11 +22939,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj, - Intent origIntent, String resolvedType, Intent launchIntent, - String callingPackage, int userId) { - PackageManagerService.this.requestEphemeralResolutionPhaseTwo( - responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId); + public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, + Intent origIntent, String resolvedType, String callingPackage, int userId) { + PackageManagerService.this.requestInstantAppResolutionPhaseTwo( + responseObj, origIntent, resolvedType, callingPackage, userId); } @Override -- 2.11.0