SD异常拔出流程浅析

来源:互联网 发布:人工智能发展现状数据 编辑:程序博客网 时间:2024/05/02 00:56

前言

在Android手机上SD卡异常拔出有时候会导致一些很奇怪的问题,比如重启/闪屏/Crash问题鉴于这种情况,就很有必要来了解下SD卡异常拔出流程了。


背景知识

  • 先了解下SD卡挂载吧
SD卡挂载到手机中,会挂载到多个分区,如下图所示:adb shell dfAdb shell df.jpg
从上面的图中可以看出,SD卡会挂载到/mnt/secure/asec ,/storage/sdcard1 ,/mnt/media_rw/sdcard1分区如果有apk是安装到SD卡,那么会生成相应的分区,如上图中的/mnt/asec/com.baidu.BaiduMap-1和/mnt/asec/com.tencent.mobileqq-1
  • app安装位置
如果把apk安装到SD卡上,那么会安装到SD卡的哪个位置呢?Asec apk locate.jpg
  • 如果SD卡异常拔出,系统会做哪些操作呢?
系统会依次卸载/mnt/asec/... -> /mnt/secure/asec  -> /storage/sdcard1  -> /mnt/media_rw/sdcard只有当以上都成功卸载,那SD卡才能成功unmount
  • SD卡挂载/卸载类型
1.主动挂载和卸载,在设置->存储中进行设置,与PC上安全删除硬件并弹出一样2.被动挂载和卸载,比如异常插拔或叫热插拔


角色分析

  • 安卓系统中与SD卡挂载、卸载相关的有哪些进程或服务呢?分别扮演一个什么样的角色呢?
先来看看下面这张图:从图中可以看出SD卡挂载会从底层到上层会涉及到4个模块Sd card unmount role.png
  • Vold进程分析
代码位置:system/vold
在system/vold下执行mm会生成可执行文件,在手机中的位置:/system/bin/vold
功能:负责大存储设备的挂载和卸载,连接framework与kernelVold .jpg
启动:由init进程启动,init进程会去解析init.rc,vold为core类型服务,被定义在init.rc中,并且使用的socket通信Vold init rc.jpg
主要关键类:system/vold/VolumeManager.cpp : 接收经过NetlinkManager处理过后的事件消息system/vold/NetlinkManager.cpp : 主要是创建于内核通信的 socket,接收来自底层的信息,然后传交给VolumeManager 处理system/vold/CommandListener.cpp : 主要收到上层 MountService 通过 doMountVolume 发来的命令,分析后,转交给 VolumeManager 处理;VolumeManager处理信息后,或报告给上层 MountService,或交给 volume 执行具体操作。system/core/libsysutils/src/SocketListener.cpp : 监听来自内核的事件。SocketListener中会开一个线程运行runListener,不停的监听来自内核的消息。system/vold/Volume.cpp : 负责SD卡挂载的主要类system/vold/DirectVolume.cpp : 该类是一个工具类,主要负责对传入的事件进行进一步的处理
  • MountService
代码位置:frameworks/base/services/java/com/android/server/MountService.java
编译生成的文件:/system/framework/services.jar
功能:控制Vold,并下达命令,获取来自vold发来的状态信息,并通知PackageManagerService,StorageManager等,并发送SD卡移除广播
启动:由system_server启动System server mount service.jpg


流程分析

注意:以下分析的是SD卡异常拔出后的流程

  • 看下面三张时序图吧
图1NetlinkManager.jpg
图1中的main.cpp位于system/vold下1.在main.cpp的人口函数main()中会调用NetlinkManager的start方法2.NetlinkManager的start方法中会调用NetlinkHandler的start方法3.NetlinkHandler的start方法中会调用父类SocketListener的startListener方法。4.在SocketListener的startListener方法中,会启动一个线程来执行:
   146 void SocketListener::runListener() {   147    148     SocketClientCollection *pendingList = new SocketClientCollection();   149    150     while(1) {   151         SocketClientCollection::iterator it;    ......   227             if (!onDataAvailable(c) && mListen) {   ......     240             }   241         }   242     }   243     delete pendingList;   244 }
5.上面方法中while(1)会一直监听内核发来的消息,如果有消息会调用NetlinkListener的onDataAvailable方法进行处理6.在NetlinkListener的onDataAvailable方法中,会调用NetlinkHandler的onEvent方法进行处理
图2Vold.jpg
7.紧接图1中调用NetlinkHandler的onEvent方法,接着看图2,在该方法中会调用VolumeManager的handleBlockEvent方法8.VolumeManager的handleBlockEvent方法并没有做处理,会调用工具类DirectVolume的handleBlockEvent来处理9.在DirectVolume的handleBlockEvent中主要会调用handlePartitionRemoved和handleDiskRemoved方法  移除SD卡前需要先移除他的分区,在上面讲到SD卡会挂载到三个分区,如果有apk安装到SD卡,那还会增加  handlePartitionRemoved方法会依次移除这些分区10.首先调用cleanupAsec去unmount掉asec,也就是/mnt/asec/com.baidu.BaiduMap-1这类  在unmountAsec方法中会调用unmountLoopImage  在unmountLoopImage时,如果有进程还打开了/mnt/asec/com.baidu.BaiduMap-1下的apk,那么就会调用killProcessesWithOpenFiles去干掉他,这就出现了之前说的重启问题 == >传送门11.如果unmountAsec成功执行,接着就会调用sendBroadcast告诉MountService  注意:这里的广播不是framework中的广播,是通过socket来通信的
 图3MountService.jpg
12.在MountService的构造函数中会调用如下代码开启一个线程
      mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);         Thread thread = new Thread(mConnector, VOLD_TAG);        thread.start();
13.在NativeDaemonConnector实现了Runnable,在run方法中会一直监听vold发来的消息
     public void run() {        mCallbackHandler = new Handler(FgThread.get().getLooper(), this);         while (true) {            try {                listenToSocket();            } catch (Exception e) {                loge("Error in NativeDaemonConnector: " + e);                SystemClock.sleep(5000);            }        }    }
14.当vold给framework发送消息时会被listenToSocket监听到,这时就会调用MountService的onEvent方法来处理   图2中的第9步发送的状态码为:VolumeBadRemoval   图3中的第3步接收到的状态码也为:VolumeBadRemoval15.在onEvent方法对于的状态码处理中会调用如下方法
             } else if (code == VoldResponseCode.VolumeBadRemoval) {                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");                /* Send the media unmounted event first */                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");                updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);                action = Intent.ACTION_MEDIA_BAD_REMOVAL;            }
16.在updatePublicVolumeState方法中会做两件事:   1.通知PackageManagerService卸载SD卡上的apk   2.通过StorageManager存储状态发送改变了
     private void updatePublicVolumeState(StorageVolume volume, String state) {        final String path = volume.getPath();        final String oldState;......            if (Environment.MEDIA_UNMOUNTED.equals(state)) {                mPms.updateExternalMediaStatus(false, false);   //让PMS去卸载APK                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(                        OBB_FLUSH_MOUNT_STATE, path));            } else if (Environment.MEDIA_MOUNTED.equals(state)) {                mPms.updateExternalMediaStatus(true, false);            }         synchronized (mListeners) {            for (int i = mListeners.size() -1; i >= 0; i--) {                MountServiceBinderListener bl = mListeners.get(i);                try {                    bl.mListener.onStorageStateChanged(path, oldState, state); //通知StorageManager存储状态改变了                } catch (RemoteException rex) {                    Slog.e(TAG, "Listener dead");                    mListeners.remove(i);                } catch (Exception ex) {                    Slog.e(TAG, "Listener failed", ex);                }            }        }    }
17.PMS这时候就会卸载掉SD卡上的APK,StorageManager就会调用到StorageEventListeneron的StorageStateChanged方法   onStorageStateChanged方法在Settings的Memory.java中,用于更新存储状态信息   ==============到这里为止,已经从底层到上层走通了---从SD卡被拔出到设置中存储信息更新==============17.通知完后,紧接着就会发送一条ACTION_MEDIA_UNMOUNTED的广播出来,告诉大家当前SD的状态18.接着继续调updatePublicVolumeState,参数为MEDIA_BAD_REMOVAL,这次就不会通知PMS了,只会继续让StorageManager更新存储信息,状态改变,信息也会相应改变19.完成以上操作后,回到图2中步骤10 - unmountVol   在这个方法中就开始依次unmount那三个分区   在此期间会设置SD卡状态和给上层发送广播。每次发送广播都会执行图3中的流程,底层做了什么得告诉他的上级MountService   如果把图2中的步骤5-cleanupAsec注释掉,那么就不会去unmountAsec,Asec会一直占用/mnt/secure/asec分区,就会导致unmount /mnt/secure/asec失败,这就是前面说的SD卡热插拔不能识别问题 ==> 传送门   如果/mnt/asec下没有apk,那/mnt/secure/asec就不会被占用,就可以成功unmount   注意:在图2中的步骤16中仍然有killProcessesWithOpenFiles,但是不会导致上面上面提到的重启问题,原因是:/mnt/asec在使用/mnt/secure/asec,system_server,关键服务不会open,所以不会有重启问题20.handlePartitionRemoved成功后,执行handleDiskRemoved,完成SD卡卸载流程
  • SD卡拔出后,系统依次会发送的广播
MEDIA_UNMOUNTED -> MEDIA_BAD_REMOVAL -> MEDIA_EJECT -> MEDIA_UNMOUNTED -> MEDIA_REMOVED省略了:android.intent.action.


总结

Mount 2.jpg
从下到上:
从上到下:Command down.jpg


参考资料

  • 源码
Vold:system/vold为vold 提供接口:system/netd其他涉及的部分:system/core/libsysutils/src                system/core/include/sysutilsFramework:frameworks/base/services/java/com/android/server 访问和提供接口类:framework/base/core/java/android/os/storage/ 可能还要参考的库:framework/base/libs/storage                  framework/base/nativeUI:Settings/src/com/android/setting/deviceinfo
  • 链接
http://www.cnblogs.com/TerryBlog/archive/2012/03/22/2411628.htmlhttp://blog.csdn.net/yihongyuelan/article/details/6926034http://www.it165.net/pro/html/201409/21202.htmlhttp://blog.csdn.net/yangwen123/article/details/8919503http://www.boyunjian.com/do/article/snapshot.do?uid=2845902311583808563
0 0
原创粉丝点击