Android的USB系统之二 (Vold和mountservice分析)

1Vold功能分析

1.1Vold的主要作用

Android系统中为了统一管理磁盘而引入Vold 负责磁盘的挂载等。

vold是一个中间层,负责来连接上层以及驱动层。其实vold主要抓住两点:一是在CommandListener.cpp中处理上层发下来的命令,二是在NetlinkHandler.cpp中处理底层发送上来的各种信息。

vold与上层之间是通过DomainSocket来通讯的,与下层之间是通过NetLinkSocket和sysfs来实现通讯的,即通过NetLinkSocket来截取usb驱动和mmc发送上来的uevent事件。主要处理subsys= block的uevent,即block设备的添加、移除等。

有关磁盘管理部分其他文章分析。

《Android的USB系统之二 (Vold和mountservice分析)》

1.2Vold启动

Vold是在init进程中启动的,在init进程中将解析 init.rc 文件,在该文件中有启动Vold的配置。在这里将启动 Vold ,并且创建一个 socket。这socket 主要是为了与 framework 层通信。 如下面所示:

servicevold/system/bin/vold

class core

socket vold stream 0660 root mount

ioprio be 2

1.3Vold通信

由于它负责两方面的通信,所以他有一共有两条socket:

1)Domainsocket: 负责vold与framework层的信息传递;

Domainsocket是在init进程启动Vold时创建的,这是一个用于和framework层通信的 socket,在android系统中有个封装类Localsocket。

在framework层的MountService类中调用下面

mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), “vold“, 10,VOLD_TAG,25);

MountService通过NativeDaemonConnector和Vold中的CommandListener建立socket通信。其中 mSocket值为“vold”表示要获得 socket的名字,该 socket如上所述,是在vold被 init的时候创建的,并且保存为全局变量。

NavtiveDaemonConnector位于frameworks\base\services\java\com\android\server目录下,他会开启一个线程不停的监听来自vold的消息,所以这里有可能消息堵塞。NavtiveDaemonConnector   类封装实例化一个 LocalSocket 来与 vold 通信。LocalSocket 里面有一个类 LocalSocketImpl  ,该类部分函数时通过 JNI实现的。

2)Netlinksocket: 负责接受kernel的信息;

Netlinksocket是在NetLinkManager的start()中创建的。

1.4事件处理

这里通过对两个连结的监听,完成对动态事件的处理,以及对上层应用操作的响应。我们来具体分析一下代码过程。

1)kernel发出 uevent

NetLinkManager 检测 kernel 发出的 uevent,经过解析后,调用NetLinkHandler::onEvent()。如下所示:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

   VolumeManager *vm = VolumeManager::Instance();

   constchar*subsys = evt->getSubsystem();

   if(!subsys) {

           SLOGW(“No subsystem found innetlinkevent”);

           return;

   }

if(!strcmp(subsys,”block”)) {

     vm->handleBlockEvent(evt);

}elseif(!strcmp(subsys,”usb”)|| !strcmp(subsys,”scsi_device”)) {

     SLOGW(“subsystem found innetlinkevent”);

    MiscManager *mm = MiscManager::Instance(); 

   mm->handleEvent(evt);

}

}

对于不同的时间类型分别处理。“block”事件由 VolumeManager的 handleBlockEvent(evt)处理,最终调用的是DirectVolume的 handleBlockEvent(evt)。block主要指对 volume的一些操作的事件,如插入,拔掉外接存储设备。

vm->handleBlockEvent(evt)->DirectVolume::handleBlockEvent(NetlinkEvent *evt).

DirectVolume::handleBlockEvent这个方法就是具体负责拔插操作的方法。

“scsi_device””事件由MiscManager处理。现在一般是处理3G DONGLE热拔插设备。

2)framework层发出命令

和上面过程正好相反,CommandListener检测到framework层的命令,调用VolumeManager的函数,VolumeManager遍历Volume清单找出对应的volume, 调用volume的函数。 而volume类中的相关调用,最终调用到Linux库函数,完成相关操作。

代码位于CommandListener.cpp中:

if(!strcmp(argv[1],”list”)) {

   returnvm->listVolumes(cli);

}elseif(!strcmp(argv[1],”mount”)) {

   if(argc != 3) {

         cli->sendMsg(ResponseCode::CommandSyntaxError,”Usage: volume mount “,false);

       return 0;

    }

    rc = vm->mountVolume(argv[2]);

}elseif(!strcmp(argv[1],”unmount”))

……

1.5Uevent简介

Uevent 由内核发出,通过netlink sokect 来传递给 vold,在 kobject 被创建的时候,就会发生uevent的传递。 对于未传递的uevent, 会在kset下产生uevent文件, 这是供用户态触发uevent使用的,通过向uevent 档写入 action(add,remove 等) ,可以触发一个 uevent,这些 uevent

可以被vold 捕获,从而完成未完成的 vold 处理。在系统启动的时候,vold 未启动的时候,这些 uevent 写入了 uevent 檔,vold 启动后,会扫描 sys 目录查找 uevent,然后触发它们,来完成之前未完成的事宜。uevent文件的内容,就是uevent 事件的数据。

1.6APP2SD

1.6.1APP2SD简介

App2SD的功能是将apk安装在外部存储器上,而不是内部data分区,这样可以节省内部data分区。从Gingerbread之后就开始支持这个功能,具体的方法是在apk的AndroidManifest.xml中声明要安装的位置。

android:installLocation=”preferExternal”    … >

1.6.2APP2SD实现简单分析

app2sd的实现,需要vold里面asec相关操作支持才能完成,否则安装到外部存储器的apk在机器重启后就会消失。

PM在判断akp是安装在内部还是外部之后,会调用createAsec(…bool isExternal)来创建用来存放apk数据的一个Fat或者Ext4的文件系统。

具体调用流程如下:PackageHelper::createSdDir()—>mountService.createSecureContainer(cid, sizeMb, “fat”, sdEncKey, uid, isExternal);—CommandListener–>VolumeManager::createAsec(const char *id, …bool isExternal)

其中创建asec的流程请详细分析代码,其中主要思路是在/mnt/secure/asec/id.asec这路径下创建一个loop设备,然后把这个loop设备挂载到/mnt/asec/目录下。此时如果vold没有创建/mnt/secure/asec的话,这个目录就是在根文件系统中的一个临时文件,在重启后就会消失。具体看代码VolumeManager.cpp中有关asec和obb部分的实现。

1.6.3/mnt/secure/asec/的由来

在Volume.cpp中的mountVol()函数中,如果是flash挂载就按照如下流程处理:

1).把flash设备挂载在/mnt/secure/staging

2).创建/mnt/secure/staging/.android_secure文件夹

3).Bind mount /mnt/secure/staging/.android_secure -> /mnt/secure/asec //一个文件系统

4).Mount a read-only, zero-sized tmpfs  on /android_secure

5).把/mnt/secure/staging move到mountpoint

这样子看来其实/mnt/secure/asec/是flash中的一个文件夹.android_secure被挂载成了一个fat的文件系统,这样就保证在里面创建的文件不会丢失。

挂载成功后在mount命令中可以看到如下信息:

/dev/block/vold/31:9 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec……..

/dev/block/vold/31:9 /mnt/secure/asec vfat rw,dirsync,nosuid,nodev…………

1.6.4判断是否支持APP2SD

Android上层代码中判断是否支持APP2SD的方法一般是判断主分区是否是虚拟的,如果不是虚拟的就要实现APP2SD。

public static boolean isExternalStorageEmulated() {

   final StorageVolume primary = getPrimaryVolume();

   return (primary != null && primary.isEmulated());

}

2MountService简介


《Android的USB系统之二 (Vold和mountservice分析)》

2.1MountService功能介绍:

MountService是一个服务类,在ServiceManager中注册为系统服务,提供对外部存储设备的管理、查询等服务,并在存储设备状态变更时发出通知。MountService起到了一个承上启下的作用,向上公开方法供上层对存储设备进行操作(enable/disable/mount…),并在存储设备状态变更时发出通知。向下接收Vold发来的事件(设备状态变更,设备插入,设备移除等),同时也会将命令发送给Vold,进行更底层的操作。

MountService中的主要方法如下表所示:

《Android的USB系统之二 (Vold和mountservice分析)》
《Android的USB系统之二 (Vold和mountservice分析)》

2.2MountService同Vold的交互

MountService通过NativeDaemonConnector来和Vold传递信息, NativeDaemonConnector是一个继承自 Runnable 的类,它的构造函数中需要传入一个回调接口,在它的 run 函数里会创建 socket 并一直监听,当收到需要上层处理的信息时,会调用传入接口中的 onEvent。NativeDaemonConnector中也提供了 doCommond方法向 socket 另一端发送信息。 MountService 会在构造函数中创建一个 NativeDaemonConnector的实例,把自身作为参数传递进去用以回调,然后开启这个线程,这样就创建了同 Vold 通信的 socket 并监听来自vold的消息。 这些消息包括: VolumeStateChange, ShareAvailabilityChange, VolumeDiskInserted,VolumeDiskRemoved,VolumeBadRemoval,收到消息后回调 onEvent 做处理。MountService也能通过调用 doCommond向 Vold发送消息。一个简单的发送命令的代码:

public int mountSecureContainer(String id, String key,intownerUid) {

  int rc = StorageResultCode.OperationSucceeded;

  try{

        mConnector.execute(“asec”,”mount”, id,newSensitiveArg(key),ownerUid);

  }catch(NativeDaemonConnectorException e) {

     int code = e.getCode();

     if(code != VoldResponseCode.OpFailedStorageBusy) {

           rc = StorageResultCode.OperationFailedInternalError;

      }

  }

returnrc;

}

2.3MountService同StroageManager的交互

MountService 是运行在 SystemService 这个进程中,所以上层应用无法直接访问,StorageManager就是提供给应用层来访问存储服务的,它通过Binder 机制与 MountService所在进程进行通信,将使用者的请求转发进MountService 中进行处理。 目前StorageManager中支持的方法有enableUsbMassStorage, disableUsbMassStorage,isUsbMassStorageConnected,isUsbMassStorageConnected。在 StorageManager 的构造函数中,还通过调用 MountService中的 registerListener 函数来注册 listener 到 MountService,同时,它自己也提供了registerListener 函数供其它应用来注册 listener,这样,当 MountService 知道存储设备状态变更时,会调用 StorageManager中 listener的方法,而 StorageManager又会继续回调上去上层应用也就可以做相应的操作。

2.4MountService同PackageManagerService的交互

PackageManagerService 是一个用于管理设备中所有 apk的服务。 当外部存储设备被挂载或卸除时,会通知 PackagemanagerService 更新状态。然后 PackageManagerService 通过PackageHelper调用MountService中 secure container相关方法。比如app 安装在外部存储上,当外部存储状态变化就会通知应用更新。

    原文作者:android之子
    原文地址: https://www.jianshu.com/p/6d6b3f121c1c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞