在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监听线程进行详细讲解。
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对象实例:
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>
该文件的读取这里不在介绍,读者自行研究,使用XML解析器读取该XML的文件内容,根据读取到的存储设备参数来构造StorageVolume对象,并将构造的所有StorageVolume对象存放到列表mVolumes中。通过源码清晰地知道,在构造MountService时,注册了一个广播接收器,用于接收开机完成广播及USB状态广播,当开机完成时自动挂载存储设备,在大容量设备存储有效情况下,当USB状态变化也自动地挂载存储设备。创建了两个工作线程,MountService线程用于消息循环处理,为什么要开启一个异步消息处理线程呢?我们知道大量的Java Service驻留在SystemServer进程中,如果所有的服务消息都发送到SystemServer的主线程中处理的话,主线程的负荷很重,消息不能及时得到处理,因此需为每一个Service开启一个消息处理线程,专门处理本Service的消息。如下图所示:
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 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 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; }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]; }调用executeForList来发送命令和命令参数,并在这里设置超时时间:
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 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); } } }
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; }
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); } } }最后调用ListenerDelegate的sendStorageStateChanged来实现
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; }整个MountService与Vold的通信到此就介绍完了。