summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2015-04-08 21:02:14 -0700
committerJeff Sharkey <jsharkey@android.com>2015-04-10 00:24:03 -0700
commitb9f3674c11ed9c89b80a69f728cbc5f540b2ecde (patch)
treebef6b7bbd6eb34aa933aa137056a39cac46854aa
parentd119e52050aecffbc66f14d2e2af5e873211a6de (diff)
downloadframeworks_base-b9f3674c11ed9c89b80a69f728cbc5f540b2ecde.zip
frameworks_base-b9f3674c11ed9c89b80a69f728cbc5f540b2ecde.tar.gz
frameworks_base-b9f3674c11ed9c89b80a69f728cbc5f540b2ecde.tar.bz2
Support moving apps to expanded storage.
Start deriving the data path for apps based on the volume UUID where the app lives. This path is used for all higher-level APIs, giving us a clean place to switch app storage. When parsing a package, keep track of the volume UUID where it lives and update PackageSetting once installed. For now continue treating moves as installs, but we'll eventually clean this up to avoid the additional dexopt pass. Wire up move to use the new installd command to move private data between devices. Cache LoadedApk only for the current user, since otherwise the data dir points at the wrong path. Bug: 19993667 Change-Id: I53336e3b147d5fd3130e6800869af172b628da37
-rw-r--r--core/java/android/app/ActivityThread.java19
-rw-r--r--core/java/android/app/LoadedApk.java6
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/Installer.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java64
-rw-r--r--services/core/java/com/android/server/pm/Settings.java18
9 files changed, 110 insertions, 61 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ed05321..ed7d11b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -220,6 +220,7 @@ public final class ActivityThread {
// which means this lock gets held while the activity and window managers
// holds their own lock. Thus you MUST NEVER call back into the activity manager
// or window manager or anything that depends on them while holding this lock.
+ // These LoadedApk are only valid for the userId that we're running as.
final ArrayMap<String, WeakReference<LoadedApk>> mPackages
= new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
@@ -1705,13 +1706,18 @@ public final class ActivityThread {
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags, int userId) {
+ final boolean differentUser = (UserHandle.myUserId() != userId);
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
- if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
+ if (differentUser) {
+ // Caching not supported across users
+ ref = null;
+ } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) {
ref = mPackages.get(packageName);
} else {
ref = mResourcePackages.get(packageName);
}
+
LoadedApk packageInfo = ref != null ? ref.get() : null;
//Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
//if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
@@ -1791,13 +1797,18 @@ public final class ActivityThread {
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
+ final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
- if (includeCode) {
+ if (differentUser) {
+ // Caching not supported across users
+ ref = null;
+ } else if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
+
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
@@ -1816,7 +1827,9 @@ public final class ActivityThread {
getSystemContext().mPackageInfo.getClassLoader());
}
- if (includeCode) {
+ if (differentUser) {
+ // Caching not supported across users
+ } else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 83c6c2b..9604789 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -18,7 +18,6 @@ package android.app;
import android.text.TextUtils;
import android.util.ArrayMap;
-
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -46,6 +45,7 @@ import android.util.SparseArray;
import android.view.DisplayAdjustments;
import android.view.Display;
import android.os.SystemProperties;
+
import dalvik.system.VMRuntime;
import java.io.File;
@@ -136,10 +136,6 @@ public final class LoadedApk {
mSplitAppDirs = aInfo.splitSourceDirs;
mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
mOverlayDirs = aInfo.resourceDirs;
- if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
- aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
- mPackageName);
- }
mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 303b709..491fc94 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
+import android.text.TextUtils;
import android.util.AndroidException;
import com.android.internal.util.ArrayUtils;
@@ -4149,16 +4150,19 @@ public abstract class PackageManager {
public abstract @NonNull PackageInstaller getPackageInstaller();
/**
- * Returns the data directory for a particular user and package, given the uid of the package.
- * @param uid uid of the package, including the userId and appId
- * @param packageName name of the package
- * @return the user-specific data directory for the package
+ * Returns the data directory for a particular package and user.
+ *
* @hide
*/
- public static String getDataDirForUser(int userId, String packageName) {
+ public static File getDataDirForUser(String volumeUuid, String packageName, int userId) {
// TODO: This should be shared with Installer's knowledge of user directory
- return Environment.getDataDirectory().toString() + "/user/" + userId
- + "/" + packageName;
+ final File base;
+ if (TextUtils.isEmpty(volumeUuid)) {
+ base = Environment.getDataDirectory();
+ } else {
+ base = new File("/mnt/expand/" + volumeUuid);
+ }
+ return new File(base, "user/" + userId + "/" + packageName);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7523675..763a017 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -111,6 +111,9 @@ public class PackageParser {
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ /** Path prefix for apps on expanded storage */
+ private static final String MNT_EXPAND = "/mnt/expand/";
+
/** @hide */
public static class NewPermissionInfo {
public final String name;
@@ -860,6 +863,12 @@ public class PackageParser {
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
+ String volumeUuid = null;
+ if (apkPath.startsWith(MNT_EXPAND)) {
+ final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+ volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
+ }
+
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
@@ -882,6 +891,7 @@ public class PackageParser {
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
+ pkg.volumeUuid = volumeUuid;
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
@@ -4206,6 +4216,8 @@ public class PackageParser {
// TODO: work towards making these paths invariant
+ public String volumeUuid;
+
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
@@ -4727,7 +4739,8 @@ public class PackageParser {
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
if (userId != 0) {
ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
}
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
@@ -4755,7 +4768,8 @@ public class PackageParser {
ai = new ApplicationInfo(ai);
if (userId != 0) {
ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
}
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a59581b..279bfbf 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -576,14 +576,6 @@ public class PackageManagerTests extends AndroidTestCase {
fail(pkgName + " shouldnt be installed");
} catch (NameNotFoundException e) {
}
-
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- List<UserInfo> users = um.getUsers();
- for (UserInfo user : users) {
- String dataDir = PackageManager.getDataDirForUser(user.id, pkgName);
- assertFalse("Application data directory should not exist: " + dataDir,
- new File(dataDir).exists());
- }
}
class InstallParams {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7193384..ebf1930 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19608,8 +19608,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (info == null) return null;
ApplicationInfo newInfo = new ApplicationInfo(info);
newInfo.uid = applyUserId(info.uid, userId);
- newInfo.dataDir = USER_DATA_DIR + userId + "/"
- + info.packageName;
+ newInfo.dataDir = PackageManager.getDataDirForUser(info.volumeUuid, info.packageName,
+ userId).getAbsolutePath();
return newInfo;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index ce31f98..a32363d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -261,6 +261,22 @@ public final class Installer extends SystemService {
return mInstaller.execute(builder.toString());
}
+ public int moveUserDataDirs(String fromUuid, String toUuid, String packageName, int appId,
+ String seinfo) {
+ StringBuilder builder = new StringBuilder("mvuserdata");
+ builder.append(' ');
+ builder.append(escapeNull(fromUuid));
+ builder.append(' ');
+ builder.append(escapeNull(toUuid));
+ builder.append(' ');
+ builder.append(packageName);
+ builder.append(' ');
+ builder.append(appId);
+ builder.append(' ');
+ builder.append(seinfo);
+ return mInstaller.execute(builder.toString());
+ }
+
@Deprecated
public int clearUserData(String name, int userId) {
return clearUserData(null, name, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f5042ed..9465682 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -51,6 +51,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MOVE_EXTERNAL_MEDIA;
import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
+import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.MOVE_INTERNAL;
@@ -71,7 +72,6 @@ import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import android.Manifest;
-import org.xmlpull.v1.XmlPullParser;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -202,6 +202,7 @@ import com.android.server.Watchdog;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
@@ -2597,8 +2598,8 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags;
- pkg.applicationInfo.dataDir =
- getDataPathForPackage(packageName, 0).getPath();
+ pkg.applicationInfo.dataDir = PackageManager.getDataDirForUser(ps.volumeUuid,
+ packageName, userId).getAbsolutePath();
pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
@@ -5103,6 +5104,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Set application objects path explicitly.
+ pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
pkg.applicationInfo.setCodePath(pkg.codePath);
pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
@@ -5510,20 +5512,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private File getDataPathForPackage(String packageName, int userId) {
- /*
- * Until we fully support multiple users, return the directory we
- * previously would have. The PackageManagerTests will need to be
- * revised when this is changed back..
- */
- if (userId == 0) {
- return new File(mAppDataDir, packageName);
- } else {
- return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId
- + File.separator + packageName);
- }
- }
-
private int createDataDirsLI(String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
int res = mInstaller.install(packageName, uid, uid, seinfo);
@@ -6043,7 +6031,8 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
// This is a normal package, need to make its data directory.
- dataPath = getDataPathForPackage(pkg.packageName, 0);
+ dataPath = PackageManager.getDataDirForUser(pkg.volumeUuid, pkg.packageName,
+ UserHandle.USER_OWNER);
boolean uidError = false;
if (dataPath.exists()) {
@@ -10136,6 +10125,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.splitCodePaths);
// Reflect the rename in app info
+ pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
pkg.applicationInfo.setCodePath(pkg.codePath);
pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
@@ -10426,6 +10416,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.splitCodePaths);
// Reflect the rename in app info
+ pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
pkg.applicationInfo.setCodePath(pkg.codePath);
pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
@@ -10684,7 +10675,8 @@ public class PackageManagerService extends IPackageManager.Stub {
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
- boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
+ final boolean dataDirExists = PackageManager.getDataDirForUser(volumeUuid, pkgName,
+ UserHandle.USER_OWNER).exists();
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
// A package with the same name is already installed, though
@@ -11056,7 +11048,6 @@ public class PackageManagerService extends IPackageManager.Stub {
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
- mSettings.setVolumeUuid(pkgName, volumeUuid);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
mSettings.writeLPr();
@@ -14175,11 +14166,12 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean andData, final IPackageMoveObserver observer) throws PackageManagerException {
final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
- File codeFile = null;
- String installerPackageName = null;
- String packageAbiOverride = null;
-
- // TOOD: move app private data before installing
+ final String currentVolumeUuid;
+ final File codeFile;
+ final String installerPackageName;
+ final String packageAbiOverride;
+ final int appId;
+ final String seinfo;
// reader
synchronized (mPackages) {
@@ -14201,9 +14193,31 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.mOperationPending = true;
+ currentVolumeUuid = ps.volumeUuid;
codeFile = new File(pkg.codePath);
installerPackageName = ps.installerPackageName;
packageAbiOverride = ps.cpuAbiOverrideString;
+ appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+ seinfo = pkg.applicationInfo.seinfo;
+ }
+
+ if (andData) {
+ Slog.d(TAG, "Moving " + packageName + " private data from " + currentVolumeUuid + " to "
+ + volumeUuid);
+ synchronized (mInstallLock) {
+ if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId,
+ seinfo) != 0) {
+ synchronized (mPackages) {
+ final PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg != null) {
+ pkg.mOperationPending = false;
+ }
+ }
+
+ throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+ "Failed to move private data");
+ }
+ }
}
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bfcc3db..c068934 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -344,13 +344,6 @@ final class Settings {
}
}
- void setVolumeUuid(String pkgName, String volumeUuid) {
- PackageSetting p = mPackages.get(pkgName);
- if (p != null) {
- p.setVolumeUuid(volumeUuid);
- }
- }
-
SharedUserSetting getSharedUserLPw(String name,
int pkgFlags, int pkgPrivateFlags, boolean create) {
SharedUserSetting s = mSharedUsers.get(name);
@@ -693,19 +686,26 @@ final class Settings {
p.pkg = pkg;
// pkg.mSetEnabled = p.getEnabled(userId);
// pkg.mSetStopped = p.getStopped(userId);
+ final String volumeUuid = pkg.applicationInfo.volumeUuid;
final String codePath = pkg.applicationInfo.getCodePath();
final String resourcePath = pkg.applicationInfo.getResourcePath();
final String legacyNativeLibraryPath = pkg.applicationInfo.nativeLibraryRootDir;
+ // Update volume if needed
+ if (!Objects.equals(volumeUuid, p.volumeUuid)) {
+ Slog.w(PackageManagerService.TAG, "Volume for " + p.pkg.packageName +
+ " changing from " + p.volumeUuid + " to " + volumeUuid);
+ p.volumeUuid = volumeUuid;
+ }
// Update code path if needed
if (!Objects.equals(codePath, p.codePathString)) {
- Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
+ Slog.w(PackageManagerService.TAG, "Code path for " + p.pkg.packageName +
" changing from " + p.codePathString + " to " + codePath);
p.codePath = new File(codePath);
p.codePathString = codePath;
}
//Update resource path if needed
if (!Objects.equals(resourcePath, p.resourcePathString)) {
- Slog.w(PackageManagerService.TAG, "Resource path for pkg : " + p.pkg.packageName +
+ Slog.w(PackageManagerService.TAG, "Resource path for " + p.pkg.packageName +
" changing from " + p.resourcePathString + " to " + resourcePath);
p.resourcePath = new File(resourcePath);
p.resourcePathString = resourcePath;