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

Commit 0b5dedfb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add pm command for getOwnersForDomain API" into sc-dev

parents e723159e cf207c30
Loading
Loading
Loading
Loading
+68 −14
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.pm.verify.domain;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Intent;
@@ -33,6 +34,8 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;

import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@@ -44,6 +47,12 @@ public class DomainVerificationCollector {

    private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024;

    private static final BiFunction<ArraySet<String>, String, Boolean> ARRAY_SET_COLLECTOR =
            (set, domain) -> {
                set.add(domain);
                return null;
            };

    @NonNull
    private final PlatformCompat mPlatformCompat;

@@ -105,27 +114,62 @@ public class DomainVerificationCollector {
        return collectDomains(pkg, true /* checkAutoVerify */, false /* valid */);
    }

    public boolean containsWebDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) {
        return collectDomains(pkg, false /* checkAutoVerify */, true /* valid */, null,
                (BiFunction<Void, String, Boolean>) (unused, domain) -> {
                    if (Objects.equals(targetDomain, domain)) {
                        return true;
                    }
                    return null;
                }) != null;
    }

    public boolean containsAutoVerifyDomain(@NonNull AndroidPackage pkg,
            @NonNull String targetDomain) {
        return collectDomains(pkg, true /* checkAutoVerify */, true /* valid */, null,
                (BiFunction<Void, String, Boolean>) (unused, domain) -> {
                    if (Objects.equals(targetDomain, domain)) {
                        return true;
                    }
                    return null;
                }) != null;
    }

    @NonNull
    private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg,
            boolean checkAutoVerify, boolean valid) {
        ArraySet<String> domains = new ArraySet<>();
        collectDomains(pkg, checkAutoVerify, valid, domains, ARRAY_SET_COLLECTOR);
        return domains;
    }

    @NonNull
    private <InitialValue, ReturnValue> ReturnValue collectDomains(@NonNull AndroidPackage pkg,
            boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue,
            @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) {
        boolean restrictDomains =
                DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);

        if (restrictDomains) {
            return collectDomainsInternal(pkg, checkAutoVerify, valid);
            return collectDomainsInternal(pkg, checkAutoVerify, valid, initialValue,
                    domainCollector);
        } else {
            return collectDomainsLegacy(pkg, checkAutoVerify, valid);
            return collectDomainsLegacy(pkg, checkAutoVerify, valid, initialValue, domainCollector);
        }
    }

    /**
     * @see #RESTRICT_DOMAINS
     */
    private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg,
            boolean checkAutoVerify, boolean valid) {
    @Nullable
    private <InitialValue, ReturnValue> ReturnValue collectDomainsLegacy(
            @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid,
            @Nullable InitialValue initialValue,
            @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) {
        if (!checkAutoVerify) {
            // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
            return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */);
            return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */,
                    initialValue, domainCollector);
        }

        List<ParsedActivity> activities = pkg.getActivities();
@@ -148,11 +192,10 @@ public class DomainVerificationCollector {
            }

            if (!needsAutoVerify) {
                return new ArraySet<>();
                return null;
            }
        }

        ArraySet<String> domains = new ArraySet<>();
        int totalSize = 0;
        boolean underMaxSize = true;
        for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize;
@@ -169,22 +212,30 @@ public class DomainVerificationCollector {
                        if (isValidHost(host) == valid) {
                            totalSize += byteSizeOf(host);
                            underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
                            domains.add(host);
                            ReturnValue returnValue = domainCollector.apply(initialValue, host);
                            if (returnValue != null) {
                                return returnValue;
                            }
                        }
                    }
                }
            }
        }

        return domains;
        return null;
    }

    /**
     * @see #RESTRICT_DOMAINS
     * @param domainCollector Function to call with initialValue and a valid host. Should return
     *                        a non-null value if the function should return immediately
     *                        after the currently processed host.
     */
    private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg,
            boolean checkAutoVerify, boolean valid) {
        ArraySet<String> domains = new ArraySet<>();
    @Nullable
    private <InitialValue, ReturnValue> ReturnValue collectDomainsInternal(
            @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid,
            @Nullable InitialValue initialValue,
            @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) {
        int totalSize = 0;
        boolean underMaxSize = true;

@@ -226,13 +277,16 @@ public class DomainVerificationCollector {
                    if (isValidHost(host) == valid) {
                        totalSize += byteSizeOf(host);
                        underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
                        domains.add(host);
                        ReturnValue returnValue = domainCollector.apply(initialValue, host);
                        if (returnValue != null) {
                            return returnValue;
                        }
                    }
                }
            }
        }

        return domains;
        return null;
    }

    /**
+65 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

@SuppressWarnings("PointlessBooleanExpression")
@@ -100,6 +101,70 @@ public class DomainVerificationDebug {
        }
    }

    /**
     * @param userIdToApprovalLevelToOwners Mapping of user ID to approval level to domain owners.
     */
    public void printOwners(@NonNull IndentingPrintWriter writer, @NonNull String domain,
            SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners) {
        writer.println(domain + ":");
        writer.increaseIndent();

        if (userIdToApprovalLevelToOwners.size() == 0) {
            writer.println("none");
            writer.decreaseIndent();
            return;
        }

        int usersSize = userIdToApprovalLevelToOwners.size();
        for (int userIndex = 0; userIndex < usersSize; userIndex++) {
            int userId = userIdToApprovalLevelToOwners.keyAt(userIndex);
            SparseArray<List<String>> approvalLevelToOwners =
                    userIdToApprovalLevelToOwners.valueAt(userIndex);

            if (approvalLevelToOwners.size() == 0) {
                continue;
            }

            boolean printedUserHeader = false;
            int approvalsSize = approvalLevelToOwners.size();
            for (int approvalIndex = 0; approvalIndex < approvalsSize; approvalIndex++) {
                int approvalLevel = approvalLevelToOwners.keyAt(approvalIndex);
                if (approvalLevel < DomainVerificationManagerInternal.APPROVAL_LEVEL_UNVERIFIED) {
                    continue;
                }

                if (!printedUserHeader) {
                    writer.println("User " + userId + ":");
                    writer.increaseIndent();
                    printedUserHeader = true;
                }

                String approvalString =
                        DomainVerificationManagerInternal.approvalLevelToDebugString(approvalLevel);
                List<String> owners = approvalLevelToOwners.valueAt(approvalIndex);
                writer.println(approvalString + "[" + approvalLevel + "]" + ":");
                writer.increaseIndent();

                if (owners.size() == 0) {
                    writer.println("none");
                    writer.decreaseIndent();
                    continue;
                }

                int ownersSize = owners.size();
                for (int ownersIndex = 0; ownersIndex < ownersSize; ownersIndex++) {
                    writer.println(owners.get(ownersIndex));
                }
                writer.decreaseIndent();
            }

            if (printedUserHeader) {
                writer.decreaseIndent();
            }
        }
        writer.decreaseIndent();
    }

    boolean printState(@NonNull IndentingPrintWriter writer,
            @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
            @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) {
+55 −2
Original line number Diff line number Diff line
@@ -56,6 +56,28 @@ public interface DomainVerificationManagerInternal {

    UUID DISABLED_ID = new UUID(0, 0);

    /**
     * The app was not installed for the user.
     */
    int APPROVAL_LEVEL_NOT_INSTALLED = -4;

    /**
     * The app was not enabled for the user.
     */
    int APPROVAL_LEVEL_DISABLED = -3;

    /**
     * The app has not declared this domain in a valid web intent-filter in their manifest, and so
     * would never be able to be approved for this domain.
     */
    int APPROVAL_LEVEL_UNDECLARED = -2;

    /**
     * The app has declared this domain as a valid autoVerify domain, but it failed or has not
     * succeeded verification.
     */
    int APPROVAL_LEVEL_UNVERIFIED = -1;

    /**
     * The app has not been approved for this domain and should never be able to open it through
     * an implicit web intent.
@@ -117,10 +139,14 @@ public interface DomainVerificationManagerInternal {
     * by approval priority. A higher numerical value means the package should override all lower
     * values. This means that comparison using less/greater than IS valid.
     *
     * Negative values are possible, although not implemented, reserved if explicit disable of a
     * package for a domain needs to be tracked.
     * Negative values are possible, used for tracking specific reasons for why an app doesn't have
     * approval.
     */
    @IntDef({
            APPROVAL_LEVEL_NOT_INSTALLED,
            APPROVAL_LEVEL_DISABLED,
            APPROVAL_LEVEL_UNDECLARED,
            APPROVAL_LEVEL_UNVERIFIED,
            APPROVAL_LEVEL_NONE,
            APPROVAL_LEVEL_LEGACY_ASK,
            APPROVAL_LEVEL_LEGACY_ALWAYS,
@@ -131,6 +157,33 @@ public interface DomainVerificationManagerInternal {
    @interface ApprovalLevel {
    }

    static String approvalLevelToDebugString(@ApprovalLevel int level) {
        switch (level) {
            case APPROVAL_LEVEL_NOT_INSTALLED:
                return "NOT_INSTALLED";
            case APPROVAL_LEVEL_DISABLED:
                return "DISABLED";
            case APPROVAL_LEVEL_UNDECLARED:
                return "UNDECLARED";
            case APPROVAL_LEVEL_UNVERIFIED:
                return "UNVERIFIED";
            case APPROVAL_LEVEL_NONE:
                return "NONE";
            case APPROVAL_LEVEL_LEGACY_ASK:
                return "LEGACY_ASK";
            case APPROVAL_LEVEL_LEGACY_ALWAYS:
                return "LEGACY_ALWAYS";
            case APPROVAL_LEVEL_SELECTION:
                return "USER_SELECTION";
            case APPROVAL_LEVEL_VERIFIED:
                return "VERIFIED";
            case APPROVAL_LEVEL_INSTANT_APP:
                return "INSTANT_APP";
            default:
                return "UNKNOWN";
        }
    }

    /** @see DomainVerificationManager#getDomainVerificationInfo(String) */
    @Nullable
    @RequiresPermission(anyOf = {
+194 −53
Original line number Diff line number Diff line
@@ -741,26 +741,60 @@ public class DomainVerificationService extends SystemService
        });
    }

    @NonNull
    public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) {
        Objects.requireNonNull(domain);
        mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
                userId);

        SparseArray<List<String>> levelToPackages = new SparseArray<>();
        return mConnection.withPackageSettingsReturningThrowing(pkgSettings -> {
            SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false,
                    userId, pkgSettings);
            if (levelToPackages.size() == 0) {
                return emptyList();
            }

            List<DomainOwner> owners = new ArrayList<>();
            int size = levelToPackages.size();
            for (int index = 0; index < size; index++) {
                int level = levelToPackages.keyAt(index);
                boolean overrideable = level <= APPROVAL_LEVEL_SELECTION;
                List<String> packages = levelToPackages.valueAt(index);
                int packagesSize = packages.size();
                for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
                    owners.add(new DomainOwner(packages.get(packageIndex), overrideable));
                }
            }

            return owners;
        });
    }

    /**
     * @param includeNegative See {@link #approvalLevelForDomain(PackageSetting, String, boolean,
     *                        int, Object)}.
     * @return Mapping of approval level to packages; packages are sorted by firstInstallTime. Null
     * if no owners were found.
     */
    @NonNull
    private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain,
            boolean includeNegative, @UserIdInt int userId,
            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
        SparseArray<List<String>> levelToPackages = new SparseArray<>();
        // First, collect the raw approval level values
        synchronized (mLock) {
            final int size = mAttachedPkgStates.size();
            for (int index = 0; index < size; index++) {
                DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
                String packageName = pkgState.getPackageName();
                    PackageSetting pkgSetting = pkgSettings.apply(packageName);
                PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
                if (pkgSetting == null) {
                    continue;
                }

                    int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
                    if (level <= APPROVAL_LEVEL_NONE) {
                int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
                        domain);
                if (!includeNegative && level <= APPROVAL_LEVEL_NONE) {
                    continue;
                }
                List<String> list = levelToPackages.get(level);
@@ -774,14 +808,14 @@ public class DomainVerificationService extends SystemService

        final int size = levelToPackages.size();
        if (size == 0) {
                return emptyList();
            return levelToPackages;
        }

        // Then sort them ascending by first installed time, with package name as tie breaker
        for (int index = 0; index < size; index++) {
            levelToPackages.valueAt(index).sort((first, second) -> {
                    PackageSetting firstPkgSetting = pkgSettings.apply(first);
                    PackageSetting secondPkgSetting = pkgSettings.apply(second);
                PackageSetting firstPkgSetting = pkgSettingFunction.apply(first);
                PackageSetting secondPkgSetting = pkgSettingFunction.apply(second);

                long firstInstallTime =
                        firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
@@ -796,19 +830,7 @@ public class DomainVerificationService extends SystemService
            });
        }

            List<DomainOwner> owners = new ArrayList<>();
            for (int index = 0; index < size; index++) {
                int level = levelToPackages.keyAt(index);
                boolean overrideable = level <= APPROVAL_LEVEL_SELECTION;
                List<String> packages = levelToPackages.valueAt(index);
                int packagesSize = packages.size();
                for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
                    owners.add(new DomainOwner(packages.get(packageIndex), overrideable));
                }
            }

            return owners;
        });
        return levelToPackages;
    }

    @NonNull
@@ -1153,6 +1175,88 @@ public class DomainVerificationService extends SystemService
        }
    }

    @Override
    public void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
            @Nullable String packageName, @Nullable @UserIdInt Integer userId)
            throws NameNotFoundException {
        mConnection.withPackageSettingsThrowing(pkgSettings -> {
            synchronized (mLock) {
                if (packageName == null) {
                    int size = mAttachedPkgStates.size();
                    for (int index = 0; index < size; index++) {
                        try {
                            printOwnersForPackage(writer,
                                    mAttachedPkgStates.valueAt(index).getPackageName(), userId,
                                    pkgSettings);
                        } catch (NameNotFoundException ignored) {
                            // When iterating packages, if one doesn't exist somehow, ignore
                        }
                    }
                } else {
                    printOwnersForPackage(writer, packageName, userId, pkgSettings);
                }
            }
        });
    }

    private void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
            @NonNull String packageName, @Nullable @UserIdInt Integer userId,
            @NonNull Function<String, PackageSetting> pkgSettingFunction)
            throws NameNotFoundException {
        PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
        AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
        if (pkg == null) {
            throw DomainVerificationUtils.throwPackageUnavailable(packageName);
        }

        ArraySet<String> domains = mCollector.collectAllWebDomains(pkg);
        int size = domains.size();
        if (size == 0) {
            return;
        }

        writer.println(packageName + ":");
        writer.increaseIndent();

        for (int index = 0; index < size; index++) {
            printOwnersForDomain(writer, domains.valueAt(index), userId, pkgSettingFunction);
        }

        writer.decreaseIndent();
    }

    @Override
    public void printOwnersForDomains(@NonNull IndentingPrintWriter writer,
            @NonNull List<String> domains, @Nullable @UserIdInt Integer userId) {
        mConnection.withPackageSettings(pkgSettings -> {
            synchronized (mLock) {
                int size = domains.size();
                for (int index = 0; index < size; index++) {
                    printOwnersForDomain(writer, domains.get(index), userId, pkgSettings);
                }
            }
        });
    }

    private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain,
            @Nullable @UserIdInt Integer userId,
            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
        SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners =
                new SparseArray<>();

        if (userId == null || userId == UserHandle.USER_ALL) {
            for (int aUserId : mConnection.getAllUserIds()) {
                userIdToApprovalLevelToOwners.put(aUserId,
                        getOwnersForDomainInternal(domain, true, aUserId, pkgSettingFunction));
            }
        } else {
            userIdToApprovalLevelToOwners.put(userId,
                    getOwnersForDomainInternal(domain, true, userId, pkgSettingFunction));
        }

        mDebug.printOwners(writer, domain, userIdToApprovalLevelToOwners);
    }

    @NonNull
    @Override
    public DomainVerificationShell getShell() {
@@ -1427,7 +1531,7 @@ public class DomainVerificationService extends SystemService
        // Find all approval levels
        int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId,
                pkgSettingFunction);
        if (highestApproval == APPROVAL_LEVEL_NONE) {
        if (highestApproval <= APPROVAL_LEVEL_NONE) {
            return Pair.create(emptyList(), highestApproval);
        }

@@ -1484,7 +1588,7 @@ public class DomainVerificationService extends SystemService
                fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE);
                continue;
            }
            int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain);
            int approval = approvalLevelForDomain(pkgSetting, domain, false, userId, domain);
            highestApproval = Math.max(highestApproval, approval);
            fillInfoMapForSamePackage(inputMap, packageName, approval);
        }
@@ -1600,18 +1704,53 @@ public class DomainVerificationService extends SystemService
            return APPROVAL_LEVEL_NONE;
        }

        return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), userId, intent);
        return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), false, userId,
                intent);
    }

    /**
     * @param debugObject Should be an {@link Intent} if checking for resolution or a {@link String}
     *                    otherwise.
     * @param includeNegative Whether to include negative values, which requires an expensive
     *                          domain comparison operation.
     * @param debugObject       Should be an {@link Intent} if checking for resolution or a
     *                          {@link String} otherwise.
     */
    private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host,
            @UserIdInt int userId, @NonNull Object debugObject) {
            boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) {
        int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative,
                userId, debugObject);
        if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) {
            PackageUserState pkgUserState = pkgSetting.readUserState(userId);
            if (!pkgUserState.installed) {
                return APPROVAL_LEVEL_NOT_INSTALLED;
            }

            AndroidPackage pkg = pkgSetting.getPkg();
            if (pkg != null) {
                if (!pkgUserState.isPackageEnabled(pkg)) {
                    return APPROVAL_LEVEL_DISABLED;
                } else if (mCollector.containsAutoVerifyDomain(pkgSetting.getPkg(), host)) {
                    return APPROVAL_LEVEL_UNVERIFIED;
                }
            }
        }

        return approvalLevel;
    }

    private int approvalLevelForDomainInternal(@NonNull PackageSetting pkgSetting,
            @NonNull String host, boolean includeNegative, @UserIdInt int userId,
            @NonNull Object debugObject) {
        String packageName = pkgSetting.getName();
        final AndroidPackage pkg = pkgSetting.getPkg();

        if (pkg != null && includeNegative && !mCollector.containsWebDomain(pkg, host)) {
            if (DEBUG_APPROVAL) {
                debugApproval(packageName, debugObject, userId, false,
                        "domain not declared");
            }
            return APPROVAL_LEVEL_UNDECLARED;
        }

        final PackageUserState pkgUserState = pkgSetting.readUserState(userId);
        if (pkgUserState == null) {
            if (DEBUG_APPROVAL) {
@@ -1749,6 +1888,7 @@ public class DomainVerificationService extends SystemService
    private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain,
            @UserIdInt int userId, int minimumApproval,
            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
        boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE;
        int highestApproval = minimumApproval;
        List<String> approvedPackages = emptyList();

@@ -1762,7 +1902,8 @@ public class DomainVerificationService extends SystemService
                    continue;
                }

                int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
                int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
                        domain);
                if (level < minimumApproval) {
                    continue;
                }
+83 −0

File changed.

Preview size limit exceeded, changes collapsed.