Android 存储设备管理框架
在android之VOLD进程启动源码分析一文中介绍了存储设备的管控中心Vold进程,Vold属于native后台进程,通过netlink方式接收kernel的uevent消息,并通过socket方式将uevent消息发送给MountService,同时实时接收MountService的命令消息,MountService,Vold,Kernel三者的关系如下图所示:
android之VOLD进程启动源码分析一文中介绍了NetlinkManager模块在启动过程中,创建了一个socket监听线程,用于监听kernel发送过来的uevent消息;CommandListener模块在启动时同样创建了一个socket监听线程,不同的是该线程用于监听MountServcie的连接,接收MountService向Vold发送的命令消息;MountService要接收来自kernel的uevent消息,必定也需要创建一个socket监听线程,在接下来将对该socket监听线程进行详细讲解。
Android MountService框架设计
MountService作为Android的Java服务之一,在SystemServer进程启动的第二阶段创建并注册到ServiceManager中,同时长驻于SystemServer进程中,MountService创建及注册过程如下:
- MountService mountService = null;
- if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
- try {
- /*
- * NotificationManagerService is dependant on MountService,
- * so we must start MountService first.
- */
- Slog.i(TAG, "Mount Service");
- mountService = new MountService(context);
- //注册到ServiceManager中
- ServiceManager.addService("mount", mountService);
- } catch (Throwable e) {
- reportWtf("starting Mount Service", e);
- }
- }
MountService mountService = null;
if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
/*
* NotificationManagerService is dependant on MountService,
* so we must start MountService first.
*/
Slog.i(TAG, "Mount Service");
mountService = new MountService(context);
//注册到ServiceManager中
ServiceManager.addService("mount", mountService);
} catch (Throwable e) {
reportWtf("starting Mount Service", e);
}
}
MountService各个类关系图:
构造MountService对象实例:
- public MountService(Context context) {
- mContext = context;
- //从xml中读取存储设备列表
- readStorageList();
- if (mPrimaryVolume != null) {
- mExternalStoragePath = mPrimaryVolume.getPath();
- mEmulateExternalStorage = mPrimaryVolume.isEmulated();
- if (mEmulateExternalStorage) {
- Slog.d(TAG, "using emulated external storage");
- mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
- }
- }
- //add mount state for inernal storage in NAND
- if (Environment.getSecondStorageType() == Environment.SECOND_STORAGE_TYPE_NAND) {
- mVolumeStates.put(Environment.getSecondStorageDirectory().getPath(), Environment.MEDIA_MOUNTED);
- }
- // 查询PackageManagerService服务
- mPms = (PackageManagerService) ServiceManager.getService("package");
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BOOT_COMPLETED);
- // don't bother monitoring USB if mass storage is not supported on our primary volume
- if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
- filter.addAction(UsbManager.ACTION_USB_STATE);
- }
- //注册开机完成及USB状态变化广播接收器
- mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
- //创建并启动一个带消息循环的MountService工作线程
- mHandlerThread = new HandlerThread("MountService");
- mHandlerThread.start();
- //为MountService工作线程创建一个Handler
- mHandler = new MountServiceHandler(mHandlerThread.getLooper());
- //为MountService工作线程创建一个ObbActionHandler
- mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
- /*
- * Create the connection to vold with a maximum queue of twice the
- * amount of containers we'd ever expect to have. This keeps an
- * "asec list" from blocking a thread repeatedly.
- */
- mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
- //创建并启动一个socket连接监听线程
- Thread thread = new Thread(mConnector, VOLD_TAG);
- thread.start();
- // Add ourself to the Watchdog monitors if enabled.
- if (WATCHDOG_ENABLE) {
- Watchdog.getInstance().addMonitor(this);
- }
- }
public MountService(Context context) {
mContext = context;
//从xml中读取存储设备列表
readStorageList();
if (mPrimaryVolume != null) {
mExternalStoragePath = mPrimaryVolume.getPath();
mEmulateExternalStorage = mPrimaryVolume.isEmulated();
if (mEmulateExternalStorage) {
Slog.d(TAG, "using emulated external storage");
mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
}
}
//add mount state for inernal storage in NAND
if (Environment.getSecondStorageType() == Environment.SECOND_STORAGE_TYPE_NAND) {
mVolumeStates.put(Environment.getSecondStorageDirectory().getPath(), Environment.MEDIA_MOUNTED);
}
// 查询PackageManagerService服务
mPms = (PackageManagerService) ServiceManager.getService("package");
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
// don't bother monitoring USB if mass storage is not supported on our primary volume
if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
filter.addAction(UsbManager.ACTION_USB_STATE);
}
//注册开机完成及USB状态变化广播接收器
mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
//创建并启动一个带消息循环的MountService工作线程
mHandlerThread = new HandlerThread("MountService");
mHandlerThread.start();
//为MountService工作线程创建一个Handler
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
//为MountService工作线程创建一个ObbActionHandler
mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
/*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
//创建并启动一个socket连接监听线程
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
// Add ourself to the Watchdog monitors if enabled.
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
}
在开始构造MountService前,首先读取frameworks/base/core/res/res/xml/storage_list.xml文件,该文件以XML方式保存了所有存储设备的参数,文件内容如下所示:
- <StorageList xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- removable is not set in nosdcard product -->
- <storage android:mountPoint="/mnt/sdcard"
- android:storageDescription="@string/storage_usb"
- android:primary="true" />
- </StorageList>
<StorageList xmlns:android="http://schemas.android.com/apk/res/android">
<!-- removable is not set in nosdcard product -->
<storage android:mountPoint="/mnt/sdcard"
android:storageDescription="@string/storage_usb"
android:primary="true" />
</StorageList>
该文件的读取这里不在介绍,读者自行研究,使用XML解析器读取该XML的文件内容,根据读取到的存储设备参数来构造StorageVolume对象,并将构造的所有StorageVolume对象存放到列表mVolumes中。通过源码清晰地知道,在构造MountService时,注册了一个广播接收器,用于接收开机完成广播及USB状态广播,当开机完成时自动挂载存储设备,在大容量设备存储有效情况下,当USB状态变化也自动地挂载存储设备。创建了两个工作线程,MountService线程用于消息循环处理,为什么要开启一个异步消息处理线程呢?我们知道大量的Java Service驻留在SystemServer进程中,如果所有的服务消息都发送到SystemServer的主线程中处理的话,主线程的负荷很重,消息不能及时得到处理,因此需为每一个Service开启一个消息处理线程,专门处理本Service的消息。如下图所示:
MountService服务的线程模型
1.MountService线程创建
- mHandlerThread = new HandlerThread("MountService");
- mHandlerThread.start();
- mHandler = new MountServiceHandler(mHandlerThread.getLooper());
- // Add OBB Action Handler to MountService thread.
- mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
mHandlerThread = new HandlerThread("MountService");
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
MountServiceHandler消息处理:
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case H_UNMOUNT_PM_UPDATE: {
- UnmountCallBack ucb = (UnmountCallBack) msg.obj;
- mForceUnmounts.add(ucb);
- // Register only if needed.
- if (!mUpdatingStatus) {
- if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
- mUpdatingStatus = true;
- mPms.updateExternalMediaStatus(false, true);
- }
- break;
- }
- case H_UNMOUNT_PM_DONE: {
- mUpdatingStatus = false;
- int size = mForceUnmounts.size();
- int sizeArr[] = new int[size];
- int sizeArrN = 0;
- // Kill processes holding references first
- ActivityManagerService ams = (ActivityManagerService)
- ServiceManager.getService("activity");
- for (int i = 0; i < size; i++) {
- UnmountCallBack ucb = mForceUnmounts.get(i);
- 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 {
- // Eliminate system process here?
- ams.killPids(pids, "unmount media", true);
- // Confirm if file references have been freed.
- pids = getStorageUsers(path);
- if (pids == null || pids.length == 0) {
- done = true;
- }
- }
- }
- if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
- // Retry again
- Slog.i(TAG, "Retrying to kill storage users again");
- mHandler.sendMessageDelayed(mHandler.obtainMessage(H_UNMOUNT_PM_DONE,ucb.retries++),RETRY_UNMOUNT_DELAY);
- } else {
- if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
- Slog.i(TAG, "Failed to unmount media inspite of " +
- MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
- }
- sizeArr[sizeArrN++] = i;
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,ucb));
- }
- }
- // Remove already processed elements from list.
- for (int i = (sizeArrN-1); i >= 0; i--) {
- mForceUnmounts.remove(sizeArr[i]);
- }
- break;
- }
- case H_UNMOUNT_MS: {
- UnmountCallBack ucb = (UnmountCallBack) msg.obj;
- ucb.handleFinished();
- break;
- }
- }
- }
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
// Register only if needed.
if (!mUpdatingStatus) {
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
mUpdatingStatus = true;
mPms.updateExternalMediaStatus(false, true);
}
break;
}
case H_UNMOUNT_PM_DONE: {
mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {
UnmountCallBack ucb = mForceUnmounts.get(i);
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 {
// Eliminate system process here?
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(mHandler.obtainMessage(H_UNMOUNT_PM_DONE,ucb.retries++),RETRY_UNMOUNT_DELAY);
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,ucb));
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
case H_UNMOUNT_MS: {
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
}
}
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case OBB_RUN_ACTION: {
- final ObbAction action = (ObbAction) msg.obj;
- // If a bind was already initiated we don't really
- // need to do anything. The pending install
- // will be processed later on.
- if (!mBound) {
- // If this is the only one pending we might
- // have to bind to the service again.
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- action.handleError();
- return;
- }
- }
- mActions.add(action);
- break;
- }
- case OBB_MCS_BOUND: {
- if (msg.obj != null) {
- mContainerService = (IMediaContainerService) msg.obj;
- }
- if (mContainerService == null) {
- for (ObbAction action : mActions) {
- // Indicate service bind error
- action.handleError();
- }
- mActions.clear();
- } else if (mActions.size() > 0) {
- final ObbAction action = mActions.get(0);
- if (action != null) {
- action.execute(this);
- }
- } else {
- // Should never happen ideally.
- Slog.w(TAG, "Empty queue");
- }
- break;
- }
- case OBB_MCS_RECONNECT: {
- if (mActions.size() > 0) {
- if (mBound) {
- disconnectService();
- }
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- for (ObbAction action : mActions) {
- // Indicate service bind error
- action.handleError();
- }
- mActions.clear();
- }
- }
- break;
- }
- case OBB_MCS_UNBIND: {
- // Delete pending install
- if (mActions.size() > 0) {
- mActions.remove(0);
- }
- if (mActions.size() == 0) {
- if (mBound) {
- disconnectService();
- }
- } else {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
- }
- break;
- }
- case OBB_FLUSH_MOUNT_STATE: {
- final String path = (String) msg.obj;
- synchronized (mObbMounts) {
- final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
- final Iterator<Entry<String, ObbState>> i =mObbPathToStateMap.entrySet().iterator();
- while (i.hasNext()) {
- final Entry<String, ObbState> obbEntry = i.next();
- if (obbEntry.getKey().startsWith(path)) {
- obbStatesToRemove.add(obbEntry.getValue());
- }
- }
- for (final ObbState obbState : obbStatesToRemove) {
- removeObbStateLocked(obbState);
- try {
- obbState.token.onObbResult(obbState.filename, obbState.nonce,
- OnObbStateChangeListener.UNMOUNTED);
- } catch (RemoteException e) {
- Slog.i(TAG, "Couldn't send unmount notification for OBB: "+ obbState.filename);
- }
- }
- }
- break;
- }
- }
- }
public void handleMessage(Message msg) {
switch (msg.what) {
case OBB_RUN_ACTION: {
final ObbAction action = (ObbAction) msg.obj;
// If a bind was already initiated we don't really
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
action.handleError();
return;
}
}
mActions.add(action);
break;
}
case OBB_MCS_BOUND: {
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
}
mActions.clear();
} else if (mActions.size() > 0) {
final ObbAction action = mActions.get(0);
if (action != null) {
action.execute(this);
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
case OBB_MCS_RECONNECT: {
if (mActions.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
}
mActions.clear();
}
}
break;
}
case OBB_MCS_UNBIND: {
// Delete pending install
if (mActions.size() > 0) {
mActions.remove(0);
}
if (mActions.size() == 0) {
if (mBound) {
disconnectService();
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
}
break;
}
case OBB_FLUSH_MOUNT_STATE: {
final String path = (String) msg.obj;
synchronized (mObbMounts) {
final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
final Iterator<Entry<String, ObbState>> i =mObbPathToStateMap.entrySet().iterator();
while (i.hasNext()) {
final Entry<String, ObbState> obbEntry = i.next();
if (obbEntry.getKey().startsWith(path)) {
obbStatesToRemove.add(obbEntry.getValue());
}
}
for (final ObbState obbState : obbStatesToRemove) {
removeObbStateLocked(obbState);
try {
obbState.token.onObbResult(obbState.filename, obbState.nonce,
OnObbStateChangeListener.UNMOUNTED);
} catch (RemoteException e) {
Slog.i(TAG, "Couldn't send unmount notification for OBB: "+ obbState.filename);
}
}
}
break;
}
}
}
MountService命令下发流程
- public int mountVolume(String path) {
- //权限检验
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
- return doMountVolume(path);
- }
public int mountVolume(String path) {
//权限检验
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
return doMountVolume(path);
}
调用doMountVolume函数来挂载存储设备:
- private int doMountVolume(String path) {
- int rc = StorageResultCode.OperationSucceeded;
- try {
- //命令交给NativeDaemonConnector去发送
- mConnector.execute("volume", "mount", path);
- } catch (NativeDaemonConnectorException e) {
- //捕获命令发送的异常,根据异常码来决定发送失败的原因
- String action = null;
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedNoMedia) {
- /*
- * Attempt to mount but no media inserted
- */
- rc = StorageResultCode.OperationFailedNoMedia;
- } else if (code == VoldResponseCode.OpFailedMediaBlank) {
- if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
- /*
- * Media is blank or does not contain a supported filesystem
- */
- updatePublicVolumeState(path, Environment.MEDIA_NOFS);
- action = Intent.ACTION_MEDIA_NOFS;
- rc = StorageResultCode.OperationFailedMediaBlank;
- } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
- if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
- /*
- * Volume consistency check failed
- */
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
- action = Intent.ACTION_MEDIA_UNMOUNTABLE;
- rc = StorageResultCode.OperationFailedMediaCorrupt;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- /*
- * Send broadcast intent (if required for the failure)
- */
- if (action != null) {
- sendStorageIntent(action, path);
- }
- }
- return rc;
- }
private int doMountVolume(String path) {
int rc = StorageResultCode.OperationSucceeded;
try {
//命令交给NativeDaemonConnector去发送
mConnector.execute("volume", "mount", path);
} catch (NativeDaemonConnectorException e) {
//捕获命令发送的异常,根据异常码来决定发送失败的原因
String action = null;
int code = e.getCode();
if (code == VoldResponseCode.OpFailedNoMedia) {
/*
* Attempt to mount but no media inserted
*/
rc = StorageResultCode.OperationFailedNoMedia;
} else if (code == VoldResponseCode.OpFailedMediaBlank) {
if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
/*
* Media is blank or does not contain a supported filesystem
*/
updatePublicVolumeState(path, Environment.MEDIA_NOFS);
action = Intent.ACTION_MEDIA_NOFS;
rc = StorageResultCode.OperationFailedMediaBlank;
} else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
/*
* Volume consistency check failed
*/
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
action = Intent.ACTION_MEDIA_UNMOUNTABLE;
rc = StorageResultCode.OperationFailedMediaCorrupt;
} else {
rc = StorageResultCode.OperationFailedInternalError;
}
/*
* Send broadcast intent (if required for the failure)
*/
if (action != null) {
sendStorageIntent(action, path);
}
}
return rc;
}
NativeDaemonConnector命令发送:
- public NativeDaemonEvent execute(String cmd, Object... args)
- throws NativeDaemonConnectorException {
- //使用executeForList函数来发送命令和命令参数,并返回一组NativeDaemonEvent事件
- final NativeDaemonEvent[] events = executeForList(cmd, args);
- if (events.length != 1) {
- throw new NativeDaemonConnectorException("Expected exactly one response, but received " + events.length);
- }
- return events[0];
- }
public NativeDaemonEvent execute(String cmd, Object... args)
throws NativeDaemonConnectorException {
//使用executeForList函数来发送命令和命令参数,并返回一组NativeDaemonEvent事件
final NativeDaemonEvent[] events = executeForList(cmd, args);
if (events.length != 1) {
throw new NativeDaemonConnectorException("Expected exactly one response, but received " + events.length);
}
return events[0];
}
调用executeForList来发送命令和命令参数,并在这里设置超时时间:
- public NativeDaemonEvent[] executeForList(String cmd, Object... args)
- throws NativeDaemonConnectorException {
- //设置超时时间:DEFAULT_TIMEOUT = 1 * 60 * 1000
- return execute(DEFAULT_TIMEOUT, cmd, args);
- }
public NativeDaemonEvent[] executeForList(String cmd, Object... args)
throws NativeDaemonConnectorException {
//设置超时时间:DEFAULT_TIMEOUT = 1 * 60 * 1000
return execute(DEFAULT_TIMEOUT, cmd, args);
}
真正命令发送:
- public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
- throws NativeDaemonConnectorException {
- final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
- final int sequenceNumber = mSequenceNumber.incrementAndGet();
- final StringBuilder cmdBuilder = new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
- //发送起始时间
- final long startTime = SystemClock.elapsedRealtime();
- //命令组合
- makeCommand(cmdBuilder, cmd, args);
- final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */
- log("SND -> {" + logCmd + "}"); //SND -> {8 volume mount /storage/sdcard1}
- cmdBuilder.append('\0');
- final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */
- synchronized (mDaemonLock) {
- if (mOutputStream == null) {
- throw new NativeDaemonConnectorException("missing output stream");
- } else {
- try {
- //向socket中写入命令
- mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
- } catch (IOException e) {
- throw new NativeDaemonConnectorException("problem sending command", e);
- }
- }
- }
- NativeDaemonEvent event = null;
- do {
- event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd);
- if (event == null) {
- loge("timed-out waiting for response to " + logCmd);
- throw new NativeDaemonFailureException(logCmd, event);
- }
- log("RMV <- {" + event + "}");
- events.add(event);
- } while (event.isClassContinue());
- //发送结束时间
- final long endTime = SystemClock.elapsedRealtime();
- if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
- loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
- }
- if (event.isClassClientError()) {
- throw new NativeDaemonArgumentException(logCmd, event);
- }
- if (event.isClassServerError()) {
- throw new NativeDaemonFailureException(logCmd, event);
- }
- return events.toArray(new NativeDaemonEvent[events.size()]);
- }
public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
throws NativeDaemonConnectorException {
final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
final int sequenceNumber = mSequenceNumber.incrementAndGet();
final StringBuilder cmdBuilder = new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
//发送起始时间
final long startTime = SystemClock.elapsedRealtime();
//命令组合
makeCommand(cmdBuilder, cmd, args);
final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */
log("SND -> {" + logCmd + "}"); //SND -> {8 volume mount /storage/sdcard1}
cmdBuilder.append('\0');
final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
//向socket中写入命令
mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
NativeDaemonEvent event = null;
do {
event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd);
if (event == null) {
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonFailureException(logCmd, event);
}
log("RMV <- {" + event + "}");
events.add(event);
} while (event.isClassContinue());
//发送结束时间
final long endTime = SystemClock.elapsedRealtime();
if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
}
if (event.isClassClientError()) {
throw new NativeDaemonArgumentException(logCmd, event);
}
if (event.isClassServerError()) {
throw new NativeDaemonFailureException(logCmd, event);
}
return events.toArray(new NativeDaemonEvent[events.size()]);
}
MountService消息接收流程
1)socket连接
- public void run() {
- //创建并启动VoldConnector.CallbackHandler线程,用于处理native层的Vold进程发送过来的uevent事件消息
- HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
- thread.start();
- //为VoldConnector.CallbackHandler线程创建一个Handler,用于向该线程分发消息
- mCallbackHandler = new Handler(thread.getLooper(), this);
- //进入闭环socket连接模式
- while (true) {
- try {
- listenToSocket();
- } catch (Exception e) {
- loge("Error in NativeDaemonConnector: " + e);
- SystemClock.sleep(5000);
- }
- }
- }
public void run() {
//创建并启动VoldConnector.CallbackHandler线程,用于处理native层的Vold进程发送过来的uevent事件消息
HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
thread.start();
//为VoldConnector.CallbackHandler线程创建一个Handler,用于向该线程分发消息
mCallbackHandler = new Handler(thread.getLooper(), this);
//进入闭环socket连接模式
while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
连接服务端Socket,并读取数据:
- private void listenToSocket() throws IOException {
- LocalSocket socket = null;
- try {
- //创建Vold socket
- socket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress(mSocket,LocalSocketAddress.Namespace.RESERVED);
- //向服务端发起连接请求
- socket.connect(address);
- //从连接的socket中得到输入输出流
- InputStream inputStream = socket.getInputStream();
- synchronized (mDaemonLock) {
- mOutputStream = socket.getOutputStream();
- }
- //对本次连接请求做一些回调处理
- mCallbacks.onDaemonConnected();
- //定义buffer
- byte[] buffer = new byte[BUFFER_SIZE];
- int start = 0;
- //进入闭环数据读取模式
- while (true) {
- int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
- //当读取的数据长度小于0时,表示连接已断开,跳出循环,重新向服务端发起新的连接请求
- if (count < 0) {
- loge("got " + count + " reading with start = " + start);
- break;
- }
- // Add our starting point to the count and reset the start.
- count += start;
- start = 0;
- //解析读取到的数据,得到NativeDaemonEvent
- for (int i = 0; i < count; i++) {
- if (buffer[i] == 0) {
- final String rawEvent = new String(buffer, start, i - start, Charsets.UTF_8);
- //RCV <- {632 Volume sdcard /storage/sdcard1 bad removal (179:1)}
- log("RCV <- {" + rawEvent + "}");
- try {
- final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent);
- //如果命令码code >= 600 && code < 700
- if (event.isClassUnsolicited()) {
- //将读取到的事件发送到VoldConnector.CallbackHandler线程中处理
- mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
- event.getCode(), event.getRawEvent()));
- //否则将改事件添加到响应队列中
- } else {
- mResponseQueue.add(event.getCmdNumber(), event);
- }
- } catch (IllegalArgumentException e) {
- log("Problem parsing message: " + rawEvent + " - " + e);
- }
- start = i + 1;
- }
- }
- if (start == 0) {
- final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
- log("RCV incomplete <- {" + rawEvent + "}");
- }
- // We should end at the amount we read. If not, compact then
- // buffer and read again.
- if (start != count) {
- final int remaining = BUFFER_SIZE - start;
- System.arraycopy(buffer, start, buffer, 0, remaining);
- start = remaining;
- } else {
- start = 0;
- }
- }
- } catch (IOException ex) {
- loge("Communications error: " + ex);
- throw ex;
- } finally {
- synchronized (mDaemonLock) {
- if (mOutputStream != null) {
- try {
- loge("closing stream for " + mSocket);
- mOutputStream.close();
- } catch (IOException e) {
- loge("Failed closing output stream: " + e);
- }
- mOutputStream = null;
- }
- }
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (IOException ex) {
- loge("Failed closing socket: " + ex);
- }
- }
- }
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
//创建Vold socket
socket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress(mSocket,LocalSocketAddress.Namespace.RESERVED);
//向服务端发起连接请求
socket.connect(address);
//从连接的socket中得到输入输出流
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
//对本次连接请求做一些回调处理
mCallbacks.onDaemonConnected();
//定义buffer
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
//进入闭环数据读取模式
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
//当读取的数据长度小于0时,表示连接已断开,跳出循环,重新向服务端发起新的连接请求
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
// Add our starting point to the count and reset the start.
count += start;
start = 0;
//解析读取到的数据,得到NativeDaemonEvent
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
final String rawEvent = new String(buffer, start, i - start, Charsets.UTF_8);
//RCV <- {632 Volume sdcard /storage/sdcard1 bad removal (179:1)}
log("RCV <- {" + rawEvent + "}");
try {
final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent);
//如果命令码code >= 600 && code < 700
if (event.isClassUnsolicited()) {
//将读取到的事件发送到VoldConnector.CallbackHandler线程中处理
mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()));
//否则将改事件添加到响应队列中
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message: " + rawEvent + " - " + e);
}
start = i + 1;
}
}
if (start == 0) {
final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
log("RCV incomplete <- {" + rawEvent + "}");
}
// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}
2)连接成功回调处理
- public void onDaemonConnected() {
- //创建一个工作线程
- new Thread("MountService#onDaemonConnected") {
- @Override
- public void run() {
- /**
- * Determine media state and UMS detection status
- */
- try {
- //向vold查询所有的存储设备
- final String[] vols = NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("volume", "list"),
- VoldResponseCode.VolumeListResult);
- //判断存储设备状态
- for (String volstr : vols) {
- String[] tok = volstr.split(" ");
- // FMT: <label> <mountpoint> <state>
- String path = tok[1];
- String state = Environment.MEDIA_REMOVED;
- int st = Integer.parseInt(tok[2]);
- if (st == VolumeState.NoMedia) {
- state = Environment.MEDIA_REMOVED;
- } else if (st == VolumeState.Idle) {
- state = Environment.MEDIA_UNMOUNTED;
- } else if (st == VolumeState.Mounted) {
- state = Environment.MEDIA_MOUNTED;
- Slog.i(TAG, "Media already mounted on daemon connection");
- } else if (st == VolumeState.Shared) {
- state = Environment.MEDIA_SHARED;
- Slog.i(TAG, "Media shared on daemon connection");
- } else {
- throw new Exception(String.format("Unexpected state %d", st));
- }
- if (state != null) {
- if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
- //更新Volume状态
- updatePublicVolumeState(path, state);
- }
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error processing initial volume state", e);
- updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
- }
- /*
- * Now that we've done our initialization, release
- * the hounds!
- */
- mConnectedSignal.countDown();
- mConnectedSignal = null;
- // 使用PackageManagerService扫描外边存储设备上的APK信息
- mPms.scanAvailableAsecs();
- // Notify people waiting for ASECs to be scanned that it's done.
- mAsecsScanned.countDown();
- mAsecsScanned = null;
- }
- }.start();
- }
public void onDaemonConnected() {
//创建一个工作线程
new Thread("MountService#onDaemonConnected") {
@Override
public void run() {
/**
* Determine media state and UMS detection status
*/
try {
//向vold查询所有的存储设备
final String[] vols = NativeDaemonEvent.filterMessageList(
mConnector.executeForList("volume", "list"),
VoldResponseCode.VolumeListResult);
//判断存储设备状态
for (String volstr : vols) {
String[] tok = volstr.split(" ");
// FMT: <label> <mountpoint> <state>
String path = tok[1];
String state = Environment.MEDIA_REMOVED;
int st = Integer.parseInt(tok[2]);
if (st == VolumeState.NoMedia) {
state = Environment.MEDIA_REMOVED;
} else if (st == VolumeState.Idle) {
state = Environment.MEDIA_UNMOUNTED;
} else if (st == VolumeState.Mounted) {
state = Environment.MEDIA_MOUNTED;
Slog.i(TAG, "Media already mounted on daemon connection");
} else if (st == VolumeState.Shared) {
state = Environment.MEDIA_SHARED;
Slog.i(TAG, "Media shared on daemon connection");
} else {
throw new Exception(String.format("Unexpected state %d", st));
}
if (state != null) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
//更新Volume状态
updatePublicVolumeState(path, state);
}
}
} catch (Exception e) {
Slog.e(TAG, "Error processing initial volume state", e);
updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
}
/*
* Now that we've done our initialization, release
* the hounds!
*/
mConnectedSignal.countDown();
mConnectedSignal = null;
// 使用PackageManagerService扫描外边存储设备上的APK信息
mPms.scanAvailableAsecs();
// Notify people waiting for ASECs to be scanned that it's done.
mAsecsScanned.countDown();
mAsecsScanned = null;
}
}.start();
}
存储设备状态更新:
- private boolean updatePublicVolumeState(String path, String state) {
- String oldState;
- synchronized(mVolumeStates) {
- oldState = mVolumeStates.put(path, state);
- }
- if (state.equals(oldState)) {
- Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",state, state, path));
- return false;
- }
- Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
- if (path.equals(mExternalStoragePath)) {
- // Update state on PackageManager, but only of real events
- if (!mEmulateExternalStorage) {
- if (Environment.MEDIA_UNMOUNTED.equals(state)) {
- 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)) {
- mPms.updateExternalMediaStatus(true, false);
- }
- }
- }
- synchronized (mListeners) {
- for (int i = mListeners.size() -1; i >= 0; i--) {
- MountServiceBinderListener bl = mListeners.get(i);
- try {
- //调用已注册的MountServiceBinderListener来通知存储设备状态改变
- 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);
- }
- }
- }
- return true;
- }
private boolean updatePublicVolumeState(String path, String state) {
String oldState;
synchronized(mVolumeStates) {
oldState = mVolumeStates.put(path, state);
}
if (state.equals(oldState)) {
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",state, state, path));
return false;
}
Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
if (path.equals(mExternalStoragePath)) {
// Update state on PackageManager, but only of real events
if (!mEmulateExternalStorage) {
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
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)) {
mPms.updateExternalMediaStatus(true, false);
}
}
}
synchronized (mListeners) {
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
//调用已注册的MountServiceBinderListener来通知存储设备状态改变
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);
}
}
}
return true;
}
- public void registerListener(IMountServiceListener listener) {
- synchronized (mListeners) {
- MountServiceBinderListener bl = new MountServiceBinderListener(listener);
- try {
- listener.asBinder().linkToDeath(bl, 0);
- mListeners.add(bl);
- } catch (RemoteException rex) {
- Slog.e(TAG, "Failed to link to listener death");
- }
- }
- }
public void registerListener(IMountServiceListener listener) {
synchronized (mListeners) {
MountServiceBinderListener bl = new MountServiceBinderListener(listener);
try {
listener.asBinder().linkToDeath(bl, 0);
mListeners.add(bl);
} catch (RemoteException rex) {
Slog.e(TAG, "Failed to link to listener death");
}
}
}
使用StorageManager的内部类MountServiceBinderListener对象来构造MountService的内部类MountServiceBinderListener对象,并添加到MountService的成员变量mListeners列表中。StorageManager的内部类MountServiceBinderListener定义如下:
- private class MountServiceBinderListener extends IMountServiceListener.Stub {
- public void onUsbMassStorageConnectionChanged(boolean available) {
- final int size = mListeners.size();
- for (int i = 0; i < size; i++) {
- mListeners.get(i).sendShareAvailabilityChanged(available);
- }
- }
- 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);
- }
- }
- }
private class MountServiceBinderListener extends IMountServiceListener.Stub {
public void onUsbMassStorageConnectionChanged(boolean available) {
final int size = mListeners.size();
for (int i = 0; i < size; i++) {
mListeners.get(i).sendShareAvailabilityChanged(available);
}
}
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);
}
}
}
最后调用ListenerDelegate的sendStorageStateChanged来实现
3)事件处理
- public boolean handleMessage(Message msg) {
- String event = (String) msg.obj;
- try {
- //回调MountService的onEvent函数进行处理
- if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
- log(String.format("Unhandled event '%s'", event));
- }
- } catch (Exception e) {
- loge("Error handling '" + event + "': " + e);
- }
- return true;
- }
public boolean handleMessage(Message msg) {
String event = (String) msg.obj;
try {
//回调MountService的onEvent函数进行处理
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format("Unhandled event '%s'", event));
}
} catch (Exception e) {
loge("Error handling '" + event + "': " + e);
}
return true;
}
- public boolean onEvent(int code, String raw, String[] cooked) {
- 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);
- }
- }
- Slog.i(TAG, builder.toString());
- }
- if (code == VoldResponseCode.VolumeStateChange) {
- /*
- * One of the volumes we're managing has changed state.
- * Format: "NNN Volume <label> <path> state changed
- * from <old_#> (<old_str>) to <new_#> (<new_str>)"
- */
- notifyVolumeStateChange(cooked[2], cooked[3], Integer.parseInt(cooked[7]),Integer.parseInt(cooked[10]));
- } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
- (code == VoldResponseCode.VolumeDiskRemoved) ||
- (code == VoldResponseCode.VolumeBadRemoval)) {
- // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
- // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
- // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
- String action = null;
- final String label = cooked[2];
- final String path = cooked[3];
- int major = -1;
- int minor = -1;
- try {
- String devComp = cooked[6].substring(1, cooked[6].length() -1);
- String[] devTok = devComp.split(":");
- major = Integer.parseInt(devTok[0]);
- minor = Integer.parseInt(devTok[1]);
- } catch (Exception ex) {
- Slog.e(TAG, "Failed to parse major/minor", ex);
- }
- if (code == VoldResponseCode.VolumeDiskInserted) {
- new Thread() {
- @Override
- public void run() {
- try {
- int rc;
- if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
- Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
- }
- } catch (Exception ex) {
- Slog.w(TAG, "Failed to mount media on insertion", ex);
- }
- }
- }.start();
- } else if (code == VoldResponseCode.VolumeDiskRemoved) {
- /*
- * This event gets trumped if we're already in BAD_REMOVAL state
- */
- if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
- return true;
- }
- /* Send the media unmounted event first */
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
- updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
- action = Intent.ACTION_MEDIA_REMOVED;
- } else if (code == VoldResponseCode.VolumeBadRemoval) {
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
- /* Send the media unmounted event first */
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
- action = Intent.ACTION_MEDIA_UNMOUNTED;
- if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
- updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
- action = Intent.ACTION_MEDIA_BAD_REMOVAL;
- } else {
- Slog.e(TAG, String.format("Unknown code {%d}", code));
- }
- if (action != null) {
- sendStorageIntent(action, path);
- }
- } else {
- return false;
- }
- return true;
- }
public boolean onEvent(int code, String raw, String[] cooked) {
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);
}
}
Slog.i(TAG, builder.toString());
}
if (code == VoldResponseCode.VolumeStateChange) {
/*
* One of the volumes we're managing has changed state.
* Format: "NNN Volume <label> <path> state changed
* from <old_#> (<old_str>) to <new_#> (<new_str>)"
*/
notifyVolumeStateChange(cooked[2], cooked[3], Integer.parseInt(cooked[7]),Integer.parseInt(cooked[10]));
} else if ((code == VoldResponseCode.VolumeDiskInserted) ||
(code == VoldResponseCode.VolumeDiskRemoved) ||
(code == VoldResponseCode.VolumeBadRemoval)) {
// FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
// FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
// FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
String action = null;
final String label = cooked[2];
final String path = cooked[3];
int major = -1;
int minor = -1;
try {
String devComp = cooked[6].substring(1, cooked[6].length() -1);
String[] devTok = devComp.split(":");
major = Integer.parseInt(devTok[0]);
minor = Integer.parseInt(devTok[1]);
} catch (Exception ex) {
Slog.e(TAG, "Failed to parse major/minor", ex);
}
if (code == VoldResponseCode.VolumeDiskInserted) {
new Thread() {
@Override
public void run() {
try {
int rc;
if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
}
} catch (Exception ex) {
Slog.w(TAG, "Failed to mount media on insertion", ex);
}
}
}.start();
} else if (code == VoldResponseCode.VolumeDiskRemoved) {
/*
* This event gets trumped if we're already in BAD_REMOVAL state
*/
if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
return true;
}
/* Send the media unmounted event first */
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
action = Intent.ACTION_MEDIA_REMOVED;
} else if (code == VoldResponseCode.VolumeBadRemoval) {
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
/* Send the media unmounted event first */
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
action = Intent.ACTION_MEDIA_UNMOUNTED;
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
action = Intent.ACTION_MEDIA_BAD_REMOVAL;
} else {
Slog.e(TAG, String.format("Unknown code {%d}", code));
}
if (action != null) {
sendStorageIntent(action, path);
}
} else {
return false;
}
return true;
}
整个MountService与Vold的通信到此就介绍完了。