diff options
4 files changed, 156 insertions, 19 deletions
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl index 79a6cfe..2b2dcf4 100644 --- a/core/java/android/os/storage/IMountService.aidl +++ b/core/java/android/os/storage/IMountService.aidl @@ -61,9 +61,12 @@ interface IMountService /** * Safely unmount external storage at given mount point. - * Returns an int consistent with MountServiceResultCode + * The unmount is an asynchronous operation. Applications + * should register StorageEventListener for storage related + * status changes. + * */ - int unmountVolume(String mountPoint, boolean force); + void unmountVolume(String mountPoint, boolean force); /** * Format external storage given a mount point. diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index fb17538..4485c79 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -16,32 +16,29 @@ package com.android.server; +import com.android.server.am.ActivityManagerService; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.res.Resources; import android.net.Uri; import android.os.storage.IMountService; import android.os.storage.IMountServiceListener; import android.os.storage.StorageResultCode; +import android.os.Handler; +import android.os.Message; import android.os.RemoteException; import android.os.IBinder; import android.os.Environment; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UEventObserver; -import android.os.Handler; -import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; import java.util.HashSet; -import java.io.File; -import java.io.FileReader; - /** * MountService implements back-end services for platform storage * management. @@ -124,6 +121,110 @@ class MountService extends IMountService.Stub */ private HashSet<String> mAsecMountSet = new HashSet<String>(); + private static final int H_UNMOUNT_PM_UPDATE = 1; + private static final int H_UNMOUNT_PM_DONE = 2; + private static final int H_UNMOUNT_MS = 3; + private static final int RETRY_UNMOUNT_DELAY = 30; // in ms + private static final int MAX_UNMOUNT_RETRIES = 4; + + private IntentFilter mPmFilter = new IntentFilter( + Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + private BroadcastReceiver mPmReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } + } + }; + + class UnmountCallBack { + String path; + int retries; + boolean force; + + UnmountCallBack(String path, boolean force) { + retries = 0; + this.path = path; + this.force = force; + } + } + + final private Handler mHandler = new Handler() { + ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); + + public void handleMessage(Message msg) { + switch (msg.what) { + case H_UNMOUNT_PM_UPDATE: { + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + mForceUnmounts.add(ucb); + mContext.registerReceiver(mPmReceiver, mPmFilter); + boolean hasExtPkgs = mPms.updateExternalMediaStatus(false); + if (!hasExtPkgs) { + // Unregister right away + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } + break; + } + case H_UNMOUNT_PM_DONE: { + // Unregister receiver + mContext.unregisterReceiver(mPmReceiver); + UnmountCallBack ucb = mForceUnmounts.get(0); + if (ucb == null || ucb.path == null) { + // Just ignore + return; + } + String path = ucb.path; + boolean done = false; + if (!ucb.force) { + done = true; + } else { + int pids[] = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } else { + // Kill processes holding references first + ActivityManagerService ams = (ActivityManagerService) + ServiceManager.getService("activity"); + // Eliminate system process here? + boolean ret = ams.killPidsForMemory(pids); + if (ret) { + // Confirm if file references have been freed. + pids = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } + } + } + } + if (done) { + mForceUnmounts.remove(0); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, + ucb)); + } else { + if (ucb.retries >= MAX_UNMOUNT_RETRIES) { + Log.i(TAG, "Cannot unmount inspite of " + + MAX_UNMOUNT_RETRIES + " to unmount media"); + // Send final broadcast indicating failure to unmount. + } else { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(H_UNMOUNT_PM_DONE, + ucb.retries++), + RETRY_UNMOUNT_DELAY); + } + } + break; + } + case H_UNMOUNT_MS : { + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + String path = ucb.path; + doUnmountVolume(path, true); + break; + } + } + } + }; + private void waitForReady() { while (mReady == false) { for (int retries = 5; retries > 0; retries--) { @@ -554,14 +655,28 @@ class MountService extends IMountService.Stub return rc; } + /* + * If force is not set, we do not unmount if there are + * processes holding references to the volume about to be unmounted. + * If force is set, all the processes holding references need to be + * killed via the ActivityManager before actually unmounting the volume. + * This might even take a while and might be retried after timed delays + * to make sure we dont end up in an instable state and kill some core + * processes. + */ private int doUnmountVolume(String path, boolean force) { if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) { return VoldResponseCode.OpFailedVolNotMounted; } + // We unmounted the volume. No of the asec containers are available now. + synchronized (mAsecMountSet) { + mAsecMountSet.clear(); + } // Notify PackageManager of potential media removal and deal with // return code later on. The caller of this api should be aware or have been // notified that the applications installed on the media will be killed. + // Redundant probably. But no harm in updating state again. mPms.updateExternalMediaStatus(false); try { mConnector.doCommand(String.format( @@ -814,11 +929,12 @@ class MountService extends IMountService.Stub return doMountVolume(path); } - public int unmountVolume(String path, boolean force) { + public void unmountVolume(String path, boolean force) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); - return doUnmountVolume(path, force); + UnmountCallBack ucb = new UnmountCallBack(path, force); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } public int formatVolume(String path) { @@ -1029,6 +1145,13 @@ class MountService extends IMountService.Stub } catch (NativeDaemonConnectorException e) { rc = StorageResultCode.OperationFailedInternalError; } + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + if (!mAsecMountSet.contains(newId)) { + mAsecMountSet.add(newId); + } + } + } return rc; } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 9e0d623..a23fac4 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -8877,6 +8877,9 @@ class PackageManagerService extends IPackageManager.Stub { return prefix + tmpIdx; } + /* + * Return true if PackageManager does have packages to be updated. + */ public boolean updateExternalMediaStatus(final boolean mediaStatus) { synchronized (mPackages) { if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + @@ -8885,16 +8888,24 @@ class PackageManagerService extends IPackageManager.Stub { return false; } mMediaMounted = mediaStatus; - final HashMap<SdInstallArgs, String> processCids = - new HashMap<SdInstallArgs, String>(); - final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids); - if (processCids.size() == 0) { + boolean ret = false; + synchronized (mPackages) { + Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD); + ret = appList != null && appList.size() > 0; + } + if (!ret) { + // No packages will be effected by the sdcard update. Just return. return false; } // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); + // If we are up here that means there are packages to be + // enabled or disabled. + final HashMap<SdInstallArgs, String> processCids = + new HashMap<SdInstallArgs, String>(); + final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids); if (mediaStatus) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); loadMediaPackages(processCids, uidArr); @@ -9019,9 +9030,9 @@ class PackageManagerService extends IPackageManager.Stub { } args.doPostInstall(retCode); } - // Send broadcasts first + // Send a broadcast to let everyone know we are done processing + sendResourcesChangedBroadcast(true, pkgList, uidArr); if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(true, pkgList, uidArr); Runtime.getRuntime().gc(); // If something failed do we clean up here or next install? } @@ -9049,9 +9060,9 @@ class PackageManagerService extends IPackageManager.Stub { } } } + sendResourcesChangedBroadcast(false, pkgList, uidArr); // Send broadcasts if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(false, pkgList, uidArr); Runtime.getRuntime().gc(); } // Do clean up. Just unmount diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index d161a88..5e3895a 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -403,7 +403,7 @@ public class PackageManagerTests extends AndroidTestCase { return ip; } finally { if (cleanUp) { - cleanUpInstall(ip); + //cleanUpInstall(ip); } } } |