Android SDCard UnMounted 流程分析

http://www.cnblogs.com/TerryBlog/archive/2012/03/22/2411628.html

Android SDCard框架 

Android SDCard框架,我们修改一般涉及到四大模块

  1. Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及
  2. Vold 作为Kernel 与 Framework 之间的桥梁
  3. Framework 操作Vold ,给Vold 下发操作命令
  4. UI 与Framework 交互,用于挂载/卸载SD卡

框架涉及的源码位置

Vold :System/vold

        为vold 提供接口:System/Netd

        其他涉及的部分:System/core/libsysutils/src

                               System/core/include/sysutils

 

Framework:frameworks/base/services/java/com/android/server 

                 访问和提供接口类:framework/base/core/java/android/os/storage/ 

                 可能还要参考的库:framework/base/libs/storage

                                          framework/base/native

UI:Settings/src/com/android/setting/deviceinfo

 

SDCard UnMounted流程分析

初始化 

VolumeManager, CommandListener, NetlinkManager 都是在 main()函数里面初始化的。

其中 VolumeManager,NetlinkManager 内部采用单例模式。
(1) Class NetlinkManager 主要是创建于内核通信的 socket,接收来自底层的信息,然后传交给VolumeManager 处理。
(2) class CommandListener 主要收到上层 MountService 通过 doMountVolume 发来的命令,分析后,转交给 VolumeManager 处理;VolumeManager 处理信息后,或报告给上层 MountService,
或交给 volume 执行具体操作。CommandListener在main()初始化后,之后开始监听,会开一个线程不停的监听来自内核的消息。

深入main文件

 在Vold 的main.cpp里面,启动一个线程用来监听kernel 发出unMounted 的uevent事件,代码:

复制代码
 NetlinkManager *nm;
// NetlinkManager内部使用的单例模式
  if (!(nm = NetlinkManager::Instance())) {
        SLOGE( " Unable to create NetlinkManager ");
        exit( 1);
    };

// 开始监听,从服务启动就一直监听
if ( nm->start()) {
        SLOGE( " Unable to start NetlinkManager (%s) ", strerror(errno));
        exit( 1);
    } 
复制代码

 

 NetlinkManager的start 函数是实例化了一个NetlinkHandler(继承关系:NetlinkHandler->NetlinkListener->SocketLinstener),并调用handler 的start方法,如下代码:

  mHandler =  new NetlinkHandler(mSock);
     if ( mHandler->start()) {
        SLOGE( " Unable to start NetlinkHandler: %s ", strerror(errno));
         return - 1;
    } 

 

 深入NetlinkHandler 的start函数,见代码:

int NetlinkHandler::start() {
     return  this-> startListener();

 

 上面有说过NetlinkHandler其实是SocketLinstener的子类,NetlinkHandler直接调用父类的startListener 方法,startListener开启了一个线程用来执行threadStart函数,代码太多,贴出主心代码:

  if (pthread_create(&mThread, NULL,  SocketListener::threadStartthis)) {
        SLOGE( " pthread_create (%s) ", strerror(errno));
         return - 1;
    } 

 

而threadStart函数则调用了runListener方法,代码如下:

void *SocketListener::threadStart( void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

     me->runListener();
    pthread_exit(NULL);
     return NULL;

 runListener会判断socket 有无信息可读,不会阻滞UI,最后调用onDataAvailable函数,代码:

复制代码
void SocketListener::runListener() {

 SocketClientCollection *pendingList =  new SocketClientCollection();

      //代码有所省略
  while (!pendingList->empty()) {
            /* Pop the first item from the list */
            it = pendingList->begin();
            SocketClient* c = *it;
            pendingList->erase(it);
            /* Process it, if false is returned and our sockets are
             * connection-based, remove and destroy it */
            if (! onDataAvailable(c) && mListen) {
                /* Remove the client from our array */
                pthread_mutex_lock(&mClientsLock);
                for (it = mClients->begin(); it != mClients->end(); ++it) {
                    if (*it == c) {
                        mClients->erase(it);
                        break;
                    }
                }
                pthread_mutex_unlock(&mClientsLock);
                /* Remove our reference to the client */
                c->decRef();
            }
        }

}
复制代码

 

 onDataAvailable会处理来自uEvent 的命令,并最终调用onEvent函数,onDataAvailable 位于System/core/libsysutils/src/NetlinkListener.cpp 这个主要处理一些socket方法的知识,一般不用修改。

最后由Netlinklinstener 来解析 ,代码:

复制代码
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
     int socket = cli->getSocket();
    ssize_t count;

    count = TEMP_FAILURE_RETRY( uevent_kernel_multicast_recv(socket, mBuffer,  sizeof(mBuffer)));
     if (count <  0) {
        SLOGE( " recvmsg failed (%s) ", strerror(errno));
         return  false;
    }

    NetlinkEvent *evt =  new NetlinkEvent();
     if (!evt->decode(mBuffer, count, mFormat)) {
        SLOGE( " Error decoding NetlinkEvent ");
    }  else {
         onEvent(evt);
    }

    delete evt;
     return  true;
复制代码

 

小结

NetlinkManager其实就是用来处理uEvent 命令,并最终发送到vold/NetlinkHandler 的onEvent 。

上一篇讲到通过NetlinkManager发送uevent 命令到NetlinkHandler 的onEvent,代码如下:

复制代码
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
     VolumeManager *vm = VolumeManager::Instance();
     const  char *subsys = evt->getSubsystem();

     if (!subsys) {
        SLOGW( " No subsystem found in netlink event ");
         return;
    }
    SLOGD( " NetlinkHandler:OnEvent subsys values is %s ",subsys);
     if (!strcmp(subsys,  " block ")) {
        SLOGD( " NetlinkHandler:onEvent ");
         vm->handleBlockEvent(evt);
    }
复制代码

 在NetlinkHandler 里面得一个VolumeManager,当收到的命令为block时调用VolumnManager的handleBlockEvent,如上加红加粗的代码。

handleBlockEvent实则是通过一个循环将事先将main事先读取的配置文件:etc/vold.fstab存进VolumeCollection,得到VolumeCollection的对象,然后调用Volume 的handleBlockEvent,如代码:

复制代码
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
     const  char *devpath = evt->findParam( " DEVPATH ");

     /*  Lookup a volume to handle this device  */
    VolumeCollection::iterator it;
     bool hit =  false;
     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
         if (! (*it)->handleBlockEvent(evt)) {
#ifdef NETLINK_DEBUG
            SLOGD( " Device '%s' event handled by volume %s\n ", devpath, (*it)->getLabel());
#endif
            hit =  true;
             break;
        }
    }

     if (!hit) {
#ifdef NETLINK_DEBUG
        SLOGW( " No volumes handled block event for '%s' ", devpath);
#endif
    }
复制代码

看一下Volume 的handleblockEvent代码:

int Volume::handleBlockEvent(NetlinkEvent *evt) {
    errno = ENOSYS;
     return - 1;

 看起来好像没做什么事,其实真的实现在于Volume 的子类,DirectVolume,DirectVolme 中重写了handleBlockEvent,看代码:

复制代码
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
     const  char *dp = evt->findParam( " DEVPATH ");

    PathCollection::iterator  it;
     for (it = mPaths->begin(); it != mPaths->end(); ++it) {
         if (!strncmp(dp, *it, strlen(*it))) {
             /*  We can handle this disk  */
             int action = evt->getAction();
             const  char *devtype = evt->findParam( " DEVTYPE ");

             if (action == NetlinkEvent::NlActionAdd) {
                 int major = atoi(evt->findParam( " MAJOR "));
                 int minor = atoi(evt->findParam( " MINOR "));
                 char nodepath[ 255];

                snprintf(nodepath,
                          sizeof(nodepath),  " /dev/block/vold/%d:%d ",
                         major, minor);
                 if (createDeviceNode(nodepath, major, minor)) {
                    SLOGE( " Error making device node '%s' (%s) ", nodepath,
                                                               strerror(errno));
                }
                 if (!strcmp(devtype,  " disk ")) {
                    handleDiskAdded(dp, evt);
                }  else {
                    handlePartitionAdded(dp, evt);
                }
            }  else  if (action == NetlinkEvent::NlActionRemove) {
                 if (!strcmp(devtype,  " disk ")) {
                     handleDiskRemoved(dp, evt);
                }  else {
                    SLOGD( " DirectVolume:handleBlockEvent--->handlePartitionRemoved ");
                     handlePartitionRemoved(dp, evt);
                }
            }  else  if (action == NetlinkEvent::NlActionChange) {
                 if (!strcmp(devtype,  " disk ")) {
                    handleDiskChanged(dp, evt);
                }  else {
                    handlePartitionChanged(dp, evt);
                }
            }  else {
                    SLOGW( " Ignoring non add/remove/change event ");
            }

             return  0;
        }
    }
    errno = ENODEV;
     return - 1;
复制代码

 

因为我的板子还未完善,所以这里它认为我的sdcard是一个分区,但无关紧要,原理一样,就根据分区的代码跟踪。:handlePartitionRemoved,由于代码过多,只贴出核心代码:

复制代码
void DirectVolume::handlePartitionRemoved( const  char *devpath, NetlinkEvent *evt) {

  if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
         /*
         * Yikes, our mounted partition is going away!
         
*/

        snprintf(msg,  sizeof(msg),  " Volume %s %s bad removal (%d:%d) ",
                 getLabel(), getMountpoint(), major, minor);
        SLOGD( " DirectVolume:(dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev:%d,msg is :%s. ",mCurrentlyMountedKdev,msg);
         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
                                             msg, false);


     if (mVm->cleanupAsec( thistrue)) {
            SLOGE( " Failed to cleanup ASEC - unmount will probably fail! ");
        }

         if ( Volume::unmountVol(truefalse)) {
            SLOGE( " Failed to unmount volume on bad removal (%s) "
                 strerror(errno));
             //  XXX: At this point we're screwed for now
        }  else {
            SLOGD( " Crisis averted ");
        }
    }
复制代码

 

 到此,直接调用父类的unmountVol方法,unmountVol会通过setState通知框架状态改变。代码太多,只推荐核心代码:

int Volume::unmountVol( bool force,  bool revert) {
 setState(Volume::State_Unmounting);

 

 而setState会通过socket将msg消息传给框架

mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
                                         msg,  false); 

接下去的步骤是关于socket的操作,就不深入了。

小结

   到了这一步,Vold 向上层反馈的动作基本己经完成,下一篇文章将会讲解Framework 如何取得Vold 反馈过来的数据。 

前一篇讲到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 <  0break;

                 //  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, eventevent.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 进行判断,然后再下发执行命令。 

 

粗糙图

最后附一张比较粗糙的结构图,时间较急,没仔细画好

 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值