sdcard的插拔是通过uevet事件传递给上层的,在kernel的 kernel\lib\kobject_uevent.c
kobject_uevent_env 函数中添加打印信息获取查看所有的uevent事件,发现只有sdcard插上时上传的add事件,sdcard拔掉时并没有相应的remove事件。。。
跟踪sd卡驱动源码,插拔会触发一个中断,在中断处理函数中添加打印,没有问题,插拔都会执行到;中断处理函数会调度一个工作队列,工作队列的核心函数是 drivers\mmc\core\core.c 的 mmc_rescan 函数,
所有sdcard、mmc、sdio添加移除工作都是这函数处理。添加过程没有问题,关注sdcard移除阶段的操作
bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行bus_ops->remove()拔出SD卡。对应下面的代码,跟踪发现“ !(host->caps & MMC_CAP_NONREMOVABLE))” 这个条件不满足,所以根本没有执行后面的 detect 、remove操作
//如果已经有 bus_ops ,表示注册过sdcard,发生了中断现在去用 bus_ops->detect 函数去检测sdcard是否存在
if (host->bus_ops && host->bus_ops->detect && !host->bus_dead && !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host);
对比机中插入不支持sdcard会弹出一个view提示不支持格式,格式不支持那么出错阶段就在挂载过程
梳理一遍sdcard插入流程
// 插入sd卡-》kernel发出add的uevent事件,上层处理去挂载sd卡
// 上层处理挂载消息的对应代码为 mountService.java 的doMountVolume方法
// 添加log打印出错误码,发现没有一个对应的,心累,果断将OpFailedMediaBlank的错误码改成打印出来的值fix
try {
mConnector.execute("volume", "mount", path);
} catch (NativeDaemonConnectorException e) {
/*
* Mount failed for some reason
*/
String action = null;
int code = e.getCode();
Slog.i("zch","MountService.java=====MountService.java doMountVolume code ="+code);
if (code == VoldResponseCode.OpFailedNoMedia) {
...
// 挂载失败时不支持格式的错误码
else if (code == VoldResponseCode.OpFailedMediaBlank) {
if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
/*
* Media is blank or does not contain a supported filesystem
*/
updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
action = Intent.ACTION_MEDIA_NOFS;
rc = StorageResultCode.OperationFailedMediaBlank;
}
log中看出在 /mnt/secure 目录创建一个文件失败,提示没有权限,果断关掉selinux,仍然失败,adb shell手动进去创建一个文件,仍然失败,这下懵逼了….
求助大神,大神说/mnt/目录下的文件都会挂载,如果没有指定,就会按照默认的 rootfs 格式挂载,rootfs的挂载格式为 ro ,在init.rc中重新挂载 /mnt/secure 目录为rw的tmpfs,fix。
mkdir /mnt/secure 0700 system system
mount tmpfs tmpfs /mnt/secure mode=0700,gid=1000
分析移动apk到sdcard的log,发现会将 /data/app/com.xxx.xxx apk安装包文件全部拷贝到 /mnt/asec目录,不是移到sdcard吗,套路不对啊,代码逻辑有问题?查看对比机的log,也是移到/mnt/asec。网上搜“android /mnt/asec ”果然找到答案了:这个目录就是sdcard 的.android_secure目录的挂载点。进adb shell一看sd卡下面压根就没有这个目录,android_secure是啥时候创建、挂载的?
system/vold 目录下一grep找到了: Volume::mountAsecExternal 方法就是专门挂载这个目录的。添加一些打印,压根没有执行这个函数。。。。
分析源码 Volume 在构造函数中传入的flags为 VOL_PROVIDES_ASEC 时才会执行Volume::mountAsecExternal
代码追踪 flags的赋值
// DirectVolume.cpp 追踪到Volume的构造
DirectVolume::DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags) :
Volume(vm, rec, flags) {
----------
// system/vold/main.cpp 追踪到 DirectVolume 的构造和flags的赋值
for (i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
DirectVolume *dv = NULL;
flags = 0;
/* Set any flags that might be set for this volume */
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
flags |= VOL_NONREMOVABLE;
}
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= VOL_ENCRYPTABLE;
}
/* Only set this flag if there is not an emulated sd card */
// ★条件判断★
if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
!strcmp(fstab->recs[i].fs_type, "vfat")) {
flags |= VOL_PROVIDES_ASEC; // flags 赋值为需要的值
}
dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
if (dv->addPath(fstab->recs[i].blk_device)) {
SLOGE("Failed to add devpath %s to volume %s",
fstab->recs[i].blk_device, fstab->recs[i].label);
goto out_fail;
}
查看 fstab.qcom配置文件,发现vfat 满足,追踪 fs_mgr_is_noemulatedsd 函数
int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
}
fstab.qcom 文件
发现源文件中并未设置 NOEMULATEDSD flag,添加验证fix。