diff --git a/api/current.txt b/api/current.txt index 44f2f08360495..085fa33ebe3f8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10050,6 +10050,7 @@ package android.content { field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000 field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000 field public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096; // 0x1000 + field public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048; // 0x800 field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000 field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000 field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000 diff --git a/api/system-current.txt b/api/system-current.txt index 034ee3090fc45..08ac295c93610 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -311,8 +311,10 @@ package android.app { ctor public InstantAppResolverService(); method public final void attachBaseContext(android.content.Context); method public final android.os.IBinder onBind(android.content.Intent); - method public void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method public void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public deprecated void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppIntentFilter(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public deprecated void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppResolveInfo(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); } public static final class InstantAppResolverService.InstantAppResolutionCallback { @@ -821,12 +823,14 @@ package android.content { field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; field public static final java.lang.String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS"; field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET"; + field public static final java.lang.String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES"; field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON"; field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; + field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; } public class IntentFilter implements android.os.Parcelable { @@ -869,6 +873,7 @@ package android.content.pm { ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List, int); ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List, long, android.os.Bundle); ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List); + ctor public InstantAppResolveInfo(android.os.Bundle); method public int describeContents(); method public byte[] getDigestBytes(); method public int getDigestPrefix(); @@ -877,6 +882,7 @@ package android.content.pm { method public long getLongVersionCode(); method public java.lang.String getPackageName(); method public deprecated int getVersionCode(); + method public boolean shouldLetInstallerDecide(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -888,6 +894,7 @@ package android.content.pm { method public int[] getDigestPrefix(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public static final android.content.pm.InstantAppResolveInfo.InstantAppDigest UNDEFINED; } public final class IntentFilterVerificationInfo implements android.os.Parcelable { diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java index 427a0386e87c4..d1c244136ec98 100644 --- a/core/java/android/app/EphemeralResolverService.java +++ b/core/java/android/app/EphemeralResolverService.java @@ -17,20 +17,10 @@ package android.app; import android.annotation.SystemApi; -import android.app.Service; -import android.app.InstantAppResolverService.InstantAppResolutionCallback; -import android.content.Context; -import android.content.Intent; import android.content.pm.EphemeralResolveInfo; import android.content.pm.InstantAppResolveInfo; import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.IRemoteCallback; import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; @@ -85,7 +75,6 @@ public abstract class EphemeralResolverService extends InstantAppResolverService return super.getLooper(); } - @Override void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, InstantAppResolutionCallback callback) { if (DEBUG_EPHEMERAL) { @@ -101,7 +90,6 @@ public abstract class EphemeralResolverService extends InstantAppResolverService callback.onInstantAppResolveInfo(resultList); } - @Override void _onGetInstantAppIntentFilter(int[] digestPrefix, String token, String hostName, InstantAppResolutionCallback callback) { if (DEBUG_EPHEMERAL) { diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl index 805d8c057d27d..ae200578d829b 100644 --- a/core/java/android/app/IInstantAppResolver.aidl +++ b/core/java/android/app/IInstantAppResolver.aidl @@ -16,13 +16,15 @@ package android.app; +import android.content.Intent; import android.os.IRemoteCallback; /** @hide */ oneway interface IInstantAppResolver { - void getInstantAppResolveInfoList(in int[] digestPrefix, + void getInstantAppResolveInfoList(in Intent sanitizedIntent, in int[] hostDigestPrefix, String token, int sequence, IRemoteCallback callback); - void getInstantAppIntentFilterList(in int[] digestPrefix, - String token, String hostName, IRemoteCallback callback); + void getInstantAppIntentFilterList(in Intent sanitizedIntent, in int[] hostDigestPrefix, + String token, IRemoteCallback callback); + } diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index c5dc86c79ef95..89aff36f883a1 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -17,7 +17,6 @@ package android.app; import android.annotation.SystemApi; -import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.InstantAppResolveInfo; @@ -35,6 +34,7 @@ import android.util.Slog; import com.android.internal.os.SomeArgs; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -53,23 +53,65 @@ public abstract class InstantAppResolverService extends Service { Handler mHandler; /** - * Called to retrieve resolve info for instant applications. + * Called to retrieve resolve info for instant applications immediately. * * @param digestPrefix The hash prefix of the instant app's domain. + * @deprecated should implement {@link #onGetInstantAppResolveInfo(Intent, int[], String, + * InstantAppResolutionCallback)} */ + @Deprecated public void onGetInstantAppResolveInfo( int digestPrefix[], String token, InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define"); } /** - * Called to retrieve intent filters for instant applications. + * Called to retrieve intent filters for instant applications from potentially expensive + * sources. * * @param digestPrefix The hash prefix of the instant app's domain. + * @deprecated should implement {@link #onGetInstantAppIntentFilter(Intent, int[], String, + * InstantAppResolutionCallback)} */ + @Deprecated public void onGetInstantAppIntentFilter( int digestPrefix[], String token, InstantAppResolutionCallback callback) { - throw new IllegalStateException("Must define"); + throw new IllegalStateException("Must define onGetInstantAppIntentFilter"); + } + + /** + * Called to retrieve resolve info for instant applications immediately. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * @param hostDigestPrefix The hash prefix of the instant app's domain. + */ + public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, + String token, InstantAppResolutionCallback callback) { + // if not overridden, forward to old methods and filter out non-web intents + if (sanitizedIntent.isBrowsableWebIntent()) { + onGetInstantAppResolveInfo(hostDigestPrefix, token, callback); + } else { + callback.onInstantAppResolveInfo(Collections.emptyList()); + } + } + + /** + * Called to retrieve intent filters for instant applications from potentially expensive + * sources. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is + * defined. + */ + public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, + String token, InstantAppResolutionCallback callback) { + Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden"); + // if not overridden, forward to old methods and filter out non-web intents + if (sanitizedIntent.isBrowsableWebIntent()) { + onGetInstantAppIntentFilter(hostDigestPrefix, token, callback); + } else { + callback.onInstantAppResolveInfo(Collections.emptyList()); + } } /** @@ -89,8 +131,8 @@ public abstract class InstantAppResolverService extends Service { public final IBinder onBind(Intent intent) { return new IInstantAppResolver.Stub() { @Override - public void getInstantAppResolveInfoList( - int digestPrefix[], String token, int sequence, IRemoteCallback callback) { + public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix, + String token, int sequence, IRemoteCallback callback) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "[" + token + "] Phase1 called; posting"); } @@ -98,14 +140,14 @@ public abstract class InstantAppResolverService extends Service { args.arg1 = callback; args.arg2 = digestPrefix; args.arg3 = token; - mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args) - .sendToTarget(); + args.arg4 = sanitizedIntent; + mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, + sequence, 0, args).sendToTarget(); } @Override - public void getInstantAppIntentFilterList( - int digestPrefix[], String token, String hostName, IRemoteCallback callback) { + public void getInstantAppIntentFilterList(Intent sanitizedIntent, + int[] digestPrefix, String token, IRemoteCallback callback) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "[" + token + "] Phase2 called; posting"); } @@ -113,9 +155,9 @@ public abstract class InstantAppResolverService extends Service { args.arg1 = callback; args.arg2 = digestPrefix; args.arg3 = token; - args.arg4 = hostName; - mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget(); + args.arg4 = sanitizedIntent; + mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, + callback).sendToTarget(); } }; } @@ -142,29 +184,9 @@ public abstract class InstantAppResolverService extends Service { } } - @Deprecated - void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, - InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "[" + token + "] Phase1 request;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - onGetInstantAppResolveInfo(digestPrefix, token, callback); - } - @Deprecated - void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName, - InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "[" + token + "] Phase2 request;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - onGetInstantAppIntentFilter(digestPrefix, token, callback); - } - private final class ServiceHandler extends Handler { public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1; public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2; - public ServiceHandler(Looper looper) { super(looper, null /*callback*/, true /*async*/); } @@ -179,9 +201,13 @@ public abstract class InstantAppResolverService extends Service { final IRemoteCallback callback = (IRemoteCallback) args.arg1; final int[] digestPrefix = (int[]) args.arg2; final String token = (String) args.arg3; + final Intent intent = (Intent) args.arg4; final int sequence = message.arg1; - _onGetInstantAppResolveInfo( - digestPrefix, token, + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "[" + token + "] Phase1 request;" + + " prefix: " + Arrays.toString(digestPrefix)); + } + onGetInstantAppResolveInfo(intent, digestPrefix, token, new InstantAppResolutionCallback(sequence, callback)); } break; @@ -190,9 +216,12 @@ public abstract class InstantAppResolverService extends Service { final IRemoteCallback callback = (IRemoteCallback) args.arg1; final int[] digestPrefix = (int[]) args.arg2; final String token = (String) args.arg3; - final String hostName = (String) args.arg4; - _onGetInstantAppIntentFilter( - digestPrefix, token, hostName, + final Intent intent = (Intent) args.arg4; + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "[" + token + "] Phase2 request;" + + " prefix: " + Arrays.toString(digestPrefix)); + } + onGetInstantAppIntentFilter(intent, digestPrefix, token, new InstantAppResolutionCallback(-1 /*sequence*/, callback)); } break; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e02a29494296e..b3c8737a28311 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -50,6 +50,7 @@ import android.provider.DocumentsContract; import android.provider.DocumentsProvider; import android.provider.MediaStore; import android.provider.OpenableColumns; +import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -4472,7 +4473,15 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION"; /** - * A {@link Bundle} of metadata that describes the instanta application that needs to be + * An array of {@link Bundle}s containing details about resolved instant apps.. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_BUNDLES = + "android.intent.extra.INSTANT_APP_BUNDLES"; + + /** + * A {@link Bundle} of metadata that describes the instant application that needs to be * installed. This data is populated from the response to * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered * instant application resolver. @@ -4481,6 +4490,16 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_INSTANT_APP_EXTRAS = "android.intent.extra.INSTANT_APP_EXTRAS"; + /** + * A boolean value indicating that the instant app resolver was unable to state with certainty + * that it did or did not have an app for the sanitized {@link Intent} defined at + * {@link #EXTRA_INTENT}. + * @hide + */ + @SystemApi + public static final String EXTRA_UNKNOWN_INSTANT_APP = + "android.intent.extra.UNKNOWN_INSTANT_APP"; + /** * The version code of the app to install components from. * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE). @@ -5029,6 +5048,7 @@ public class Intent implements Parcelable, Cloneable { FLAG_GRANT_PREFIX_URI_PERMISSION, FLAG_DEBUG_TRIAGED_MISSING, FLAG_IGNORE_EPHEMERAL, + FLAG_ACTIVITY_MATCH_EXTERNAL, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_NEW_TASK, @@ -5072,6 +5092,7 @@ public class Intent implements Parcelable, Cloneable { FLAG_INCLUDE_STOPPED_PACKAGES, FLAG_DEBUG_TRIAGED_MISSING, FLAG_IGNORE_EPHEMERAL, + FLAG_ACTIVITY_MATCH_EXTERNAL, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_NEW_TASK, @@ -5475,6 +5496,14 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000; + + /** + * If set, resolution of this intent may take place via an instant app not + * yet on the device if there does not yet exist an app on device to + * resolve it. + */ + public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800; + /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. @@ -10028,6 +10057,25 @@ public class Intent implements Parcelable, Cloneable { } } + /** @hide */ + public boolean hasWebURI() { + if (getData() == null) { + return false; + } + final String scheme = getScheme(); + if (TextUtils.isEmpty(scheme)) { + return false; + } + return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS); + } + + /** @hide */ + public boolean isBrowsableWebIntent() { + return ACTION_VIEW.equals(mAction) + && hasCategory(CATEGORY_BROWSABLE) + && hasWebURI(); + } + /** * @hide */ diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java index 6bdcefbe974e5..202df50dda6f8 100644 --- a/core/java/android/content/pm/AuxiliaryResolveInfo.java +++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java @@ -21,6 +21,10 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; + +import java.util.Collections; +import java.util.List; /** * Auxiliary application resolution response. @@ -31,56 +35,95 @@ import android.content.IntentFilter; * hasn't been installed. * @hide */ -public final class AuxiliaryResolveInfo extends IntentFilter { - /** Resolved information returned from the external instant resolver */ - public final InstantAppResolveInfo resolveInfo; - /** The resolved package. Copied from {@link #resolveInfo}. */ - public final String packageName; +public final class AuxiliaryResolveInfo { /** The activity to launch if there's an installation failure. */ public final ComponentName installFailureActivity; - /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */ - public final String splitName; /** Whether or not instant resolution needs the second phase */ public final boolean needsPhaseTwo; /** Opaque token to track the instant application resolution */ public final String token; - /** The version code of the package */ - public final long versionCode; /** An intent to start upon failure to install */ public final Intent failureIntent; + /** The matching filters for this resolve info. */ + public final List filters; /** Create a response for installing an instant application. */ - public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo, - @NonNull IntentFilter orig, - @Nullable String splitName, - @NonNull String token, + public AuxiliaryResolveInfo(@NonNull String token, boolean needsPhase2, - @Nullable Intent failureIntent) { - super(orig); - this.resolveInfo = resolveInfo; - this.packageName = resolveInfo.getPackageName(); - this.splitName = splitName; + @Nullable Intent failureIntent, + @Nullable List filters) { this.token = token; this.needsPhaseTwo = needsPhase2; - this.versionCode = resolveInfo.getVersionCode(); this.failureIntent = failureIntent; + this.filters = filters; this.installFailureActivity = null; } /** Create a response for installing a split on demand. */ - public AuxiliaryResolveInfo(@NonNull String packageName, - @Nullable String splitName, - @Nullable ComponentName failureActivity, - long versionCode, - @Nullable Intent failureIntent) { + public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity, + @Nullable Intent failureIntent, + @Nullable List filters) { super(); - this.packageName = packageName; this.installFailureActivity = failureActivity; - this.splitName = splitName; - this.versionCode = versionCode; - this.resolveInfo = null; + this.filters = filters; this.token = null; this.needsPhaseTwo = false; this.failureIntent = failureIntent; } + + /** Create a response for installing a split on demand. */ + public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity, + String packageName, long versionCode, String splitName) { + this(failureActivity, null, Collections.singletonList( + new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName))); + } + + /** @hide */ + public static final class AuxiliaryFilter extends IntentFilter { + /** Resolved information returned from the external instant resolver */ + public final InstantAppResolveInfo resolveInfo; + /** The resolved package. Copied from {@link #resolveInfo}. */ + public final String packageName; + /** The version code of the package */ + public final long versionCode; + /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */ + public final String splitName; + /** The extras to pass on to the installer for this filter. */ + public final Bundle extras; + + public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo, + String splitName, Bundle extras) { + super(orig); + this.resolveInfo = resolveInfo; + this.packageName = resolveInfo.getPackageName(); + this.versionCode = resolveInfo.getLongVersionCode(); + this.splitName = splitName; + this.extras = extras; + } + + public AuxiliaryFilter(InstantAppResolveInfo resolveInfo, + String splitName, Bundle extras) { + this.resolveInfo = resolveInfo; + this.packageName = resolveInfo.getPackageName(); + this.versionCode = resolveInfo.getLongVersionCode(); + this.splitName = splitName; + this.extras = extras; + } + + public AuxiliaryFilter(String packageName, long versionCode, String splitName) { + this.resolveInfo = null; + this.packageName = packageName; + this.versionCode = versionCode; + this.splitName = splitName; + this.extras = null; + } + + @Override + public String toString() { + return "AuxiliaryFilter{" + + "packageName='" + packageName + '\'' + + ", versionCode=" + versionCode + + ", splitName='" + splitName + '\'' + '}'; + } + } } \ No newline at end of file diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 19cb9323ba935..112c5dae6731f 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Intent; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -26,11 +27,35 @@ import android.os.Parcelable; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; /** - * Information about an instant application. + * Describes an externally resolvable instant application. There are three states that this class + * can represent:

+ *

    + *
  • + * The first, usable only for non http/s intents, implies that the resolver cannot + * immediately resolve this intent and would prefer that resolution be deferred to the + * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}. + * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest + * prefixes were passed into one of the resolve methods in + * {@link android.app.InstantAppResolverService}, this state cannot be used. + *
  • + *
  • + * The second represents a partial match and is constructed with any of the other + * constructors. By setting one or more of the {@link Nullable}arguments to null, you + * communicate to the resolver in response to + * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[], + * String, InstantAppResolverService.InstantAppResolutionCallback)} + * that you need a 2nd round of resolution to complete the request. + *
  • + *
  • + * The third represents a complete match and is constructed with all @Nullable parameters + * populated. + *
  • + *
* @hide */ @SystemApi @@ -38,6 +63,8 @@ public final class InstantAppResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ private static final String SHA_ALGORITHM = "SHA-256"; + private static final byte[] EMPTY_DIGEST = new byte[0]; + private final InstantAppDigest mDigest; private final String mPackageName; /** The filters used to match domain */ @@ -46,15 +73,30 @@ public final class InstantAppResolveInfo implements Parcelable { private final long mVersionCode; /** Data about the app that should be passed along to the Instant App installer on resolve */ private final Bundle mExtras; + /** + * A flag that indicates that the resolver is aware that an app may match, but would prefer + * that the installer get the sanitized intent to decide. This should not be used for + * resolutions that include a host and will be ignored in such cases. + */ + private final boolean mShouldLetInstallerDecide; + /** Constructor for intent-based InstantApp resolution results. */ public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List filters, int versionCode) { this(digest, packageName, filters, (long) versionCode, null /* extras */); } + /** Constructor for intent-based InstantApp resolution results with extras. */ public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List filters, long versionCode, @Nullable Bundle extras) { + this(digest, packageName, filters, versionCode, extras, false); + } + + /** Constructor for intent-based InstantApp resolution results with extras. */ + private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, + @Nullable List filters, long versionCode, + @Nullable Bundle extras, boolean shouldLetInstallerDecide) { // validate arguments if ((packageName == null && (filters != null && filters.size() != 0)) || (packageName != null && (filters == null || filters.size() == 0))) { @@ -62,7 +104,7 @@ public final class InstantAppResolveInfo implements Parcelable { } mDigest = digest; if (filters != null) { - mFilters = new ArrayList(filters.size()); + mFilters = new ArrayList<>(filters.size()); mFilters.addAll(filters); } else { mFilters = null; @@ -70,25 +112,48 @@ public final class InstantAppResolveInfo implements Parcelable { mPackageName = packageName; mVersionCode = versionCode; mExtras = extras; + mShouldLetInstallerDecide = shouldLetInstallerDecide; } + /** Constructor for intent-based InstantApp resolution results by hostname. */ public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, @Nullable List filters) { this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, null /* extras */); } + /** + * Constructor that creates a "let the installer decide" response with optional included + * extras. + */ + public InstantAppResolveInfo(@Nullable Bundle extras) { + this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); + } + InstantAppResolveInfo(Parcel in) { - mDigest = in.readParcelable(null /*loader*/); - mPackageName = in.readString(); - mFilters = new ArrayList(); - in.readList(mFilters, null /*loader*/); - mVersionCode = in.readLong(); + mShouldLetInstallerDecide = in.readBoolean(); mExtras = in.readBundle(); + if (mShouldLetInstallerDecide) { + mDigest = InstantAppDigest.UNDEFINED; + mPackageName = null; + mFilters = Collections.emptyList(); + mVersionCode = -1; + } else { + mDigest = in.readParcelable(null /*loader*/); + mPackageName = in.readString(); + mFilters = new ArrayList<>(); + in.readList(mFilters, null /*loader*/); + mVersionCode = in.readLong(); + } + } + + /** Returns true if the installer should be notified that it should query for packages. */ + public boolean shouldLetInstallerDecide() { + return mShouldLetInstallerDecide; } public byte[] getDigestBytes() { - return mDigest.getDigestBytes()[0]; + return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST; } public int getDigestPrefix() { @@ -127,11 +192,15 @@ public final class InstantAppResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeBoolean(mShouldLetInstallerDecide); + out.writeBundle(mExtras); + if (mShouldLetInstallerDecide) { + return; + } out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); out.writeLong(mVersionCode); - out.writeBundle(mExtras); } public static final Parcelable.Creator CREATOR @@ -159,7 +228,9 @@ public final class InstantAppResolveInfo implements Parcelable { @SystemApi public static final class InstantAppDigest implements Parcelable { private static final int DIGEST_MASK = 0xfffff000; - private static final int DIGEST_PREFIX_COUNT = 5; + + public static final InstantAppDigest UNDEFINED = + new InstantAppDigest(new byte[][]{}, new int[]{}); /** Full digest of the domain hashes */ private final byte[][] mDigestBytes; /** The first 4 bytes of the domain hashes */ @@ -186,6 +257,11 @@ public final class InstantAppResolveInfo implements Parcelable { } } + private InstantAppDigest(byte[][] digestBytes, int[] prefix) { + this.mDigestPrefix = prefix; + this.mDigestBytes = digestBytes; + } + private static byte[][] generateDigest(String hostName, int maxDigests) { ArrayList digests = new ArrayList<>(); try { diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 2ab2d20ed20de..1dfff5efcab52 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -103,11 +103,14 @@ public class ResolverListController { List resolvedComponents = null; for (int i = 0, N = intents.size(); i < N; i++) { final Intent intent = intents.get(i); - final List infos = mpm.queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY - | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) - | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0) - | PackageManager.MATCH_INSTANT); + int flags = PackageManager.MATCH_DEFAULT_ONLY + | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) + | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0); + if (intent.isBrowsableWebIntent() + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + flags |= PackageManager.MATCH_INSTANT; + } + final List infos = mpm.queryIntentActivities(intent, flags); // Remove any activities that are not exported. int totalSize = infos.size(); for (int j = totalSize - 1; j >= 0 ; j--) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6beafcb94ead9..bf38825876930 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1251,10 +1251,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { try { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + int modifiedFlags = flags + | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS; + if (intent.isBrowsableWebIntent() + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + modifiedFlags |= PackageManager.MATCH_INSTANT; + } return mService.getPackageManagerInternalLocked().resolveIntent( - intent, resolvedType, PackageManager.MATCH_INSTANT - | PackageManager.MATCH_DEFAULT_ONLY | flags - | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + intent, resolvedType, modifiedFlags, userId, true); } finally { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 8fd754af1a0fe..eab88aa45e445 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -31,6 +31,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -789,7 +790,7 @@ 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.auxiliaryInfo != null) { + if (rInfo != null && rInfo.isInstantAppAvailable) { intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent, callingPackage, verificationBundle, resolvedType, userId); resolvedType = null; @@ -849,22 +850,27 @@ class ActivityStarter { /** * Creates a launch intent for the given auxiliary resolution data. */ - private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse, + private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse, Intent originalIntent, String callingPackage, Bundle verificationBundle, String resolvedType, int userId) { - if (auxiliaryResponse.needsPhaseTwo) { + if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) { // request phase two resolution mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo( auxiliaryResponse, originalIntent, resolvedType, callingPackage, verificationBundle, userId); } return InstantAppResolver.buildEphemeralInstallerIntent( - Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, originalIntent, - auxiliaryResponse.failureIntent, callingPackage, verificationBundle, - resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName, - auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode, - auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(), - auxiliaryResponse.needsPhaseTwo); + originalIntent, + InstantAppResolver.sanitizeIntent(originalIntent), + auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent, + callingPackage, + verificationBundle, + resolvedType, + userId, + auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity, + auxiliaryResponse == null ? null : auxiliaryResponse.token, + auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo, + auxiliaryResponse == null ? null : auxiliaryResponse.filters); } void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) { @@ -924,12 +930,12 @@ class ActivityStarter { // Don't modify the client's object! intent = new Intent(intent); if (componentSpecified - && intent.getData() != null - && Intent.ACTION_VIEW.equals(intent.getAction()) + && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction()) + && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction()) && mService.getPackageManagerInternalLocked() .isInstantAppInstallerComponent(intent.getComponent())) { // intercept intents targeted directly to the ephemeral installer the - // ephemeral installer should never be started with a raw URL; instead + // ephemeral installer should never be started with a raw Intent; instead // adjust the intent so it looks like a "normal" instant app launch intent.setComponent(null /*component*/); componentSpecified = false; diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index b5ddf8c511f15..6d6c960eed7e6 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -43,6 +43,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeoutException; @@ -84,8 +85,8 @@ final class EphemeralResolverConnection implements DeathRecipient { mIntent = new Intent(action).setComponent(componentName); } - public final List getInstantAppResolveInfoList(int hashPrefix[], - String token) throws ConnectionException { + public final List getInstantAppResolveInfoList(Intent sanitizedIntent, + int hashPrefix[], String token) throws ConnectionException { throwIfCalledOnMainThread(); IInstantAppResolver target = null; try { @@ -98,7 +99,7 @@ final class EphemeralResolverConnection implements DeathRecipient { } try { return mGetEphemeralResolveInfoCaller - .getEphemeralResolveInfoList(target, hashPrefix, token); + .getEphemeralResolveInfoList(target, sanitizedIntent, hashPrefix, token); } catch (TimeoutException e) { throw new ConnectionException(ConnectionException.FAILURE_CALL); } catch (RemoteException ignore) { @@ -111,26 +112,22 @@ final class EphemeralResolverConnection implements DeathRecipient { return null; } - public final void getInstantAppIntentFilterList(int hashPrefix[], String token, - String hostName, PhaseTwoCallback callback, Handler callbackHandler, - final long startTime) throws ConnectionException { + public final void getInstantAppIntentFilterList(Intent sanitizedIntent, int hashPrefix[], + String token, PhaseTwoCallback callback, Handler callbackHandler, final long startTime) + throws ConnectionException { final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { final ArrayList resolveList = data.getParcelableArrayList( InstantAppResolverService.EXTRA_RESOLVE_INFO); - callbackHandler.post(new Runnable() { - @Override - public void run() { - callback.onPhaseTwoResolved(resolveList, startTime); - } - }); + callbackHandler.post(() -> callback.onPhaseTwoResolved(resolveList, startTime)); } }; try { getRemoteInstanceLazy(token) - .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback); + .getInstantAppIntentFilterList(sanitizedIntent, hashPrefix, token, + remoteCallback); } catch (TimeoutException e) { throw new ConnectionException(ConnectionException.FAILURE_BIND); } catch (InterruptedException e) { @@ -337,10 +334,11 @@ final class EphemeralResolverConnection implements DeathRecipient { } public List getEphemeralResolveInfoList( - IInstantAppResolver target, int hashPrefix[], String token) + IInstantAppResolver target, Intent sanitizedIntent, int hashPrefix[], String token) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); - target.getInstantAppResolveInfoList(hashPrefix, token, sequence, mCallback); + target.getInstantAppResolveInfoList(sanitizedIntent, hashPrefix, token, sequence, + mCallback); return getResultTimed(sequence); } } diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 30072d45ccabb..55212cc6b3d8c 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -40,11 +40,14 @@ import android.content.pm.InstantAppIntentFilter; import android.content.pm.InstantAppResolveInfo; import android.content.pm.InstantAppResolveInfo.InstantAppDigest; import android.metrics.LogMaker; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -53,8 +56,12 @@ import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.UUID; /** @hide */ @@ -79,6 +86,7 @@ public abstract class InstantAppResolver { public @interface ResolutionStatus {} private static MetricsLogger sMetricsLogger; + private static MetricsLogger getLogger() { if (sMetricsLogger == null) { sMetricsLogger = new MetricsLogger(); @@ -86,26 +94,49 @@ public abstract class InstantAppResolver { return sMetricsLogger; } - public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context, + /** + * Returns an intent with potential PII removed from the original intent. Fields removed + * include extras and the host + path of the data, if defined. + */ + public static Intent sanitizeIntent(Intent origIntent) { + final Intent sanitizedIntent; + sanitizedIntent = new Intent(origIntent.getAction()); + Set categories = origIntent.getCategories(); + if (categories != null) { + for (String category : categories) { + sanitizedIntent.addCategory(category); + } + } + Uri sanitizedUri = origIntent.getData() == null + ? null + : Uri.fromParts(origIntent.getScheme(), "", ""); + sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType()); + sanitizedIntent.addFlags(origIntent.getFlags()); + sanitizedIntent.setPackage(origIntent.getPackage()); + return sanitizedIntent; + } + + public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne( EphemeralResolverConnection connection, InstantAppRequest requestObj) { final long startTime = System.currentTimeMillis(); final String token = UUID.randomUUID().toString(); if (DEBUG_EPHEMERAL) { Log.d(TAG, "[" + token + "] Phase1; resolving"); } - final Intent intent = requestObj.origIntent; - final InstantAppDigest digest = - new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/); + final Intent origIntent = requestObj.origIntent; + final Intent sanitizedIntent = sanitizeIntent(origIntent); + + final InstantAppDigest digest = getInstantAppDigest(origIntent); final int[] shaPrefix = digest.getDigestPrefix(); AuxiliaryResolveInfo resolveInfo = null; @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS; try { final List instantAppResolveInfoList = - connection.getInstantAppResolveInfoList(shaPrefix, token); + connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token); if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { resolveInfo = InstantAppResolver.filterInstantAppIntent( - instantAppResolveInfoList, intent, requestObj.resolvedType, - requestObj.userId, intent.getPackage(), digest, token); + instantAppResolveInfoList, origIntent, requestObj.resolvedType, + requestObj.userId, origIntent.getPackage(), digest, token); } } catch (ConnectionException e) { if (e.failure == ConnectionException.FAILURE_BIND) { @@ -135,6 +166,12 @@ public abstract class InstantAppResolver { return resolveInfo; } + private static InstantAppDigest getInstantAppDigest(Intent origIntent) { + return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost()) + ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/) + : InstantAppDigest.UNDEFINED; + } + public static void doInstantAppResolutionPhaseTwo(Context context, EphemeralResolverConnection connection, InstantAppRequest requestObj, ActivityInfo instantAppInstaller, Handler callbackHandler) { @@ -143,73 +180,53 @@ public abstract class InstantAppResolver { if (DEBUG_EPHEMERAL) { Log.d(TAG, "[" + token + "] Phase2; resolving"); } - final Intent intent = requestObj.origIntent; - final String hostName = intent.getData().getHost(); - final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/); + final Intent origIntent = requestObj.origIntent; + final Intent sanitizedIntent = sanitizeIntent(origIntent); + final InstantAppDigest digest = getInstantAppDigest(origIntent); final int[] shaPrefix = digest.getDigestPrefix(); final PhaseTwoCallback callback = new PhaseTwoCallback() { @Override void onPhaseTwoResolved(List instantAppResolveInfoList, long startTime) { - final String packageName; - final String splitName; - final long versionCode; final Intent failureIntent; - final Bundle extras; if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { final AuxiliaryResolveInfo instantAppIntentInfo = InstantAppResolver.filterInstantAppIntent( - instantAppResolveInfoList, intent, null /*resolvedType*/, - 0 /*userId*/, intent.getPackage(), digest, token); - if (instantAppIntentInfo != null - && instantAppIntentInfo.resolveInfo != null) { - packageName = instantAppIntentInfo.resolveInfo.getPackageName(); - splitName = instantAppIntentInfo.splitName; - versionCode = instantAppIntentInfo.resolveInfo.getVersionCode(); + instantAppResolveInfoList, origIntent, null /*resolvedType*/, + 0 /*userId*/, origIntent.getPackage(), digest, token); + if (instantAppIntentInfo != null) { failureIntent = instantAppIntentInfo.failureIntent; - extras = instantAppIntentInfo.resolveInfo.getExtras(); } else { - packageName = null; - splitName = null; - versionCode = -1; failureIntent = null; - extras = null; } } else { - packageName = null; - splitName = null; - versionCode = -1; failureIntent = null; - extras = null; } final Intent installerIntent = buildEphemeralInstallerIntent( - Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE, requestObj.origIntent, + sanitizedIntent, failureIntent, requestObj.callingPackage, requestObj.verificationBundle, requestObj.resolvedType, requestObj.userId, - packageName, - splitName, requestObj.responseObj.installFailureActivity, - versionCode, token, - extras, - false /*needsPhaseTwo*/); + false /*needsPhaseTwo*/, + requestObj.responseObj.filters); installerIntent.setComponent(new ComponentName( instantAppInstaller.packageName, instantAppInstaller.name)); logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token, - packageName != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); + requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); context.startActivity(installerIntent); } }; try { - connection.getInstantAppIntentFilterList( - shaPrefix, token, hostName, callback, callbackHandler, startTime); + connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback, + callbackHandler, startTime); } catch (ConnectionException e) { @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE; if (e.failure == ConnectionException.FAILURE_BIND) { @@ -231,23 +248,20 @@ public abstract class InstantAppResolver { * Builds and returns an intent to launch the instant installer. */ public static Intent buildEphemeralInstallerIntent( - @NonNull String action, @NonNull Intent origIntent, - @NonNull Intent failureIntent, + @NonNull Intent sanitizedIntent, + @Nullable Intent failureIntent, @NonNull String callingPackage, @Nullable Bundle verificationBundle, @NonNull String resolvedType, int userId, - @NonNull String instantAppPackageName, - @Nullable String instantAppSplitName, @Nullable ComponentName installFailureActivity, - long versionCode, @Nullable String token, - @Nullable Bundle extras, - boolean needsPhaseTwo) { + boolean needsPhaseTwo, + List filters) { // Construct the intent that launches the instant installer int flags = origIntent.getFlags(); - final Intent intent = new Intent(action); + final Intent intent = new Intent(); intent.setFlags(flags | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK @@ -260,20 +274,23 @@ public abstract class InstantAppResolver { intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost()); } intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction()); - if (extras != null) { - intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras); - } + intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent); - // We have all of the data we need; just start the installer without a second phase - if (!needsPhaseTwo) { - // Intent that is launched if the package couldn't be installed for any reason. + if (needsPhaseTwo) { + intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); + } else { + // We have all of the data we need; just start the installer without a second phase if (failureIntent != null || installFailureActivity != null) { + // Intent that is launched if the package couldn't be installed for any reason. try { final Intent onFailureIntent; if (installFailureActivity != null) { onFailureIntent = new Intent(); onFailureIntent.setComponent(installFailureActivity); - onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); + if (filters != null && filters.size() == 1) { + onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, + filters.get(0).splitName); + } onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent); } else { onFailureIntent = failureIntent; @@ -309,17 +326,35 @@ public abstract class InstantAppResolver { intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(successIntentTarget)); } catch (RemoteException ignore) { /* ignore; same process */ } - - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName); - intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); - intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff)); - intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode); - intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); if (verificationBundle != null) { intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle); } - } + intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); + if (filters != null) { + Bundle resolvableFilters[] = new Bundle[filters.size()]; + for (int i = 0, max = filters.size(); i < max; i++) { + Bundle resolvableFilter = new Bundle(); + AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i); + resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP, + filter.resolveInfo != null + && filter.resolveInfo.shouldLetInstallerDecide()); + resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName); + resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName); + resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode); + resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras); + resolvableFilters[i] = resolvableFilter; + if (i == 0) { + // for backwards compat, always set the first result on the intent and add + // the int version code + intent.putExtras(resolvableFilter); + intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode); + } + } + intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters); + } + intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); + } return intent; } @@ -330,69 +365,134 @@ public abstract class InstantAppResolver { final int[] shaPrefix = digest.getDigestPrefix(); final byte[][] digestBytes = digest.getDigestBytes(); final Intent failureIntent = new Intent(origIntent); + boolean requiresSecondPhase = false; failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); failureIntent.setLaunchToken(token); - // Go in reverse order so we match the narrowest scope first. - for (int i = shaPrefix.length - 1; i >= 0 ; --i) { - for (InstantAppResolveInfo instantAppInfo : instantAppResolveInfoList) { - if (!Arrays.equals(digestBytes[i], instantAppInfo.getDigestBytes())) { + ArrayList filters = null; + boolean isWebIntent = origIntent.isBrowsableWebIntent(); + for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) { + if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) { + Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest" + + " provided; ignoring"); + continue; + } + byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes(); + // Only include matching digests if we have a prefix and we're either dealing with a + // web intent or the resolveInfo specifies digest details. + if (shaPrefix.length > 0 && (isWebIntent || filterDigestBytes.length > 0)) { + boolean matchFound = false; + // Go in reverse order so we match the narrowest scope first. + for (int i = shaPrefix.length - 1; i >= 0; --i) { + if (Arrays.equals(digestBytes[i], filterDigestBytes)) { + matchFound = true; + break; + } + } + if (!matchFound) { continue; } - if (packageName != null - && !packageName.equals(instantAppInfo.getPackageName())) { - continue; + } + // We matched a resolve info; resolve the filters to see if anything matches completely. + List matchFilters = computeResolveFilters( + origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo); + if (matchFilters != null) { + if (matchFilters.isEmpty()) { + requiresSecondPhase = true; } - final List instantAppFilters = - instantAppInfo.getIntentFilters(); - // No filters; we need to start phase two - if (instantAppFilters == null || instantAppFilters.isEmpty()) { - if (DEBUG_EPHEMERAL) { - Log.d(TAG, "No app filters; go to phase 2"); - } - return new AuxiliaryResolveInfo(instantAppInfo, - new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/, - null /*splitName*/, token, true /*needsPhase2*/, - null /*failureIntent*/); - } - // We have a domain match; resolve the filters to see if anything matches. - final PackageManagerService.EphemeralIntentResolver instantAppResolver = - new PackageManagerService.EphemeralIntentResolver(); - for (int j = instantAppFilters.size() - 1; j >= 0; --j) { - final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); - final List splitFilters = instantAppFilter.getFilters(); - if (splitFilters == null || splitFilters.isEmpty()) { - continue; - } - for (int k = splitFilters.size() - 1; k >= 0; --k) { - final AuxiliaryResolveInfo intentInfo = - new AuxiliaryResolveInfo(instantAppInfo, - splitFilters.get(k), instantAppFilter.getSplitName(), - token, false /*needsPhase2*/, failureIntent); - instantAppResolver.addFilter(intentInfo); - } - } - List matchedResolveInfoList = instantAppResolver.queryIntent( - origIntent, resolvedType, false /*defaultOnly*/, userId); - if (!matchedResolveInfoList.isEmpty()) { - if (DEBUG_EPHEMERAL) { - final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0); - Log.d(TAG, "[" + token + "] Found match;" - + " package: " + info.packageName - + ", split: " + info.splitName - + ", versionCode: " + info.versionCode); - } - return matchedResolveInfoList.get(0); - } else if (DEBUG_EPHEMERAL) { - Log.d(TAG, "[" + token + "] No matches found" - + " package: " + instantAppInfo.getPackageName() - + ", versionCode: " + instantAppInfo.getVersionCode()); + if (filters == null) { + filters = new ArrayList<>(matchFilters); + } else { + filters.addAll(matchFilters); } } } + if (filters != null && !filters.isEmpty()) { + return new AuxiliaryResolveInfo(token, requiresSecondPhase, failureIntent, filters); + } // Hash or filter mis-match; no instant apps for this domain. return null; } + /** + * Returns one of three states:

+ *

    + *
  • {@code null} if there are no matches will not be; resolution is unnecessary.
  • + *
  • An empty list signifying that a 2nd phase of resolution is required.
  • + *
  • A populated list meaning that matches were found and should be sent directly to the + * installer
  • + *
+ * + */ + private static List computeResolveFilters( + Intent origIntent, String resolvedType, int userId, String packageName, String token, + InstantAppResolveInfo instantAppInfo) { + if (instantAppInfo.shouldLetInstallerDecide()) { + return Collections.singletonList( + new AuxiliaryResolveInfo.AuxiliaryFilter( + instantAppInfo, null /* splitName */, + instantAppInfo.getExtras())); + } + if (packageName != null + && !packageName.equals(instantAppInfo.getPackageName())) { + return null; + } + final List instantAppFilters = + instantAppInfo.getIntentFilters(); + if (instantAppFilters == null || instantAppFilters.isEmpty()) { + // No filters on web intent; no matches, 2nd phase unnecessary. + if (origIntent.isBrowsableWebIntent()) { + return null; + } + // No filters; we need to start phase two + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "No app filters; go to phase 2"); + } + return Collections.emptyList(); + } + final PackageManagerService.EphemeralIntentResolver instantAppResolver = + new PackageManagerService.EphemeralIntentResolver(); + for (int j = instantAppFilters.size() - 1; j >= 0; --j) { + final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); + final List splitFilters = instantAppFilter.getFilters(); + if (splitFilters == null || splitFilters.isEmpty()) { + continue; + } + for (int k = splitFilters.size() - 1; k >= 0; --k) { + IntentFilter filter = splitFilters.get(k); + Iterator authorities = + filter.authoritiesIterator(); + // ignore http/s-only filters. + if ((authorities == null || !authorities.hasNext()) + && (filter.hasDataScheme("http") || filter.hasDataScheme("https")) + && filter.hasAction(Intent.ACTION_VIEW) + && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) { + continue; + } + instantAppResolver.addFilter( + new AuxiliaryResolveInfo.AuxiliaryFilter( + filter, + instantAppInfo, + instantAppFilter.getSplitName(), + instantAppInfo.getExtras() + )); + } + } + List matchedResolveInfoList = + instantAppResolver.queryIntent( + origIntent, resolvedType, false /*defaultOnly*/, userId); + if (!matchedResolveInfoList.isEmpty()) { + if (DEBUG_EPHEMERAL) { + Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList); + } + return matchedResolveInfoList; + } else if (DEBUG_EPHEMERAL) { + Log.d(TAG, "[" + token + "] No matches found" + + " package: " + instantAppInfo.getPackageName() + + ", versionCode: " + instantAppInfo.getVersionCode()); + } + return null; + } + private static void logMetrics(int action, long startTime, String token, @ResolutionStatus int status) { final LogMaker logMaker = new LogMaker(action) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 01c44af586edf..be988f2eb6fcc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -582,6 +582,7 @@ public class PackageManagerService extends IPackageManager.Stub sBrowserIntent.setAction(Intent.ACTION_VIEW); sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE); sBrowserIntent.setData(Uri.parse("http:")); + sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL); } /** @@ -3594,24 +3595,35 @@ Slog.e("TODD", } private @Nullable ActivityInfo getInstantAppInstallerLPr() { - final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); + String[] orderedActions = Build.IS_ENG + ? new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, + Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE} + : new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, + Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE}; final int resolveFlags = MATCH_DIRECT_BOOT_AWARE - | MATCH_DIRECT_BOOT_UNAWARE - | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - List matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, - resolveFlags, UserHandle.USER_SYSTEM); - // temporarily look for the old action - if (matches.isEmpty()) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "Ephemeral installer not found with new action; try old one"); - } - intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE); + | MATCH_DIRECT_BOOT_UNAWARE + | Intent.FLAG_IGNORE_EPHEMERAL + | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); + final Intent intent = new Intent(); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); + List matches = null; + for (String action : orderedActions) { + intent.setAction(action); matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); + if (matches.isEmpty()) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Instant App installer not found with " + action); + } + } else { + break; + } } Iterator iter = matches.iterator(); while (iter.hasNext()) { @@ -3619,7 +3631,8 @@ Slog.e("TODD", final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); if (ps != null) { final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)) { + if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) + || Build.IS_ENG) { continue; } } @@ -4778,10 +4791,7 @@ Slog.e("TODD", flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; - final boolean allowMatchInstant = - (wantInstantApps - && Intent.ACTION_VIEW.equals(intent.getAction()) - && hasWebURI(intent)) + final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); @@ -5971,8 +5981,14 @@ Slog.e("TODD", if (!skipPackageCheck && intent.getPackage() != null) { return false; } - final boolean isWebUri = hasWebURI(intent); - if (!isWebUri || intent.getData().getHost() == null) { + if (!intent.isBrowsableWebIntent()) { + // for non web intents, we should not resolve externally if an app already exists to + // handle it or if the caller didn't explicitly request it. + if ((resolvedActivities != null && resolvedActivities.size() != 0) + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { + return false; + } + } else if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. @@ -6370,7 +6386,7 @@ Slog.e("TODD", if (matches.get(i).getTargetUserId() == targetUserId) return true; } } - if (hasWebURI(intent)) { + if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); @@ -6545,7 +6561,7 @@ Slog.e("TODD", sortResult = true; } } - if (hasWebURI(intent)) { + if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { @@ -6631,7 +6647,6 @@ Slog.e("TODD", if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); - final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge @@ -6663,9 +6678,8 @@ Slog.e("TODD", null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); - auxiliaryResponse = - InstantAppResolver.doInstantAppResolutionPhaseOne( - mContext, mInstantAppResolverConnection, requestObject); + auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( + mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since @@ -6674,35 +6688,40 @@ Slog.e("TODD", // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; - auxiliaryResponse = new AuxiliaryResolveInfo( - ai.packageName, null /*splitName*/, null /*failureActivity*/, - ai.versionCode, null /*failureIntent*/); + auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, + ai.packageName, ai.versionCode, null /* splitName */); } } - if (auxiliaryResponse != null) { - if (DEBUG_EPHEMERAL) { - Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); - } - final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); - final PackageSetting ps = - mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); - if (ps != null) { - ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( - mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); - ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token; - ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; - // make sure this resolver is the default - ephemeralInstaller.isDefault = true; - ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART - | IntentFilter.MATCH_ADJUSTMENT_NORMAL; - // add a non-generic filter - ephemeralInstaller.filter = new IntentFilter(intent.getAction()); - ephemeralInstaller.filter.addDataPath( - intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); - ephemeralInstaller.isInstantAppAvailable = true; - result.add(ephemeralInstaller); - } + if (intent.isBrowsableWebIntent() && auxiliaryResponse == null) { + return result; } + final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); + if (ps == null) { + return result; + } + final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); + ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( + mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); + ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + // add a non-generic filter + ephemeralInstaller.filter = new IntentFilter(); + if (intent.getAction() != null) { + ephemeralInstaller.filter.addAction(intent.getAction()); + } + if (intent.getData() != null && intent.getData().getPath() != null) { + ephemeralInstaller.filter.addDataPath( + intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); + } + ephemeralInstaller.isInstantAppAvailable = true; + // make sure this resolver is the default + ephemeralInstaller.isDefault = true; + ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; + if (DEBUG_EPHEMERAL) { + Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); + } + + result.add(ephemeralInstaller); return result; } @@ -6817,10 +6836,11 @@ Slog.e("TODD", final ResolveInfo info = resolveInfos.get(i); // allow activities that are defined in the provided package if (allowDynamicSplits + && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { - if (mInstantAppInstallerInfo == null) { + if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } @@ -6832,14 +6852,15 @@ Slog.e("TODD", if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.activityInfo.packageName, info.activityInfo.splitName, installFailureActivity, + info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, - null /*failureIntent*/); + info.activityInfo.splitName); installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter @@ -6856,6 +6877,7 @@ Slog.e("TODD", installerInfo.priority = info.priority; installerInfo.preferredOrder = info.preferredOrder; installerInfo.isDefault = info.isDefault; + installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } @@ -6913,17 +6935,6 @@ Slog.e("TODD", return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } - private static boolean hasWebURI(Intent intent) { - if (intent.getData() == null) { - return false; - } - final String scheme = intent.getScheme(); - if (TextUtils.isEmpty(scheme)) { - return false; - } - return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS); - } - private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { @@ -7596,11 +7607,13 @@ Slog.e("TODD", if (DEBUG_EPHEMERAL) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.serviceInfo.packageName, info.serviceInfo.splitName, - null /*failureActivity*/, info.serviceInfo.applicationInfo.versionCode, - null /*failureIntent*/); + null /* installFailureActivity */, + info.serviceInfo.packageName, + info.serviceInfo.applicationInfo.versionCode, + info.serviceInfo.splitName); // make sure this resolver is the default installerInfo.isDefault = true; installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -7716,11 +7729,13 @@ Slog.e("TODD", if (DEBUG_EPHEMERAL) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.providerInfo.packageName, info.providerInfo.splitName, - null /*failureActivity*/, info.providerInfo.applicationInfo.versionCode, - null /*failureIntent*/); + null /*failureActivity*/, + info.providerInfo.packageName, + info.providerInfo.applicationInfo.versionCode, + info.providerInfo.splitName); // make sure this resolver is the default installerInfo.isDefault = true; installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -11794,7 +11809,7 @@ Slog.e("TODD", mInstantAppInstallerActivity.exported = true; mInstantAppInstallerActivity.enabled = true; mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity; - mInstantAppInstallerInfo.priority = 0; + mInstantAppInstallerInfo.priority = 1; mInstantAppInstallerInfo.preferredOrder = 1; mInstantAppInstallerInfo.isDefault = true; mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -13244,7 +13259,8 @@ Slog.e("TODD", } 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 @@ -13259,18 +13275,19 @@ Slog.e("TODD", final ArrayMap> mOrderResult = new ArrayMap<>(); @Override - protected AuxiliaryResolveInfo[] newArray(int size) { - return new AuxiliaryResolveInfo[size]; + protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) { + return new AuxiliaryResolveInfo.AuxiliaryFilter[size]; } @Override - protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) { + protected boolean isPackageForFilter(String packageName, + AuxiliaryResolveInfo.AuxiliaryFilter responseObj) { return true; } @Override - protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match, - int userId) { + protected AuxiliaryResolveInfo.AuxiliaryFilter newResult( + AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) { if (!sUserManager.exists(userId)) { return null; } @@ -13291,7 +13308,7 @@ Slog.e("TODD", } @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;