blob: d67613cb68d77ca1764313d13a383bbabe58faa2 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm.verify.domain;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.content.pm.verify.domain.DomainVerificationState;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.PackageUtils;
import android.util.SparseArray;
import com.android.internal.util.CollectionUtils;
import com.android.server.pm.Computer;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
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;
@SuppressWarnings("PointlessBooleanExpression")
public class DomainVerificationDebug {
// Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be
// enabled and checked in, without having everything be on or off.
public static final boolean DEBUG_ANY = false;
// Enable to turn on all logging. Requires enabling DEBUG_ANY.
public static final boolean DEBUG_ALL = false;
public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true);
public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false);
public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false);
@NonNull
private final DomainVerificationCollector mCollector;
DomainVerificationDebug(DomainVerificationCollector collector) {
mCollector = collector;
}
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId, @NonNull Computer snapshot,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
throws NameNotFoundException {
ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
ArraySet<String> reusedSet = new ArraySet<>();
if (packageName == null) {
int size = stateMap.size();
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = stateMap.valueAt(index);
String pkgName = pkgState.getPackageName();
PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
boolean wasHeaderPrinted = printState(writer, pkgState, pkgSetting.getPkg(),
reusedMap, false);
printState(writer, pkgState, pkgSetting.getPkg(), userId, reusedSet,
wasHeaderPrinted);
}
} else {
DomainVerificationPkgState pkgState = stateMap.get(packageName);
if (pkgState == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
AndroidPackage pkg = pkgSetting.getPkg();
printState(writer, pkgState, pkg, reusedMap, false);
printState(writer, pkgState, pkg, userId, reusedSet, true);
}
}
/**
* @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) {
reusedMap.clear();
reusedMap.putAll(pkgState.getStateMap());
ArraySet<String> declaredDomains = mCollector.collectValidAutoVerifyDomains(pkg);
int declaredSize = declaredDomains.size();
for (int declaredIndex = 0; declaredIndex < declaredSize; declaredIndex++) {
String domain = declaredDomains.valueAt(declaredIndex);
reusedMap.putIfAbsent(domain, DomainVerificationState.STATE_NO_RESPONSE);
}
boolean printedHeader = false;
if (!reusedMap.isEmpty()) {
if (!wasHeaderPrinted) {
Signature[] signatures = pkg.getSigningDetails().getSignatures();
String signaturesDigest = signatures == null ? null : Arrays.toString(
PackageUtils.computeSignaturesSha256Digests(
pkg.getSigningDetails().getSignatures(), ":"));
writer.println(pkgState.getPackageName() + ":");
writer.increaseIndent();
writer.println("ID: " + pkgState.getId());
writer.println("Signatures: " + signaturesDigest);
writer.decreaseIndent();
printedHeader = true;
}
writer.increaseIndent();
final ArraySet<String> invalidDomains = mCollector.collectInvalidAutoVerifyDomains(pkg);
if (!invalidDomains.isEmpty()) {
writer.println("Invalid autoVerify domains:");
writer.increaseIndent();
int size = invalidDomains.size();
for (int index = 0; index < size; index++) {
writer.println(invalidDomains.valueAt(index));
}
writer.decreaseIndent();
}
writer.println("Domain verification state:");
writer.increaseIndent();
int stateSize = reusedMap.size();
for (int stateIndex = 0; stateIndex < stateSize; stateIndex++) {
String domain = reusedMap.keyAt(stateIndex);
Integer state = reusedMap.valueAt(stateIndex);
writer.print(domain);
writer.print(": ");
writer.println(DomainVerificationState.stateToDebugString(state));
}
writer.decreaseIndent();
writer.decreaseIndent();
}
return printedHeader;
}
void printState(@NonNull IndentingPrintWriter writer,
@NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
@Nullable @UserIdInt Integer userId, @NonNull ArraySet<String> reusedSet,
boolean wasHeaderPrinted) {
if (userId == null) {
return;
}
ArraySet<String> allWebDomains = mCollector.collectAllWebDomains(pkg);
SparseArray<DomainVerificationInternalUserState> userStates =
pkgState.getUserStates();
if (userId == UserHandle.USER_ALL) {
int size = userStates.size();
if (size == 0) {
printState(writer, pkgState, userId, null, reusedSet, allWebDomains,
wasHeaderPrinted);
} else {
for (int index = 0; index < size; index++) {
DomainVerificationInternalUserState userState = userStates.valueAt(index);
printState(writer, pkgState, userState.getUserId(), userState, reusedSet,
allWebDomains, wasHeaderPrinted);
}
}
} else {
DomainVerificationInternalUserState userState = userStates.get(userId);
printState(writer, pkgState, userId, userState, reusedSet, allWebDomains,
wasHeaderPrinted);
}
}
boolean printState(@NonNull IndentingPrintWriter writer,
@NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
@Nullable DomainVerificationInternalUserState userState,
@NonNull ArraySet<String> reusedSet, @NonNull ArraySet<String> allWebDomains,
boolean wasHeaderPrinted) {
reusedSet.clear();
reusedSet.addAll(allWebDomains);
if (userState != null) {
reusedSet.removeAll(userState.getEnabledHosts());
}
boolean printedHeader = false;
ArraySet<String> enabledHosts = userState == null ? null : userState.getEnabledHosts();
int enabledSize = CollectionUtils.size(enabledHosts);
int disabledSize = reusedSet.size();
if (enabledSize > 0 || disabledSize > 0) {
if (!wasHeaderPrinted) {
writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":");
printedHeader = true;
}
boolean isLinkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed();
writer.increaseIndent();
writer.print("User ");
writer.print(userId == UserHandle.USER_ALL ? "all" : userId);
writer.println(":");
writer.increaseIndent();
writer.print("Verification link handling allowed: ");
writer.println(isLinkHandlingAllowed);
writer.println("Selection state:");
writer.increaseIndent();
if (enabledSize > 0) {
writer.println("Enabled:");
writer.increaseIndent();
for (int enabledIndex = 0; enabledIndex < enabledSize; enabledIndex++) {
//noinspection ConstantConditions
writer.println(enabledHosts.valueAt(enabledIndex));
}
writer.decreaseIndent();
}
if (disabledSize > 0) {
writer.println("Disabled:");
writer.increaseIndent();
for (int disabledIndex = 0; disabledIndex < disabledSize; disabledIndex++) {
writer.println(reusedSet.valueAt(disabledIndex));
}
writer.decreaseIndent();
}
writer.decreaseIndent();
writer.decreaseIndent();
writer.decreaseIndent();
}
return printedHeader;
}
}