>>分享Android开发相关的技术 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 19679 个阅读者 刷新本主题
 * 贴子主题:  Android SDCard UnMounted 流程分析 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2020-03-31 19:59:48     消息  查看  搜索  好友  邮件  复制  引用

    

前篇地址

Android SDCard UnMounted 流程分析(一)

Android SDCard UnMounted 流程分析(二)

前一篇讲到SDCard unmout onEvent 发送socket 到框架层,接下来分析框架层得到数据后的流程。

           MoutService

         当android 系统启动时,system将MountService 添加到启动服务里面,而MountService 会开启一个线程来运行NativeDaemonConnector,由它来监听vold的消息,代码:

      mConnector =  new NativeDaemonConnector( this,  " vold ", MAX_CONTAINERS *  2, VOLD_TAG);

      mReady =  false;

      Thread thread =  new Thread(mConnector, VOLD_TAG);

       thread.start();

         该函数运行在MountService的构造函数里面,而NativeDaemonConnector 本身就是继承自Runnable。

         NativeDaemonConnector

          Framework与vold 的通信是通过socket来实现的,不过该socket 由 android做了一个封装,LocalSocket 实现的socket功能。

         NativeDaecomConnector 位于framework/base/service/java/com/android/server目录下, 监听vold 的消息代码在继承自Runnable对象的run方法里面 :

        @Override

         public  void run() {

            HandlerThread thread =  new HandlerThread(TAG +  " .CallbackHandler ");

            thread.start();

            mCallbackHandler =  new Handler(thread.getLooper(),  this);

            while ( true) {

                 try {

                     listenToSocket();

                }  catch (Exception e) {

                    Slog.e(TAG,  " Error in NativeDaemonConnector ", e);

                    SystemClock.sleep( 5000);

                }

            }

        }

         NativeDaemonConnector 类实例化了一个LocalSocket来与vold 通信。LocalSocket 里面有一个类LocalSocketImpl,该类部分是通过JNI实现的。

         关于socket 内部如何通信,这个不是我们所关心的内容,因为如果要深入进去估计没完没了,有兴趣的朋友可以参考源码进入SocketListener查看:

         建立连接

         SocketListener::SocketListener

         当main初始化CommandListener 后,会为socketName 传入一个叫vold 的字符串

         SocketListener::startListener

         等待并接收连接请求

         SocketListener::runListener

         获得命令参数

         bool FrameworkListener::onDataAvailable

         dispatchCommand 到相应的命令类,并返回一部分消息给上层

         FrameworkListener::dispatchCommand

                再回过头看NativeDaemonConnector 的listenToSocket,代码中实例化了一个LocalSocketAddress的实例,并传入一个叫"vold"字符串的socket 名称,这与CommandListener中继承了FrameworkListener时给的"vold"名称是一致的,两个socket名称一致则可以互相进行通讯了,代码如下:

      private  void listenToSocket() throws IOException {

            LocalSocket socket =  null;

        Slog.w(TAG,String.format( " NativeDaemonConnector--->listenToSocket:start "));

             try {

                socket =  new LocalSocket();

                LocalSocketAddress address =  new  LocalSocketAddress(mSocket,

                         LocalSocketAddress.Namespace.RESERVED);

                socket.connect(address);

                InputStream inputStream = socket.getInputStream();

                mOutputStream = socket.getOutputStream();

                mCallbacks.onDaemonConnected();

                 byte[] buffer =  new  byte[BUFFER_SIZE];

                 int start =  0;

                 while ( true) {

                     int count = inputStream.read(buffer, start, BUFFER_SIZE - start);

                     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);//解析socket 的数据并获取event

                             if (LOCAL_LOGD) Slog.d(TAG, String.format( " RCV <- {%s} ",  event));

                                        String[] tokens =  event.split( "   ",  2);

                             try {

                                 int code = Integer.parseInt(tokens[ 0]);

                                if (code >= ResponseCode.UnsolicitedInformational) {

                                    mCallbackHandler.sendMessage(

                                            mCallbackHandler.obtainMessage(code,  event));//发送消息给handler

                                }  else {

                                     try {

                                        mResponseQueue.put( event);

                                    }  catch (InterruptedException ex) {

                                        Slog.e(TAG,  " Failed to put response onto queue ", ex);

                                    }

                                }

                            }  catch (NumberFormatException nfe) {

                                Slog.w(TAG, String.format( " Bad msg (%s) ",  event));

                            }

                            start = i +  1;

                        }

                    }

                                 //  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) {

                Slog.e(TAG,  " Communications error ", ex);

                 throw ex;

            }  finally {

                synchronized (mDaemonLock) {

                     if (mOutputStream !=  null) {

                         try {

                            mOutputStream.close();

                        }  catch (IOException e) {

                            Slog.w(TAG,  " Failed closing output stream ", e);

                        }

                        mOutputStream =  null;

                    }

                }

                  try {

                     if (socket !=  null) {

                        socket.close();

                    }

                }  catch (IOException ex) {

                    Slog.w(TAG,  " Failed closing socket ", ex);

                }

            }

        }

     上面代码,通过socket 并event 解析出来,并通handler 发送到handleMessage 中,当handleMessage接收到传过来的消息时,会调用MountService 的onEvent 方法将code和event和sdcard 的状态传递进去。代码如下:

       public boolean handleMessage(Message msg) {

            String  event = (String) msg.obj;

            Slog.w(TAG,String.format( " NativeDaemonConnector--->handleMessage the event value is  "+ event));

             try {

                 if (! mCallbacks.onEvent(msg.what,  event,  event.split( "   "))) {

                    Slog.w(TAG, String.format(

                             " Unhandled event '%s' ",  event));

                }

            }  catch (Exception e) {

                Slog.e(TAG, String.format(

                         " Error handling '%s' ",  event), e);

            }

             return  true;

        }

       又回到MountService ,在onEvent里面当接收到的code ==VoldResponseCode.VolumeBadRemoval时会调用updatePublicVolumeState,
      发送unmount改变的广播,代码如下:

             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;}

              到这里,进入updatePublicVolumeState看该函数里的主要代码:

     synchronized (mListeners) {

                 for ( int i = mListeners.size() - 1; i >=  0; i--) {

                    MountServiceBinderListener bl = mListeners. get(i);

                     try {

                        Slog.w(TAG, " MountService--->updatePublicVolumeState-->bl.mListener.onStorageStateChanged ");

                         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);

                    }

                }

            }

    }

              并且调用sendStorageIntent 方法将SDCard的Action:android.intent.action.MEDIA_BAD_REMOVAL 和dat:file:///mnt/sdcard 通过这个广播发送出去,代码如下:

       private  void sendStorageIntent(String action, String path) {

            Intent intent =  new Intent(action, Uri.parse( " file:// " + path));

             //  add StorageVolume extra

            intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap. get(path));

            Slog.d(TAG,  " sendStorageIntent  " + intent);

            mContext.sendBroadcast(intent);

        }

     再回到 updatePublicVolumeState ,调用了stateChange 后,将状态为632的标识发送到NativeDaemonConnector 的handlemessage,当NativeDaemonConnector 发现SDCard的状态发送改变时,比如unmount 的时候,从632(VolumeBadRemoval)变到605(VolumeStateChange)到onEvent,当onEvent再次得到请求时,进入判断会直接执行notifyVolumeStateChange 函数,代码如下:

      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]));

            }

           notifyStateChange 会调用updatePublicVolumeState通知packageManger SDCard己经unmount.

         再回到Vold

         由于vold 启动文件一开始就启动了CommandListener的runcommand由于socket 一直在通讯,当发现值改变后,进入以下代码runCommand 方法里面:

      else  if (!strcmp(argv[ 1],  " unmount ")) {

             if (argc <  3 || argc >  4 ||

               ((argc ==  4 && strcmp(argv[ 3],  " force ")) &&

                (argc ==  4 && strcmp(argv[ 3],  " force_and_revert ")))) {

                cli->sendMsg(ResponseCode::CommandSyntaxError,  " Usage: volume unmount <path> [force|force_and_revert] ",  false);

                 return  0;

            }

            bool force =  false;

             bool revert =  false;

             if (argc >=  4 && !strcmp(argv[ 3],  " force ")) {

                force =  true;

            }  else  if (argc >=  4 && !strcmp(argv[ 3],  " force_and_revert ")) {

                force =  true;

                revert =  true;

            }

            rc = vm->unmountVolume(argv[ 2], force, revert);

        }  

              这时调用VolumeManage的unmoutVolume。该方法来源于Volume 的unmountVol,调用这个函数会unmount 三个挂载点,并同时调用setState通知框架unmount 成功,可以改变UI等一系列动作。    

最后总结

         MountService: 实现用于管理存储设备的后台服务

         StorageManage:访问MountService 接口,并向应用层提供接口

         PackageMangeService:是用于管理系统中所有apk,当SDCard发生变化时,向应用层发送消息

         NativeDaemonConnector:创建socket实现mountservice 和vold 的通信

         可以这么说:当vold 捕获到uevent 事件,会将事件消息通知framework,framework 进行判断,然后再下发执行命令。



----------------------------
原文链接:https://blog.51cto.com/terryblog/817015

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-04-08 09:46:32 重新编辑]
  Java面向对象编程-->内部类
  JavaWeb开发-->JavaWeb应用入门(Ⅱ)
  JSP与Hibernate开发-->第一个helloapp应用
  Java网络编程-->基于MVC和RMI的分布式应用
  精通Spring-->Vue指令
  Vue3开发-->Vue Router路由管理器
  android 自动化测试之MonkeyRunner学习
  Android自定义组件
  android 手势操作GestureDetector
  Android Fragments 详细使用
  Android中的几个布局
  Android Application Theme的实现及管理
  编译Irrlicht On Android
  Android代码混淆的实践
  Android网络开发-创建请求队列
  在Window中下载和安装Android源代码
  创建 和使用Android服务
  Android数据存储之Content Providers
  Android内存优化—dumpsys meminfo详解
  Android 代码混淆技术
  Android 启动页倒计时自定义view实现
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。