android 6.0弹通知,Android 6.0 弹出"SD卡不受支持"通知的前因后果

1、"SD卡不受支持"通知的来源

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)

@Override

public 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<: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对象的设备节点:

0818b9ca8b590ca3270a3433284dd417.png

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<:string> cmd;

cmd.push_back(kSgdiskPath);

cmd.push_back("--android-dump");

cmd.push_back(mDevPath);

std::vector<: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、创建Volume

if (!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吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值