Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2db8c825 authored by Winson's avatar Winson
Browse files

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
parent 8760b38f
Loading
Loading
Loading
Loading
+13 −12
Original line number Diff line number Diff line
@@ -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;
    }
    /**
+1 −0
Original line number Diff line number Diff line
@@ -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);

    /**
+3 −1
Original line number Diff line number Diff line
@@ -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");
            }
+57 −4
Original line number Diff line number Diff line
@@ -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;
        }

        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);
        }

        return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0)
                || intent.hasCategory(Intent.CATEGORY_DEFAULT);
            // 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,