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

Commit efdf07b6 authored by Winson's avatar Winson
Browse files

Revoke domain user selection when approved through shell

This branch existed for the API, but not for the internal method used
for shell.

Bug: 183226822

Test: manual, debug while writing CTS

Change-Id: If19bf9395f445249c3ca3b93b7f6d1a1bad3c5d8
parent a9bb9e1b
Loading
Loading
Loading
Loading
+95 −99
Original line number Diff line number Diff line
@@ -357,6 +357,8 @@ public class DomainVerificationService extends SystemService
                        "State must be one of NO_RESPONSE, SUCCESS, APPROVED, or DENIED");
        }

        ArraySet<String> verifiedDomains = new ArraySet<>();

        if (packageName == null) {
            synchronized (mLock) {
                ArraySet<String> validDomains = new ArraySet<>();
@@ -383,6 +385,10 @@ public class DomainVerificationService extends SystemService
                        validDomains.retainAll(autoVerifyDomains);
                    }

                    if (DomainVerificationState.isVerified(state)) {
                        verifiedDomains.addAll(validDomains);
                    }

                    setDomainVerificationStatusInternal(pkgState, state, validDomains);
                }
            }
@@ -405,10 +411,22 @@ public class DomainVerificationService extends SystemService
                    domains.retainAll(mCollector.collectValidAutoVerifyDomains(pkg));
                }

                if (DomainVerificationState.isVerified(state)) {
                    verifiedDomains.addAll(domains);
                }

                setDomainVerificationStatusInternal(pkgState, state, domains);
            }
        }

        // Mirror SystemApi behavior of revoking user selection for approved domains.
        if (DomainVerificationState.isVerified(state)) {
            final int size = verifiedDomains.size();
            for (int index = 0; index < size; index++) {
                removeUserStatesForDomain(verifiedDomains.valueAt(index));
            }
        }

        mConnection.scheduleWriteSettings();
    }

@@ -522,47 +540,9 @@ public class DomainVerificationService extends SystemService
            // enabling. This allows an escape hatch in case multiple packages somehow get selected.
            // They can be disabled without blocking in a circular dependency.
            if (enabled) {
                // Cache the approved packages from the 1st pass because the search is expensive
                ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>();

                for (String domain : domains) {
                    if (userState.getEnabledHosts().contains(domain)) {
                        continue;
                    }

                    Pair<List<String>, Integer> packagesToLevel = getApprovedPackages(domain,
                            userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
                    int highestApproval = packagesToLevel.second;
                    if (highestApproval > APPROVAL_LEVEL_SELECTION) {
                        return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
                    }

                    domainToApprovedPackages.put(domain, packagesToLevel.first);
                }

                // The removal for other packages must be done in a 2nd pass after it's determined
                // that no higher priority owners exist for all of the domains in the set.
                int mapSize = domainToApprovedPackages.size();
                for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) {
                    String domain = domainToApprovedPackages.keyAt(mapIndex);
                    List<String> approvedPackages = domainToApprovedPackages.valueAt(mapIndex);
                    int approvedSize = approvedPackages.size();
                    for (int approvedIndex = 0; approvedIndex < approvedSize; approvedIndex++) {
                        String approvedPackage = approvedPackages.get(approvedIndex);
                        DomainVerificationPkgState approvedPkgState =
                                mAttachedPkgStates.get(approvedPackage);
                        if (approvedPkgState == null) {
                            continue;
                        }

                        DomainVerificationInternalUserState approvedUserState =
                                approvedPkgState.getUserState(userId);
                        if (approvedUserState == null) {
                            continue;
                        }

                        approvedUserState.removeHost(domain);
                    }
                int statusCode = revokeOtherUserSelections(userState, userId, domains);
                if (statusCode != DomainVerificationManager.STATUS_OK) {
                    return statusCode;
                }
            }

@@ -579,36 +559,10 @@ public class DomainVerificationService extends SystemService

    @Override
    public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
            @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains)
            @NonNull String packageName, boolean enabled, @Nullable ArraySet<String> domains)
            throws NameNotFoundException {
        mEnforcer.assertInternal(mConnection.getCallingUid());


        if (packageName == null) {
            synchronized (mLock) {
                Set<String> validDomains = new ArraySet<>();
                int size = mAttachedPkgStates.size();
                for (int index = 0; index < size; index++) {
                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
                    String pkgName = pkgState.getPackageName();
                    PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
                    AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
                    if (pkg == null) {
                        continue;
                    }

                    if (domains == null) {
                        validDomains = mCollector.collectAllWebDomains(pkg);
                    } else {
                        validDomains.clear();
                        validDomains.addAll(domains);
                    }

                    setDomainVerificationUserSelectionInternal(userId, pkgState,
                            pkg, enabled, validDomains);
                }
            }
        } else {
        synchronized (mLock) {
            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
            if (pkgState == null) {
@@ -624,38 +578,80 @@ public class DomainVerificationService extends SystemService
            Set<String> validDomains =
                    domains == null ? mCollector.collectAllWebDomains(pkg) : domains;

                setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(),
                        enabled, validDomains);
            }
        }

        mConnection.scheduleWriteSettings();
    }

    private void setDomainVerificationUserSelectionInternal(int userId,
            @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
            boolean enabled, Set<String> domains) {
        domains.retainAll(mCollector.collectAllWebDomains(pkg));
            validDomains.retainAll(mCollector.collectAllWebDomains(pkg));

            if (userId == UserHandle.USER_ALL) {
                for (int aUserId : mConnection.getAllUserIds()) {
                    DomainVerificationInternalUserState userState =
                            pkgState.getOrCreateUserState(aUserId);
                    revokeOtherUserSelections(userState, aUserId, validDomains);
                    if (enabled) {
                    userState.addHosts(domains);
                        userState.addHosts(validDomains);
                    } else {
                    userState.removeHosts(domains);
                        userState.removeHosts(validDomains);
                    }
                }
            } else {
                DomainVerificationInternalUserState userState =
                        pkgState.getOrCreateUserState(userId);
                revokeOtherUserSelections(userState, userId, validDomains);
                if (enabled) {
                userState.addHosts(domains);
                    userState.addHosts(validDomains);
                } else {
                userState.removeHosts(domains);
                    userState.removeHosts(validDomains);
                }
            }
        }

        mConnection.scheduleWriteSettings();
    }

    private int revokeOtherUserSelections(@NonNull DomainVerificationInternalUserState userState,
            @UserIdInt int userId, @NonNull Set<String> domains) {
        // Cache the approved packages from the 1st pass because the search is expensive
        ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>();

        for (String domain : domains) {
            if (userState.getEnabledHosts().contains(domain)) {
                continue;
            }

            Pair<List<String>, Integer> packagesToLevel = getApprovedPackages(domain,
                    userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
            int highestApproval = packagesToLevel.second;
            if (highestApproval > APPROVAL_LEVEL_SELECTION) {
                return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
            }

            domainToApprovedPackages.put(domain, packagesToLevel.first);
        }

        // The removal for other packages must be done in a 2nd pass after it's determined
        // that no higher priority owners exist for all of the domains in the set.
        int mapSize = domainToApprovedPackages.size();
        for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) {
            String domain = domainToApprovedPackages.keyAt(mapIndex);
            List<String> approvedPackages = domainToApprovedPackages.valueAt(mapIndex);
            int approvedSize = approvedPackages.size();
            for (int approvedIndex = 0; approvedIndex < approvedSize; approvedIndex++) {
                String approvedPackage = approvedPackages.get(approvedIndex);
                DomainVerificationPkgState approvedPkgState =
                        mAttachedPkgStates.get(approvedPackage);
                if (approvedPkgState == null) {
                    continue;
                }

                DomainVerificationInternalUserState approvedUserState =
                        approvedPkgState.getUserState(userId);
                if (approvedUserState == null) {
                    continue;
                }

                approvedUserState.removeHost(domain);
            }
        }

        return DomainVerificationManager.STATUS_OK;
    }

    @Nullable
+6 −6
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ public class DomainVerificationShell {
        pw.println("    must be declared by the package for this to work. This command will not");
        pw.println("    report a failure for domains that could not be applied.");
        pw.println("      --user <USER_ID>: the user to change selections for");
        pw.println("      --package <PACKAGE>: the package to set, or \"all\" to set all packages");
        pw.println("      --package <PACKAGE>: the package to set");
        pw.println("      <ENABLED>: whether or not to approve the domain");
        pw.println("      <DOMAINS>: space separated list of domains to change, or \"all\" to");
        pw.println("        change every domain.");
@@ -217,8 +217,6 @@ public class DomainVerificationShell {
        if (TextUtils.isEmpty(packageName)) {
            commandHandler.getErrPrintWriter().println("Error: no package specified");
            return false;
        } else if (packageName.equalsIgnoreCase("all")) {
            packageName = null;
        }

        if (userId == null) {
@@ -469,13 +467,15 @@ public class DomainVerificationShell {
         * Variant for use by PackageManagerShellCommand to allow the system/developer to override
         * the state for a domain.
         *
         * @param packageName the package whose state to change, or all packages if non is
         *                    specified
         * If an approval fails because of a higher level owner, this method will silently skip the
         * domain.
         *
         * @param packageName the package whose state to change
         * @param enabled     whether the domain is now approved by the user
         * @param domains     the set of domains to change, or null to affect all domains
         */
        void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
                @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains)
                @NonNull String packageName, boolean enabled, @Nullable ArraySet<String> domains)
                throws PackageManager.NameNotFoundException;

        /**