Android5.1新增挂载目录支持多个U盘

SoC : RK3288
Platform : Android 5.1

现状:开发板板载7个USB口(3路host),但是只能识别一路作为U盘挂载。
需求:支持三个U盘同时挂载

一、问题集锦
1.U盘不能自动挂载
将U盘插入开发板,在资源管理器中打开USB存储器显示”USB已被卸载”,问题源于fstab分区文件:

/devices/ff540000.usb /mnt/usb_storage vfat defaults        voldmanaged=usb_storage:auto -->HOST挂载
/devices/ff500000.usb /mnt/usb_storage vfat defaults        voldmanaged=usb_storage:auto --> HUB挂载

usb挂载总线地址和PCB设计有关,将对应的HOST—XXXX,和HUB–xxx两路打开,其他不需要的全部注释掉。
结论:上述操作后,只能识别一路U盘(自动挂载),另一路能识别,但是不会挂载,可以手动挂载。

2.Nand falsh挂载出错
手动点击设置–存储下的挂载nand flash出错:

D/VoldCmdListener(  156): volume mount /mnt/internal_sd
D/Vold    (  156):  volume status = 1----------------------
D/DirectVolume(  156): getDeviceNodes mPartIdx = 15, mDiskNumParts = 14.
E/Vold    (  156): deviceNodes[0]= 0xffffffff,[1]= 0x00000000,[2]= 0x00000000,[3]= 0x00000000
I/Vold    (  156): /dev/block/vold/4095:1048575 being considered for volume internal_sd,1
I/Vold    (  156):  
D/Vold    (  156): Volume internal_sd state changing 1 (Idle-Unmounted) -> 3 (Checking)
I/MountService(  455): onEvent:: raw= 605 Volume internal_sd /mnt/internal_sd state changed from 1 (Idle-Unmounted) to 3 (Checking) cooked =  605 Volume internal_sd /mnt/internal_sd state changed from 1 (Idle-Unmounted) to 3 (Checking)
I/MountService(  455): notifyVolumeStateChange::unmounted
I/MountService(  455): updating volume state checking
D/MountService(  455): volume state changed for /mnt/internal_sd (unmounted -> checking)
W/AudioTrack(  455): AUDIO_OUTPUT_FLAG_FAST denied by client
D/MountService(  455): sendStorageIntent Intent { act=android.intent.action.MEDIA_CHECKING dat=file:///mnt/internal_sd flg=0x4000000 (has extras) } to UserHandle{-1}
I/fsck_msdos(  156): ** /dev/block/vold/4095:1048575
I/fsck_msdos(  156): Can't open: No such file or directory
I/fsck_msdos(  156): fsck_msdos terminated by exit(8)
E/Vold    (  156): Filesystem check failed (unknown exit code 8)
E/Vold    (  156): /dev/block/vold/4095:1048575 failed to mount via VFAT (No such file or directory)
E/Vold    (  156): ---------set mSkipAsec to disable app2sd because mount Vfat fail for internal_sd, mountpoint =/mnt/internal_sd
I/ValidateNoPeople(  455): skipping global notification
D/NTFS-3G ( 1403): ntfs-3g: Failed to access volume '/dev/block/vold/4095:1048575'
D/NTFS-3G ( 1403): 
D/NTFS-3G ( 1403): ntfs-3g 2015.3.14 integrated FUSE 27 - Third Generation NTFS Driver
D/NTFS-3G ( 1403):      Configuration type 1, XATTRS are on, POSIX ACLS are off
D/NTFS-3G ( 1403): 
D/NTFS-3G ( 1403): Copyright (C) 2005-2007 Yura Pakhuchiy
D/NTFS-3G ( 1403): Copyright (C) 2006-2009 Szabolcs Szakacsits
D/NTFS-3G ( 1403): Copyright (C) 2007-2015 Jean-Pierre Andre
D/NTFS-3G ( 1403): Co
I/ntfs-3g (  156): ntfs-3g terminated by exit(11)
I/Vold    (  156):  /system/bin/ntfs-3g /dev/block/vold/4095:1048575 /mnt/internal_sd
E/Vold    (  156): ntfs-3g fail  WEXITSTATUS 11
E/Vold    (  156): /dev/block/vold/4095:1048575 failed to mount via VNTFS (No such file or directory)
E/Vold    (  156): Volume internal_sd found no suitable devices for mounting :(
D/Vold    (  156): Volume internal_sd state changing 3 (Checking) -> 1 (Idle-Unmounted)
W/Vold    (  156): Returning OperationFailed - no handler for errno 0
I/MountService(  455): onEvent:: raw= 605 Volume internal_sd /mnt/internal_sd state changed from 3 (Checking) to 1 (Idle-Unmounted) cooked =  605 Volume internal_sd /mnt/internal_sd state changed from 3 (Checking) to 1 (Idle-Unmounted)
I/MountService(  455): notifyVolumeStateChange::checking
I/MountService(  455): updating volume state for media bad removal nofs and unmountable
D/MountService(  455): volume state changed for /mnt/internal_sd (checking -> unmounted)
I/PackageManager(  455): Updating external media status from unmounted to unmounted
I/MemorySettings( 1081): Received storage state changed notification that /mnt/internal_sd changed state from unmounted to checking
D/MountService(  455): sendStorageIntent Intent { act=android.intent.action.MEDIA_UNMOUNTED dat=file:///mnt/internal_sd flg=0x4000000 (has extras) } to UserHandle{-1}
D/MediaPlaybackService(  965): EJECT/UNMOUNT received. current unmount action is at /mnt/internal_sd...
D/ExternalStorage(  694): After updating volumes, found 0 active roots
W/AppOps  (  455): Bad call: specified package media under uid 1000 but it is really 1013
D/AudioHardwareTiny(  163): start_output_stream
D/AudioHardwareTiny(  163): Device     : 0x400
D/AudioHardwareTiny(  163): SampleRate : 44100
D/AudioHardwareTiny(  163): Channels   : 2
D/AudioHardwareTiny(  163): Formate    : 0
D/AudioHardwareTiny(  163): PreiodSize : 2048
D/AudioHardwareTiny(  163): The current HDMI is DVI mode
E/AudioHardwareTiny(  163): pcm_open(PCM_CARD) failed: cannot open device '/dev/snd/pcmC0D0p': No such file or directory
I/MemorySettings( 1081): Received storage state changed notification that /mnt/internal_sd changed state from checking to unmounted
D/MediaScannerReceiver(  572): action: android.intent.action.MEDIA_UNMOUNTED path: /mnt/internal_sd
D/RKUpdateReceiver(  659): action = android.intent.action.MEDIA_UNMOUNTED
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Ringtones) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Pictures) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/DCIM) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Podcasts) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Notifications) returned 0
D/AudioHardwareTiny(  163): start_output_stream
D/AudioHardwareTiny(  163): Device     : 0x400
D/AudioHardwareTiny(  163): SampleRate : 44100
D/AudioHardwareTiny(  163): Channels   : 2
D/AudioHardwareTiny(  163): Formate    : 0
D/AudioHardwareTiny(  163): PreiodSize : 2048
D/AudioHardwareTiny(  163): The current HDMI is DVI mode
E/AudioHardwareTiny(  163): pcm_open(PCM_CARD) failed: cannot open device '/dev/snd/pcmC0D0p': No such file or directory
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Music) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Movies) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Android) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Download) returned 0
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd/Alarms) returned 0
I/DefContainer-JNI( 1344): error opening: /mnt/internal_sd: Permission denied
D/StorageMeasurement( 1081): getDirectorySize(/mnt/internal_sd) returned 0

问题在于fstab分区文件出错:

/devices/ff0f0000.rksdmmc/mmc_host/mmc          /mnt/internal_sd     vfat      defaults        voldmanaged=internal_sd:15,noemulatedsd

从fstab中发现/mnt/internal_sd挂载项为15号分区:

cat /proc/partitions
major minor  #blocks  name

 254        0     520912 zram0
 179        0    7634944 mmcblk0                                    总容量 8G
 179        1       4096 mmcblk0p1      uboot                       
 179        2       4096 mmcblk0p2      misc                            
 179        3      16384 mmcblk0p3      resource                        
 179        4      16384 mmcblk0p4      kernel  
 179        5      32768 mmcblk0p5      boot
 179        6      32768 mmcblk0p6      recovery
 179        7      53248 mmcblk0p7      backup
 179        8     131072 mmcblk0p8      cache
 179        9       4096 mmcblk0p9      kpanic
 179       10     524288 mmcblk0p10     system                      512M
 179       11       4096 mmcblk0p11     metadata            
 179       12    1048576 mmcblk0p12     userdata                    1G          --->  /data
 179       13      65536 mmcblk0p13     radical_update              64M
 179       14    5689344 mmcblk0p14     user                        5.4G        ----> /sdcard
   8        0   31266648 sda
   8        1   30541784 sda1

但是根本没有15号分区,而user是14号分区,因此将15改为14。

二、overlay机制
在修改支持多个U盘时,设置–存储下也需要做相应的修改来显示多个U盘的挂载详情,这就需要学习新的知识:overlay机制

Android overlay机制允许在不修改packages中apk的情况下,来自定义framework和package中的资源文件,实现资源的定制,来达到显示不同UI的目的。
比如,这里需要添加多个U盘的支持,首先在"设置"---"存储"下可以显示多个USB存储器,最后对每个显示的"USB存储器"对应不同的物理设备即可。
Product overlay和device overlay:有两种不同的overlay来决定最终的UI效果

其中DEVICE_PACKAGE_OVERLAYS += device/rockchip/common/overlay
PRODUCT_PACKAGE_OVERLAYS += device/rockchip/rk3288/overlay
由此可见是两个不同目录下的overlay,有何区别呢?
如果包含了同一资源,那么PRODUCT_PACKAGE_OVERLAYS将覆盖DEVICE_PACKAGE_OVERLAYS中的,定义如下build/core/package_internal.mk(Line 90):

# LOCAL_RESOURCE_DIR may point to resource generated during the build
need_compile_res :=
ifeq (,$(LOCAL_RESOURCE_DIR))
  LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
else
  need_compile_res := true
endif

package_resource_overlays := $(strip \
    $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \
      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))) \
    $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \
      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))

LOCAL_RESOURCE_DIR := $(package_resource_overlays) $(LOCAL_RESOURCE_DIR)

PRODUCT_PACKAGE_OVERLAYS和DEVICE_PACKAGE_OVERLAYS功能一样,只是优先级不一样。PRODUCT_PACKAGE_OVERLAYS优先于DEVICE_PACKAGE_OVERLAYS
在这里,两个定义的资源文件并不同(只关注USB存储的),因此两个都会被加载进去:

PRODUCT_PACKAGE_OVERLAYS  -->  storage_list_box.xml
DEVICE_PACKAGE_OVERLAYS   -->  storage_list.xml

那究竟使用的是哪一个呢?先查查加载storage_list.xml的地方。在frameworks/base/core/res/res/values/symbols.xml中有:

type="xml" name="storage_list" />
type="xml" name="storage_list_box" />

MountService会解析配置storage_list.xml,
在frameworks/base/services/core/java/com/android/server/MountService.java中:

private void readStorageListLocked() {
        mVolumes.clear();
        mVolumeStates.clear();

        Resources resources = mContext.getResources();
        boolean isTablet = "tablet".equals(SystemProperties.get("ro.target.product", "tablet"));
        int id = com.android.internal.R.xml.storage_list;
        int boxid = com.android.internal.R.xml.storage_list_box;
        XmlResourceParser parser = resources.getXml(isTablet ?id:boxid);
        AttributeSet attrs = Xml.asAttributeSet(parser);
        String enableUms= SystemProperties.get("ro.factory.hasUMS","false");

        try {
            XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
            while (true) {
                XmlUtils.nextElement(parser);

                String element = parser.getName();
                if (element == null) break;

                if (TAG_STORAGE.equals(element)) {
                    TypedArray a = resources.obtainAttributes(attrs,
                            com.android.internal.R.styleable.Storage);
                                    ...
                                    ...
                }
              ...
}

从代码中可以看到,首先判断ro.target.product的值,这里:ro.target.product=tablet
因此,加载的是storage_list.xml,如果ro.target.product=box,加载的就是storage_list_box.xml
同样的,在MountService.java中,readStorageListLocked()函数中也有与ro.factory.hasUMS相关的东西,有待研究。

因此,我们需要修改storage_list.xml,如下:


<StorageList xmlns:android="http://schemas.android.com/apk/res/android">
    <storage android:mountPoint="/storage/sdcard0"
             android:storageDescription="@string/storage_internal"
             android:primary="true"
             android:emulated="true"
             android:allowMassStorage="false"
             android:removable="true"
             android:mtpReserve="100" />    
    <storage android:mountPoint="/mnt/internal_sd"
             android:storageDescription="@string/storage_nand_flash"
             android:primary="false"
             android:emulated="false"
             android:allowMassStorage="true"
             android:removable="true"
             android:mtpReserve="100" />
    <storage android:mountPoint="/mnt/external_sd"
             android:storageDescription="@string/storage_sd_card"
             android:primary="false"
             android:allowMassStorage="true"
             android:removable="true"
             android:mtpReserve="100" />   
    <storage android:mountPoint="/mnt/usb_storage/USB_DISK0"
             android:storageDescription="@string/storage_usb0"
             android:primary="false"
             android:allowMassStorage="true"
             android:removable="true"
             android:mtpReserve="100" />   
    <storage android:mountPoint="/mnt/usb_storage/USB_DISK1"
             android:storageDescription="@string/storage_usb1"
             android:primary="false"
             android:allowMassStorage="true"
             android:removable="true"
             android:mtpReserve="100" />    
    <storage android:mountPoint="/mnt/usb_storage/USB_DISK2"
             android:storageDescription="@string/storage_usb2"
             android:primary="false"
             android:allowMassStorage="true"
             android:removable="true"
             android:mtpReserve="100" />   


    StorageList>

三、挂载配置修改
主要修改两个:fstab.rk30board.bootmode.emmc、init.rockchip.rc

sed -i '27s/^#//'     $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i '28s/^#//'     $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i '29s/^#//'     $OUT_DIR/root/fstab.rk30board.bootmode.emmc

sed -i '27s/^/#/'     $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i '28s/^/#/'           $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i '29s/^/#/'           $OUT_DIR/root/fstab.rk30board.bootmode.emmc

sed -i "s/\/devices\/ff580000.usb                           \/mnt\/usb_storage     vfat      defaults        voldmanaged=usb_storage:auto/\/devices\/ff580000.usb                           \/mnt\/usb_storage\/USB_DISK2     vfat      defaults        voldmanaged=usb_storage\/USB_DISK2:auto/g"   $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i "s/\/devices\/ff500000.usb                           \/mnt\/usb_storage     vfat      defaults        voldmanaged=usb_storage:auto/\/devices\/ff500000.usb                           \/mnt\/usb_storage\/USB_DISK1     vfat      defaults        voldmanaged=usb_storage\/USB_DISK1:auto/g"   $OUT_DIR/root/fstab.rk30board.bootmode.emmc
sed -i "s/\/devices\/ff540000.usb                           \/mnt\/usb_storage     vfat      defaults        voldmanaged=usb_storage:auto/\/devices\/ff540000.usb                           \/mnt\/usb_storage\/USB_DISK0     vfat      defaults        voldmanaged=usb_storage\/USB_DISK0:auto/g"   $OUT_DIR/root/fstab.rk30board.bootmode.emmc

sed -i -e "/mkdir \/mnt\/usb_storage 0000 system system/a\    mkdir /mnt/usb_storage/USB_DISK0 0000 system system"    $OUT_DIR/root/init.rockchip.rc
sed -i -e "/mkdir \/mnt\/usb_storage 0000 system system/a\    mkdir /mnt/usb_storage/USB_DISK1 0000 system system"    $OUT_DIR/root/init.rockchip.rc
sed -i -e "/mkdir \/mnt\/usb_storage 0000 system system/a\    mkdir /mnt/usb_storage/USB_DISK2 0000 system system"    $OUT_DIR/root/init.rockchip.rc


sed -i 's/on property:ro.target.product=box/on property:ro.target.product=tablet/g'    $OUT_DIR/root/init.rockchip.rc
sed -i '/export THIRD_VOLUME_STORAGE \/mnt\/USB_DISK0/c\    mkdir \/mnt\/usb_storage\/USB_DISK0 0755 system system'  $OUT_DIR/root/init.rockchip.rc
sed -i '/mkdir \/mnt\/usb_storage 0755 system system/c\    mkdir \/mnt\/usb_storage\/USB_DISK1 0755 system system'   $OUT_DIR/root/init.rockchip.rc
sed -i -e "/mkdir \/mnt\/usb_storage\/USB_DISK1 0755 system system/a\    mkdir /mnt/usb_storage/USB_DISK2 0755 system system"   $OUT_DIR/root/init.rockchip.rc

你可能感兴趣的:(RK3288)