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分配的,之前的無法釋放了!

發佈了11 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章