blob: 630a39caeb0817f81477de7cfacff5cac681bb0b [file] [log] [blame]
Hai Zhangb7776682018-09-25 15:10:57 -07001/*
2 * Copyright (C) 2018 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
17package com.android.server.role;
18
19import android.annotation.CheckResult;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.UserIdInt;
23import android.annotation.WorkerThread;
24import android.os.Environment;
25import android.os.Handler;
26import android.util.ArrayMap;
27import android.util.ArraySet;
28import android.util.AtomicFile;
29import android.util.Slog;
30import android.util.Xml;
31
32import com.android.internal.annotations.GuardedBy;
33import com.android.internal.os.BackgroundThread;
Eugene Suslaabdefba2018-11-09 18:06:43 -080034import com.android.internal.util.CollectionUtils;
Hai Zhang33456fb2018-12-05 17:30:35 -080035import com.android.internal.util.dump.DualDumpOutputStream;
Hai Zhangb7776682018-09-25 15:10:57 -070036import com.android.internal.util.function.pooled.PooledLambda;
37
38import libcore.io.IoUtils;
39
40import org.xmlpull.v1.XmlPullParser;
41import org.xmlpull.v1.XmlPullParserException;
42import org.xmlpull.v1.XmlSerializer;
43
44import java.io.File;
45import java.io.FileInputStream;
46import java.io.FileNotFoundException;
47import java.io.FileOutputStream;
48import java.io.IOException;
49import java.nio.charset.StandardCharsets;
Eugene Suslaa2a80322018-12-12 17:09:38 -080050import java.util.ArrayList;
Hai Zhang8e60a8f2018-11-20 11:21:09 -080051import java.util.List;
Hai Zhang458cedb2018-12-03 15:41:11 -080052import java.util.Objects;
Hai Zhangb7776682018-09-25 15:10:57 -070053
54/**
55 * Stores the state of roles for a user.
56 */
57public class RoleUserState {
58
59 private static final String LOG_TAG = RoleUserState.class.getSimpleName();
60
61 public static final int VERSION_UNDEFINED = -1;
62
63 private static final String ROLES_FILE_NAME = "roles.xml";
64
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080065 private static final long WRITE_DELAY_MILLIS = 200;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080066
Hai Zhangb7776682018-09-25 15:10:57 -070067 private static final String TAG_ROLES = "roles";
68 private static final String TAG_ROLE = "role";
69 private static final String TAG_HOLDER = "holder";
70 private static final String ATTRIBUTE_VERSION = "version";
71 private static final String ATTRIBUTE_NAME = "name";
Eugene Suslaabdefba2018-11-09 18:06:43 -080072 private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
Hai Zhangb7776682018-09-25 15:10:57 -070073
74 @UserIdInt
75 private final int mUserId;
76
Hai Zhangcdc85c52018-12-06 13:56:55 -080077 @NonNull
Hai Zhang31d06ba2018-12-06 18:14:42 -080078 private final Callback mCallback;
79
80 @NonNull
Hai Zhangcdc85c52018-12-06 13:56:55 -080081 private final Object mLock = new Object();
82
83 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080084 private int mVersion = VERSION_UNDEFINED;
Hai Zhangb7776682018-09-25 15:10:57 -070085
Hai Zhangcdc85c52018-12-06 13:56:55 -080086 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080087 @Nullable
Hai Zhang33456fb2018-12-05 17:30:35 -080088 private String mPackagesHash;
Eugene Suslaabdefba2018-11-09 18:06:43 -080089
Hai Zhangb7776682018-09-25 15:10:57 -070090 /**
91 * Maps role names to its holders' package names. The values should never be null.
92 */
Hai Zhangcdc85c52018-12-06 13:56:55 -080093 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080094 @NonNull
95 private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
Hai Zhangb7776682018-09-25 15:10:57 -070096
Hai Zhangcdc85c52018-12-06 13:56:55 -080097 @GuardedBy("mLock")
Hai Zhangbc5430852018-12-06 23:25:43 -080098 private boolean mWriteScheduled;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080099
Hai Zhangcdc85c52018-12-06 13:56:55 -0800100 @GuardedBy("mLock")
Hai Zhangb7776682018-09-25 15:10:57 -0700101 private boolean mDestroyed;
102
Hai Zhang458cedb2018-12-03 15:41:11 -0800103 @NonNull
Hai Zhangb7776682018-09-25 15:10:57 -0700104 private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
105
Hai Zhang458cedb2018-12-03 15:41:11 -0800106 /**
Hai Zhang31d06ba2018-12-06 18:14:42 -0800107 * Create a new user state, and read its state from disk if previously persisted.
Hai Zhang458cedb2018-12-03 15:41:11 -0800108 *
Hai Zhang31d06ba2018-12-06 18:14:42 -0800109 * @param userId the user id for this user state
110 * @param callback the callback for this user state
Hai Zhang458cedb2018-12-03 15:41:11 -0800111 */
Hai Zhang31d06ba2018-12-06 18:14:42 -0800112 public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) {
Hai Zhangcdc85c52018-12-06 13:56:55 -0800113 mUserId = userId;
Hai Zhang31d06ba2018-12-06 18:14:42 -0800114 mCallback = callback;
Hai Zhangcdc85c52018-12-06 13:56:55 -0800115
116 readFile();
Hai Zhangb7776682018-09-25 15:10:57 -0700117 }
118
119 /**
120 * Get the version of this user state.
121 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800122 public int getVersion() {
123 synchronized (mLock) {
124 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800125
Hai Zhangcdc85c52018-12-06 13:56:55 -0800126 return mVersion;
127 }
Hai Zhangb7776682018-09-25 15:10:57 -0700128 }
129
130 /**
131 * Set the version of this user state.
132 *
133 * @param version the version to set
134 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800135 public void setVersion(int version) {
136 synchronized (mLock) {
137 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800138
Hai Zhangcdc85c52018-12-06 13:56:55 -0800139 if (mVersion == version) {
140 return;
141 }
142 mVersion = version;
143 scheduleWriteFileLocked();
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800144 }
Hai Zhangb7776682018-09-25 15:10:57 -0700145 }
146
147 /**
Hai Zhang458cedb2018-12-03 15:41:11 -0800148 * Get the hash representing the state of packages during the last time initial grants was run.
149 *
150 * @return the hash representing the state of packages
Eugene Suslaabdefba2018-11-09 18:06:43 -0800151 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800152 @Nullable
153 public String getPackagesHash() {
154 synchronized (mLock) {
155 return mPackagesHash;
156 }
Eugene Suslaabdefba2018-11-09 18:06:43 -0800157 }
158
159 /**
Hai Zhang458cedb2018-12-03 15:41:11 -0800160 * Set the hash representing the state of packages during the last time initial grants was run.
161 *
Hai Zhang33456fb2018-12-05 17:30:35 -0800162 * @param packagesHash the hash representing the state of packages
Eugene Suslaabdefba2018-11-09 18:06:43 -0800163 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800164 public void setPackagesHash(@Nullable String packagesHash) {
165 synchronized (mLock) {
166 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800167
Hai Zhangcdc85c52018-12-06 13:56:55 -0800168 if (Objects.equals(mPackagesHash, packagesHash)) {
169 return;
170 }
171 mPackagesHash = packagesHash;
172 scheduleWriteFileLocked();
Hai Zhang458cedb2018-12-03 15:41:11 -0800173 }
Eugene Suslaabdefba2018-11-09 18:06:43 -0800174 }
175
176 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700177 * Get whether the role is available.
178 *
179 * @param roleName the name of the role to get the holders for
180 *
181 * @return whether the role is available
182 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800183 public boolean isRoleAvailable(@NonNull String roleName) {
184 synchronized (mLock) {
185 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800186
Hai Zhangcdc85c52018-12-06 13:56:55 -0800187 return mRoles.containsKey(roleName);
188 }
Hai Zhangb7776682018-09-25 15:10:57 -0700189 }
190
191 /**
192 * Get the holders of a role.
193 *
194 * @param roleName the name of the role to query for
195 *
196 * @return the set of role holders. {@code null} should not be returned and indicates an issue.
197 */
Hai Zhangb7776682018-09-25 15:10:57 -0700198 @Nullable
Hai Zhangcdc85c52018-12-06 13:56:55 -0800199 public ArraySet<String> getRoleHolders(@NonNull String roleName) {
200 synchronized (mLock) {
201 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800202
Hai Zhangcdc85c52018-12-06 13:56:55 -0800203 return new ArraySet<>(mRoles.get(roleName));
204 }
Hai Zhangb7776682018-09-25 15:10:57 -0700205 }
206
207 /**
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800208 * Set the names of all available roles.
209 *
210 * @param roleNames the names of all the available roles
211 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800212 public void setRoleNames(@NonNull List<String> roleNames) {
213 synchronized (mLock) {
214 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800215
Hai Zhangcdc85c52018-12-06 13:56:55 -0800216 boolean changed = false;
Hai Zhang31d06ba2018-12-06 18:14:42 -0800217
Hai Zhangcdc85c52018-12-06 13:56:55 -0800218 for (int i = mRoles.size() - 1; i >= 0; i--) {
219 String roleName = mRoles.keyAt(i);
Hai Zhang31d06ba2018-12-06 18:14:42 -0800220
Hai Zhangcdc85c52018-12-06 13:56:55 -0800221 if (!roleNames.contains(roleName)) {
222 ArraySet<String> packageNames = mRoles.valueAt(i);
223 if (!packageNames.isEmpty()) {
Hai Zhang31d06ba2018-12-06 18:14:42 -0800224 Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
225 + " role: " + roleName + ", holders: " + packageNames);
Hai Zhangcdc85c52018-12-06 13:56:55 -0800226 }
227 mRoles.removeAt(i);
228 changed = true;
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800229 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800230 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800231
Hai Zhangcdc85c52018-12-06 13:56:55 -0800232 int roleNamesSize = roleNames.size();
233 for (int i = 0; i < roleNamesSize; i++) {
234 String roleName = roleNames.get(i);
Hai Zhang31d06ba2018-12-06 18:14:42 -0800235
Hai Zhangcdc85c52018-12-06 13:56:55 -0800236 if (!mRoles.containsKey(roleName)) {
237 mRoles.put(roleName, new ArraySet<>());
238 Slog.i(LOG_TAG, "Added new role: " + roleName);
239 changed = true;
240 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800241 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800242
Hai Zhangcdc85c52018-12-06 13:56:55 -0800243 if (changed) {
244 scheduleWriteFileLocked();
245 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800246 }
247 }
248
249 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700250 * Add a holder to a role.
251 *
252 * @param roleName the name of the role to add the holder to
253 * @param packageName the package name of the new holder
254 *
255 * @return {@code false} only if the set of role holders is null, which should not happen and
256 * indicates an issue.
257 */
258 @CheckResult
Hai Zhangcdc85c52018-12-06 13:56:55 -0800259 public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
Hai Zhang31d06ba2018-12-06 18:14:42 -0800260 boolean changed;
261
Hai Zhangcdc85c52018-12-06 13:56:55 -0800262 synchronized (mLock) {
263 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800264
Hai Zhangcdc85c52018-12-06 13:56:55 -0800265 ArraySet<String> roleHolders = mRoles.get(roleName);
266 if (roleHolders == null) {
267 Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
268 + ", package: " + packageName);
269 return false;
270 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800271 changed = roleHolders.add(packageName);
Hai Zhangcdc85c52018-12-06 13:56:55 -0800272 if (changed) {
273 scheduleWriteFileLocked();
274 }
Hai Zhangb7776682018-09-25 15:10:57 -0700275 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800276
277 if (changed) {
278 mCallback.onRoleHoldersChanged(roleName, mUserId);
279 }
280 return true;
Hai Zhangb7776682018-09-25 15:10:57 -0700281 }
282
283 /**
284 * Remove a holder from a role.
285 *
286 * @param roleName the name of the role to remove the holder from
287 * @param packageName the package name of the holder to remove
288 *
289 * @return {@code false} only if the set of role holders is null, which should not happen and
290 * indicates an issue.
291 */
292 @CheckResult
Hai Zhangcdc85c52018-12-06 13:56:55 -0800293 public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
Hai Zhang31d06ba2018-12-06 18:14:42 -0800294 boolean changed;
295
Hai Zhangcdc85c52018-12-06 13:56:55 -0800296 synchronized (mLock) {
297 throwIfDestroyedLocked();
Hai Zhang31d06ba2018-12-06 18:14:42 -0800298
Hai Zhangcdc85c52018-12-06 13:56:55 -0800299 ArraySet<String> roleHolders = mRoles.get(roleName);
300 if (roleHolders == null) {
301 Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
302 + ", package: " + packageName);
303 return false;
304 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800305
306 changed = roleHolders.remove(packageName);
Hai Zhangcdc85c52018-12-06 13:56:55 -0800307 if (changed) {
308 scheduleWriteFileLocked();
309 }
Hai Zhangb7776682018-09-25 15:10:57 -0700310 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800311
312 if (changed) {
313 mCallback.onRoleHoldersChanged(roleName, mUserId);
314 }
315 return true;
Hai Zhangb7776682018-09-25 15:10:57 -0700316 }
317
318 /**
Eugene Suslaa2a80322018-12-12 17:09:38 -0800319 * @see android.app.role.RoleManager#getHeldRolesFromController
320 */
321 @NonNull
322 public List<String> getHeldRoles(@NonNull String packageName) {
323 ArrayList<String> result = new ArrayList<>();
324 int size = mRoles.size();
325 for (int i = 0; i < size; i++) {
326 if (mRoles.valueAt(i).contains(packageName)) {
327 result.add(mRoles.keyAt(i));
328 }
329 }
330 return result;
331 }
332
333 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700334 * Schedule writing the state to file.
335 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800336 @GuardedBy("mLock")
337 private void scheduleWriteFileLocked() {
Hai Zhangb7776682018-09-25 15:10:57 -0700338 throwIfDestroyedLocked();
Hai Zhang458cedb2018-12-03 15:41:11 -0800339
Hai Zhangbc5430852018-12-06 23:25:43 -0800340 if (!mWriteScheduled) {
341 mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile,
342 this), WRITE_DELAY_MILLIS);
343 mWriteScheduled = true;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -0800344 }
Hai Zhangb7776682018-09-25 15:10:57 -0700345 }
346
347 @WorkerThread
Hai Zhangcdc85c52018-12-06 13:56:55 -0800348 private void writeFile() {
349 int version;
350 String packagesHash;
351 ArrayMap<String, ArraySet<String>> roles;
352 synchronized (mLock) {
353 if (mDestroyed) {
354 return;
355 }
356
Hai Zhangbc5430852018-12-06 23:25:43 -0800357 mWriteScheduled = false;
358
Hai Zhangcdc85c52018-12-06 13:56:55 -0800359 version = mVersion;
360 packagesHash = mPackagesHash;
361 roles = snapshotRolesLocked();
362 }
363
Hai Zhangb295ac42018-11-16 16:08:18 -0800364 AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
Hai Zhangb7776682018-09-25 15:10:57 -0700365 FileOutputStream out = null;
366 try {
Hai Zhangb295ac42018-11-16 16:08:18 -0800367 out = atomicFile.startWrite();
Hai Zhangb7776682018-09-25 15:10:57 -0700368
369 XmlSerializer serializer = Xml.newSerializer();
370 serializer.setOutput(out, StandardCharsets.UTF_8.name());
371 serializer.setFeature(
372 "http://xmlpull.org/v1/doc/features.html#indent-output", true);
373 serializer.startDocument(null, true);
374
Hai Zhang458cedb2018-12-03 15:41:11 -0800375 serializeRoles(serializer, version, packagesHash, roles);
Hai Zhangb7776682018-09-25 15:10:57 -0700376
377 serializer.endDocument();
Hai Zhangb295ac42018-11-16 16:08:18 -0800378 atomicFile.finishWrite(out);
Hai Zhangb3e9b9b2018-12-05 18:32:40 -0800379 Slog.i(LOG_TAG, "Wrote roles.xml successfully");
Hai Zhangb295ac42018-11-16 16:08:18 -0800380 } catch (IllegalArgumentException | IllegalStateException | IOException e) {
381 Slog.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup", e);
382 if (out != null) {
383 atomicFile.failWrite(out);
384 }
Hai Zhangb7776682018-09-25 15:10:57 -0700385 } finally {
386 IoUtils.closeQuietly(out);
387 }
388 }
389
390 @WorkerThread
391 private void serializeRoles(@NonNull XmlSerializer serializer, int version,
Hai Zhang458cedb2018-12-03 15:41:11 -0800392 @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
Eugene Suslaabdefba2018-11-09 18:06:43 -0800393 throws IOException {
Hai Zhangb7776682018-09-25 15:10:57 -0700394 serializer.startTag(null, TAG_ROLES);
Hai Zhang458cedb2018-12-03 15:41:11 -0800395
Hai Zhangb7776682018-09-25 15:10:57 -0700396 serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
Hai Zhang458cedb2018-12-03 15:41:11 -0800397
398 if (packagesHash != null) {
399 serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
400 }
401
Hai Zhangb7776682018-09-25 15:10:57 -0700402 for (int i = 0, size = roles.size(); i < size; ++i) {
403 String roleName = roles.keyAt(i);
404 ArraySet<String> roleHolders = roles.valueAt(i);
Hai Zhang458cedb2018-12-03 15:41:11 -0800405
Hai Zhangb7776682018-09-25 15:10:57 -0700406 serializer.startTag(null, TAG_ROLE);
407 serializer.attribute(null, ATTRIBUTE_NAME, roleName);
408 serializeRoleHolders(serializer, roleHolders);
409 serializer.endTag(null, TAG_ROLE);
410 }
Hai Zhang458cedb2018-12-03 15:41:11 -0800411
Hai Zhangb7776682018-09-25 15:10:57 -0700412 serializer.endTag(null, TAG_ROLES);
413 }
414
415 @WorkerThread
416 private void serializeRoleHolders(@NonNull XmlSerializer serializer,
417 @NonNull ArraySet<String> roleHolders) throws IOException {
418 for (int i = 0, size = roleHolders.size(); i < size; ++i) {
419 String roleHolder = roleHolders.valueAt(i);
Hai Zhang458cedb2018-12-03 15:41:11 -0800420
Hai Zhangb7776682018-09-25 15:10:57 -0700421 serializer.startTag(null, TAG_HOLDER);
422 serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
423 serializer.endTag(null, TAG_HOLDER);
424 }
425 }
426
427 /**
428 * Read the state from file.
429 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800430 private void readFile() {
431 synchronized (mLock) {
432 File file = getFile(mUserId);
433 try (FileInputStream in = new AtomicFile(file).openRead()) {
434 XmlPullParser parser = Xml.newPullParser();
435 parser.setInput(in, null);
436 parseXmlLocked(parser);
437 Slog.i(LOG_TAG, "Read roles.xml successfully");
438 } catch (FileNotFoundException e) {
439 Slog.i(LOG_TAG, "roles.xml not found");
440 } catch (XmlPullParserException | IOException e) {
441 throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
442 }
Hai Zhangb7776682018-09-25 15:10:57 -0700443 }
444 }
445
446 private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
447 XmlPullParserException {
Hai Zhangb7776682018-09-25 15:10:57 -0700448 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800449 int depth;
450 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700451 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800452 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
453 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700454 continue;
455 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800456
Hai Zhangb7776682018-09-25 15:10:57 -0700457 if (parser.getName().equals(TAG_ROLES)) {
458 parseRolesLocked(parser);
459 return;
460 }
461 }
Hai Zhang458cedb2018-12-03 15:41:11 -0800462 Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
Hai Zhangb7776682018-09-25 15:10:57 -0700463 }
464
465 private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
466 XmlPullParserException {
467 mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
Hai Zhang33456fb2018-12-05 17:30:35 -0800468 mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
Hai Zhang458cedb2018-12-03 15:41:11 -0800469 mRoles.clear();
Hai Zhangb295ac42018-11-16 16:08:18 -0800470
Hai Zhangb7776682018-09-25 15:10:57 -0700471 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800472 int depth;
473 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700474 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800475 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
476 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700477 continue;
478 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800479
Hai Zhangb7776682018-09-25 15:10:57 -0700480 if (parser.getName().equals(TAG_ROLE)) {
481 String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
482 ArraySet<String> roleHolders = parseRoleHoldersLocked(parser);
483 mRoles.put(roleName, roleHolders);
484 }
485 }
486 }
487
488 @NonNull
489 private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
490 throws IOException, XmlPullParserException {
491 ArraySet<String> roleHolders = new ArraySet<>();
Hai Zhangb295ac42018-11-16 16:08:18 -0800492
Hai Zhangb7776682018-09-25 15:10:57 -0700493 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800494 int depth;
495 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700496 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800497 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
498 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700499 continue;
500 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800501
Hai Zhangb7776682018-09-25 15:10:57 -0700502 if (parser.getName().equals(TAG_HOLDER)) {
503 String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
504 roleHolders.add(roleHolder);
505 }
506 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800507
Hai Zhangb7776682018-09-25 15:10:57 -0700508 return roleHolders;
509 }
510
511 /**
Hai Zhang33456fb2018-12-05 17:30:35 -0800512 * Dump this user state.
513 *
514 * @param dumpOutputStream the output stream to dump to
515 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800516 public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
517 long fieldId) {
518 int version;
519 String packagesHash;
520 ArrayMap<String, ArraySet<String>> roles;
521 synchronized (mLock) {
522 throwIfDestroyedLocked();
523
524 version = mVersion;
525 packagesHash = mPackagesHash;
526 roles = snapshotRolesLocked();
527 }
Hai Zhang33456fb2018-12-05 17:30:35 -0800528
529 long fieldToken = dumpOutputStream.start(fieldName, fieldId);
530 dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
Hai Zhangcdc85c52018-12-06 13:56:55 -0800531 dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
532 dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);
Hai Zhang33456fb2018-12-05 17:30:35 -0800533
Hai Zhangcdc85c52018-12-06 13:56:55 -0800534 int rolesSize = roles.size();
Hai Zhang33456fb2018-12-05 17:30:35 -0800535 for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
Hai Zhangcdc85c52018-12-06 13:56:55 -0800536 String roleName = roles.keyAt(rolesIndex);
537 ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
Hai Zhang33456fb2018-12-05 17:30:35 -0800538
539 long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
540 dumpOutputStream.write("name", RoleProto.NAME, roleName);
541
542 int roleHoldersSize = roleHolders.size();
543 for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
544 String roleHolder = roleHolders.valueAt(roleHoldersIndex);
545
546 dumpOutputStream.write("holders", RoleProto.HOLDERS, roleHolder);
547 }
548
549 dumpOutputStream.end(rolesToken);
550 }
551
552 dumpOutputStream.end(fieldToken);
553 }
554
Hai Zhangcdc85c52018-12-06 13:56:55 -0800555 @GuardedBy("mLock")
556 private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
557 ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
558 for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
559 String roleName = mRoles.keyAt(i);
560 ArraySet<String> roleHolders = mRoles.valueAt(i);
561
562 roleHolders = new ArraySet<>(roleHolders);
563 roles.put(roleName, roleHolders);
564 }
565 return roles;
566 }
567
Hai Zhang33456fb2018-12-05 17:30:35 -0800568 /**
Hai Zhang31d06ba2018-12-06 18:14:42 -0800569 * Destroy this user state and delete the corresponding file. Any pending writes to the file
570 * will be cancelled, and any future interaction with this state will throw an exception.
Hai Zhangb7776682018-09-25 15:10:57 -0700571 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800572 public void destroy() {
573 synchronized (mLock) {
574 throwIfDestroyedLocked();
575 mWriteHandler.removeCallbacksAndMessages(null);
576 getFile(mUserId).delete();
577 mDestroyed = true;
578 }
Hai Zhangb7776682018-09-25 15:10:57 -0700579 }
580
Hai Zhangcdc85c52018-12-06 13:56:55 -0800581 @GuardedBy("mLock")
Hai Zhangb7776682018-09-25 15:10:57 -0700582 private void throwIfDestroyedLocked() {
583 if (mDestroyed) {
584 throw new IllegalStateException("This RoleUserState has already been destroyed");
585 }
586 }
587
588 private static @NonNull File getFile(@UserIdInt int userId) {
589 return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
590 }
Hai Zhang31d06ba2018-12-06 18:14:42 -0800591
592 /**
593 * Callback for a user state.
594 */
595 public interface Callback {
596
597 /**
598 * Called when the holders of roles are changed.
599 *
600 * @param roleName the name of the role whose holders are changed
601 * @param userId the user id for this role holder change
602 */
603 void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
604 }
Hai Zhangb7776682018-09-25 15:10:57 -0700605}