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_POLICY_DEFINITION_ENTRY = "policy-definition-entry";
44     private static final String TAG_RESOLVED_VALUE_ENTRY = "resolved-value-entry";
45     private static final String TAG_ENFORCING_ADMIN_ENTRY = "enforcing-admin-entry";
46     private static final String TAG_POLICY_VALUE_ENTRY = "policy-value-entry";
47     private final PolicyDefinition<V> mPolicyDefinition;
48     private final LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mPoliciesSetByAdmins =
49             new LinkedHashMap<>();
50     private PolicyValue<V> mCurrentResolvedPolicy;
51 
PolicyState(@onNull PolicyDefinition<V> policyDefinition)52     PolicyState(@NonNull PolicyDefinition<V> policyDefinition) {
53         mPolicyDefinition = Objects.requireNonNull(policyDefinition);
54     }
55 
PolicyState( @onNull PolicyDefinition<V> policyDefinition, @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins, PolicyValue<V> currentEnforcedPolicy)56     private PolicyState(
57             @NonNull PolicyDefinition<V> policyDefinition,
58             @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins,
59             PolicyValue<V> currentEnforcedPolicy) {
60         Objects.requireNonNull(policyDefinition);
61         Objects.requireNonNull(policiesSetByAdmins);
62 
63         mPolicyDefinition = policyDefinition;
64         mPoliciesSetByAdmins.putAll(policiesSetByAdmins);
65         mCurrentResolvedPolicy = currentEnforcedPolicy;
66     }
67 
68     /**
69      * Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
70      */
addPolicy(@onNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy)71     boolean addPolicy(@NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy) {
72         Objects.requireNonNull(admin);
73 
74         //LinkedHashMap doesn't update the insertion order of existing keys, removing the existing
75         // key will cause it to update.
76         mPoliciesSetByAdmins.remove(admin);
77         mPoliciesSetByAdmins.put(admin, policy);
78 
79         return resolvePolicy();
80     }
81 
82     /**
83      * Takes into account global policies set by the admin when resolving the policy, this is only
84      * relevant to local policies that can be applied globally as well.
85      *
86      * <p> Note that local policies set by an admin takes precedence over global policies set by the
87      * same admin.
88      *
89      * Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
90      */
addPolicy( @onNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy, LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)91     boolean addPolicy(
92             @NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy,
93             LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
94         mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), policy);
95 
96         return resolvePolicy(globalPoliciesSetByAdmins);
97     }
98 
99     /**
100      * Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
101      */
removePolicy(@onNull EnforcingAdmin admin)102     boolean removePolicy(@NonNull EnforcingAdmin admin) {
103         Objects.requireNonNull(admin);
104 
105         if (mPoliciesSetByAdmins.remove(admin) == null) {
106             return false;
107         }
108 
109         return resolvePolicy();
110     }
111 
112     /**
113      * Takes into account global policies set by the admin when resolving the policy, this is only
114      * relevant to local policies that can be applied globally as well.
115      *
116      * <p> Note that local policies set by an admin takes precedence over global policies set by the
117      * same admin.
118      *
119      * Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
120      */
removePolicy( @onNull EnforcingAdmin admin, LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)121     boolean removePolicy(
122             @NonNull EnforcingAdmin admin,
123             LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
124         Objects.requireNonNull(admin);
125 
126         if (mPoliciesSetByAdmins.remove(admin) == null) {
127             return false;
128         }
129 
130         return resolvePolicy(globalPoliciesSetByAdmins);
131     }
132 
133     /**
134      * Takes into account global policies set by the admin when resolving the policy, this is only
135      * relevant to local policies that can be applied globally as well.
136      *
137      * <p> Note that local policies set by an admin takes precedence over global policies set by the
138      * same admin.
139      *
140      * Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
141      */
resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins)142     boolean resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
143         //Non coexistable policies don't need resolving
144         if (mPolicyDefinition.isNonCoexistablePolicy()) {
145             return false;
146         }
147         // Add global policies first then override with local policies for the same admin.
148         LinkedHashMap<EnforcingAdmin, PolicyValue<V>> mergedPolicies =
149                 new LinkedHashMap<>(globalPoliciesSetByAdmins);
150         mergedPolicies.putAll(mPoliciesSetByAdmins);
151 
152         PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mergedPolicies);
153         boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy);
154         mCurrentResolvedPolicy = resolvedPolicy;
155 
156         return policyChanged;
157     }
158 
159     @NonNull
getPoliciesSetByAdmins()160     LinkedHashMap<EnforcingAdmin, PolicyValue<V>> getPoliciesSetByAdmins() {
161         return new LinkedHashMap<>(mPoliciesSetByAdmins);
162     }
163 
resolvePolicy()164     private boolean resolvePolicy() {
165         //Non coexistable policies don't need resolving
166         if (mPolicyDefinition.isNonCoexistablePolicy()) {
167             return false;
168         }
169         PolicyValue<V> resolvedPolicy = mPolicyDefinition.resolvePolicy(mPoliciesSetByAdmins);
170         boolean policyChanged = !Objects.equals(resolvedPolicy, mCurrentResolvedPolicy);
171         mCurrentResolvedPolicy = resolvedPolicy;
172 
173         return policyChanged;
174     }
175 
176     @Nullable
getCurrentResolvedPolicy()177     PolicyValue<V> getCurrentResolvedPolicy() {
178         return mCurrentResolvedPolicy;
179     }
180 
getParcelablePolicyState()181     android.app.admin.PolicyState<V> getParcelablePolicyState() {
182         LinkedHashMap<android.app.admin.EnforcingAdmin, PolicyValue<V>> adminPolicies =
183                 new LinkedHashMap<>();
184         for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
185             adminPolicies.put(admin.getParcelableAdmin(), mPoliciesSetByAdmins.get(admin));
186         }
187         return new android.app.admin.PolicyState<>(adminPolicies, mCurrentResolvedPolicy,
188                 mPolicyDefinition.getResolutionMechanism().getParcelableResolutionMechanism());
189     }
190 
191     @Override
toString()192     public String toString() {
193         return "\nPolicyKey - " + mPolicyDefinition.getPolicyKey()
194                 + "\nmPolicyDefinition= \n\t" + mPolicyDefinition
195                 + "\nmPoliciesSetByAdmins= \n\t" + mPoliciesSetByAdmins
196                 + ",\nmCurrentResolvedPolicy= \n\t" + mCurrentResolvedPolicy + " }";
197     }
198 
dump(IndentingPrintWriter pw)199     public void dump(IndentingPrintWriter pw) {
200         pw.println(mPolicyDefinition.getPolicyKey());
201         pw.increaseIndent();
202 
203         pw.println("Per-admin Policy:");
204         pw.increaseIndent();
205         if (mPoliciesSetByAdmins.size() == 0) {
206             pw.println("null");
207         } else {
208             for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
209                 pw.println(admin);
210                 pw.increaseIndent();
211                 pw.println(mPoliciesSetByAdmins.get(admin));
212                 pw.decreaseIndent();
213             }
214         }
215         pw.decreaseIndent();
216 
217         pw.printf("Resolved Policy (%s):\n",
218                 mPolicyDefinition.getResolutionMechanism().getClass().getSimpleName());
219         pw.increaseIndent();
220         pw.println(mCurrentResolvedPolicy);
221         pw.decreaseIndent();
222 
223         pw.decreaseIndent();
224     }
225 
saveToXml(TypedXmlSerializer serializer)226     void saveToXml(TypedXmlSerializer serializer) throws IOException {
227         serializer.startTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
228         mPolicyDefinition.saveToXml(serializer);
229         serializer.endTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
230 
231         if (mCurrentResolvedPolicy != null) {
232             serializer.startTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
233             mPolicyDefinition.savePolicyValueToXml(
234                     serializer, mCurrentResolvedPolicy.getValue());
235             serializer.endTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
236         }
237 
238         for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
239             serializer.startTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
240 
241             if (mPoliciesSetByAdmins.get(admin) != null) {
242                 serializer.startTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
243                 mPolicyDefinition.savePolicyValueToXml(
244                         serializer, mPoliciesSetByAdmins.get(admin).getValue());
245                 serializer.endTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
246             }
247 
248             serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
249             admin.saveToXml(serializer);
250             serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
251 
252             serializer.endTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
253         }
254     }
255 
256     @Nullable
readFromXml(TypedXmlPullParser parser)257     static <V> PolicyState<V> readFromXml(TypedXmlPullParser parser)
258             throws IOException, XmlPullParserException {
259 
260         PolicyDefinition<V> policyDefinition = null;
261 
262         PolicyValue<V> currentResolvedPolicy = null;
263 
264         LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins = new LinkedHashMap<>();
265         int outerDepth = parser.getDepth();
266         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
267             String tag = parser.getName();
268             switch (tag) {
269                 case TAG_ADMIN_POLICY_ENTRY:
270                     PolicyValue<V> value = null;
271                     EnforcingAdmin admin = null;
272                     int adminPolicyDepth = parser.getDepth();
273                     while (XmlUtils.nextElementWithin(parser, adminPolicyDepth)) {
274                         String adminPolicyTag = parser.getName();
275                         switch (adminPolicyTag) {
276                             case TAG_ENFORCING_ADMIN_ENTRY:
277                                 admin = EnforcingAdmin.readFromXml(parser);
278                                 if (admin == null) {
279                                     Slogf.wtf(TAG, "Error Parsing TAG_ENFORCING_ADMIN_ENTRY, "
280                                             + "EnforcingAdmin is null");
281                                 }
282                                 break;
283                             case TAG_POLICY_VALUE_ENTRY:
284                                 value = policyDefinition.readPolicyValueFromXml(parser);
285                                 if (value == null) {
286                                     Slogf.wtf(TAG, "Error Parsing TAG_POLICY_VALUE_ENTRY, "
287                                             + "PolicyValue is null");
288                                 }
289                                 break;
290                         }
291                     }
292                     if (admin != null && value != null) {
293                         policiesSetByAdmins.put(admin, value);
294                     } else {
295                         Slogf.wtf(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY for "
296                                 + (policyDefinition == null ? "unknown policy" : "policy with "
297                                 + "definition " + policyDefinition) + ", EnforcingAdmin is: "
298                                 + (admin == null ? "null" : admin) + ", value is : "
299                                 + (value == null ? "null" : value));
300                     }
301                     break;
302                 case TAG_POLICY_DEFINITION_ENTRY:
303                     policyDefinition = PolicyDefinition.readFromXml(parser);
304                     if (policyDefinition == null) {
305                         Slogf.wtf(TAG, "Error Parsing TAG_POLICY_DEFINITION_ENTRY, "
306                                 + "PolicyDefinition is null");
307                     }
308                     break;
309 
310                 case TAG_RESOLVED_VALUE_ENTRY:
311                     if (policyDefinition == null) {
312                         Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY, "
313                                 + "policyDefinition is null");
314                         break;
315                     }
316                     currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(parser);
317                     if (currentResolvedPolicy == null) {
318                         Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY for "
319                                 + (policyDefinition == null ? "unknown policy" : "policy with "
320                                 + "definition " + policyDefinition) + ", "
321                                 + "currentResolvedPolicy is null");
322                     }
323                     break;
324                 default:
325                     Slogf.wtf(TAG, "Unknown tag: " + tag);
326             }
327         }
328         if (policyDefinition != null) {
329             return new PolicyState<V>(policyDefinition, policiesSetByAdmins, currentResolvedPolicy);
330         } else {
331             Slogf.wtf(TAG, "Error parsing policyState, policyDefinition is null");
332             return null;
333         }
334     }
335 
336 
337 
getPolicyDefinition()338     PolicyDefinition<V> getPolicyDefinition() {
339         return mPolicyDefinition;
340     }
341 }
342