Android 8.1 客制化OTG U盘的挂载路径名称

Android 8.1 客制化OTG U盘的挂载路径名称

有时候项目需要特殊的或者固定的U盘挂载路径,可以参考下面的办法修改!

先大概看一下U盘挂载的过程:
Android 8.1默认U盘是没有挂载到storage目录下面的,并且文件管理里面也看不到U盘,如果需要能在文件管理里面看到U盘,参考我另外一篇博客:Android 8.1 OTG U盘无法显示在系统文件管理的修改

1.当U盘插入之后,会先new一个Disk类,其构造函数会传入一个参数“eventPath”,源文件:system/vold/Disk.cpp

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;		//这里将这个参数eventPath,赋值给一个类的私有变量mEventPath 

    mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
    mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
    CreateDeviceNode(mDevPath, mDevice);
}

类的私有变量“mEventPath”保存的是每一个U盘的device路径,例如“/devices/platform/mt_usb/musb-hdrc.0.auto/usb1/1-1/1-1.4/…”,每个插入的U盘的这个路径是不一样的,取决于USB HUB和U盘的挂载端口,使用这个变量可以判断是不是自己需要的U盘。

2.接下来会new PublicVolume类和VolumeBase类,并执行status_t VolumeBase::create(),源文件:system/vold/VolumeBase.cpp

status_t VolumeBase::create() {
    CHECK(!mCreated);
    
    mCreated = true;
    status_t res = doCreate();
    notifyEvent(ResponseCode::VolumeCreated,
            StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
    setState(State::kUnmounted);
    return res;
}

此时状态是“kUnmounted”。

3.然后执行mount操作,源文件:system/vold/VolumeBase.cpp;system/vold/PublicVolume.cpp

status_t VolumeBase::mount() {
    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
        return -EBUSY;
    }

    setState(State::kChecking);
    status_t res = doMount();
    if (res == OK) {
        setState(State::kMounted);
    } else {
        setState(State::kUnmountable);
    }

    return res;
}

调用PublicVolume的doMount(),并根据是否mount成功,设置状态“kMounted”和“kUnmountable”,

status_t PublicVolume::doMount() {
    // TODO: expand to support mounting other filesystems
    readMetadata();
    
	......
	
	// Use UUID as stable name, if available
    std::string stableName = getId();			//此处就是得到挂载路径的名称,修改名称主要就是修改这里
    if (!mFsUuid.empty()) {
        stableName = mFsUuid;
    }
    
    mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());

    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
    mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());

    setInternalPath(mRawPath);
    if (getMountFlags() & MountFlags::kVisible) {	//此处决定是否需要挂载到storage下面
        setPath(StringPrintf("/storage/%s", stableName.c_str()));
    } else {
        setPath(mRawPath);
    }

    if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create mount points";
        return -errno;
    }

	......
	
    return OK;
}

如果永远只会挂载一个U盘,那么直接修改上面代码里面的变量“stableName”为自己想要的名称就可以了;如果会挂HUB,会有多个U盘,那么继续看下面。

4.上面说了,mount可能会成功或者失败,但是不管是否成功,卸载U盘的时候都会执行status_t VolumeBase::destroy(),这个是和status_t VolumeBase::create()相对应的,源文件:system/vold/VolumeBase.cpp

status_t VolumeBase::destroy() {
    CHECK(mCreated);

    if (mState == State::kMounted) {
        unmount();
        setState(State::kBadRemoval);
    } else {
        setState(State::kRemoved);
    }

    notifyEvent(ResponseCode::VolumeDestroyed);
    status_t res = doDestroy();
    mCreated = false;
    return res;
}

根据mount状态,如果是“kMounted”,才执行unmount。

5.执行unmount,源文件:system/vold/VolumeBase.cpp;system/vold/PublicVolume.cpp

status_t VolumeBase::unmount() {
    if (mState != State::kMounted) {
        LOG(WARNING) << getId() << " unmount requires state mounted";
        return -EBUSY;
    }

    setState(State::kEjecting);
    for (const auto& vol : mVolumes) {
        if (vol->destroy()) {
            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
                    << " stacked above";
        }
    }
    mVolumes.clear();

    status_t res = doUnmount();
    setState(State::kUnmounted);
    return res;
}

调用PublicVolume的doUnmount(),

status_t PublicVolume::doUnmount() {	
    // Unmount the storage before we kill the FUSE process. If we kill
    // the FUSE process first, most file system operations will return
    // ENOTCONN until the unmount completes. This is an exotic and unusual
    // error code and might cause broken behaviour in applications.
    KillProcessesUsingPath(getPath());

    ForceUnmount(kAsecPath);

    ForceUnmount(mFuseDefault);
    ForceUnmount(mFuseRead);
    ForceUnmount(mFuseWrite);
    ForceUnmount(mRawPath);

    if (mFusePid > 0) {
        kill(mFusePid, SIGTERM);
        TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
        mFusePid = 0;
    }

    rmdir(mFuseDefault.c_str());
    rmdir(mFuseRead.c_str());
    rmdir(mFuseWrite.c_str());
    rmdir(mRawPath.c_str());

    mFuseDefault.clear();
    mFuseRead.clear();
    mFuseWrite.clear();
    mRawPath.clear();

    return OK;
}

以上就是需要用到的大致U盘挂载流程。

我的实现多个U盘定制挂载路径的办法就是,在U盘status_t PublicVolume::doMount()的时候,获得Disk类的私有变量“mEventPath”的值,然后判断是否是需要特殊名称的U盘,如果是需要特殊名称的U盘,使用统一名称前缀+数字编号,例如“USB1 ~ USB10”,总共支持10个U盘使用特殊名称,每当需要mount,先分配一个名称编号,并使用一个全局变量数组(自己定义)去标记对应的编号是否已经分配,已分配的编号,有可能mount成功,有可能失败,所以要在status_t VolumeBase::destroy()里面去清除已分配的编号的标记。

下面是参考代码:

status_t PublicVolume::doMount() {
    // TODO: expand to support mounting other filesystems
    readMetadata();

	......

	#if 0	//注释掉这里先
	// Use UUID as stable name, if available
    std::string stableName = getId();
    if (!mFsUuid.empty()) {
        stableName = mFsUuid;
    }
	#endif
	
	/***************************************************************************/
	std::string stableName = getId();
	unsigned char i;
	const char *Path = getEventPath().data();//这是获取Disk类私有变量mEventPath的方法,可以自己实现
	//下面这个判断是,主控USB控制器下挂载了一个HUB,然后在这个HUB下面挂载的,任何一个端口号大于1的设备
	//在这里都需要分配特殊的名字,下面字符串的含义,是kernel驱动在sysfs下的device路径,不同平台不一样
	if( strncmp(Path,"/devices/platform/mt_usb/musb-hdrc.0.auto/usb1/1-1/1-1.1",strlen("/devices/platform/mt_usb/musb-hdrc.0.auto/usb1/1-1/1-1.*")) >= 0 )
	{
		//在数组中查找没有使用的挂载路径编号
		for(i = 0; i < 10; i++)
		{
			//flag变量为0,表示没有使用
			if(MountPointFlag[i] == 0)		//这个自己定义的数组需要是全局静态数组,所有进程都使用同一个数组
				break;
		}
		
		if(i < 10)
		{
			stableName = StringPrintf("USB%d", (i + 1));
			MountPointFlag[i] = 1;		//置1,表示已经分配
		}
	}
	/***************************************************************************/
	
	//这里就是U盘挂载路径,有可能在/mnt/media_rw下,有可能在/storage下
    mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());

    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
    mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());

    setInternalPath(mRawPath);
    if (getMountFlags() & MountFlags::kVisible) {	//判断是要挂载到哪个目录下面
        setPath(StringPrintf("/storage/%s", stableName.c_str()));
    } else {
        setPath(mRawPath);
    }
    //上面这里代码有去调用setPath(),这里的路径名称会保存在VolumeBase类的私有变量“mPath”里

    if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create mount points";
        return -errno;
    }

	......

    return OK;
}
status_t VolumeBase::destroy() {
    CHECK(mCreated);

	/***************************************************************************/
	const std::string Path = getPath();//从VolumeBase类的私有变量“mPath”里获取这个设备的挂载路径
	//我这里是让U盘挂载到storage,所以判断storage下的名称
	if( strncmp(Path.data(),"/storage/USB",strlen("/storage/USB")) == 0 )
	{
		//得到路径名称编号值
		unsigned char clear = (unsigned char)atoi(Path.substr(strlen("/storage/USB")).c_str());
		//这里将自己定义的数组的指针传过来使用,先判断有没有传过来
		if(pMountPointFlag!= NULL)
		{
			if( (clear > 0) && (clear <= 10) )
				pMountPointFlag[clear - 1] = 0;		//清0,表示已不在使用
		}
	}
	/***************************************************************************/

    if (mState == State::kMounted) {
        unmount();
        setState(State::kBadRemoval);
    } else {
        setState(State::kRemoved);
    }

    notifyEvent(ResponseCode::VolumeDestroyed);
    status_t res = doDestroy();
    mCreated = false;
    return res;
}

以上就是修改办法和参考代码!

几点注意:
1.像SD卡读卡器插上去的时候,会new Disk类,但是并不会执行mount,我一开始是在Disk的构造函数里直接判断“mEventPath”的值,并分配的名称编号,这样导致SD读卡器会占用一个编号,并且不会释放,所以要在mount的时候,需要名称的时候再分配;同样,另外一种情况也是,执行了status_t VolumeBase::create(),但是没有mount;
2.在status_t VolumeBase::destroy()里面去释放分配的编号,而不是在status_t PublicVolume::doUnmount()里,因为如果mount不成功,并不会执行unmount,这样会导致编号无法释放;
3.有的时候会出现,进程执行了status_t VolumeBase::create(),接着会执行两次mount,比如,插了空的SD卡读卡器之后,再插一个损坏的SD卡,就会出现,这样就要在mount函数里去判断,同一个进程,第一次mount已经分配了编号,再去mount,就用之前已经分配的编号,不要再去新分配了,要不然destroy()的时候,只会清除最后一次mount分配的,之前的无法释放了!

你可能感兴趣的:(技术随笔)