前面四篇博文:《Android 2.3 SD卡挂载流程浅析(一)》、《Android
2.3 SD卡挂载流程浅析(二)》、《Android 2.3 SD卡挂载流程浅析(三)》、《Android
2.3 SD卡挂载流程浅析(四)》主要是对SD卡的挂载流程从底到上的一个分析,本文将继续接着《Android 2.3 SD卡挂载流程浅析(四)》文章分析,前文主要分析了C/C++的一些代码,本文将主要分析Java代码。废话不多说,依然上这张老图:
图中绿色箭头表示的就是SD卡挂载消息从底向上传递的一个流程。本文主要是分析红色箭头的传递了,因为现在消息要在上层反应出来,这里是从VolumeManager开始分析,我们把从Mount SD/USB到VolumeManager之间的流程总体当作Vold来讲,也就是Vold向上层反馈SD卡挂载的消息。
上文我们分析到,SD卡被doMount方法执行挂载了,该消息由setState方法将消息传递到上层,setState是通过发送一个广播,这里所说的广播不是Android中的BroadCast,这里实际山是Socket,上层负责监听这个Socket,并解析其中的内容。我们需要从MountService.java开始查找。这里我要解释以下为什么要从这里开始找,不是说一开始我就知道这个类里面有我们需要的东西,这是在查找SD卡挂载过程的时候,通过不同的线索联系起来的。那我们先来看看MountService吧。
MountService
位于AndroidSourcecode2.3/frameworks/base/services/java/com/android/server/MountService.java
MountService是一个服务类,通过ServiceManager注册为系统服务,对外部存储设备提供管理和查询等服务,在外部存储设备状态发生改变的时候发出相应的通知给注册了该服务的应用程序。MountService相当于一个中间桥梁,负责接收Vold的消息并传递给上层应用。这里就不详细阐述MountService是如何启动的了,对于Android服务这一块,我将另写后续的文章分析。
MountService在SystemServer.java(AndroidSourcecode2.3/frameworks/base/services/java/com/android/server/SystemServer.java)中启动,在启动的时候调用MountService的构造函数:
- ServiceManager.addService("mount", new MountService(context));
找到MountService的构造函数:
- public MountService(Context context) {
- mContext = context;
- // XXX: This will go away soon in favor of IMountServiceObserver
- mPms = (PackageManagerService) ServiceManager.getService("package");
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
- mHandlerThread = new HandlerThread("MountService");
- mHandlerThread.start();
- mHandler = new MountServiceHandler(mHandlerThread.getLooper());
- // Add OBB Action Handler to MountService thread.
- mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
- /*
- * Vold does not run in the simulator, so pretend the connector thread
- * ran and did its thing.
- */
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- mReady = true;
- mUmsEnabling = true;
- return;
- }
- /*
- * 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.
- */
- <span style="color:#000000;">mConnector = new NativeDaemonConnector(this, "vold",
- PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);//通过调用带参数的构造函数生成了一个Runnable对象
- mReady = false;
- </span><span style="color:#ff0000;"><span style="color:#000000;">Thread thread = new Thread(mConnector, VOLD_TAG);</span><span style="color:#000000;">//这里开启了一个新线程,传递了一个Runnable对象
- thread.start();</span>
- </span> }
这里我们重点关注最后两句,这两句的意思我相信有一点java基础的人都知道吧,对,没错,就是开启一个新线程,我继续跟踪这个传进来的Runnable对象mConnector,查看NativeDaemonConnector.java后可以知道,该类实现了Runnable接口,同时也覆写了Runnable中的run()方法,在该方法中有一个死循环,主要负责监听来自Vold的Socket消息,这是一个阻塞方法。
1.监听者
listenToSocket();
//代码路径:AndroidSourcecode2.3/frameworks/base/services/java/com/android/server/NativeDaemonConnector.java/run()方法中
//该方法负责监听来自Vold的Socket消息,这些消息包括SD卡的插入,SD的检测,SD卡的挂载等等。
- public void run() {
- while (true) {
- try {
- <span style="color:#ff0000;"><span style="color:#000000;">listenToSocket();</span>
- </span> } catch (Exception e) {
- Slog.e(TAG, "Error in NativeDaemonConnector", e);
- SystemClock.sleep(5000);
- }
- }
- }
我们查看listenToSocket()中的代码,如下:
- private void listenToSocket() throws IOException {
- LocalSocket socket = null;<span style="color:#000000;">//这些Socket就是用来与底层通信的,接收底层传递上来的关于SD卡挂载的信息</span>
- try {
- socket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress(mSocket,
- LocalSocketAddress.Namespace.RESERVED);
- socket.connect(address);
- <span style="color:#000000;">mCallbacks.onDaemonConnected();</span>//主要处理队列中的event方法,后文将接着这里分析。
- InputStream inputStream = socket.getInputStream();
- mOutputStream = socket.getOutputStream();<span style="color:#000000;">//同时也可以向底层发出控制命令</span>
- byte[] buffer = new byte[BUFFER_SIZE];
- int start = 0;
- while (true) {
- int count = inputStream.read(buffer, start, BUFFER_SIZE - start);<span style="color:#000000;">//通过inputStream.read来读取Socket中的信息</span>
- if (count < 0) break;
- // Add our starting point to the count and reset the start.
- count += start;
- start = 0;
- for (int i = 0; i < count; i++) {//对信息进行处理
- if (buffer[i] == 0) {
- String event = new String(buffer, start, i - start);
- if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
- String[] tokens = event.split(" ");