Merge "Add pm command for getOwnersForDomain API" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0b5dedfb14
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -741,62 +741,21 @@ 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 -> {
|
||||
// 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);
|
||||
if (pkgSetting == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
|
||||
if (level <= APPROVAL_LEVEL_NONE) {
|
||||
continue;
|
||||
}
|
||||
List<String> list = levelToPackages.get(level);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
levelToPackages.put(level, list);
|
||||
}
|
||||
list.add(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
final int size = levelToPackages.size();
|
||||
if (size == 0) {
|
||||
SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false,
|
||||
userId, pkgSettings);
|
||||
if (levelToPackages.size() == 0) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
long firstInstallTime =
|
||||
firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
|
||||
long secondInstallTime =
|
||||
secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
|
||||
|
||||
if (firstInstallTime != secondInstallTime) {
|
||||
return (int) (firstInstallTime - secondInstallTime);
|
||||
}
|
||||
|
||||
return first.compareToIgnoreCase(second);
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -811,6 +770,69 @@ public class DomainVerificationService extends SystemService
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = pkgSettingFunction.apply(packageName);
|
||||
if (pkgSetting == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
|
||||
domain);
|
||||
if (!includeNegative && level <= APPROVAL_LEVEL_NONE) {
|
||||
continue;
|
||||
}
|
||||
List<String> list = levelToPackages.get(level);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
levelToPackages.put(level, list);
|
||||
}
|
||||
list.add(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
final int size = levelToPackages.size();
|
||||
if (size == 0) {
|
||||
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 = pkgSettingFunction.apply(first);
|
||||
PackageSetting secondPkgSetting = pkgSettingFunction.apply(second);
|
||||
|
||||
long firstInstallTime =
|
||||
firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
|
||||
long secondInstallTime =
|
||||
secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
|
||||
|
||||
if (firstInstallTime != secondInstallTime) {
|
||||
return (int) (firstInstallTime - secondInstallTime);
|
||||
}
|
||||
|
||||
return first.compareToIgnoreCase(second);
|
||||
});
|
||||
}
|
||||
|
||||
return levelToPackages;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UUID generateNewId() {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,13 @@ public class DomainVerificationShell {
|
||||
pw.println(" packages will be reset if no one package is specified.");
|
||||
pw.println(" <ALLOWED>: true to allow the package to open auto verified links, false");
|
||||
pw.println(" to disable");
|
||||
pw.println(" get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>]");
|
||||
pw.println(" Print the owners for a specific domain for a given user in low to high");
|
||||
pw.println(" priority order.");
|
||||
pw.println(" --user <USER_ID>: the user to query for");
|
||||
pw.println(" --package <PACKAGE>: optionally also print for all web domains declared");
|
||||
pw.println(" by a package, or \"all\" to print all packages");
|
||||
pw.println(" --<DOMAINS>: space separated list of domains to query for");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +139,8 @@ public class DomainVerificationShell {
|
||||
return runSetAppLinksUserState(commandHandler);
|
||||
case "set-app-links-allowed":
|
||||
return runSetAppLinksAllowed(commandHandler);
|
||||
case "get-app-link-owners":
|
||||
return runGetAppLinkOwners(commandHandler);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -420,6 +429,67 @@ public class DomainVerificationShell {
|
||||
return true;
|
||||
}
|
||||
|
||||
// pm get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>]
|
||||
private boolean runGetAppLinkOwners(@NonNull BasicShellCommandHandler commandHandler) {
|
||||
String packageName = null;
|
||||
Integer userId = null;
|
||||
String option;
|
||||
while ((option = commandHandler.getNextOption()) != null) {
|
||||
switch (option) {
|
||||
case "--user":
|
||||
userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
|
||||
break;
|
||||
case "--package":
|
||||
packageName = commandHandler.getNextArgRequired();
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
commandHandler.getErrPrintWriter().println("Error: no package specified");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
commandHandler.getErrPrintWriter().println(
|
||||
"Error: unexpected option: " + option);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> domains = getRemainingArgs(commandHandler);
|
||||
if (domains.isEmpty() && TextUtils.isEmpty(packageName)) {
|
||||
commandHandler.getErrPrintWriter()
|
||||
.println("Error: no package name or domain specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (userId != null) {
|
||||
userId = translateUserId(userId, "runSetAppLinksAllowed");
|
||||
}
|
||||
|
||||
try (IndentingPrintWriter writer = new IndentingPrintWriter(
|
||||
commandHandler.getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */
|
||||
120)) {
|
||||
writer.increaseIndent();
|
||||
if (packageName != null) {
|
||||
if (packageName.equals("all")) {
|
||||
packageName = null;
|
||||
}
|
||||
|
||||
try {
|
||||
mCallback.printOwnersForPackage(writer, packageName, userId);
|
||||
} catch (NameNotFoundException e) {
|
||||
commandHandler.getErrPrintWriter()
|
||||
.println("Error: package not found: " + packageName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!domains.isEmpty()) {
|
||||
mCallback.printOwnersForDomains(writer, domains, userId);
|
||||
}
|
||||
writer.decreaseIndent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ArrayList<String> getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) {
|
||||
ArrayList<String> args = new ArrayList<>();
|
||||
String arg;
|
||||
@@ -534,5 +604,18 @@ public class DomainVerificationShell {
|
||||
*/
|
||||
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
|
||||
@Nullable @UserIdInt Integer userId) throws NameNotFoundException;
|
||||
|
||||
/**
|
||||
* Print the owners for all domains in a given package.
|
||||
*/
|
||||
void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
|
||||
@Nullable String packageName, @Nullable @UserIdInt Integer userId)
|
||||
throws NameNotFoundException;
|
||||
|
||||
/**
|
||||
* Print the owners for the given domains.
|
||||
*/
|
||||
void printOwnersForDomains(@NonNull IndentingPrintWriter writer,
|
||||
@NonNull List<String> domains, @Nullable @UserIdInt Integer userId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user