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

Commit 0cab1296 authored by Winson's avatar Winson
Browse files

Use new always open during PMS Activity resolution

Implements the behavior of either delegating to an approved app for
a web domain, or the browsers, removing the "always ask" and "never"
options for applications.

This also considers cross profile intents, and will allow those to be
shown if the other profile has an approved handler.

This also migrates instant app state to the new behavior. Instant apps
will now be granted completely immutable autoVerify approval, to
preserve their automatic opening behavior. The user would have to
disable the app in settings similar to an auto approved system app.

This is flagged through USE_DOMAIN_VERIFICATION_V2. For now, both v1
and v2 run entirely parallel, until the v1 APIs are removed in a
follow up.

Exempt-From-Owner-Approval: Already approved by owners on main branch

Bug: 163564991

Test: TODO

Change-Id: I1a90f6b2b9bf2f108c5dc1b944985bc150c405a7
parent fa302df9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ public interface DomainVerificationManager {
                return "restored";
            case DomainVerificationState.STATE_LEGACY_FAILURE:
                return "legacy_failure";
            case DomainVerificationState.STATE_SYS_CONFIG:
                return "system_configured";
            default:
                return String.valueOf(state);
        }
@@ -115,6 +117,7 @@ public interface DomainVerificationManager {
            case DomainVerificationState.STATE_APPROVED:
            case DomainVerificationState.STATE_MIGRATED:
            case DomainVerificationState.STATE_RESTORED:
            case DomainVerificationState.STATE_SYS_CONFIG:
                return true;
            case DomainVerificationState.STATE_NO_RESPONSE:
            case DomainVerificationState.STATE_DENIED:
@@ -140,6 +143,7 @@ public interface DomainVerificationManager {
                return true;
            case DomainVerificationState.STATE_APPROVED:
            case DomainVerificationState.STATE_DENIED:
            case DomainVerificationState.STATE_SYS_CONFIG:
                return false;
            default:
                return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
@@ -161,6 +165,7 @@ public interface DomainVerificationManager {
            case DomainVerificationState.STATE_APPROVED:
            case DomainVerificationState.STATE_DENIED:
            case DomainVerificationState.STATE_LEGACY_FAILURE:
            case DomainVerificationState.STATE_SYS_CONFIG:
            default:
                return false;
        }
+9 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ public interface DomainVerificationState {
            STATE_APPROVED,
            STATE_DENIED,
            STATE_LEGACY_FAILURE,
            STATE_SYS_CONFIG,
            STATE_FIRST_VERIFIER_DEFINED
    })
    @interface State {
@@ -53,8 +54,6 @@ public interface DomainVerificationState {
    /**
     * The system has chosen to ignore the verification agent's opinion on whether the domain should
     * be verified. This will treat the domain as verified.
     * <p>
     * TODO: This currently combines SysConfig and instant app. Is it worth separating those?
     */
    int STATE_APPROVED = 2;

@@ -86,6 +85,14 @@ public interface DomainVerificationState {
     */
    int STATE_LEGACY_FAILURE = 6;

    /**
     * The application has been granted auto verification for all domains by configuration on the
     * system image.
     *
     * TODO: Can be stored per-package rather than for all domains for a package to save memory.
     */
    int STATE_SYS_CONFIG = 7;

    /**
     * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
     */
+99 −28
Original line number Diff line number Diff line
@@ -238,6 +238,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
import android.content.pm.domain.verify.DomainVerificationManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -1624,6 +1625,8 @@ public class PackageManagerService extends IPackageManager.Stub
    private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
            2 * 60 * 60 * 1000L; /* two hours */
    private static final boolean USE_DOMAIN_VERIFICATION_V2 = true;
    final UserManagerService mUserManager;
    // Stores a list of users whose package restrictions file needs to be updated
@@ -2154,6 +2157,7 @@ public class PackageManagerService extends IPackageManager.Stub
        private final ComponentResolver mComponentResolver;
        private final InstantAppResolverConnection mInstantAppResolverConnection;
        private final DefaultAppProvider mDefaultAppProvider;
        private final DomainVerificationManagerInternal mDomainVerificationManager;
        // PackageManagerService attributes that are primitives are referenced through the
        // pms object directly.  Primitives are the only attributes so referenced.
@@ -2199,6 +2203,7 @@ public class PackageManagerService extends IPackageManager.Stub
            mComponentResolver = args.service.mComponentResolver;
            mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
            mDefaultAppProvider = args.service.mDefaultAppProvider;
            mDomainVerificationManager = args.service.mDomainVerificationManager;
            // Used to reference PMS attributes that are primitives and which are not
            // updated under control of the PMS lock.
@@ -2727,6 +2732,18 @@ public class PackageManagerService extends IPackageManager.Stub
                        matchAllList.add(info);
                        continue;
                    }
                    if (USE_DOMAIN_VERIFICATION_V2) {
                        boolean isAlways = mDomainVerificationManager
                                .isApprovedForDomain(ps, intent, userId);
                        if (isAlways) {
                            alwaysList.add(info);
                        } else {
                            undefinedList.add(info);
                        }
                        continue;
                    }
                    // Try to get the status from User settings first
                    long packedStatus = IntentVerifyUtils.getDomainVerificationStatus(ps, userId);
                    int status = (int)(packedStatus >> 32);
@@ -2777,11 +2794,16 @@ public class PackageManagerService extends IPackageManager.Stub
                // Add all undefined apps as we want them to appear in the disambiguation dialog.
                result.addAll(undefinedList);
                // Maybe add one for the other profile.
                if (xpDomainInfo != null && (
                        xpDomainInfo.bestDomainVerificationStatus
                        != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
                if (xpDomainInfo != null) {
                    if (USE_DOMAIN_VERIFICATION_V2) {
                        if (xpDomainInfo.wereAnyDomainsVerificationApproved) {
                            result.add(xpDomainInfo.resolveInfo);
                        }
                    } else if (xpDomainInfo.bestDomainVerificationStatus
                            != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
                        result.add(xpDomainInfo.resolveInfo);
                    }
                }
                includeBrowser = true;
            }
@@ -2940,24 +2962,34 @@ public class PackageManagerService extends IPackageManager.Stub
                if (ps == null) {
                    continue;
                }
                long verificationState =
                        IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId);
                int status = (int)(verificationState >> 32);
                if (result == null) {
                    result = new CrossProfileDomainInfo();
                    result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
                            sourceUserId, parentUserId);
                    result.bestDomainVerificationStatus = status;
                }
                if (USE_DOMAIN_VERIFICATION_V2) {
                    result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager
                            .isApprovedForDomain(ps, intent, riTargetUser.targetUserId);
                } else {
                    long verificationState =
                            IntentVerifyUtils.getDomainVerificationStatus(ps, parentUserId);
                    int status = (int) (verificationState >> 32);
                    result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
                            result.bestDomainVerificationStatus);
                }
            }
            // Don't consider matches with status NEVER across profiles.
            if (result != null && result.bestDomainVerificationStatus
            if (result != null) {
                if (USE_DOMAIN_VERIFICATION_V2) {
                    if (!result.wereAnyDomainsVerificationApproved) {
                        return null;
                    }
                } else if (result.bestDomainVerificationStatus
                        == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
                    // Don't consider matches with status NEVER across profiles.
                    return null;
                }
            }
            return result;
        }
@@ -3198,6 +3230,25 @@ public class PackageManagerService extends IPackageManager.Stub
                    final String packageName = info.activityInfo.packageName;
                    final PackageSetting ps = mSettings.getPackageLPr(packageName);
                    if (ps.getInstantApp(userId)) {
                        if (USE_DOMAIN_VERIFICATION_V2) {
                            if (mDomainVerificationManager
                                    .isApprovedForDomain(ps, intent, userId)) {
                                if (DEBUG_INSTANT) {
                                    Slog.v(TAG, "Instant app approvd for intent; pkg: "
                                            + packageName);
                                }
                                localInstantApp = info;
                                break;
                            } else {
                                if (DEBUG_INSTANT) {
                                    Slog.v(TAG, "Instant app not approved for intent; pkg: "
                                            + packageName);
                                }
                                blockResolution = true;
                                break;
                            }
                        }
                        final long packedStatus =
                                IntentVerifyUtils.getDomainVerificationStatus(ps, userId);
                        final int status = (int)(packedStatus >> 32);
@@ -4132,12 +4183,23 @@ 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 (USE_DOMAIN_VERIFICATION_V2) {
                            if (mDomainVerificationManager
                                    .isApprovedForDomain(ps, intent, userId)) {
                                if (DEBUG_INSTANT) {
                                    Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
                                            + ", approved");
                                }
                                return false;
                            }
                        } else {
                            // Try to get the status from User settings first
                            final long packedStatus =
                                    IntentVerifyUtils.getDomainVerificationStatus(ps, userId);
                            final int status = (int) (packedStatus >> 32);
                            if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
                                || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
                                    || status
                                    == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
                                if (DEBUG_INSTANT) {
                                    Slog.v(TAG, "DENY instant app;"
                                            + " pkg: " + packageName + ", status: " + status);
@@ -4145,6 +4207,7 @@ public class PackageManagerService extends IPackageManager.Stub
                                return false;
                            }
                        }
                    }
                    if (ps.getInstantApp(userId)) {
                        if (DEBUG_INSTANT) {
                            Slog.v(TAG, "DENY instant app installed;"
@@ -9573,6 +9636,12 @@ public class PackageManagerService extends IPackageManager.Stub
                    if (ri.activityInfo.applicationInfo.isInstantApp()) {
                        final String packageName = ri.activityInfo.packageName;
                        final PackageSetting ps = mSettings.getPackageLPr(packageName);
                        if (USE_DOMAIN_VERIFICATION_V2) {
                            if (ps != null && mDomainVerificationManager
                                    .isApprovedForDomain(ps, intent, userId)) {
                                return ri;
                            }
                        } else {
                            final long packedStatus =
                                    IntentVerifyUtils.getDomainVerificationStatus(ps, userId);
                            final int status = (int) (packedStatus >> 32);
@@ -9581,6 +9650,7 @@ public class PackageManagerService extends IPackageManager.Stub
                            }
                        }
                    }
                }
                if ((privateResolveFlags
                        & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
                    return null;
@@ -10069,7 +10139,8 @@ public class PackageManagerService extends IPackageManager.Stub
        /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
        ResolveInfo resolveInfo;
        /* Best domain verification status of the activities found in the other profile */
        int bestDomainVerificationStatus;
        int bestDomainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
        boolean wereAnyDomainsVerificationApproved;
    }
    private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
+9 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.pm.domain.verify;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.domain.verify.DomainVerificationManager;
import android.content.pm.domain.verify.DomainVerificationSet;
@@ -157,4 +158,12 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan

    @NonNull
    DomainVerificationShell getShell();

    /**
     * Check if a resolving URI is approved to takeover the domain as the sole resolved target.
     * This can be because the domain was auto-verified for the package, or if the user manually
     * chose to enable the domain for the package.
     */
    boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
            @UserIdInt int userId);
}
+128 −11
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -70,6 +71,8 @@ public class DomainVerificationService extends SystemService

    private static final String TAG = "DomainVerificationService";

    public static final boolean DEBUG_APPROVAL = true;

    /**
     * States that are currently alive and attached to a package. Entries are exclusive with the
     * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be
@@ -612,13 +615,10 @@ public class DomainVerificationService extends SystemService
            }

            boolean hasAutoVerifyDomains = newDomainsSize > 0;
            boolean stateApplied = applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);
            boolean needsBroadcast =
                    applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);

            // TODO(b/159952358): sendBroadcast should be abstracted so it doesn't have to be aware
            //  of whether/what state was applied. Probably some method which iterates the map to
            //  check for any domains that actually have state changeable by the domain verification
            //  agent.
            sendBroadcast = hasAutoVerifyDomains && !stateApplied;
            sendBroadcast = hasAutoVerifyDomains && needsBroadcast;

            mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState(
                    pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates));
@@ -661,8 +661,8 @@ public class DomainVerificationService extends SystemService
            pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
        }

        boolean stateApplied = applyImmutableState(pkgState, domains);
        if (!stateApplied && !isPendingOrRestored) {
        boolean needsBroadcast = applyImmutableState(pkgState, domains);
        if (needsBroadcast && !isPendingOrRestored) {
            // TODO(b/159952358): Test this behavior
            // Attempt to preserve user experience by automatically verifying all domains from
            // legacy state if they were previously approved, or by automatically enabling all
@@ -719,6 +719,8 @@ public class DomainVerificationService extends SystemService
    /**
     * Applies any immutable state as the final step when adding or migrating state. Currently only
     * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package.
     *
     * @return whether or not a broadcast is necessary for this package
     */
    private boolean applyImmutableState(@NonNull String packageName,
            @NonNull ArrayMap<String, Integer> stateMap,
@@ -727,12 +729,21 @@ public class DomainVerificationService extends SystemService
            int domainsSize = autoVerifyDomains.size();
            for (int index = 0; index < domainsSize; index++) {
                stateMap.put(autoVerifyDomains.valueAt(index),
                        DomainVerificationState.STATE_APPROVED);
                        DomainVerificationState.STATE_SYS_CONFIG);
            }
            return false;
        } else {
            int size = stateMap.size();
            for (int index = size - 1; index >= 0; index--) {
                Integer state = stateMap.valueAt(index);
                // If no longer marked in SysConfig, demote any previous SysConfig state
                if (state == DomainVerificationState.STATE_SYS_CONFIG) {
                    stateMap.removeAt(index);
                }
            return true;
            }

        return false;
            return true;
        }
    }

    @Override
@@ -1002,6 +1013,112 @@ public class DomainVerificationService extends SystemService
        }
    }

    @Override
    public boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
            @UserIdInt int userId) {
        String packageName = pkgSetting.name;
        if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
            if (DEBUG_APPROVAL) {
                debugApproval(packageName, intent, userId, false, "not valid intent");
            }
            return false;
        }

        // To allow an instant app to immediately open domains after being installed by the user,
        // auto approve them for any declared autoVerify domains.
        String host = intent.getData().getHost();
        final AndroidPackage pkg = pkgSetting.getPkg();
        if (pkgSetting.getInstantApp(userId) && pkg != null
                && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
            return true;
        }

        synchronized (mLock) {
            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
            if (pkgState == null) {
                if (DEBUG_APPROVAL) {
                    debugApproval(packageName, intent, userId, false, "pkgState unavailable");
                }
                return false;
            }

            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
            DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);

            // Only allow autoVerify approval if the user hasn't disabled it
            if (userState == null || !userState.isDisallowLinkHandling()) {
                // Check if the exact host matches
                Integer state = stateMap.get(host);
                if (state != null && DomainVerificationManager.isStateVerified(state)) {
                    if (DEBUG_APPROVAL) {
                        debugApproval(packageName, intent, userId, true, "host verified exactly");
                    }
                    return true;
                }

                // Otherwise see if the host matches a verified domain by wildcard
                int stateMapSize = stateMap.size();
                for (int index = 0; index < stateMapSize; index++) {
                    if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
                        continue;
                    }

                    String domain = stateMap.keyAt(index);
                    if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
                        if (DEBUG_APPROVAL) {
                            debugApproval(packageName, intent, userId, true,
                                    "host verified by wildcard");
                        }
                        return true;
                    }
                }
            }

            // Check user state if available
            if (userState == null) {
                if (DEBUG_APPROVAL) {
                    debugApproval(packageName, intent, userId, false, "userState unavailable");
                }
                return false;
            }

            // See if the user has approved the exact host
            ArraySet<String> enabledHosts = userState.getEnabledHosts();
            if (enabledHosts.contains(host)) {
                if (DEBUG_APPROVAL) {
                    debugApproval(packageName, intent, userId, true,
                            "host enabled by user exactly");
                }
                return true;
            }

            // See if the host matches a user selection by wildcard
            int enabledHostsSize = enabledHosts.size();
            for (int index = 0; index < enabledHostsSize; index++) {
                String domain = enabledHosts.valueAt(index);
                if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
                    if (DEBUG_APPROVAL) {
                        debugApproval(packageName, intent, userId, true,
                                "host enabled by user through wildcard");
                    }
                    return true;
                }
            }

            if (DEBUG_APPROVAL) {
                debugApproval(packageName, intent, userId, false, "not approved");
            }
            return false;
        }
    }

    private void debugApproval(@NonNull String packageName, @NonNull Intent intent,
            @UserIdInt int userId, boolean approved, @NonNull String reason) {
        String approvalString = approved ? "approved" : "denied";
        Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent
                + " for user " + userId + ": " + reason);
    }

    public interface Connection {

        /**
Loading