Android SDCard UnMounted 流程分析(三)
前篇地址
Android SDCard UnMounted 流程分析(一)
Android SDCard UnMounted 流程分析(二)
前一篇讲到SDCard unmout onEvent 发送socket 到框架层,接下来分析框架层得到数据后的流程。
MoutService
当android 系统启动时,system将MountService 添加到启动服务里面,而MountService 会开启一个线程来运行NativeDaemonConnector,由它来监听vold的消息,代码:
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方法里面 :
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
再回过头看NativeDaemonConnector 的listenToSocket,代码中实例化了一个LocalSocketAddress的实例,并传入一个叫"vold"字符串的socket 名称,这与CommandListener中继承了FrameworkListener时给的"vold"名称是一致的,两个socket名称一致则可以互相进行通讯了,代码如下:
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 的状态传递进去。代码如下:
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改变的广播,代码如下:
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看该函数里的主要代码:
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 通过这个广播发送出去,代码如下:
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 函数,代码如下:
/*
* 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 方法里面:
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 流程分析(三)相关推荐
- android volume挂载流程,Android SDCard UnMounted 流程分析(一)
Android SDCard框架 Android SDCard框架,我们修改一般涉及到四大模块 Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及 Vold 作为Kernel ...
- Android SDCard Mount 流程分析(一)
点击打开链接 前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误.今天重新梳理了头绪,针对moun ...
- Android UI绘制流程分析(三)measure
源码版本Android 6.0 请参阅:http://androidxref.com/6.0.1_r10 本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟 ...
- android camera2 API流程分析
Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原 ...
- Android -- Wifi启动流程分析
Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了:下面就简单介绍下Android中Wifi的启动流程. 当我在Setting菜单里点击 ...
- 【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)
[SemiDrive源码分析][X9芯片启动流程]27 - AP1 Android Preloader启动流程分析(加载atf.tos.bootloader镜像后进入BL31环境) 一.Android ...
- Android 亮屏流程分析
https://blog.csdn.net/FightFightFight/article/details/79808100 相关文章: [Android Framework] 8.1 PowerMa ...
- android 4.4 按键分析三
.5 Android Framework层消息处理 3.5.1 基本介绍 关于Android消息处理机制的全面分析,可参考另外的文档,这里着重介绍事件处理相关问题,作为 ...
- Android 8.1 PowerManagerService分析(三)——WakeLock机制
欢迎大家关注我的掘金帐号 我会在那里定期更新最新版本的Android Framework源码模块分析~~ 在Android 8.1 PowerManagerService分析(一)中,主要分析了PMS ...
最新文章
- 一个顽猴沿着一座小山的n级台阶向上跳,猴子上山一步可跳1级或3级,试求上山的n级台阶有多少种不同的爬法。...
- 设计模式笔记(9)---组合模式(结构型)
- 为什么从前那些.NET开发者都不写单元测试呢?
- nginx源码分析—数组结构ngx_array_t
- java反射成员变量_java反射之成员变量的反射
- mysql 左连接 怎么走索引_mysql left join查询没走索引
- linux学习第四周作业练习
- RocketMQ大数据畅想
- 外呼机器人起名_电销外呼机器人如此受欢迎,今天终于知道原因了
- iOS 地图移动中心点获取
- pmp第六版错题集6.1
- 嵌入式Linux入门指南(一)——学习路线篇
- 三七互娱秋招web前端笔试题编程题(使用原生JS实现一个英雄类Hero, 可以按照以下方式调用正确输出)
- IOS开发-常用的第三方库
- 微信小程序订阅信息之Java实现详解
- 川土微电子8通道隔离式数字输入接收器
- 利用Matlab将任意曲线旋转任意角度
- 基于java的药店管理系统
- 物联网中的“网”正在经历一次“脱胎换骨”,不仅洞察人性,还将修炼成精...
- 网络工程师成长日记422-奇葩先生
热门文章
- Puppet常用配置与管理
- linux任务计划学习
- 配置了tomcat,更改了默认端口为8070,还是访问不了
- 详解Android中AsyncTask的使用
- java jar命令来运行jar包
- armv6, armv7, armv7s和i386
- 不要相信 errno 可靠
- java wsdl反向生成源码,并使用CXF实现客户端调用代码
- Spring注入方式及用到的注解 -----@Component,@Service,@Controller,@Repository
- 四种方法下载网络文本数据到本地内存