summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2011-04-19 10:41:20 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-04-19 10:41:20 -0700
commit25641ca1ac5b09727f86fe01389877332a00455d (patch)
treee18f66c852164f4fbc6fbdf3d7c689b9b2ac3d83 /services
parent08d1f937236230756bffde241ad6b335da368cf9 (diff)
parent0b285499db739ba50f2f839d633e763c70e67f96 (diff)
downloadframeworks_base-25641ca1ac5b09727f86fe01389877332a00455d.zip
frameworks_base-25641ca1ac5b09727f86fe01389877332a00455d.tar.gz
frameworks_base-25641ca1ac5b09727f86fe01389877332a00455d.tar.bz2
Merge "Plumbing in PackageManager and installd for multi-user support."
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/pm/Installer.java26
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java68
-rw-r--r--services/java/com/android/server/pm/UserManager.java (renamed from services/java/com/android/server/pm/UserDetails.java)136
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java (renamed from services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java)31
4 files changed, 211 insertions, 50 deletions
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index da3ebaf..d10aa97 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -225,10 +225,12 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, int userId) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
@@ -248,10 +250,30 @@ class Installer {
return execute(builder.toString());
}
- public int clearUserData(String name) {
+ public int createUserData(String name, int uid, int userId) {
+ StringBuilder builder = new StringBuilder("mkuserdata");
+ builder.append(' ');
+ builder.append(name);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int removeUserDataDirs(int userId) {
+ StringBuilder builder = new StringBuilder("rmuser");
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int clearUserData(String name, int userId) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index a9d49b4..6e1093f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -65,6 +65,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -208,6 +209,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // This is where all application persistent data goes for secondary users.
+ final File mUserAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -359,6 +363,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
+ final UserManager mUserManager;
+
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
@@ -797,8 +803,11 @@ public class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
+ mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
// the simulator.
@@ -806,6 +815,7 @@ public class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mUserAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
@@ -974,7 +984,8 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ mInstaller.remove(ps.name, 0);
+ mUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -1059,10 +1070,12 @@ public class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ int retCode = mInstaller.remove(ps.name, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
+ } else {
+ mUserManager.removePackageForAllUsers(ps.name);
}
} else {
//for emulator
@@ -1510,7 +1523,8 @@ public class PackageManagerService extends IPackageManager.Stub {
ps.pkg.applicationInfo.flags = ps.pkgFlags;
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
- ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.dataDir =
+ getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
ps.pkg.mSetEnabled = ps.enabled;
ps.pkg.mSetStopped = ps.stopped;
@@ -2836,11 +2850,15 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private File getDataPathForPackage(PackageParser.Package pkg) {
- final File dataPath = new File(mAppDataDir, pkg.packageName);
- return dataPath;
+ File getDataPathForUser(int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId);
}
-
+
+ private File getDataPathForPackage(String packageName, int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator
+ + userId + File.separator + packageName);
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
File scanFile = new File(pkg.mScanPath);
@@ -3162,7 +3180,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
- dataPath = getDataPathForPackage(pkg);
+ dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError = false;
@@ -3178,8 +3196,11 @@ public class PackageManagerService extends IPackageManager.Stub {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, 0);
if (ret >= 0) {
+ // TODO: Kill the processes first
+ // Remove the data directories for all users
+ mUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3199,6 +3220,9 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName,
+ pkg.applicationInfo.uid);
}
}
if (!recovered) {
@@ -3235,11 +3259,13 @@ public class PackageManagerService extends IPackageManager.Stub {
if (mInstaller != null) {
int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
- if(ret < 0) {
+ if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
} else {
dataPath.mkdirs();
if (dataPath.exists()) {
@@ -5703,7 +5729,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
- boolean dataDirExists = getDataPathForPackage(pkg).exists();
+ boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -6390,11 +6416,14 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
+ packageName + ", retcode=" + retCode);
// we don't consider this to be a failure of the core package deletion
+ } else {
+ // TODO: Kill the processes first
+ mUserManager.removePackageForAllUsers(packageName);
}
} else {
// for simulator
@@ -6654,7 +6683,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -8015,4 +8044,17 @@ public class PackageManagerService extends IPackageManager.Stub {
android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
}
+
+ public UserInfo createUser(String name, int flags) {
+ UserInfo userInfo = mUserManager.createUser(name, flags, getInstalledApplications(0));
+ return userInfo;
+ }
+
+ public boolean removeUser(int userId) {
+ if (userId == 0) {
+ return false;
+ }
+ mUserManager.removeUser(userId);
+ return true;
+ }
}
diff --git a/services/java/com/android/server/pm/UserDetails.java b/services/java/com/android/server/pm/UserManager.java
index 2aeed7c..76fa5ab 100644
--- a/services/java/com/android/server/pm/UserDetails.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -18,9 +18,13 @@ package com.android.server.pm;
import com.android.internal.util.FastXmlSerializer;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -37,7 +41,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-public class UserDetails {
+public class UserManager {
private static final String TAG_NAME = "name";
private static final String ATTR_FLAGS = "flags";
@@ -48,22 +52,27 @@ public class UserDetails {
private static final String TAG_USER = "user";
- private static final String TAG = "UserDetails";
+ private static final String LOG_TAG = "UserManager";
- private static final String USER_INFO_DIR = "system/users";
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private SparseArray<UserInfo> mUsers;
private final File mUsersDir;
private final File mUserListFile;
+ private int[] mUserIds;
+
+ private Installer mInstaller;
+ private File mBaseUserPath;
/**
* Available for testing purposes.
*/
- UserDetails(File dataDir) {
+ UserManager(File dataDir, File baseUserPath) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
+ mBaseUserPath = baseUserPath;
FileUtils.setPermissions(mUsersDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
@@ -72,8 +81,9 @@ public class UserDetails {
readUserList();
}
- public UserDetails() {
- this(Environment.getDataDirectory());
+ public UserManager(Installer installer, File baseUserPath) {
+ this(Environment.getDataDirectory(), baseUserPath);
+ mInstaller = installer;
}
public List<UserInfo> getUsers() {
@@ -84,6 +94,15 @@ public class UserDetails {
return users;
}
+ /**
+ * Returns an array of user ids. This array is cached here for quick access, so do not modify or
+ * cache it elsewhere.
+ * @return the array of user ids.
+ */
+ int[] getUserIds() {
+ return mUserIds;
+ }
+
private void readUserList() {
mUsers = new SparseArray<UserInfo>();
if (!mUserListFile.exists()) {
@@ -102,7 +121,7 @@ public class UserDetails {
}
if (type != XmlPullParser.START_TAG) {
- Slog.e(TAG, "Unable to read user list");
+ Slog.e(LOG_TAG, "Unable to read user list");
fallbackToSingleUser();
return;
}
@@ -116,6 +135,7 @@ public class UserDetails {
}
}
}
+ updateUserIds();
} catch (IOException ioe) {
fallbackToSingleUser();
} catch (XmlPullParserException pe) {
@@ -128,6 +148,7 @@ public class UserDetails {
UserInfo primary = new UserInfo(0, "Primary",
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
mUsers.put(0, primary);
+ updateUserIds();
writeUserList();
writeUser(primary);
@@ -164,7 +185,7 @@ public class UserDetails {
serializer.endDocument();
} catch (IOException ioe) {
- Slog.e(TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
}
}
@@ -194,14 +215,13 @@ public class UserDetails {
serializer.startTag(null, TAG_USER);
serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
serializer.endTag(null, TAG_USER);
- Slog.e(TAG, "Wrote user " + user.id + " to userlist.xml");
}
serializer.endTag(null, TAG_USERS);
serializer.endDocument();
} catch (IOException ioe) {
- Slog.e(TAG, "Error writing user list");
+ Slog.e(LOG_TAG, "Error writing user list");
}
}
@@ -222,14 +242,14 @@ public class UserDetails {
}
if (type != XmlPullParser.START_TAG) {
- Slog.e(TAG, "Unable to read user " + id);
+ Slog.e(LOG_TAG, "Unable to read user " + id);
return null;
}
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String storedId = parser.getAttributeValue(null, ATTR_ID);
if (Integer.parseInt(storedId) != id) {
- Slog.e(TAG, "User id does not match the file name");
+ Slog.e(LOG_TAG, "User id does not match the file name");
return null;
}
String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
@@ -256,18 +276,25 @@ public class UserDetails {
return null;
}
- public UserInfo createUser(String name, int flags) {
- int id = getNextAvailableId();
- UserInfo userInfo = new UserInfo(id, name, flags);
- if (!createPackageFolders(id)) {
+ public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ int userId = getNextAvailableId();
+ UserInfo userInfo = new UserInfo(userId, name, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ if (!createPackageFolders(userId, userPath, apps)) {
return null;
}
- mUsers.put(id, userInfo);
+ mUsers.put(userId, userInfo);
writeUserList();
writeUser(userInfo);
+ updateUserIds();
return userInfo;
}
+ /**
+ * Removes a user and all data directories created for that user. This method should be called
+ * after the user's processes have been terminated.
+ * @param id the user's id
+ */
public void removeUser(int id) {
// Remove from the list
UserInfo userInfo = mUsers.get(id);
@@ -277,11 +304,58 @@ public class UserDetails {
// Remove user file
File userFile = new File(mUsersDir, id + ".xml");
userFile.delete();
+ // Update the user list
writeUserList();
+ // Remove the data directories for all packages for this user
removePackageFolders(id);
+ updateUserIds();
+ }
+ }
+
+ public void installPackageForAllUsers(String packageName, int uid) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ userId);
+ }
+ }
+
+ public void clearUserDataForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.clearUserData(packageName, userId);
+ }
+ }
+
+ public void removePackageForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.remove(packageName, userId);
+ }
+ }
+
+ /**
+ * Caches the list of user ids in an array, adjusting the array size when necessary.
+ */
+ private void updateUserIds() {
+ if (mUserIds == null || mUserIds.length != mUsers.size()) {
+ mUserIds = new int[mUsers.size()];
+ }
+ for (int i = 0; i < mUsers.size(); i++) {
+ mUserIds[i] = mUsers.keyAt(i);
}
}
+ /**
+ * Returns the next available user id, filling in any holes in the ids.
+ * @return
+ */
private int getNextAvailableId() {
int i = 0;
while (i < Integer.MAX_VALUE) {
@@ -293,13 +367,35 @@ public class UserDetails {
return i;
}
- private boolean createPackageFolders(int id) {
- // TODO: Create data directories for all the packages for a new user, w/ specified user id.
+ private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null || apps == null) return true;
+
+ final long startTime = SystemClock.elapsedRealtime();
+ // Create the user path
+ userPath.mkdir();
+ FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+
+ // Create the individual data directories
+ for (ApplicationInfo app : apps) {
+ if (app.uid > android.os.Process.FIRST_APPLICATION_UID
+ && app.uid < PackageManager.PER_USER_RANGE) {
+ mInstaller.createUserData(app.packageName,
+ PackageManager.getUid(id, app.uid), id);
+ }
+ }
+ final long stopTime = SystemClock.elapsedRealtime();
+ Log.i(LOG_TAG,
+ "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
return true;
}
private boolean removePackageFolders(int id) {
- // TODO: Remove all the data directories for the specified user.
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null) return true;
+
+ mInstaller.removeUserDataDirs(id);
return true;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7b77aac..e8188e7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,7 +16,7 @@
package com.android.server.pm;
-import com.android.server.pm.UserDetails;
+import com.android.server.pm.UserManager;
import android.content.pm.UserInfo;
import android.os.Debug;
@@ -25,23 +25,24 @@ import android.test.AndroidTestCase;
import java.util.List;
-/** Test {@link UserDetails} functionality. */
-public class UserDetailsTest extends AndroidTestCase {
+/** Test {@link UserManager} functionality. */
+public class UserManagerTest extends AndroidTestCase {
- UserDetails mDetails = null;
+ UserManager mUserManager = null;
@Override
public void setUp() throws Exception {
- mDetails = new UserDetails(Environment.getExternalStorageDirectory());
+ mUserManager = new UserManager(Environment.getExternalStorageDirectory(),
+ Environment.getExternalStorageDirectory());
}
@Override
public void tearDown() throws Exception {
- List<UserInfo> users = mDetails.getUsers();
+ List<UserInfo> users = mUserManager.getUsers();
// Remove all except the primary user
for (UserInfo user : users) {
if (!user.isPrimary()) {
- mDetails.removeUser(user.id);
+ mUserManager.removeUser(user.id);
}
}
}
@@ -51,9 +52,9 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testAddUser() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
assertTrue(userInfo != null);
List<UserInfo> list = details.getUsers();
@@ -70,10 +71,10 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testAdd2Users() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
- UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
+ UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN, null);
assertTrue(user1 != null);
assertTrue(user2 != null);
@@ -84,9 +85,9 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testRemoveUser() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
details.removeUser(userInfo.id);
@@ -94,7 +95,7 @@ public class UserDetailsTest extends AndroidTestCase {
}
private boolean findUser(int id) {
- List<UserInfo> list = mDetails.getUsers();
+ List<UserInfo> list = mUserManager.getUsers();
for (UserInfo user : list) {
if (user.id == id) {