blob: ec614a451f545a48db1f219f56afcc51e1fa939f [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;
Hai Zhang8e60a8f2018-11-20 11:21:09 -080050import java.util.List;
Hai Zhang458cedb2018-12-03 15:41:11 -080051import java.util.Objects;
Hai Zhangb7776682018-09-25 15:10:57 -070052
53/**
54 * Stores the state of roles for a user.
55 */
56public class RoleUserState {
57
58 private static final String LOG_TAG = RoleUserState.class.getSimpleName();
59
60 public static final int VERSION_UNDEFINED = -1;
61
62 private static final String ROLES_FILE_NAME = "roles.xml";
63
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080064 private static final long WRITE_DELAY_MILLIS = 200;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080065
Hai Zhangb7776682018-09-25 15:10:57 -070066 private static final String TAG_ROLES = "roles";
67 private static final String TAG_ROLE = "role";
68 private static final String TAG_HOLDER = "holder";
69 private static final String ATTRIBUTE_VERSION = "version";
70 private static final String ATTRIBUTE_NAME = "name";
Eugene Suslaabdefba2018-11-09 18:06:43 -080071 private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
Hai Zhangb7776682018-09-25 15:10:57 -070072
73 @UserIdInt
74 private final int mUserId;
75
Hai Zhangcdc85c52018-12-06 13:56:55 -080076 @NonNull
77 private final Object mLock = new Object();
78
79 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080080 private int mVersion = VERSION_UNDEFINED;
Hai Zhangb7776682018-09-25 15:10:57 -070081
Hai Zhangcdc85c52018-12-06 13:56:55 -080082 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080083 @Nullable
Hai Zhang33456fb2018-12-05 17:30:35 -080084 private String mPackagesHash;
Eugene Suslaabdefba2018-11-09 18:06:43 -080085
Hai Zhangb7776682018-09-25 15:10:57 -070086 /**
87 * Maps role names to its holders' package names. The values should never be null.
88 */
Hai Zhangcdc85c52018-12-06 13:56:55 -080089 @GuardedBy("mLock")
Hai Zhang458cedb2018-12-03 15:41:11 -080090 @NonNull
91 private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
Hai Zhangb7776682018-09-25 15:10:57 -070092
Hai Zhangcdc85c52018-12-06 13:56:55 -080093 @GuardedBy("mLock")
Hai Zhangbc5430852018-12-06 23:25:43 -080094 private boolean mWriteScheduled;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -080095
Hai Zhangcdc85c52018-12-06 13:56:55 -080096 @GuardedBy("mLock")
Hai Zhangb7776682018-09-25 15:10:57 -070097 private boolean mDestroyed;
98
Hai Zhang458cedb2018-12-03 15:41:11 -080099 @NonNull
Hai Zhangb7776682018-09-25 15:10:57 -0700100 private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
101
Hai Zhang458cedb2018-12-03 15:41:11 -0800102 /**
103 * Create a new instance of user state, and read its state from disk if previously persisted.
104 *
105 * @param userId the user id for the new user state
Hai Zhang458cedb2018-12-03 15:41:11 -0800106 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800107 public RoleUserState(@UserIdInt int userId) {
108 mUserId = userId;
109
110 readFile();
Hai Zhangb7776682018-09-25 15:10:57 -0700111 }
112
113 /**
114 * Get the version of this user state.
115 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800116 public int getVersion() {
117 synchronized (mLock) {
118 throwIfDestroyedLocked();
119 return mVersion;
120 }
Hai Zhangb7776682018-09-25 15:10:57 -0700121 }
122
123 /**
124 * Set the version of this user state.
125 *
126 * @param version the version to set
127 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800128 public void setVersion(int version) {
129 synchronized (mLock) {
130 throwIfDestroyedLocked();
131 if (mVersion == version) {
132 return;
133 }
134 mVersion = version;
135 scheduleWriteFileLocked();
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800136 }
Hai Zhangb7776682018-09-25 15:10:57 -0700137 }
138
139 /**
Hai Zhang458cedb2018-12-03 15:41:11 -0800140 * Get the hash representing the state of packages during the last time initial grants was run.
141 *
142 * @return the hash representing the state of packages
Eugene Suslaabdefba2018-11-09 18:06:43 -0800143 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800144 @Nullable
145 public String getPackagesHash() {
146 synchronized (mLock) {
147 return mPackagesHash;
148 }
Eugene Suslaabdefba2018-11-09 18:06:43 -0800149 }
150
151 /**
Hai Zhang458cedb2018-12-03 15:41:11 -0800152 * Set the hash representing the state of packages during the last time initial grants was run.
153 *
Hai Zhang33456fb2018-12-05 17:30:35 -0800154 * @param packagesHash the hash representing the state of packages
Eugene Suslaabdefba2018-11-09 18:06:43 -0800155 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800156 public void setPackagesHash(@Nullable String packagesHash) {
157 synchronized (mLock) {
158 throwIfDestroyedLocked();
159 if (Objects.equals(mPackagesHash, packagesHash)) {
160 return;
161 }
162 mPackagesHash = packagesHash;
163 scheduleWriteFileLocked();
Hai Zhang458cedb2018-12-03 15:41:11 -0800164 }
Eugene Suslaabdefba2018-11-09 18:06:43 -0800165 }
166
167 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700168 * Get whether the role is available.
169 *
170 * @param roleName the name of the role to get the holders for
171 *
172 * @return whether the role is available
173 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800174 public boolean isRoleAvailable(@NonNull String roleName) {
175 synchronized (mLock) {
176 throwIfDestroyedLocked();
177 return mRoles.containsKey(roleName);
178 }
Hai Zhangb7776682018-09-25 15:10:57 -0700179 }
180
181 /**
182 * Get the holders of a role.
183 *
184 * @param roleName the name of the role to query for
185 *
186 * @return the set of role holders. {@code null} should not be returned and indicates an issue.
187 */
Hai Zhangb7776682018-09-25 15:10:57 -0700188 @Nullable
Hai Zhangcdc85c52018-12-06 13:56:55 -0800189 public ArraySet<String> getRoleHolders(@NonNull String roleName) {
190 synchronized (mLock) {
191 throwIfDestroyedLocked();
192 return new ArraySet<>(mRoles.get(roleName));
193 }
Hai Zhangb7776682018-09-25 15:10:57 -0700194 }
195
196 /**
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800197 * Set the names of all available roles.
198 *
199 * @param roleNames the names of all the available roles
200 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800201 public void setRoleNames(@NonNull List<String> roleNames) {
202 synchronized (mLock) {
203 throwIfDestroyedLocked();
204 boolean changed = false;
205 for (int i = mRoles.size() - 1; i >= 0; i--) {
206 String roleName = mRoles.keyAt(i);
207 if (!roleNames.contains(roleName)) {
208 ArraySet<String> packageNames = mRoles.valueAt(i);
209 if (!packageNames.isEmpty()) {
210 Slog.e(LOG_TAG,
211 "Holders of a removed role should have been cleaned up, role: "
212 + roleName + ", holders: " + packageNames);
213 }
214 mRoles.removeAt(i);
215 changed = true;
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800216 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800217 }
Hai Zhangcdc85c52018-12-06 13:56:55 -0800218 int roleNamesSize = roleNames.size();
219 for (int i = 0; i < roleNamesSize; i++) {
220 String roleName = roleNames.get(i);
221 if (!mRoles.containsKey(roleName)) {
222 mRoles.put(roleName, new ArraySet<>());
223 Slog.i(LOG_TAG, "Added new role: " + roleName);
224 changed = true;
225 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800226 }
Hai Zhangcdc85c52018-12-06 13:56:55 -0800227 if (changed) {
228 scheduleWriteFileLocked();
229 }
Hai Zhang8e60a8f2018-11-20 11:21:09 -0800230 }
231 }
232
233 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700234 * Add a holder to a role.
235 *
236 * @param roleName the name of the role to add the holder to
237 * @param packageName the package name of the new holder
238 *
239 * @return {@code false} only if the set of role holders is null, which should not happen and
240 * indicates an issue.
241 */
242 @CheckResult
Hai Zhangcdc85c52018-12-06 13:56:55 -0800243 public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
244 synchronized (mLock) {
245 throwIfDestroyedLocked();
246 ArraySet<String> roleHolders = mRoles.get(roleName);
247 if (roleHolders == null) {
248 Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
249 + ", package: " + packageName);
250 return false;
251 }
252 boolean changed = roleHolders.add(packageName);
253 if (changed) {
254 scheduleWriteFileLocked();
255 }
256 return true;
Hai Zhangb7776682018-09-25 15:10:57 -0700257 }
Hai Zhangb7776682018-09-25 15:10:57 -0700258 }
259
260 /**
261 * Remove a holder from a role.
262 *
263 * @param roleName the name of the role to remove the holder from
264 * @param packageName the package name of the holder to remove
265 *
266 * @return {@code false} only if the set of role holders is null, which should not happen and
267 * indicates an issue.
268 */
269 @CheckResult
Hai Zhangcdc85c52018-12-06 13:56:55 -0800270 public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
271 synchronized (mLock) {
272 throwIfDestroyedLocked();
273 ArraySet<String> roleHolders = mRoles.get(roleName);
274 if (roleHolders == null) {
275 Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
276 + ", package: " + packageName);
277 return false;
278 }
279 boolean changed = roleHolders.remove(packageName);
280 if (changed) {
281 scheduleWriteFileLocked();
282 }
283 return true;
Hai Zhangb7776682018-09-25 15:10:57 -0700284 }
Hai Zhangb7776682018-09-25 15:10:57 -0700285 }
286
287 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700288 * Schedule writing the state to file.
289 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800290 @GuardedBy("mLock")
291 private void scheduleWriteFileLocked() {
Hai Zhangb7776682018-09-25 15:10:57 -0700292 throwIfDestroyedLocked();
Hai Zhang458cedb2018-12-03 15:41:11 -0800293
Hai Zhangbc5430852018-12-06 23:25:43 -0800294 if (!mWriteScheduled) {
295 mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile,
296 this), WRITE_DELAY_MILLIS);
297 mWriteScheduled = true;
Hai Zhangb3e9b9b2018-12-05 18:32:40 -0800298 }
Hai Zhangb7776682018-09-25 15:10:57 -0700299 }
300
301 @WorkerThread
Hai Zhangcdc85c52018-12-06 13:56:55 -0800302 private void writeFile() {
303 int version;
304 String packagesHash;
305 ArrayMap<String, ArraySet<String>> roles;
306 synchronized (mLock) {
307 if (mDestroyed) {
308 return;
309 }
310
Hai Zhangbc5430852018-12-06 23:25:43 -0800311 mWriteScheduled = false;
312
Hai Zhangcdc85c52018-12-06 13:56:55 -0800313 version = mVersion;
314 packagesHash = mPackagesHash;
315 roles = snapshotRolesLocked();
316 }
317
Hai Zhangb295ac42018-11-16 16:08:18 -0800318 AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
Hai Zhangb7776682018-09-25 15:10:57 -0700319 FileOutputStream out = null;
320 try {
Hai Zhangb295ac42018-11-16 16:08:18 -0800321 out = atomicFile.startWrite();
Hai Zhangb7776682018-09-25 15:10:57 -0700322
323 XmlSerializer serializer = Xml.newSerializer();
324 serializer.setOutput(out, StandardCharsets.UTF_8.name());
325 serializer.setFeature(
326 "http://xmlpull.org/v1/doc/features.html#indent-output", true);
327 serializer.startDocument(null, true);
328
Hai Zhang458cedb2018-12-03 15:41:11 -0800329 serializeRoles(serializer, version, packagesHash, roles);
Hai Zhangb7776682018-09-25 15:10:57 -0700330
331 serializer.endDocument();
Hai Zhangb295ac42018-11-16 16:08:18 -0800332 atomicFile.finishWrite(out);
Hai Zhangb3e9b9b2018-12-05 18:32:40 -0800333 Slog.i(LOG_TAG, "Wrote roles.xml successfully");
Hai Zhangb295ac42018-11-16 16:08:18 -0800334 } catch (IllegalArgumentException | IllegalStateException | IOException e) {
335 Slog.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup", e);
336 if (out != null) {
337 atomicFile.failWrite(out);
338 }
Hai Zhangb7776682018-09-25 15:10:57 -0700339 } finally {
340 IoUtils.closeQuietly(out);
341 }
342 }
343
344 @WorkerThread
345 private void serializeRoles(@NonNull XmlSerializer serializer, int version,
Hai Zhang458cedb2018-12-03 15:41:11 -0800346 @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
Eugene Suslaabdefba2018-11-09 18:06:43 -0800347 throws IOException {
Hai Zhangb7776682018-09-25 15:10:57 -0700348 serializer.startTag(null, TAG_ROLES);
Hai Zhang458cedb2018-12-03 15:41:11 -0800349
Hai Zhangb7776682018-09-25 15:10:57 -0700350 serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
Hai Zhang458cedb2018-12-03 15:41:11 -0800351
352 if (packagesHash != null) {
353 serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
354 }
355
Hai Zhangb7776682018-09-25 15:10:57 -0700356 for (int i = 0, size = roles.size(); i < size; ++i) {
357 String roleName = roles.keyAt(i);
358 ArraySet<String> roleHolders = roles.valueAt(i);
Hai Zhang458cedb2018-12-03 15:41:11 -0800359
Hai Zhangb7776682018-09-25 15:10:57 -0700360 serializer.startTag(null, TAG_ROLE);
361 serializer.attribute(null, ATTRIBUTE_NAME, roleName);
362 serializeRoleHolders(serializer, roleHolders);
363 serializer.endTag(null, TAG_ROLE);
364 }
Hai Zhang458cedb2018-12-03 15:41:11 -0800365
Hai Zhangb7776682018-09-25 15:10:57 -0700366 serializer.endTag(null, TAG_ROLES);
367 }
368
369 @WorkerThread
370 private void serializeRoleHolders(@NonNull XmlSerializer serializer,
371 @NonNull ArraySet<String> roleHolders) throws IOException {
372 for (int i = 0, size = roleHolders.size(); i < size; ++i) {
373 String roleHolder = roleHolders.valueAt(i);
Hai Zhang458cedb2018-12-03 15:41:11 -0800374
Hai Zhangb7776682018-09-25 15:10:57 -0700375 serializer.startTag(null, TAG_HOLDER);
376 serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
377 serializer.endTag(null, TAG_HOLDER);
378 }
379 }
380
381 /**
382 * Read the state from file.
383 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800384 private void readFile() {
385 synchronized (mLock) {
386 File file = getFile(mUserId);
387 try (FileInputStream in = new AtomicFile(file).openRead()) {
388 XmlPullParser parser = Xml.newPullParser();
389 parser.setInput(in, null);
390 parseXmlLocked(parser);
391 Slog.i(LOG_TAG, "Read roles.xml successfully");
392 } catch (FileNotFoundException e) {
393 Slog.i(LOG_TAG, "roles.xml not found");
394 } catch (XmlPullParserException | IOException e) {
395 throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
396 }
Hai Zhangb7776682018-09-25 15:10:57 -0700397 }
398 }
399
400 private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
401 XmlPullParserException {
Hai Zhangb7776682018-09-25 15:10:57 -0700402 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800403 int depth;
404 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700405 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800406 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
407 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700408 continue;
409 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800410
Hai Zhangb7776682018-09-25 15:10:57 -0700411 if (parser.getName().equals(TAG_ROLES)) {
412 parseRolesLocked(parser);
413 return;
414 }
415 }
Hai Zhang458cedb2018-12-03 15:41:11 -0800416 Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
Hai Zhangb7776682018-09-25 15:10:57 -0700417 }
418
419 private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
420 XmlPullParserException {
421 mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
Hai Zhang33456fb2018-12-05 17:30:35 -0800422 mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
Hai Zhang458cedb2018-12-03 15:41:11 -0800423 mRoles.clear();
Hai Zhangb295ac42018-11-16 16:08:18 -0800424
Hai Zhangb7776682018-09-25 15:10:57 -0700425 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800426 int depth;
427 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700428 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800429 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
430 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700431 continue;
432 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800433
Hai Zhangb7776682018-09-25 15:10:57 -0700434 if (parser.getName().equals(TAG_ROLE)) {
435 String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
436 ArraySet<String> roleHolders = parseRoleHoldersLocked(parser);
437 mRoles.put(roleName, roleHolders);
438 }
439 }
440 }
441
442 @NonNull
443 private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
444 throws IOException, XmlPullParserException {
445 ArraySet<String> roleHolders = new ArraySet<>();
Hai Zhangb295ac42018-11-16 16:08:18 -0800446
Hai Zhangb7776682018-09-25 15:10:57 -0700447 int type;
Hai Zhangb295ac42018-11-16 16:08:18 -0800448 int depth;
449 int innerDepth = parser.getDepth() + 1;
Hai Zhangb7776682018-09-25 15:10:57 -0700450 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Hai Zhangb295ac42018-11-16 16:08:18 -0800451 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
452 if (depth > innerDepth || type != XmlPullParser.START_TAG) {
Hai Zhangb7776682018-09-25 15:10:57 -0700453 continue;
454 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800455
Hai Zhangb7776682018-09-25 15:10:57 -0700456 if (parser.getName().equals(TAG_HOLDER)) {
457 String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
458 roleHolders.add(roleHolder);
459 }
460 }
Hai Zhangb295ac42018-11-16 16:08:18 -0800461
Hai Zhangb7776682018-09-25 15:10:57 -0700462 return roleHolders;
463 }
464
465 /**
Hai Zhang33456fb2018-12-05 17:30:35 -0800466 * Dump this user state.
467 *
468 * @param dumpOutputStream the output stream to dump to
469 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800470 public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
471 long fieldId) {
472 int version;
473 String packagesHash;
474 ArrayMap<String, ArraySet<String>> roles;
475 synchronized (mLock) {
476 throwIfDestroyedLocked();
477
478 version = mVersion;
479 packagesHash = mPackagesHash;
480 roles = snapshotRolesLocked();
481 }
Hai Zhang33456fb2018-12-05 17:30:35 -0800482
483 long fieldToken = dumpOutputStream.start(fieldName, fieldId);
484 dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
Hai Zhangcdc85c52018-12-06 13:56:55 -0800485 dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
486 dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);
Hai Zhang33456fb2018-12-05 17:30:35 -0800487
Hai Zhangcdc85c52018-12-06 13:56:55 -0800488 int rolesSize = roles.size();
Hai Zhang33456fb2018-12-05 17:30:35 -0800489 for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
Hai Zhangcdc85c52018-12-06 13:56:55 -0800490 String roleName = roles.keyAt(rolesIndex);
491 ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
Hai Zhang33456fb2018-12-05 17:30:35 -0800492
493 long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
494 dumpOutputStream.write("name", RoleProto.NAME, roleName);
495
496 int roleHoldersSize = roleHolders.size();
497 for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
498 String roleHolder = roleHolders.valueAt(roleHoldersIndex);
499
500 dumpOutputStream.write("holders", RoleProto.HOLDERS, roleHolder);
501 }
502
503 dumpOutputStream.end(rolesToken);
504 }
505
506 dumpOutputStream.end(fieldToken);
507 }
508
Hai Zhangcdc85c52018-12-06 13:56:55 -0800509 @GuardedBy("mLock")
510 private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
511 ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
512 for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
513 String roleName = mRoles.keyAt(i);
514 ArraySet<String> roleHolders = mRoles.valueAt(i);
515
516 roleHolders = new ArraySet<>(roleHolders);
517 roles.put(roleName, roleHolders);
518 }
519 return roles;
520 }
521
Hai Zhang33456fb2018-12-05 17:30:35 -0800522 /**
Hai Zhangb7776682018-09-25 15:10:57 -0700523 * Destroy this state and delete the corresponding file. Any pending writes to the file will be
524 * cancelled and any future interaction with this state will throw an exception.
525 */
Hai Zhangcdc85c52018-12-06 13:56:55 -0800526 public void destroy() {
527 synchronized (mLock) {
528 throwIfDestroyedLocked();
529 mWriteHandler.removeCallbacksAndMessages(null);
530 getFile(mUserId).delete();
531 mDestroyed = true;
532 }
Hai Zhangb7776682018-09-25 15:10:57 -0700533 }
534
Hai Zhangcdc85c52018-12-06 13:56:55 -0800535 @GuardedBy("mLock")
Hai Zhangb7776682018-09-25 15:10:57 -0700536 private void throwIfDestroyedLocked() {
537 if (mDestroyed) {
538 throw new IllegalStateException("This RoleUserState has already been destroyed");
539 }
540 }
541
542 private static @NonNull File getFile(@UserIdInt int userId) {
543 return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
544 }
545}