Android设备间通过otg进行通信

首先这个需求的来源是在一个特殊的环境的下单机使用设备,不可连接网络,蓝牙,nfc等通信的情况下需要在两台设备间共享文件,其实这个系统本身就有这个功能,我们想让用户更加简介,一键操作,不需要进行更多的选择,就打算把这个功能提取出来。

首先看看系统的功能

 

通过otg传输文件的模式可以获取到另一台设备的目录,并且可以进行操作。基于这一功能开始研究,首先通过日志打印发现系统在插入usb设备时候

I/ActivityManager: START u0 {act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x11000000 cmp=com.android.mtp/.ReceiverActivity (has extras)} from uid 1000

I/ActivityManager: START u0 {act=android.intent.action.VIEW cat=[android.intent.category.DEFAULT] dat=content://com.android.mtp.documents/root/44 typ=vnd.android.document/root cmp=com.android.documentsui/.files.FilesActivity} from uid 10017

去源码里面搜了一下这个类,这是在MtpDocumentsProvider\MtpDocumentsProvider\src\com\android\mtp\ReceiverActivity.java

看一下源码,这里很简单

public class ReceiverActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
            final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
            try {
                final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
                provider.openDevice(device.getDeviceId());
                final String deviceRootId = provider.getDeviceDocumentId(device.getDeviceId());
                final Uri uri = DocumentsContract.buildRootUri(
                        MtpDocumentsProvider.AUTHORITY, deviceRootId);

                final Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
                intent.addCategory(Intent.CATEGORY_DEFAULT);
                this.startActivity(intent);
            } catch (IOException exception) {
                Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exception);
            }
        }
        finish();
    }
}

就是监听usb的广播后直接打开了一个activity,这个Activity正好就是FilesActivity,这个MtpDocumentsProvider是系统mtp对外提供的一个provider.

FilesActivity这个类是在系统的DocumentUi下面的一个类,通过查看代码发现是通过contentprovider来获取的数据,

这时候在去GitHub上浏览了一番发现了一个AndroidOtgUSBMtpSample,参考发现这个注意是对U盘这里的有作用,另外还有一个项目libaums可直接读取U盘进行操作,在两份开源项目的参考下自己再进行一番打磨成功实现改效果

首先通过一个广播接收器接收到

  IntentFilter intentFilter = new IntentFilter();
        //注册监听自定义广播
        intentFilter.addAction(READ_USB_DEVICE_PERMISSION);
        //设备插入
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        //设备拔出
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(usbmtpReceiver, intentFilter);

监听广播

  override fun onReceive(context: Context, intent: Intent) {
        when (intent.action) {
            //Usb设备已连接
            UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
                mUSBDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
                if (mUSBDevice != null) {
                    checkConnectedDevice()
                }
            }
            //设备已断开
            UsbManager.ACTION_USB_DEVICE_DETACHED -> {
                Log.e(TAG, "设备已断开")
            }
            //USB权限检测成功
            USBReceiverConstant.READ_USB_DEVICE_PERMISSION -> {
                mUSBDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
                //检查连接的设备
                checkConnectedDevice()
            }
        }
    }
 @RequiresApi(api = Build.VERSION_CODES.N)
    private fun checkConnectedDevice() {
        SmartLogUtils.showDebug("开始检查USB连接设备", true)
        //获取Android 设备管理器
        val usbManager =
            MyApplication.getContext().getSystemService(Context.USB_SERVICE) as UsbManager
        //获取所有已连接上的USB列表
        val allConnectedUSBDeviceList = usbManager.deviceList
        if (allConnectedUSBDeviceList.size <= 0) {
            Log.e(TAG, "未检测到任何USB连接设备")
            return
        } else {
            Log.e(TAG, "当前连接设备个数:" + allConnectedUSBDeviceList.size)
        }
        for (hasConnectedDevice in allConnectedUSBDeviceList.values) {
            mUSBDevice = hasConnectedDevice
            //遍历连接的设备接口
            for (i in 0 until mUSBDevice!!.interfaceCount) {
                //获取连接设备接口
                val usbInterface = mUSBDevice!!.getInterface(i)
                //检查设备连接接口 USB class for mass storage devices.
                if (usbInterface.interfaceClass == UsbConstants.USB_CLASS_VENDOR_SPEC) {
                    if (usbManager.hasPermission(mUSBDevice)) {
                        Log.e(TAG,"设备已获取权限")
                        if (mUSBDevice == null) {
                           Log.e(TAG,"设备为空")
                            return
                        }
                        //打开USB设备连接通道
                        val usbDeviceConnection = usbManager.openDevice(mUSBDevice)
                        //获取MTP设备
                        val mtpDevice = MtpDevice(mUSBDevice)
                        if (!mtpDevice.open(usbDeviceConnection)) {
                            Log.e(TAG, "设备打开失败")
                            return
                        }
                        val storageIds = mtpDevice.storageIds
                        for (storageId in storageIds) {
                            //读取MTP 设备上的数据
                            readAllFileFromMTPDevice(mtpDevice, storageId)
                        }
                        usbDeviceConnection?.close()
                        mtpDevice.close()
                    } else {
                        Log.e(TAG,"数码相机未获取权限")
                        该代码执行后,系统弹出一个对话框
                        val pendingIntent = PendingIntent.getBroadcast(MyApplication.getContext(),
                            0,
                            Intent(USBReceiverConstant.READ_USB_DEVICE_PERMISSION),
                            0)
                        usbManager.requestPermission(mUSBDevice, pendingIntent)
                    }
                } //end switch
            }
        }
    }

    private fun readAllFileFromMTPDevice(mtpDevice: MtpDevice?, storageId: Int) {
        Log.e(TAG,"readAllFileFromMTPDevice start")
        Log.e(TAG,"storageId=$storageId")
        //返回给定存储单元上所有对象的对象句柄列表,具有给定格式和父级。
        val objectHandles = mtpDevice!!.getObjectHandles(storageId, 0, -1)
        for (itemObjectHandles in objectHandles) {
            //每一个mtpObjectInfo 是一个图片对象 检索MtpObjectInfo对象
            val mtpObjectInfo = mtpDevice.getObjectInfo(itemObjectHandles)

            Log.e(TAG, "mHandle:${mtpObjectInfo.objectHandle}," +
                        "storageId:${mtpObjectInfo.storageId}," +
                        "mFormat:${mtpObjectInfo.format}," +
                        "mCompressedSize:${mtpObjectInfo.compressedSize}," +
                        "mParent:${mtpObjectInfo.parent}," +
                        "mAssociationType:${mtpObjectInfo.associationType}," +
                        "mAssociationDesc:${mtpObjectInfo.associationDesc}," +
                        "mName:${mtpObjectInfo.name}")
        }
       Log.e(TAG,"readAllFileFromMTPDevice end")
    }

1.首先通过UsbManager获取所以已连接的设备,这里我们可以将所以设备信息显示到界面上去选择,我目前只做了一个demo所以就直接打印获取内容了(我测试只连接了一台设备,其实可以连接多个设备),

2.再获取设备中的所以的接口(比如有的手机有内部存储和存储卡之类的),遍历获取每个接口的读写权限,

usbInterface.interfaceClass == UsbConstants.USB_CLASS_VENDOR_SPEC

这个是设备间识别到的接口类型,如果是U盘或者其他设备可能获取到的接口类型不同

 


    /**
     * USB class indicating that the class is determined on a per-interface basis.
     */
    public static final int USB_CLASS_PER_INTERFACE = 0;
    /**
     * USB class for audio devices.
     */
    public static final int USB_CLASS_AUDIO = 1;
    /**
     * USB class for communication devices.
     */
    public static final int USB_CLASS_COMM = 2;
    /**
     * USB class for human interface devices (for example, mice and keyboards).
     */
    public static final int USB_CLASS_HID = 3;
    /**
     * USB class for physical devices.
     */
    public static final int USB_CLASS_PHYSICA = 5;
    /**
     * USB class for still image devices (digital cameras).
     */
    public static final int USB_CLASS_STILL_IMAGE = 6;
    /**
     * USB class for printers.
     */
    public static final int USB_CLASS_PRINTER = 7;
    /**
     * USB class for mass storage devices.
     */
    public static final int USB_CLASS_MASS_STORAGE = 8;
    /**
     * USB class for USB hubs.
     */
    public static final int USB_CLASS_HUB = 9;
    /**
     * USB class for CDC devices (communications device class).
     */
    public static final int USB_CLASS_CDC_DATA = 0x0a;
    /**
     * USB class for content smart card devices.
     */
    public static final int USB_CLASS_CSCID = 0x0b;
     /**
     * USB class for content security devices.
     */
    public static final int USB_CLASS_CONTENT_SEC = 0x0d;
    /**
     * USB class for video devices.
     */
    public static final int USB_CLASS_VIDEO = 0x0e;
    /**
     * USB class for wireless controller devices.
     */
    public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0;
    /**
     * USB class for wireless miscellaneous devices.
     */
    public static final int USB_CLASS_MISC = 0xef;
    /**
     * Application specific USB class.
     */
    public static final int USB_CLASS_APP_SPEC = 0xfe;
    /**
     * Vendor specific USB class.
     */
    public static final int USB_CLASS_VENDOR_SPEC = 0xff;

3.通过UsbManager打开设备

val usbDeviceConnection = usbManager.openDevice(mUSBDevice)

再创建通过mUsbDevice创建MtpDevice对象,使用mtpDevice打开usb设备

val mtpDevice = MtpDevice(mUSBDevice)
if (!mtpDevice.open(usbDeviceConnection)) {
    Log.e(TAG, "设备打开失败")
    return
}

getObjectHandlers获取改接口下的文件,第二参数类型,0代表所有,还可以根据需要设置,最后一个参数为parentId,-1代表根目录下的搜索,0代表全部,具体的还可以根据自己获取的父目录id来获取

val objectHandles = mtpDevice!!.getObjectHandles(storageId, 0, -1)

这里的数据来源注意来自Mtp数据库

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值