Android vold进程三 MountService介绍
来源:互联网 发布:徐留平一汽改革知乎 编辑:程序博客网 时间:2024/05/17 16:46
一、MountService的创建
MountService:Android Binder服务端,运行在system_server进程,用于跟Vold进行消息通信,比如MountService向Vold发送挂载SD卡的命令,或者接收到来自Vold的外设热插拔事件。MountService作为Binder服务端,那么相应的Binder客户端便是StorageManager,通过binder IPC与MountService交互。
MountService是一个系统服务,是在SystemServer中启动的,这里new 了一个MountService,并把service添加到了ServiceManager管理:
./frameworks/base/services/java/com/android/server/SystemServer.javaprivate void startOtherServices() { ...... try { /* * NotificationManagerService is dependant on MountService, * (for media / usb notifications) so we must start MountService first. */ //启动MountService服务 mSystemServiceManager.startService(MOUNT_SERVICE_CLASS); //等价new IMountService.Stub.Proxy(),即获取MountService的proxy对象 mountService = IMountService.Stub.asInterface( ServiceManager.getService("mount")); } catch (Throwable e) { reportWtf("starting Mount Service", e); } ......}
上面mSystemServiceManager.startService(MOUNT_SERVICE_CLASS)主要完成3件事:
1、创建MOUNT_SERVICE_CLASS所指类的Lifecycle对象;
2、将该对象添加SystemServiceManager的 mServices 服务列表;
3、最后调用Lifecycle的onStart()方法;
下面看Lifecycle的onStart方法:
public static class Lifecycle extends SystemService { private MountService mMountService; private String oldDefaultPath = ""; public Lifecycle(Context context) { super(context); } @Override public void onStart() { Slog.d(TAG, "MountService onStart"); sSelf.isBootingPhase = true; //创建MountService对象 mMountService = new MountService(getContext()); //通知Binder服务 publishBinderService("mount", mMountService); //启动mMountService mMountService.start(); oldDefaultPath = sSelf.getDefaultPath(); Slog.d(TAG, "get Default path onStart default path=" + oldDefaultPath); } ...... }
上面创建了MountService对象,并通知Binder服务的大管家ServiceManager,MountService的服务名为“mount”,对应服务对象为mMountService。通知之后,其他地方需要MountService的服务时便可以通过服务名来向ServiceManager来查询具体的MountService服务。
二、MountService的启动
上面介绍了SystemServer创建MountService的流程,下面将详细介绍MountService的启动流程,先看MountService的构造函数:
frameworks/base/services/core/java/com/android/server/MountService.java public MountService(Context context) { sSelf = this; mContext = context; //FgThread线程名为“"android.fg",创建IMountServiceListener回调方法 mCallbacks = new Callbacks(FgThread.get().getLooper()); mLockPatternUtils = new LockPatternUtils(mContext); //获取PackageManagerService的Client端对象,管理服务 mPms = (PackageManagerService) ServiceManager.getService("package"); //创建“MountService”线程,处理消息 HandlerThread hthread = new HandlerThread(TAG); hthread.start(); //MountService中的消息处理运行在hthread线程中 mHandler = new MountServiceHandler(hthread.getLooper()); //IoThread线程名为"android.io",创建OBB操作的handler mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); //判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间 File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE); if (!mLastMaintenanceFile.exists()) { // Not setting mLastMaintenance here means that we will force an // fstrim during reboot following the OTA that installs this code. try { (new FileOutputStream(mLastMaintenanceFile)).close(); } catch (IOException e) { Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath()); } } else { mLastMaintenance = mLastMaintenanceFile.lastModified(); } mSettingsFile = new AtomicFile( new File(Environment.getDataSystemDirectory(), "storage.xml")); synchronized (mLock) { readSettingsLocked(); } //将MountServiceInternalImpl登记到sLocalServiceObjects LocalServices.addService(MountServiceInternal.class, mMountServiceInternal); /* * 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. */ //创建用于VoldConnector的NativeDaemonConnector对象 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, null); mConnector.setDebug(true); //创建线程名为"VoldConnector"的线程,用于跟vold通信 mConnector.setWarnIfHeld(mLock); mConnectorThread = new Thread(mConnector, VOLD_TAG); //创建用于CryptdConnector的NativeDaemonConnector对象 // Reuse parameters from first connector since they are tested and safe mCryptConnector = new NativeDaemonConnector(this, "cryptd", MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null); mCryptConnector.setDebug(true); //创建线程名为"CryptdConnector"的线程,用于加密 mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG); //注册监听用户添加、删除的广播 final IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_ADDED); userFilter.addAction(Intent.ACTION_USER_REMOVED); userFilter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); synchronized (mLock) { addInternalVolumeLocked(); } // Add ourself to the Watchdog monitors if enabled. //默认为false if (WATCHDOG_ENABLE) { Watchdog.getInstance().addMonitor(this); } initMTKFeature(); }
其中MountService完成的主要任务如下:
1、创建ICallbacks回调方法,FgThread线程名为”android.fg”;
3、创建并启动线程名为”MountService”的handlerThread用来处理消息;
4、创建OBB操作的handler,IoThread线程名为”android.io“;
5、创建NativeDaemonConnector对象;
6、创建并启动线程名为”VoldConnector”的线程;
7、创建并启动线程名为”CryptdConnector”的线程;
8、注册监听用户添加、删除的广播;
由上可以看出Java层与MountService相关主要线程如下:
//ps -t |grep 804system 804 442 1987512 155668 SyS_epoll_ 73d36167a0 S system_serversystem 891 804 1987512 155668 SyS_epoll_ 73d36167a0 S android.fgsystem 892 804 1987512 155668 SyS_epoll_ 73d36167a0 S android.iosystem 1083 804 1987512 155668 SyS_epoll_ 73d36167a0 S MountServicesystem 1084 804 1987512 155668 unix_strea 73d3617328 S VoldConnectorsystem 1085 804 1987512 155668 unix_strea 73d3617328 S CryptdConnector
同时在native层中的守护进程vold在系统中显示如下,可通过ps -t |grep -nr vold查看系统线程:
USER PID PPID VSIZE RSS WCHAN PC NAMEroot 255 1 62588 4488 hrtimer_na 7a06573190 S /system/bin/voldroot 258 255 62588 4488 poll_sched 7a065728d8 S voldroot 259 255 62588 4488 poll_sched 7a065728d8 S voldroot 260 255 62588 4488 poll_sched 7a065728d8 S voldmedia_rw 1201 255 21280 2712 inotify_re 709185d298 S /system/bin/sdcard
三、MountService的指令发送
上面介绍了MountService的启动,和一系列与MountService相关的线程的创建,下面分析MountService命令的传输,首先Vold作为存储设备的管控中心,需要接收来自上层MountService的操作命令,MountService驻留在SystemServer进程中,和Vold作为两个不同的进程,它们之间的通信方式采用的是socket通信,而在MountService这端,同样启动了VoldConnector socket连接线程,用于循环连接服务端,保证连接不被中断,当成功连接Vold时,循环从服务端读取数据。MountService按照指定格式向Vold发送命令,由于发送的命令比较多,这里不做一一接收,只对其中的mount命令的发送流程进行介绍,首先看下面的时序图:
下面看详细的代码:
./frameworks/base/services/core/java/com/android/server/MountService.java
public int mountVolume(String path) { Slog.i(TAG, "mountVolume, path=" + path); mount(findVolumeIdForPathOrThrow(path)); return 0; }
调用自身的mount来传送挂载命令:
public void mount(String volId) { //权限检验 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); final VolumeInfo vol = findVolumeByIdOrThrow(volId); Slog.i(TAG, "mount, volId=" + volId + ", volumeInfo=" + vol); if (isMountDisallowed(vol)) { throw new SecurityException("Mounting " + volId + " restricted by policy"); } try { //调用execute将命令交给NativeDaemonConnector去发送 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId); } catch (NativeDaemonConnectorException e) { //throw e.rethrowAsParcelableException(); Slog.e(TAG, "mount" + vol + "ERROR!!"); } }
下面继续看线程NativeDaemonConnector中所做的工作:
frameworks\base\services\core\java\com\android\server\NativeDaemonConnector.java public NativeDaemonEvent execute(String cmd, Object... args) throws NativeDaemonConnectorException { //设置超时时间:DEFAULT_TIMEOUT = 1 * 60 * 1000 return execute(DEFAULT_TIMEOUT, cmd, args); } public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args) throws NativeDaemonConnectorException { //调用executeForList发送命令 final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args); if (events.length != 1) { throw new NativeDaemonConnectorException( "Expected exactly one response, but received " + events.length); } return events[0]; }
在executeForList中首先判断命令的合法性,并对命令进行处理后写入到socket输出流mOutputStream中。
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args) throws NativeDaemonConnectorException { if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { loge("Calling thread " + Thread.currentThread().getName() + " is holding 0x" + Integer.toHexString(System.identityHashCode(mWarnIfHeld))); } //获取数据传输的开始时间 final long startTime = SystemClock.elapsedRealtime(); final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); final StringBuilder rawBuilder = new StringBuilder(); final StringBuilder logBuilder = new StringBuilder(); //mSequenceNumber初始化值为0,每执行一次该方法则进行加1操作 final int sequenceNumber = mSequenceNumber.incrementAndGet(); //对指令进行处理 makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args); final String rawCmd = rawBuilder.toString(); final String logCmd = logBuilder.toString(); //eg:VoldConnector: SND -> {13 volume unmount public:179,129} log("SND -> {" + logCmd + "}"); synchronized (mDaemonLock) { if (mOutputStream == null) { throw new NativeDaemonConnectorException("missing output stream"); } else { try { //将命令写入到socket输出流 mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new NativeDaemonConnectorException("problem sending command", e); } } } NativeDaemonEvent event = null; do { //设置指令接收时间60S,超过60S跳出循环 event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd); if (event == null) { loge("timed-out waiting for response to " + logCmd); throw new NativeDaemonTimeoutException(logCmd, event); } if (VDBG) log("RMV <- {" + event + "}"); events.add(event); //当收到的事件响应码属于[100,200)区间,则继续等待后续事件上报,否则跳出循环 } while (event.isClassContinue()); //获取数据传输的结束时间,并判断是否超过WARN_EXECUTE_DELAY_MS=0.5S 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()]); }
以上executeForList是MountService指令传输中的重要函数,其重主要完成的任务总结如下:
(1) 带执行的命令mSequenceNumber执行加1操作;
(2) 将cmd写入到socket的输出流;
(3) 通过循环与poll机制阻塞等待底层响应该操作完成的结果;
有两个情况会跳出循环:
a、当超过1分钟未收到vold相应事件的响应码,则跳出阻塞等待;
b、当收到底层的响应码,且响应码不属于[100,200)区间,则跳出循环。
(4)对于执行时间超过500ms的时间,则额外输出以NDC Command开头的log信息。
MountService向vold发送消息后,便阻塞在MountService线程的NDC.execute()方法,当MonutService接收到vold返回的消息,且消息响应吗不属于区间[600,700)则添加事件到ResponseQueue,从而唤醒阻塞的MountService继续执行。
以上接收了MountService发送消息给vold的处理过程下面继续分析MountService接收消息的处理过程。
四、MountService指令接收
MountService接收来自vold的消息主要分为两种类型:
(1)当MountService向Vold发送命令后,将接收到Vold的响应消息;
(2)当外部存储设备发生热插拔时,kernel将通过netlink方式通知Vold,Vold进程经过一系列处理后最终还是要将uevent事件消息发送给MountService,Vold发送uevent的过程前文[ vold进程二]已经介绍(http://blog.csdn.net/frank_zyp/article/details/56666576)
以上消息类型MountService都是通过VoldConnector线程来循环接收Vold的请求。
1、接收来自vold的返回码
前面已经介绍当Vold在处理完完MountService发送过来的消息后,会通过sendGenericOkFail发送应答消息给上层的MountService。
//./system/vold/CommandListener.cppint CommandListener::sendGenericOkFail(SocketClient *cli, int cond) { if (!cond) { //返回响应码为200的成功应答消息 return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false); } else { //返回响应码为400的失败应答消息 return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false); }}
其中返回的ResponseCode主要包含以下几种:
class ResponseCode {public: //部分响应,随后继续产生事件 // 100 series - Requestion action was initiated; expect another reply // before proceeding with a new command. static const int ActionInitiated = 100; static const int VolumeListResult = 110; static const int AsecListResult = 111; static const int StorageUsersListResult = 112; static const int CryptfsGetfieldResult = 113; //成功响应 // 200 series - Requested action has been successfully completed static const int CommandOkay = 200; static const int ShareStatusResult = 210; static const int AsecPathResult = 211; static const int ShareEnabledResult = 212; static const int PasswordTypeResult = 213; static const int CdromStatusResult = 214; //本地客户端错误 // 500 series - The command was not accepted and the requested // action did not take place. static const int CommandSyntaxError = 500; static const int CommandParameterError = 501; static const int CommandNoPermission = 502; //远程Vold进程自触发的事件,主要是针对disk,volume的一系列操作, //比如设备创建,状态、路径改变,以及文件类型、uid、标签改变等事件都是底层直接触发。 // 600 series - Unsolicited broadcasts static const int UnsolicitedInformational = 600; static const int VolumeStateChange = 605; static const int VolumeMountFailedBlank = 610; static const int VolumeMountFailedDamaged = 611; static const int VolumeMountFailedNoMedia = 612; static const int VolumeUuidChange = 613; static const int VolumeUserLabelChange = 614; static const int ShareAvailabilityChange = 620; static const int VolumeDiskInserted = 630; static const int VolumeDiskRemoved = 631; static const int VolumeBadRemoval = 632; static const int DiskCreated = 640; static const int DiskSizeChanged = 641; static const int DiskLabelChanged = 642; static const int DiskScanned = 643; static const int DiskSysPathChanged = 644; static const int DiskDestroyed = 649; static const int VolumeCreated = 650; static const int VolumeStateChanged = 651; static const int VolumeFsTypeChanged = 652; static const int VolumeFsUuidChanged = 653; static const int VolumeFsLabelChanged = 654; static const int VolumePathChanged = 655; static const int VolumeInternalPathChanged = 656; static const int VolumeDestroyed = 659; static const int MoveStatus = 660; static const int BenchmarkResult = 661; static const int TrimResult = 662; static int convertFromErrno();};
上面的消息通过socket管道发送,最后将消息写入到管道中:
//SocketClient.cppint SocketClient::sendMsg(int code, const char *msg, bool addErrno) { return sendMsg(code, msg, addErrno, mUseCmdNum);}
当vold进程将消息写入到管道中后,MountService会有线程NativeDaemonConnector循环的的读取并处理管道中的消息:
//./frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java public void run() { //为VoldConnector线程创建一个Handler,用于向该线程分发消息 mCallbackHandler = new Handler(mLooper, this); //循环监听来自kernel的消息 while (true) { try { listenToSocket();//处理并分发消息 } catch (Exception e) { loge("Error in NativeDaemonConnector: " + e); SystemClock.sleep(5000); } } }
下面看处理消息的重要函数listenToSocket:
//./frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java private void listenToSocket() throws IOException { LocalSocket socket = null; try { //创建Vold socket socket = new LocalSocket(); LocalSocketAddress address = determineSocketAddress(); //向服务端发起连接请求 socket.connect(address); //获取连接的socket中得到输入输出流 InputStream inputStream = socket.getInputStream(); synchronized (mDaemonLock) { mOutputStream = socket.getOutputStream(); } //对本次连接请求做一些回调处理 mCallbacks.onDaemonConnected(); FileDescriptor[] fdList = null; byte[] buffer = new byte[BUFFER_SIZE]; int start = 0; while (true) { //从socket输出流中读取数据 int count = inputStream.read(buffer, start, BUFFER_SIZE - start); if (count < 0) { loge("got " + count + " reading with start = " + start); break; } fdList = socket.getAncillaryFileDescriptors(); // 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) { // Note - do not log this raw message since it may contain // sensitive data final String rawEvent = new String( buffer, start, i - start, StandardCharsets.UTF_8); boolean releaseWl = false; try { final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent, fdList); log("RCV <- {" + event + "}"); //如果命令码code >= 600 && code < 700 if (event.isClassUnsolicited()) { //将读取到的事件发送到VoldConnector.CallbackHandler线程中处理 if (mCallbacks.onCheckHoldWakeLock(event.getCode()) && mWakeLock != null) { mWakeLock.acquire(); releaseWl = true; } Message msg = mCallbackHandler.obtainMessage( event.getCode(), uptimeMillisInt(), 0, event.getRawEvent()); if (mCallbackHandler.sendMessage(msg)) { releaseWl = false; } } else { //对于其他响应码则添加到mResponseQueue队列 mResponseQueue.add(event.getCmdNumber(), event); } } catch (IllegalArgumentException e) { log("Problem parsing message " + e); } finally { if (releaseWl) { mWakeLock.release(); } } start = i + 1; } } if (start == 0) { log("RCV incomplete"); } ...... }
以上代码中主要完成了两项工作:
(1)当vold返回的响应吗不在区间[600,700),则将该事件添加到mResponseQueue,并且触发响应事件所对应的请求事件不再阻塞到ResponseQueue.poll,那么线程继续往下执行;
(2)当返回的响应码区间为[600,700):则发送消息交由mCallbackHandler处理,向线程android.fg发送Handler消息,该线程收到后回调NativeDaemonConnector的handleMessage来处理。
2、处理设备挂载等命令
上面介绍了listenToSocket中数据处理的第一种情况,下面将分析来自不请自来的广播的消息。分析之前看看前面文章分析的vold传输命令的过程,如下面的通信流程图:
在NativeDaemonConnector的run函数中,可知消息通过handler消息机制,由mCallbackHandler处理,NativeDaemonConnector的handleMessage如下:
public boolean handleMessage(Message msg) { ...... //mCallbacks是由实例化NativeDaemonConnector对象时传递进来的,在这里是指MountService if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { log(String.format("Unhandled event '%s'", event)); } ...... }
接着看MountService的onEvent方法:
//./frameworks/base/services/core/java/com/android/server/MountService.java public boolean onEvent(int code, String raw, String[] cooked) { synchronized (mLock) { return onEventLocked(code, raw, cooked); } } private boolean onEventLocked(int code, String raw, String[] cooked) { //处理各种设备挂载等的命令 ...... switch (code) { case VoldResponseCode.DISK_CREATED: { Slog.d(TAG, "DISK_CREATED"); if (cooked.length != 3) break; final String id = cooked[1]; int flags = Integer.parseInt(cooked[2]); .. if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false) || mForceAdoptable) { flags |= DiskInfo.FLAG_ADOPTABLE; } mDisks.put(id, new DiskInfo(id, flags)); Slog.d(TAG, "create diskInfo=" + mDisks.get(id)); isDiskInsert = true; break; ....... } ...... }
在MountService中采用 1个主线程(system_server) + 3个子线程(VoldConnector, MountService, CryptdConnector);MountService线程不断向vold下发存储相关的命令,比如mount, mkdirs等操作;而线程VoldConnector一直处于阻塞接收vold发送过来的应答事件并进行处理。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。
- Android vold进程三 MountService介绍
- android(ics)vold,mountservice
- 深入解析MountService、vold(三)--fuse_sdcard1(and5.1)
- vold/mountservice框架
- Android的存储系统—Vold与MountService分析
- Android6.0 MountService和vold详解(三) vold SD卡、otg
- android守护进程vold
- Android7.0 Vold 进程工作机制分析之由MountService发起挂载请求
- 深入分析vold、MountService(and5.1)
- Android vold进程一 Vold启动和NetlinkManager数据获取
- Android下Vold守护进程分析
- Android之vold进程启动源码分析
- Android之vold进程启动源码分析
- Android之vold进程启动源码分析
- Android之vold进程启动源码分析
- android vold:图解VOLD
- 深入解析vold、MountService(二)(and5.1)
- 深入Mountservice vold(六) handleDiskRemoved handlePartitionRemoved(and5.1)
- linux杀掉带有特定进程的脚本
- VS2013连接mysql进行C++编程
- AndroidStudio Bug收集-Manifest merger failed with multiple errors
- Unity3D 学习从简单开始-GUI探索(一)
- 安装IDEA和运行SCALA程序
- Android vold进程三 MountService介绍
- ionic2 tabs 自定义图标
- img标签设置本地路径
- ArcGIS Catalog添加ArcGIS Server服务器出现GXAGSUI 参数错误的解决方法
- GetLongPathName和GetShortPathName
- Idea开发环境中搭建Maven并且使用Maven打包部署程序
- Spring中引入其它配置文件
- orcle 分区表创建、查询、知识总结
- 初识项目管理