blob: c588ace7bac8694d8b533dc13e891bff5d5dbb22 [file] [log] [blame]
/*
* Copyright (C) 2021 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.bedstead.harrier;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_IN_BACKGROUND;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_AFFILIATED_OTHER_USERS;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_PARENT;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_UNAFFILIATED_OTHER_USERS;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.DO_NOT_APPLY_TO_NEGATIVE_TESTS;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.NO;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
import com.android.bedstead.harrier.annotations.parameterized.IncludeNone;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnAffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnAffiliatedProfileOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnBackgroundDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfCorporateOwnedProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwnerPrimaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwnerProfileWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnUnaffiliatedProfileOwnerSecondaryUser;
import com.google.auto.value.AutoAnnotation;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utility class for enterprise policy tests.
*/
public final class Policy {
private Policy() {
}
@AutoAnnotation
private static IncludeNone includeNone() {
return new AutoAnnotation_Policy_includeNone();
}
@AutoAnnotation
private static IncludeRunOnDeviceOwnerUser includeRunOnDeviceOwnerUser() {
return new AutoAnnotation_Policy_includeRunOnDeviceOwnerUser();
}
@AutoAnnotation
private static IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser includeRunOnNonAffiliatedDeviceOwnerSecondaryUser() {
return new AutoAnnotation_Policy_includeRunOnNonAffiliatedDeviceOwnerSecondaryUser();
}
@AutoAnnotation
private static IncludeRunOnAffiliatedDeviceOwnerSecondaryUser includeRunOnAffiliatedDeviceOwnerSecondaryUser() {
return new AutoAnnotation_Policy_includeRunOnAffiliatedDeviceOwnerSecondaryUser();
}
@AutoAnnotation
private static IncludeRunOnAffiliatedProfileOwnerSecondaryUser includeRunOnAffiliatedProfileOwnerSecondaryUser() {
return new AutoAnnotation_Policy_includeRunOnAffiliatedProfileOwnerSecondaryUser();
}
@AutoAnnotation
private static IncludeRunOnUnaffiliatedProfileOwnerSecondaryUser includeRunOnUnaffiliatedProfileOwnerSecondaryUser() {
return new AutoAnnotation_Policy_includeRunOnUnaffiliatedProfileOwnerSecondaryUser();
}
@AutoAnnotation
private static IncludeRunOnProfileOwnerProfileWithNoDeviceOwner includeRunOnProfileOwnerProfileWithNoDeviceOwner() {
return new AutoAnnotation_Policy_includeRunOnProfileOwnerProfileWithNoDeviceOwner();
}
@AutoAnnotation
private static IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile() {
return new AutoAnnotation_Policy_includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile();
}
@AutoAnnotation
private static IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner includeRunOnParentOfProfileOwnerWithNoDeviceOwner() {
return new AutoAnnotation_Policy_includeRunOnParentOfProfileOwnerWithNoDeviceOwner();
}
@AutoAnnotation
private static IncludeRunOnParentOfCorporateOwnedProfileOwner includeRunOnParentOfCorporateOwnedProfileOwner() {
return new AutoAnnotation_Policy_includeRunOnParentOfCorporateOwnedProfileOwner();
}
@AutoAnnotation
private static IncludeRunOnProfileOwnerPrimaryUser includeRunOnProfileOwnerPrimaryUser() {
return new AutoAnnotation_Policy_includeRunOnProfileOwnerPrimaryUser();
}
@AutoAnnotation
private static IncludeRunOnBackgroundDeviceOwnerUser includeRunOnBackgroundDeviceOwnerUser() {
return new AutoAnnotation_Policy_includeRunOnBackgroundDeviceOwnerUser();
}
// This is a map containing all Include* annotations and the flags which lead to them
// This is not validated - every state must have a single APPLIED_BY annotation
private static final ImmutableMap<Integer, Annotation> STATE_ANNOTATIONS = ImmutableMap.<Integer, Annotation>builder()
.put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER, includeRunOnDeviceOwnerUser())
.put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_OWN_USER | APPLIES_IN_BACKGROUND, includeRunOnBackgroundDeviceOwnerUser())
.put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_UNAFFILIATED_OTHER_USERS, includeRunOnNonAffiliatedDeviceOwnerSecondaryUser())
.put(APPLIED_BY_DEVICE_OWNER | APPLIES_TO_AFFILIATED_OTHER_USERS, includeRunOnAffiliatedDeviceOwnerSecondaryUser())
.put(APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, includeRunOnAffiliatedProfileOwnerSecondaryUser())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER | APPLIES_TO_OWN_USER, includeRunOnUnaffiliatedProfileOwnerSecondaryUser())
.put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO | APPLIES_TO_OWN_USER, includeRunOnProfileOwnerPrimaryUser())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER, includeRunOnProfileOwnerProfileWithNoDeviceOwner())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_PARENT, includeRunOnParentOfProfileOwnerWithNoDeviceOwner())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE | APPLIES_TO_UNAFFILIATED_OTHER_USERS, includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile())
.build();
// This must contain one key for every APPLIED_BY that is being used, and maps to the "default" for testing that DPC type
// in general this will be a state which runs on the same user as the dpc.
private static final ImmutableMap<Integer, Annotation> DPC_STATE_ANNOTATIONS = ImmutableMap.<Integer, Annotation>builder()
.put(APPLIED_BY_DEVICE_OWNER, includeRunOnDeviceOwnerUser())
.put(APPLIED_BY_AFFILIATED_PROFILE_OWNER, includeRunOnAffiliatedProfileOwnerSecondaryUser())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER, includeRunOnProfileOwnerPrimaryUser())
.put(APPLIED_BY_PROFILE_OWNER_USER_WITH_NO_DO, includeRunOnProfileOwnerPrimaryUser())
.put(APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE, includeRunOnProfileOwnerProfileWithNoDeviceOwner())
.build();
private static final int APPLIED_BY_FLAGS =
APPLIED_BY_DEVICE_OWNER | APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_PROFILE
| APPLIED_BY_AFFILIATED_PROFILE_OWNER_PROFILE
| APPLIED_BY_UNAFFILIATED_PROFILE_OWNER_USER
| APPLIED_BY_AFFILIATED_PROFILE_OWNER_USER;
private static final Map<Annotation, Set<Integer>> ANNOTATIONS_MAP = calculateAnnotationsMap(STATE_ANNOTATIONS);
private static Map<Annotation, Set<Integer>> calculateAnnotationsMap(
Map<Integer, Annotation> annotations) {
Map<Annotation, Set<Integer>> b = new HashMap<>();
for (Map.Entry<Integer, Annotation> i : annotations.entrySet()) {
if (!b.containsKey(i.getValue())) {
b.put(i.getValue(), new HashSet<>());
}
b.get(i.getValue()).add(i.getKey());
}
return b;
}
/**
* Get positive state annotations for the given policy.
*
* <p>These are states which should be run where the policy is able to be applied.
*/
public static List<Annotation> positiveStates(String policyName, EnterprisePolicy enterprisePolicy) {
Set<Annotation> annotations = new HashSet<>();
validateFlags(policyName, enterprisePolicy.dpc());
for (Map.Entry<Annotation, Set<Integer>> annotation : ANNOTATIONS_MAP.entrySet()) {
if (isPositive(enterprisePolicy.dpc(), annotation.getValue())) {
annotations.add(annotation.getKey());
}
}
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
}
return new ArrayList<>(annotations);
}
private static boolean isPositive(int[] policyFlags, Set<Integer> annotationFlags) {
for (int annotationFlag : annotationFlags) {
if (hasFlag(policyFlags, annotationFlag)) {
return true;
}
}
return false;
}
private static boolean isNegative(int[] policyFlags, Set<Integer> annotationFlags) {
for (int annotationFlag : annotationFlags) {
if (hasFlag(annotationFlag, DO_NOT_APPLY_TO_NEGATIVE_TESTS, /* nonMatchingFlag= */ NO)) {
return false; // We don't support using this annotation for negative tests
}
int appliedByFlag = APPLIED_BY_FLAGS & annotationFlag;
int otherFlags = annotationFlag ^ appliedByFlag; // remove the appliedByFlag
if (hasFlag(policyFlags, /* matchingFlag= */ appliedByFlag, /* nonMatchingFlag= */ otherFlags)) {
return true;
}
}
return false;
}
/**
* Get negative state annotations for the given policy.
*
* <p>These are states which should be run where the policy is not able to be applied.
*/
public static List<Annotation> negativeStates(String policyName, EnterprisePolicy enterprisePolicy) {
Set<Annotation> annotations = new HashSet<>();
validateFlags(policyName, enterprisePolicy.dpc());
for (Map.Entry<Annotation, Set<Integer>> annotation : ANNOTATIONS_MAP.entrySet()) {
if (isNegative(enterprisePolicy.dpc(), annotation.getValue())) {
annotations.add(annotation.getKey());
}
}
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
}
return new ArrayList<>(annotations);
}
/**
* Get state annotations where the policy cannot be set for the given policy.
*/
public static List<Annotation> cannotSetPolicyStates(String policyName, EnterprisePolicy enterprisePolicy) {
Set<Annotation> annotations = new HashSet<>();
validateFlags(policyName, enterprisePolicy.dpc());
// TODO(scottjonathan): Always include a state without a dpc
int allFlags = 0;
for (int p : enterprisePolicy.dpc()) {
allFlags = allFlags | p;
}
for (Map.Entry<Integer, Annotation> appliedByFlag : DPC_STATE_ANNOTATIONS.entrySet()) {
if ((appliedByFlag.getKey() & allFlags) == 0) {
annotations.add(appliedByFlag.getValue());
}
}
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
}
return new ArrayList<>(annotations);
}
/**
* Get state annotations where the policy can be set for the given policy.
*/
public static List<Annotation> canSetPolicyStates(
String policyName, EnterprisePolicy enterprisePolicy, boolean singleTestOnly) {
Set<Annotation> annotations = new HashSet<>();
validateFlags(policyName, enterprisePolicy.dpc());
int allFlags = 0;
for (int p : enterprisePolicy.dpc()) {
allFlags = allFlags | p;
}
for (Map.Entry<Integer, Annotation> appliedByFlag : DPC_STATE_ANNOTATIONS.entrySet()) {
if ((appliedByFlag.getKey() & allFlags) == appliedByFlag.getKey()) {
annotations.add(appliedByFlag.getValue());
}
}
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
}
List<Annotation> annotationList = new ArrayList<>(annotations);
if (singleTestOnly) {
// We select one annotation in an arbitrary but deterministic way
annotationList.sort(Comparator.comparing(a -> a.annotationType().getName()));
Annotation firstAnnotation = annotationList.get(0);
annotationList.clear();
annotationList.add(firstAnnotation);
}
return annotationList;
}
private static void validateFlags(String policyName, int[] values) {
int usedAppliedByFlags = 0;
for (int value : values) {
validateFlags(policyName, value);
int newUsedAppliedByFlags = usedAppliedByFlags | (value & APPLIED_BY_FLAGS);
if (newUsedAppliedByFlags == usedAppliedByFlags) {
throw new IllegalStateException(
"Cannot have more than one policy flag APPLIED by the same component. "
+ "Error in policy " + policyName);
}
usedAppliedByFlags = newUsedAppliedByFlags;
}
}
private static void validateFlags(String policyName, int value) {
int matchingAppliedByFlags = APPLIED_BY_FLAGS & value;
if (matchingAppliedByFlags == 0) {
throw new IllegalStateException(
"All policy flags must specify 1 APPLIED_BY flag. Policy " + policyName
+ " did not.");
}
}
private static boolean hasFlag(int[] values, int matchingFlag) {
return hasFlag(values, matchingFlag, /* nonMatchingFlag= */ NO);
}
private static boolean hasFlag(int[] values, int matchingFlag, int nonMatchingFlag) {
for (int value : values) {
if (hasFlag(value, matchingFlag, nonMatchingFlag)) {
return true;
}
}
return false;
}
private static boolean hasFlag(int value, int matchingFlag, int nonMatchingFlag) {
if (!((value & matchingFlag) == matchingFlag)) {
return false;
}
if (nonMatchingFlag != NO) {
return (value & nonMatchingFlag) != nonMatchingFlag;
}
return true;
}
}