Android OTG U盘开发相关文档

Android OTG  U盘相关文档
                                                      
Media Team

                                                      2017-03-02

目录

Android OTG  U盘相关文档.. 1

1. 使用OTG模 式 播放U盘音乐… 4

1.1需要完成的任务列表.. 4

1.2代码参照… 4

2. 预研Android USB Digital Audio. 9

参考文档.. 9

车机中,可能需要对支持以下几种音频输入模式,并且拥有优先级,根据自已的使用经验播放音乐的优先级如下

1.    苹果底座

2.    Aux-in

3.    U盘

4.    蓝牙

注:此文档只记录U盘的使用方式

1. 使用OTG模 式 播放U盘音乐
1.1需要完成的任务列表
1.    需要的权限

2.    监控OTG USB的插入和弹出

3.    得到U盘挂载的路径

4.    通知系统媒体管理器扫描系统文件

5.    通过ContentProvider得到U盘上所有音频文件的ID3标签信息(包括,标题,艺术家,封面),显示 在播放器界面上,并进行播放

6.    处理USB的弹出事件

1.2代码参照
1.    需要的权限:

经过测试需要系统签名,

Mainifest添加 android:sharedUserId=”android.uid.system”

使用权限:

<uses-permission
android:name=”android.permission.WRITE_MEDIA_STORAGE” />

使用功能:

<uses-feature
android:name=”android.hardware.usb.host” />

 <uses-permission android:name=”android.permission.USB_PERMISSION” />

 

2.    全局的广播接收器

<!–USB–>

        <receiver android:name=”.receiver.USBBroadcastReceiver”>

            <intent-filter>

                <action android:name=”android.hardware.usb.action.USB_DEVICE_ATTACHED” />

                <action android:name=”android.hardware.usb.action.USB_DEVICE_DETACHED” />

            </intent-filter>

       
</receiver>

            android.hardware.usb.action.USB_DEVICE_ATTACHED : USB的插入

android.hardware.usb.action.USB_DEVICE_DETACHED
: USB弹出

 

具体的BroadcastReceiver

public class USBBroadcastReceiver extends
BroadcastReceiver {
    @Override
    public void onReceive(final
Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED))
{
            Toast.makeText(context, “USB插入”, Toast.LENGTH_SHORT).show();
            Intent
scanService = new Intent(context, ScanUSBService.class);
            scanService.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
            context.startService(scanService);
        } else
if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
{
            Toast.makeText(context, “USB拨出”, Toast.LENGTH_SHORT).show();
            intent.setAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
            Intent
scanService = new Intent(context, ScanUSBService.class);
            scanService.setAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
            context.startService(scanService);
        }
    }

}

 

            Service中处理插入和弹出事件写法:

@Override

protected void onHandleIntent(Intent intent) {

    if (intent == null)

        return;

    String action = intent.getAction();

    NextevLog.d(“mp3”, “action=” + action);

    if (TextUtils.isEmpty(action))

        return;

    if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED) || action.equals(Intent.ACTION_BOOT_COMPLETED)) {

        MediaCenterApplication.getInstance().mHandler.postDelayed(new Runnable() {

            @Override

            public void run() {

                onInsertUSB();

            }

        }, 3000);

 

    } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {

        onEjectUSB();

    }

 

}
 

3.    得到U盘的挂载路径

暂时无法通过公开的API得到U盘的挂载路径,只能通过Framework的私有API得到路径,需要通过系统层去做

com.android.systemui. StorageNotification

系统层私有@Hide API : VolumeInfo

type=PUBLIC diskId=disk:8_16
partGuid=null mountFlags=0 mountUserId=0

state=MOUNTED

fsType=vfat fsUuid=A884-8764
fsLabel=usbdisk

path=/mnt/media_rw/A884-8764 internalPath=/mnt/media_rw/

 

 

 

 

以下是VolumeInfo中的路径的字段

public final String id;

public final int type;

public final DiskInfo disk;

public final String partGuid;

public int mountFlags = 0;

public int mountUserId = -1;

public int state = STATE_UNMOUNTED;

public String fsType;

public String fsUuid;

public String fsLabel;

public String path;

public String internalPath;  //路径地址
 

注:在三星平板上没有系统权限不能动态得到路径,使用固定的路径

String mUSBRootDirPath =
“/mnt/media_rw/A884-8764/”;

 

4.    得到U盘 中所有文件的url,并添加到系统媒体管理器中扫描

 

private void getMp3File(final List<File> list, File file) {

    file.listFiles(new FileFilter() {

        @Override

        public boolean accept(File file) {

            String name = file.getName();

            NextevLog.d(“mp3”, “扫描文件” + name);

            int i = name.lastIndexOf(“.”);

            if (i != -1) {

                name = name.substring(i);

                if (name.equalsIgnoreCase(“.mp3”)) {

                    list.add(file);

                    return true;

                }

            } else if (file.isDirectory()) {

                getMp3File(list, file);

            }

            return false;

        }

    });

}
void scanOneFile() {

    if (files.size() < 1)

        return;

    String[] mimeTypes = new String[]{“audio/mpeg”};

    MediaScannerConnection.scanFile(this, new String[]{files.get(i).getAbsolutePath()}, mimeTypes, new MediaScannerConnection.OnScanCompletedListener() {

        @Override

        public void onScanCompleted(String path, Uri uri) {

            if (i == files.size() – 1) {

                onScanFinished();

            } else {

                i++;

                scanOneFile();

            }

        }

    });

}
 

scanMusic()

public List<TrackItem> scanMusic() {

    List<TrackItem> list = new ArrayList<>();

    Cursor cursor = this.getContentResolver().query(

            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,

            MediaStore.Audio.Media.DEFAULT_SORT_ORDER);

    if (cursor == null) {

        return list;

    }

 

 

    while (cursor.moveToNext()) {

        // 是否为音乐

        int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));

        NextevLog.e(“mp3”, “file.isMusic=” + isMusic);

        if (isMusic == 0) {

            continue;

        }

        long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID));

        String title = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));

        String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));

        String unknown = this.getString(R.string.unknown_album_name);

        artist = artist.equals(“<unknown>”) ? unknown : artist;

        String album = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));

        long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));

        String uri = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));

        long albumId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));

        String coverUri = getCoverUri(this, albumId);

        String fileName = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));

        long fileSize = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));

        String year = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.YEAR)));

        String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));

        // depend on mediaType add music

        if (filePath.contains(“mnt/media_rw/A884-8764”)) {

            TrackItem music = new TrackItem();

            music.setId(id);

            music.setType(SoundType.LOCAL_TRACK.intValue());

            music.setTrackTitle(title);

            music.setArtistName(artist);

            music.setAlbumName(album);

            music.setDuration(duration);

            music.setLocalPath(uri);

            music.setCoverUrl(coverUri);

            list.add(music);

        }

    }

    cursor.close();

    return list;

}
 

 

 

2. 预研Android USB Digital Audio
            如果想让车机上使用苹果的lighting的底座接口,需要两个条件

条件一:需要苹果官方的授权,市面上出售的支持苹果底座的音箱,包括飞利普和部分汽车的车机系统(like 别克)),有了苹果的授权,可以USB连上苹果手机后,直接播放苹果手机里的音乐,优先极高于蓝牙连接

条件二:   开发针对Lighting底座的协议的硬件  

 

另外,Android设备是可以直接通过otg来当成一个音频输入或者输出设备的,在

在三星平板上通过OTG线连接苹果手机尝试通过Android输出苹果的音频,尝试失败,应该是不能如此简单的连接,因为与我们车机硬件不匹配,不再进入下一步更深入的调研

 

参考文档
1.    https://developer.android.com/guide/topics/connectivity/usb/host.html

2.    https://source.android.com/devices/audio/usb.html

3.    在 Nexus 上使用 USB 主机模式录制和播放音频

https://support.google.com/nexus/answer/6127700

Share your thoughts