diff options
author | Amith Yamasani <yamasani@google.com> | 2011-04-19 10:41:20 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-04-19 10:41:20 -0700 |
commit | 25641ca1ac5b09727f86fe01389877332a00455d (patch) | |
tree | e18f66c852164f4fbc6fbdf3d7c689b9b2ac3d83 /services | |
parent | 08d1f937236230756bffde241ad6b335da368cf9 (diff) | |
parent | 0b285499db739ba50f2f839d633e763c70e67f96 (diff) | |
download | frameworks_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.java | 26 | ||||
-rw-r--r-- | services/java/com/android/server/pm/PackageManagerService.java | 68 | ||||
-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) { |