• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import android.util.IndentingPrintWriter;
23 
24 import com.android.internal.util.XmlUtils;
25 import com.android.modules.utils.TypedXmlPullParser;
26 import com.android.modules.utils.TypedXmlSerializer;
27 import com.android.server.utils.Slogf;
28 
29 import org.xmlpull.v1.XmlPullParserException;
30 
31 import java.io.IOException;
32 import java.util.LinkedHashMap;
33 import java.util.Objects;
34 
35 /**
36  * Class containing all values set for a certain policy by different admins.
37  */
38 final class PolicyState<V> {
39 
40     private static final String TAG = "PolicyState";
41     private static final String TAG_ADMIN_POLICY_ENTRY = "admin-policy-entry";
42 
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 "\nPolicyKey - " + mPolicyDefinition.getPolicyKey()
193                 + "\nmPolicyDefinition= \n\t" + mPolicyDefinition
194                 + "\nmPoliciesSetByAdmins= \n\t" + mPoliciesSetByAdmins
195                 + ",\nmCurrentResolvedPolicy= \n\t" + mCurrentResolvedPolicy + " }";
196     }
197 
dump(IndentingPrintWriter pw)198     public void dump(IndentingPrintWriter pw) {
199         pw.println(mPolicyDefinition.getPolicyKey());
200         pw.increaseIndent();
201 
202         pw.println("Per-admin Policy:");
203         pw.increaseIndent();
204         if (mPoliciesSetByAdmins.size() == 0) {
205             pw.println("null");
206         } else {
207             for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
208                 pw.println(admin);
209                 pw.increaseIndent();
210                 pw.println(mPoliciesSetByAdmins.get(admin));
211                 pw.decreaseIndent();
212             }
213         }
214         pw.decreaseIndent();
215 
216         pw.printf("Resolved Policy (%s):\n",
217                 mPolicyDefinition.getResolutionMechanism().getClass().getSimpleName());
218         pw.increaseIndent();
219         pw.println(mCurrentResolvedPolicy);
220         pw.decreaseIndent();
221 
222         pw.decreaseIndent();
223     }
224 
saveToXml(TypedXmlSerializer serializer)225     void saveToXml(TypedXmlSerializer serializer) throws IOException {
226         if (mCurrentResolvedPolicy != null) {
227             serializer.startTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
228             mPolicyDefinition.savePolicyValueToXml(
229                     serializer, mCurrentResolvedPolicy.getValue());
230             serializer.endTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
231         }
232 
233         for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
234             serializer.startTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
235 
236             if (mPoliciesSetByAdmins.get(admin) != null) {
237                 serializer.startTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
238                 mPolicyDefinition.savePolicyValueToXml(
239                         serializer, mPoliciesSetByAdmins.get(admin).getValue());
240                 serializer.endTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
241             }
242 
243             serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
244             admin.saveToXml(serializer);
245             serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
246 
247             serializer.endTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
248         }
249     }
250 
251     @Nullable
readFromXml( PolicyDefinition<V> policyDefinition, TypedXmlPullParser parser)252     static <V> PolicyState<V> readFromXml(
253             PolicyDefinition<V> policyDefinition, TypedXmlPullParser parser)
254             throws IOException, XmlPullParserException {
255         PolicyValue<V> currentResolvedPolicy = null;
256 
257         LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins = new LinkedHashMap<>();
258         int outerDepth = parser.getDepth();
259         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
260             String tag = parser.getName();
261             switch (tag) {
262                 case TAG_ADMIN_POLICY_ENTRY:
263                     PolicyValue<V> value = null;
264                     EnforcingAdmin admin = null;
265                     int adminPolicyDepth = parser.getDepth();
266                     while (XmlUtils.nextElementWithin(parser, adminPolicyDepth)) {
267                         String adminPolicyTag = parser.getName();
268                         switch (adminPolicyTag) {
269                             case TAG_ENFORCING_ADMIN_ENTRY:
270                                 admin = EnforcingAdmin.readFromXml(parser);
271                                 if (admin == null) {
272                                     Slogf.wtf(TAG, "Error Parsing TAG_ENFORCING_ADMIN_ENTRY, "
273                                             + "EnforcingAdmin is null");
274                                 }
275                                 break;
276                             case TAG_POLICY_VALUE_ENTRY:
277                                 value = policyDefinition.readPolicyValueFromXml(parser);
278                                 if (value == null) {
279                                     Slogf.wtf(TAG, "Error Parsing TAG_POLICY_VALUE_ENTRY, "
280                                             + "PolicyValue is null");
281                                 }
282                                 break;
283                         }
284                     }
285                     if (admin != null && value != null) {
286                         policiesSetByAdmins.put(admin, value);
287                     } else {
288                         Slogf.wtf(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY for "
289                                 + (policyDefinition == null ? "unknown policy" : "policy with "
290                                 + "definition " + policyDefinition) + ", EnforcingAdmin is: "
291                                 + (admin == null ? "null" : admin) + ", value is : "
292                                 + (value == null ? "null" : value));
293                     }
294                     break;
295 
296                 case TAG_RESOLVED_VALUE_ENTRY:
297                     if (policyDefinition == null) {
298                         Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY, "
299                                 + "policyDefinition is null");
300                         break;
301                     }
302                     currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(parser);
303                     if (currentResolvedPolicy == null) {
304                         Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY for "
305                                 + (policyDefinition == null ? "unknown policy" : "policy with "
306                                 + "definition " + policyDefinition) + ", "
307                                 + "currentResolvedPolicy is null");
308                     }
309                     break;
310                 default:
311                     Slogf.wtf(TAG, "Unknown tag: " + tag);
312             }
313         }
314         if (policyDefinition != null) {
315             return new PolicyState<V>(policyDefinition, policiesSetByAdmins, currentResolvedPolicy);
316         } else {
317             Slogf.wtf(TAG, "Error parsing policyState, policyDefinition is null");
318             return null;
319         }
320     }
321 
322 
323 
getPolicyDefinition()324     PolicyDefinition<V> getPolicyDefinition() {
325         return mPolicyDefinition;
326     }
327 }
328