前篇地址

Android SDCard UnMounted 流程分析(一)

Android SDCard UnMounted 流程分析(二)

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

粗糙图

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

转载于:https://blog.51cto.com/terryblog/817015

Android SDCard UnMounted 流程分析(三)相关推荐

  1. android volume挂载流程,Android SDCard UnMounted 流程分析(一)

    Android SDCard框架 Android SDCard框架,我们修改一般涉及到四大模块 Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及 Vold 作为Kernel ...

  2. Android SDCard Mount 流程分析(一)

    点击打开链接 前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误.今天重新梳理了头绪,针对moun ...

  3. Android UI绘制流程分析(三)measure

    源码版本Android 6.0 请参阅:http://androidxref.com/6.0.1_r10 本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟 ...

  4. android camera2 API流程分析

    Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原 ...

  5. Android -- Wifi启动流程分析

    Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了:下面就简单介绍下Android中Wifi的启动流程. 当我在Setting菜单里点击 ...

  6. 【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)

    [SemiDrive源码分析][X9芯片启动流程]27 - AP1 Android Preloader启动流程分析(加载atf.tos.bootloader镜像后进入BL31环境) 一.Android ...

  7. Android 亮屏流程分析

    https://blog.csdn.net/FightFightFight/article/details/79808100 相关文章: [Android Framework] 8.1 PowerMa ...

  8. android 4.4 按键分析三

    .5         Android Framework层消息处理 3.5.1         基本介绍 关于Android消息处理机制的全面分析,可参考另外的文档,这里着重介绍事件处理相关问题,作为 ...

  9. Android 8.1 PowerManagerService分析(三)——WakeLock机制

    欢迎大家关注我的掘金帐号 我会在那里定期更新最新版本的Android Framework源码模块分析~~ 在Android 8.1 PowerManagerService分析(一)中,主要分析了PMS ...

最新文章

  1. 一个顽猴沿着一座小山的n级台阶向上跳,猴子上山一步可跳1级或3级,试求上山的n级台阶有多少种不同的爬法。...
  2. 设计模式笔记(9)---组合模式(结构型)
  3. 为什么从前那些.NET开发者都不写单元测试呢?
  4. nginx源码分析—数组结构ngx_array_t
  5. java反射成员变量_java反射之成员变量的反射
  6. mysql 左连接 怎么走索引_mysql left join查询没走索引
  7. linux学习第四周作业练习
  8. RocketMQ大数据畅想
  9. 外呼机器人起名_电销外呼机器人如此受欢迎,今天终于知道原因了
  10. iOS 地图移动中心点获取
  11. pmp第六版错题集6.1
  12. 嵌入式Linux入门指南(一)——学习路线篇
  13. 三七互娱秋招web前端笔试题编程题(使用原生JS实现一个英雄类Hero, 可以按照以下方式调用正确输出)
  14. IOS开发-常用的第三方库
  15. 微信小程序订阅信息之Java实现详解
  16. 川土微电子8通道隔离式数字输入接收器
  17. 利用Matlab将任意曲线旋转任意角度
  18. 基于java的药店管理系统
  19. 物联网中的“网”正在经历一次“脱胎换骨”,不仅洞察人性,还将修炼成精...
  20. 网络工程师成长日记422-奇葩先生

热门文章

  1. Puppet常用配置与管理
  2. linux任务计划学习
  3. 配置了tomcat,更改了默认端口为8070,还是访问不了
  4. 详解Android中AsyncTask的使用
  5. java jar命令来运行jar包
  6. armv6, armv7, armv7s和i386
  7. 不要相信 errno 可靠
  8. java wsdl反向生成源码,并使用CXF实现客户端调用代码
  9. Spring注入方式及用到的注解 -----@Component,@Service,@Controller,@Repository
  10. 四种方法下载网络文本数据到本地内存