Android 6.0 弹出"SD卡不受支持"通知的前因后果
来源:互联网 发布:淘宝如何快速提升信誉 编辑:程序博客网 时间:2024/06/03 12:47
1、"SD卡不受支持"通知的来源
由上述代码段我们知道出现问题的原因是,系统上报了一个存储容量大于0的Disk设备,但是该Disk的volume数量为0。
这样,问题就被转移了。要解释Disk的volume count为何为0,需要分析vold系统进程。
2、vold进程
vold进程用来管理和控制系统存储设备,比如SD/U盘热插拨、挂载、卸载、格式化。
vold进程由init进程启动,在init.rc里面注册:
对上,vold进程监听到kernel上报的事件(比如设备add,chang,remove等),处理后告知Java层。
这部分功能vold使用socket和Java层通信,负责通信实现的Java类是NativeDaemonConnector:
对下,void监听kernel事件,并对事件做相应的处理。这部分的原理是:
kernel检测到某些设备发生变化(典型的例子就是热插拔),会通过uevent事件告知用户,事件传递方向:内核空间->用户空间。
用来实现上述事件传递的,就是Netlink机制,它是一个socket,用户空间使用标准socket API就可以实现和kernel的通信:
那么重点来了:存储设备插入,NetlinkHandler接收到该事件并进行分发:
在Linux的世界里,设备分三类:字符设备、网络设备、块设备。SD卡、U盘、硬盘等自然就是块设备了,块设备事件由VolumeManager处理:
kernel上报事件中含有产生事件的设备对应在/sys文件系统下的路径,通过findParam("DEVPATH")获取,设备类型通过findParam("DEVTYPE")获取。
对于不是"disk"的设备产生的事件,pass调。
"MAJOR"、"MINOR"是设备的主次设备号,用于在/dev目录下创建设备节点。
switch语句处理不同类型的事件,add、change、remove。
mDiskSources是一个DiskSource对象指针的list,它来自系统根目录下的配置脚本,形如"/fstab.*"。这里的for循环用于进一步确认kernel上报的事件属于我们关心的事件。
判断方式就是根据事件设备的路径和配置文件对应项比对,match成功就创建一个Disk对象。
Disk,终于到Disk了!
创建好Disk对象,Java层的onDiskScannedInternal()方法就可以运行了,然后接着看为什么这个Disk对象不存在volume。
这样Java层也有了一个Disk对象。
readMetadata()函数通过读取/sys文件系统下设备的属性文件获取设备的信息,如厂商、容量大小等。
readPartitions()函数读取Disk的分区信息,导致弹出"SD卡不受支持"通知的原因就在这里:
至于sgdisk为何执行失败,去man一下sgdisk吧。
StorageNotification.java (frameworks\base\packages\systemui\src\com\android\systemui\usb)private void onDiskScannedInternal(DiskInfo disk, int volumeCount) {if (volumeCount == 0 && disk.size > 0) {// 构建"SD卡不受支持"通知}}
由上述代码段我们知道出现问题的原因是,系统上报了一个存储容量大于0的Disk设备,但是该Disk的volume数量为0。
这样,问题就被转移了。要解释Disk的volume count为何为0,需要分析vold系统进程。
2、vold进程
vold进程用来管理和控制系统存储设备,比如SD/U盘热插拨、挂载、卸载、格式化。
vold进程由init进程启动,在init.rc里面注册:
service vold /system/bin/vold class core socket vold stream 0660 root mount socket cryptd stream 0660 root mount ioprio be 2
对上,vold进程监听到kernel上报的事件(比如设备add,chang,remove等),处理后告知Java层。
这部分功能vold使用socket和Java层通信,负责通信实现的Java类是NativeDaemonConnector:
NativeDaemonConnector.java (frameworks\base\services\core\java\com\android\server)@Overridepublic void run() {while (true) {try {listenToSocket();} catch (Exception e) {}}}
对下,void监听kernel事件,并对事件做相应的处理。这部分的原理是:
kernel检测到某些设备发生变化(典型的例子就是热插拔),会通过uevent事件告知用户,事件传递方向:内核空间->用户空间。
用来实现上述事件传递的,就是Netlink机制,它是一个socket,用户空间使用标准socket API就可以实现和kernel的通信:
NetlinkManager.cpp (system\vold)int NetlinkManager::start() { struct sockaddr_nl nladdr; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); nladdr.nl_groups = 0xffffffff; if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; }}
那么重点来了:存储设备插入,NetlinkHandler接收到该事件并进行分发:
NetlinkHandler.cpp (system\vold)void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); }}
在Linux的世界里,设备分三类:字符设备、网络设备、块设备。SD卡、U盘、硬盘等自然就是块设备了,块设备事件由VolumeManager处理:
VolumeManager.cpp (system\vold)void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {std::string eventPath(evt->findParam("DEVPATH")); std::string devType(evt->findParam("DEVTYPE")); if (devType != "disk") return;int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); dev_t device = makedev(major, minor); switch (evt->getAction()) { case NetlinkEvent::Action::kAdd: { for (auto source : mDiskSources) { if (source->matches(eventPath)) { // For now, assume that MMC devices are SD, and that // everything else is USB int flags = source->getFlags(); if (major == kMajorBlockMmc) { flags |= android::vold::Disk::Flags::kSd; } else { flags |= android::vold::Disk::Flags::kUsb; } auto disk = new android::vold::Disk(eventPath, device, source->getNickname(), flags); disk->create(); mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk)); break; } } break; } case NetlinkEvent::Action::kChange: { break; } case NetlinkEvent::Action::kRemove: { break; } default: { LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction(); break; } }}
kernel上报事件中含有产生事件的设备对应在/sys文件系统下的路径,通过findParam("DEVPATH")获取,设备类型通过findParam("DEVTYPE")获取。
对于不是"disk"的设备产生的事件,pass调。
"MAJOR"、"MINOR"是设备的主次设备号,用于在/dev目录下创建设备节点。
switch语句处理不同类型的事件,add、change、remove。
mDiskSources是一个DiskSource对象指针的list,它来自系统根目录下的配置脚本,形如"/fstab.*"。这里的for循环用于进一步确认kernel上报的事件属于我们关心的事件。
判断方式就是根据事件设备的路径和配置文件对应项比对,match成功就创建一个Disk对象。
Disk,终于到Disk了!
创建好Disk对象,Java层的onDiskScannedInternal()方法就可以运行了,然后接着看为什么这个Disk对象不存在volume。
Disk.cpp (system\vold)Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags) : mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated( false), mJustPartitioned(false) { mId = StringPrintf("disk:%u,%u", major(device), minor(device)); mEventPath = eventPath; mSysPath = StringPrintf("/sys/%s", eventPath.c_str()); mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str()); CreateDeviceNode(mDevPath, mDevice);}初始化对象成员,调用CreateDeviceNode()后在/dev/block/vold/目录下创建Disk对象的设备节点:
disk->create():Disk.cpp (system\vold)status_t Disk::create() { CHECK(!mCreated); mCreated = true; notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags)); readMetadata(); readPartitions(); return OK;}notifyEvent()把DiskCreated事件上报,由MountService系统服务处理:
MountService.java (frameworks\base\services\core\java\com\android\server)private boolean onEventLocked(int code, String raw, String[] cooked) {switch (code) {case VoldResponseCode.DISK_CREATED: {mDisks.put(id, new DiskInfo(id, flags));break;}}}
这样Java层也有了一个Disk对象。
readMetadata()函数通过读取/sys文件系统下设备的属性文件获取设备的信息,如厂商、容量大小等。
readPartitions()函数读取Disk的分区信息,导致弹出"SD卡不受支持"通知的原因就在这里:
Disk.cpp (system\vold)status_t Disk::readPartitions() { // 1、构建/system/bin/sgdisk --android-dump /dev/block/vold/xxx命令 std::vector<std::string> cmd; cmd.push_back(kSgdiskPath); cmd.push_back("--android-dump"); cmd.push_back(mDevPath); std::vector<std::string> output;// 2、运行上述命令 status_t res = ForkExecvp(cmd, output); if (res != OK) { LOG(WARNING) << "sgdisk failed to scan " << mDevPath; notifyEvent(ResponseCode::DiskScanned); mJustPartitioned = false; return res; }// 4、解析命令返回的结果 for (auto line : output) {// 5、创建Volumeif (!strcasecmp(typeGuid, kGptBasicData)) {createPublicVolume(partDevice);} else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {createPrivateVolume(partDevice, partGuid);} } notifyEvent(ResponseCode::DiskScanned); return OK;}显而易见,如果sgdisk命令执行失败,就不会为Disk创建Volume,"SD卡不受支持"通知就出来了。
至于sgdisk为何执行失败,去man一下sgdisk吧。
阅读全文
1 0
- Android 6.0 弹出"SD卡不受支持"通知的前因后果
- 未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。
- 让你的Android应用支持转移到SD卡
- 让你的Android应用支持转移到SD卡
- 让你的Android应用支持转移到SD卡
- Android 弹出通知Toast的使用
- 未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker
- 未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker
- 未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker
- Android应用程序支持安装到SD卡
- android模拟器如何支持sd卡
- Android应用支持转移到SD卡
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- Android中,SD卡上的媒体文件(图片、视频)的改变与通知
- python
- python小程序-0009
- 利用站点恢复服务迁移物理机到Azure
- 【有配图】Spring Boot 第一个Hello World 程序
- Play wave file in python
- Android 6.0 弹出"SD卡不受支持"通知的前因后果
- 如何访问google浏览器
- 欢迎使用CSDN-markdown编辑器
- sts pom文件打开方式
- 微信开发1---------------------获得access_token
- pm2常用的命令用法介绍
- Sublime Text 3 快捷键
- myeclipse svn信息突然消失
- ajax中解析json ,ajax一直错误