diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 654ee68fcd53d..b8ba3f6db5580 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -389,9 +389,10 @@ int get_size(const char *pkgname, const char *apkpath, int cachesize = 0; /* count the source apk as code -- but only if it's not - * on the /system partition + * on the /system partition and its not on the sdcard. */ - if (strncmp(apkpath, "/system", 7) != 0) { + if (strncmp(apkpath, "/system", 7) != 0 && + strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) { if (stat(apkpath, &s) == 0) { codesize += stat_size(&s); } @@ -403,17 +404,6 @@ int get_size(const char *pkgname, const char *apkpath, codesize += stat_size(&s); } } - - /* count the source apk as code -- but only if it's not - * installed on the sdcard - */ - if (strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) { - if (stat(apkpath, &s) == 0) { - codesize += stat_size(&s); - } - } - - /* count the cached dexfile as code */ if (!create_cache_path(path, apkpath)) { if (stat(path, &s) == 0) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 271f47732b3fd..e1fbe487b33b5 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -438,6 +438,15 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19; + /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the new package couldn't be installed in the specified install + * location because the media is not available. + * @hide + */ + public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20; + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} @@ -585,6 +594,14 @@ public abstract class PackageManager { */ public static final int MOVE_FAILED_INVALID_LOCATION = -5; + /** + * Error code that is passed to the {@link IPackageMoveObserver} by + * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} + * if the specified package cannot be moved to the specified location. + * @hide + */ + public static final int MOVE_FAILED_INTERNAL_ERROR = -6; + /** * Flag parameter for {@link #movePackage} to indicate that * the package should be moved to internal storage if its diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index fd1fd587d7f03..badabb0ce8673 100755 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -27,4 +27,5 @@ interface IMediaContainerService { boolean copyResource(in Uri packageURI, in ParcelFileDescriptor outStream); PackageInfoLite getMinimalPackageInfo(in Uri fileUri); + boolean checkFreeStorage(boolean external, in Uri fileUri); } \ No newline at end of file diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 80efca4da04da..4d0a9e055ac69 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -38,6 +38,7 @@ public class PackageHelper { public static final int RECOMMEND_FAILED_INVALID_APK = -2; public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3; public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4; + public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5; private static final boolean localLOGV = true; private static final String TAG = "PackageHelper"; // App installation location settings values @@ -70,7 +71,7 @@ public class PackageHelper { try { int rc = mountService.createSecureContainer( - cid, mbLen, "vfat", sdEncKey, uid); + cid, mbLen, "fat", sdEncKey, uid); if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to create secure container " + cid); return null; diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index c6b617d3f8f69..77d11cc6ed518 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -24,7 +24,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; -import android.content.pm.PackageParser.Package; import android.net.Uri; import android.os.Environment; import android.os.IBinder; @@ -45,7 +44,6 @@ import java.io.IOException; import java.io.InputStream; import android.os.FileUtils; -import android.os.storage.IMountService; import android.provider.Settings; /* @@ -130,26 +128,21 @@ public class DefaultContainerService extends IntentService { ret.packageName = pkg.packageName; ret.installLocation = pkg.installLocation; // Nuke the parser reference right away and force a gc - Runtime.getRuntime().gc(); packageParser = null; + Runtime.getRuntime().gc(); if (pkg == null) { Log.w(TAG, "Failed to parse package"); ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; return ret; } ret.packageName = pkg.packageName; - int loc = recommendAppInstallLocation(pkg.installLocation, archiveFilePath); - if (loc == PackageManager.INSTALL_EXTERNAL) { - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_EXTERNAL; - } else if (loc == ERR_LOC) { - Log.i(TAG, "Failed to install insufficient storage"); - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; - } else { - // Implies install on internal storage. - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL; - } + ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath); return ret; } + + public boolean checkFreeStorage(boolean external, Uri fileUri) { + return checkFreeStorageInner(external, fileUri); + } }; public DefaultContainerService() { @@ -190,6 +183,12 @@ public class DefaultContainerService extends IntentService { } private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) { + // Make sure the sdcard is mounted. + String status = Environment.getExternalStorageState(); + if (!status.equals(Environment.MEDIA_MOUNTED)) { + Log.w(TAG, "Make sure sdcard is mounted."); + return null; + } // Create new container at newCachePath String codePath = packageURI.getPath(); File codeFile = new File(codePath); @@ -313,11 +312,13 @@ public class DefaultContainerService extends IntentService { // Else install on internal NAND flash, unless space on NAND is less than 10% String status = Environment.getExternalStorageState(); long availSDSize = -1; + boolean mediaAvailable = false; if (status.equals(Environment.MEDIA_MOUNTED)) { StatFs sdStats = new StatFs( Environment.getExternalStorageDirectory().getPath()); availSDSize = (long)sdStats.getAvailableBlocks() * (long)sdStats.getBlockSize(); + mediaAvailable = true; } StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath()); long totalInternalSize = (long)internalStats.getBlockCount() * @@ -337,7 +338,8 @@ public class DefaultContainerService extends IntentService { long reqInternalSize = 0; boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize); - boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk && + boolean fitsOnSd = mediaAvailable && (reqInstallSize < availSDSize) + && intThresholdOk && (reqInternalSize < availInternalSize); boolean fitsOnInt = intThresholdOk && intAvailOk; @@ -377,21 +379,58 @@ public class DefaultContainerService extends IntentService { } if (!auto) { if (installOnlyOnSd) { - return fitsOnSd ? PackageManager.INSTALL_EXTERNAL : ERR_LOC; + if (fitsOnSd) { + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + if (!mediaAvailable) { + return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE; + } + return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } else if (installOnlyInternal){ // Check on internal flash - return fitsOnInt ? 0 : ERR_LOC; + return fitsOnInt ? PackageHelper.RECOMMEND_INSTALL_INTERNAL : + PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } // Try to install internally if (fitsOnInt) { - return 0; + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; } // Try the sdcard now. if (fitsOnSd) { - return PackageManager.INSTALL_EXTERNAL; + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; } // Return error code - return ERR_LOC; + return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; + } + + private boolean checkFreeStorageInner(boolean external, Uri packageURI) { + File apkFile = new File(packageURI.getPath()); + long size = apkFile.length(); + if (external) { + String status = Environment.getExternalStorageState(); + long availSDSize = -1; + if (status.equals(Environment.MEDIA_MOUNTED)) { + StatFs sdStats = new StatFs( + Environment.getExternalStorageDirectory().getPath()); + availSDSize = (long)sdStats.getAvailableBlocks() * + (long)sdStats.getBlockSize(); + } + return availSDSize > size; + } + StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath()); + long totalInternalSize = (long)internalStats.getBlockCount() * + (long)internalStats.getBlockSize(); + long availInternalSize = (long)internalStats.getAvailableBlocks() * + (long)internalStats.getBlockSize(); + + double pctNandFree = (double)availInternalSize / (double)totalInternalSize; + // To make final copy + long reqInstallSize = size; + // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now. + long reqInternalSize = 0; + boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); + boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize); + return intThresholdOk && intAvailOk; } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index b8b94a1cc881c..d50f591051ab0 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -51,6 +51,8 @@ import java.util.HashSet; class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks { private static final boolean LOCAL_LOGD = false; + private static final boolean DEBUG_UNMOUNT = false; + private static final boolean DEBUG_EVENTS = false; private static final String TAG = "MountService"; @@ -156,6 +158,7 @@ class MountService extends IMountService.Stub } void handleFinished() { + if (DEBUG_UNMOUNT) Log.i(TAG, "Unmounting " + path); doUnmountVolume(path, true); } } @@ -205,22 +208,27 @@ class MountService extends IMountService.Stub void registerReceiver() { mRegistered = true; + if (DEBUG_UNMOUNT) Log.i(TAG, "Registering receiver"); mContext.registerReceiver(mPmReceiver, mPmFilter); } void unregisterReceiver() { mRegistered = false; + if (DEBUG_UNMOUNT) Log.i(TAG, "Unregistering receiver"); mContext.unregisterReceiver(mPmReceiver); } public void handleMessage(Message msg) { switch (msg.what) { case H_UNMOUNT_PM_UPDATE: { + if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_UPDATE"); UnmountCallBack ucb = (UnmountCallBack) msg.obj; mForceUnmounts.add(ucb); + if (DEBUG_UNMOUNT) Log.i(TAG, " registered = " + mRegistered); // Register only if needed. if (!mRegistered) { registerReceiver(); + if (DEBUG_UNMOUNT) Log.i(TAG, "Updating external media status"); boolean hasExtPkgs = mPms.updateExternalMediaStatus(false); if (!hasExtPkgs) { // Unregister right away @@ -230,6 +238,7 @@ class MountService extends IMountService.Stub break; } case H_UNMOUNT_PM_DONE: { + if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_DONE"); // Unregister now. if (mRegistered) { unregisterReceiver(); @@ -286,6 +295,7 @@ class MountService extends IMountService.Stub break; } case H_UNMOUNT_MS : { + if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_MS"); UnmountCallBack ucb = (UnmountCallBack) msg.obj; ucb.handleFinished(); break; @@ -393,7 +403,12 @@ class MountService extends IMountService.Stub Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state)); return; } - + // Update state on PackageManager + if (Environment.MEDIA_UNMOUNTED.equals(state)) { + mPms.updateExternalMediaStatus(false); + } else if (Environment.MEDIA_MOUNTED.equals(state)) { + mPms.updateExternalMediaStatus(true); + } String oldState = mLegacyState; mLegacyState = state; @@ -456,6 +471,7 @@ class MountService extends IMountService.Stub } } if (state != null) { + if (DEBUG_EVENTS) Log.i(TAG, "Updating valid state " + state); updatePublicVolumeState(path, state); } } catch (Exception e) { @@ -484,6 +500,18 @@ class MountService extends IMountService.Stub public boolean onEvent(int code, String raw, String[] cooked) { Intent in = null; + if (DEBUG_EVENTS) { + StringBuilder builder = new StringBuilder(); + builder.append("onEvent::"); + builder.append(" raw= " + raw); + if (cooked != null) { + builder.append(" cooked = " ); + for (String str : cooked) { + builder.append(" " + str); + } + } + Log.i(TAG, builder.toString()); + } if (code == VoldResponseCode.VolumeStateChange) { /* * One of the volumes we're managing has changed state. @@ -541,18 +569,22 @@ class MountService extends IMountService.Stub return true; } /* Send the media unmounted event first */ + if (DEBUG_EVENTS) Log.i(TAG, "Sending unmounted event first"); updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); mContext.sendBroadcast(in); + if (DEBUG_EVENTS) Log.i(TAG, "Sending media removed"); updatePublicVolumeState(path, Environment.MEDIA_REMOVED); in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path)); } else if (code == VoldResponseCode.VolumeBadRemoval) { + if (DEBUG_EVENTS) Log.i(TAG, "Sending unmounted event first"); /* Send the media unmounted event first */ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); mContext.sendBroadcast(in); + if (DEBUG_EVENTS) Log.i(TAG, "Sending media bad removal"); updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path)); } else { @@ -570,6 +602,7 @@ class MountService extends IMountService.Stub private void notifyVolumeStateChange(String label, String path, int oldState, int newState) { String vs = getVolumeState(path); + if (DEBUG_EVENTS) Log.i(TAG, "notifyVolumeStateChanged::" + vs); Intent in = null; @@ -591,29 +624,31 @@ class MountService extends IMountService.Stub Environment.MEDIA_BAD_REMOVAL) && !vs.equals( Environment.MEDIA_NOFS) && !vs.equals( Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) { + if (DEBUG_EVENTS) Log.i(TAG, "updating volume state for media bad removal nofs and unmountable"); updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); } } else if (newState == VolumeState.Pending) { } else if (newState == VolumeState.Checking) { + if (DEBUG_EVENTS) Log.i(TAG, "updating volume state checking"); updatePublicVolumeState(path, Environment.MEDIA_CHECKING); in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path)); } else if (newState == VolumeState.Mounted) { + if (DEBUG_EVENTS) Log.i(TAG, "updating volume state mounted"); updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); - // Update media status on PackageManagerService to mount packages on sdcard - mPms.updateExternalMediaStatus(true); in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path)); in.putExtra("read-only", false); } else if (newState == VolumeState.Unmounting) { - mPms.updateExternalMediaStatus(false); in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path)); } else if (newState == VolumeState.Formatting) { } else if (newState == VolumeState.Shared) { + if (DEBUG_EVENTS) Log.i(TAG, "Updating volume state media mounted"); /* Send the media unmounted event first */ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); mContext.sendBroadcast(in); + if (DEBUG_EVENTS) Log.i(TAG, "Updating media shared"); updatePublicVolumeState(path, Environment.MEDIA_SHARED); in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path)); if (LOCAL_LOGD) Log.d(TAG, "Sending ACTION_MEDIA_SHARED intent"); @@ -657,6 +692,7 @@ class MountService extends IMountService.Stub private int doMountVolume(String path) { int rc = StorageResultCode.OperationSucceeded; + if (DEBUG_EVENTS) Log.i(TAG, "doMountVolume: Mouting " + path); try { mConnector.doCommand(String.format("volume mount %s", path)); } catch (NativeDaemonConnectorException e) { @@ -671,6 +707,7 @@ class MountService extends IMountService.Stub */ rc = StorageResultCode.OperationFailedNoMedia; } else if (code == VoldResponseCode.OpFailedMediaBlank) { + if (DEBUG_EVENTS) Log.i(TAG, " updating volume state :: media nofs"); /* * Media is blank or does not contain a supported filesystem */ @@ -678,6 +715,7 @@ class MountService extends IMountService.Stub in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path)); rc = StorageResultCode.OperationFailedMediaBlank; } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { + if (DEBUG_EVENTS) Log.i(TAG, "updating volume state media corrupt"); /* * Volume consistency check failed */ @@ -1040,6 +1078,16 @@ class MountService extends IMountService.Stub validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); + String volState = getVolumeState(path); + if (DEBUG_UNMOUNT) Log.i(TAG, "Unmounting " + path + " force = " + force); + if (Environment.MEDIA_UNMOUNTED.equals(volState) || + Environment.MEDIA_REMOVED.equals(volState) || + Environment.MEDIA_SHARED.equals(volState) || + Environment.MEDIA_UNMOUNTABLE.equals(volState)) { + // Media already unmounted or cannot be unmounted. + // TODO return valid return code when adding observer call back. + return; + } UnmountCallBack ucb = new UnmountCallBack(path, force); mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 139c05f120a68..818e99e6f221a 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -4613,6 +4613,12 @@ class PackageManagerService extends IPackageManager.Stub { return pkgLite.recommendedInstallLocation; } + /* + * Invoke remote method to get package information and install + * location values. Override install location based on default + * policy if needed and then create install arguments based + * on the install location. + */ public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; @@ -4624,9 +4630,7 @@ class PackageManagerService extends IPackageManager.Stub { } else { // Remote call to find out default install location PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI); - int loc = installLocationPolicy(pkgLite, flags); - // Use install location to create InstallArgs and temporary - // install location + int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS){ @@ -4635,7 +4639,11 @@ class PackageManagerService extends IPackageManager.Stub { ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { ret = PackageManager.INSTALL_FAILED_INVALID_APK; + } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { + ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; } else { + // Override with defaults if needed. + loc = installLocationPolicy(pkgLite, flags); // Override install location with flags if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){ if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { @@ -4701,6 +4709,12 @@ class PackageManagerService extends IPackageManager.Stub { } public void handleStartCopy() throws RemoteException { + mRet = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + // Check for storage space on target medium + if (!targetArgs.checkFreeStorage(mContainerService)) { + Log.w(TAG, "Insufficient storage to install"); + return; + } // Create the file args now. mRet = targetArgs.copyApk(mContainerService, false); targetArgs.doPreInstall(mRet); @@ -4714,15 +4728,20 @@ class PackageManagerService extends IPackageManager.Stub { builder.append(" target : "); builder.append(targetArgs.getCodePath()); } - Log.i(TAG, "Posting move MCS_UNBIND for " + builder.toString()); + Log.i(TAG, builder.toString()); } } @Override void handleReturnCode() { targetArgs.doPostInstall(mRet); - // TODO invoke pending move - processPendingMove(this, mRet); + int currentStatus = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + if (mRet == PackageManager.INSTALL_SUCCEEDED) { + currentStatus = PackageManager.MOVE_SUCCEEDED; + } else if (mRet == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){ + currentStatus = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; + } + processPendingMove(this, currentStatus); } @Override @@ -4782,6 +4801,7 @@ class PackageManagerService extends IPackageManager.Stub { // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); abstract boolean doPostDeleteLI(boolean delete); + abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; } class FileInstallArgs extends InstallArgs { @@ -4812,6 +4832,10 @@ class PackageManagerService extends IPackageManager.Stub { resourceFileName = getResourcePathFromCodePath(); } + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + return imcs.checkFreeStorage(false, packageURI); + } + String getCodePath() { return codeFileName; } @@ -5013,6 +5037,10 @@ class PackageManagerService extends IPackageManager.Stub { cid = getTempContainerId(); } + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + return imcs.checkFreeStorage(true, packageURI); + } + int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (temp) { createCopyFile(); @@ -9109,6 +9137,9 @@ class PackageManagerService extends IPackageManager.Stub { public boolean updateExternalMediaStatus(final boolean mediaStatus) { final boolean ret; synchronized (mPackages) { + Log.i(TAG, "Updating external media status from " + + (mMediaMounted ? "mounted" : "unmounted") + " to " + + (mediaStatus ? "mounted" : "unmounted")); if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus+", mMediaMounted=" + mMediaMounted); if (mediaStatus == mMediaMounted) { @@ -9117,6 +9148,14 @@ class PackageManagerService extends IPackageManager.Stub { mMediaMounted = mediaStatus; Set appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE); ret = appList != null && appList.size() > 0; + if (DEBUG_SD_INSTALL) { + if (appList != null) { + for (String app : appList) { + Log.i(TAG, "Should enable " + app + " on sdcard"); + } + } + } + if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus returning " + ret); } // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { @@ -9134,6 +9173,7 @@ class PackageManagerService extends IPackageManager.Stub { // enabled or disabled. final String list[] = PackageHelper.getSecureContainerList(); if (list == null || list.length == 0) { + Log.i(TAG, "No secure containers on sdcard"); return; } @@ -9141,16 +9181,6 @@ class PackageManagerService extends IPackageManager.Stub { int num = 0; HashSet removeCids = new HashSet(); HashMap processCids = new HashMap(); - /*HashMap cidPathMap = new HashMap(); - // Don't hold any locks when getting cache paths - for (String cid : list) { - String cpath = PackageHelper.getSdDir(cid); - if (cpath == null) { - removeCids.add(cid); - } else { - cidPathMap.put(cid, cpath); - } - }*/ synchronized (mPackages) { for (String cid : list) { SdInstallArgs args = new SdInstallArgs(cid); diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index 8c8b00cb224d1..a06a13b696f0f 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -72,8 +72,8 @@ import android.provider.Settings.SettingNotFoundException; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; - public final long MAX_WAIT_TIME=120*1000; - public final long WAIT_TIME_INCR=20*1000; + public final long MAX_WAIT_TIME = 25*1000; + public final long WAIT_TIME_INCR = 5*1000; private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO; private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL; @@ -378,39 +378,50 @@ public class PackageManagerTests extends AndroidTestCase { private InstallParams installFromRawResource(String outFileName, int rawResId, int flags, boolean cleanUp, boolean fail, int result, int expInstallLocation) { + PackageManager pm = mContext.getPackageManager(); File filesDir = mContext.getFilesDir(); File outFile = new File(filesDir, outFileName); Uri packageURI = getInstallablePackage(rawResId, outFile); PackageParser.Package pkg = parsePackage(packageURI); assertNotNull(pkg); - InstallParams ip = null; - // Make sure the package doesn't exist - getPm().deletePackage(pkg.packageName, null, 0); - // Clean up the containers as well - clearSecureContainersForPkg(pkg.packageName); - try { + if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) { + // Make sure the package doesn't exist try { - if (fail) { - assertTrue(invokeInstallPackageFail(packageURI, flags, - pkg.packageName, result)); - assertNotInstalled(pkg.packageName); - } else { - InstallReceiver receiver = new InstallReceiver(pkg.packageName); - assertTrue(invokeInstallPackage(packageURI, flags, - pkg.packageName, receiver)); - // Verify installed information - assertInstall(pkg, flags, expInstallLocation); - ip = new InstallParams(pkg, outFileName, packageURI); - } + ApplicationInfo appInfo = pm.getApplicationInfo(pkg.packageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + GenericReceiver receiver = new DeleteReceiver(pkg.packageName); + invokeDeletePackage(packageURI, 0, + pkg.packageName, receiver); + } catch (NameNotFoundException e1) { } catch (Exception e) { - failStr("Failed with exception : " + e); + failStr(e); + } + // Clean up the containers as well + clearSecureContainersForPkg(pkg.packageName); + } + InstallParams ip = null; + try { + if (fail) { + assertTrue(invokeInstallPackageFail(packageURI, flags, + pkg.packageName, result)); + assertNotInstalled(pkg.packageName); + } else { + InstallReceiver receiver = new InstallReceiver(pkg.packageName); + assertTrue(invokeInstallPackage(packageURI, flags, + pkg.packageName, receiver)); + // Verify installed information + assertInstall(pkg, flags, expInstallLocation); + ip = new InstallParams(pkg, outFileName, packageURI); } return ip; + } catch (Exception e) { + failStr("Failed with exception : " + e); } finally { if (cleanUp) { cleanUpInstall(ip); } } + return ip; } @MediumTest @@ -820,7 +831,7 @@ public class PackageManagerTests extends AndroidTestCase { try { // Wait on observer synchronized(observer) { - getMs().unmountVolume(path, false); + getMs().unmountVolume(path, true); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { observer.wait(WAIT_TIME_INCR); @@ -949,14 +960,21 @@ public class PackageManagerTests extends AndroidTestCase { PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - private void replaceManifestLocation(int iFlags, int rFlags) { + /* + * Install a package on internal flash via PackageManager install flag. Replace + * the package via flag to install on sdcard. Make sure the new flag overrides + * the old install location. + */ + public void testReplaceFlagInternalSdcard() { + int iFlags = 0; + int rFlags = PackageManager.INSTALL_EXTERNAL; InstallParams ip = sampleInstallFromRawResource(iFlags, false); GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; try { assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags, ip.pkg.packageName, receiver), true); - assertInstall(ip.pkg, replaceFlags, ip.pkg.installLocation); + assertInstall(ip.pkg, rFlags, ip.pkg.installLocation); } catch (Exception e) { failStr("Failed with exception : " + e); } finally { @@ -964,12 +982,26 @@ public class PackageManagerTests extends AndroidTestCase { } } - public void testReplaceFlagInternalSdcard() { - replaceManifestLocation(0, PackageManager.INSTALL_EXTERNAL); - } - + /* + * Install a package on sdcard via PackageManager install flag. Replace + * the package with no flags or manifest option and make sure the old + * install location is retained. + */ public void testReplaceFlagSdcardInternal() { - replaceManifestLocation(PackageManager.INSTALL_EXTERNAL, 0); + int iFlags = PackageManager.INSTALL_EXTERNAL; + int rFlags = 0; + InstallParams ip = sampleInstallFromRawResource(iFlags, false); + GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); + int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; + try { + assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags, + ip.pkg.packageName, receiver), true); + assertInstall(ip.pkg, iFlags, ip.pkg.installLocation); + } catch (Exception e) { + failStr("Failed with exception : " + e); + } finally { + cleanUpInstall(ip); + } } public void testManifestInstallLocationReplaceInternalSdcard() { @@ -984,7 +1016,7 @@ public class PackageManagerTests extends AndroidTestCase { int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; try { InstallParams rp = installFromRawResource("install.apk", rApk, - rFlags, false, + replaceFlags, false, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); assertInstall(rp.pkg, replaceFlags, rp.pkg.installLocation); } catch (Exception e) { @@ -1002,11 +1034,10 @@ public class PackageManagerTests extends AndroidTestCase { InstallParams ip = installFromRawResource("install.apk", iApk, iFlags, false, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); - GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; try { InstallParams rp = installFromRawResource("install.apk", rApk, - rFlags, false, + replaceFlags, false, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); assertInstall(rp.pkg, replaceFlags, ip.pkg.installLocation); } catch (Exception e) { @@ -1211,6 +1242,56 @@ public class PackageManagerTests extends AndroidTestCase { moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_INTERNAL, PackageManager.MOVE_SUCCEEDED); } + + /* + * Test that an install error code is returned when media is unmounted + * and package installed on sdcard via package manager flag. + */ + public void testInstallSdcardUnmount() { + boolean origState = getMediaState(); + try { + // Unmount sdcard + assertTrue(unmountMedia()); + // Try to install and make sure an error code is returned. + assertNull(installFromRawResource("install.apk", R.raw.install, + PackageManager.INSTALL_EXTERNAL, false, + true, PackageManager.INSTALL_FAILED_CONTAINER_ERROR, + PackageInfo.INSTALL_LOCATION_AUTO)); + } finally { + // Restore original media state + if (origState) { + mountMedia(); + } else { + unmountMedia(); + } + } + } + + /* + * Unmount sdcard. Try installing an app with manifest option to install + * on sdcard. Make sure it gets installed on internal flash. + */ + public void testInstallManifestSdcardUnmount() { + boolean origState = getMediaState(); + try { + // Unmount sdcard + assertTrue(unmountMedia()); + // Try to install and make sure an error code is returned. + assertNotNull(installFromRawResource("install.apk", R.raw.install_loc_sdcard, + 0, false, + false, -1, + PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY)); + } finally { + // Restore original media state + if (origState) { + mountMedia(); + } else { + unmountMedia(); + } + } + } + + /*---------- Recommended install location tests ----*/ /* * TODO's * check version numbers for upgrades