Android-Vold, Framework和UI的通信

Framework作为界面与vold之间的桥梁,用户是Framework的 用户,那么Framework就是Vold的大哥大,Framework发出一个命令,Vold不敢不遵从,只能照着Framework的意思照办,iOS VS Android VS WP
在Framework里面,有一个目录是用来存放一些Java的系统服务,这些都在后台跑着,在:/android-2.2r2/frameworks/base/services/java/com/android/server目录下,比较重要的是这两个源文件:MountService.java和NativeDaemonConnector.java。
这里先列出在Vold中,VolumeCmd类处理的一些磁盘操作命令,这些命令均是有Framework下发的:
1.volume list:Framework先得到系统目前存在几个Volume对象,需要获取到这些对象的标签;
2.volume debug:设置USB调试模式
3.volume mount sdcard:挂载SD卡
4.volume unmount force:卸载SD卡
5.volume format sdcard:格式化SD卡
6.volume share sdcard ums:开启SD卡的OTG功能(大容量存储),也就是连接电脑
7.volume unshare sdcard ums:关闭SD卡的OTG功能(大容量存储)
8.volume shared sdcard ums:获取目前OTG的开启状态,就是是否连接电脑的状态。

以下分别列出每个命令的下发函数,对Java不熟,但看得懂程序的流程,真是惭愧啊。


一、Framework磁盘管理服务的开启?

在NativeDaemonConnector服务里面,开始监听底层Vold发送过来的磁盘热插拔事件的状态信息,当收到底层广播上来的状态,调用MountService服务中的onDaemonConnected函数进行处理,当然这是开机第一次去获取信息的,也就是下发"volume list"命令。

  1. publicvoidrun(){
  2. while(true){
  3. try{
  4. /*开始监听底层广播信息*/
  5. listenToSocket();
  6. }catch(Exceptione){
  7. Slog.e(TAG,"ErrorinNativeDaemonConnector",e);
  8. SystemClock.sleep(5000);
  9. }
  10. }
  11. }
  12. privatevoidlistenToSocket()throwsIOException{
  13. /*函数太长,以下是执行顺序*/
  14. //连接SOCKET
  15. socket.connect(address);
  16. ->
  17. /*调用该函数来处理下发"volumelist"命令的反馈结果*/
  18. mCallbacks.onDaemonConnected();
  19. ->
  20. /*处理磁盘的状态*/
  21. mCallbacks.onEvent(code,event,tokens);
  22. }

以上两个比较重要的函数在MountService当中,处理相当多的内容,源码太长。
下发volume list命令,Framework收到反馈值,将调用onDaemonConnected函数获取到了磁盘的标签,挂载点与状态,然后调用doGetShareMethodAvailable函数判断现在是否连接OTG,若连接OTG,那么调用doShareUnshareVolume函数下发otg连接命令(volume share sdcard ums)。

二、Vold与Framework如何通信?
onEvent主要是处理状态信息的解析,将每一种状态进行判断,并调用相应的操作函数。比如此时vold发送一个VolumeDiskInserted状态,意味着系统插入一个磁盘,于是onEvent就调用doMountVolume挂载函数进行下发命令(volume mount sdcard)。
在系统使用当中,用户可能会插入,移除,挂载,卸载,格式化磁盘,那么这儿多状态如何告诉Framework呢?之前已经说过,vold使用了setState函数来广播磁盘的状态消息,使得Framework能够及时地判断下发什么命令与操作。该函数在Volume.cpp源文件中,先贴出setState源码看看:

  1. voidVolume::setState(intstate){
  2. charmsg[255];
  3. intoldState=mState;
  4. if(oldState==state){
  5. SLOGW("Duplicatestate(%d)\n",state);
  6. return;
  7. }
  8. mState=state;
  9. SLOGD("Volume%sstatechanging%d(%s)->%d(%s)",mLabel,
  10. oldState,stateToStr(oldState),mState,stateToStr(mState));
  11. snprintf(msg,sizeof(msg),
  12. "Volume%s%sstatechangedfrom%d(%s)to%d(%s)",getLabel(),
  13. getMountpoint(),oldState,stateToStr(oldState),mState,
  14. stateToStr(mState));
  15. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
  16. msg,false);
  17. }

可以看到,setState函数将磁盘的label,mountpoint,oldstate,statestr,newstate,statestr消息通知给Framework,这样Framework就知道vold中SD卡的新旧状态。然后调用通过SocketListener类继承下来的sendBroadcast函数广播消息,反馈码是ResponseCode::VolumeStateChange,代表状态改变的消息。
这里可能有个疑问,vold广播这么多消息,Framework是如何分清哪条消息是代表哪一类的反馈消息呢?
广播消息在开头使用了ResponseCode类提供的一些状态反馈码,每一类消息都用一个反馈码,这样Framework的MountService服务能够很快的判断出类型。之前的文章说过了,这里列出几个重要的反馈码:

  1. staticconstintVolumeStateChange=605;//磁盘状态改变的反馈码
  2. staticconstintShareAvailabilityChange=620;//OTG状态改变的反馈码
  3. staticconstintVolumeDiskInserted=630;//插入磁盘的反馈码
  4. staticconstintVolumeDiskRemoved=631;//移除磁盘的反馈码
  5. staticconstintVolumeBadRemoval=632;//没有安全删除地移除磁盘的反馈码。

setState函数只负责磁盘状态改变的广播,其他插入磁盘或者移除磁盘的都是直接调用sendBroadcast函数来广播,这里贴出插拔事件的广播函数:

  1. voidDirectVolume::broadcastDiskAdded()
  2. {
  3. setState(Volume::State_Idle);
  4. charmsg[255];
  5. snprintf(msg,sizeof(msg),"Volume%s%sdiskinserted(%d:%d)",
  6. getLabel(),getMountpoint(),mDiskMajor,mDiskMinor);
  7. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg,false);
  8. }

三、下发操作命令?

volume mount sdcard:

  1. publicintmountVolume(Stringpath){
  2. validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
  3. waitForReady();
  4. returndoMountVolume(path);
  5. }

volume unmount force:

  1. publicvoidunmountVolume(Stringpath,booleanforce){
  2. validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
  3. waitForReady();
  4. StringvolState=getVolumeState(path);
  5. if(DEBUG_UNMOUNT)Slog.i(TAG,"Unmounting"+path+"force="+force);
  6. if(Environment.MEDIA_UNMOUNTED.equals(volState)||
  7. Environment.MEDIA_REMOVED.equals(volState)||
  8. Environment.MEDIA_SHARED.equals(volState)||
  9. Environment.MEDIA_UNMOUNTABLE.equals(volState)){
  10. //Mediaalreadyunmountedorcannotbeunmounted.
  11. //TODOreturnvalidreturncodewhenaddingobservercallback.
  12. return;
  13. }
  14. UnmountCallBackucb=newUnmountCallBack(path,force);
  15. mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE,ucb));
  16. }

volume format sdcard:

  1. publicintformatVolume(Stringpath){
  2. validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
  3. waitForReady();
  4. returndoFormatVolume(path);
  5. }

volume share sdcard ums
volume unshare sdcard ums:

  1. privatevoiddoShareUnshareVolume(Stringpath,Stringmethod,booleanenable){
  2. //TODO:Addsupportformultiplesharemethods
  3. if(!method.equals("ums")){
  4. thrownewIllegalArgumentException(String.format("Method%snotsupported",method));
  5. }
  6. try{
  7. mConnector.doCommand(String.format(
  8. "volume%sshare%s%s",(enable?"":"un"),path,method));
  9. }catch(NativeDaemonConnectorExceptione){
  10. Slog.e(TAG,"Failedtoshare/unshare",e);
  11. }
  12. }

volume shared sdcard ums:

  1. privatebooleandoGetVolumeShared(Stringpath,Stringmethod){
  2. Stringcmd=String.format("volumeshared%s%s",path,method);
  3. ArrayList<String>rsp;
  4. try{
  5. rsp=mConnector.doCommand(cmd);
  6. }catch(NativeDaemonConnectorExceptionex){
  7. Slog.e(TAG,"Failedtoreadresponsetovolumeshared"+path+""+method);
  8. returnfalse;
  9. }
  10. for(Stringline:rsp){
  11. String[]tok=line.split("");
  12. if(tok.length<3){
  13. Slog.e(TAG,"Malformedresponsetovolumeshared"+path+""+method+"command");
  14. returnfalse;
  15. }
  16. intcode;
  17. try{
  18. code=Integer.parseInt(tok[0]);
  19. }catch(NumberFormatExceptionnfe){
  20. Slog.e(TAG,String.format("Errorparsingcode%s",tok[0]));
  21. returnfalse;
  22. }
  23. if(code==VoldResponseCode.ShareEnabledResult){
  24. return"enabled".equals(tok[2]);
  25. }else{
  26. Slog.e(TAG,String.format("Unexpectedresponsecode%d",code));
  27. returnfalse;
  28. }
  29. }
  30. Slog.e(TAG,"Gotanemptyresponse");
  31. returnfalse;
  32. }

在Framework中的磁盘管理部分,也涉及到很多代码,源码太多,只贴出比较重要的功能模块代码。

四、UI的处理
从vold走到了Framework,最后一层就是UI,是用户操作磁盘的界面。有Android手机的哥们儿都知道,在设置里面可以挂载,卸载与格式化SD卡。当然这图形界面不是咱擅长的,需要Java的功底与XML,那边主要就是界面的实现。
UI的源码路径是:/android-2.2r2/packages/apps/Settings/src/com/android/settings/deviceinfo/Memory.java
我们可以发现,在Android手机的设置界面,或者主页面,只要插入SD卡或者移除SD卡,都会有相应的提示,

1.这些磁盘状态是如何与UI通信的呢?
在MountService服务中,每次改变状态,都会调用updatePublicVolumeState函数,从意思上看,可以理解成:更新一个公用的磁盘状态。这个函数就是起到了这么一个作用,这会设置到Environment类中的一个变量中,这样UI就能够取到磁盘的状态。

2.UI是如何调用Framework中的函数的?
前面有提到过,UI想要调用Framework中的MountService服务中的函数,比如以注册的方式来得到调用操作磁盘函数的权限。以下贴出UI中,Memory.java源码中注册MountService磁盘操作函数的方法:

  1. privatesynchronizedIMountServicegetMountService(){
  2. if(mMountService==null){
  3. /*调用了getService函数来注册"mount"服务的操作权限*/
  4. IBinderservice=ServiceManager.getService("mount");
  5. if(service!=null){
  6. mMountService=IMountService.Stub.asInterface(service);
  7. }else{
  8. Log.e(TAG,"Can'tgetmountservice");
  9. }
  10. }
  11. returnmMountService;
  12. }

来看一个挂载SD卡的操作函数,就知道如何来调用Framework系统服务的函数:

  1. privatevoidmount(){
  2. /*先注册*/
  3. IMountServicemountService=getMountService();
  4. try{
  5. if(mountService!=null){
  6. /*挂载SD卡*/
  7. mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
  8. }else{
  9. Log.e(TAG,"Mountserviceisnull,can'tmount");
  10. }
  11. }catch(RemoteExceptionex){
  12. }
  13. }

3.UI获取SD卡的当前状态
在第一点已经解释过了,这里再仔细的说明下:
SD卡的状态是这样获取到的:
Vold (setState)-->
MountService (onEvent)-->
MountService (updatePublicVolumeState)-->
UI (getExternalStorageState)

Vold调用setState函数广播SD卡的状态,Framework的MountService服务通过onEvent函数收到该状态消息,调用updatePublicVolumeState函数设置到Environment中的一个变量中,UI再通过Environment.getExternalStorageState函数获取到最新状态,于是UI调用updateMemoryStatus函数将最新状态设置到界面,这样用户就能看到状态的改变。
(用户都会发现,挂载或卸载SD卡,状态由挂载变为卸载需要一小段时间,这中间就是经过这些处理来得到的,包括上层发送命令->底层解析命令调用相应函数->操作完成后发送操作结果->最后将SD卡的最新状态广播给Framework,并设置到UI。这中间涉及到很多东西,再加上Java虚拟机的速度,于是界面就有一个停顿的时间)。
以下贴出UI状态的更新代码:

  1. privatevoidupdateMemoryStatus(){
  2. Stringstatus=Environment.getExternalStorageState();
  3. StringreadOnly="";
  4. if(status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)){
  5. status=Environment.MEDIA_MOUNTED;
  6. readOnly=mRes.getString(R.string.read_only);
  7. }
  8. mSdFormat.setEnabled(false);
  9. if(status.equals(Environment.MEDIA_MOUNTED)){
  10. try{
  11. Filepath=Environment.getExternalStorageDirectory();
  12. StatFsstat=newStatFs(path.getPath());
  13. longblockSize=stat.getBlockSize();
  14. longtotalBlocks=stat.getBlockCount();
  15. longavailableBlocks=stat.getAvailableBlocks();
  16. mSdSize.setSummary(formatSize(totalBlocks*blockSize));
  17. mSdAvail.setSummary(formatSize(availableBlocks*blockSize)+readOnly);
  18. mSdMountToggle.setEnabled(true);
  19. mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
  20. mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
  21. }catch(IllegalArgumentExceptione){
  22. //thiscanoccuriftheSDcardisremoved,butwehaven'treceivedthe
  23. //ACTION_MEDIA_REMOVEDIntentyet.
  24. status=Environment.MEDIA_REMOVED;
  25. }
  26. }else{
  27. mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
  28. mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
  29. if(status.equals(Environment.MEDIA_UNMOUNTED)||
  30. status.equals(Environment.MEDIA_NOFS)||
  31. status.equals(Environment.MEDIA_UNMOUNTABLE)){
  32. mSdFormat.setEnabled(true);
  33. mSdMountToggle.setEnabled(true);
  34. mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
  35. mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
  36. }else{
  37. mSdMountToggle.setEnabled(false);
  38. mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
  39. mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
  40. }
  41. }
  42. Filepath=Environment.getDataDirectory();
  43. StatFsstat=newStatFs(path.getPath());
  44. longblockSize=stat.getBlockSize();
  45. longavailableBlocks=stat.getAvailableBlocks();
  46. findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks*blockSize));
  47. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值