android系统中修改sd卡挂载目录
这段时间,一直在弄Android系统上的SD挂载。那为什么要鼓捣Android系统的SD挂载呢?下面不得不从开始说起。
一、Android系统挂载位置SD卡现状。
Android系统默认挂载的外置SD与首选(primary)的外置存储设备的路径是并列。USB存储设备同样也与这两种存储设备并列。比如三种外置存储设备挂载路径如下:
1) 首选外置存储设备挂载路径:/mnt/sdcard(我的首选外置存储设备为系统自带的Nand,大小2G多)。
2) 外置SD卡的挂载路径:/mnt/external_sd(系统自带一个SD卡槽,供用户扩展)。
3) 外置USB存储设备挂载路径:/mnt/usb_storage(系统自带一个USB接口,供用户使用)。
而在Android4.0及Android4.1的系统中,仅提供了一个获取首选外置存储设备路径的API。对于除首选的外置存储设备外,Android并没有提供标准的方法给应用程序访问。在这种情况下,对于我的系统,首选外置存储设备的大小有限。这样对于较大的应用程序,比如一些导航软件来说,我可能会想到把一些较大的地图数据放到我的外置的SD卡中,而只有应用安装到我系统的内置存储设备或首选SD卡中。这时,应用程序没有标准的方法来访问我的外置SD卡。出现了矛盾。
二、解决矛盾的思路。
纵观当前流行的导航软件,它们会去/mnt/sdcard/external_sd中搜索自己认识的目录。而并不去/mnt/external_sd目录中搜索。这样有了解决方法,就是在/mnt/sdcard/目录下创建一个external_sd目录,这个目录与外置SD中的内容同步。这样就有了两种方法:
1) 在/mnt/sdcard/目录下创建一个软连接连接到/mnt/external_sd目录。
2) 在/mnt/sdcard/目录下创建一个external_sd目录,并将外置的SD卡挂载到/mnt/sdcard/external_sd目录下。
这两种方法首先说第一种方法,也就是创建软连接的方法。因为我的/mnt/sdcard挂载了首选的存储设备,这个存储设备格式化为vfat格式。而fat/vfat等这样的文件系统不支持symlink函数。所以放弃了这种方法。
而对于第二种方法,要做的的事情可就多了。如果想要了解Android系统中的存储设备挂载流程,可以在网上搜索以下。我这里提供一个我参考的文章:
http://blog.csdn.net/yihongyuelan/article/details/6926034
三、解决问题过程
下面说说我是怎么挂载我的外置SD卡到/mnt/sdcard/external_sd目录的过程。
1) 在系统源码中将所有/mnt/external_sd修改为/mnt/sdcard/external_sd。
你可以使用Android提供个resgrep、jgrep来个命令来搜索相关的资源。当然你也可以使用下面的命令来搜索相关的关键字:
Find . –name “*.*”| xargs grep “external_sd” –color -$@
这样其实就可以修改外置SD卡的挂载目录了,但是这里有一个问题。因为外置的SD卡的挂载是以首选存储设备挂载完成为基础。所以要首先确保首选存储设备已经挂载上,然后再去挂载外置SD卡。同理,卸载的时候要先确保外置SD卡先卸载。下面说明我对源码的修改过程及遇到的一些问题。
1. 修改SD卡的挂载路径。
Android系统存储设备的挂载路径主要由vold.fstab文件决定。它告诉系统怎么去挂载外部存储。在Android源码中有很多vold.fstab,比如我的源码中就有以下文件:
./device/samsumg/crespo/vold.fstab
./device/moto/wingray/vold.fstab
./device/rockchip/rk30sdk/vold.fstab
./system/core/rootdir/etc/vold.fstab
到底哪个是呢?当然是你所使用平台所相关的那个vold.fstab。我用的是Rockchip的CPU,所以与我相关的vold.fstab文件为./device/rockchip/rk30sdk/vold.fstab。对于/system/core/rootdir/etc/vold.fstab,我也不很清楚是做什么的。我认为是google给我们的一个参考的例子。有清楚的人请一定告诉我。在这个文件我把/mnt/external_sd(外置SD卡的挂载路径)修改为/mnt/sdcard/external_sd(Nand的挂载路径,意思是把外置SD卡挂载到Nand的目录下)。这样一个简单的修改,系统就知道怎么挂载外置的存储设备了。
现在系统虽然知道怎么挂载,我们还得给它创建好挂载点,否则它不会随便挂载的。就好像你知道怎么烧水,但是没有火炉子你就不知道把水壶放到什么地方烧。那么现在咱们就来创建挂载点。
修改以下文件:
./device/rockchip/rk30sdk/init.rk30board.rc
. /device/rockchip/rk30sdk/recovery.fstab
. /device/rockchip/rk30sdk/recovery/etc/init.rc
将上面文件中的/mnt/external_sd修改为/mnt/sdcard/external_sd。
为了小心一点,我在init.rk30board.rc文件中通过symlink命令创建一个软连接到/mnt/external_sd。这种情况symlink命令是支持的。因为你创建的软连接是在ext4文件系统上的。如下:
symlink /mnt/sdcard/external_sd /mnt/external_sd
好了,现在算是把挂载点创建好了,但是上层是不是有地方用到/mnt/external_sd这样的绝对路径呢?用Find . –name “*.*” | xargs grep “/mnt/external_sd ” –color -$@命令搜索一下,可以看到的确有地方用啊:
./device/rockchip/rk30sdk/overlay/frameworks/base/core/res/res/xml/storage_list.xml
./framework/bash/services/java/com/android/server/MountService.java
./packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java
同样,将上面文件中的/mnt/external_sd修改为/mnt/sdcard/external_sd。
至此,SD的挂载的目录已经被修改为/mnt/sdcard/external_sd。
虽然说挂载路径已经修改完成了,但是有可能系统并没有正确挂载上SD卡。下面说说我遇到的问题以及解决方法。
现在make以下,烧一遍,发现没有挂载上…为什么呢?下面来说这个问题。
1. 上面已经告诉系统如何挂载外置存储设备并且已经把挂载点创建好了,那为什么还挂载不上去呢?
这个可把我难住了。但是不怕。我们在源码中加trace,看看系统到底是如何挂载的,怎么他就不听话呢?经过80难终于发现了那个小bug。
案发现场:Nand挂载正常。/mnt/external_sd目录已经变成了软连接了,但是它的链接目标不存在。/mnt/sdcard/目录下不存在external_sd目录。
推理思路:难道挂载点没有创建成功?找到./system/vold/main.cpp,通过open()函数打开/mnt/sdcard/external_sd,发现系统在解析init.rk30board.rc文件后已经创建了/mnt/sdcard/external_sd目录。open成功。并且在./system/vold/main.cpp中while(1)之前这个目录都存在。那看来系统已经创建了这个挂载点了,但是不知道哪里把挂载点弄没了。顺着sd卡挂载流程加trace,终于让我找到了,原来系统创建的/mnt/sdcard/external_sd在系统挂载Nand(Nand的挂载路径为/mnt/sdcard)完成后就被覆盖了。知道这一点了,那么我就有办法了:在Nand挂载完成后用mkdir函数在/mnt/sdcard/目录下再创建一次SD卡的挂载路径。
编译,烧录。又有问题了…..
2. 上面可算是把挂载点创建好了,但是问题有出在哪里了呢?下面说明现场情况。
案发现场:Nand挂载正常,/mnt/external_sd软连接和/mnt/sdcard/external_sd都存在了。在系统启动后插入SD卡,系统可正常挂载。插着SD卡重启系统,SD卡挂载失败。各个目录仍然存在。为什么SD卡得不到系统的青睐?Nand到底与SD卡有何深仇大恨呢?非要置SD卡与死地?下面请继续收看…
那么大家请继续听我扯。经过详细的侦察发现,原来系统启动的时候,最先挂载是不是Nand,而是SD卡。这样怪不得人家系统,Nand是后妈养的,SD卡人家是亲生的。这样的顺序就导致本来已经挂载上的SD卡(挂载目录/mnt/sdcard/external_sd)被后来挂载的Nand覆盖。没办法,Nand是老大,没有老大(在storage_list.xml文件中android:primary="true"),老二就不应该出来。
既然这样,我就强制把你挂载的顺序调整一下。保证两个都能正常挂载上。那怎么调整呢?在./system/vold/VolumeManager.h文件中增加五个成员变量如下:
其中
sIsFlashMounted表示Nand是否已经挂载完成。如果挂载完成则为true,否则为false。
sIsHavaSD表示是否有SD请求挂载。如果有则为true,否则为false。
sIsSdMounted表示SD是否已经挂载完成,如果挂载完成则为true,否则为false。
sIsHavaUsb对于Usb的挂载,未使用到。
mMountingSD为请求挂载的SD的Volume类。
在./system/vold/VolumeManager.cpp文件修改mountVolume方法。
修改unmountVolume方法:
注意,如果只修改mountVolume不修改unmountVolume,在使用“打开USB存储设备”的功能时,会有问题。
至此,经过81难,SD的挂载终于成功。
一、 改变SD卡挂载路径带来的问题及解决方法:
1) 问题一:点击Setting-〉存储,Setting程序崩溃。
解决思路:经过trace跟踪发现,是framework/base/core/jni/android_os_StatFs.cpp层的statfs函数执行时没有权限造成的。这样需要给/mnt/sdcard/目录读和执行的权限。
解决方法:在./system/vold/Volume.cpp文件中的intVolume::mountVol() 函数中挂载Nand的时候,修改其权限掩码permMask 0702为0202.
2) 问题二:在Setting-〉存储选项中如果卸载Nand,Setting会崩溃。
解决思路:Nand本来就在系统内部。没有必要让用户卸载。所以可以把Setting-〉存储下NAND FLASH项下的“卸载存储设备”隐藏掉。
解决方法:修改
packages/apps/Settings/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java文件。将该文件中所有使用到mMountTogglePreference变量处的代码都加上如下条件:
if((mStorageVolume == null)||(!(mStorageVolume.getPath().equals("/mnt/sdcard"))))
如下图为其中一处修改:
也就是判断如果要检查的外置设备为Nand Flash,则不显示“卸载存储设备”选项。
3) 问题三: 如果使用了Android系统的“打开USB存储设备”功能,当所有设备都已USB存储设备打开后,再进入Setting-〉存储,Setting崩溃。
解决思路:通过trace(adb logcat)发现,这种情况下,Nand和SD卡都已经自动卸载下来。在/mnt/目录下为init.rc和init.rk30board.rc文件创建的目录。/mnt/sdcard和/mnt/sdcard/external_sd两个目录的用户组和权限都有以上两个文件中相关命令确定。这时由于/mnt/sdcard/目录权限为0000. statfs函数没有权限读取/mnt/sdcard/external的信息,造成Setting崩溃。
解决方法:需要给/mnt/sdcard目录读和执行的权限。修改下面两个文件:
./device/rockchip/rk30sdk/init.rc
./device/rockchip/rk30sdk/init.rk30board.rc
在mkdir/mnt/sdcard 和mkdir /mnt/sdcard/external_sd的地方,将权限修改为0500.
修改init.rc:
修改init.******board.rc:
4) 问题四:删除使用绝对路径/mnt/external_sd的RkExplorer.apk。因为Rockchip自己写的资源管理器的代码中使用了绝对路径,而当我们修改了SD卡的挂载路径后,这个APK总是崩溃。而在Android系统中,资源管理器这样的软件,完全可以让用户自己安装,所以我们可以删除这个软件。