Update app links resolution to handle no category Intents
If an Intent has no categories except DEFAULT, and there is only 1 non-browser result resolved along with 1+ browsers, the set of browsers will be ignored and app links will be run as if the intent was resolved with the BROWSABLE category. This means callers who omit BROWSABLE but resolve to only 1 app + browsers will automatically launch into the app. Bug: 181570854 Test: manual, am start launch test intents Change-Id: I1adfd41c64abd46dc98930ba36409550955275a9
This commit is contained in:
@@ -2631,7 +2631,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
// We'll want to include browser possibilities in a few cases
|
||||
boolean includeBrowser = false;
|
||||
|
||||
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
|
||||
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
|
||||
matchFlags)) {
|
||||
result.addAll(undefinedList);
|
||||
// Maybe add one for the other profile.
|
||||
if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
|
||||
@@ -2815,8 +2816,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
result.highestApprovalLevel = Math.max(mDomainVerificationManager
|
||||
.approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId),
|
||||
result.highestApprovalLevel);
|
||||
.approvalLevelForDomain(ps, intent, resultTargetUser, flags,
|
||||
riTargetUser.targetUserId), result.highestApprovalLevel);
|
||||
}
|
||||
if (result != null && result.highestApprovalLevel
|
||||
<= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
|
||||
@@ -3062,8 +3063,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
final String packageName = info.activityInfo.packageName;
|
||||
final PackageSetting ps = mSettings.getPackageLPr(packageName);
|
||||
if (ps.getInstantApp(userId)) {
|
||||
if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
|
||||
userId)) {
|
||||
if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
|
||||
instantApps, flags, userId)) {
|
||||
if (DEBUG_INSTANT) {
|
||||
Slog.v(TAG, "Instant app approved for intent; pkg: "
|
||||
+ packageName);
|
||||
@@ -3990,8 +3991,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
if (ps != null) {
|
||||
// only check domain verification status if the app is not a browser
|
||||
if (!info.handleAllWebDataURI) {
|
||||
if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
|
||||
userId)) {
|
||||
if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
|
||||
resolvedActivities, flags, userId)) {
|
||||
if (DEBUG_INSTANT) {
|
||||
Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
|
||||
+ ", approved");
|
||||
@@ -9610,7 +9611,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
final String packageName = ri.activityInfo.packageName;
|
||||
final PackageSetting ps = mSettings.getPackageLPr(packageName);
|
||||
if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
|
||||
intent, flags, userId)) {
|
||||
intent, query, flags, userId)) {
|
||||
return ri;
|
||||
}
|
||||
}
|
||||
@@ -9667,10 +9668,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
*/
|
||||
private static boolean hasAnyDomainApproval(
|
||||
@NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
|
||||
@NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
|
||||
@UserIdInt int userId) {
|
||||
return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
|
||||
> DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
|
||||
@NonNull Intent intent, @NonNull List<ResolveInfo> candidates,
|
||||
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
|
||||
return manager.approvalLevelForDomain(pkgSetting, intent, candidates, resolveInfoFlags,
|
||||
userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -310,6 +310,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
|
||||
*/
|
||||
@ApprovalLevel
|
||||
int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
|
||||
@NonNull List<ResolveInfo> candidates,
|
||||
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1494,9 +1494,11 @@ public class DomainVerificationService extends SystemService
|
||||
|
||||
@Override
|
||||
public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
|
||||
@NonNull List<ResolveInfo> candidates,
|
||||
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
|
||||
String packageName = pkgSetting.getName();
|
||||
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
|
||||
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
|
||||
resolveInfoFlags)) {
|
||||
if (DEBUG_APPROVAL) {
|
||||
debugApproval(packageName, intent, userId, false, "not valid intent");
|
||||
}
|
||||
|
||||
@@ -22,12 +22,17 @@ import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Binder;
|
||||
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.server.compat.PlatformCompat;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class DomainVerificationUtils {
|
||||
|
||||
/**
|
||||
@@ -40,13 +45,61 @@ public final class DomainVerificationUtils {
|
||||
throw new NameNotFoundException("Package " + packageName + " unavailable");
|
||||
}
|
||||
|
||||
public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) {
|
||||
if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
|
||||
public static boolean isDomainVerificationIntent(Intent intent,
|
||||
@NonNull List<ResolveInfo> candidates,
|
||||
@PackageManager.ResolveInfoFlags int resolveInfoFlags) {
|
||||
if (!intent.isWebIntent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0)
|
||||
|| intent.hasCategory(Intent.CATEGORY_DEFAULT);
|
||||
Set<String> categories = intent.getCategories();
|
||||
int categoriesSize = CollectionUtils.size(categories);
|
||||
if (categoriesSize > 2) {
|
||||
// Specifying at least one non-app-link category
|
||||
return false;
|
||||
} else if (categoriesSize == 2) {
|
||||
// Check for explicit app link intent with exactly BROWSABLE && DEFAULT
|
||||
return intent.hasCategory(Intent.CATEGORY_DEFAULT)
|
||||
&& intent.hasCategory(Intent.CATEGORY_BROWSABLE);
|
||||
}
|
||||
|
||||
// In cases where at least one browser is resolved and only one non-browser is resolved,
|
||||
// the Intent is coerced into an app links intent, under the assumption the browser can
|
||||
// be skipped if the app is approved at any level for the domain.
|
||||
boolean foundBrowser = false;
|
||||
boolean foundOneApp = false;
|
||||
|
||||
final int candidatesSize = candidates.size();
|
||||
for (int index = 0; index < candidatesSize; index++) {
|
||||
final ResolveInfo info = candidates.get(index);
|
||||
if (info.handleAllWebDataURI) {
|
||||
foundBrowser = true;
|
||||
} else if (foundOneApp) {
|
||||
// Already true, so duplicate app
|
||||
foundOneApp = false;
|
||||
break;
|
||||
} else {
|
||||
foundOneApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
|
||||
boolean onlyOneNonBrowser = foundBrowser && foundOneApp;
|
||||
|
||||
// Check if matches (BROWSABLE || none) && DEFAULT
|
||||
if (categoriesSize == 0) {
|
||||
// No categories, run coerce case, matching DEFAULT by flags
|
||||
return onlyOneNonBrowser && matchDefaultByFlags;
|
||||
} else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
|
||||
// Run coerce case, matching by explicit DEFAULT
|
||||
return onlyOneNonBrowser;
|
||||
} else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
|
||||
// Intent matches BROWSABLE, must match DEFAULT by flags
|
||||
return matchDefaultByFlags;
|
||||
} else {
|
||||
// Otherwise not matching any app link categories
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
|
||||
|
||||
Reference in New Issue
Block a user