从前面的知识我们看到,在vold层收到 FrameWork层的消息后,会进行相应的处理,同时在处理的过程中会上报相应的状态给FrameWork层,在这个过程中主要上报了两种消息:
1、开始挂载前上报State_Checking消息。
2、挂载成功后上报State_Mounted消息。
针对这两个消息,我们看下FrameWork层相应的处理,这两个消息处理的流程基本差不多,只是对于State_Mounted在处理的时候多了一个updateExternalMediaStatus通知PackageManagerService进行相应的更新,所以我们把两个的状态处理都在同一个图中画出来了:
首先还是阻塞在NativeDaemonConnector中的listenToSocket等待vold层消息的到来,收到消息后,调用onEvent函数进行处理,这里收到的这两个消息类型都是VolumeStateChange,所以调用notifyVolumeStateChange函数
private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
String vs = getVolumeState(path);
if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
Intent in = null;
if (oldState == VolumeState.Shared && newState != oldState) {
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
Uri.parse("file://" + path)));
}
if (newState == VolumeState.Init) {
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
} else if (newState == VolumeState.Idle) {
/*
* Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
* if we're in the process of enabling UMS
*/
if (!vs.equals(
Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
Environment.MEDIA_NOFS) && !vs.equals(
Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
if (DEBUG_EVENTS) Slog.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) Slog.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) Slog.i(TAG, "updating volume state mounted");
updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
in.putExtra("read-only", false);
} else if (newState == VolumeState.Unmounting) {
in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
} else if (newState == VolumeState.Formatting) {
} else if (newState == VolumeState.Shared) {
if (DEBUG_EVENTS) Slog.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) Slog.i(TAG, "Updating media shared");
updatePublicVolumeState(path, Environment.MEDIA_SHARED);
in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
} else if (newState == VolumeState.SharedMnt) {
Slog.e(TAG, "Live shared mounts not supported yet!");
return;
} else {
Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
}
if (in != null) {
mContext.sendBroadcast(in);
}
}
从代码我们可以看到对
Checking和Mount两类消息都是调用updatePublicVolumeState进行处理,我们看下这个函数:
private void updatePublicVolumeState(String path, String state) {
if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
Slog.w(TAG, "Multiple volumes not currently supported");
return;
}
if (mLegacyState.equals(state)) {
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
return;
}
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
// Tell the package manager the media is gone.
mPms.updateExternalMediaStatus(false, false);
/*
* Some OBBs might have been unmounted when this volume was
* unmounted, so send a message to the handler to let it know to
* remove those from the list of mounted OBBS.
*/
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
path));
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
// Tell the package manager the media is available for use.
mPms.updateExternalMediaStatus(true, false);
}
String oldState = mLegacyState;
mLegacyState = state;
synchronized (mListeners) {
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
bl.mListener.onStorageStateChanged(path, oldState, state);
} catch (RemoteException rex) {
Slog.e(TAG, "Listener dead");
mListeners.remove(i);
} catch (Exception ex) {
Slog.e(TAG, "Listener failed", ex);
}
}
}
}
针对Mount消息会调用updateExternalMediaStatus去更新PackageManagerService中的一些信息,这个我们一会再讲,接着往下看,调用onStorageStateChanged通知SDCARD状态改变,这个listener是哪里来的呢,我们跟踪源码可以看到是MountServiceBinderListener的一个成员变量,其类型是IMountServiceListener,IMountServiceListener是一个接口,我们跟踪其实现,最后可以看到是在StorageManager的MountServiceBinderListener的这个类继承了IMountServiceListener这个接口,而且我们看StorageManager的构造函数:
public StorageManager(Looper tgtLooper) throws RemoteException {
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
if (mMountService == null) {
Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
return;
}
mTgtLooper = tgtLooper;
mBinderListener = new MountServiceBinderListener();
mMountService.registerListener(mBinderListener);
}
public void onStorageStateChanged(String path, String oldState, String newState) {
final int size = mListeners.size();
for (int i = 0; i < size; i++) {
mListeners.get(i).sendStorageStateChanged(path, oldState, newState);
}
}
这里的mListeners是一个List,其中的成员是ListenerDelegate类,所以这里调用了ListenerDelegate的sendStorageStateChanged方法
void sendStorageStateChanged(String path, String oldState, String newState) {
StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);
mHandler.sendMessage(e.getMessage());
}
这里只是简单的发一条StorageStateChangedStorageEvent 消息,我们看看下这个Handle 的处理函数,在ListenerDelegate类中重写了handleMessage方法:
public void handleMessage(Message msg) {
StorageEvent e = (StorageEvent) msg.obj;
if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {
UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;
mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
} else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {
StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;
mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);
} else {
Log.e(TAG, "Unsupported event " + msg.what);
}
}
};
一般应用需要在sd卡状态改变的时候做一些处理也就只要继承StorageEventListener并重写mStorageEventListener这个方法,然后把这个Listener 调用StorageManager 的registerListener注册进来,这样在sdcard状态变化的时候就能收到消息了,进行处理了。
好了,对于vold的发的这两个消息的处理就差不多了 State_Checking和State_Mounted处理的流程基本都是一样的,只是对于State_Mounted消息还多了一个和PackageManageService打交道的过程,我们最后来看下这个交互过程都做了什么:
/*
* Update media status on PackageManager.
*/
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Media status can only be updated by the system");
}
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) {
Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
reportStatus ? 1 : 0, -1);
mHandler.sendMessage(msg);
return;
}
mMediaMounted = mediaStatus;
}
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
updateExternalMediaStatusInner(mediaStatus, reportStatus);
}
});
}
/*
* Look at potentially valid container ids from processCids
* If package information doesn't match the one on record
* or package scanning fails, the cid is added to list of
* removeCids. We currently don't delete stale containers.
*/
private void loadMediaPackages(HashMap processCids,
int uidArr[], HashSet removeCids) {
ArrayList pkgList = new ArrayList();
Set keys = processCids.keySet();
boolean doGc = false;
for (SdInstallArgs args : keys) {
String codePath = processCids.get(args);
if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
+ args.cid);
int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
try {
// Make sure there are no container errors first.
if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
!= PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to mount cid : " + args.cid +
" when installing from sdcard");
continue;
}
// Check code path here.
if (codePath == null || !codePath.equals(args.getCodePath())) {
Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
" does not match one in settings " + codePath);
continue;
}
// Parse package
int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath),
parseFlags, 0, 0);
// Scan the package
if (pkg != null) {
synchronized (mPackages) {
retCode = PackageManager.INSTALL_SUCCEEDED;
pkgList.add(pkg.packageName);
// Post process args
args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
}
} else {
Slog.i(TAG, "Failed to install pkg from " +
codePath + " from sdcard");
}
}
} finally {
if (retCode != PackageManager.INSTALL_SUCCEEDED) {
// Don't destroy container here. Wait till gc clears things up.
removeCids.add(args.cid);
}
}
}
synchronized (mPackages) {
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
final boolean regrantPermissions = mSettings.mExternalSdkPlatform
!= mSdkVersion;
if (regrantPermissions) Slog.i(TAG, "Platform changed from "
+ mSettings.mExternalSdkPlatform + " to " + mSdkVersion
+ "; regranting permissions for external storage");
mSettings.mExternalSdkPlatform = mSdkVersion;
// Make sure group IDs have been assigned, and any permission
// changes in other apps are accounted for
updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
// Persist settings
mSettings.writeLP();
}
// Send a broadcast to let everyone know we are done processing
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
}
// Force gc to avoid any stale parser references that we might have.
if (doGc) {
Runtime.getRuntime().gc();
}
// List stale containers and destroy stale temporary containers.
if (removeCids != null) {
for (String cid : removeCids) {
if (cid.startsWith(mTempContainerPrefix)) {
Log.i(TAG, "Destroying stale temporary container " + cid);
PackageHelper.destroySdDir(cid);
} else {
Log.w(TAG, "Container " + cid + " is stale");
}
}
}
}
这里面主要是调用scanPackageLI对sd卡里面的东西进行处理。