前面四篇博文:《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",newMountService(context));
找到MountService的构造函数:
- publicMountService(Contextcontext){
- mContext=context;
- mPms=(PackageManagerService)ServiceManager.getService("package");
- mContext.registerReceiver(mBroadcastReceiver,
- newIntentFilter(Intent.ACTION_BOOT_COMPLETED),null,null);
- mHandlerThread=newHandlerThread("MountService");
- mHandlerThread.start();
- mHandler=newMountServiceHandler(mHandlerThread.getLooper());
- mObbActionHandler=newObbActionHandler(mHandlerThread.getLooper());
- if("simulator".equals(SystemProperties.get("ro.product.device"))){
- mReady=true;
- mUmsEnabling=true;
- return;
- }
- <spanstyle="color:#000000;">mConnector=newNativeDaemonConnector(this,"vold",
- PackageManagerService.MAX_CONTAINERS*2,VOLD_TAG);
- mReady=false;
- </span><spanstyle="color:#ff0000;"><spanstyle="color:#000000;">Threadthread=newThread(mConnector,VOLD_TAG);</span><spanstyle="color:#000000;">
- 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卡的挂载等等。
- publicvoidrun(){
- while(true){
- try{
- <spanstyle="color:#ff0000;"><spanstyle="color:#000000;">listenToSocket();</span>
- </span>}catch(Exceptione){
- Slog.e(TAG,"ErrorinNativeDaemonConnector",e);
- SystemClock.sleep(5000);
- }
- }
- }
我们查看listenToSocket()中的代码,如下:
- privatevoidlistenToSocket()throwsIOException{
- LocalSocketsocket=null;<spanstyle="color:#000000;">
- try{
- socket=newLocalSocket();
- LocalSocketAddressaddress=newLocalSocketAddress(mSocket,
- LocalSocketAddress.Namespace.RESERVED);
- socket.connect(address);
- <spanstyle="color:#000000;">mCallbacks.onDaemonConnected();</span>
- InputStreaminputStream=socket.getInputStream();
- mOutputStream=socket.getOutputStream();<spanstyle="color:#000000;">
- byte[]buffer=newbyte[BUFFER_SIZE];
- intstart=0;
- while(true){
- intcount=inputStream.read(buffer,start,BUFFER_SIZE-start);<spanstyle="color:#000000;">
- if(count<0)break;
- count+=start;
- start=0;
- for(inti=0;i<count;i++){
- if(buffer[i]==0){
- Stringevent=newString(buffer,start,i-start);
- if(LOCAL_LOGD)Slog.d(TAG,String.format("RCV<-{%s}",event));
- String[]tokens=event.split("");
- try{
- intcode=Integer.parseInt(tokens[0]);
- if(code>=ResponseCode.UnsolicitedInformational){
- try{
- if(!mCallbacks.onEvent(code,event,tokens)){
- Slog.w(TAG,String.format(
- "Unhandledevent(%s)",event));
- }
- }catch(Exceptionex){
- Slog.e(TAG,String.format(
- "Errorhandling'%s'",event),ex);
- }
- }else{
- try{
- mResponseQueue.put(event);
- }catch(InterruptedExceptionex){
- Slog.e(TAG,"Failedtoputresponseontoqueue",ex);
- }
- }
- }catch(NumberFormatExceptionnfe){
- Slog.w(TAG,String.format("Badmsg(%s)",event));
- }
- start=i+1;
- }
- }
- if(start!=count){
- finalintremaining=BUFFER_SIZE-start;
- System.arraycopy(buffer,start,buffer,0,remaining);
- start=remaining;
- }else{
- start=0;
- }
- }
- }catch(IOExceptionex){
- Slog.e(TAG,"Communicationserror",ex);
- throwex;
- }finally{
- synchronized(this){
- if(mOutputStream!=null){
- try{
- mOutputStream.close();
- }catch(IOExceptione){
- Slog.w(TAG,"Failedclosingoutputstream",e);
- }
- mOutputStream=null;
- }
- }
- try{
- if(socket!=null){
- socket.close();
- }
- }catch(IOExceptionex){
- Slog.w(TAG,"Failedclosingsocket",ex);
- }
- }
- }
2.处理者
mCallbacks.onDaemonConnected();
//代码路径:AndroidSourcecode2.3/frameworks/base/services/java/com/android/server/MountService.java
//因为MountService实现了INativeDaemonConnectorCallbacks接口并覆写了其中的方法,因此这里会调用MountService中的onDaemonConnected()方法。该方法完成了对挂载消息的处理
- publicvoidonDaemonConnected(){
- newThread(){
- publicvoidrun(){
- Stringpath=Environment.getExternalStorageDirectory().getPath();
- Stringstate=Environment.MEDIA_REMOVED;
- try{
- <spanstyle="color:#ff0000;"><spanstyle="color:#000000;">String[]vols=mConnector.doListCommand(
- "volumelist",VoldResponseCode.VolumeListResult);</span>
- </span>for(Stringvolstr:vols){
- String[]tok=volstr.split("");
- if(!tok[1].equals(path)){
- Slog.w(TAG,String.format(
- "Skippingunknownvolume'%s'",tok[1]));
- continue;
- }
- intst=Integer.parseInt(tok[2]);
- if(st==VolumeState.NoMedia){
- state=Environment.MEDIA_REMOVED;
- }elseif(st==VolumeState.Idle){
- state=Environment.MEDIA_UNMOUNTED;
- }elseif(st==VolumeState.Mounted){
- state=Environment.MEDIA_MOUNTED;
- Slog.i(TAG,"Mediaalreadymountedondaemonconnection");
- }elseif(st==VolumeState.Shared){
- state=Environment.MEDIA_SHARED;
- Slog.i(TAG,"Mediasharedondaemonconnection");
- }else{
- thrownewException(String.format("Unexpectedstate%d",st));
- }
- }
- if(state!=null){
- if(DEBUG_EVENTS)Slog.i(TAG,"Updatingvalidstate"+state);
- updatePublicVolumeState(path,state);
- }
- }catch(Exceptione){
- Slog.e(TAG,"Errorprocessinginitialvolumestate",e);
- updatePublicVolumeState(path,Environment.MEDIA_REMOVED);
- }
- try{
- booleanavail=doGetShareMethodAvailable("ums");
- notifyShareAvailabilityChange("ums",avail);
- }catch(Exceptionex){
- Slog.w(TAG,"Failedtogetshareavailability");
- }
- mReady=true;
- }
- }.start();
- }
在该方法中首先执行:
- String[]vols=mConnector.doListCommand(
- "volumelist",VoldResponseCode.VolumeListResult);
继续跟踪doListCommand可以知道:
- publicString[]doListCommand(Stringcmd,intexpectedResponseCode)
- throwsNativeDaemonConnectorException{
- ArrayList<String>rsp=<spanstyle="color:#000000;">doCommand</span>(cmd);
- String[]rdata=newString[rsp.size()-1];
- intidx=0;
- for(inti=0;i<rsp.size();i++){
- Stringline=rsp.get(i);
- try{
- String[]tok=line.split("");
- intcode=Integer.parseInt(tok[0]);
- if(code==expectedResponseCode){
- rdata[idx++]=line.substring(tok[0].length()+1);
- }elseif(code==NativeDaemonConnector.ResponseCode.CommandOkay){
- if(LOCAL_LOGD)Slog.d(TAG,String.format("Listterminatedwith{%s}",line));
- intlast=rsp.size()-1;
- if(i!=last){
- Slog.w(TAG,String.format("Recv'd%dlinesafterendoflist{%s}",(last-i),cmd));
- for(intj=i;j<=last;j++){
- Slog.w(TAG,String.format("ExtraData<%s>",rsp.get(i)));
- }
- }
- returnrdata;
- }else{
- thrownewNativeDaemonConnectorException(
- String.format("Expectedlistresponse%d,butgot%d",
- expectedResponseCode,code));
- }
- }catch(NumberFormatExceptionnfe){
- thrownewNativeDaemonConnectorException(
- String.format("Errorreadingcode'%s'",line));
- }
- }
- thrownewNativeDaemonConnectorException("Gotanemptyresponse");
- }
继续跟踪doCommand:
- publicsynchronizedArrayList<String>doCommand(Stringcmd)
- throwsNativeDaemonConnectorException{
- mResponseQueue.clear();
- <spanstyle="color:#000000;">sendCommand</span>(cmd);
- ArrayList<String>response=newArrayList<String>();
- booleancomplete=false;
- intcode=-1;
- while(!complete){
- try{
- Stringline=mResponseQueue.take();
- if(LOCAL_LOGD)Slog.d(TAG,String.format("RSP<-{%s}",line));
- String[]tokens=line.split("");
- try{
- code=Integer.parseInt(tokens[0]);
- }catch(NumberFormatExceptionnfe){
- thrownewNativeDaemonConnectorException(
- String.format("Invalidresponsefromdaemon(%s)",line));
- }
- if((code>=200)&&(code<600)){
- complete=true;
- }
- response.add(line);
- }catch(InterruptedExceptionex){
- Slog.e(TAG,"Failedtoprocessresponse",ex);
- }
- }
- if(code>=ResponseCode.FailedRangeStart&&
- code<=ResponseCode.FailedRangeEnd){
- thrownewNativeDaemonConnectorException(
- code,cmd,response.get(response.size()-1).substring(4));
- }
- returnresponse;
返回doListCommand方法中,大致信息是从ArrayList中取出之前存入的符合要求的event,然后对这些event进行拆分,并截取其中的前部分存放在rdata这个字符串数组中返回。
这里继续返回到onDaemonConnected()新开的线程中,接着往下走,对返回的这个字符串数组再次进行分拆并分析,从代码中可以知道,这些字符串中存储了SD卡的挂载路径以及目前的状态信息。因为我们从之前的分析中可以知道,我们的SD卡已经挂载成功了,因此这里的状态是state = Environment.MEDIA_MOUNTED然后执行updatePublicVolumeState(path, state);方法。
3.中转站
private void updatePublicVolumeState(String path, String state)
//代码路径:AndroidSourcecode2.3/frameworks/base/services/java/com/android/server/MountService.java
- privatevoidupdatePublicVolumeState(Stringpath,Stringstate){
- if(!path.equals(Environment.getExternalStorageDirectory().getPath())){
- Slog.w(TAG,"Multiplevolumesnotcurrentlysupported");
- return;
- }
- if(mLegacyState.equals(state)){
- Slog.w(TAG,String.format("Duplicatestatetransition(%s->%s)",mLegacyState,state));
- return;
- }
- if(Environment.MEDIA_UNMOUNTED.equals(state)){
- mPms.updateExternalMediaStatus(false,false);
- mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
- path));
- }<spanstyle="color:#ff0000;"><spanstyle="color:#000000;">elseif(Environment.MEDIA_MOUNTED.equals(state)){
- mPms.updateExternalMediaStatus(true,false);</span>
- </span>}
- StringoldState=mLegacyState;
- mLegacyState=state;
- synchronized(mListeners){
- for(inti=mListeners.size()-1;i>=0;i--){
- MountServiceBinderListenerbl=mListeners.get(i);
- try{
- bl.mListener.onStorageStateChanged(path,oldState,state);
- }catch(RemoteExceptionrex){
- Slog.e(TAG,"Listenerdead");
- mListeners.remove(i);
- }catch(Exceptionex){
- Slog.e(TAG,"Listenerfailed",ex);
- }
- }
- }
- }
这里首先是执行
- mPms.updateExternalMediaStatus(true,false);
该方法位于PackageManagerService.java中,作用是告诉PackageManager外置media可用。在updateExternalMediaStatus方法中,通知PackageManagerService去更新外置media的状态,这包括了读取SD卡中的内容并识别。通过这个步骤以后,我们打开SD卡才能发现哪些东西是系统已经识别的,哪些东西系统不能识别。
接下来我们看看
- synchronized(mListeners){
- for(inti=mListeners.size()-1;i>=0;i--){
- MountServiceBinderListenerbl=mListeners.get(i);
- try{
- bl.mListener.onStorageStateChanged(path,oldState,state);
- }catch(RemoteExceptionrex){
- Slog.e(TAG,"Listenerdead");
- mListeners.remove(i);
- }catch(Exceptionex){
- Slog.e(TAG,"Listenerfailed",ex);
- }
- }
- }
这是一个同步块,最重要的一句代码是
- bl.mListener.onStorageStateChanged(path,oldState,state);
分析到这里,如果不去了解StorageManager和MountService关系的话,后面是没有办法分析下去的。我们的目的是从底层SD卡的挂载信息如何传递到上层的"设置-存储-SD卡"这个界面中,从而理清一条从底向上的线路。如果对于这一块也有疑问的朋友,希望能够真的去看看源码,并逐步自己一步一步的跟踪看看,有的时候真的有的复杂,很多机制不懂更多的是没听过的机制,但是只要自己想要弄清楚,那么就坚持下去吧。一开始我插入SD卡系统居然有时候识别不到,我就跟踪上层的源码,结果发现解决不了问题,那么就跟踪下去吧,不会的就一边查资料一边问别人,同时一边做记录,这些记录一方面可以帮助自己整理学习的资料,另一方面可以帮助也遇到同样问题的朋友。所以在此写下这些自己的拙见,错误百出但初衷是单纯的。
说了这么多废话,下一篇文章将继续分析SD卡挂载消息是如何在"设置"中显示出来的。
分享到:
相关推荐
Android23SD卡挂载流程浅析.docx
Android23SD卡挂载流程浅析.doc
本文档基于android4.2平台,sd卡热插拔在android层是如何传递消息
主要介绍了Android2.3实现SD卡与U盘自动挂载的方法,较为详细的分析了Android2.3实现SD卡与U盘自动挂载的具体步骤与相关技巧,需要的朋友可以参考下
但是每款定制过的android 系统的外置SD卡的路径都不一样,那我们怎么才能去获取这个路径呢,我们可以想其它的办法,我这里提供了一个类可以获取外置SD卡或内置SD卡的 label(名称),path(路径),mount_point(挂载点)...
本文实例讲述了Android判断SD卡是否已经挂载的方法。分享给大家供大家参考。具体如下: 提供一个监听方法BroadcastReceiver 设置IntentFilter为: Intent.ACTION_MEDIA_MOUNTED Intent.ACTION_MEDIA_EJECT Intent....
AndroidStudio编写
SD卡任意挂载工具
SD卡挂载高歌好用
android 内部存储 sd卡app私有文件 等
android读取sd卡中MP3文件
在ubuntu系统中1、 查看sd卡名字,2、挂在sd卡,3、 查看sd卡内容,
这是一个修改自内核的SD driver 模块 :linux3.4.2 SD/MMC/TF卡挂载问题的解决
android 读取和存储sd卡一个例子,欢迎下载!
Android手机SD卡文件浏览器:遍历出手机Sd卡中的文件。
android 读取外置和内置存储卡路径和大小,亲测好使,项目中以运用
在Android N上并没有提供直接的方法获取外置SD卡或挂载U盘路径,可以通过下面方法获取内置sd卡路径 Environment.getExternalStorageDirectory().getAbsolutePath(); 通过查看getExternalStorageDirectory源码发现,...