From af8e9f4805643f90a9dc0ecfa119e0a860c12f8a Mon Sep 17 00:00:00 2001 From: Suchi Amalapurapu Date: Tue, 12 Jan 2010 10:17:28 -0800 Subject: [PATCH] app install on sdcard. provide skeleton implementation to install an app on sdcard, just resources. Add new install path for /asec in installd. ignore . when checking for apk path since the sdcard packages id'ed by package name. Add new -s option to adb shell pm Refactor fwd locked from scanMode to ApplicationInfo. Add new flag for sd install Add new parse flags for fwd locking and installing on sdcard New mock api's in PackageManagerService to invoke MountService api's. These will be refactored again and so have been wrapped internally. Some error codes in PackageManager Changes in PackageManagerService to use mPath and mScanPath during installation and switch to using PackageParser.Package.applicationInfo attributes for source and public resource directories. Some known issues that will be addressed later using system_uid for now. needs some tinkering with uid and packagesetting creation to use the actual app uid error handling from vold not very robust. ignoring lot of things for now sending a delayed destroy to delete packages. will revisit later revisit temp file creation later. just copy for now --- cmds/installd/commands.c | 12 +- cmds/installd/installd.h | 1 + cmds/pm/src/com/android/commands/pm/Pm.java | 5 +- .../android/content/pm/ApplicationInfo.java | 18 + .../android/content/pm/IPackageManager.aidl | 9 + .../android/content/pm/PackageManager.java | 16 + .../android/content/pm/PackageParser.java | 31 +- .../android/server/PackageManagerService.java | 483 +++++++++++------- 8 files changed, 372 insertions(+), 203 deletions(-) diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index dcae0c7dd8a11..79bda74e84c34 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -218,14 +218,20 @@ int free_cache(int free_size) static int is_valid_apk_path(const char *path) { int len = strlen(APK_DIR_PREFIX); +int nosubdircheck = 0; if (strncmp(path, APK_DIR_PREFIX, len)) { len = strlen(PROTECTED_DIR_PREFIX); if (strncmp(path, PROTECTED_DIR_PREFIX, len)) { - LOGE("invalid apk path '%s' (bad prefix)\n", path); - return 0; + len = strlen(SDCARD_DIR_PREFIX); + if (strncmp(path, SDCARD_DIR_PREFIX, len)) { + LOGE("invalid apk path '%s' (bad prefix)\n", path); + return 0; + } else { + nosubdircheck = 1; + } } } - if (strchr(path + len, '/')) { + if ((nosubdircheck != 1) && strchr(path + len, '/')) { LOGE("invalid apk path '%s' (subdir?)\n", path); return 0; } diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 1679d148f4ec0..35a173ea860d0 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -68,6 +68,7 @@ /* other handy constants */ #define PROTECTED_DIR_PREFIX "/data/app-private/" +#define SDCARD_DIR_PREFIX "/asec/" #define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" #define DALVIK_CACHE_POSTFIX "/classes.dex" diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 79eb310bb0e54..4953f5deaf8d9 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -594,6 +594,8 @@ public final class Pm { } } else if (opt.equals("-t")) { installFlags |= PackageManager.INSTALL_ALLOW_TEST; + } else if (opt.equals("-s")) { + installFlags |= PackageManager.INSTALL_ON_SDCARD; } else { System.err.println("Error: Unknown option: " + opt); showUsage(); @@ -822,7 +824,7 @@ public final class Pm { System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]"); System.err.println(" pm list features"); System.err.println(" pm path PACKAGE"); - System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] PATH"); + System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH"); System.err.println(" pm uninstall [-k] PACKAGE"); System.err.println(" pm enable PACKAGE_OR_COMPONENT"); System.err.println(" pm disable PACKAGE_OR_COMPONENT"); @@ -854,6 +856,7 @@ public final class Pm { System.err.println(" -r: reinstall an exisiting app, keeping its data."); System.err.println(" -t: allow test .apks to be installed."); System.err.println(" -i: specify the installer package name."); + System.err.println(" -s: install package on sdcard."); System.err.println(""); System.err.println("The uninstall command removes a package from the system. Options:"); System.err.println(" -k: keep the data and cache directories around."); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 3dea28630ad02..b27cd6cda1533 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -217,6 +217,22 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int FLAG_NEVER_ENCRYPT = 1<<17; + /** + * Value for {@link #flags}: Set to true if the application has been + * installed using the forward lock option. + * + * {@hide} + */ + public static final int FLAG_FORWARD_LOCK = 1<<18; + + /** + * Value for {@link #flags}: Set to true if the application is + * currently installed on the sdcard. + * + * {@hide} + */ + public static final int FLAG_ON_SDCARD = 1<<19; + /** * Flags associated with the application. Any combination of * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE}, @@ -227,6 +243,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@link #FLAG_SUPPORTS_NORMAL_SCREENS}, * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS}, * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}. + * {@link #FLAG_FWD_LOCKED}, + * {@link #FLAG_ON_SDCARD} */ public int flags = 0; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fc6538fbe05d5..54db5e04c7d29 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -296,4 +296,13 @@ interface IPackageManager { * in the special development "no pre-dexopt" mode. */ boolean performDexOpt(String packageName); + + /** + * Update status of external media on the package manager to scan and + * install packages installed on the external media. Like say the + * MountService uses this to call into the package manager to update + * status of sdcard. + */ + void updateExternalMediaStatus(boolean mounted); + } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 53a966d3db126..bc59c94526d62 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -251,6 +251,13 @@ public abstract class PackageManager { */ public static final int INSTALL_ALLOW_TEST = 0x00000004; + /** + * Flag parameter for {@link #installPackage} to indicate that this + * package has to be installed on the sdcard. + * @hide + */ + public static final int INSTALL_ON_SDCARD = 0x00000008; + /** * Flag parameter for * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate @@ -411,6 +418,15 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_MISSING_FEATURE = -17; + // ------ Errors related to sdcard + /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * a secure container mount point couldn't be accessed on external media. + * @hide + */ + public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ad99f54aeac52..8a5df324e60de 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -327,16 +327,18 @@ public class PackageParser { return null; } - public final static int PARSE_IS_SYSTEM = 0x0001; - public final static int PARSE_CHATTY = 0x0002; - public final static int PARSE_MUST_BE_APK = 0x0004; - public final static int PARSE_IGNORE_PROCESSES = 0x0008; + public final static int PARSE_IS_SYSTEM = 1<<0; + public final static int PARSE_CHATTY = 1<<1; + public final static int PARSE_MUST_BE_APK = 1<<2; + public final static int PARSE_IGNORE_PROCESSES = 1<<3; + public final static int PARSE_FORWARD_LOCK = 1<<4; + public final static int PARSE_ON_SDCARD = 1<<5; public int getParseError() { return mParseError; } - public Package parsePackage(File sourceFile, String destFileName, + public Package parsePackage(File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) { mParseError = PackageManager.INSTALL_SUCCEEDED; @@ -413,8 +415,11 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.applicationInfo.sourceDir = destFileName; - pkg.applicationInfo.publicSourceDir = destFileName; + // Set code and resource paths + pkg.mPath = destCodePath; + pkg.mScanPath = mArchiveSourcePath; + //pkg.applicationInfo.sourceDir = destCodePath; + //pkg.applicationInfo.publicSourceDir = destRes; pkg.mSignatures = null; return pkg; @@ -1369,6 +1374,14 @@ public class PackageParser { } } + if ((flags & PARSE_FORWARD_LOCK) != 0) { + ai.flags |= ApplicationInfo.FLAG_FORWARD_LOCK; + } + + if ((flags & PARSE_ON_SDCARD) != 0) { + ai.flags |= ApplicationInfo.FLAG_ON_SDCARD; + } + if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_debuggable, false)) { @@ -2530,10 +2543,6 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; - // For use by package manager service to keep track of which apps - // have been installed with forward locking. - public boolean mForwardLocked; - // For use by the package manager to keep track of the path to the // file an app came from. public String mScanPath; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 2b12268babbd6..4b9b36637dae5 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -135,8 +135,8 @@ class PackageManagerService extends IPackageManager.Stub { static final int SCAN_NO_DEX = 1<<1; static final int SCAN_FORCE_DEX = 1<<2; static final int SCAN_UPDATE_SIGNATURE = 1<<3; - static final int SCAN_FORWARD_LOCKED = 1<<4; - static final int SCAN_NEW_INSTALL = 1<<5; + static final int SCAN_NEW_INSTALL = 1<<4; + static final int SCAN_NO_PATHS = 1<<5; final HandlerThread mHandlerThread = new HandlerThread("PackageManager", Process.THREAD_PRIORITY_BACKGROUND); @@ -281,8 +281,10 @@ class PackageManagerService extends IPackageManager.Stub { final HashMap> mPendingBroadcasts = new HashMap>(); static final int SEND_PENDING_BROADCAST = 1; + static final int DESTROY_SD_CONTAINER = 2; // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; + static final int DESTROY_SD_CONTAINER_DELAY = 30 * 1000; class PackageHandler extends Handler { PackageHandler(Looper looper) { @@ -290,6 +292,15 @@ class PackageManagerService extends IPackageManager.Stub { } public void handleMessage(Message msg) { switch (msg.what) { + case DESTROY_SD_CONTAINER: + String pkgName = (String) msg.obj; + if (pkgName != null) { + // Too bad we cannot handle the errors from destroying the containers. + if (!destroySdDir(pkgName)) { + Log.e(TAG, "Failed to destroy container for pkg : " + pkgName); + } + } + break; case SEND_PENDING_BROADCAST : { String packages[]; ArrayList components[]; @@ -562,12 +573,12 @@ class PackageManagerService extends IPackageManager.Stub { mFrameworkDir.getPath(), OBSERVER_EVENTS, true); mFrameworkInstallObserver.startWatching(); scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM, - scanMode | SCAN_NO_DEX); + scanMode | SCAN_NO_DEX | SCAN_NO_PATHS); mSystemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( mSystemAppDir.getPath(), OBSERVER_EVENTS, true); mSystemInstallObserver.startWatching(); - scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode); + scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode | SCAN_NO_PATHS); mAppInstallDir = new File(dataDir, "app"); if (mInstaller == null) { // Make sure these dirs exist, when we are running in @@ -594,7 +605,7 @@ class PackageManagerService extends IPackageManager.Stub { mDrmAppInstallObserver = new AppDirObserver( mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); mDrmAppInstallObserver.startWatching(); - scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode | SCAN_FORWARD_LOCKED); + scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); @@ -1961,12 +1972,7 @@ class PackageManagerService extends IPackageManager.Stub { int i; for (i=0; i 0) { + mbLen++; + } + if (DEBUG_SD_INSTALL) Log.i(TAG, "mbLen="+mbLen); + String cachePath = null; + // Remove any pending destroy messages + mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName); + try { + cachePath = mountService.createSecureContainer(pkgName, + mbLen, + "vfat", mSdEncryptKey, Process.SYSTEM_UID); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath); + return cachePath; + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); + } + // TODO just fail here and let the user delete later on. + try { + mountService.destroySecureContainer(pkgName); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Destroying cache for " + pkgName + ", cachePath =" + cachePath); + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to destroy existing cache: " + e); + return null; + } + try { + cachePath = mountService.createSecureContainer(pkgName, + mbLen, + "vfat", mSdEncryptKey, Process.SYSTEM_UID); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath); + return cachePath; + } catch(IllegalStateException e) { + Log.e(TAG, "Failed to create storage on sdcard with exception: " + e); + return null; + } + } + + private String mountSdDir(String pkgName, int ownerUid) { + try { + return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid); + } catch (IllegalStateException e) { + Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e); + } + return null; + } + + private String getSdDir(String pkgName) { + String cachePath = null; + try { + cachePath = getMountService().getSecureContainerPath(pkgName); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to retrieve secure container path for pkg : " + pkgName + " with exception " + e); + } + return cachePath; + } + + private boolean finalizeSdDir(String pkgName) { + try { + getMountService().finalizeSecureContainer(pkgName); + return true; + } catch (IllegalStateException e) { + Log.i(TAG, "Failed to destroy container for pkg : " + pkgName); + return false; + } + } + + private boolean destroySdDir(String pkgName) { + try { + if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) { + // Don't have to send message again + mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName); + } + // We need to destroy right away + getMountService().destroySecureContainer(pkgName); + return true; + } catch (IllegalStateException e) { + Log.i(TAG, "Failed to destroy container for pkg : " + pkgName); + return false; + } + } + + private void sendDelayedDestroySdDir(String pkgName) { + if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) { + // Don't have to send message again + return; + } + Message msg = mHandler.obtainMessage(DESTROY_SD_CONTAINER, pkgName); + mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY); + } + + public void updateExternalMediaStatus(boolean mediaStatus) { + // TODO + } }