diff options
Diffstat (limited to 'services/java/com/android/server/pm')
9 files changed, 2350 insertions, 1839 deletions
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index 48004bb..ad85c0d 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -25,7 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -class Installer { +public final class Installer { private static final String TAG = "Installer"; private static final boolean LOCAL_DEBUG = false; @@ -324,26 +324,14 @@ class Installer { return execute(builder.toString()); } - /* - * @param packagePathSuffix The name of the path relative to install - * directory. Say if the path name is /data/app/com.test-1.apk, the package - * suffix path will be com.test-1 - */ - public int setForwardLockPerm(String packagePathSuffix, int gid) { - StringBuilder builder = new StringBuilder("protect"); - builder.append(' '); - builder.append(packagePathSuffix); - builder.append(' '); - builder.append(gid); - return execute(builder.toString()); - } - - public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, + public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath, String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); builder.append(' '); + builder.append(persona); + builder.append(' '); builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); @@ -371,12 +359,20 @@ class Installer { return execute("movefiles"); } + /** + * Links the native library directory in an application's directory to its + * real location. + * + * @param dataPath data directory where the application is + * @param nativeLibPath target native library path + * @return -1 on error + */ public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) { if (dataPath == null) { - Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null"); + Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); return -1; } else if (nativeLibPath == null) { - Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null"); + Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); return -1; } @@ -387,16 +383,4 @@ class Installer { return execute(builder.toString()); } - - public int unlinkNativeLibraryDirectory(String dataPath) { - if (dataPath == null) { - Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null"); - return -1; - } - - StringBuilder builder = new StringBuilder("unlinklib "); - builder.append(dataPath); - - 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 f914271..536c612 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -25,6 +25,11 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; import static libcore.io.OsConstants.S_ISLNK; +import static libcore.io.OsConstants.S_IRWXU; +import static libcore.io.OsConstants.S_IRGRP; +import static libcore.io.OsConstants.S_IXGRP; +import static libcore.io.OsConstants.S_IROTH; +import static libcore.io.OsConstants.S_IXOTH; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; @@ -64,10 +69,13 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageUserState; +import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageStats; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; @@ -76,8 +84,8 @@ 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.content.pm.ManifestDigest; +import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.net.Uri; @@ -100,7 +108,8 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserId; +import android.os.UserHandle; +import android.os.Environment.UserEnvironment; import android.provider.Settings.Secure; import android.security.SystemKeyStore; import android.util.DisplayMetrics; @@ -144,6 +153,7 @@ import java.util.Set; import libcore.io.ErrnoException; import libcore.io.IoUtils; import libcore.io.Libcore; +import libcore.io.OsConstants; import libcore.io.StructStat; /** @@ -165,6 +175,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final boolean DEBUG_UPGRADE = false; private static final boolean DEBUG_INSTALL = false; private static final boolean DEBUG_REMOVE = false; + private static final boolean DEBUG_BROADCASTS = false; private static final boolean DEBUG_SHOW_INFO = false; private static final boolean DEBUG_PACKAGE_INFO = false; private static final boolean DEBUG_INTENT_MATCHING = false; @@ -175,6 +186,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; + private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID; private static final boolean GET_CERTIFICATES = true; @@ -203,14 +215,21 @@ public class PackageManagerService extends IPackageManager.Stub { /** * Whether verification is enabled by default. */ - // STOPSHIP: change this to true - private static final boolean DEFAULT_VERIFY_ENABLE = false; + private static final boolean DEFAULT_VERIFY_ENABLE = true; /** * The default maximum time to wait for the verification agent to return in * milliseconds. */ - private static final long DEFAULT_VERIFICATION_TIMEOUT = 60 * 1000; + private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000; + + /** + * The default response for package verification timeout. + * + * This can be either PackageManager.VERIFICATION_ALLOW or + * PackageManager.VERIFICATION_REJECT. + */ + private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW; static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; @@ -264,7 +283,7 @@ public class PackageManagerService extends IPackageManager.Stub { // This is the object monitoring mDrmAppPrivateInstallDir. final FileObserver mDrmAppInstallObserver; - // Used for priviledge escalation. MUST NOT BE CALLED WITH mPackages + // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages // LOCK HELD. Can be called with mInstallLock held. final Installer mInstaller; @@ -274,6 +293,12 @@ public class PackageManagerService extends IPackageManager.Stub { final File mAppInstallDir; final File mDalvikCacheDir; + /** + * Directory to which applications installed internally have native + * libraries copied. + */ + private File mAppLibInstallDir; + // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked // apps. final File mDrmAppPrivateInstallDir; @@ -388,6 +413,7 @@ public class PackageManagerService extends IPackageManager.Stub { // package uri's from external media onto secure containers // or internal storage. private IMediaContainerService mContainerService = null; + private int mContainerServiceUserId; static final int SEND_PENDING_BROADCAST = 1; static final int MCS_BOUND = 3; @@ -410,7 +436,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; - static UserManager sUserManager; + static UserManagerService sUserManager; // Stores a list of users whose package restrictions file needs to be updated private HashSet<Integer> mDirtyUsers = new HashSet<Integer>(); @@ -456,8 +482,15 @@ public class PackageManagerService extends IPackageManager.Stub { " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + mContainerServiceUserId = 0; + if (mPendingInstalls.size() > 0) { + mContainerServiceUserId = mPendingInstalls.get(0).getUser().getIdentifier(); + if (mContainerServiceUserId == UserHandle.USER_ALL) { + mContainerServiceUserId = 0; + } + } if (mContext.bindService(service, mDefContainerConn, - Context.BIND_AUTO_CREATE)) { + Context.BIND_AUTO_CREATE, mContainerServiceUserId)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true; return true; @@ -534,6 +567,15 @@ public class PackageManagerService extends IPackageManager.Stub { } else if (mPendingInstalls.size() > 0) { HandlerParams params = mPendingInstalls.get(0); if (params != null) { + // Check if we're connected to the correct service, if it's an install + // request. + final int installFor = params.getUser().getIdentifier(); + if (installFor != mContainerServiceUserId + && (installFor == UserHandle.USER_ALL + && mContainerServiceUserId != 0)) { + mHandler.sendEmptyMessage(MCS_RECONNECT); + return; + } if (params.startCopy()) { // We are done... look for more work or to // go idle. @@ -650,15 +692,21 @@ public class PackageManagerService extends IPackageManager.Stub { break; } case START_CLEANING_PACKAGE: { - String packageName = (String)msg.obj; Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + PackageCleanItem item = new PackageCleanItem((String)msg.obj, + msg.arg2 != 0); synchronized (mPackages) { - if (!mSettings.mPackagesToBeCleaned.contains(packageName)) { - mSettings.mPackagesToBeCleaned.add(packageName); + if (msg.arg1 == UserHandle.USER_ALL) { + int[] users = sUserManager.getUserIds(); + for (int user : users) { + mSettings.addPackageToCleanLPw(user, item); + } + } else { + mSettings.addPackageToCleanLPw(msg.arg1, item); } } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - startCleaningPackages(); + startCleaningPackages(-1); } break; case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); @@ -674,26 +722,65 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); + // Determine the set of users who are adding this + // package for the first time vs. those who are seeing + // an update. + int[] firstUsers; + int[] updateUsers = new int[0]; + if (res.origUsers == null || res.origUsers.length == 0) { + firstUsers = res.newUsers; + } else { + firstUsers = new int[0]; + for (int i=0; i<res.newUsers.length; i++) { + int user = res.newUsers[i]; + boolean isNew = true; + for (int j=0; j<res.origUsers.length; j++) { + if (res.origUsers[j] == user) { + isNew = false; + break; + } + } + if (isNew) { + int[] newFirst = new int[firstUsers.length+1]; + System.arraycopy(firstUsers, 0, newFirst, 0, + firstUsers.length); + newFirst[firstUsers.length] = user; + firstUsers = newFirst; + } else { + int[] newUpdate = new int[updateUsers.length+1]; + System.arraycopy(updateUsers, 0, newUpdate, 0, + updateUsers.length); + newUpdate[updateUsers.length] = user; + updateUsers = newUpdate; + } + } + } + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + res.pkg.applicationInfo.packageName, + extras, null, null, firstUsers); final boolean update = res.removedInfo.removedPackage != null; if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, updateUsers); if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, res.pkg.applicationInfo.packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, updateUsers); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, - res.pkg.applicationInfo.packageName, null, - UserId.USER_ALL); + res.pkg.applicationInfo.packageName, null, updateUsers); } if (res.removedInfo.args != null) { // Remove the replaced package's older resources safely now deleteOld = true; } + + // Log current value of "unknown sources" setting + EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, + getUnknownSourcesSettings()); } // Force a gc to clear up things Runtime.getRuntime().gc(); @@ -764,17 +851,33 @@ public class PackageManagerService extends IPackageManager.Stub { final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); - if (state != null) { + if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); Slog.i(TAG, "Verification timed out for " + args.packageURI.toString()); mPendingVerification.remove(verificationId); - int ret = PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT; - processPendingInstall(args, ret); + int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; + + if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) { + Slog.i(TAG, "Continuing with installation of " + + args.packageURI.toString()); + state.setVerifierResponse(Binder.getCallingUid(), + PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); + broadcastPackageVerified(verificationId, args.packageURI, + PackageManager.VERIFICATION_ALLOW); + try { + ret = args.copyApk(mContainerService, true); + } catch (RemoteException e) { + Slog.e(TAG, "Could not contact the ContainerService"); + } + } else { + broadcastPackageVerified(verificationId, args.packageURI, + PackageManager.VERIFICATION_REJECT); + } + processPendingInstall(args, ret); mHandler.sendEmptyMessage(MCS_UNBIND); } - break; } case PACKAGE_VERIFIED: { @@ -798,6 +901,8 @@ public class PackageManagerService extends IPackageManager.Stub { int ret; if (state.isInstallAllowed()) { ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + broadcastPackageVerified(verificationId, args.packageURI, + response.code); try { ret = args.copyApk(mContainerService, true); } catch (RemoteException e) { @@ -832,9 +937,10 @@ public class PackageManagerService extends IPackageManager.Stub { } } - public static final IPackageManager main(Context context, boolean factoryTest, - boolean onlyCore) { - PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore); + public static final IPackageManager main(Context context, Installer installer, + boolean factoryTest, boolean onlyCore) { + PackageManagerService m = new PackageManagerService(context, installer, + factoryTest, onlyCore); ServiceManager.addService("package", m); return m; } @@ -861,7 +967,8 @@ public class PackageManagerService extends IPackageManager.Stub { return res; } - public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) { + public PackageManagerService(Context context, Installer installer, + boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); @@ -874,12 +981,13 @@ public class PackageManagerService extends IPackageManager.Stub { mOnlyCore = onlyCore; mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); - mSettings = new Settings(); + mSettings = new Settings(context); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM); + mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { @@ -898,7 +1006,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSeparateProcesses = null; } - mInstaller = new Installer(); + mInstaller = installer; WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); @@ -916,11 +1024,12 @@ public class PackageManagerService extends IPackageManager.Stub { mUserAppDataDir = new File(dataDir, "user"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); - sUserManager = new UserManager(mInstaller, mUserAppDataDir); + sUserManager = new UserManagerService(context, this, + mInstallLock, mPackages); readPermissions(); - mRestoredSettings = mSettings.readLPw(getUsers()); + mRestoredSettings = mSettings.readLPw(sUserManager.getUsers()); long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, @@ -1099,7 +1208,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (mSettings.isDisabledSystemPackageLPr(ps.name)) { Slog.i(TAG, "Expecting better updatd system app for " + ps.name + "; removing system app"); - removePackageLI(scannedPkg, true); + removePackageLI(ps, true); } continue; @@ -1110,8 +1219,7 @@ public class PackageManagerService extends IPackageManager.Stub { String msg = "System package " + ps.name + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); - mInstaller.remove(ps.name, 0); - sUserManager.removePackageForAllUsers(ps.name); + removeDataDirsLI(ps.name); } else { final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); if (disabledPs.codePath == null || !disabledPs.codePath.exists()) { @@ -1122,6 +1230,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mAppInstallDir = new File(dataDir, "app"); + mAppLibInstallDir = new File(dataDir, "app-lib"); //look for any incomplete package installations ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); //clean up list @@ -1160,9 +1269,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (deletedPkg == null) { msg = "Updated system package " + deletedAppName + " no longer exists; wiping its data"; - - mInstaller.remove(deletedAppName, 0); - sUserManager.removePackageForAllUsers(deletedAppName); + removeDataDirsLI(deletedAppName); } else { msg = "Updated system app + " + deletedAppName + " no longer present; removing system privileges for " @@ -1297,13 +1404,7 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + 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 { - sUserManager.removePackageForAllUsers(ps.name); - } + removeDataDirsLI(ps.name); if (ps.codePath != null) { if (!ps.codePath.delete()) { Slog.w(TAG, "Unable to remove old code file: " + ps.codePath); @@ -1533,19 +1634,17 @@ public class PackageManagerService extends IPackageManager.Stub { PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { if (!sUserManager.exists(userId)) return null; PackageInfo pi; - if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - // The package has been uninstalled but has retained data and resources. - pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId); - } else { - final PackageSetting ps = (PackageSetting) p.mExtras; - if (ps == null) { - return null; - } - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - pi = PackageParser.generatePackageInfo(p, gp.gids, flags, - ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions, - ps.getStopped(userId), ps.getEnabled(userId), userId); - pi.applicationInfo.enabledSetting = ps.getEnabled(userId); + final PackageSetting ps = (PackageSetting) p.mExtras; + if (ps == null) { + return null; + } + final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + final PackageUserState state = ps.readUserState(userId); + pi = PackageParser.generatePackageInfo(p, gp.gids, flags, + ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions, + state, userId); + if (pi != null) { + pi.applicationInfo.enabledSetting = state.enabled; pi.applicationInfo.enabled = pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED; @@ -1556,6 +1655,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package info"); // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -1598,18 +1698,19 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public int getPackageUid(String packageName, int userId) { if (!sUserManager.exists(userId)) return -1; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package uid"); // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if(p != null) { - return UserId.getUid(userId, p.applicationInfo.uid); + return UserHandle.getUid(userId, p.applicationInfo.uid); } PackageSetting ps = mSettings.mPackages.get(packageName); if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { return -1; } p = ps.pkg; - return p != null ? UserId.getUid(userId, p.applicationInfo.uid) : -1; + return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1; } } @@ -1712,14 +1813,15 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { if (ps.pkg == null) { - PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId); + PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, + flags, userId); if (pInfo != null) { return pInfo.applicationInfo; } return null; } - return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateApplicationInfo(ps.pkg, flags, + ps.readUserState(userId), userId); } return null; } @@ -1729,20 +1831,23 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) return null; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { - PackageParser.Package pkg = new PackageParser.Package(packageName); - if (ps.pkg == null) { - ps.pkg = new PackageParser.Package(packageName); - ps.pkg.applicationInfo.packageName = packageName; - 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.packageName, 0).getPath(); - ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; - } - // ps.pkg.mSetEnabled = ps.getEnabled(userId); - // ps.pkg.mSetStopped = ps.getStopped(userId); - return generatePackageInfo(ps.pkg, flags, userId); + PackageParser.Package pkg = ps.pkg; + if (pkg == null) { + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { + return null; + } + pkg = new PackageParser.Package(packageName); + pkg.applicationInfo.packageName = packageName; + pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; + pkg.applicationInfo.publicSourceDir = ps.resourcePathString; + pkg.applicationInfo.sourceDir = ps.codePathString; + pkg.applicationInfo.dataDir = + getDataPathForPackage(packageName, 0).getPath(); + pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; + } + // pkg.mSetEnabled = ps.getEnabled(userId); + // pkg.mSetStopped = ps.getStopped(userId); + return generatePackageInfo(pkg, flags, userId); } return null; } @@ -1750,6 +1855,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get application info"); // writer synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -1760,13 +1866,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; // Note: isEnabledLP() does not apply here - always return info - return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId)); + return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId)); } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } - if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { return generateApplicationInfoFromSettingsLPw(packageName, flags, userId); } } @@ -1782,9 +1887,11 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + synchronized (mInstallLock) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } } if (observer != null) { try { @@ -1805,9 +1912,11 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + synchronized (mInstallLock) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } } if(pi != null) { try { @@ -1826,6 +1935,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get activity info"); synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -1833,8 +1943,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), + userId); } if (mResolveComponentName.equals(component)) { return mResolveActivity; @@ -1846,6 +1956,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mReceivers.mActivities.get(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -1853,8 +1964,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), + userId); } } return null; @@ -1863,6 +1974,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get service info"); synchronized (mPackages) { PackageParser.Service s = mServices.mServices.get(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -1870,8 +1982,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId), + userId); } } return null; @@ -1880,6 +1992,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mProvidersByComponent.get(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -1887,8 +2000,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId), + userId); } } return null; @@ -1933,7 +2046,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void checkValidCaller(int uid, int userId) { - if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) + if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return; throw new SecurityException("Caller uid=" + uid @@ -1962,7 +2075,7 @@ public class PackageManagerService extends IPackageManager.Stub { public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { - Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid)); + Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { GrantedPermissions gp = (GrantedPermissions)obj; if (gp.grantedPermissions.contains(permName)) { @@ -1981,6 +2094,34 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.PERMISSION_DENIED; } + /** + * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. + * @param message the message to log on security exception + * @return + */ + private void enforceCrossUserPermission(int callingUid, int userId, + boolean requireFullPermission, String message) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid userId " + userId); + } + if (userId == UserHandle.getUserId(callingUid)) return; + if (callingUid != Process.SYSTEM_UID && callingUid != 0) { + if (requireFullPermission) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } else { + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } catch (SecurityException se) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, message); + } + } + } + } + private BasePermission findPermissionTreeLP(String permName) { for(BasePermission bp : mSettings.mPermissionTrees.values()) { if (permName.startsWith(bp.name) && @@ -1996,7 +2137,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (permName != null) { BasePermission bp = findPermissionTreeLP(permName); if (bp != null) { - if (bp.uid == UserId.getAppId(Binder.getCallingUid())) { + if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) { return bp; } throw new SecurityException("Calling uid " @@ -2199,8 +2340,8 @@ public class PackageManagerService extends IPackageManager.Stub { public int checkUidSignatures(int uid1, int uid2) { // Map to base uids. - uid1 = UserId.getAppId(uid1); - uid2 = UserId.getAppId(uid2); + uid1 = UserHandle.getAppId(uid1); + uid2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; @@ -2258,7 +2399,7 @@ public class PackageManagerService extends IPackageManager.Stub { } public String[] getPackagesForUid(int uid) { - uid = UserId.getAppId(uid); + uid = UserHandle.getAppId(uid); // reader synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(uid); @@ -2283,7 +2424,7 @@ public class PackageManagerService extends IPackageManager.Stub { public String getNameForUid(int uid) { // reader synchronized (mPackages) { - Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid)); + Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.name + ":" + sus.userId; @@ -2313,6 +2454,7 @@ public class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "resolve intent"); List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId); return chooseBestActivity(intent, resolvedType, flags, query, userId); } @@ -2394,6 +2536,9 @@ public class PackageManagerService extends IPackageManager.Stub { final int M = prefs.size(); for (int i=0; i<M; i++) { final PreferredActivity pa = prefs.get(i); + if (pa.mUserId != userId) { + continue; + } if (pa.mPref.mMatch != match) { continue; } @@ -2451,6 +2596,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities"); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -2490,6 +2636,8 @@ public class PackageManagerService extends IPackageManager.Stub { Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, + "query intent activity options"); final String resultsAction = intent.getAction(); List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags @@ -2759,11 +2907,14 @@ public class PackageManagerService extends IPackageManager.Stub { return index; } - public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, String lastRead) { + @Override + public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, String lastRead, + int userId) { final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>(); final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; final String[] keys; - int userId = UserId.getCallingUserId(); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "get installed packages"); // writer synchronized (mPackages) { @@ -2838,8 +2989,8 @@ public class PackageManagerService extends IPackageManager.Stub { } else { final PackageParser.Package p = mPackages.get(packageName); if (p != null && ps != null) { - ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + ai = PackageParser.generateApplicationInfo(p, flags, + ps.readUserState(userId), userId); } } @@ -2862,17 +3013,20 @@ public class PackageManagerService extends IPackageManager.Stub { // reader synchronized (mPackages) { final Iterator<PackageParser.Package> i = mPackages.values().iterator(); - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo != null && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p))) { PackageSetting ps = mSettings.mPackages.get(p.packageName); - finalList.add(PackageParser.generateApplicationInfo(p, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + if (ps != null) { + ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, + ps.readUserState(userId), userId); + if (ai != null) { + finalList.add(ai); + } + } } } } @@ -2889,14 +3043,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = provider != null ? mSettings.mPackages.get(provider.owner.packageName) : null; - return provider != null + return ps != null && mSettings.isEnabledLPr(provider.info, flags, userId) && (!mSafeMode || (provider.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0) ? PackageParser.generateProviderInfo(provider, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId) + ps.readUserState(userId), userId) : null; } } @@ -2910,20 +3062,21 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet() .iterator(); - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { Map.Entry<String, PackageParser.Provider> entry = i.next(); PackageParser.Provider p = entry.getValue(); PackageSetting ps = mSettings.mPackages.get(p.owner.packageName); - if (p.syncable + if (ps != null && p.syncable && (!mSafeMode || (p.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0)) { - outNames.add(entry.getKey()); - outInfo.add(PackageParser.generateProviderInfo(p, 0, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + ProviderInfo info = PackageParser.generateProviderInfo(p, 0, + ps.readUserState(userId), userId); + if (info != null) { + outNames.add(entry.getKey()); + outInfo.add(info); + } } } } @@ -2937,24 +3090,25 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); final int userId = processName != null ? - UserId.getUserId(uid) : UserId.getCallingUserId(); + UserHandle.getUserId(uid) : UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Provider p = i.next(); PackageSetting ps = mSettings.mPackages.get(p.owner.packageName); - if (p.info.authority != null + if (ps != null && p.info.authority != null && (processName == null || (p.info.processName.equals(processName) - && UserId.isSameApp(p.info.applicationInfo.uid, uid))) + && UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) && mSettings.isEnabledLPr(p.info, flags, userId) && (!mSafeMode || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) { if (finalList == null) { finalList = new ArrayList<ProviderInfo>(3); } - finalList.add(PackageParser.generateProviderInfo(p, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + ProviderInfo info = PackageParser.generateProviderInfo(p, flags, + ps.readUserState(userId), userId); + if (info != null) { + finalList.add(info); + } } } } @@ -2987,8 +3141,11 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { - finalList.add(PackageParser.generateInstrumentationInfo(p, - flags)); + InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, + flags); + if (ii != null) { + finalList.add(ii); + } } } } @@ -3015,7 +3172,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } PackageParser.Package pkg = scanPackageLI(file, - flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); + flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null); // Don't mess around with apps in system partition. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { @@ -3083,7 +3240,7 @@ public class PackageManagerService extends IPackageManager.Stub { * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, - int parseFlags, int scanMode, long currentTime) { + int parseFlags, int scanMode, long currentTime, UserHandle user) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; @@ -3149,7 +3306,7 @@ public class PackageManagerService extends IPackageManager.Stub { InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); - synchronized (mInstaller) { + synchronized (mInstallLock) { args.cleanUpResourcesLI(); } synchronized (mPackages) { @@ -3183,7 +3340,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { - deletePackageLI(pkg.packageName, true, 0, null, false); + deletePackageLI(pkg.packageName, null, true, 0, null, false); ps = null; } else { /* @@ -3205,7 +3362,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); - synchronized (mInstaller) { + synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } @@ -3236,7 +3393,7 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode - | SCAN_UPDATE_SIGNATURE, currentTime); + | SCAN_UPDATE_SIGNATURE, currentTime, user); /* * If the system app should be overridden by a previously installed @@ -3378,8 +3535,8 @@ public class PackageManagerService extends IPackageManager.Stub { return DEX_OPT_DEFERRED; } else { Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - !isForwardLocked(pkg)); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg)); pkg.mDidDexOpt = true; performed = true; } @@ -3439,8 +3596,45 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private int createDataDirsLI(String packageName, int uid) { + int[] users = sUserManager.getUserIds(); + int res = mInstaller.install(packageName, uid, uid); + if (res < 0) { + return res; + } + for (int user : users) { + if (user != 0) { + res = mInstaller.createUserData(packageName, + UserHandle.getUid(user, uid), user); + if (res < 0) { + return res; + } + } + } + return res; + } + + private int removeDataDirsLI(String packageName) { + int[] users = sUserManager.getUserIds(); + int res = 0; + for (int user : users) { + int resInner = mInstaller.remove(packageName, user); + if (resInner < 0) { + res = resInner; + } + } + + final File nativeLibraryFile = new File(mAppLibInstallDir, packageName); + NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); + if (!nativeLibraryFile.delete()) { + Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath()); + } + + return res; + } + private PackageParser.Package scanPackageLI(PackageParser.Package pkg, - int parseFlags, int scanMode, long currentTime) { + int parseFlags, int scanMode, long currentTime, UserHandle user) { File scanFile = new File(pkg.mScanPath); if (scanFile == null || pkg.applicationInfo.sourceDir == null || pkg.applicationInfo.publicSourceDir == null) { @@ -3632,7 +3826,7 @@ public class PackageManagerService extends IPackageManager.Stub { // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, - pkg.applicationInfo.flags, true, false); + pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -3791,11 +3985,9 @@ public class PackageManagerService extends IPackageManager.Stub { || (scanMode&SCAN_BOOTING) != 0)) { // If this is a system app, we can at least delete its // current data so the application will still work. - int ret = mInstaller.remove(pkgName, 0); + int ret = removeDataDirsLI(pkgName); if (ret >= 0) { // TODO: Kill the processes first - // Remove the data directories for all users - sUserManager.removePackageForAllUsers(pkgName); // Old data gone! String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 ? "System package " : "Third party package "; @@ -3807,8 +3999,7 @@ public class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); + ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! msg = prefix + pkg.packageName @@ -3817,9 +4008,6 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - // Create data directories for all users - sUserManager.installPackageForAllUsers(pkgName, - pkg.applicationInfo.uid); } if (!recovered) { mHasSystemUidErrors = true; @@ -3857,15 +4045,12 @@ public class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation - int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); + int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - // Create data directories for all users - sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); @@ -3886,9 +4071,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) { if (pkgSetting.nativeLibraryPathString == null) { - final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath(); - pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath; - pkgSetting.nativeLibraryPathString = nativeLibraryPath; + setInternalAppNativeLibraryPath(pkg, pkgSetting); } else { pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString; } @@ -3910,7 +4093,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ if (pkg.applicationInfo.nativeLibraryDir != null) { try { - final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); + File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { @@ -3925,37 +4108,43 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "removed obsolete native libraries for system package " + path); } - } else if (nativeLibraryDir.getParentFile().getCanonicalPath() - .equals(dataPathString)) { - /* - * Make sure the native library dir isn't a symlink to - * something. If it is, ask installd to remove it and create - * a directory so we can copy to it afterwards. - */ - boolean isSymLink; - try { - isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode); - } catch (ErrnoException e) { - // This shouldn't happen, but we'll fail-safe. - isSymLink = true; + } else if (!isForwardLocked(pkg) && !isExternal(pkg)) { + // Update native library dir if it starts with /data/data + if (nativeLibraryDir.getPath().startsWith(dataPathString)) { + setInternalAppNativeLibraryPath(pkg, pkgSetting); + nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); } - if (isSymLink) { - mInstaller.unlinkNativeLibraryDirectory(dataPathString); + + try { + if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) { + Slog.e(TAG, "Unable to copy native libraries"); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } + } catch (IOException e) { + Slog.e(TAG, "Unable to copy native libraries", e); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; } - /* - * If this is an internal application or our - * nativeLibraryPath points to our data directory, unpack - * the libraries if necessary. - */ - NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); + if (mInstaller.linkNativeLibraryDirectory(dataPathString, + pkg.applicationInfo.nativeLibraryDir) == -1) { + Slog.e(TAG, "Unable to link native library directory"); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } } else { Slog.i(TAG, "Linking native library dir for " + path); - mInstaller.linkNativeLibraryDirectory(dataPathString, + int ret = mInstaller.linkNativeLibraryDirectory(dataPathString, pkg.applicationInfo.nativeLibraryDir); + if (ret < 0) { + Slog.w(TAG, "Failed linking native library dir for " + path); + mLastScanError = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; + return null; + } } } catch (IOException ioe) { - Log.e(TAG, "Unable to get canonical file " + ioe.toString()); + Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); } } pkg.mScanPath = path; @@ -3992,7 +4181,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); // Make sure we don't accidentally delete its data. - mSettings.mPackagesToBeCleaned.remove(pkgName); + for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) { + mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName); + } // Take care of first install / last update times. if (currentTime != 0) { @@ -4268,20 +4459,75 @@ public class PackageManagerService extends IPackageManager.Stub { return pkg; } - private void killApplication(String pkgName, int uid) { + private void setInternalAppNativeLibraryPath(PackageParser.Package pkg, + PackageSetting pkgSetting) { + final String apkLibPath = getApkName(pkgSetting.codePathString); + final String nativeLibraryPath = new File(mAppLibInstallDir, apkLibPath).getPath(); + pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath; + pkgSetting.nativeLibraryPathString = nativeLibraryPath; + } + + private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir) + throws IOException { + if (!nativeLibraryDir.isDirectory()) { + nativeLibraryDir.delete(); + + if (!nativeLibraryDir.mkdir()) { + throw new IOException("Cannot create " + nativeLibraryDir.getPath()); + } + + try { + Libcore.os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH + | S_IXOTH); + } catch (ErrnoException e) { + throw new IOException("Cannot chmod native library directory " + + nativeLibraryDir.getPath(), e); + } + } else if (!SELinux.restorecon(nativeLibraryDir)) { + throw new IOException("Cannot set SELinux context for " + nativeLibraryDir.getPath()); + } + + /* + * If this is an internal application or our nativeLibraryPath points to + * the app-lib directory, unpack the libraries if necessary. + */ + return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); + } + + private void killApplication(String pkgName, int appId) { // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { - am.killApplicationWithUid(pkgName, uid); + am.killApplicationWithAppId(pkgName, appId); } catch (RemoteException e) { } } } - void removePackageLI(PackageParser.Package pkg, boolean chatty) { + void removePackageLI(PackageSetting ps, boolean chatty) { + if (DEBUG_INSTALL) { + if (chatty) + Log.d(TAG, "Removing package " + ps.name); + } + + // writer + synchronized (mPackages) { + mPackages.remove(ps.name); + if (ps.codePathString != null) { + mAppDirs.remove(ps.codePathString); + } + + final PackageParser.Package pkg = ps.pkg; + if (pkg != null) { + cleanPackageDataStructuresLILPw(pkg, chatty); + } + } + } + + void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) { if (DEBUG_INSTALL) { if (chatty) Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName); @@ -4293,143 +4539,146 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkg.mPath != null) { mAppDirs.remove(pkg.mPath); } + cleanPackageDataStructuresLILPw(pkg, chatty); + } + } - int N = pkg.providers.size(); - StringBuilder r = null; - int i; - for (i=0; i<N; i++) { - PackageParser.Provider p = pkg.providers.get(i); - mProvidersByComponent.remove(new ComponentName(p.info.packageName, - p.info.name)); - if (p.info.authority == null) { - - /* The is another ContentProvider with this authority when - * this app was installed so this authority is null, - * Ignore it as we don't have to unregister the provider. - */ - continue; - } - String names[] = p.info.authority.split(";"); - for (int j = 0; j < names.length; j++) { - if (mProviders.get(names[j]) == p) { - mProviders.remove(names[j]); - if (DEBUG_REMOVE) { - if (chatty) - Log.d(TAG, "Unregistered content provider: " + names[j] - + ", className = " + p.info.name + ", isSyncable = " - + p.info.isSyncable); - } - } - } - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(p.info.name); - } - } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r); + void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) { + int N = pkg.providers.size(); + StringBuilder r = null; + int i; + for (i=0; i<N; i++) { + PackageParser.Provider p = pkg.providers.get(i); + mProvidersByComponent.remove(new ComponentName(p.info.packageName, + p.info.name)); + if (p.info.authority == null) { + + /* There was another ContentProvider with this authority when + * this app was installed so this authority is null, + * Ignore it as we don't have to unregister the provider. + */ + continue; } - - N = pkg.services.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Service s = pkg.services.get(i); - mServices.removeService(s); - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); + String names[] = p.info.authority.split(";"); + for (int j = 0; j < names.length; j++) { + if (mProviders.get(names[j]) == p) { + mProviders.remove(names[j]); + if (DEBUG_REMOVE) { + if (chatty) + Log.d(TAG, "Unregistered content provider: " + names[j] + + ", className = " + p.info.name + ", isSyncable = " + + p.info.isSyncable); } - r.append(s.info.name); } } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); } + } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r); + } - N = pkg.receivers.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Activity a = pkg.receivers.get(i); - mReceivers.removeActivity(a, "receiver"); - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(a.info.name); + N = pkg.services.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Service s = pkg.services.get(i); + mServices.removeService(s); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); } + r.append(s.info.name); } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r); - } + } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r); + } - N = pkg.activities.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Activity a = pkg.activities.get(i); - mActivities.removeActivity(a, "activity"); - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(a.info.name); + N = pkg.receivers.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.receivers.get(i); + mReceivers.removeActivity(a, "receiver"); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); } + r.append(a.info.name); } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r); - } + } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r); + } - N = pkg.permissions.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Permission p = pkg.permissions.get(i); - BasePermission bp = mSettings.mPermissions.get(p.info.name); - if (bp == null) { - bp = mSettings.mPermissionTrees.get(p.info.name); - } - if (bp != null && bp.perm == p) { - bp.perm = null; - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(p.info.name); - } + N = pkg.activities.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.activities.get(i); + mActivities.removeActivity(a, "activity"); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); } + r.append(a.info.name); } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); - } + } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r); + } - N = pkg.instrumentation.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Instrumentation a = pkg.instrumentation.get(i); - mInstrumentation.remove(a.getComponentName()); + N = pkg.permissions.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Permission p = pkg.permissions.get(i); + BasePermission bp = mSettings.mPermissions.get(p.info.name); + if (bp == null) { + bp = mSettings.mPermissionTrees.get(p.info.name); + } + if (bp != null && bp.perm == p) { + bp.perm = null; if (chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } - r.append(a.info.name); + r.append(p.info.name); } } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); + } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); + } + + N = pkg.instrumentation.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Instrumentation a = pkg.instrumentation.get(i); + mInstrumentation.remove(a.getComponentName()); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); } } + if (r != null) { + if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); + } } private static final boolean isPackageFilename(String name) { @@ -4716,14 +4965,17 @@ public class PackageManagerService extends IPackageManager.Stub { mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; final int N = packageActivities.size(); - ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut = - new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N); + ArrayList<PackageParser.ActivityIntentInfo[]> listCut = + new ArrayList<PackageParser.ActivityIntentInfo[]>(N); ArrayList<PackageParser.ActivityIntentInfo> intentFilters; for (int i = 0; i < N; ++i) { intentFilters = packageActivities.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { - listCut.add(intentFilters); + PackageParser.ActivityIntentInfo[] array = + new PackageParser.ActivityIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); @@ -4791,6 +5043,11 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + protected ActivityIntentInfo[] newArray(int size) { + return new ActivityIntentInfo[size]; + } + + @Override protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.activity.owner; @@ -4800,7 +5057,8 @@ public class PackageManagerService extends IPackageManager.Stub { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. - return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0; + return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 + && ps.getStopped(userId); } } return false; @@ -4823,12 +5081,17 @@ public class PackageManagerService extends IPackageManager.Stub { &ApplicationInfo.FLAG_SYSTEM) == 0) { return null; } - final ResolveInfo res = new ResolveInfo(); PackageSetting ps = (PackageSetting) activity.owner.mExtras; - res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId); + if (ps == null) { + return null; + } + ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags, + ps.readUserState(userId), userId); + if (ai == null) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.activityInfo = ai; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = info; } @@ -4904,14 +5167,17 @@ public class PackageManagerService extends IPackageManager.Stub { mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; final int N = packageServices.size(); - ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = - new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); + ArrayList<PackageParser.ServiceIntentInfo[]> listCut = + new ArrayList<PackageParser.ServiceIntentInfo[]>(N); ArrayList<PackageParser.ServiceIntentInfo> intentFilters; for (int i = 0; i < N; ++i) { intentFilters = packageServices.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { - listCut.add(intentFilters); + PackageParser.ServiceIntentInfo[] array = + new PackageParser.ServiceIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); @@ -4974,6 +5240,11 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + protected PackageParser.ServiceIntentInfo[] newArray(int size) { + return new PackageParser.ServiceIntentInfo[size]; + } + + @Override protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.service.owner; @@ -4983,8 +5254,8 @@ public class PackageManagerService extends IPackageManager.Stub { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. - return ps.getStopped(userId) - && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0; + return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0 + && ps.getStopped(userId); } } return false; @@ -5008,12 +5279,17 @@ public class PackageManagerService extends IPackageManager.Stub { &ApplicationInfo.FLAG_SYSTEM) == 0) { return null; } - final ResolveInfo res = new ResolveInfo(); PackageSetting ps = (PackageSetting) service.owner.mExtras; - res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId); + if (ps == null) { + return null; + } + ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags, + ps.readUserState(userId), userId); + if (si == null) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.serviceInfo = si; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = filter; } @@ -5104,13 +5380,14 @@ public class PackageManagerService extends IPackageManager.Stub { }; static final void sendPackageBroadcast(String action, String pkg, - Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) { + Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, + int[] userIds) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { - int[] userIds = userId == UserId.USER_ALL - ? sUserManager.getUserIds() - : new int[] {userId}; + if (userIds == null) { + userIds = sUserManager.getUserIds(); + } for (int id : userIds) { final Intent intent = new Intent(action, pkg != null ? Uri.fromParts("package", pkg, null) : null); @@ -5122,11 +5399,18 @@ public class PackageManagerService extends IPackageManager.Stub { } // Modify the UID when posting to other users int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (uid > 0 && id > 0) { - uid = UserId.getUid(id, UserId.getAppId(uid)); + if (uid > 0 && UserHandle.getUserId(uid) != id) { + uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); intent.putExtra(Intent.EXTRA_UID, uid); } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (DEBUG_BROADCASTS) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Sending to user " + id + ": " + + intent.toShortString(false, true, false, false) + + " " + intent.getExtras(), here); + } am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, finishedReceiver != null, false, id); } @@ -5144,8 +5428,9 @@ public class PackageManagerService extends IPackageManager.Stub { return mMediaMounted || Environment.isExternalStorageEmulated(); } - public String nextPackageToClean(String lastPackage) { + public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) { // writer + final int userId = UserHandle.getCallingUserId(); synchronized (mPackages) { if (!isExternalMediaAvailable()) { // If the external storage is no longer mounted at this point, @@ -5153,34 +5438,66 @@ public class PackageManagerService extends IPackageManager.Stub { // packages files and can not delete any more. Bail. return null; } - if (lastPackage != null) { - mSettings.mPackagesToBeCleaned.remove(lastPackage); + ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId); + if (pkgs != null) { + if (lastPackage != null) { + pkgs.remove(lastPackage); + } + if (pkgs.size() > 0) { + return pkgs.get(0); + } } - return mSettings.mPackagesToBeCleaned.size() > 0 - ? mSettings.mPackagesToBeCleaned.get(0) : null; + mSettings.mPackagesToBeCleaned.remove(userId); + } + // Move on to the next user to clean. + long ident = Binder.clearCallingIdentity(); + try { + startCleaningPackages(userId); + } finally { + Binder.restoreCallingIdentity(ident); } + return null; } - void schedulePackageCleaning(String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName)); + void schedulePackageCleaning(String packageName, int userId, boolean andCode) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId + + " andCode=" + andCode, here); + } + mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, + userId, andCode ? 1 : 0, packageName)); } - void startCleaningPackages() { + void startCleaningPackages(int lastUser) { // reader + int nextUser = -1; synchronized (mPackages) { if (!isExternalMediaAvailable()) { return; } - if (mSettings.mPackagesToBeCleaned.size() <= 0) { + final int N = mSettings.mPackagesToBeCleaned.size(); + if (N <= 0) { return; } + for (int i=0; i<N; i++) { + int user = mSettings.mPackagesToBeCleaned.keyAt(i); + if (user > lastUser) { + nextUser = user; + break; + } + } + if (nextUser < 0) { + nextUser = mSettings.mPackagesToBeCleaned.keyAt(0); + } } Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE); intent.setComponent(DEFAULT_CONTAINER_COMPONENT); IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { - am.startService(null, intent, null); + am.startService(null, intent, null, nextUser); } catch (RemoteException e) { } } @@ -5195,9 +5512,11 @@ public class PackageManagerService extends IPackageManager.Stub { public void onEvent(int event, String path) { String removedPackage = null; - int removedUid = -1; + int removedAppId = -1; + int[] removedUsers = null; String addedPackage = null; - int addedUid = -1; + int addedAppId = -1; + int[] addedUsers = null; // TODO post a message to the handler to obtain serial ordering synchronized (mInstallLock) { @@ -5223,15 +5542,25 @@ public class PackageManagerService extends IPackageManager.Stub { return; } PackageParser.Package p = null; + PackageSetting ps = null; // reader synchronized (mPackages) { p = mAppDirs.get(fullPathStr); + if (p != null) { + ps = mSettings.mPackages.get(p.applicationInfo.packageName); + if (ps != null) { + removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); + } else { + removedUsers = sUserManager.getUserIds(); + } + } + addedUsers = sUserManager.getUserIds(); } if ((event&REMOVE_EVENTS) != 0) { - if (p != null) { - removePackageLI(p, true); - removedPackage = p.applicationInfo.packageName; - removedUid = p.applicationInfo.uid; + if (ps != null) { + removePackageLI(ps, true); + removedPackage = ps.name; + removedAppId = ps.appId; } } @@ -5243,7 +5572,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, - System.currentTimeMillis()); + System.currentTimeMillis(), UserHandle.ALL); if (p != null) { /* * TODO this seems dangerous as the package may have @@ -5256,7 +5585,7 @@ public class PackageManagerService extends IPackageManager.Stub { p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0); } addedPackage = p.applicationInfo.packageName; - addedUid = p.applicationInfo.uid; + addedAppId = UserHandle.getAppId(p.applicationInfo.uid); } } } @@ -5269,16 +5598,16 @@ public class PackageManagerService extends IPackageManager.Stub { if (removedPackage != null) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putInt(Intent.EXTRA_UID, removedAppId); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, removedUsers); } if (addedPackage != null) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, addedUid); + extras.putInt(Intent.EXTRA_UID, addedAppId); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, addedUsers); } } @@ -5304,9 +5633,25 @@ public class PackageManagerService extends IPackageManager.Stub { public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); + VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, + manifestDigest); + installPackageWithVerificationAndEncryption(packageURI, observer, flags, + installerPackageName, verificationParams, encryptionParams); + } + + public void installPackageWithVerificationAndEncryption(Uri packageURI, + IPackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, + null); final int uid = Binder.getCallingUid(); + UserHandle user; + if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) { + user = UserHandle.ALL; + } else { + user = new UserHandle(UserHandle.getUserId(uid)); + } final int filteredFlags; @@ -5319,14 +5664,61 @@ public class PackageManagerService extends IPackageManager.Stub { filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB; } + verificationParams.setInstallerUid(uid); + final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName, - verificationURI, manifestDigest, encryptionParams); + verificationParams, encryptionParams, user); mHandler.sendMessage(msg); } + /** + * @hide + */ + @Override + public int installExistingPackage(String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, + null); + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + + long callingId = Binder.clearCallingIdentity(); + try { + boolean sendAdded = false; + Bundle extras = new Bundle(1); + + // writer + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + return PackageManager.INSTALL_FAILED_INVALID_URI; + } + if (!pkgSetting.getInstalled(userId)) { + pkgSetting.setInstalled(true, userId); + mSettings.writePackageRestrictionsLPr(userId); + extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId)); + sendAdded = true; + } + } + + if (sendAdded) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + packageName, extras, null, null, new int[] {userId}); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + + return PackageManager.INSTALL_SUCCEEDED; + } + @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can verify applications"); + final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED); final PackageVerificationResponse response = new PackageVerificationResponse( verificationCode, Binder.getCallingUid()); @@ -5335,6 +5727,49 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } + @Override + public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, + long millisecondsToDelay) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can extend verification timeouts"); + + final PackageVerificationState state = mPendingVerification.get(id); + final PackageVerificationResponse response = new PackageVerificationResponse( + verificationCodeAtTimeout, Binder.getCallingUid()); + + if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) { + millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT; + } + if (millisecondsToDelay < 0) { + millisecondsToDelay = 0; + } + if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW) + && (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) { + verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT; + } + + if ((state != null) && !state.timeoutExtended()) { + state.extendTimeout(); + + final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED); + msg.arg1 = id; + msg.obj = response; + mHandler.sendMessageDelayed(msg, millisecondsToDelay); + } + } + + private void broadcastPackageVerified(int verificationId, Uri packageUri, + int verificationCode) { + final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); + intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); + intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode); + + mContext.sendBroadcast(intent, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT); + } + private ComponentName matchComponentForVerifier(String packageName, List<ResolveInfo> receivers) { ActivityInfo targetReceiver = null; @@ -5447,20 +5882,45 @@ public class PackageManagerService extends IPackageManager.Stub { * @return verification timeout in milliseconds */ private long getVerificationTimeout() { - return android.provider.Settings.Secure.getLong(mContext.getContentResolver(), - android.provider.Settings.Secure.PACKAGE_VERIFIER_TIMEOUT, + return android.provider.Settings.Global.getLong(mContext.getContentResolver(), + android.provider.Settings.Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT); } /** + * Get the default verification agent response code. + * + * @return default verification response code + */ + private int getDefaultVerificationResponse() { + return android.provider.Settings.Global.getInt(mContext.getContentResolver(), + android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE, + DEFAULT_VERIFICATION_RESPONSE); + } + + /** * Check whether or not package verification has been enabled. * * @return true if verification should be performed */ private boolean isVerificationEnabled() { + if (!DEFAULT_VERIFY_ENABLE) { + return false; + } + + return android.provider.Settings.Global.getInt(mContext.getContentResolver(), + android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1; + } + + /** + * Get the "allow unknown sources" setting. + * + * @return the current "allow unknown sources" setting + */ + private int getUnknownSourcesSettings() { return android.provider.Settings.Secure.getInt(mContext.getContentResolver(), - android.provider.Settings.Secure.PACKAGE_VERIFIER_ENABLE, - DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false; + android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, + -1); } public void setInstallerPackageName(String targetPackage, String installerPackageName) { @@ -5615,6 +6075,17 @@ public class PackageManagerService extends IPackageManager.Stub { */ private int mRetries = 0; + /** User handle for the user requesting the information or installation. */ + private final UserHandle mUser; + + HandlerParams(UserHandle user) { + mUser = user; + } + + UserHandle getUser() { + return mUser; + } + final boolean startCopy() { boolean res; try { @@ -5656,6 +6127,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final IPackageStatsObserver mObserver; public MeasureParams(PackageStats stats, IPackageStatsObserver observer) { + super(new UserHandle(stats.userHandle)); mObserver = observer; mStats = stats; } @@ -5663,7 +6135,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override void handleStartCopy() throws RemoteException { synchronized (mInstallLock) { - mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats); + mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } final boolean mounted; @@ -5671,19 +6143,20 @@ public class PackageManagerService extends IPackageManager.Stub { mounted = true; } else { final String status = Environment.getExternalStorageState(); - - mounted = status.equals(Environment.MEDIA_MOUNTED) - || status.equals(Environment.MEDIA_MOUNTED_READ_ONLY); + mounted = (Environment.MEDIA_MOUNTED.equals(status) + || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { - final File externalCacheDir = Environment + final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle); + + final File externalCacheDir = userEnv .getExternalStorageAppCacheDirectory(mStats.packageName); final long externalCacheSize = mContainerService .calculateDirectorySize(externalCacheDir.getPath()); mStats.externalCacheSize = externalCacheSize; - final File externalDataDir = Environment + final File externalDataDir = userEnv .getExternalStorageAppDataDirectory(mStats.packageName); long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir .getPath()); @@ -5693,12 +6166,12 @@ public class PackageManagerService extends IPackageManager.Stub { } mStats.externalDataSize = externalDataSize; - final File externalMediaDir = Environment + final File externalMediaDir = userEnv .getExternalStorageAppMediaDirectory(mStats.packageName); mStats.externalMediaSize = mContainerService .calculateDirectorySize(externalMediaDir.getPath()); - final File externalObbDir = Environment + final File externalObbDir = userEnv .getExternalStorageAppObbDirectory(mStats.packageName); mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir .getPath()); @@ -5729,8 +6202,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final Uri mPackageURI; final String installerPackageName; - final Uri verificationURI; - final ManifestDigest manifestDigest; + final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; private File mTempPackage; @@ -5738,17 +6210,24 @@ public class PackageManagerService extends IPackageManager.Stub { InstallParams(Uri packageURI, IPackageInstallObserver observer, int flags, - String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, - ContainerEncryptionParams encryptionParams) { + String installerPackageName, VerificationParams verificationParams, + ContainerEncryptionParams encryptionParams, UserHandle user) { + super(user); this.mPackageURI = packageURI; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; - this.verificationURI = verificationURI; - this.manifestDigest = manifestDigest; + this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; } + public ManifestDigest getManifestDigest() { + if (verificationParams == null) { + return null; + } + return verificationParams.getManifestDigest(); + } + private int installLocationPolicy(PackageInfoLite pkgLite, int flags) { String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; @@ -5758,6 +6237,16 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + // Check for downgrading. + if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) { + if (pkgLite.versionCode < pkg.mVersionCode) { + Slog.w(TAG, "Can't install update of " + packageName + + " update version " + pkgLite.versionCode + + " is older than installed version " + + pkg.mVersionCode); + return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; + } + } // Check for updated system application. if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (onSd) { @@ -5850,7 +6339,8 @@ public class PackageManagerService extends IPackageManager.Stub { packageFile = mTempPackage; FileUtils.setPermissions(packageFile.getAbsolutePath(), - FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IROTH, + FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP + | FileUtils.S_IROTH, -1, -1); } else { packageFile = null; @@ -5884,6 +6374,8 @@ public class PackageManagerService extends IPackageManager.Stub { ret = PackageManager.INSTALL_FAILED_INVALID_URI; } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; + } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { + ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; } else { // Override with defaults if needed. loc = installLocationPolicy(pkgLite, flags); @@ -5919,8 +6411,9 @@ public class PackageManagerService extends IPackageManager.Stub { verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - final List<ResolveInfo> receivers = queryIntentReceivers(verification, null, - PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */); + final List<ResolveInfo> receivers = queryIntentReceivers(verification, + PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS, + 0 /* TODO: Which userId? */); if (DEBUG_VERIFY) { Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " @@ -5937,9 +6430,29 @@ public class PackageManagerService extends IPackageManager.Stub { verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, flags); - if (verificationURI != null) { - verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI, - verificationURI); + verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, + pkgLite.packageName); + + verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE, + pkgLite.versionCode); + + if (verificationParams != null) { + if (verificationParams.getVerificationURI() != null) { + verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI, + verificationParams.getVerificationURI()); + } + if (verificationParams.getOriginatingURI() != null) { + verification.putExtra(Intent.EXTRA_ORIGINATING_URI, + verificationParams.getOriginatingURI()); + } + if (verificationParams.getReferrer() != null) { + verification.putExtra(Intent.EXTRA_REFERRER, + verificationParams.getReferrer()); + } + if (verificationParams.getInstallerUid() >= 0) { + verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, + verificationParams.getInstallerUid()); + } } final PackageVerificationState verificationState = new PackageVerificationState( @@ -6018,12 +6531,12 @@ public class PackageManagerService extends IPackageManager.Stub { // will succeed. if (mArgs != null) { processPendingInstall(mArgs, mRet); - } - if (mTempPackage != null) { - if (!mTempPackage.delete()) { - Slog.w(TAG, "Couldn't delete temporary file: " - + mTempPackage.getAbsolutePath()); + if (mTempPackage != null) { + if (!mTempPackage.delete()) { + Slog.w(TAG, "Couldn't delete temporary file: " + + mTempPackage.getAbsolutePath()); + } } } } @@ -6064,7 +6577,8 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String dataDir, int uid) { + String packageName, String dataDir, int uid, UserHandle user) { + super(user); this.srcArgs = srcArgs; this.observer = observer; this.flags = flags; @@ -6217,14 +6731,17 @@ public class PackageManagerService extends IPackageManager.Stub { final Uri packageURI; final String installerPackageName; final ManifestDigest manifestDigest; + final UserHandle user; InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags, - String installerPackageName, ManifestDigest manifestDigest) { + String installerPackageName, ManifestDigest manifestDigest, + UserHandle user) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; this.manifestDigest = manifestDigest; + this.user = user; } abstract void createCopyFile(); @@ -6275,11 +6792,12 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, - params.installerPackageName, params.manifestDigest); + params.installerPackageName, params.getManifestDigest(), + params.getUser()); } FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) { - super(null, null, 0, null, null); + super(null, null, 0, null, null, null); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -6288,12 +6806,12 @@ public class PackageManagerService extends IPackageManager.Stub { } FileInstallArgs(Uri packageURI, String pkgName, String dataDir) { - super(packageURI, null, 0, null, null); + super(packageURI, null, 0, null, null, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); resourceFileName = getResourcePathFromCodePath(); - libraryPath = new File(dataDir, LIB_DIR_NAME).getPath(); + libraryPath = new File(mAppLibInstallDir, pkgName).getPath(); } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { @@ -6330,6 +6848,7 @@ public class PackageManagerService extends IPackageManager.Stub { installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; codeFileName = createTempPackageFile(installDir).getPath(); resourceFileName = getResourcePathFromCodePath(); + libraryPath = getLibraryPathFromCodePath(); created = true; } @@ -6384,6 +6903,23 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } } + + final File nativeLibraryFile = new File(getNativeLibraryPath()); + Slog.i(TAG, "Copying native libraries to " + nativeLibraryFile.getPath()); + if (nativeLibraryFile.exists()) { + NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); + nativeLibraryFile.delete(); + } + try { + int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile); + if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + return copyRet; + } + } catch (IOException e) { + Slog.e(TAG, "Copying native libraries failed", e); + ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + return ret; } @@ -6401,6 +6937,7 @@ public class PackageManagerService extends IPackageManager.Stub { } else { final File oldCodeFile = new File(getCodePath()); final File oldResourceFile = new File(getResourcePath()); + final File oldLibraryFile = new File(getNativeLibraryPath()); // Rename APK file based on packageName final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk"); @@ -6415,7 +6952,20 @@ public class PackageManagerService extends IPackageManager.Stub { if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) { return false; } - resourceFileName = getResourcePathFromCodePath(); + resourceFileName = newResFile.getPath(); + + // Rename library path + final File newLibraryFile = new File(getLibraryPathFromCodePath()); + if (newLibraryFile.exists()) { + NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile); + newLibraryFile.delete(); + } + if (!oldLibraryFile.renameTo(newLibraryFile)) { + Slog.e(TAG, "Cannot rename native library directory " + + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath()); + return false; + } + libraryPath = newLibraryFile.getPath(); // Attempt to set permissions if (!setPermissions()) { @@ -6466,8 +7016,15 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private String getLibraryPathFromCodePath() { + return new File(mAppLibInstallDir, getApkName(getCodePath())).getPath(); + } + @Override String getNativeLibraryPath() { + if (libraryPath == null) { + libraryPath = getLibraryPathFromCodePath(); + } return libraryPath; } @@ -6493,6 +7050,15 @@ public class PackageManagerService extends IPackageManager.Stub { publicSourceFile.delete(); } } + + if (libraryPath != null) { + File nativeLibraryFile = new File(libraryPath); + NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); + if (!nativeLibraryFile.delete()) { + Slog.w(TAG, "Couldn't delete native library directory " + libraryPath); + } + } + return ret; } @@ -6562,13 +7128,15 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, - params.installerPackageName, params.manifestDigest); + params.installerPackageName, params.getManifestDigest(), + params.getUser()); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, boolean isExternal, boolean isForwardLocked) { super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -6579,14 +7147,16 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String cid, boolean isForwardLocked) { super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) { super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); this.cid = cid; } @@ -6735,7 +7305,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int groupOwner; final String protectedFile; if (isFwdLocked()) { - groupOwner = uid; + groupOwner = UserHandle.getSharedAppGid(uid); protectedFile = RES_FILE_NAME; } else { groupOwner = -1; @@ -6817,7 +7387,8 @@ public class PackageManagerService extends IPackageManager.Stub { int doPostCopy(int uid) { if (isFwdLocked()) { if (uid < Process.FIRST_APPLICATION_UID - || !PackageHelper.fixSdPermissions(cid, uid, RES_FILE_NAME)) { + || !PackageHelper.fixSdPermissions(cid, UserHandle.getSharedAppGid(uid), + RES_FILE_NAME)) { Slog.e(TAG, "Failed to finalize " + cid); PackageHelper.destroySdDir(cid); return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; @@ -6910,6 +7481,10 @@ public class PackageManagerService extends IPackageManager.Stub { class PackageInstalledInfo { String name; int uid; + // The set of users that originally had this package installed. + int[] origUsers; + // The set of users that now have this package installed. + int[] newUsers; PackageParser.Package pkg; int returnCode; PackageRemovedInfo removedInfo; @@ -6919,14 +7494,12 @@ public class PackageManagerService extends IPackageManager.Stub { * Install a non-existing package. */ private void installNewPackageLI(PackageParser.Package pkg, - int parseFlags, - int scanMode, + int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists(); - res.name = pkgName; synchronized(mPackages) { if (mSettings.mRenamedPackages.containsKey(pkgName)) { // A package with the same name is already installed, though @@ -6949,7 +7522,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, - System.currentTimeMillis()); + System.currentTimeMillis(), user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -6966,17 +7539,15 @@ public class PackageManagerService extends IPackageManager.Stub { // delete the package data and cache directories that it created in // scanPackageLocked, unless those directories existed before we even tried to // install. - deletePackageLI( - pkgName, false, - dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, + deletePackageLI(pkgName, UserHandle.ALL, false, + dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0, res.removedInfo, true); } } } private void replacePackageLI(PackageParser.Package pkg, - int parseFlags, - int scanMode, + int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package oldPackage; @@ -6993,15 +7564,16 @@ public class PackageManagerService extends IPackageManager.Stub { } boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { - replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); + replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, + user, installerPackageName, res); } else { - replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); + replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, + user, installerPackageName, res); } } private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, - PackageParser.Package pkg, - int parseFlags, int scanMode, + PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; String pkgName = deletedPackage.packageName; @@ -7016,7 +7588,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // First delete the existing package while retaining the data directory - if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA, + if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA, res.removedInfo, true)) { // If the existing package wasn't successfully deleted res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; @@ -7025,7 +7597,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully deleted the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME, - System.currentTimeMillis()); + System.currentTimeMillis(), user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -7046,8 +7618,8 @@ public class PackageManagerService extends IPackageManager.Stub { // install. if(updatedSettings) { deletePackageLI( - pkgName, true, - PackageManager.DONT_DELETE_DATA, + pkgName, null, true, + PackageManager.DELETE_KEEP_DATA, res.removedInfo, true); } // Since we failed to install the new package we need to restore the old @@ -7062,7 +7634,7 @@ public class PackageManagerService extends IPackageManager.Stub { int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode, - origUpdateTime) == null) { + origUpdateTime, null) == null) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } @@ -7080,8 +7652,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void replaceSystemPackageLI(PackageParser.Package deletedPackage, - PackageParser.Package pkg, - int parseFlags, int scanMode, + PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; boolean updatedSettings = false; @@ -7111,7 +7682,7 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.uid = oldPkg.applicationInfo.uid; res.removedInfo.removedPackage = packageName; // Remove existing system package - removePackageLI(oldPkg, true); + removePackageLI(oldPkgSetting, true); // writer synchronized (mPackages) { if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) { @@ -7130,7 +7701,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully disabled the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0); + newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -7150,10 +7721,10 @@ public class PackageManagerService extends IPackageManager.Stub { // Re installation failed. Restore old information // Remove new pkg information if (newPackage != null) { - removePackageLI(newPackage, true); + removeInstalledPackageLI(newPackage, true); } // Add back the old system package - scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0); + scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user); // Restore the old system information in Settings synchronized(mPackages) { if (updatedSettings) { @@ -7309,6 +7880,7 @@ public class PackageManagerService extends IPackageManager.Stub { systemApp = (ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } @@ -7327,11 +7899,17 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { - replacePackageLI(pkg, parseFlags, scanMode, + replacePackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res); } else { - installNewPackageLI(pkg, parseFlags, scanMode, - installerPackageName,res); + installNewPackageLI(pkg, parseFlags, scanMode, args.user, + installerPackageName, res); + } + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); + } } } @@ -7380,17 +7958,23 @@ public class PackageManagerService extends IPackageManager.Stub { } private void deleteTempPackageFiles() { - FilenameFilter filter = new FilenameFilter() { + final FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { return name.startsWith("vmdl") && name.endsWith(".tmp"); } }; - String tmpFilesList[] = mAppInstallDir.list(filter); - if(tmpFilesList == null) { + deleteTempPackageFilesInDirectory(mAppInstallDir, filter); + deleteTempPackageFilesInDirectory(mDrmAppPrivateInstallDir, filter); + } + + private static final void deleteTempPackageFilesInDirectory(File directory, + FilenameFilter filter) { + final String[] tmpFilesList = directory.list(filter); + if (tmpFilesList == null) { return; } - for(int i = 0; i < tmpFilesList.length; i++) { - File tmpFile = new File(mAppInstallDir, tmpFilesList[i]); + for (int i = 0; i < tmpFilesList.length; i++) { + final File tmpFile = new File(directory, tmpFilesList[i]); tmpFile.delete(); } } @@ -7423,10 +8007,11 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); // Queue up an async operation since the package deletion may take a little while. + final int uid = Binder.getCallingUid(); mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - final int returnCode = deletePackageX(packageName, true, true, flags); + final int returnCode = deletePackageX(packageName, uid, flags); if (observer != null) { try { observer.packageDeleted(packageName, returnCode); @@ -7452,8 +8037,7 @@ public class PackageManagerService extends IPackageManager.Stub { * persisting settings for later use * sending a broadcast if necessary */ - private int deletePackageX(String packageName, boolean sendBroadCast, - boolean deleteCodeAndResources, int flags) { + private int deletePackageX(String packageName, int uid, int flags) { final PackageRemovedInfo info = new PackageRemovedInfo(); final boolean res; @@ -7468,27 +8052,30 @@ public class PackageManagerService extends IPackageManager.Stub { } synchronized (mInstallLock) { - res = deletePackageLI(packageName, deleteCodeAndResources, - flags | REMOVE_CHATTY, info, true); + res = deletePackageLI(packageName, + (flags & PackageManager.DELETE_ALL_USERS) != 0 + ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)), + true, flags | REMOVE_CHATTY, info, true); } - if (res && sendBroadCast) { + if (res) { boolean systemUpdate = info.isRemovedPackageSystemUpdate; - info.sendBroadcast(deleteCodeAndResources, systemUpdate); + info.sendBroadcast(true, systemUpdate); // If the removed package was a system update, the old system packaged // was re-enabled; we need to broadcast this information if (systemUpdate) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid); + extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0 + ? info.removedAppId : info.uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, - null, packageName, null, UserId.USER_ALL); + null, packageName, null, null); } } // Force a gc here. @@ -7497,7 +8084,7 @@ public class PackageManagerService extends IPackageManager.Stub { // other processes clean up before deleting resources. if (info.args != null) { synchronized (mInstallLock) { - info.args.doPostDeleteLI(deleteCodeAndResources); + info.args.doPostDeleteLI(true); } } @@ -7507,29 +8094,30 @@ public class PackageManagerService extends IPackageManager.Stub { static class PackageRemovedInfo { String removedPackage; int uid = -1; - int removedUid = -1; + int removedAppId = -1; + int[] removedUsers = null; boolean isRemovedPackageSystemUpdate = false; // Clean up resources deleted packages. InstallArgs args = null; void sendBroadcast(boolean fullRemove, boolean replacing) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid); + extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove); if (replacing) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, removedUsers); if (fullRemove && !replacing) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, removedUsers); } } - if (removedUid >= 0) { + if (removedAppId >= 0) { sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null, - UserId.getUserId(removedUid)); + removedUsers); } } } @@ -7540,37 +8128,32 @@ public class PackageManagerService extends IPackageManager.Stub { * make sure this flag is set for partially installed apps. If not its meaningless to * delete a partially installed application. */ - private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, + private void removePackageDataLI(PackageSetting ps, PackageRemovedInfo outInfo, int flags, boolean writeSettings) { - String packageName = p.packageName; - if (outInfo != null) { - outInfo.removedPackage = packageName; - } - removePackageLI(p, (flags&REMOVE_CHATTY) != 0); + String packageName = ps.name; + removePackageLI(ps, (flags&REMOVE_CHATTY) != 0); // Retrieve object to delete permissions for shared user later on final PackageSetting deletedPs; // reader synchronized (mPackages) { deletedPs = mSettings.mPackages.get(packageName); - } - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - 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 - sUserManager.removePackageForAllUsers(packageName); + if (outInfo != null) { + outInfo.removedPackage = packageName; + outInfo.removedUsers = deletedPs != null + ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true) + : null; } - schedulePackageCleaning(packageName); + } + if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { + removeDataDirsLI(packageName); + schedulePackageCleaning(packageName, UserHandle.USER_ALL, true); } // writer synchronized (mPackages) { if (deletedPs != null) { - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { + if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { if (outInfo != null) { - outInfo.removedUid = mSettings.removePackageLPw(packageName); + outInfo.removedAppId = mSettings.removePackageLPw(packageName); } if (deletedPs != null) { updatePermissionsLPw(deletedPs.name, null, 0); @@ -7579,7 +8162,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids); } } - clearPackagePreferredActivitiesLPw(deletedPs.name); + clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL); } } // can downgrade to reader @@ -7593,38 +8176,32 @@ public class PackageManagerService extends IPackageManager.Stub { /* * Tries to delete system package. */ - private boolean deleteSystemPackageLI(PackageParser.Package p, + private boolean deleteSystemPackageLI(PackageSetting newPs, int flags, PackageRemovedInfo outInfo, boolean writeSettings) { - ApplicationInfo applicationInfo = p.applicationInfo; - //applicable for non-partially installed applications only - if (applicationInfo == null) { - Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); - return false; - } - PackageSetting ps = null; + PackageSetting disabledPs = null; // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore // the system pkg from system partition // reader synchronized (mPackages) { - ps = mSettings.getDisabledSystemPkgLPr(p.packageName); + disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name); } - if (ps == null) { - Slog.w(TAG, "Attempt to delete unknown system package "+ p.packageName); + if (disabledPs == null) { + Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name); return false; } else { Log.i(TAG, "Deleting system pkg from data partition"); } // Delete the updated package outInfo.isRemovedPackageSystemUpdate = true; - if (ps.versionCode < p.mVersionCode) { + if (disabledPs.versionCode < newPs.versionCode) { // Delete data for downgrades - flags &= ~PackageManager.DONT_DELETE_DATA; + flags &= ~PackageManager.DELETE_KEEP_DATA; } else { // Preserve data by setting flag - flags |= PackageManager.DONT_DELETE_DATA; + flags |= PackageManager.DELETE_KEEP_DATA; } - boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo, + boolean ret = deleteInstalledPackageLI(newPs, true, flags, outInfo, writeSettings); if (!ret) { return false; @@ -7632,17 +8209,18 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { // Reinstate the old system package - mSettings.enableSystemPackageLPw(p.packageName); + mSettings.enableSystemPackageLPw(newPs.name); // Remove any native libraries from the upgraded package. - NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir); + NativeLibraryHelper.removeNativeBinariesLI(newPs.nativeLibraryPathString); } // Install the system package - PackageParser.Package newPkg = scanPackageLI(ps.codePath, + PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath, PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM, - SCAN_MONITOR | SCAN_NO_PATHS, 0); + SCAN_MONITOR | SCAN_NO_PATHS, 0, null); if (newPkg == null) { - Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); + Slog.w(TAG, "Failed to restore system package:" + newPs.name + + " with error:" + mLastScanError); return false; } // writer @@ -7657,28 +8235,20 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } - private boolean deleteInstalledPackageLI(PackageParser.Package p, + private boolean deleteInstalledPackageLI(PackageSetting ps, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo, boolean writeSettings) { - ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); - return false; - } if (outInfo != null) { - outInfo.uid = applicationInfo.uid; + outInfo.uid = ps.appId; } // Delete package data from internal structures and also remove data if flag is set - removePackageDataLI(p, outInfo, flags, writeSettings); + removePackageDataLI(ps, outInfo, flags, writeSettings); // Delete application code and resources if (deleteCodeAndResources) { - // TODO can pick up from PackageSettings as well - int installFlags = isExternal(p) ? PackageManager.INSTALL_EXTERNAL : 0; - installFlags |= isForwardLocked(p) ? PackageManager.INSTALL_FORWARD_LOCK : 0; - outInfo.args = createInstallArgs(installFlags, applicationInfo.sourceDir, - applicationInfo.publicSourceDir, applicationInfo.nativeLibraryDir); + outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, + ps.resourcePathString, ps.nativeLibraryPathString); } return true; } @@ -7686,54 +8256,78 @@ public class PackageManagerService extends IPackageManager.Stub { /* * This method handles package deletion in general */ - private boolean deletePackageLI(String packageName, + private boolean deletePackageLI(String packageName, UserHandle user, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo, boolean writeSettings) { if (packageName == null) { Slog.w(TAG, "Attempt to delete null packageName."); return false; } - PackageParser.Package p; + PackageSetting ps; boolean dataOnly = false; + int removeUser = -1; + int appId = -1; synchronized (mPackages) { - p = mPackages.get(packageName); - if (p == null) { - //this retrieves partially installed apps - dataOnly = true; - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; + ps = mSettings.mPackages.get(packageName); + if (ps == null) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); + return false; + } + if (!isSystemApp(ps) && user != null + && user.getIdentifier() != UserHandle.USER_ALL) { + // The caller is asking that the package only be deleted for a single + // user. To do this, we just mark its uninstalled state and delete + // its data. + ps.setUserState(user.getIdentifier(), + COMPONENT_ENABLED_STATE_DEFAULT, + false, //installed + true, //stopped + true, //notLaunched + null, null); + if (ps.isAnyInstalled(sUserManager.getUserIds())) { + // Other user still have this package installed, so all + // we need to do is clear this user's data and save that + // it is uninstalled. + removeUser = user.getIdentifier(); + appId = ps.appId; + mSettings.writePackageRestrictionsLPr(removeUser); + } else { + // We need to set it back to 'installed' so the uninstall + // broadcasts will be sent correctly. + ps.setInstalled(true, user.getIdentifier()); } - p = ps.pkg; } } - if (p == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; + + if (removeUser >= 0) { + // From above, we determined that we are deleting this only + // for a single user. Continue the work here. + if (outInfo != null) { + outInfo.removedPackage = packageName; + outInfo.removedAppId = appId; + outInfo.removedUsers = new int[] {removeUser}; + } + mInstaller.clearUserData(packageName, removeUser); + schedulePackageCleaning(packageName, removeUser, false); + return true; } if (dataOnly) { // Delete application data first - removePackageDataLI(p, outInfo, flags, writeSettings); + removePackageDataLI(ps, outInfo, flags, writeSettings); return true; } - // At this point the package should have ApplicationInfo associated with it - if (p.applicationInfo == null) { - Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo."); - return false; - } boolean ret = false; - if (isSystemApp(p)) { - Log.i(TAG, "Removing system package:"+p.packageName); + if (isSystemApp(ps)) { + Log.i(TAG, "Removing system package:" + ps.name); // When an updated system application is deleted we delete the existing resources as well and // fall back to existing code in system partition - ret = deleteSystemPackageLI(p, flags, outInfo, writeSettings); + ret = deleteSystemPackageLI(ps, flags, outInfo, writeSettings); } else { - Log.i(TAG, "Removing non-system package:"+p.packageName); + Log.i(TAG, "Removing non-system package:" + ps.name); // Kill application pre-emptively especially for apps on sd. - killApplication(packageName, p.applicationInfo.uid); - ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo, + killApplication(packageName, ps.appId); + ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo, writeSettings); } return ret; @@ -7755,7 +8349,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void clearExternalStorageDataSync(String packageName, boolean allData) { + private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) { final boolean mounted; if (Environment.isExternalStorageEmulated()) { mounted = true; @@ -7771,44 +8365,54 @@ public class PackageManagerService extends IPackageManager.Stub { } final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); - ClearStorageConnection conn = new ClearStorageConnection(); - if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) { - try { - long timeout = SystemClock.uptimeMillis() + 5000; - synchronized (conn) { - long now = SystemClock.uptimeMillis(); - while (conn.mContainerService == null && now < timeout) { - try { - conn.wait(timeout - now); - } catch (InterruptedException e) { + int[] users; + if (userId == UserHandle.USER_ALL) { + users = sUserManager.getUserIds(); + } else { + users = new int[] { userId }; + } + for (int curUser : users) { + ClearStorageConnection conn = new ClearStorageConnection(); + if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) { + try { + long timeout = SystemClock.uptimeMillis() + 5000; + synchronized (conn) { + long now = SystemClock.uptimeMillis(); + while (conn.mContainerService == null && now < timeout) { + try { + conn.wait(timeout - now); + } catch (InterruptedException e) { + } } } - } - if (conn.mContainerService == null) { - return; - } - final File externalCacheDir = Environment - .getExternalStorageAppCacheDirectory(packageName); - try { - conn.mContainerService.clearDirectory(externalCacheDir.toString()); - } catch (RemoteException e) { - } - if (allData) { - final File externalDataDir = Environment - .getExternalStorageAppDataDirectory(packageName); - try { - conn.mContainerService.clearDirectory(externalDataDir.toString()); - } catch (RemoteException e) { + if (conn.mContainerService == null) { + return; } - final File externalMediaDir = Environment - .getExternalStorageAppMediaDirectory(packageName); + + final UserEnvironment userEnv = new UserEnvironment(curUser); + final File externalCacheDir = userEnv + .getExternalStorageAppCacheDirectory(packageName); try { - conn.mContainerService.clearDirectory(externalMediaDir.toString()); + conn.mContainerService.clearDirectory(externalCacheDir.toString()); } catch (RemoteException e) { } + if (allData) { + final File externalDataDir = userEnv + .getExternalStorageAppDataDirectory(packageName); + try { + conn.mContainerService.clearDirectory(externalDataDir.toString()); + } catch (RemoteException e) { + } + final File externalMediaDir = userEnv + .getExternalStorageAppMediaDirectory(packageName); + try { + conn.mContainerService.clearDirectory(externalMediaDir.toString()); + } catch (RemoteException e) { + } + } + } finally { + mContext.unbindService(conn); } - } finally { - mContext.unbindService(conn); } } } @@ -7818,7 +8422,7 @@ public class PackageManagerService extends IPackageManager.Stub { final IPackageDataObserver observer, final int userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_USER_DATA, null); - checkValidCaller(Binder.getCallingUid(), userId); + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "clear application data"); // Queue up an async operation since the package deletion may take a little while. mHandler.post(new Runnable() { public void run() { @@ -7827,7 +8431,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { succeeded = clearApplicationUserDataLI(packageName, userId); } - clearExternalStorageDataSync(packageName, true); + clearExternalStorageDataSync(packageName, userId, true); if (succeeded) { // invoke DeviceStorageMonitor's update method to clear any notifications DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) @@ -7893,7 +8497,7 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_CACHE_FILES, null); // Queue up an async operation since the package deletion may take a little while. - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); @@ -7901,7 +8505,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { succeded = deleteApplicationCacheFilesLI(packageName, userId); } - clearExternalStorageDataSync(packageName, false); + clearExternalStorageDataSync(packageName, userId, false); if(observer != null) { try { observer.onRemoveCompleted(packageName, succeded); @@ -7941,12 +8545,12 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } - public void getPackageSizeInfo(final String packageName, + public void getPackageSizeInfo(final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GET_PACKAGE_SIZE, null); - PackageStats stats = new PackageStats(packageName); + PackageStats stats = new PackageStats(packageName, userHandle); /* * Queue up an async operation since the package measurement may take a @@ -7957,7 +8561,8 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } - private boolean getPackageSizeInfoLI(String packageName, PackageStats pStats) { + private boolean getPackageSizeInfoLI(String packageName, int userHandle, + PackageStats pStats) { if (packageName == null) { Slog.w(TAG, "Attempt to get size of null packageName."); return false; @@ -7994,7 +8599,7 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir, asecPath, pStats); if (res < 0) { return false; @@ -8046,26 +8651,28 @@ public class PackageManagerService extends IPackageManager.Stub { } public void addPreferredActivity(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity) { + ComponentName[] set, ComponentName activity, int userId) { // writer + int callingUid = Binder.getCallingUid(); + enforceCrossUserPermission(callingUid, userId, true, "add preferred activity"); synchronized (mPackages) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid()) + if (getUidTargetSdkVersionLockedLPr(callingUid) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring addPreferredActivity() from uid " - + Binder.getCallingUid()); + + callingUid); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - - Slog.i(TAG, "Adding preferred activity " + activity + ":"); + + Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); mSettings.mPreferredActivities.addFilter( - new PreferredActivity(filter, match, set, activity)); + new PreferredActivity(filter, match, set, activity, userId)); scheduleWriteSettingsLocked(); } } @@ -8101,13 +8708,15 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - + + final int callingUserId = UserHandle.getCallingUserId(); ArrayList<PreferredActivity> removed = null; Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); String action = filter.getAction(0); String category = filter.getCategory(0); while (it.hasNext()) { PreferredActivity pa = it.next(); + if (pa.mUserId != callingUserId) continue; if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) { if (removed == null) { removed = new ArrayList<PreferredActivity>(); @@ -8123,7 +8732,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.mPreferredActivities.removeFilter(pa); } } - addPreferredActivity(filter, match, set, activity); + addPreferredActivity(filter, match, set, activity, callingUserId); } } @@ -8147,17 +8756,21 @@ public class PackageManagerService extends IPackageManager.Stub { } } - if (clearPackagePreferredActivitiesLPw(packageName)) { + if (clearPackagePreferredActivitiesLPw(packageName, UserHandle.getCallingUserId())) { scheduleWriteSettingsLocked(); } } } - boolean clearPackagePreferredActivitiesLPw(String packageName) { + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) { ArrayList<PreferredActivity> removed = null; Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); while (it.hasNext()) { PreferredActivity pa = it.next(); + if (userId != UserHandle.USER_ALL && pa.mUserId != userId) { + continue; + } if (pa.mPref.mComponent.getPackageName().equals(packageName)) { if (removed == null) { removed = new ArrayList<PreferredActivity>(); @@ -8179,11 +8792,15 @@ public class PackageManagerService extends IPackageManager.Stub { List<ComponentName> outActivities, String packageName) { int num = 0; + final int userId = UserHandle.getCallingUserId(); // reader synchronized (mPackages) { final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); while (it.hasNext()) { final PreferredActivity pa = it.next(); + if (pa.mUserId != userId) { + continue; + } if (packageName == null || pa.mPref.mComponent.getPackageName().equals(packageName)) { if (outFilters != null) { @@ -8227,7 +8844,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int uid = Binder.getCallingUid(); final int permission = mContext.checkCallingPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); - checkValidCaller(uid, userId); + enforceCrossUserPermission(uid, userId, false, "set enabled"); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); boolean sendNow = false; boolean isApp = (className == null); @@ -8248,7 +8865,7 @@ public class PackageManagerService extends IPackageManager.Stub { + "/" + className); } // Allow root and verify that userId is not being specified by a different user - if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) { + if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) { throw new SecurityException( "Permission Denial: attempt to change component state from pid=" + Binder.getCallingPid() @@ -8297,7 +8914,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } mSettings.writePackageRestrictionsLPr(userId); - packageUid = UserId.getUid(userId, pkgSetting.appId); + packageUid = UserHandle.getUid(userId, pkgSetting.appId); components = mPendingBroadcasts.get(packageName); final boolean newPackage = components == null; if (newPackage) { @@ -8346,7 +8963,7 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null, - UserId.getUserId(packageUid)); + new int[] {UserHandle.getUserId(packageUid)}); } public void setPackageStoppedState(String packageName, boolean stopped, int userId) { @@ -8355,7 +8972,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); - checkValidCaller(uid, userId); + enforceCrossUserPermission(uid, userId, true, "stop package"); // writer synchronized (mPackages) { if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission, @@ -8376,7 +8993,7 @@ public class PackageManagerService extends IPackageManager.Stub { public int getApplicationEnabledSetting(String packageName, int userId) { if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int uid = Binder.getCallingUid(); - checkValidCaller(uid, userId); + enforceCrossUserPermission(uid, userId, false, "get enabled"); // reader synchronized (mPackages) { return mSettings.getApplicationEnabledSettingLPr(packageName, userId); @@ -8387,7 +9004,7 @@ public class PackageManagerService extends IPackageManager.Stub { public int getComponentEnabledSetting(ComponentName componentName, int userId) { if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int uid = Binder.getCallingUid(); - checkValidCaller(uid, userId); + enforceCrossUserPermission(uid, userId, false, "get component enabled"); // reader synchronized (mPackages) { return mSettings.getComponentEnabledSettingLPr(componentName, userId); @@ -8951,7 +9568,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); loadMediaPackages(processCids, uidArr, removeCids); - startCleaningPackages(); + startCleaningPackages(-1); } else { if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); @@ -8972,7 +9589,7 @@ public class PackageManagerService extends IPackageManager.Stub { } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL); + sendPackageBroadcast(action, null, extras, null, finishedReceiver, null); } } @@ -9017,7 +9634,7 @@ public class PackageManagerService extends IPackageManager.Stub { doGc = true; synchronized (mInstallLock) { final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags, - 0, 0); + 0, 0, null); // Scan the package if (pkg != null) { /* @@ -9126,8 +9743,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete package internally PackageRemovedInfo outInfo = new PackageRemovedInfo(); synchronized (mInstallLock) { - boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, - outInfo, false); + boolean res = deletePackageLI(pkgName, null, false, + PackageManager.DELETE_KEEP_DATA, outInfo, false); if (res) { pkgList.add(pkgName); } else { @@ -9151,7 +9768,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkgList.size() > 0) { sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() { public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky) throws RemoteException { + Bundle extras, boolean ordered, boolean sticky, + int sendingUser) throws RemoteException { Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, 1, keys); mHandler.sendMessage(msg); @@ -9164,9 +9782,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** Binder call */ + @Override public void movePackage(final String packageName, final IPackageMoveObserver observer, final int flags) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); + UserHandle user = new UserHandle(UserHandle.getCallingUserId()); int returnCode = PackageManager.MOVE_SUCCEEDED; int currFlags = 0; int newFlags = 0; @@ -9217,14 +9838,15 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1), + processPendingMove(new MoveParams(null, observer, 0, packageName, + null, -1, user), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - pkg.applicationInfo.dataDir, pkg.applicationInfo.uid); + pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user); msg.obj = mp; mHandler.sendMessage(msg); } @@ -9288,31 +9910,26 @@ public class PackageManagerService extends IPackageManager.Stub { final String newNativePath = mp.targetArgs .getNativeLibraryPath(); - try { - final File newNativeDir = new File(newNativePath); + final File newNativeDir = new File(newNativePath); - final String libParentDir = newNativeDir.getParentFile() - .getCanonicalPath(); - if (newNativeDir.getParentFile().getCanonicalPath() - .equals(pkg.applicationInfo.dataDir)) { - if (mInstaller - .unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) { + if (!isForwardLocked(pkg) && !isExternal(pkg)) { + synchronized (mInstallLock) { + if (mInstaller.linkNativeLibraryDirectory( + pkg.applicationInfo.dataDir, newNativePath) < 0) { returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; - } else { - NativeLibraryHelper.copyNativeBinariesIfNeededLI( - new File(newCodePath), newNativeDir); } - } else { + } + NativeLibraryHelper.copyNativeBinariesIfNeededLI(new File( + newCodePath), newNativeDir); + } else { + synchronized (mInstallLock) { if (mInstaller.linkNativeLibraryDirectory( pkg.applicationInfo.dataDir, newNativePath) < 0) { returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } } - } catch (IOException e) { - returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; } - if (returnCode == PackageManager.MOVE_SUCCEEDED) { pkg.mPath = newCodePath; // Move dex files around @@ -9415,48 +10032,37 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.APP_INSTALL_AUTO); } - public UserInfo createUser(String name, int flags) { - // TODO(kroot): Add a real permission for creating users - enforceSystemOrRoot("Only the system can create users"); - - UserInfo userInfo = sUserManager.createUser(name, flags); - if (userInfo != null) { - Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS); - } - return userInfo; - } - - public boolean removeUser(int userId) { - // TODO(kroot): Add a real permission for removing users - enforceSystemOrRoot("Only the system can remove users"); - - if (userId == 0 || !sUserManager.exists(userId)) { - return false; + /** Called by UserManagerService */ + void cleanUpUserLILPw(int userHandle) { + // Disable all the packages for the user first + Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet(); + for (Entry<String, PackageSetting> entry : entries) { + entry.getValue().removeUser(userHandle); } - - cleanUpUser(userId); - - if (sUserManager.removeUser(userId)) { - // Let other services shutdown any activity - Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); - addedIntent.putExtra(Intent.EXTRA_USERID, userId); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS); + if (mDirtyUsers.remove(userHandle)); + mSettings.removeUserLPr(userHandle); + if (mInstaller != null) { + // Technically, we shouldn't be doing this with the package lock + // held. However, this is very rare, and there is already so much + // other disk I/O going on, that we'll let it slide for now. + mInstaller.removeUserDataDirs(userHandle); } - sUserManager.removePackageFolders(userId); - return true; } - private void cleanUpUser(int userId) { - // Disable all the packages for the user first - synchronized (mPackages) { - Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet(); - for (Entry<String, PackageSetting> entry : entries) { - entry.getValue().removeUser(userId); + /** Called by UserManagerService */ + void createNewUserLILPw(int userHandle, File path) { + if (mInstaller != null) { + path.mkdir(); + FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IXOTH, -1, -1); + for (PackageSetting ps : mSettings.mPackages.values()) { + // Only system apps are initially installed. + ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); + // Need to create a data directory for all apps under this user. + mInstaller.createUserData(ps.name, + UserHandle.getUid(userHandle, ps.appId), userHandle); } - if (mDirtyUsers.remove(userId)); - mSettings.removeUserLPr(userId); + mSettings.writePackageRestrictionsLPr(userHandle); } } @@ -9472,24 +10078,6 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public List<UserInfo> getUsers() { - enforceSystemOrRoot("Only the system can query users"); - return sUserManager.getUsers(); - } - - @Override - public UserInfo getUser(int userId) { - enforceSystemOrRoot("Only the system can remove users"); - return sUserManager.getUser(userId); - } - - @Override - public void updateUserName(int userId, String name) { - enforceSystemOrRoot("Only the system can rename users"); - sUserManager.updateUserName(userId, name); - } - - @Override public void setPermissionEnforced(String permission, boolean enforced) { mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null); if (READ_EXTERNAL_STORAGE.equals(permission)) { diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index 56f2166..d8f7345 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -20,11 +20,13 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import android.content.pm.PackageUserState; +import android.content.pm.UserInfo; import android.util.SparseArray; -import android.util.SparseIntArray; import java.io.File; import java.util.HashSet; +import java.util.List; /** * Settings base class for pending and resolved classes. @@ -62,19 +64,11 @@ class PackageSettingBase extends GrantedPermissions { boolean permissionsFixed; boolean haveGids; + private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState(); + // Whether this package is currently stopped, thus can not be // started until explicitly launched by the user. - private SparseArray<Boolean> stopped = new SparseArray<Boolean>(); - - // Set to true if we have never launched this app. - private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>(); - - /* Explicitly disabled components */ - private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>(); - /* Explicitly enabled components */ - private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>(); - /* Enabled state */ - private SparseIntArray enabled = new SparseIntArray(); + private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>(); int installStatus = PKG_INSTALL_COMPLETE; @@ -115,12 +109,11 @@ class PackageSettingBase extends GrantedPermissions { permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; - notLaunched = base.notLaunched; - - disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone(); - enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone(); - enabled = (SparseIntArray) base.enabled.clone(); - stopped = (SparseArray<Boolean>) base.stopped.clone(); + userState.clear(); + for (int i=0; i<base.userState.size(); i++) { + userState.put(base.userState.keyAt(i), + new PackageUserState(base.userState.valueAt(i))); + } installStatus = base.installStatus; origPackage = base.origPackage; @@ -171,103 +164,174 @@ class PackageSettingBase extends GrantedPermissions { signatures = base.signatures; permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; - stopped = base.stopped; - notLaunched = base.notLaunched; - disabledComponents = base.disabledComponents; - enabledComponents = base.enabledComponents; - enabled = base.enabled; + userState.clear(); + for (int i=0; i<base.userState.size(); i++) { + userState.put(base.userState.keyAt(i), base.userState.valueAt(i)); + } installStatus = base.installStatus; } + private PackageUserState modifyUserState(int userId) { + PackageUserState state = userState.get(userId); + if (state == null) { + state = new PackageUserState(); + userState.put(userId, state); + } + return state; + } + + public PackageUserState readUserState(int userId) { + PackageUserState state = userState.get(userId); + return state != null ? state : DEFAULT_USER_STATE; + } + void setEnabled(int state, int userId) { - enabled.put(userId, state); + modifyUserState(userId).enabled = state; } int getEnabled(int userId) { - return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT); + return readUserState(userId).enabled; + } + + void setInstalled(boolean inst, int userId) { + modifyUserState(userId).installed = inst; + } + + boolean getInstalled(int userId) { + return readUserState(userId).installed; + } + + boolean isAnyInstalled(int[] users) { + for (int user: users) { + if (readUserState(user).installed) { + return true; + } + } + return false; + } + + int[] queryInstalledUsers(int[] users, boolean installed) { + int num = 0; + for (int user : users) { + if (getInstalled(user) == installed) { + num++; + } + } + int[] res = new int[num]; + num = 0; + for (int user : users) { + if (getInstalled(user) == installed) { + res[num] = user; + num++; + } + } + return res; } boolean getStopped(int userId) { - return stopped.get(userId, false); + return readUserState(userId).stopped; } void setStopped(boolean stop, int userId) { - stopped.put(userId, stop); + modifyUserState(userId).stopped = stop; } boolean getNotLaunched(int userId) { - return notLaunched.get(userId, false); + return readUserState(userId).notLaunched; } void setNotLaunched(boolean stop, int userId) { - notLaunched.put(userId, stop); + modifyUserState(userId).notLaunched = stop; + } + + void setUserState(int userId, int enabled, boolean installed, boolean stopped, + boolean notLaunched, HashSet<String> enabledComponents, + HashSet<String> disabledComponents) { + PackageUserState state = modifyUserState(userId); + state.enabled = enabled; + state.installed = installed; + state.stopped = stopped; + state.notLaunched = notLaunched; + state.enabledComponents = enabledComponents; + state.disabledComponents = disabledComponents; } HashSet<String> getEnabledComponents(int userId) { - return getComponentHashSet(enabledComponents, userId); + return readUserState(userId).enabledComponents; } HashSet<String> getDisabledComponents(int userId) { - return getComponentHashSet(disabledComponents, userId); + return readUserState(userId).disabledComponents; } void setEnabledComponents(HashSet<String> components, int userId) { - enabledComponents.put(userId, components); + modifyUserState(userId).enabledComponents = components; } void setDisabledComponents(HashSet<String> components, int userId) { - disabledComponents.put(userId, components); + modifyUserState(userId).disabledComponents = components; + } + + void setEnabledComponentsCopy(HashSet<String> components, int userId) { + modifyUserState(userId).enabledComponents = components != null + ? new HashSet<String>(components) : null; } - private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) { - HashSet<String> set = setArray.get(userId); - if (set == null) { - set = new HashSet<String>(1); - setArray.put(userId, set); + void setDisabledComponentsCopy(HashSet<String> components, int userId) { + modifyUserState(userId).disabledComponents = components != null + ? new HashSet<String>(components) : null; + } + + PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) { + PackageUserState state = modifyUserState(userId); + if (disabled && state.disabledComponents == null) { + state.disabledComponents = new HashSet<String>(1); + } + if (enabled && state.enabledComponents == null) { + state.enabledComponents = new HashSet<String>(1); } - return set; + return state; } void addDisabledComponent(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - disabled.add(componentClassName); + modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName); } void addEnabledComponent(String componentClassName, int userId) { - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - enabled.add(componentClassName); + modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName); } boolean enableComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = disabled.remove(componentClassName); - changed |= enabled.add(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, false, true); + boolean changed = state.disabledComponents != null + ? state.disabledComponents.remove(componentClassName) : false; + changed |= state.enabledComponents.add(componentClassName); return changed; } boolean disableComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = enabled.remove(componentClassName); - changed |= disabled.add(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, true, false); + boolean changed = state.enabledComponents != null + ? state.enabledComponents.remove(componentClassName) : false; + changed |= state.disabledComponents.add(componentClassName); return changed; } boolean restoreComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = enabled.remove(componentClassName); - changed |= disabled.remove(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, true, true); + boolean changed = state.disabledComponents != null + ? state.disabledComponents.remove(componentClassName) : false; + changed |= state.enabledComponents != null + ? state.enabledComponents.remove(componentClassName) : false; return changed; } int getCurrentEnabledStateLPr(String componentName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - if (enabled.contains(componentName)) { + PackageUserState state = readUserState(userId); + if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) { return COMPONENT_ENABLED_STATE_ENABLED; - } else if (disabled.contains(componentName)) { + } else if (state.disabledComponents != null + && state.disabledComponents.contains(componentName)) { return COMPONENT_ENABLED_STATE_DISABLED; } else { return COMPONENT_ENABLED_STATE_DEFAULT; @@ -275,11 +339,6 @@ class PackageSettingBase extends GrantedPermissions { } void removeUser(int userId) { - enabled.delete(userId); - stopped.delete(userId); - enabledComponents.delete(userId); - disabledComponents.delete(userId); - notLaunched.delete(userId); + userState.delete(userId); } - } diff --git a/services/java/com/android/server/pm/PackageVerificationState.java b/services/java/com/android/server/pm/PackageVerificationState.java index e5b89c1..3214e88 100644 --- a/services/java/com/android/server/pm/PackageVerificationState.java +++ b/services/java/com/android/server/pm/PackageVerificationState.java @@ -43,6 +43,8 @@ class PackageVerificationState { private boolean mRequiredVerificationPassed; + private boolean mExtendedTimeout; + /** * Create a new package verification state where {@code requiredVerifierUid} * is the user ID for the package that must reply affirmative before things @@ -55,6 +57,7 @@ class PackageVerificationState { mRequiredVerifierUid = requiredVerifierUid; mArgs = args; mSufficientVerifierUids = new SparseBooleanArray(); + mExtendedTimeout = false; } public InstallArgs getInstallArgs() { @@ -146,4 +149,22 @@ class PackageVerificationState { return true; } + + /** + * Extend the timeout for this Package to be verified. + */ + public void extendTimeout() { + if (!mExtendedTimeout) { + mExtendedTimeout = true; + } + } + + /** + * Returns whether the timeout was extended for verification. + * + * @return {@code true} if a timeout was already extended. + */ + public boolean timeoutExtended() { + return mExtendedTimeout; + } } diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java index b100eb1..5539e84 100644 --- a/services/java/com/android/server/pm/PreferredActivity.java +++ b/services/java/com/android/server/pm/PreferredActivity.java @@ -33,22 +33,38 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb private static final String TAG = "PreferredActivity"; private static final boolean DEBUG_FILTERS = false; + static final String ATTR_USER_ID = "userId"; final PreferredComponent mPref; + final int mUserId; PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { + this(filter, match, set, activity, 0); + } + + PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, + int userId) { super(filter); + mUserId = userId; mPref = new PreferredComponent(this, match, set, activity); } PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException { + String userIdString = parser.getAttributeValue(null, ATTR_USER_ID); + if (userIdString != null && userIdString.length() > 0) { + mUserId = Integer.parseInt(userIdString); + } else { + // Old format with no userId specified - assume primary user + mUserId = 0; + } mPref = new PreferredComponent(this, parser); } public void writeToXml(XmlSerializer serializer) throws IOException { + serializer.attribute(null, ATTR_USER_ID, Integer.toString(mUserId)); mPref.writeToXml(serializer); serializer.startTag(null, "filter"); - super.writeToXml(serializer); + super.writeToXml(serializer); serializer.endTag(null, "filter"); } diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index 120b650..b075da3 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -32,23 +32,24 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; -import android.app.AppGlobals; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.PackageCleanItem; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.content.pm.Signature; import android.content.pm.UserInfo; +import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; import android.os.Process; -import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -79,6 +80,7 @@ final class Settings { private static final String TAG = "PackageSettings"; private static final boolean DEBUG_STOPPED = false; + private static final boolean DEBUG_MU = false; private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage"; private static final String ATTR_ENFORCEMENT = "enforcement"; @@ -90,9 +92,12 @@ final class Settings { private static final String TAG_PACKAGE = "pkg"; private static final String ATTR_NAME = "name"; + private static final String ATTR_USER = "user"; + private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; private static final String ATTR_STOPPED = "stopped"; + private static final String ATTR_INSTALLED = "inst"; private final File mSettingsFilename; private final File mBackupSettingsFilename; @@ -121,6 +126,10 @@ final class Settings { final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = new IntentResolver<PreferredActivity, PreferredActivity>() { @Override + protected PreferredActivity[] newArray(int size) { + return new PreferredActivity[size]; + } + @Override protected String packageForFilter(PreferredActivity filter) { return filter.mPref.mComponent.getPackageName(); } @@ -150,7 +159,8 @@ final class Settings { // Packages that have been uninstalled and still need their external // storage data deleted. - final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); + final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned + = new SparseArray<ArrayList<PackageCleanItem>>(); // Packages that have been renamed since they were first installed. // Keys are the new names of the packages, values are the original @@ -169,12 +179,15 @@ final class Settings { */ private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>(); + private final Context mContext; + private final File mSystemDir; - Settings() { - this(Environment.getDataDirectory()); + Settings(Context context) { + this(context, Environment.getDataDirectory()); } - Settings(File dataDir) { + Settings(Context context, File dataDir) { + mContext = context; mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); FileUtils.setPermissions(mSystemDir.toString(), @@ -191,10 +204,11 @@ final class Settings { PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) { + String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add); + resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, + user, add); return p; } @@ -355,7 +369,8 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { + String nativeLibraryPathString, int vc, int pkgFlags, + UserHandle installUser, boolean add) { PackageSetting p = mPackages.get(name); if (p != null) { if (!p.codePath.equals(codePath)) { @@ -398,11 +413,6 @@ final class Settings { } } if (p == null) { - // Create a new PackageSettings entry. this can end up here because - // of code path mismatch or user id mismatch of an updated system partition - if (!create) { - return null; - } if (origPackage != null) { // We are consuming the data from an existing package. p = new PackageSetting(origPackage.name, name, codePath, resourcePath, @@ -436,8 +446,20 @@ final class Settings { List<UserInfo> users = getAllUsers(); if (users != null) { for (UserInfo user : users) { - p.setStopped(true, user.id); - p.setNotLaunched(true, user.id); + // By default we consider this app to be installed + // for the user if no user has been specified (which + // means to leave it at its original value, and the + // original default value is true), or we are being + // asked to install for all users, or this is the + // user we are installing for. + final boolean installed = installUser == null + || installUser.getIdentifier() == UserHandle.USER_ALL + || installUser.getIdentifier() == user.id; + p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT, + installed, + true, // stopped, + true, // notLaunched + null, null); writePackageRestrictionsLPr(user.id); } } @@ -463,12 +485,10 @@ final class Settings { if (users != null) { for (UserInfo user : users) { int userId = user.id; - p.setDisabledComponents( - new HashSet<String>(dis.getDisabledComponents(userId)), - userId); - p.setEnabledComponents( - new HashSet<String>(dis.getEnabledComponents(userId)), - userId); + p.setDisabledComponentsCopy( + dis.getDisabledComponents(userId), userId); + p.setEnabledComponentsCopy( + dis.getEnabledComponents(userId), userId); } } // Add new setting to list of user ids @@ -489,6 +509,25 @@ final class Settings { // user preferences addPackageSettingLPw(p, name, sharedUser); } + } else { + if (installUser != null) { + // The caller has explicitly specified the user they want this + // package installed for, and the package already exists. + // Make sure it conforms to the new request. + List<UserInfo> users = getAllUsers(); + if (users != null) { + for (UserInfo user : users) { + if (installUser.getIdentifier() == UserHandle.USER_ALL + || installUser.getIdentifier() == user.id) { + boolean installed = p.getInstalled(user.id); + if (!installed) { + p.setInstalled(true, user.id); + writePackageRestrictionsLPr(user.id); + } + } + } + } + } } return p; } @@ -527,6 +566,10 @@ final class Settings { if (p.signatures.mSignatures == null) { p.signatures.assignSignatures(pkg.mSignatures); } + // Update flags if needed. + if (pkg.applicationInfo.flags != p.pkgFlags) { + p.pkgFlags = pkg.applicationInfo.flags; + } // If this app defines a shared user id initialize // the shared user signatures as well. if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { @@ -704,13 +747,12 @@ final class Settings { } private File getUserPackagesStateFile(int userId) { - return new File(mSystemDir, - "users/" + userId + "/package-restrictions.xml"); + return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml"); } private File getUserPackagesStateBackupFile(int userId) { - return new File(mSystemDir, - "users/" + userId + "/package-restrictions-backup.xml"); + return new File(Environment.getUserSystemDirectory(userId), + "package-restrictions-backup.xml"); } void writeAllUsersPackageRestrictionsLPr() { @@ -735,6 +777,9 @@ final class Settings { } void readPackageRestrictionsLPr(int userId) { + if (DEBUG_MU) { + Log.i(TAG, "Reading package restrictions for user=" + userId); + } FileInputStream str = null; File userPackagesStateFile = getUserPackagesStateFile(userId); File backupFile = getUserPackagesStateBackupFile(userId); @@ -766,10 +811,14 @@ final class Settings { + "assuming all started"); // At first boot, make sure no packages are stopped. // We usually want to have third party apps initialize - // in the stopped state, but not at first boot. + // in the stopped state, but not at first boot. Also + // consider all applications to be installed. for (PackageSetting pkg : mPackages.values()) { - pkg.setStopped(false, userId); - pkg.setNotLaunched(false, userId); + pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT, + true, // installed + false, // stopped + false, // notLaunched + null, null); } return; } @@ -811,17 +860,21 @@ final class Settings { XmlUtils.skipCurrentTag(parser); continue; } - String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); - int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT - : Integer.parseInt(enabledStr); - ps.setEnabled(enabled, userId); - String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED); - boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr); - ps.setStopped(stopped, userId); - String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED); - boolean notLaunched = stoppedStr == null ? false - : Boolean.parseBoolean(notLaunchedStr); - ps.setNotLaunched(notLaunched, userId); + final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); + final int enabled = enabledStr == null + ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr); + final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED); + final boolean installed = installedStr == null + ? true : Boolean.parseBoolean(installedStr); + final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED); + final boolean stopped = stoppedStr == null + ? false : Boolean.parseBoolean(stoppedStr); + final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED); + final boolean notLaunched = stoppedStr == null + ? false : Boolean.parseBoolean(notLaunchedStr); + + HashSet<String> enabledComponents = null; + HashSet<String> disabledComponents = null; int packageDepth = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -833,13 +886,14 @@ final class Settings { } tagName = parser.getName(); if (tagName.equals(TAG_ENABLED_COMPONENTS)) { - HashSet<String> components = readComponentsLPr(parser); - ps.setEnabledComponents(components, userId); + enabledComponents = readComponentsLPr(parser); } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) { - HashSet<String> components = readComponentsLPr(parser); - ps.setDisabledComponents(components, userId); + disabledComponents = readComponentsLPr(parser); } } + + ps.setUserState(userId, enabled, installed, stopped, notLaunched, + enabledComponents, disabledComponents); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -864,7 +918,7 @@ final class Settings { private HashSet<String> readComponentsLPr(XmlPullParser parser) throws IOException, XmlPullParserException { - HashSet<String> components = new HashSet<String>(); + HashSet<String> components = null; int type; int outerDepth = parser.getDepth(); String tagName; @@ -879,6 +933,9 @@ final class Settings { if (tagName.equals(TAG_ITEM)) { String componentName = parser.getAttributeValue(null, ATTR_NAME); if (componentName != null) { + if (components == null) { + components = new HashSet<String>(); + } components.add(componentName); } } @@ -887,6 +944,9 @@ final class Settings { } void writePackageRestrictionsLPr(int userId) { + if (DEBUG_MU) { + Log.i(TAG, "Writing package restrictions for user=" + userId); + } // Keep the old stopped packages around until we know the new ones have // been successfully written. File userPackagesStateFile = getUserPackagesStateFile(userId); @@ -921,40 +981,44 @@ final class Settings { serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS); for (final PackageSetting pkg : mPackages.values()) { - if (pkg.getStopped(userId) - || pkg.getNotLaunched(userId) - || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT - || pkg.getEnabledComponents(userId).size() > 0 - || pkg.getDisabledComponents(userId).size() > 0) { + PackageUserState ustate = pkg.readUserState(userId); + if (ustate.stopped || ustate.notLaunched || !ustate.installed + || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT + || (ustate.enabledComponents != null + && ustate.enabledComponents.size() > 0) + || (ustate.disabledComponents != null + && ustate.disabledComponents.size() > 0)) { serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, pkg.name); - boolean stopped = pkg.getStopped(userId); - boolean notLaunched = pkg.getNotLaunched(userId); - int enabled = pkg.getEnabled(userId); - HashSet<String> enabledComponents = pkg.getEnabledComponents(userId); - HashSet<String> disabledComponents = pkg.getDisabledComponents(userId); + if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled); - if (stopped) { + if (!ustate.installed) { + serializer.attribute(null, ATTR_INSTALLED, "false"); + } + if (ustate.stopped) { serializer.attribute(null, ATTR_STOPPED, "true"); } - if (notLaunched) { + if (ustate.notLaunched) { serializer.attribute(null, ATTR_NOT_LAUNCHED, "true"); } - if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) { - serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled)); + if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attribute(null, ATTR_ENABLED, + Integer.toString(ustate.enabled)); } - if (enabledComponents.size() > 0) { + if (ustate.enabledComponents != null + && ustate.enabledComponents.size() > 0) { serializer.startTag(null, TAG_ENABLED_COMPONENTS); - for (final String name : enabledComponents) { + for (final String name : ustate.enabledComponents) { serializer.startTag(null, TAG_ITEM); serializer.attribute(null, ATTR_NAME, name); serializer.endTag(null, TAG_ITEM); } serializer.endTag(null, TAG_ENABLED_COMPONENTS); } - if (disabledComponents.size() > 0) { + if (ustate.disabledComponents != null + && ustate.disabledComponents.size() > 0) { serializer.startTag(null, TAG_DISABLED_COMPONENTS); - for (final String name : disabledComponents) { + for (final String name : ustate.disabledComponents) { serializer.startTag(null, TAG_ITEM); serializer.attribute(null, ATTR_NAME, name); serializer.endTag(null, TAG_ITEM); @@ -1194,9 +1258,17 @@ final class Settings { if (mPackagesToBeCleaned.size() > 0) { for (int i=0; i<mPackagesToBeCleaned.size(); i++) { - serializer.startTag(null, "cleaning-package"); - serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i)); - serializer.endTag(null, "cleaning-package"); + final int userId = mPackagesToBeCleaned.keyAt(i); + final String userStr = Integer.toString(userId); + final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i); + for (int j=0; j<pkgs.size(); j++) { + serializer.startTag(null, "cleaning-package"); + PackageCleanItem item = pkgs.get(j); + serializer.attribute(null, ATTR_NAME, item.packageName); + serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false"); + serializer.attribute(null, ATTR_USER, userStr); + serializer.endTag(null, "cleaning-package"); + } } } @@ -1452,6 +1524,17 @@ final class Settings { return ret; } + void addPackageToCleanLPw(int userId, PackageCleanItem pkg) { + ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId); + if (pkgs == null) { + pkgs = new ArrayList<PackageCleanItem>(); + mPackagesToBeCleaned.put(userId, pkgs); + } + if (!pkgs.contains(pkg)) { + pkgs.add(pkg); + } + } + boolean readLPw(List<UserInfo> users) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { @@ -1529,8 +1612,21 @@ final class Settings { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { String name = parser.getAttributeValue(null, ATTR_NAME); + String userStr = parser.getAttributeValue(null, ATTR_USER); + String codeStr = parser.getAttributeValue(null, ATTR_CODE); if (name != null) { - mPackagesToBeCleaned.add(name); + int user = 0; + boolean andCode = true; + try { + if (userStr != null) { + user = Integer.parseInt(userStr); + } + } catch (NumberFormatException e) { + } + if (codeStr != null) { + andCode = Boolean.parseBoolean(codeStr); + } + addPackageToCleanLPw(user, new PackageCleanItem(name, andCode)); } } else if (tagName.equals("renamed-package")) { String nname = parser.getAttributeValue(null, "new"); @@ -1580,7 +1676,24 @@ final class Settings { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); + } + if (mBackupStoppedPackagesFilename.exists() + || mStoppedPackagesFilename.exists()) { + // Read old file + readStoppedLPw(); + mBackupStoppedPackagesFilename.delete(); + mStoppedPackagesFilename.delete(); + // Migrate to new file format + writePackageRestrictionsLPr(0); + } else { + if (users == null) { + readPackageRestrictionsLPr(0); + } else { + for (UserInfo user : users) { + readPackageRestrictionsLPr(user.id); + } + } } final int N = mPendingPackages.size(); @@ -1590,7 +1703,8 @@ final class Settings { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true); + pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, + UserHandle.ALL, true); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, "Unable to create application package for " + pp.name); @@ -1624,23 +1738,6 @@ final class Settings { } } - if (mBackupStoppedPackagesFilename.exists() - || mStoppedPackagesFilename.exists()) { - // Read old file - readStoppedLPw(); - mBackupStoppedPackagesFilename.delete(); - mStoppedPackagesFilename.delete(); - // Migrate to new file format - writePackageRestrictionsLPr(0); - } else { - if (users == null) { - readPackageRestrictionsLPr(0); - } else { - for (UserInfo user : users) { - readPackageRestrictionsLPr(user.id); - } - } - } mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + mSharedUsers.size() + " shared uids\n"); @@ -2276,6 +2373,10 @@ final class Settings { return ps; } + private String compToString(HashSet<String> cmp) { + return cmp != null ? Arrays.toString(cmp.toArray()) : "[]"; + } + boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) { if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { return true; @@ -2286,24 +2387,26 @@ final class Settings { Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " + componentInfo.packageName + " componentName = " + componentInfo.name); Log.v(PackageManagerService.TAG, "enabledComponents: " - + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray())); + + compToString(packageSettings.getEnabledComponents(userId))); Log.v(PackageManagerService.TAG, "disabledComponents: " - + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray())); + + compToString(packageSettings.getDisabledComponents(userId))); } if (packageSettings == null) { return false; } - final int enabled = packageSettings.getEnabled(userId); - if (enabled == COMPONENT_ENABLED_STATE_DISABLED - || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER + PackageUserState ustate = packageSettings.readUserState(userId); + if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED + || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled - && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { + && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { return false; } - if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) { + if (ustate.enabledComponents != null + && ustate.enabledComponents.contains(componentInfo.name)) { return true; } - if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) { + if (ustate.disabledComponents != null + && ustate.disabledComponents.contains(componentInfo.name)) { return false; } return componentInfo.enabled; @@ -2337,7 +2440,7 @@ final class Settings { boolean setPackageStoppedStateLPw(String packageName, boolean stopped, boolean allowedByPermission, int uid, int userId) { - int appId = UserId.getAppId(uid); + int appId = UserHandle.getAppId(uid); final PackageSetting pkgSetting = mPackages.get(packageName); if (pkgSetting == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -2362,7 +2465,7 @@ final class Settings { if (pkgSetting.installerPackageName != null) { PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgSetting.name, null, - pkgSetting.installerPackageName, null, userId); + pkgSetting.installerPackageName, null, new int[] {userId}); } pkgSetting.setNotLaunched(false, userId); } @@ -2374,9 +2477,7 @@ final class Settings { private List<UserInfo> getAllUsers() { long id = Binder.clearCallingIdentity(); try { - return AppGlobals.getPackageManager().getUsers(); - } catch (RemoteException re) { - // Local to system process, shouldn't happen + return UserManagerService.getInstance().getUsers(); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { @@ -2413,7 +2514,6 @@ final class Settings { ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION", ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE", ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP", - ApplicationInfo.FLAG_STOPPED, "STOPPED", ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK", ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", }; @@ -2505,8 +2605,8 @@ final class Settings { first = false; pw.print("anyDensity"); } + pw.println("]"); } - pw.println("]"); pw.print(" timeStamp="); date.setTime(ps.timeStamp); pw.println(sdf.format(date)); @@ -2520,25 +2620,31 @@ final class Settings { pw.print(" installerPackageName="); pw.println(ps.installerPackageName); } pw.print(" signatures="); pw.println(ps.signatures); - pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); - pw.print(" haveGids="); pw.println(ps.haveGids); - pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); - pw.print(" installStatus="); pw.print(ps.installStatus); + pw.print(" permissionsFixed="); pw.println(ps.permissionsFixed); + pw.print(" haveGids="); pw.println(ps.haveGids); + pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC); + pw.print(" installStatus="); pw.println(ps.installStatus); for (UserInfo user : users) { - pw.print(" User "); pw.print(user.id); pw.print(": "); + pw.print(" User "); pw.print(user.id); pw.print(": "); + pw.print(" installed="); + pw.print(ps.getInstalled(user.id)); pw.print(" stopped="); pw.print(ps.getStopped(user.id)); + pw.print(" notLaunched="); + pw.print(ps.getNotLaunched(user.id)); pw.print(" enabled="); pw.println(ps.getEnabled(user.id)); - if (ps.getDisabledComponents(user.id).size() > 0) { - pw.println(" disabledComponents:"); - for (String s : ps.getDisabledComponents(user.id)) { + HashSet<String> cmp = ps.getDisabledComponents(user.id); + if (cmp != null && cmp.size() > 0) { + pw.println(" disabledComponents:"); + for (String s : cmp) { pw.print(" "); pw.println(s); } } - if (ps.getEnabledComponents(user.id).size() > 0) { - pw.println(" enabledComponents:"); - for (String s : ps.getEnabledComponents(user.id)) { + cmp = ps.getEnabledComponents(user.id); + if (cmp != null && cmp.size() > 0) { + pw.println(" enabledComponents:"); + for (String s : cmp) { pw.print(" "); pw.println(s); } } diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/pm/ShutdownThread.java deleted file mode 100644 index 3675d41..0000000 --- a/services/java/com/android/server/pm/ShutdownThread.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.server.pm; - -import android.app.ActivityManagerNative; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.IActivityManager; -import android.app.ProgressDialog; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.IBluetooth; -import android.nfc.NfcAdapter; -import android.nfc.INfcAdapter; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Vibrator; -import android.os.SystemVibrator; -import android.os.storage.IMountService; -import android.os.storage.IMountShutdownObserver; - -import com.android.internal.telephony.ITelephony; -import com.android.server.PowerManagerService; - -import android.util.Log; -import android.view.WindowManager; - -public final class ShutdownThread extends Thread { - // constants - private static final String TAG = "ShutdownThread"; - private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500; - // maximum time we wait for the shutdown broadcast before going on. - private static final int MAX_BROADCAST_TIME = 10*1000; - private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000; - private static final int MAX_RADIO_WAIT_TIME = 12*1000; - - // length of vibration before shutting down - private static final int SHUTDOWN_VIBRATE_MS = 500; - - // state tracking - private static Object sIsStartedGuard = new Object(); - private static boolean sIsStarted = false; - - private static boolean mReboot; - private static boolean mRebootSafeMode; - private static String mRebootReason; - - // Provides shutdown assurance in case the system_server is killed - public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; - - // Indicates whether we are rebooting into safe mode - public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode"; - - // static instance of this thread - private static final ShutdownThread sInstance = new ShutdownThread(); - - private final Object mActionDoneSync = new Object(); - private boolean mActionDone; - private Context mContext; - private PowerManager mPowerManager; - private PowerManager.WakeLock mCpuWakeLock; - private PowerManager.WakeLock mScreenWakeLock; - private Handler mHandler; - - private static AlertDialog sConfirmDialog; - - private ShutdownThread() { - } - - /** - * Request a clean shutdown, waiting for subsystems to clean up their - * state etc. Must be called from a Looper thread in which its UI - * is shown. - * - * @param context Context used to display the shutdown progress dialog. - * @param confirm true if user confirmation is needed before shutting down. - */ - public static void shutdown(final Context context, boolean confirm) { - mReboot = false; - mRebootSafeMode = false; - shutdownInner(context, confirm); - } - - static void shutdownInner(final Context context, boolean confirm) { - // ensure that only one thread is trying to power down. - // any additional calls are just returned - synchronized (sIsStartedGuard) { - if (sIsStarted) { - Log.d(TAG, "Request to shutdown already running, returning."); - return; - } - } - - final int longPressBehavior = context.getResources().getInteger( - com.android.internal.R.integer.config_longPressOnPowerBehavior); - final int resourceId = mRebootSafeMode - ? com.android.internal.R.string.reboot_safemode_confirm - : (longPressBehavior == 2 - ? com.android.internal.R.string.shutdown_confirm_question - : com.android.internal.R.string.shutdown_confirm); - - Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); - - if (confirm) { - final CloseDialogReceiver closer = new CloseDialogReceiver(context); - if (sConfirmDialog != null) { - sConfirmDialog.dismiss(); - } - sConfirmDialog = new AlertDialog.Builder(context) - .setTitle(mRebootSafeMode - ? com.android.internal.R.string.reboot_safemode_title - : com.android.internal.R.string.power_off) - .setMessage(resourceId) - .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - beginShutdownSequence(context); - } - }) - .setNegativeButton(com.android.internal.R.string.no, null) - .create(); - closer.dialog = sConfirmDialog; - sConfirmDialog.setOnDismissListener(closer); - sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - sConfirmDialog.show(); - } else { - beginShutdownSequence(context); - } - } - - private static class CloseDialogReceiver extends BroadcastReceiver - implements DialogInterface.OnDismissListener { - private Context mContext; - public Dialog dialog; - - CloseDialogReceiver(Context context) { - mContext = context; - IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.registerReceiver(this, filter); - } - - @Override - public void onReceive(Context context, Intent intent) { - dialog.cancel(); - } - - public void onDismiss(DialogInterface unused) { - mContext.unregisterReceiver(this); - } - } - - /** - * Request a clean shutdown, waiting for subsystems to clean up their - * state etc. Must be called from a Looper thread in which its UI - * is shown. - * - * @param context Context used to display the shutdown progress dialog. - * @param reason code to pass to the kernel (e.g. "recovery"), or null. - * @param confirm true if user confirmation is needed before shutting down. - */ - public static void reboot(final Context context, String reason, boolean confirm) { - mReboot = true; - mRebootSafeMode = false; - mRebootReason = reason; - shutdownInner(context, confirm); - } - - /** - * Request a reboot into safe mode. Must be called from a Looper thread in which its UI - * is shown. - * - * @param context Context used to display the shutdown progress dialog. - * @param confirm true if user confirmation is needed before shutting down. - */ - public static void rebootSafeMode(final Context context, boolean confirm) { - mReboot = true; - mRebootSafeMode = true; - mRebootReason = null; - shutdownInner(context, confirm); - } - - private static void beginShutdownSequence(Context context) { - synchronized (sIsStartedGuard) { - if (sIsStarted) { - Log.d(TAG, "Shutdown sequence already running, returning."); - return; - } - sIsStarted = true; - } - - // throw up an indeterminate system dialog to indicate radio is - // shutting down. - ProgressDialog pd = new ProgressDialog(context); - pd.setTitle(context.getText(com.android.internal.R.string.power_off)); - pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); - pd.setIndeterminate(true); - pd.setCancelable(false); - pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - - pd.show(); - - sInstance.mContext = context; - sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - - // make sure we never fall asleep again - sInstance.mCpuWakeLock = null; - try { - sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); - sInstance.mCpuWakeLock.setReferenceCounted(false); - sInstance.mCpuWakeLock.acquire(); - } catch (SecurityException e) { - Log.w(TAG, "No permission to acquire wake lock", e); - sInstance.mCpuWakeLock = null; - } - - // also make sure the screen stays on for better user experience - sInstance.mScreenWakeLock = null; - if (sInstance.mPowerManager.isScreenOn()) { - try { - sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( - PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); - sInstance.mScreenWakeLock.setReferenceCounted(false); - sInstance.mScreenWakeLock.acquire(); - } catch (SecurityException e) { - Log.w(TAG, "No permission to acquire wake lock", e); - sInstance.mScreenWakeLock = null; - } - } - - // start the thread that initiates shutdown - sInstance.mHandler = new Handler() { - }; - sInstance.start(); - } - - void actionDone() { - synchronized (mActionDoneSync) { - mActionDone = true; - mActionDoneSync.notifyAll(); - } - } - - /** - * Makes sure we handle the shutdown gracefully. - * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. - */ - public void run() { - BroadcastReceiver br = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { - // We don't allow apps to cancel this, so ignore the result. - actionDone(); - } - }; - - /* - * Write a system property in case the system_server reboots before we - * get to the actual hardware restart. If that happens, we'll retry at - * the beginning of the SystemServer startup. - */ - { - String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : ""); - SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); - } - - /* - * If we are rebooting into safe mode, write a system property - * indicating so. - */ - if (mRebootSafeMode) { - SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); - } - - Log.i(TAG, "Sending shutdown broadcast..."); - - // First send the high-level shut down broadcast. - mActionDone = false; - mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null, - br, mHandler, 0, null, null); - - final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; - synchronized (mActionDoneSync) { - while (!mActionDone) { - long delay = endTime - SystemClock.elapsedRealtime(); - if (delay <= 0) { - Log.w(TAG, "Shutdown broadcast timed out"); - break; - } - try { - mActionDoneSync.wait(delay); - } catch (InterruptedException e) { - } - } - } - - Log.i(TAG, "Shutting down activity manager..."); - - final IActivityManager am = - ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); - if (am != null) { - try { - am.shutdown(MAX_BROADCAST_TIME); - } catch (RemoteException e) { - } - } - - // Shutdown radios. - shutdownRadios(MAX_RADIO_WAIT_TIME); - - // Shutdown MountService to ensure media is in a safe state - IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { - public void onShutDownComplete(int statusCode) throws RemoteException { - Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown"); - actionDone(); - } - }; - - Log.i(TAG, "Shutting down MountService"); - - // Set initial variables and time out time. - mActionDone = false; - final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; - synchronized (mActionDoneSync) { - try { - final IMountService mount = IMountService.Stub.asInterface( - ServiceManager.checkService("mount")); - if (mount != null) { - mount.shutdown(observer); - } else { - Log.w(TAG, "MountService unavailable for shutdown"); - } - } catch (Exception e) { - Log.e(TAG, "Exception during MountService shutdown", e); - } - while (!mActionDone) { - long delay = endShutTime - SystemClock.elapsedRealtime(); - if (delay <= 0) { - Log.w(TAG, "Shutdown wait timed out"); - break; - } - try { - mActionDoneSync.wait(delay); - } catch (InterruptedException e) { - } - } - } - - rebootOrShutdown(mReboot, mRebootReason); - } - - private void shutdownRadios(int timeout) { - // If a radio is wedged, disabling it may hang so we do this work in another thread, - // just in case. - final long endTime = SystemClock.elapsedRealtime() + timeout; - final boolean[] done = new boolean[1]; - Thread t = new Thread() { - public void run() { - boolean nfcOff; - boolean bluetoothOff; - boolean radioOff; - - final INfcAdapter nfc = - INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc")); - final ITelephony phone = - ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); - final IBluetooth bluetooth = - IBluetooth.Stub.asInterface(ServiceManager.checkService( - BluetoothAdapter.BLUETOOTH_SERVICE)); - - try { - nfcOff = nfc == null || - nfc.getState() == NfcAdapter.STATE_OFF; - if (!nfcOff) { - Log.w(TAG, "Turning off NFC..."); - nfc.disable(false); // Don't persist new state - } - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during NFC shutdown", ex); - nfcOff = true; - } - - try { - bluetoothOff = bluetooth == null || - bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF; - if (!bluetoothOff) { - Log.w(TAG, "Disabling Bluetooth..."); - bluetooth.disable(false); // disable but don't persist new state - } - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during bluetooth shutdown", ex); - bluetoothOff = true; - } - - try { - radioOff = phone == null || !phone.isRadioOn(); - if (!radioOff) { - Log.w(TAG, "Turning off radio..."); - phone.setRadio(false); - } - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during radio shutdown", ex); - radioOff = true; - } - - Log.i(TAG, "Waiting for NFC, Bluetooth and Radio..."); - - while (SystemClock.elapsedRealtime() < endTime) { - if (!bluetoothOff) { - try { - bluetoothOff = - bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF; - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during bluetooth shutdown", ex); - bluetoothOff = true; - } - if (bluetoothOff) { - Log.i(TAG, "Bluetooth turned off."); - } - } - if (!radioOff) { - try { - radioOff = !phone.isRadioOn(); - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during radio shutdown", ex); - radioOff = true; - } - if (radioOff) { - Log.i(TAG, "Radio turned off."); - } - } - if (!nfcOff) { - try { - nfcOff = nfc.getState() == NfcAdapter.STATE_OFF; - } catch (RemoteException ex) { - Log.e(TAG, "RemoteException during NFC shutdown", ex); - nfcOff = true; - } - if (radioOff) { - Log.i(TAG, "NFC turned off."); - } - } - - if (radioOff && bluetoothOff && nfcOff) { - Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete."); - done[0] = true; - break; - } - SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC); - } - } - }; - - t.start(); - try { - t.join(timeout); - } catch (InterruptedException ex) { - } - if (!done[0]) { - Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown."); - } - } - - /** - * Do not call this directly. Use {@link #reboot(Context, String, boolean)} - * or {@link #shutdown(Context, boolean)} instead. - * - * @param reboot true to reboot or false to shutdown - * @param reason reason for reboot - */ - public static void rebootOrShutdown(boolean reboot, String reason) { - if (reboot) { - Log.i(TAG, "Rebooting, reason: " + reason); - try { - PowerManagerService.lowLevelReboot(reason); - } catch (Exception e) { - Log.e(TAG, "Reboot failed, will attempt shutdown instead", e); - } - } else if (SHUTDOWN_VIBRATE_MS > 0) { - // vibrate before shutting down - Vibrator vibrator = new SystemVibrator(); - try { - vibrator.vibrate(SHUTDOWN_VIBRATE_MS); - } catch (Exception e) { - // Failure to vibrate shouldn't interrupt shutdown. Just log it. - Log.w(TAG, "Failed to vibrate during shutdown.", e); - } - - // vibrator is asynchronous so we need to wait to avoid shutting down too soon. - try { - Thread.sleep(SHUTDOWN_VIBRATE_MS); - } catch (InterruptedException unused) { - } - } - - // Shutdown power - Log.i(TAG, "Performing low-level shutdown..."); - PowerManagerService.lowLevelShutdown(); - } -} diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java deleted file mode 100644 index 4e9e666..0000000 --- a/services/java/com/android/server/pm/UserManager.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.pm; - -import com.android.internal.util.ArrayUtils; -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.os.UserId; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; -import android.util.Xml; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -public class UserManager { - private static final String TAG_NAME = "name"; - - private static final String ATTR_FLAGS = "flags"; - - private static final String ATTR_ID = "id"; - - private static final String TAG_USERS = "users"; - - private static final String TAG_USER = "user"; - - private static final String LOG_TAG = "UserManager"; - - private static final String USER_INFO_DIR = "system" + File.separator + "users"; - private static final String USER_LIST_FILENAME = "userlist.xml"; - - private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); - - private final File mUsersDir; - private final File mUserListFile; - private int[] mUserIds; - - private Installer mInstaller; - private File mBaseUserPath; - - /** - * Available for testing purposes. - */ - UserManager(File dataDir, File baseUserPath) { - mUsersDir = new File(dataDir, USER_INFO_DIR); - mUsersDir.mkdirs(); - // Make zeroth user directory, for services to migrate their files to that location - File userZeroDir = new File(mUsersDir, "0"); - userZeroDir.mkdirs(); - mBaseUserPath = baseUserPath; - FileUtils.setPermissions(mUsersDir.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG - |FileUtils.S_IROTH|FileUtils.S_IXOTH, - -1, -1); - mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); - readUserList(); - } - - public UserManager(Installer installer, File baseUserPath) { - this(Environment.getDataDirectory(), baseUserPath); - mInstaller = installer; - } - - public List<UserInfo> getUsers() { - synchronized (mUsers) { - ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); - for (int i = 0; i < mUsers.size(); i++) { - users.add(mUsers.valueAt(i)); - } - return users; - } - } - - public UserInfo getUser(int userId) { - synchronized (mUsers) { - UserInfo info = mUsers.get(userId); - return info; - } - } - - public boolean exists(int userId) { - synchronized (mUsers) { - return ArrayUtils.contains(mUserIds, userId); - } - } - - public void updateUserName(int userId, String name) { - synchronized (mUsers) { - UserInfo info = mUsers.get(userId); - if (name != null && !name.equals(info.name)) { - info.name = name; - writeUserLocked(info); - } - } - } - - /** - * 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() { - synchronized (mUsers) { - readUserListLocked(); - } - } - - private void readUserListLocked() { - if (!mUserListFile.exists()) { - fallbackToSingleUserLocked(); - return; - } - FileInputStream fis = null; - try { - fis = new FileInputStream(mUserListFile); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - Slog.e(LOG_TAG, "Unable to read user list"); - fallbackToSingleUserLocked(); - return; - } - - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { - String id = parser.getAttributeValue(null, ATTR_ID); - UserInfo user = readUser(Integer.parseInt(id)); - if (user != null) { - mUsers.put(user.id, user); - } - } - } - updateUserIdsLocked(); - } catch (IOException ioe) { - fallbackToSingleUserLocked(); - } catch (XmlPullParserException pe) { - fallbackToSingleUserLocked(); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - } - - private void fallbackToSingleUserLocked() { - // Create the primary user - UserInfo primary = new UserInfo(0, "Primary", - UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); - mUsers.put(0, primary); - updateUserIdsLocked(); - - writeUserListLocked(); - writeUserLocked(primary); - } - - /* - * Writes the user file in this format: - * - * <user flags="20039023" id="0"> - * <name>Primary</name> - * </user> - */ - private void writeUserLocked(UserInfo userInfo) { - FileOutputStream fos = null; - try { - final File mUserFile = new File(mUsersDir, userInfo.id + ".xml"); - fos = new FileOutputStream(mUserFile); - final BufferedOutputStream bos = new BufferedOutputStream(fos); - - // XmlSerializer serializer = XmlUtils.serializerInstance(); - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(bos, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, TAG_USER); - serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); - serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); - - serializer.startTag(null, TAG_NAME); - serializer.text(userInfo.name); - serializer.endTag(null, TAG_NAME); - - serializer.endTag(null, TAG_USER); - - serializer.endDocument(); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException ioe) { - } - } - } - } - - /* - * Writes the user list file in this format: - * - * <users> - * <user id="0"></user> - * <user id="2"></user> - * </users> - */ - private void writeUserListLocked() { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(mUserListFile); - final BufferedOutputStream bos = new BufferedOutputStream(fos); - - // XmlSerializer serializer = XmlUtils.serializerInstance(); - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(bos, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, TAG_USERS); - - for (int i = 0; i < mUsers.size(); i++) { - UserInfo user = mUsers.valueAt(i); - serializer.startTag(null, TAG_USER); - serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); - serializer.endTag(null, TAG_USER); - } - - serializer.endTag(null, TAG_USERS); - - serializer.endDocument(); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "Error writing user list"); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException ioe) { - } - } - } - } - - private UserInfo readUser(int id) { - int flags = 0; - String name = null; - - FileInputStream fis = null; - try { - File userFile = new File(mUsersDir, Integer.toString(id) + ".xml"); - fis = new FileInputStream(userFile); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - 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(LOG_TAG, "User id does not match the file name"); - return null; - } - String flagString = parser.getAttributeValue(null, ATTR_FLAGS); - flags = Integer.parseInt(flagString); - - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - } - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { - type = parser.next(); - if (type == XmlPullParser.TEXT) { - name = parser.getText(); - } - } - } - - UserInfo userInfo = new UserInfo(id, name, flags); - return userInfo; - - } catch (IOException ioe) { - } catch (XmlPullParserException pe) { - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - return null; - } - - public UserInfo createUser(String name, int flags) { - int userId = getNextAvailableId(); - UserInfo userInfo = new UserInfo(userId, name, flags); - File userPath = new File(mBaseUserPath, Integer.toString(userId)); - if (!createPackageFolders(userId, userPath)) { - return null; - } - synchronized (mUsers) { - mUsers.put(userId, userInfo); - writeUserListLocked(); - writeUserLocked(userInfo); - updateUserIdsLocked(); - } - 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 boolean removeUser(int id) { - synchronized (mUsers) { - return removeUserLocked(id); - } - } - - private boolean removeUserLocked(int id) { - // Remove from the list - UserInfo userInfo = mUsers.get(id); - if (userInfo != null) { - // Remove this user from the list - mUsers.remove(id); - // Remove user file - File userFile = new File(mUsersDir, id + ".xml"); - userFile.delete(); - // Update the user list - writeUserListLocked(); - updateUserIdsLocked(); - return true; - } - return false; - } - - 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, UserId.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 updateUserIdsLocked() { - 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. - * TODO: May not be a good idea to recycle ids, in case it results in confusion - * for data and battery stats collection, or unexpected cross-talk. - * @return - */ - private int getNextAvailableId() { - int i = 0; - while (i < Integer.MAX_VALUE) { - if (mUsers.indexOfKey(i) < 0) { - break; - } - i++; - } - return i; - } - - private boolean createPackageFolders(int id, File userPath) { - // mInstaller may not be available for unit-tests. - if (mInstaller == null) return true; - - // Create the user path - userPath.mkdir(); - FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IXOTH, -1, -1); - - mInstaller.cloneUserData(0, id, false); - - return true; - } - - boolean removePackageFolders(int id) { - // mInstaller may not be available for unit-tests. - if (mInstaller == null) return true; - - mInstaller.removeUserDataDirs(id); - return true; - } -} diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java new file mode 100644 index 0000000..be86628 --- /dev/null +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -0,0 +1,723 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastXmlSerializer; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IStopUserCallback; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; +import android.os.IUserManager; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +public class UserManagerService extends IUserManager.Stub { + + private static final String LOG_TAG = "UserManagerService"; + + private static final String TAG_NAME = "name"; + private static final String ATTR_FLAGS = "flags"; + private static final String ATTR_ICON_PATH = "icon"; + private static final String ATTR_ID = "id"; + private static final String ATTR_SERIAL_NO = "serialNumber"; + private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; + private static final String TAG_USERS = "users"; + private static final String TAG_USER = "user"; + + private static final String USER_INFO_DIR = "system" + File.separator + "users"; + private static final String USER_LIST_FILENAME = "userlist.xml"; + private static final String USER_PHOTO_FILENAME = "photo.png"; + + private final Context mContext; + private final PackageManagerService mPm; + private final Object mInstallLock; + private final Object mPackagesLock; + + private final File mUsersDir; + private final File mUserListFile; + private final File mBaseUserPath; + + private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); + + private final int mUserLimit; + + private int[] mUserIds; + private boolean mGuestEnabled; + private int mNextSerialNumber; + + private static UserManagerService sInstance; + + public static UserManagerService getInstance() { + synchronized (UserManagerService.class) { + return sInstance; + } + } + + /** + * Available for testing purposes. + */ + UserManagerService(File dataDir, File baseUserPath) { + this(null, null, new Object(), new Object(), dataDir, baseUserPath); + } + + /** + * Called by package manager to create the service. This is closely + * associated with the package manager, and the given lock is the + * package manager's own lock. + */ + UserManagerService(Context context, PackageManagerService pm, + Object installLock, Object packagesLock) { + this(context, pm, installLock, packagesLock, + Environment.getDataDirectory(), + new File(Environment.getDataDirectory(), "user")); + } + + /** + * Available for testing purposes. + */ + private UserManagerService(Context context, PackageManagerService pm, + Object installLock, Object packagesLock, + File dataDir, File baseUserPath) { + synchronized (UserManagerService.class) { + mContext = context; + mPm = pm; + mInstallLock = installLock; + mPackagesLock = packagesLock; + mUserLimit = mContext.getResources().getInteger( + com.android.internal.R.integer.config_multiuserMaximumUsers); + mUsersDir = new File(dataDir, USER_INFO_DIR); + mUsersDir.mkdirs(); + // Make zeroth user directory, for services to migrate their files to that location + File userZeroDir = new File(mUsersDir, "0"); + userZeroDir.mkdirs(); + mBaseUserPath = baseUserPath; + FileUtils.setPermissions(mUsersDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); + readUserList(); + sInstance = this; + } + } + + @Override + public List<UserInfo> getUsers() { + checkManageUsersPermission("query users"); + synchronized (mPackagesLock) { + ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); + for (int i = 0; i < mUsers.size(); i++) { + users.add(mUsers.valueAt(i)); + } + return users; + } + } + + @Override + public UserInfo getUserInfo(int userId) { + checkManageUsersPermission("query user"); + synchronized (mPackagesLock) { + return getUserInfoLocked(userId); + } + } + + /* + * Should be locked on mUsers before calling this. + */ + private UserInfo getUserInfoLocked(int userId) { + return mUsers.get(userId); + } + + public boolean exists(int userId) { + synchronized (mPackagesLock) { + return ArrayUtils.contains(mUserIds, userId); + } + } + + @Override + public void setUserName(int userId, String name) { + checkManageUsersPermission("rename users"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (name != null && !name.equals(info.name)) { + info.name = name; + writeUserLocked(info); + } + } + sendUserInfoChangedBroadcast(userId); + } + + @Override + public void setUserIcon(int userId, Bitmap bitmap) { + checkManageUsersPermission("update users"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (info == null) return; + writeBitmapLocked(info, bitmap); + writeUserLocked(info); + } + sendUserInfoChangedBroadcast(userId); + } + + private void sendUserInfoChangedBroadcast(int userId) { + Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED); + changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId)); + } + + @Override + public Bitmap getUserIcon(int userId) { + checkManageUsersPermission("read users"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (info == null || info.iconPath == null) return null; + return BitmapFactory.decodeFile(info.iconPath); + } + } + + @Override + public void setGuestEnabled(boolean enable) { + checkManageUsersPermission("enable guest users"); + synchronized (mPackagesLock) { + if (mGuestEnabled != enable) { + mGuestEnabled = enable; + // Erase any guest user that currently exists + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + if (user.isGuest()) { + if (!enable) { + removeUser(user.id); + } + return; + } + } + // No guest was found + if (enable) { + createUser("Guest", UserInfo.FLAG_GUEST); + } + } + } + } + + @Override + public boolean isGuestEnabled() { + synchronized (mPackagesLock) { + return mGuestEnabled; + } + } + + @Override + public void wipeUser(int userHandle) { + checkManageUsersPermission("wipe user"); + // TODO: + } + + public void makeInitialized(int userId) { + checkManageUsersPermission("makeInitialized"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) { + info.flags |= UserInfo.FLAG_INITIALIZED; + writeUserLocked(info); + } + } + } + + /** + * Check if we've hit the limit of how many users can be created. + */ + private boolean isUserLimitReachedLocked() { + int nUsers = mUsers.size(); + return nUsers >= mUserLimit; + } + + /** + * Enforces that only the system UID or root's UID or apps that have the + * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS} + * permission can make certain calls to the UserManager. + * + * @param message used as message if SecurityException is thrown + * @throws SecurityException if the caller is not system or root + */ + private static final void checkManageUsersPermission(String message) { + final int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID && uid != 0 + && ActivityManager.checkComponentPermission( + android.Manifest.permission.MANAGE_USERS, + uid, -1, true) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need MANAGE_USERS permission to: " + message); + } + } + + private void writeBitmapLocked(UserInfo info, Bitmap bitmap) { + try { + File dir = new File(mUsersDir, Integer.toString(info.id)); + File file = new File(dir, USER_PHOTO_FILENAME); + if (!dir.exists()) { + dir.mkdir(); + FileUtils.setPermissions( + dir.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } + FileOutputStream os; + if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) { + info.iconPath = file.getAbsolutePath(); + } + try { + os.close(); + } catch (IOException ioe) { + // What the ... ! + } + } catch (FileNotFoundException e) { + Slog.w(LOG_TAG, "Error setting photo for user ", e); + } + } + + /** + * 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. + */ + public int[] getUserIds() { + synchronized (mPackagesLock) { + return mUserIds; + } + } + + int[] getUserIdsLPr() { + return mUserIds; + } + + private void readUserList() { + synchronized (mPackagesLock) { + readUserListLocked(); + } + } + + private void readUserListLocked() { + mGuestEnabled = false; + if (!mUserListFile.exists()) { + fallbackToSingleUserLocked(); + return; + } + FileInputStream fis = null; + AtomicFile userListFile = new AtomicFile(mUserListFile); + try { + fis = userListFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(LOG_TAG, "Unable to read user list"); + fallbackToSingleUserLocked(); + return; + } + + mNextSerialNumber = -1; + if (parser.getName().equals(TAG_USERS)) { + String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO); + if (lastSerialNumber != null) { + mNextSerialNumber = Integer.parseInt(lastSerialNumber); + } + } + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + String id = parser.getAttributeValue(null, ATTR_ID); + UserInfo user = readUser(Integer.parseInt(id)); + if (user != null) { + mUsers.put(user.id, user); + if (user.isGuest()) { + mGuestEnabled = true; + } + if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { + mNextSerialNumber = user.id + 1; + } + } + } + } + updateUserIdsLocked(); + } catch (IOException ioe) { + fallbackToSingleUserLocked(); + } catch (XmlPullParserException pe) { + fallbackToSingleUserLocked(); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + } + + private void fallbackToSingleUserLocked() { + // Create the primary user + UserInfo primary = new UserInfo(0, "Primary", null, + UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); + mUsers.put(0, primary); + updateUserIdsLocked(); + + writeUserListLocked(); + writeUserLocked(primary); + } + + /* + * Writes the user file in this format: + * + * <user flags="20039023" id="0"> + * <name>Primary</name> + * </user> + */ + private void writeUserLocked(UserInfo userInfo) { + FileOutputStream fos = null; + AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml")); + try { + fos = userFile.startWrite(); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); + serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); + if (userInfo.iconPath != null) { + serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); + } + + serializer.startTag(null, TAG_NAME); + serializer.text(userInfo.name); + serializer.endTag(null, TAG_NAME); + + serializer.endTag(null, TAG_USER); + + serializer.endDocument(); + userFile.finishWrite(fos); + } catch (Exception ioe) { + Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe); + userFile.failWrite(fos); + } + } + + /* + * Writes the user list file in this format: + * + * <users nextSerialNumber="3"> + * <user id="0"></user> + * <user id="2"></user> + * </users> + */ + private void writeUserListLocked() { + FileOutputStream fos = null; + AtomicFile userListFile = new AtomicFile(mUserListFile); + try { + fos = userListFile.startWrite(); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USERS); + serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber)); + + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); + serializer.endTag(null, TAG_USER); + } + + serializer.endTag(null, TAG_USERS); + + serializer.endDocument(); + userListFile.finishWrite(fos); + } catch (Exception e) { + userListFile.failWrite(fos); + Slog.e(LOG_TAG, "Error writing user list"); + } + } + + private UserInfo readUser(int id) { + int flags = 0; + int serialNumber = id; + String name = null; + String iconPath = null; + + FileInputStream fis = null; + try { + AtomicFile userFile = + new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml")); + fis = userFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + 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(LOG_TAG, "User id does not match the file name"); + return null; + } + String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO); + if (serialNumberValue != null) { + serialNumber = Integer.parseInt(serialNumberValue); + } + String flagString = parser.getAttributeValue(null, ATTR_FLAGS); + flags = Integer.parseInt(flagString); + iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); + + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + name = parser.getText(); + } + } + } + + UserInfo userInfo = new UserInfo(id, name, iconPath, flags); + userInfo.serialNumber = serialNumber; + return userInfo; + + } catch (IOException ioe) { + } catch (XmlPullParserException pe) { + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + return null; + } + + @Override + public UserInfo createUser(String name, int flags) { + checkManageUsersPermission("Only the system can create users"); + + final long ident = Binder.clearCallingIdentity(); + final UserInfo userInfo; + try { + synchronized (mInstallLock) { + synchronized (mPackagesLock) { + if (isUserLimitReachedLocked()) return null; + int userId = getNextAvailableIdLocked(); + userInfo = new UserInfo(userId, name, null, flags); + File userPath = new File(mBaseUserPath, Integer.toString(userId)); + userInfo.serialNumber = mNextSerialNumber++; + mUsers.put(userId, userInfo); + writeUserListLocked(); + writeUserLocked(userInfo); + updateUserIdsLocked(); + mPm.createNewUserLILPw(userId, userPath); + } + } + if (userInfo != null) { + Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + 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 boolean removeUser(int userHandle) { + checkManageUsersPermission("Only the system can remove users"); + final UserInfo user; + synchronized (mPackagesLock) { + user = mUsers.get(userHandle); + if (userHandle == 0 || user == null) { + return false; + } + } + + int res; + try { + res = ActivityManagerNative.getDefault().stopUser(userHandle, + new IStopUserCallback.Stub() { + @Override + public void userStopped(int userId) { + finishRemoveUser(userId); + } + @Override + public void userStopAborted(int userId) { + } + }); + } catch (RemoteException e) { + return false; + } + + return res == ActivityManager.USER_OP_SUCCESS; + } + + void finishRemoveUser(int userHandle) { + synchronized (mInstallLock) { + synchronized (mPackagesLock) { + // Cleanup package manager settings + mPm.cleanUpUserLILPw(userHandle); + + // Remove this user from the list + mUsers.remove(userHandle); + // Remove user file + AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); + userFile.delete(); + // Update the user list + writeUserListLocked(); + updateUserIdsLocked(); + removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle)); + } + } + + // Let other services shutdown any activity + long ident = Binder.clearCallingIdentity(); + try { + Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void removeDirectoryRecursive(File parent) { + if (parent.isDirectory()) { + String[] files = parent.list(); + for (String filename : files) { + File child = new File(parent, filename); + removeDirectoryRecursive(child); + } + } + parent.delete(); + } + + @Override + public int getUserSerialNumber(int userHandle) { + synchronized (mPackagesLock) { + if (!exists(userHandle)) return -1; + return getUserInfoLocked(userHandle).serialNumber; + } + } + + @Override + public int getUserHandle(int userSerialNumber) { + synchronized (mPackagesLock) { + for (int userId : mUserIds) { + if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId; + } + // Not found + return -1; + } + } + + /** + * Caches the list of user ids in an array, adjusting the array size when necessary. + */ + private void updateUserIdsLocked() { + int[] newUsers = new int[mUsers.size()]; + for (int i = 0; i < mUsers.size(); i++) { + newUsers[i] = mUsers.keyAt(i); + } + mUserIds = newUsers; + } + + /** + * Returns the next available user id, filling in any holes in the ids. + * TODO: May not be a good idea to recycle ids, in case it results in confusion + * for data and battery stats collection, or unexpected cross-talk. + * @return + */ + private int getNextAvailableIdLocked() { + synchronized (mPackagesLock) { + int i = 10; + while (i < Integer.MAX_VALUE) { + if (mUsers.indexOfKey(i) < 0) { + break; + } + i++; + } + return i; + } + } +} |
