1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.devicepolicy; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.admin.PolicyValue; 22 23 import com.android.internal.util.XmlUtils; 24 import com.android.modules.utils.TypedXmlPullParser; 25 import com.android.modules.utils.TypedXmlSerializer; 26 import com.android.server.utils.Slogf; 27 28 import org.xmlpull.v1.XmlPullParserException; 29 30 import java.io.IOException; 31 import java.util.LinkedHashMap; 32 import java.util.Objects; 33 34 /** 35 * Class containing all values set for a certain policy by different admins. 36 */ 37 final class PolicyState<V> { 38 39 private static final String TAG = "PolicyState"; 40 private static final String TAG_ADMIN_POLICY_ENTRY = "admin-policy-entry"; 41 42 private static final String TAG_POLICY_DEFINITION_ENTRY = "policy-definition-entry"; 43 private static final String TAG_RESOLVED_VALUE_ENTRY = "resolved-value-entry"; 44 private static final String TAG_ENFORCING_ADMIN_ENTRY = "enforcing-admin-entry"; 45 private static final String TAG_POLICY_VALUE_ENTRY = "policy-value-entry"; 46 private final PolicyDefinition<V> mPolicyDefinition; 47 private final LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mPoliciesSetByAdmins = 48 new LinkedHashMap<>(); 49 private PolicyValue<V> mCurrentResolvedPolicy; 50 PolicyState(@onNull PolicyDefinition<V> policyDefinition)51 PolicyState(@NonNull PolicyDefinition<V> policyDefinition) { 52 mPolicyDefinition = Objects.requireNonNull(policyDefinition); 53 } 54 PolicyState( @onNull PolicyDefinition<V> policyDefinition, @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins, PolicyValue<V> currentEnforcedPolicy)55 private PolicyState( 56 @NonNull PolicyDefinition<V> policyDefinition, 57 @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins, 58 PolicyValue<V> currentEnforcedPolicy) { 59 Objects.requireNonNull(policyDefinition); 60 Objects.requireNonNull(policiesSetByAdmins); 61 62 mPolicyDefinition = policyDefinition; 63 mPoliciesSetByAdmins.putAll(policiesSetByAdmins); 64 mCurrentResolvedPolicy = currentEnforcedPolicy; 65 } 66 67 /** 68 * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. 69 */ addPolicy(@onNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy)70 boolean addPolicy(@NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy) { 71 Objects.requireNonNull(admin); 72 73 //LinkedHashMap doesn't update the insertion order of existing keys, removing the existing 74 // key will cause it to update. 75 mPoliciesSetByAdmins.remove(admin); 76 mPoliciesSetByAdmins.put(admin, policy); 77 78 return resolvePolicy(); 79 } 80 81 /** 82 * Takes into account global policies set by the admin when resolving the policy, this is only 83 * relevant to local policies that can be applied globally as well. 84 * 85 * <p> Note that local policies set by an admin takes precedence over global policies set by the 86 * same admin. 87 * 88 * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. 89 */ addPolicy( @onNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy, LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)90 boolean addPolicy( 91 @NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy, 92 LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) { 93 mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), policy); 94 95 return resolvePolicy(globalPoliciesSetByAdmins); 96 } 97 98 /** 99 * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. 100 */ removePolicy(@onNull EnforcingAdmin admin)101 boolean removePolicy(@NonNull EnforcingAdmin admin) { 102 Objects.requireNonNull(admin); 103 104 if (mPoliciesSetByAdmins.remove(admin) == null) { 105 return false; 106 } 107 108 return resolvePolicy(); 109 } 110 111 /** 112 * Takes into account global policies set by the admin when resolving the policy, this is only 113 * relevant to local policies that can be applied globally as well. 114 * 115 * <p> Note that local policies set by an admin takes precedence over global policies set by the 116 * same admin. 117 * 118 * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. 119 */ removePolicy( @onNull EnforcingAdmin admin, LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)120 boolean removePolicy( 121 @NonNull EnforcingAdmin admin, 122 LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) { 123 Objects.requireNonNull(admin); 124 125 if (mPoliciesSetByAdmins.remove(admin) == null) { 126 return false; 127 } 128 129 return resolvePolicy(globalPoliciesSetByAdmins); 130 } 131 132 /** 133 * Takes into account global policies set by the admin when resolving the policy, this is only 134 * relevant to local policies that can be applied globally as well. 135 * 136 * <p> Note that local policies set by an admin takes precedence over global policies set by the 137 * same admin. 138 * 139 * Returns {@code true} if the resolved policy has changed, {@code false} otherwise. 140 */ resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)141 boolean resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) { 142 //Non coexistable policies don't need resolving 143 if (mPolicyDefinition.isNonCoexistablePolicy()) { 144 return false; 145 } 146 // Add global policies first then override with local policies for the same admin. 147 LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mergedPolicies = 148 new LinkedHashMap<>(globalPoliciesSetByAdmins); 149 mergedPolicies.putAll(mPoliciesSetByAdmins); 150 151 PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies); 152 boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy); 153 mCurrentResolvedPolicy = resolvedPolicy; 154 155 return policyChanged; 156 } 157 158 @NonNull getPoliciesSetByAdmins()159 LinkedHashMap<EnforcingAdmin, PolicyValue<V>> getPoliciesSetByAdmins() { 160 return new LinkedHashMap<>(mPoliciesSetByAdmins); 161 } 162 resolvePolicy()163 private boolean resolvePolicy() { 164 //Non coexistable policies don't need resolving 165 if (mPolicyDefinition.isNonCoexistablePolicy()) { 166 return false; 167 } 168 PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mPoliciesSetByAdmins); 169 boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy); 170 mCurrentResolvedPolicy = resolvedPolicy; 171 172 return policyChanged; 173 } 174 175 @Nullable getCurrentResolvedPolicy()176 PolicyValue<V> getCurrentResolvedPolicy() { 177 return mCurrentResolvedPolicy; 178 } 179 getParcelablePolicyState()180 android.app.admin.PolicyState<V> getParcelablePolicyState() { 181 LinkedHashMap<android.app.admin.EnforcingAdmin, PolicyValue<V>> adminPolicies = 182 new LinkedHashMap<>(); 183 for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) { 184 adminPolicies.put(admin.getParcelableAdmin(), mPoliciesSetByAdmins.get(admin)); 185 } 186 return new android.app.admin.PolicyState<>(adminPolicies, mCurrentResolvedPolicy, 187 mPolicyDefinition.getResolutionMechanism().getParcelableResolutionMechanism()); 188 } 189 190 @Override toString()191 public String toString() { 192 return "PolicyState { mPolicyDefinition= " + mPolicyDefinition + ", mPoliciesSetByAdmins= " 193 + mPoliciesSetByAdmins + ", mCurrentResolvedPolicy= " + mCurrentResolvedPolicy 194 + " }"; 195 } 196 saveToXml(TypedXmlSerializer serializer)197 void saveToXml(TypedXmlSerializer serializer) throws IOException { 198 serializer.startTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY); 199 mPolicyDefinition.saveToXml(serializer); 200 serializer.endTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY); 201 202 if (mCurrentResolvedPolicy != null) { 203 serializer.startTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY); 204 mPolicyDefinition.savePolicyValueToXml( 205 serializer, mCurrentResolvedPolicy.getValue()); 206 serializer.endTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY); 207 } 208 209 for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) { 210 serializer.startTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY); 211 212 if (mPoliciesSetByAdmins.get(admin) != null) { 213 serializer.startTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY); 214 mPolicyDefinition.savePolicyValueToXml( 215 serializer, mPoliciesSetByAdmins.get(admin).getValue()); 216 serializer.endTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY); 217 } 218 219 serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY); 220 admin.saveToXml(serializer); 221 serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY); 222 223 serializer.endTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY); 224 } 225 } 226 227 @Nullable readFromXml(TypedXmlPullParser parser)228 static <V> PolicyState<V> readFromXml(TypedXmlPullParser parser) 229 throws IOException, XmlPullParserException { 230 231 PolicyDefinition<V> policyDefinition = null; 232 233 PolicyValue<V> currentResolvedPolicy = null; 234 235 LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins = new LinkedHashMap<>(); 236 int outerDepth = parser.getDepth(); 237 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 238 String tag = parser.getName(); 239 switch (tag) { 240 case TAG_ADMIN_POLICY_ENTRY: 241 PolicyValue<V> value = null; 242 EnforcingAdmin admin = null; 243 int adminPolicyDepth = parser.getDepth(); 244 while (XmlUtils.nextElementWithin(parser, adminPolicyDepth)) { 245 String adminPolicyTag = parser.getName(); 246 switch (adminPolicyTag) { 247 case TAG_ENFORCING_ADMIN_ENTRY: 248 admin = EnforcingAdmin.readFromXml(parser); 249 if (admin == null) { 250 Slogf.wtf(TAG, "Error Parsing TAG_ENFORCING_ADMIN_ENTRY, " 251 + "EnforcingAdmin is null"); 252 } 253 break; 254 case TAG_POLICY_VALUE_ENTRY: 255 value = policyDefinition.readPolicyValueFromXml(parser); 256 if (value == null) { 257 Slogf.wtf(TAG, "Error Parsing TAG_POLICY_VALUE_ENTRY, " 258 + "PolicyValue is null"); 259 } 260 break; 261 } 262 } 263 if (admin != null) { 264 policiesSetByAdmins.put(admin, value); 265 } else { 266 Slogf.wtf(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY, EnforcingAdmin " 267 + "is null"); 268 } 269 break; 270 case TAG_POLICY_DEFINITION_ENTRY: 271 policyDefinition = PolicyDefinition.readFromXml(parser); 272 if (policyDefinition == null) { 273 Slogf.wtf(TAG, "Error Parsing TAG_POLICY_DEFINITION_ENTRY, " 274 + "PolicyDefinition is null"); 275 } 276 break; 277 278 case TAG_RESOLVED_VALUE_ENTRY: 279 if (policyDefinition == null) { 280 Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY, " 281 + "policyDefinition is null"); 282 break; 283 } 284 currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(parser); 285 if (currentResolvedPolicy == null) { 286 Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY, " 287 + "currentResolvedPolicy is null"); 288 } 289 break; 290 default: 291 Slogf.wtf(TAG, "Unknown tag: " + tag); 292 } 293 } 294 if (policyDefinition != null) { 295 return new PolicyState<V>(policyDefinition, policiesSetByAdmins, currentResolvedPolicy); 296 } else { 297 Slogf.wtf(TAG, "Error parsing policyState, policyDefinition is null"); 298 return null; 299 } 300 } 301 302 303 getPolicyDefinition()304 PolicyDefinition<V> getPolicyDefinition() { 305 return mPolicyDefinition; 306 } 307 } 308