blob: 3ef93706dd72f42b82ff9b29875a5a7ece5e1240 [file] [log] [blame]
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001/*
2 * Copyright (C) 2011 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.pm;
18
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -080019import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070020
Amith Yamasanidb6a14c2012-10-17 21:16:52 -070021import android.app.Activity;
Amith Yamasani2a003292012-08-14 18:25:45 -070022import android.app.ActivityManager;
Dianne Hackborn80a4af22012-08-27 19:18:31 -070023import android.app.ActivityManagerNative;
24import android.app.IStopUserCallback;
Amith Yamasanidb6a14c2012-10-17 21:16:52 -070025import android.content.BroadcastReceiver;
Amith Yamasani258848d2012-08-10 17:06:33 -070026import android.content.Context;
27import android.content.Intent;
Amith Yamasanidf2e92a2013-03-01 17:04:38 -080028import android.content.RestrictionEntry;
Amith Yamasani0b285492011-04-14 17:35:23 -070029import android.content.pm.PackageManager;
Amith Yamasanidf2e92a2013-03-01 17:04:38 -080030import android.content.pm.PackageManager.NameNotFoundException;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070031import android.content.pm.UserInfo;
Amith Yamasanie928d7d2012-09-17 21:46:51 -070032import android.graphics.Bitmap;
33import android.graphics.BitmapFactory;
Amith Yamasani258848d2012-08-10 17:06:33 -070034import android.os.Binder;
Amith Yamasanie4cf7342012-12-17 11:12:09 -080035import android.os.Bundle;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070036import android.os.Environment;
37import android.os.FileUtils;
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -080038import android.os.Handler;
Amith Yamasani258848d2012-08-10 17:06:33 -070039import android.os.IUserManager;
Amith Yamasani258848d2012-08-10 17:06:33 -070040import android.os.Process;
Dianne Hackborn80a4af22012-08-27 19:18:31 -070041import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070042import android.os.UserHandle;
Jeff Sharkey27bd34d2012-09-16 12:49:00 -070043import android.os.UserManager;
Amith Yamasani2a003292012-08-14 18:25:45 -070044import android.util.AtomicFile;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070045import android.util.Slog;
46import android.util.SparseArray;
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -080047import android.util.SparseBooleanArray;
Amith Yamasani920ace02012-09-20 22:15:37 -070048import android.util.TimeUtils;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070049import android.util.Xml;
50
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -080051import com.android.internal.util.ArrayUtils;
52import com.android.internal.util.FastXmlSerializer;
53
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
56import org.xmlpull.v1.XmlSerializer;
57
Amith Yamasani4b2e9342011-03-31 12:38:53 -070058import java.io.BufferedOutputStream;
59import java.io.File;
Amith Yamasani920ace02012-09-20 22:15:37 -070060import java.io.FileDescriptor;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070061import java.io.FileInputStream;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070062import java.io.FileNotFoundException;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070063import java.io.FileOutputStream;
64import java.io.IOException;
Amith Yamasani920ace02012-09-20 22:15:37 -070065import java.io.PrintWriter;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070066import java.util.ArrayList;
67import java.util.List;
68
Amith Yamasani258848d2012-08-10 17:06:33 -070069public class UserManagerService extends IUserManager.Stub {
Amith Yamasanib8151ec2012-04-18 18:02:48 -070070
Amith Yamasani2a003292012-08-14 18:25:45 -070071 private static final String LOG_TAG = "UserManagerService";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070072
Amith Yamasani16389312012-10-17 21:20:14 -070073 private static final boolean DBG = false;
74
Amith Yamasani4b2e9342011-03-31 12:38:53 -070075 private static final String TAG_NAME = "name";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070076 private static final String ATTR_FLAGS = "flags";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070077 private static final String ATTR_ICON_PATH = "icon";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070078 private static final String ATTR_ID = "id";
Amith Yamasani920ace02012-09-20 22:15:37 -070079 private static final String ATTR_CREATION_TIME = "created";
80 private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
Amith Yamasani2a003292012-08-14 18:25:45 -070081 private static final String ATTR_SERIAL_NO = "serialNumber";
82 private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -070083 private static final String ATTR_PARTIAL = "partial";
Amith Yamasani6f34b412012-10-22 18:19:27 -070084 private static final String ATTR_USER_VERSION = "version";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070085 private static final String TAG_USERS = "users";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070086 private static final String TAG_USER = "user";
Amith Yamasanie4cf7342012-12-17 11:12:09 -080087 private static final String TAG_RESTRICTIONS = "restrictions";
Amith Yamasanidf2e92a2013-03-01 17:04:38 -080088 private static final String TAG_ENTRY = "entry";
89 private static final String TAG_VALUE = "value";
90 private static final String ATTR_KEY = "key";
91 private static final String ATTR_MULTIPLE = "m";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070092
Amith Yamasani0b285492011-04-14 17:35:23 -070093 private static final String USER_INFO_DIR = "system" + File.separator + "users";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070094 private static final String USER_LIST_FILENAME = "userlist.xml";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070095 private static final String USER_PHOTO_FILENAME = "photo.png";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070096
Amith Yamasanidf2e92a2013-03-01 17:04:38 -080097 private static final String RESTRICTIONS_FILE_PREFIX = "res_";
98
Amith Yamasani634cf312012-10-04 17:34:21 -070099 private static final int MIN_USER_ID = 10;
100
Amith Yamasanibc9625052012-11-15 14:39:18 -0800101 private static final int USER_VERSION = 2;
Amith Yamasani6f34b412012-10-22 18:19:27 -0700102
Amith Yamasani920ace02012-09-20 22:15:37 -0700103 private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
104
Dianne Hackborn4428e172012-08-24 17:43:05 -0700105 private final Context mContext;
106 private final PackageManagerService mPm;
107 private final Object mInstallLock;
108 private final Object mPackagesLock;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700109
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800110 private final Handler mHandler;
111
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700112 private final File mUsersDir;
113 private final File mUserListFile;
Dianne Hackborn4428e172012-08-24 17:43:05 -0700114 private final File mBaseUserPath;
115
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800116 private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800117 private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800118
119 /**
120 * Set of user IDs being actively removed. Removed IDs linger in this set
121 * for several seconds to work around a VFS caching issue.
122 */
123 // @GuardedBy("mPackagesLock")
124 private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
Dianne Hackborn4428e172012-08-24 17:43:05 -0700125
Amith Yamasani0b285492011-04-14 17:35:23 -0700126 private int[] mUserIds;
Amith Yamasani258848d2012-08-10 17:06:33 -0700127 private boolean mGuestEnabled;
Amith Yamasani2a003292012-08-14 18:25:45 -0700128 private int mNextSerialNumber;
Amith Yamasani6f34b412012-10-22 18:19:27 -0700129 private int mUserVersion = 0;
Amith Yamasani0b285492011-04-14 17:35:23 -0700130
Amith Yamasani258848d2012-08-10 17:06:33 -0700131 private static UserManagerService sInstance;
Amith Yamasani258848d2012-08-10 17:06:33 -0700132
Dianne Hackborn4428e172012-08-24 17:43:05 -0700133 public static UserManagerService getInstance() {
134 synchronized (UserManagerService.class) {
135 return sInstance;
Amith Yamasani258848d2012-08-10 17:06:33 -0700136 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700137 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700138
139 /**
140 * Available for testing purposes.
141 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700142 UserManagerService(File dataDir, File baseUserPath) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700143 this(null, null, new Object(), new Object(), dataDir, baseUserPath);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700144 }
145
Dianne Hackborn4428e172012-08-24 17:43:05 -0700146 /**
147 * Called by package manager to create the service. This is closely
148 * associated with the package manager, and the given lock is the
149 * package manager's own lock.
150 */
151 UserManagerService(Context context, PackageManagerService pm,
152 Object installLock, Object packagesLock) {
153 this(context, pm, installLock, packagesLock,
154 Environment.getDataDirectory(),
155 new File(Environment.getDataDirectory(), "user"));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700156 }
157
Dianne Hackborn4428e172012-08-24 17:43:05 -0700158 /**
159 * Available for testing purposes.
160 */
161 private UserManagerService(Context context, PackageManagerService pm,
162 Object installLock, Object packagesLock,
163 File dataDir, File baseUserPath) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700164 mContext = context;
165 mPm = pm;
166 mInstallLock = installLock;
167 mPackagesLock = packagesLock;
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800168 mHandler = new Handler();
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700169 synchronized (mInstallLock) {
170 synchronized (mPackagesLock) {
171 mUsersDir = new File(dataDir, USER_INFO_DIR);
172 mUsersDir.mkdirs();
173 // Make zeroth user directory, for services to migrate their files to that location
174 File userZeroDir = new File(mUsersDir, "0");
175 userZeroDir.mkdirs();
176 mBaseUserPath = baseUserPath;
177 FileUtils.setPermissions(mUsersDir.toString(),
178 FileUtils.S_IRWXU|FileUtils.S_IRWXG
179 |FileUtils.S_IROTH|FileUtils.S_IXOTH,
180 -1, -1);
181 mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
182 readUserListLocked();
Amith Yamasani756901d2012-10-12 12:30:07 -0700183 // Prune out any partially created/partially removed users.
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700184 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
185 for (int i = 0; i < mUsers.size(); i++) {
186 UserInfo ui = mUsers.valueAt(i);
187 if (ui.partial && i != 0) {
188 partials.add(ui);
189 }
190 }
191 for (int i = 0; i < partials.size(); i++) {
192 UserInfo ui = partials.get(i);
193 Slog.w(LOG_TAG, "Removing partially created user #" + i
194 + " (name=" + ui.name + ")");
195 removeUserStateLocked(ui.id);
196 }
197 sInstance = this;
198 }
Dianne Hackborn4428e172012-08-24 17:43:05 -0700199 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700200 }
201
202 @Override
Amith Yamasani920ace02012-09-20 22:15:37 -0700203 public List<UserInfo> getUsers(boolean excludeDying) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700204 checkManageUsersPermission("query users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700205 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700206 ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
207 for (int i = 0; i < mUsers.size(); i++) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700208 UserInfo ui = mUsers.valueAt(i);
209 if (ui.partial) {
210 continue;
211 }
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800212 if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700213 users.add(ui);
Amith Yamasani920ace02012-09-20 22:15:37 -0700214 }
Amith Yamasani13593602012-03-22 16:16:17 -0700215 }
216 return users;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700217 }
Amith Yamasani13593602012-03-22 16:16:17 -0700218 }
219
Amith Yamasani258848d2012-08-10 17:06:33 -0700220 @Override
221 public UserInfo getUserInfo(int userId) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700222 checkManageUsersPermission("query user");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700223 synchronized (mPackagesLock) {
Amith Yamasani195263742012-08-21 15:40:12 -0700224 return getUserInfoLocked(userId);
Amith Yamasani13593602012-03-22 16:16:17 -0700225 }
226 }
227
Amith Yamasani71e6c692013-03-24 17:39:28 -0700228 @Override
229 public boolean isRestricted() {
230 synchronized (mPackagesLock) {
231 return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
232 }
233 }
234
Amith Yamasani195263742012-08-21 15:40:12 -0700235 /*
236 * Should be locked on mUsers before calling this.
237 */
238 private UserInfo getUserInfoLocked(int userId) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700239 UserInfo ui = mUsers.get(userId);
Amith Yamasani16389312012-10-17 21:20:14 -0700240 // If it is partial and not in the process of being removed, return as unknown user.
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800241 if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700242 Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
243 return null;
244 }
245 return ui;
Amith Yamasani195263742012-08-21 15:40:12 -0700246 }
247
Amith Yamasani13593602012-03-22 16:16:17 -0700248 public boolean exists(int userId) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700249 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700250 return ArrayUtils.contains(mUserIds, userId);
251 }
252 }
253
Amith Yamasani258848d2012-08-10 17:06:33 -0700254 @Override
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700255 public void setUserName(int userId, String name) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700256 checkManageUsersPermission("rename users");
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700257 boolean changed = false;
Dianne Hackborn4428e172012-08-24 17:43:05 -0700258 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700259 UserInfo info = mUsers.get(userId);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700260 if (info == null || info.partial) {
261 Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
262 return;
263 }
Amith Yamasani13593602012-03-22 16:16:17 -0700264 if (name != null && !name.equals(info.name)) {
265 info.name = name;
266 writeUserLocked(info);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700267 changed = true;
Amith Yamasani13593602012-03-22 16:16:17 -0700268 }
269 }
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700270 if (changed) {
271 sendUserInfoChangedBroadcast(userId);
272 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700273 }
274
Amith Yamasani258848d2012-08-10 17:06:33 -0700275 @Override
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700276 public void setUserIcon(int userId, Bitmap bitmap) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700277 checkManageUsersPermission("update users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700278 synchronized (mPackagesLock) {
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700279 UserInfo info = mUsers.get(userId);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700280 if (info == null || info.partial) {
281 Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
282 return;
283 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700284 writeBitmapLocked(info, bitmap);
285 writeUserLocked(info);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700286 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700287 sendUserInfoChangedBroadcast(userId);
288 }
289
290 private void sendUserInfoChangedBroadcast(int userId) {
291 Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
292 changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
293 changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
294 mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700295 }
296
Amith Yamasani258848d2012-08-10 17:06:33 -0700297 @Override
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700298 public Bitmap getUserIcon(int userId) {
Amith Yamasani3b49f072012-09-17 10:21:43 -0700299 checkManageUsersPermission("read users");
300 synchronized (mPackagesLock) {
301 UserInfo info = mUsers.get(userId);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700302 if (info == null || info.partial) {
303 Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
304 return null;
305 }
306 if (info.iconPath == null) {
307 return null;
308 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700309 return BitmapFactory.decodeFile(info.iconPath);
Amith Yamasani3b49f072012-09-17 10:21:43 -0700310 }
311 }
312
313 @Override
Amith Yamasani258848d2012-08-10 17:06:33 -0700314 public void setGuestEnabled(boolean enable) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700315 checkManageUsersPermission("enable guest users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700316 synchronized (mPackagesLock) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700317 if (mGuestEnabled != enable) {
318 mGuestEnabled = enable;
319 // Erase any guest user that currently exists
320 for (int i = 0; i < mUsers.size(); i++) {
321 UserInfo user = mUsers.valueAt(i);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700322 if (!user.partial && user.isGuest()) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700323 if (!enable) {
324 removeUser(user.id);
325 }
326 return;
327 }
328 }
329 // No guest was found
330 if (enable) {
331 createUser("Guest", UserInfo.FLAG_GUEST);
332 }
333 }
334 }
335 }
336
337 @Override
338 public boolean isGuestEnabled() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700339 synchronized (mPackagesLock) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700340 return mGuestEnabled;
341 }
342 }
343
344 @Override
345 public void wipeUser(int userHandle) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700346 checkManageUsersPermission("wipe user");
Amith Yamasani258848d2012-08-10 17:06:33 -0700347 // TODO:
348 }
349
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700350 public void makeInitialized(int userId) {
351 checkManageUsersPermission("makeInitialized");
352 synchronized (mPackagesLock) {
353 UserInfo info = mUsers.get(userId);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700354 if (info == null || info.partial) {
355 Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
356 }
357 if ((info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700358 info.flags |= UserInfo.FLAG_INITIALIZED;
359 writeUserLocked(info);
360 }
361 }
362 }
363
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800364 @Override
365 public Bundle getUserRestrictions(int userId) {
366 // checkManageUsersPermission("getUserRestrictions");
367
368 synchronized (mPackagesLock) {
369 Bundle restrictions = mUserRestrictions.get(userId);
370 return restrictions != null ? restrictions : Bundle.EMPTY;
371 }
372 }
373
374 @Override
375 public void setUserRestrictions(Bundle restrictions, int userId) {
376 checkManageUsersPermission("setUserRestrictions");
377
378 synchronized (mPackagesLock) {
379 mUserRestrictions.get(userId).putAll(restrictions);
380 writeUserLocked(mUsers.get(userId));
381 }
382 }
383
Amith Yamasani258848d2012-08-10 17:06:33 -0700384 /**
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700385 * Check if we've hit the limit of how many users can be created.
386 */
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700387 private boolean isUserLimitReachedLocked() {
388 int nUsers = mUsers.size();
Jeff Sharkey27bd34d2012-09-16 12:49:00 -0700389 return nUsers >= UserManager.getMaxSupportedUsers();
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700390 }
391
392 /**
Amith Yamasani195263742012-08-21 15:40:12 -0700393 * Enforces that only the system UID or root's UID or apps that have the
394 * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
395 * permission can make certain calls to the UserManager.
Amith Yamasani258848d2012-08-10 17:06:33 -0700396 *
397 * @param message used as message if SecurityException is thrown
398 * @throws SecurityException if the caller is not system or root
399 */
Amith Yamasani2a003292012-08-14 18:25:45 -0700400 private static final void checkManageUsersPermission(String message) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700401 final int uid = Binder.getCallingUid();
Amith Yamasani2a003292012-08-14 18:25:45 -0700402 if (uid != Process.SYSTEM_UID && uid != 0
403 && ActivityManager.checkComponentPermission(
404 android.Manifest.permission.MANAGE_USERS,
405 uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
406 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
Amith Yamasani258848d2012-08-10 17:06:33 -0700407 }
408 }
409
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700410 private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700411 try {
412 File dir = new File(mUsersDir, Integer.toString(info.id));
413 File file = new File(dir, USER_PHOTO_FILENAME);
414 if (!dir.exists()) {
415 dir.mkdir();
416 FileUtils.setPermissions(
417 dir.getPath(),
418 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
419 -1, -1);
420 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700421 FileOutputStream os;
422 if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
Amith Yamasani3b49f072012-09-17 10:21:43 -0700423 info.iconPath = file.getAbsolutePath();
424 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700425 try {
426 os.close();
427 } catch (IOException ioe) {
428 // What the ... !
429 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700430 } catch (FileNotFoundException e) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700431 Slog.w(LOG_TAG, "Error setting photo for user ", e);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700432 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700433 }
434
Amith Yamasani0b285492011-04-14 17:35:23 -0700435 /**
436 * Returns an array of user ids. This array is cached here for quick access, so do not modify or
437 * cache it elsewhere.
438 * @return the array of user ids.
439 */
Dianne Hackborn1676c852012-09-10 14:52:30 -0700440 public int[] getUserIds() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700441 synchronized (mPackagesLock) {
Dianne Hackborn7767eac2012-08-23 18:25:40 -0700442 return mUserIds;
443 }
Amith Yamasani0b285492011-04-14 17:35:23 -0700444 }
445
Dianne Hackborn4428e172012-08-24 17:43:05 -0700446 int[] getUserIdsLPr() {
447 return mUserIds;
448 }
449
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700450 private void readUserList() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700451 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700452 readUserListLocked();
453 }
454 }
455
456 private void readUserListLocked() {
Amith Yamasani258848d2012-08-10 17:06:33 -0700457 mGuestEnabled = false;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700458 if (!mUserListFile.exists()) {
Amith Yamasani13593602012-03-22 16:16:17 -0700459 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700460 return;
461 }
462 FileInputStream fis = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700463 AtomicFile userListFile = new AtomicFile(mUserListFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700464 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700465 fis = userListFile.openRead();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700466 XmlPullParser parser = Xml.newPullParser();
467 parser.setInput(fis, null);
468 int type;
469 while ((type = parser.next()) != XmlPullParser.START_TAG
470 && type != XmlPullParser.END_DOCUMENT) {
471 ;
472 }
473
474 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700475 Slog.e(LOG_TAG, "Unable to read user list");
Amith Yamasani13593602012-03-22 16:16:17 -0700476 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700477 return;
478 }
479
Amith Yamasani2a003292012-08-14 18:25:45 -0700480 mNextSerialNumber = -1;
481 if (parser.getName().equals(TAG_USERS)) {
482 String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
483 if (lastSerialNumber != null) {
484 mNextSerialNumber = Integer.parseInt(lastSerialNumber);
485 }
Amith Yamasani6f34b412012-10-22 18:19:27 -0700486 String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
487 if (versionNumber != null) {
488 mUserVersion = Integer.parseInt(versionNumber);
489 }
Amith Yamasani2a003292012-08-14 18:25:45 -0700490 }
491
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700492 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
493 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
494 String id = parser.getAttributeValue(null, ATTR_ID);
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800495 UserInfo user = readUserLocked(Integer.parseInt(id));
Amith Yamasani6f34b412012-10-22 18:19:27 -0700496
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700497 if (user != null) {
498 mUsers.put(user.id, user);
Amith Yamasani2a003292012-08-14 18:25:45 -0700499 if (user.isGuest()) {
500 mGuestEnabled = true;
501 }
502 if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
503 mNextSerialNumber = user.id + 1;
504 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700505 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700506 }
507 }
Amith Yamasani13593602012-03-22 16:16:17 -0700508 updateUserIdsLocked();
Amith Yamasani6f34b412012-10-22 18:19:27 -0700509 upgradeIfNecessary();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700510 } catch (IOException ioe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700511 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700512 } catch (XmlPullParserException pe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700513 fallbackToSingleUserLocked();
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800514 } finally {
515 if (fis != null) {
516 try {
517 fis.close();
518 } catch (IOException e) {
519 }
520 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700521 }
522 }
523
Amith Yamasani6f34b412012-10-22 18:19:27 -0700524 /**
Amith Yamasanibc9625052012-11-15 14:39:18 -0800525 * Upgrade steps between versions, either for fixing bugs or changing the data format.
Amith Yamasani6f34b412012-10-22 18:19:27 -0700526 */
527 private void upgradeIfNecessary() {
528 int userVersion = mUserVersion;
529 if (userVersion < 1) {
530 // Assign a proper name for the owner, if not initialized correctly before
531 UserInfo user = mUsers.get(UserHandle.USER_OWNER);
532 if ("Primary".equals(user.name)) {
533 user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
534 writeUserLocked(user);
535 }
536 userVersion = 1;
537 }
538
Amith Yamasanibc9625052012-11-15 14:39:18 -0800539 if (userVersion < 2) {
540 // Owner should be marked as initialized
541 UserInfo user = mUsers.get(UserHandle.USER_OWNER);
542 if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
543 user.flags |= UserInfo.FLAG_INITIALIZED;
544 writeUserLocked(user);
545 }
546 userVersion = 2;
547 }
548
Amith Yamasani6f34b412012-10-22 18:19:27 -0700549 if (userVersion < USER_VERSION) {
550 Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
551 + USER_VERSION);
552 } else {
553 mUserVersion = userVersion;
554 writeUserListLocked();
555 }
556 }
557
Amith Yamasani13593602012-03-22 16:16:17 -0700558 private void fallbackToSingleUserLocked() {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700559 // Create the primary user
Amith Yamasani67df64b2012-12-14 12:09:36 -0800560 UserInfo primary = new UserInfo(UserHandle.USER_OWNER,
Amith Yamasani6f34b412012-10-22 18:19:27 -0700561 mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
Amith Yamasani756901d2012-10-12 12:30:07 -0700562 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700563 mUsers.put(0, primary);
Amith Yamasani634cf312012-10-04 17:34:21 -0700564 mNextSerialNumber = MIN_USER_ID;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800565
Geoffrey Borggaarde45e45e32013-01-24 10:03:20 -0500566 Bundle restrictions = new Bundle();
Amith Yamasani67df64b2012-12-14 12:09:36 -0800567 mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
568
Amith Yamasani13593602012-03-22 16:16:17 -0700569 updateUserIdsLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700570
Amith Yamasani13593602012-03-22 16:16:17 -0700571 writeUserListLocked();
572 writeUserLocked(primary);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700573 }
574
575 /*
576 * Writes the user file in this format:
577 *
578 * <user flags="20039023" id="0">
579 * <name>Primary</name>
580 * </user>
581 */
Amith Yamasani13593602012-03-22 16:16:17 -0700582 private void writeUserLocked(UserInfo userInfo) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700583 FileOutputStream fos = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700584 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700585 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700586 fos = userFile.startWrite();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700587 final BufferedOutputStream bos = new BufferedOutputStream(fos);
588
589 // XmlSerializer serializer = XmlUtils.serializerInstance();
590 final XmlSerializer serializer = new FastXmlSerializer();
591 serializer.setOutput(bos, "utf-8");
592 serializer.startDocument(null, true);
593 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
594
595 serializer.startTag(null, TAG_USER);
596 serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
Amith Yamasani2a003292012-08-14 18:25:45 -0700597 serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700598 serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
Amith Yamasani920ace02012-09-20 22:15:37 -0700599 serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
600 serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
601 Long.toString(userInfo.lastLoggedInTime));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700602 if (userInfo.iconPath != null) {
603 serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
604 }
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700605 if (userInfo.partial) {
606 serializer.attribute(null, ATTR_PARTIAL, "true");
607 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700608
609 serializer.startTag(null, TAG_NAME);
610 serializer.text(userInfo.name);
611 serializer.endTag(null, TAG_NAME);
612
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800613 Bundle restrictions = mUserRestrictions.get(userInfo.id);
614 if (restrictions != null) {
615 serializer.startTag(null, TAG_RESTRICTIONS);
Amith Yamasani71e6c692013-03-24 17:39:28 -0700616 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
617 writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
618 writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
619 writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
620 writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
Maggie Benthalla12fccf2013-03-14 18:02:12 -0400621 writeBoolean(serializer, restrictions,
622 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
623 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
624 writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800625 serializer.endTag(null, TAG_RESTRICTIONS);
626 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700627 serializer.endTag(null, TAG_USER);
628
629 serializer.endDocument();
Amith Yamasani2a003292012-08-14 18:25:45 -0700630 userFile.finishWrite(fos);
631 } catch (Exception ioe) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700632 Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
Amith Yamasani2a003292012-08-14 18:25:45 -0700633 userFile.failWrite(fos);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700634 }
635 }
636
637 /*
638 * Writes the user list file in this format:
639 *
Amith Yamasani2a003292012-08-14 18:25:45 -0700640 * <users nextSerialNumber="3">
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700641 * <user id="0"></user>
642 * <user id="2"></user>
643 * </users>
644 */
Amith Yamasani13593602012-03-22 16:16:17 -0700645 private void writeUserListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -0700646 FileOutputStream fos = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700647 AtomicFile userListFile = new AtomicFile(mUserListFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700648 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700649 fos = userListFile.startWrite();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700650 final BufferedOutputStream bos = new BufferedOutputStream(fos);
651
652 // XmlSerializer serializer = XmlUtils.serializerInstance();
653 final XmlSerializer serializer = new FastXmlSerializer();
654 serializer.setOutput(bos, "utf-8");
655 serializer.startDocument(null, true);
656 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
657
658 serializer.startTag(null, TAG_USERS);
Amith Yamasani2a003292012-08-14 18:25:45 -0700659 serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
Amith Yamasani6f34b412012-10-22 18:19:27 -0700660 serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700661
662 for (int i = 0; i < mUsers.size(); i++) {
663 UserInfo user = mUsers.valueAt(i);
664 serializer.startTag(null, TAG_USER);
665 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
666 serializer.endTag(null, TAG_USER);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700667 }
668
669 serializer.endTag(null, TAG_USERS);
670
671 serializer.endDocument();
Amith Yamasani2a003292012-08-14 18:25:45 -0700672 userListFile.finishWrite(fos);
673 } catch (Exception e) {
674 userListFile.failWrite(fos);
Amith Yamasani0b285492011-04-14 17:35:23 -0700675 Slog.e(LOG_TAG, "Error writing user list");
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700676 }
677 }
678
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800679 private UserInfo readUserLocked(int id) {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700680 int flags = 0;
Amith Yamasani2a003292012-08-14 18:25:45 -0700681 int serialNumber = id;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700682 String name = null;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700683 String iconPath = null;
Amith Yamasani920ace02012-09-20 22:15:37 -0700684 long creationTime = 0L;
685 long lastLoggedInTime = 0L;
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700686 boolean partial = false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800687 Bundle restrictions = new Bundle();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700688
689 FileInputStream fis = null;
690 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700691 AtomicFile userFile =
692 new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
693 fis = userFile.openRead();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700694 XmlPullParser parser = Xml.newPullParser();
695 parser.setInput(fis, null);
696 int type;
697 while ((type = parser.next()) != XmlPullParser.START_TAG
698 && type != XmlPullParser.END_DOCUMENT) {
699 ;
700 }
701
702 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700703 Slog.e(LOG_TAG, "Unable to read user " + id);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700704 return null;
705 }
706
707 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
Amith Yamasani920ace02012-09-20 22:15:37 -0700708 int storedId = readIntAttribute(parser, ATTR_ID, -1);
709 if (storedId != id) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700710 Slog.e(LOG_TAG, "User id does not match the file name");
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700711 return null;
712 }
Amith Yamasani920ace02012-09-20 22:15:37 -0700713 serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
714 flags = readIntAttribute(parser, ATTR_FLAGS, 0);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700715 iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
Amith Yamasani920ace02012-09-20 22:15:37 -0700716 creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
717 lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700718 String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
719 if ("true".equals(valueString)) {
720 partial = true;
721 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700722
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800723 int outerDepth = parser.getDepth();
724 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
725 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
726 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
727 continue;
728 }
729 String tag = parser.getName();
730 if (TAG_NAME.equals(tag)) {
731 type = parser.next();
732 if (type == XmlPullParser.TEXT) {
733 name = parser.getText();
734 }
735 } else if (TAG_RESTRICTIONS.equals(tag)) {
Amith Yamasani71e6c692013-03-24 17:39:28 -0700736 readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
737 readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
738 readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
739 readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
740 readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
Maggie Benthalla12fccf2013-03-14 18:02:12 -0400741 readBoolean(parser, restrictions,
742 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
743 readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
744 readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700745 }
746 }
747 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700748
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700749 UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
Amith Yamasani2a003292012-08-14 18:25:45 -0700750 userInfo.serialNumber = serialNumber;
Amith Yamasani920ace02012-09-20 22:15:37 -0700751 userInfo.creationTime = creationTime;
752 userInfo.lastLoggedInTime = lastLoggedInTime;
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700753 userInfo.partial = partial;
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800754 mUserRestrictions.append(id, restrictions);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700755 return userInfo;
756
757 } catch (IOException ioe) {
758 } catch (XmlPullParserException pe) {
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800759 } finally {
760 if (fis != null) {
761 try {
762 fis.close();
763 } catch (IOException e) {
764 }
765 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700766 }
767 return null;
768 }
769
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800770 private void readBoolean(XmlPullParser parser, Bundle restrictions,
771 String restrictionKey) {
772 String value = parser.getAttributeValue(null, restrictionKey);
Amith Yamasani71e6c692013-03-24 17:39:28 -0700773 if (value != null) {
774 restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
775 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -0800776 }
777
778 private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
779 throws IOException {
780 if (restrictions.containsKey(restrictionKey)) {
781 xml.attribute(null, restrictionKey,
782 Boolean.toString(restrictions.getBoolean(restrictionKey)));
783 }
784 }
785
Amith Yamasani920ace02012-09-20 22:15:37 -0700786 private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
787 String valueString = parser.getAttributeValue(null, attr);
788 if (valueString == null) return defaultValue;
789 try {
790 return Integer.parseInt(valueString);
791 } catch (NumberFormatException nfe) {
792 return defaultValue;
793 }
794 }
795
796 private long readLongAttribute(XmlPullParser parser, String attr, long defaultValue) {
797 String valueString = parser.getAttributeValue(null, attr);
798 if (valueString == null) return defaultValue;
799 try {
800 return Long.parseLong(valueString);
801 } catch (NumberFormatException nfe) {
802 return defaultValue;
803 }
804 }
805
Amith Yamasani258848d2012-08-10 17:06:33 -0700806 @Override
Amith Yamasani13593602012-03-22 16:16:17 -0700807 public UserInfo createUser(String name, int flags) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700808 checkManageUsersPermission("Only the system can create users");
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700809
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700810 final long ident = Binder.clearCallingIdentity();
811 final UserInfo userInfo;
812 try {
813 synchronized (mInstallLock) {
814 synchronized (mPackagesLock) {
815 if (isUserLimitReachedLocked()) return null;
816 int userId = getNextAvailableIdLocked();
817 userInfo = new UserInfo(userId, name, null, flags);
818 File userPath = new File(mBaseUserPath, Integer.toString(userId));
819 userInfo.serialNumber = mNextSerialNumber++;
Amith Yamasani920ace02012-09-20 22:15:37 -0700820 long now = System.currentTimeMillis();
821 userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700822 userInfo.partial = true;
Amith Yamasani16389312012-10-17 21:20:14 -0700823 Environment.getUserSystemDirectory(userInfo.id).mkdirs();
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700824 mUsers.put(userId, userInfo);
825 writeUserListLocked();
826 writeUserLocked(userInfo);
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700827 mPm.createNewUserLILPw(userId, userPath);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700828 userInfo.partial = false;
829 writeUserLocked(userInfo);
830 updateUserIdsLocked();
Geoffrey Borggaarde45e45e32013-01-24 10:03:20 -0500831 Bundle restrictions = new Bundle();
Geoffrey Borggaarde45e45e32013-01-24 10:03:20 -0500832 mUserRestrictions.append(userId, restrictions);
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700833 }
Dianne Hackborn4428e172012-08-24 17:43:05 -0700834 }
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700835 if (userInfo != null) {
836 Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
837 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
838 mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
839 android.Manifest.permission.MANAGE_USERS);
840 }
841 } finally {
842 Binder.restoreCallingIdentity(ident);
Amith Yamasani258848d2012-08-10 17:06:33 -0700843 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700844 return userInfo;
845 }
846
Amith Yamasani0b285492011-04-14 17:35:23 -0700847 /**
848 * Removes a user and all data directories created for that user. This method should be called
849 * after the user's processes have been terminated.
850 * @param id the user's id
851 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700852 public boolean removeUser(int userHandle) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700853 checkManageUsersPermission("Only the system can remove users");
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700854 final UserInfo user;
855 synchronized (mPackagesLock) {
856 user = mUsers.get(userHandle);
857 if (userHandle == 0 || user == null) {
858 return false;
859 }
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800860 mRemovingUserIds.put(userHandle, true);
Amith Yamasani756901d2012-10-12 12:30:07 -0700861 // Set this to a partially created user, so that the user will be purged
862 // on next startup, in case the runtime stops now before stopping and
863 // removing the user completely.
864 user.partial = true;
865 writeUserLocked(user);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700866 }
Amith Yamasani16389312012-10-17 21:20:14 -0700867 if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700868 int res;
869 try {
870 res = ActivityManagerNative.getDefault().stopUser(userHandle,
871 new IStopUserCallback.Stub() {
872 @Override
873 public void userStopped(int userId) {
874 finishRemoveUser(userId);
875 }
876 @Override
877 public void userStopAborted(int userId) {
878 }
879 });
880 } catch (RemoteException e) {
881 return false;
882 }
883
884 return res == ActivityManager.USER_OP_SUCCESS;
885 }
886
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700887 void finishRemoveUser(final int userHandle) {
Amith Yamasani16389312012-10-17 21:20:14 -0700888 if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700889 // Let other services shutdown any activity and clean up their state before completely
890 // wiping the user's system directory and removing from the user list
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700891 long ident = Binder.clearCallingIdentity();
892 try {
893 Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
894 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700895 mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
896 android.Manifest.permission.MANAGE_USERS,
897
898 new BroadcastReceiver() {
899 @Override
900 public void onReceive(Context context, Intent intent) {
901 if (DBG) {
902 Slog.i(LOG_TAG,
903 "USER_REMOVED broadcast sent, cleaning up user data "
904 + userHandle);
905 }
906 new Thread() {
907 public void run() {
908 synchronized (mInstallLock) {
909 synchronized (mPackagesLock) {
910 removeUserStateLocked(userHandle);
911 }
912 }
913 }
914 }.start();
915 }
916 },
917
918 null, Activity.RESULT_OK, null, null);
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700919 } finally {
920 Binder.restoreCallingIdentity(ident);
921 }
Amith Yamasani2a003292012-08-14 18:25:45 -0700922 }
923
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800924 private void removeUserStateLocked(final int userHandle) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700925 // Cleanup package manager settings
926 mPm.cleanUpUserLILPw(userHandle);
927
928 // Remove this user from the list
929 mUsers.remove(userHandle);
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -0800930
931 // Have user ID linger for several seconds to let external storage VFS
932 // cache entries expire. This must be greater than the 'entry_valid'
933 // timeout used by the FUSE daemon.
934 mHandler.postDelayed(new Runnable() {
935 @Override
936 public void run() {
937 synchronized (mPackagesLock) {
938 mRemovingUserIds.delete(userHandle);
939 }
940 }
941 }, MINUTE_IN_MILLIS);
942
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -0700943 // Remove user file
944 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
945 userFile.delete();
946 // Update the user list
947 writeUserListLocked();
948 updateUserIdsLocked();
949 removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
950 }
951
Amith Yamasani61f57372012-08-31 12:12:28 -0700952 private void removeDirectoryRecursive(File parent) {
953 if (parent.isDirectory()) {
954 String[] files = parent.list();
955 for (String filename : files) {
956 File child = new File(parent, filename);
957 removeDirectoryRecursive(child);
958 }
959 }
960 parent.delete();
961 }
962
Amith Yamasani2a003292012-08-14 18:25:45 -0700963 @Override
Amith Yamasanidf2e92a2013-03-01 17:04:38 -0800964 public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
965 if (UserHandle.getCallingUserId() != userId
Amith Yamasani9429afb2013-04-10 18:40:51 -0700966 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -0800967 checkManageUsersPermission("Only system can get restrictions for other users/apps");
968 }
969 synchronized (mPackagesLock) {
970 // Read the restrictions from XML
971 return readApplicationRestrictionsLocked(packageName, userId);
972 }
973 }
974
975 @Override
976 public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
977 int userId) {
978 if (UserHandle.getCallingUserId() != userId
Amith Yamasani9429afb2013-04-10 18:40:51 -0700979 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -0800980 checkManageUsersPermission("Only system can set restrictions for other users/apps");
981 }
982 synchronized (mPackagesLock) {
983 // Write the restrictions to XML
984 writeApplicationRestrictionsLocked(packageName, entries, userId);
985 }
986 }
987
988 private int getUidForPackage(String packageName) {
Amith Yamasani9429afb2013-04-10 18:40:51 -0700989 long ident = Binder.clearCallingIdentity();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -0800990 try {
991 return mContext.getPackageManager().getApplicationInfo(packageName,
992 PackageManager.GET_UNINSTALLED_PACKAGES).uid;
993 } catch (NameNotFoundException nnfe) {
994 return -1;
Amith Yamasani9429afb2013-04-10 18:40:51 -0700995 } finally {
996 Binder.restoreCallingIdentity(ident);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -0800997 }
998 }
999
1000 private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
1001 int userId) {
1002 final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
1003 final ArrayList<String> values = new ArrayList<String>();
1004
1005 FileInputStream fis = null;
1006 try {
1007 AtomicFile restrictionsFile =
1008 new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
1009 RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
1010 fis = restrictionsFile.openRead();
1011 XmlPullParser parser = Xml.newPullParser();
1012 parser.setInput(fis, null);
1013 int type;
1014 while ((type = parser.next()) != XmlPullParser.START_TAG
1015 && type != XmlPullParser.END_DOCUMENT) {
1016 ;
1017 }
1018
1019 if (type != XmlPullParser.START_TAG) {
1020 Slog.e(LOG_TAG, "Unable to read restrictions file "
1021 + restrictionsFile.getBaseFile());
1022 return entries;
1023 }
1024
1025 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1026 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
1027 String key = parser.getAttributeValue(null, ATTR_KEY);
1028 String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
1029 if (multiple != null) {
1030 int count = Integer.parseInt(multiple);
1031 while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1032 if (type == XmlPullParser.START_TAG
1033 && parser.getName().equals(TAG_VALUE)) {
1034 values.add(parser.nextText().trim());
1035 count--;
1036 }
1037 }
1038 String [] valueStrings = new String[values.size()];
1039 values.toArray(valueStrings);
1040 Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
1041 RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
1042 entries.add(entry);
1043 } else {
1044 String value = parser.nextText().trim();
1045 Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
1046 RestrictionEntry entry = new RestrictionEntry(key, value);
1047 entries.add(entry);
1048 }
1049 }
1050 }
1051
1052 } catch (IOException ioe) {
1053 } catch (XmlPullParserException pe) {
1054 } finally {
1055 if (fis != null) {
1056 try {
1057 fis.close();
1058 } catch (IOException e) {
1059 }
1060 }
1061 }
1062 return entries;
1063 }
1064
1065 private void writeApplicationRestrictionsLocked(String packageName,
1066 List<RestrictionEntry> entries, int userId) {
1067 FileOutputStream fos = null;
1068 AtomicFile restrictionsFile = new AtomicFile(
1069 new File(Environment.getUserSystemDirectory(userId),
1070 RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
1071 try {
1072 fos = restrictionsFile.startWrite();
1073 final BufferedOutputStream bos = new BufferedOutputStream(fos);
1074
1075 // XmlSerializer serializer = XmlUtils.serializerInstance();
1076 final XmlSerializer serializer = new FastXmlSerializer();
1077 serializer.setOutput(bos, "utf-8");
1078 serializer.startDocument(null, true);
1079 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
1080
1081 serializer.startTag(null, TAG_RESTRICTIONS);
1082
1083 for (RestrictionEntry entry : entries) {
1084 serializer.startTag(null, TAG_ENTRY);
Amith Yamasani86118ba2013-03-28 14:33:16 -07001085 serializer.attribute(null, ATTR_KEY, entry.getKey());
1086 if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
1087 String value = entry.getSelectedString();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08001088 serializer.text(value != null ? value : "");
1089 } else {
Amith Yamasani86118ba2013-03-28 14:33:16 -07001090 String[] values = entry.getAllSelectedStrings();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08001091 serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
1092 for (String value : values) {
1093 serializer.startTag(null, TAG_VALUE);
1094 serializer.text(value != null ? value : "");
1095 serializer.endTag(null, TAG_VALUE);
1096 }
1097 }
1098 serializer.endTag(null, TAG_ENTRY);
1099 }
1100
1101 serializer.endTag(null, TAG_RESTRICTIONS);
1102
1103 serializer.endDocument();
1104 restrictionsFile.finishWrite(fos);
1105 } catch (Exception e) {
1106 restrictionsFile.failWrite(fos);
1107 Slog.e(LOG_TAG, "Error writing application restrictions list");
1108 }
1109 }
1110
1111 @Override
Amith Yamasani2a003292012-08-14 18:25:45 -07001112 public int getUserSerialNumber(int userHandle) {
Dianne Hackborn4428e172012-08-24 17:43:05 -07001113 synchronized (mPackagesLock) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001114 if (!exists(userHandle)) return -1;
Amith Yamasani195263742012-08-21 15:40:12 -07001115 return getUserInfoLocked(userHandle).serialNumber;
Amith Yamasani2a003292012-08-14 18:25:45 -07001116 }
1117 }
1118
1119 @Override
1120 public int getUserHandle(int userSerialNumber) {
Dianne Hackborn4428e172012-08-24 17:43:05 -07001121 synchronized (mPackagesLock) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001122 for (int userId : mUserIds) {
Amith Yamasani195263742012-08-21 15:40:12 -07001123 if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
Amith Yamasani2a003292012-08-14 18:25:45 -07001124 }
1125 // Not found
1126 return -1;
Amith Yamasani13593602012-03-22 16:16:17 -07001127 }
1128 }
1129
Amith Yamasani0b285492011-04-14 17:35:23 -07001130 /**
1131 * Caches the list of user ids in an array, adjusting the array size when necessary.
1132 */
Amith Yamasani13593602012-03-22 16:16:17 -07001133 private void updateUserIdsLocked() {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001134 int num = 0;
Amith Yamasani0b285492011-04-14 17:35:23 -07001135 for (int i = 0; i < mUsers.size(); i++) {
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001136 if (!mUsers.valueAt(i).partial) {
1137 num++;
1138 }
1139 }
Amith Yamasani16389312012-10-17 21:20:14 -07001140 final int[] newUsers = new int[num];
1141 int n = 0;
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001142 for (int i = 0; i < mUsers.size(); i++) {
1143 if (!mUsers.valueAt(i).partial) {
Amith Yamasani16389312012-10-17 21:20:14 -07001144 newUsers[n++] = mUsers.keyAt(i);
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001145 }
Amith Yamasani0b285492011-04-14 17:35:23 -07001146 }
Dianne Hackborn7767eac2012-08-23 18:25:40 -07001147 mUserIds = newUsers;
Amith Yamasani0b285492011-04-14 17:35:23 -07001148 }
1149
1150 /**
Amith Yamasani920ace02012-09-20 22:15:37 -07001151 * Make a note of the last started time of a user.
1152 * @param userId the user that was just foregrounded
1153 */
1154 public void userForeground(int userId) {
1155 synchronized (mPackagesLock) {
1156 UserInfo user = mUsers.get(userId);
1157 long now = System.currentTimeMillis();
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001158 if (user == null || user.partial) {
1159 Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
1160 return;
1161 }
1162 if (now > EPOCH_PLUS_30_YEARS) {
Amith Yamasani920ace02012-09-20 22:15:37 -07001163 user.lastLoggedInTime = now;
1164 writeUserLocked(user);
1165 }
1166 }
1167 }
1168
1169 /**
Amith Yamasani0b285492011-04-14 17:35:23 -07001170 * Returns the next available user id, filling in any holes in the ids.
Amith Yamasani742a6712011-05-04 14:49:28 -07001171 * TODO: May not be a good idea to recycle ids, in case it results in confusion
1172 * for data and battery stats collection, or unexpected cross-talk.
Amith Yamasani0b285492011-04-14 17:35:23 -07001173 * @return
1174 */
Dianne Hackborn5dc5a002012-09-15 19:33:48 -07001175 private int getNextAvailableIdLocked() {
Dianne Hackborn4428e172012-08-24 17:43:05 -07001176 synchronized (mPackagesLock) {
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -08001177 int i = MIN_USER_ID;
Amith Yamasani195263742012-08-21 15:40:12 -07001178 while (i < Integer.MAX_VALUE) {
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -08001179 if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
Amith Yamasani195263742012-08-21 15:40:12 -07001180 break;
1181 }
1182 i++;
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001183 }
Amith Yamasani195263742012-08-21 15:40:12 -07001184 return i;
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001185 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001186 }
Amith Yamasani920ace02012-09-20 22:15:37 -07001187
1188 @Override
1189 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1190 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1191 != PackageManager.PERMISSION_GRANTED) {
1192 pw.println("Permission Denial: can't dump UserManager from from pid="
1193 + Binder.getCallingPid()
1194 + ", uid=" + Binder.getCallingUid()
1195 + " without permission "
1196 + android.Manifest.permission.DUMP);
1197 return;
1198 }
1199
1200 long now = System.currentTimeMillis();
1201 StringBuilder sb = new StringBuilder();
1202 synchronized (mPackagesLock) {
1203 pw.println("Users:");
1204 for (int i = 0; i < mUsers.size(); i++) {
1205 UserInfo user = mUsers.valueAt(i);
1206 if (user == null) continue;
Amith Yamasani634cf312012-10-04 17:34:21 -07001207 pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
Jeff Sharkeyffe0cb42012-11-05 17:24:43 -08001208 if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
Dianne Hackbornd4ac8d72012-09-27 23:20:10 -07001209 if (user.partial) pw.print(" <partial>");
1210 pw.println();
Amith Yamasani920ace02012-09-20 22:15:37 -07001211 pw.print(" Created: ");
1212 if (user.creationTime == 0) {
1213 pw.println("<unknown>");
1214 } else {
1215 sb.setLength(0);
1216 TimeUtils.formatDuration(now - user.creationTime, sb);
1217 sb.append(" ago");
1218 pw.println(sb);
1219 }
1220 pw.print(" Last logged in: ");
1221 if (user.lastLoggedInTime == 0) {
1222 pw.println("<unknown>");
1223 } else {
1224 sb.setLength(0);
1225 TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
1226 sb.append(" ago");
1227 pw.println(sb);
1228 }
1229 }
1230 }
1231 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001232}