From a6862c238039cba1b2f6175beca58f8d76e1bef9 Mon Sep 17 00:00:00 2001 From: Patrick Baumann Date: Thu, 3 May 2018 13:16:01 -0700 Subject: [PATCH] Support generic filters when EXTERNAL flag is set This change opts instant app resolution out of hash prefix matching if the intent filter being tested does not define an authority only when the MATCH_EXTERNAL flag is set. This allows for a class of instant apps that generically handle certain mime types or actions, but aren't tied to a particular authority / domain. This also moves the null check for resolution to a higher level, accounting for connection-related failures to ensure that the installer always gets a properly formatted intent. Bug: 63117034 Test: manual - force connection timeouts and observe correct install intent Test: manual - launch a generic type handler for a made up mime type; observe correct resolution Test: atest EphemeralTest Change-Id: I0101e3219f543897a488ddd3d7c4c884f7d6cac3 --- .../android/server/pm/InstantAppResolver.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index d0a3757be04d7..9a5dd5e04b072 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -47,7 +47,6 @@ 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; @@ -164,6 +163,11 @@ public abstract class InstantAppResolver { Log.d(TAG, "[" + token + "] Phase1; No results matched"); } } + // if the match external flag is set, return an empty resolve info instead of a null result. + if (resolveInfo == null && (origIntent.getFlags() & FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + return new AuxiliaryResolveInfo(token, false, createFailureIntent(origIntent, token), + null /* filters */); + } return resolveInfo; } @@ -365,23 +369,20 @@ public abstract class InstantAppResolver { InstantAppDigest digest, String token) { 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.setFlags(failureIntent.getFlags() & ~Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); - failureIntent.setLaunchToken(token); ArrayList filters = null; - boolean isWebIntent = origIntent.isWebIntent(); + boolean requiresPrefixMatch = origIntent.isWebIntent() || (shaPrefix.length > 0 + && (origIntent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0); for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) { - if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) { - Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest" - + " provided; ignoring"); + if (requiresPrefixMatch && instantAppResolveInfo.shouldLetInstallerDecide()) { + Slog.d(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest" + + " required; 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)) { + // prefixed request or the resolveInfo specifies digest details. + if (shaPrefix.length > 0 && (requiresPrefixMatch || 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) { @@ -409,16 +410,25 @@ public abstract class InstantAppResolver { } } if (filters != null && !filters.isEmpty()) { - return new AuxiliaryResolveInfo(token, requiresSecondPhase, failureIntent, filters); - } - // if the match external flag is set, return an empty resolve info - if ((origIntent.getFlags() & FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { - return new AuxiliaryResolveInfo(token, false, failureIntent, null /* filters */); + return new AuxiliaryResolveInfo(token, requiresSecondPhase, + createFailureIntent(origIntent, token), filters); } // Hash or filter mis-match; no instant apps for this domain. return null; } + /** + * Creates a failure intent for the installer to send in the case that the instant app cannot be + * launched for any reason. + */ + private static Intent createFailureIntent(Intent origIntent, String token) { + final Intent failureIntent = new Intent(origIntent); + failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); + failureIntent.setFlags(failureIntent.getFlags() & ~Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); + failureIntent.setLaunchToken(token); + return failureIntent; + } + /** * Returns one of three states:

*