分享

Android-Vold, Framework和UI的通信-大结局(12)

 free_light 2014-06-23
作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun


写了好几篇文章,花儿都快谢了,终于轮到Framework发挥领导的身份了,Framework作为界面与vold之间的桥梁,用户是Framework的大爷,那么Framework就是Vold的大哥大,Framework发出一个命令,Vold不敢不遵从,只能照着Framework的意思照办,千万不能出差错,不然Android就要丢大脸了,登不上三国争霸的舞台,iOS VS Android VS WP7?
在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. public void run() {  
  2.     while (true) {  
  3.         try {  
  4.             /*开始监听底层广播信息*/  
  5.             listenToSocket();  
  6.         } catch (Exception e) {  
  7.             Slog.e(TAG, "Error in NativeDaemonConnector", e);  
  8.             SystemClock.sleep(5000);  
  9.         }  
  10.     }  
  11. }  
  12. private void listenToSocket() throws IOException {  
  13.     /*函数太长,以下是执行顺序*/  
  14.     //连接SOCKET  
  15.     socket.connect(address);  
  16.     ->  
  17.     /*调用该函数来处理下发"volume list"命令的反馈结果*/  
  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. void Volume::setState(int state) {  
  2.     char msg[255];  
  3.     int oldState = mState;  
  4.   
  5.     if (oldState == state) {  
  6.         SLOGW("Duplicate state (%d)\n", state);  
  7.         return;  
  8.     }  
  9.   
  10.     mState = state;  
  11.   
  12.     SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,  
  13.          oldState, stateToStr(oldState), mState, stateToStr(mState));  
  14.     snprintf(msg, sizeof(msg),  
  15.              "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),  
  16.              getMountpoint(), oldState, stateToStr(oldState), mState,  
  17.              stateToStr(mState));  
  18.   
  19.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,  
  20.                                          msg, false);  
  21. }  

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

  1. static const int VolumeStateChange         = 605;//磁盘状态改变的反馈码  
  2. static const int ShareAvailabilityChange   = 620;//OTG状态改变的反馈码  
  3. static const int VolumeDiskInserted        = 630;//插入磁盘的反馈码  
  4. static const int VolumeDiskRemoved         = 631;//移除磁盘的反馈码  
  5. static const int VolumeBadRemoval          = 632;//没有安全删除地移除磁盘的反馈码。  

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

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

三、下发操作命令?

volume mount sdcard:

  1. public int mountVolume(String path) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);  
  3.   
  4.     waitForReady();  
  5.     return doMountVolume(path);  
  6. }  

volume unmount force:

  1. public void unmountVolume(String path, boolean force) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);  
  3.     waitForReady();  
  4.   
  5.     String volState = getVolumeState(path);  
  6.     if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);  
  7.     if (Environment.MEDIA_UNMOUNTED.equals(volState) ||  
  8.             Environment.MEDIA_REMOVED.equals(volState) ||  
  9.             Environment.MEDIA_SHARED.equals(volState) ||  
  10.             Environment.MEDIA_UNMOUNTABLE.equals(volState)) {  
  11.         // Media already unmounted or cannot be unmounted.  
  12.         // TODO return valid return code when adding observer call back.  
  13.         return;  
  14.     }  
  15.     UnmountCallBack ucb = new UnmountCallBack(path, force);  
  16.     mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));  
  17. }  

volume format sdcard:

  1. public int formatVolume(String path) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);  
  3.     waitForReady();  
  4.   
  5.     return doFormatVolume(path);  
  6. }  

volume share sdcard ums
volume unshare sdcard ums:

  1. private void doShareUnshareVolume(String path, String method, boolean enable) {  
  2.     // TODO: Add support for multiple share methods  
  3.     if (!method.equals("ums")) {  
  4.         throw new IllegalArgumentException(String.format("Method %s not supported", method));  
  5.     }  
  6.   
  7.     try {  
  8.         mConnector.doCommand(String.format(  
  9.                 "volume %sshare %s %s", (enable ? "" : "un"), path, method));  
  10.     } catch (NativeDaemonConnectorException e) {  
  11.         Slog.e(TAG, "Failed to share/unshare", e);  
  12.     }  
  13. }  

volume shared sdcard ums:

  1. private boolean doGetVolumeShared(String path, String method) {  
  2.     String cmd = String.format("volume shared %s %s", path, method);  
  3.     ArrayList<String> rsp;  
  4.   
  5.     try {  
  6.         rsp = mConnector.doCommand(cmd);  
  7.     } catch (NativeDaemonConnectorException ex) {  
  8.         Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);  
  9.         return false;  
  10.     }  
  11.   
  12.     for (String line : rsp) {  
  13.         String[] tok = line.split(" ");  
  14.         if (tok.length < 3) {  
  15.             Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");  
  16.             return false;  
  17.         }  
  18.   
  19.         int code;  
  20.         try {  
  21.             code = Integer.parseInt(tok[0]);  
  22.         } catch (NumberFormatException nfe) {  
  23.             Slog.e(TAG, String.format("Error parsing code %s", tok[0]));  
  24.             return false;  
  25.         }  
  26.         if (code == VoldResponseCode.ShareEnabledResult) {  
  27.             return "enabled".equals(tok[2]);  
  28.         } else {  
  29.             Slog.e(TAG, String.format("Unexpected response code %d", code));  
  30.             return false;  
  31.         }  
  32.     }  
  33.     Slog.e(TAG, "Got an empty response");  
  34.     return false;  
  35. }  

在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. private synchronized IMountService getMountService() {  
  2.    if (mMountService == null) {  
  3.        /*调用了getService函数来注册"mount"服务的操作权限*/  
  4.        IBinder service = ServiceManager.getService("mount");  
  5.        if (service != null) {  
  6.            mMountService = IMountService.Stub.asInterface(service);  
  7.        } else {  
  8.            Log.e(TAG, "Can't get mount service");  
  9.        }  
  10.    }  
  11.    return mMountService;  
  12. }  

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

  1. private void mount() {  
  2.     /*先注册*/  
  3.     IMountService mountService = getMountService();  
  4.     try {  
  5.         if (mountService != null) {  
  6.             /*挂载SD卡*/  
  7.             mountService.mountVolume(Environment.getExternalStorageDirectory().toString());  
  8.         } else {  
  9.             Log.e(TAG, "Mount service is null, can't mount");  
  10.         }  
  11.     } catch (RemoteException ex) {  
  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. private void updateMemoryStatus() {  
  2.     String status = Environment.getExternalStorageState();  
  3.     String readOnly = "";  
  4.     if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {  
  5.         status = Environment.MEDIA_MOUNTED;  
  6.         readOnly = mRes.getString(R.string.read_only);  
  7.     }  
  8.   
  9.     mSdFormat.setEnabled(false);  
  10.   
  11.     if (status.equals(Environment.MEDIA_MOUNTED)) {  
  12.         try {  
  13.             File path = Environment.getExternalStorageDirectory();  
  14.             StatFs stat = new StatFs(path.getPath());  
  15.             long blockSize = stat.getBlockSize();  
  16.             long totalBlocks = stat.getBlockCount();  
  17.             long availableBlocks = stat.getAvailableBlocks();  
  18.               
  19.             mSdSize.setSummary(formatSize(totalBlocks * blockSize));  
  20.             mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);  
  21.   
  22.             mSdMountToggle.setEnabled(true);  
  23.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));  
  24.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));  
  25.   
  26.         } catch (IllegalArgumentException e) {  
  27.             // this can occur if the SD card is removed, but we haven't received the   
  28.             // ACTION_MEDIA_REMOVED Intent yet.  
  29.             status = Environment.MEDIA_REMOVED;  
  30.         }  
  31.           
  32.     } else {  
  33.         mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));  
  34.         mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));  
  35.   
  36.   
  37.         if (status.equals(Environment.MEDIA_UNMOUNTED) ||  
  38.             status.equals(Environment.MEDIA_NOFS) ||  
  39.             status.equals(Environment.MEDIA_UNMOUNTABLE) ) {  
  40.             mSdFormat.setEnabled(true);  
  41.             mSdMountToggle.setEnabled(true);  
  42.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  43.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));  
  44.         } else {  
  45.             mSdMountToggle.setEnabled(false);  
  46.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  47.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));  
  48.         }  
  49.     }  
  50.   
  51.     File path = Environment.getDataDirectory();  
  52.     StatFs stat = new StatFs(path.getPath());  
  53.     long blockSize = stat.getBlockSize();  
  54.     long availableBlocks = stat.getAvailableBlocks();  
  55.     findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));  
  56. }  

五、好聚好散
终于将Android系统磁盘管理的这部分给over了,现在我深深地敬佩系统设计人员,他们都是非凡的人才,再次感谢谷歌对Android社区的贡献。先不说谷歌有没有对Android的开源,或者一些专利的事,但在我眼里,Google是非常强大的公司,向他学习,没错的。
三争IT天下:    Google   VS  Microsoft  VS  Apple.
三分智能领域:Android  VS  WP7         VS  iOS.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多