您的位置:首页 > 移动开发 > Android开发

Android SDCard Mount流程分析(一)(二)(三)

2012-12-04 10:01 507 查看
Android SDCard Mount流程分析(一)前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误。今天重新梳理了头绪,针对mount的流程再重新分析一次。

 本篇大纲

 

• android 系统如何开机启动监听mount服务

• 默认挂载点在Android 系统的哪个目录

• vold.fstab 配置文件的分析 

• vold 里面启动页面main做了些什么

android 系统如何开机启动监听mount服务

android sdcard 热插拔监测和执行操作是由一个启动文件vold  所统领的,系统开机会读取初始化配置文件init.rc,该文件位于比如我的板子是:device/ti/omap3evm/init.rc,具体根据自己平台查找。里面有一个是默认启动vold 服务的代码,如下:

service vold /system/bin/vold

    socket vold stream 0660 root mount

    ioprio be 2 

 如果要对该文件做出修改之类,要重新编一下boot.img 镜像文件,烧录进android 系统,之后可以在android的文件系统根目录找到init.rc文件。上述代码为启动vold 启动文件,也可以在init.rc 增加多一些我们想要的文件目录,比如增加一个可以存放多分区挂载的目录等,这个是后话。

 

 默认挂载点在Android 系统的哪个目录

 usbdisk 或者 sdcard 热插拔的时候,kernel 会发出命令执行mount或者unmount 操作,但这都是驱动级的。而mount 目录会在android 的文件系统目录下:/dev/block/vold 这个目录由vold 生成,用来存放所有的usbdisk 或者 sdcard 的挂载点。代码位于main里面最优先执行:

 

mkdir("/dev/block/vold", 0755);

 

 可以根据这个目录找到如下节点:

sh-4.1# ls /dev/block/vold/

179:0  179:1  8:0    8:1    8:2    8:3    8:4 

节点的小介绍:

0代表当前的整个设备,1代码当前设备的分区名称代号。

所以你会发现,sdcard只有一个分区它却生成了两个如:179:0 179:1

而usbdisk 有四个分区,它会生成五个挂载点: 8:0    8:1    8:2    8:3    8:4  就是这个原因。

 

 

 vold.fstab 配置文件的分析

vold 里面会通过指定文件来读取预先配置好的sdcard或者多分区配置文件,该文件位于

/system/core/rootdir/etc/vold.fstab

如以下的配置文件为:

dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1

 

 dev_mount 代表挂载格式

 sdcard 代表挂载的标签

/mnt/sdcard 代表挂载点

 auto 为自定义选项可以为任何,但必须在main 里面自己判断比如这里的意思为自动挂载

后面两个目录为设备路径,第一个如果被占用会选择第二个

 

配置文件可以根据自己的需要编写,并不是固定的,但最好遵循google vold 启动文件代码的格式编写,要不然会给我们修改代码或者增加多分区功能带来不小的麻烦,如以下我自己编写的多分区挂载支持vold.fstab 配置文件:

 

 dev_mount sdcard external /mnt/sdcard auto /devices/platform/mmci-omap-hs.0/mmc_host/mmc0 /devices/platform/mmci-omap-hs.0/mmc_host/mmc1

dev_mount usb1 external /mnt/usbdisk/usb1-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.1/

dev_mount usb2 external /mnt/usbdisk/usb2-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.2/

dev_mount usb3 external /mnt/usbdisk/usb3-disk%d all /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/

 该文件修改后经系统编译会在android 系统目录里/system/etc/vold.fstab找到。

 /devices/platform/ehci-omap.0/usb1/1-2/1-2.1/  代表要挂载的USB口。

vold.fstab 只是一个单纯的配置文件,具体的读取和取数据还 是要靠main里面的process_config函数。看代码,里面正有一段用来读取配置文件:

  if (!(fp = fopen("/etc/vold.fstab", "r"))) {

        return -1;

    }

在这个函数里面会根据读取到的数据存放起来,然后满足条件时执行操作。比如代码里面的:

 

if (!strcmp(type, "dev_mount")) {

            DirectVolume *dv = NULL;

            char *part;

            if (!(part = strtok_r(NULL, delim, &save_ptr))) {

                SLOGE("Error parsing partition");

                goto out_syntax;

            }

            if (strcmp(part, "auto") && atoi(part) == 0) {

                SLOGE("Partition must either be 'auto' or 1 based index instead of '%s'", part);

                goto out_syntax;

            }

            if (!strcmp(part, "auto")) {

                dv = new DirectVolume(vm, label, mount_point, -1);

            } else {

                dv = new DirectVolume(vm, label, mount_point, atoi(part));

            }

            while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {

                if (*sysfs_path != '/') {

                    /* If the first character is not a '/', it must be flags */

                    break;

                }

                if (dv->addPath(sysfs_path)) {

                    SLOGE("Failed to add devpath %s to volume %s", sysfs_path,

                         label);

                    goto out_fail;

                }

            }

            /* If sysfs_path is non-null at this point, then it contains

             * the optional flags for this volume

             */

            if (sysfs_path)

                flags = parse_mount_flags(sysfs_path);

            else

                flags = 0;

            dv->setFlags(flags);

            vm->addVolume(dv);

        }

 

DirectVolume后面会讲到,执行mount 和unmount 都是它在做。

另外,有时后读取配置文件会有问题,这是因为它读取是通过指标下标递增的方式在读,如果有问题可以跟踪打印一下配置文件,看哪里需要修改。

 

 vold 里面启动页面main做了些什么

main 主要是初始化socket 连接监听数据变化,在系统起来时第一时间启动,并且通过读取配置文件来识别usb口或者sdcard 的设备地址,来mount 或者unmount 。其它执行mount 、 unmount  或者删除节点等操作都是由上层或者framework 发送命令给main让其通知volumeManage 执行相应的操作。

Android SDCard Mount 流程分析(二)

 


Mount流程分为两个部分

 
主动挂载(插入SDCARD或者USB硬盘时系统自动挂载)
手动挂载(卸载SDCARD或者USB硬盘后,再点击加载设备的手动挂载) 
不同挂载走的流程并不相同,比如手动挂载是由上层发命令给vold 执行挂动作,而主动挂载是由kernel 分命令给vold 再由vold 发挂载消息给上层,上层得到挂载消息和状态后再发命令给vold 执行挂载。主动挂载较之复杂些。不过虽然流程不一样,但最终还是要调用Volume的挂载函数,下面将详细介绍两者的行走的流程。
 
由于会涉及SDCARD或者USB硬盘,其中调用的方法就不详细说明,这里只说出当插入SDCARD或者USB硬盘会走的流程。
 


 主动挂载

 
主动挂载时,会走向DirectVolume类,调用DirectVolume::mountVol方法,代码如下:




int DirectVolume::mountVol() {

    char errmsg[255];

    dev_t deviceNodes[64];

      

    int i, n = 0;

    

    if (getState() == Volume::State_NoMedia) {

        snprintf(errmsg, sizeof(errmsg),

                 "Volume %s %s mount failed - no media",

                 getLabel(), getMountpoint());

        mVm->getBroadcaster()->sendBroadcast(

                                         ResponseCode::VolumeMountFailedNoMedia,

                                         errmsg, false);

        errno = ENODEV;

        return -1;

    } else if (getState() != Volume::State_Idle) {

        errno = EBUSY;

        return -1;

    }

    

    n = getDeviceNodes((dev_t *) &deviceNodes, 64);

     

    if (!n) {

        SLOGE("Failed to get device nodes (%s)\n", strerror(errno));

        return -1;

    }

    bool mounted = false;

    

    for (i = 0; i < n; i++) {

        mDevNodeIndex = deviceNodes[i];

        //XXX: hack mountpoint
        if ( mMountpointParsed ) { free(mMountpointParsed); mMountpointParsed = NULL; }

        mMountpointParsed = getParsedMountPoint(mMountpoint, i);

        

        if (isMountpointMounted(getMountpoint())) {

            SLOGW("Volume is idle but appears to be mounted - fixing");

            setState(Volume::State_Mounted);

            // mCurrentlyMountedKdev = XXX
            errno = EBUSY;

            continue;

        }

    

        if (!Volume::mountVol()) {

            mounted = true;

        }


        

        mState = Volume::State_Idle;

    }

    

    if ( mMountpointParsed ) { free(mMountpointParsed); mMountpointParsed = NULL; }

    

    if ( mounted ) {

        // at least on partition has been mounted successful, mark disk as mounted
        setState(Volume::State_Mounted);

        return 0;

    }

    

    SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());

    setState(Volume::State_Idle);

    return -1;

}



 
 代码加亮部分,蓝色部分,会循环整个设备节点系统目录位于(/dev/block/vold),然后调用红色部分代码,调用Volume的挂载方法。
这里,无论是SDCARD或者USB硬盘在主动挂载时,都会走DirectVolume。
 


 手动挂载

手动挂载是由上层发Mount 命令,代码位于MountService里面的doMountVolume方法,具体如何实现我们先不深究,它这里通过发送socket(mount)命令到Vold 的CommandListener里面的CommandListener::VolumeCmd::runCommand方法进入代码这里:



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

        if (argc != 3) {

            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);

            return 0;

        }

        

        if(!strcmp(argv[2],"firstMount")){

            VolumeCollection::iterator i;

              if(mVolumes!=NULL){

              for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {

              if (strcmp("/sdcard", (*i)->getMountpoint())) {

                  vm->mountVolume((*i)->getMountpoint());

               }

            }

         }

        }else{

           vm->mountVolume(argv[2]);

        }


            

    } 



 
 这里执行挂载动作,看上面蓝色代码是为了系统第一次启动上层发送命令firstMount给CommandListener执行挂载USB硬盘的动作,红色代码即是核心要挂载的方法,调用的VolumeManage的mountVolume 方法,只需传入挂载点。该方法代码是:



int VolumeManager::mountVolume(const char *label) {

    Volume *v = lookupVolume(label);

    if (!v) {

        errno = ENOENT;

        return -1;

    }

    return v->mountVol();

}



 
 可以看出,这里同样调用的是Volume的mountVol方法,殊途同归,接下来着重看一下Volume类里面这个mountVol方法,究竟干了些啥。


Volume::mountVol 方法深究

 别的先不管,来看一下代码
 



 int Volume::mountVol() {
    int rc = 0;

    char errmsg[255];

    const char *mountPath;

        char devicePath[255];

        

        sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(mDevNodeIndex),

                MINOR(mDevNodeIndex));//得到设备节点,如:/dev/block/vold/8:1

     

        SLOGI("%s being considered for volume %s ...major : %d minor: %d\n", devicePath, getLabel(),

         MAJOR(mDevNodeIndex),MINOR(mDevNodeIndex));

    

        errno = 0;

        setState(Volume::State_Checking);//设置状态为checking整型为3

    

        // TODO: find a way to read the filesystem ID
        bool isFatFs = true;

        bool isNtfsFS = true;

         //检查设备格式是否为Fat32

        if (Fat::check(devicePath)) {

            if (errno == ENODATA) {

                SLOGW("%s does not contain a FAT filesystem\n", devicePath);

                isFatFs = false;

            } else {

              errno = EIO;

              /* Badness - abort the mount */

              SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));

              setState(Volume::State_Idle);

              return -1;

            }

        }

        //创建挂载目录

       // create mountpoint
        if (mkdir(getMountpoint(), 0755)) {

            if (errno != EEXIST) {

                SLOGE("Failed to create mountpoint %s (%s)", getMountpoint(), strerror(errno));

                return -1;

            }

        }

    

        /*

         * Mount the device on our internal staging mountpoint so we can

         * muck with it before exposing it to non priviledged users.

         */

        errno = 0;

        //如果为sdcard则挂载到/mnt/secure/staging,否则挂载到挂载点

         if(!strcmp(getLabel(),"sdcard"))

            mountPath="/mnt/secure/staging";

        else

            mountPath=getMountpoint();

         //接下来就是不同格式不同的挂载,这里支持两种格式:fat32,Ntfs

        if ( isFatFs ) {

            if (Fat::doMount(devicePath,mountPath, false, false, 1000, 1015, 0702, true)) {

                SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));

                

                isFatFs = false;

            }

            isNtfsFS = false;

        }

        

        if ( isNtfsFS ) {

            if (Ntfs::doMount(devicePath, mountPath, true)) {

                SLOGE("%s failed to mount via NTFS (%s)\n", devicePath, strerror(errno));

                isNtfsFS = false;

            }

        }

    

        if ( !isFatFs && !isNtfsFS ) {

            // unsupported filesystem
            return -1;

        }

        

        SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());

        

        

        if ( !strcmp(getLabel(), "sdcard") ) {

            

            protectFromAutorunStupidity();

    

            if (createBindMounts()) {

                SLOGE("Failed to create bindmounts (%s)", strerror(errno));

                umount("/mnt/secure/staging");

                setState(Volume::State_Idle);

                return -1;

            }

        }

    

        /*

         * Now that the bindmount trickery is done, atomically move the

         * whole subtree to expose it to non priviledged users.

         * 如果为sdcard则将/mnt/secure/staging 目录移动到挂载点,并将该目录unmount

         */

        if(!strcmp(getLabel(),"sdcard")){

          if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {

              SLOGE("Failed to move mount (%s)", strerror(errno));

              umount("/mnt/secure/staging");

              setState(Volume::State_Idle);

               return -1;

          }

       }

        setState(Volume::State_Mounted);//设置状态到MountService

        mCurrentlyMountedKdev = mDevNodeIndex;

                

        return 0;

    

}



注意:原生的代码可能跟上面贴出来的代码有点不同,上面的代码是增加了Ntfs-3g挂载的支持和多分区挂载的支持,但基本流程是相同的。

 代码有详细的注释,这里要注意的是:sdcard和USB的支持不同,sdcard 挂载时需要先挂载到临时目录/mnt/secure/staging,然后再移动到最终需要挂载的挂载点,而USB硬盘特别是多分区的支持,不用先挂载到临时目录,而是可以支持挂载到想要挂载的挂载点,这里是比较需要注意到的地方(在这里栽过跟头,会出现“随机性的挂载失败”)。
ok. 

 前一篇讲到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 进行判断,然后再下发执行命令。 

 


粗糙图

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

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: