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
This commit is contained in:
Winson
2021-03-17 13:26:19 -07:00
parent a9bb9e1b53
commit efdf07b668
2 changed files with 104 additions and 108 deletions

View File

@@ -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,83 +559,99 @@ 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);
}
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
} else {
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
Set<String> validDomains =
domains == null ? mCollector.collectAllWebDomains(pkg) : domains;
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(validDomains);
} else {
userState.removeHosts(validDomains);
}
}
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
} else {
DomainVerificationInternalUserState userState =
pkgState.getOrCreateUserState(userId);
revokeOtherUserSelections(userState, userId, validDomains);
if (enabled) {
userState.addHosts(validDomains);
} else {
userState.removeHosts(validDomains);
}
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));
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<>();
if (userId == UserHandle.USER_ALL) {
for (int aUserId : mConnection.getAllUserIds()) {
DomainVerificationInternalUserState userState =
pkgState.getOrCreateUserState(aUserId);
if (enabled) {
userState.addHosts(domains);
} else {
userState.removeHosts(domains);
}
for (String domain : domains) {
if (userState.getEnabledHosts().contains(domain)) {
continue;
}
} else {
DomainVerificationInternalUserState userState =
pkgState.getOrCreateUserState(userId);
if (enabled) {
userState.addHosts(domains);
} else {
userState.removeHosts(domains);
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

View File

@@ -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;
/**