• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.pm;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.os.Bundle;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.util.IntArray;
26 import android.util.SparseArray;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.modules.utils.TypedXmlPullParser;
30 import com.android.modules.utils.TypedXmlSerializer;
31 import com.android.server.BundleUtils;
32 
33 import org.xmlpull.v1.XmlPullParser;
34 import org.xmlpull.v1.XmlPullParserException;
35 
36 import java.io.IOException;
37 import java.io.PrintWriter;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 /**
42  * Data structure that contains the mapping of users to user restrictions.
43  *
44  * @hide
45  */
46 public class RestrictionsSet {
47 
48     private static final String USER_ID = "user_id";
49     private static final String TAG_RESTRICTIONS = "restrictions";
50     private static final String TAG_RESTRICTIONS_USER = "restrictions_user";
51 
52     /**
53      * Mapping of user restrictions.
54      * Only non-empty restriction bundles are stored.
55      * The key is the user id of the user.
56      * userId -> restrictionBundle
57      */
58     private final SparseArray<Bundle> mUserRestrictions = new SparseArray<>(0);
59 
RestrictionsSet()60     public RestrictionsSet() {
61     }
62 
RestrictionsSet(@serIdInt int userId, @NonNull Bundle restrictions)63     public RestrictionsSet(@UserIdInt int userId, @NonNull Bundle restrictions) {
64         if (restrictions.isEmpty()) {
65             throw new IllegalArgumentException("empty restriction bundle cannot be added.");
66         }
67         mUserRestrictions.put(userId, restrictions);
68         UserManager.invalidateUserRestriction();
69     }
70 
71     /**
72      * Updates restriction bundle for a given user.
73      * If new bundle is empty, record is removed from the array.
74      *
75      * @return whether restrictions bundle is different from the old one.
76      */
updateRestrictions(@serIdInt int userId, @Nullable Bundle restrictions)77     public boolean updateRestrictions(@UserIdInt int userId, @Nullable Bundle restrictions) {
78         final boolean changed =
79                 !UserRestrictionsUtils.areEqual(mUserRestrictions.get(userId), restrictions);
80         if (!changed) {
81             return false;
82         }
83         if (!BundleUtils.isEmpty(restrictions)) {
84             mUserRestrictions.put(userId, restrictions);
85         } else {
86             mUserRestrictions.delete(userId);
87         }
88         UserManager.invalidateUserRestriction();
89         return true;
90     }
91 
92     /**
93      * Removes a particular restriction for all users.
94      *
95      * @return whether the restriction was removed or not.
96      */
removeRestrictionsForAllUsers(String restriction)97     public boolean removeRestrictionsForAllUsers(String restriction) {
98         boolean removed = false;
99         for (int i = 0; i < mUserRestrictions.size(); i++) {
100             final Bundle restrictions = mUserRestrictions.valueAt(i);
101 
102             if (UserRestrictionsUtils.contains(restrictions, restriction)) {
103                 restrictions.remove(restriction);
104                 removed = true;
105             }
106         }
107         if (removed) {
108             UserManager.invalidateUserRestriction();
109         }
110         return removed;
111     }
112 
113     /**
114      * Moves a particular restriction from one restriction set to another, e.g. for all users.
115      */
moveRestriction(@onNull RestrictionsSet destRestrictions, String restriction)116     public void moveRestriction(@NonNull RestrictionsSet destRestrictions, String restriction) {
117         for (int i = 0; i < mUserRestrictions.size(); i++) {
118             final int userId = mUserRestrictions.keyAt(i);
119             final Bundle from = mUserRestrictions.valueAt(i);
120 
121             if (UserRestrictionsUtils.contains(from, restriction)) {
122                 from.remove(restriction);
123                 Bundle to = destRestrictions.getRestrictions(userId);
124                 if (to == null) {
125                     to = new Bundle();
126                     to.putBoolean(restriction, true);
127                     destRestrictions.updateRestrictions(userId, to);
128                 } else {
129                     to.putBoolean(restriction, true);
130                 }
131                 // Don't keep empty bundles.
132                 if (from.isEmpty()) {
133                     mUserRestrictions.removeAt(i);
134                     i--;
135                 }
136             }
137             UserManager.invalidateUserRestriction();
138         }
139     }
140 
141     /**
142      * @return whether restrictions set has no restrictions.
143      */
isEmpty()144     public boolean isEmpty() {
145         return mUserRestrictions.size() == 0;
146     }
147 
148     /**
149      * Merge all restrictions in restrictions set into one bundle. The original user restrictions
150      * set does not get modified, instead a new bundle is returned.
151      *
152      * @return restrictions bundle containing all user restrictions.
153      */
mergeAll()154     public @NonNull Bundle mergeAll() {
155         final Bundle result = new Bundle();
156         for (int i = 0; i < mUserRestrictions.size(); i++) {
157             UserRestrictionsUtils.merge(result, mUserRestrictions.valueAt(i));
158         }
159         return result;
160     }
161 
162     /**
163      * @return list of enforcing users that enforce a particular restriction.
164      */
getEnforcingUsers(String restriction, @UserIdInt int userId)165     public @NonNull List<UserManager.EnforcingUser> getEnforcingUsers(String restriction,
166             @UserIdInt int userId) {
167         final List<UserManager.EnforcingUser> result = new ArrayList<>();
168         if (getRestrictionsNonNull(userId).containsKey(restriction)) {
169             result.add(new UserManager.EnforcingUser(userId,
170                     UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
171         }
172 
173         if (getRestrictionsNonNull(UserHandle.USER_ALL).containsKey(restriction)) {
174             result.add(new UserManager.EnforcingUser(UserHandle.USER_ALL,
175                     UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
176         }
177 
178         return result;
179     }
180 
181     /**
182      * @return list of user restrictions for a given user. Null is returned if the user does not
183      * have any restrictions.
184      */
getRestrictions(@serIdInt int userId)185     public @Nullable Bundle getRestrictions(@UserIdInt int userId) {
186         return mUserRestrictions.get(userId);
187     }
188 
189     /** @return list of user restrictions for a given user that is not null. */
getRestrictionsNonNull(@serIdInt int userId)190     public @NonNull Bundle getRestrictionsNonNull(@UserIdInt int userId) {
191         return UserRestrictionsUtils.nonNull(mUserRestrictions.get(userId));
192     }
193 
194     /**
195      * Removes a given user from the restrictions set, returning true if the user has non-empty
196      * restrictions before removal.
197      */
remove(@serIdInt int userId)198     public boolean remove(@UserIdInt int userId) {
199         boolean hasUserRestriction = mUserRestrictions.contains(userId);
200         mUserRestrictions.remove(userId);
201         UserManager.invalidateUserRestriction();
202         return hasUserRestriction;
203     }
204 
205     /**
206      * Remove list of users and user restrictions.
207      */
removeAllRestrictions()208     public void removeAllRestrictions() {
209         mUserRestrictions.clear();
210         UserManager.invalidateUserRestriction();
211     }
212 
213     /**
214      * Serialize a given {@link RestrictionsSet} to XML.
215      */
writeRestrictions(@onNull TypedXmlSerializer serializer, @NonNull String outerTag)216     public void writeRestrictions(@NonNull TypedXmlSerializer serializer, @NonNull String outerTag)
217             throws IOException {
218         serializer.startTag(null, outerTag);
219         for (int i = 0; i < mUserRestrictions.size(); i++) {
220             serializer.startTag(null, TAG_RESTRICTIONS_USER);
221             serializer.attributeInt(null, USER_ID, mUserRestrictions.keyAt(i));
222             UserRestrictionsUtils.writeRestrictions(serializer, mUserRestrictions.valueAt(i),
223                     TAG_RESTRICTIONS);
224             serializer.endTag(null, TAG_RESTRICTIONS_USER);
225         }
226         serializer.endTag(null, outerTag);
227     }
228 
229     /**
230      * Read restrictions from XML.
231      */
readRestrictions(@onNull TypedXmlPullParser parser, @NonNull String outerTag)232     public static RestrictionsSet readRestrictions(@NonNull TypedXmlPullParser parser,
233             @NonNull String outerTag) throws IOException, XmlPullParserException {
234         RestrictionsSet restrictionsSet = new RestrictionsSet();
235         int userId = 0;
236         int type;
237 
238         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
239             String tag = parser.getName();
240             if (type == XmlPullParser.END_TAG && outerTag.equals(tag)) {
241                 return restrictionsSet;
242             } else if (type == XmlPullParser.START_TAG && TAG_RESTRICTIONS_USER.equals(tag)) {
243                 userId = parser.getAttributeInt(null, USER_ID);
244             } else if (type == XmlPullParser.START_TAG && TAG_RESTRICTIONS.equals(tag)) {
245                 Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser);
246                 restrictionsSet.updateRestrictions(userId, restrictions);
247             }
248         }
249         throw new XmlPullParserException("restrictions cannot be read as xml is malformed.");
250     }
251 
252     /**
253      * Dumps {@link RestrictionsSet}.
254      */
dumpRestrictions(PrintWriter pw, String prefix)255     public void dumpRestrictions(PrintWriter pw, String prefix) {
256         boolean noneSet = true;
257         for (int i = 0; i < mUserRestrictions.size(); i++) {
258             pw.println(prefix + "User Id: " + mUserRestrictions.keyAt(i));
259             UserRestrictionsUtils.dumpRestrictions(pw, prefix + "  ", mUserRestrictions.valueAt(i));
260             noneSet = false;
261         }
262         if (noneSet) {
263             pw.println(prefix + "none");
264         }
265     }
266 
267     /** @return list of users in this restriction set. */
getUserIds()268     public IntArray getUserIds() {
269         IntArray userIds = new IntArray(mUserRestrictions.size());
270         for (int i = 0; i < mUserRestrictions.size(); i++) {
271             userIds.add(mUserRestrictions.keyAt(i));
272         }
273         return userIds;
274     }
275 
containsKey(@serIdInt int userId)276     public boolean containsKey(@UserIdInt int userId) {
277         return mUserRestrictions.contains(userId);
278     }
279 
280     @VisibleForTesting
size()281     public int size() {
282         return mUserRestrictions.size();
283     }
284 
285     @VisibleForTesting
keyAt(int index)286     public int keyAt(int index) {
287         return mUserRestrictions.keyAt(index);
288     }
289 
290     @VisibleForTesting
valueAt(int index)291     public Bundle valueAt(int index) {
292         return mUserRestrictions.valueAt(index);
293     }
294 
295 }
296