diff options
| author | Patrick Baumann <patb@google.com> | 2018-01-31 16:55:10 +0000 |
|---|---|---|
| committer | Patrick Baumann <patb@google.com> | 2018-01-31 14:00:18 -0800 |
| commit | 577d402d0d938c14d415054289a5ecbc613d0046 (patch) | |
| tree | e8e9c89459ffefa10765bb1dbf51bac4ee8e2e37 /core/java/android | |
| parent | 64dd7468f3027ddd8563b6f9d8ac3b13b5ff78b0 (diff) | |
Revert "Revert "Adds generic intent Instant App resolution""
This reverts commit 860b8ba71938e9860a31881c1d1431877f9d01a2.
The original change that was reverted contained a bug that allowed an
http view/browsable intent used to query for browsers to be considered
as a candidate for instant apps. This was resulting in an attempt to
bind to the instant app resolver while holding a lock on mPackages.
This change ensures that PMS doesn't bind while checking for the browser
status of a package in both the instant app filtering code and by adding
the FLAG_IGNORE_EPHEMERAL to the canonical browser intent.
Reason for revert: Applying fix
Change-Id: I4896b3a15416a11fdc3f6c191e552c4ce8963623
Fixes: 63117034
Fixes: 71916178
Test: Manual using test app at google_experimental/users/patb/InstantAppsInP
Test: atest android.appsecurity.cts.EphemeralTest passes after modification
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/EphemeralResolverService.java | 12 | ||||
| -rw-r--r-- | core/java/android/app/IInstantAppResolver.aidl | 8 | ||||
| -rw-r--r-- | core/java/android/app/InstantAppResolverService.java | 107 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 50 | ||||
| -rw-r--r-- | core/java/android/content/pm/AuxiliaryResolveInfo.java | 99 | ||||
| -rw-r--r-- | core/java/android/content/pm/InstantAppResolveInfo.java | 96 |
6 files changed, 279 insertions, 93 deletions
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java index 427a0386e87c..d1c244136ec9 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 805d8c057d27..ae200578d829 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 c5dc86c79ef9..89aff36f883a 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 e02a29494296..b3c8737a2831 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. @@ -4482,6 +4491,16 @@ public class Intent implements Parcelable, Cloneable { "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). * @hide @@ -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 6bdcefbe974e..202df50dda6f 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<AuxiliaryFilter> 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<AuxiliaryFilter> 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<AuxiliaryFilter> 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 19cb9323ba93..112c5dae6731 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: <p/> + * <ul> + * <li> + * 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. + * </li> + * <li> + * 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. + * </li> + * <li> + * The third represents a complete match and is constructed with all @Nullable parameters + * populated. + * </li> + * </ul> * @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<InstantAppIntentFilter> 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<InstantAppIntentFilter> 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<InstantAppIntentFilter> 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<InstantAppIntentFilter>(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<InstantAppIntentFilter> 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<InstantAppIntentFilter>(); - 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<InstantAppResolveInfo> 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<byte[]> digests = new ArrayList<>(); try { |
