AS Wi-Fi调试(Android12+)
在调试USB设备之后,唯一的Type-C接口就被占用了。这个时候有无线调试就可以解决。可以通过TCPIP连接调试,也可以使用Wi-Fi配对调试。下面讲解Wi-Fi调试的配置过程:
- Wi-Fi连接:电脑和手机笔记连接在同一个路由器
- 打开AS的Wi-Fi调试配对二维码
#Android Studio
Run Devices➡️ Pair devices over Wi-Fi➡️ Pair using QR code
使用Android Studio Wi-Fi调试
- 打开手机无线调试功能
#Android 12+手机
设置 ➡️ 其他/更多设置 ➡️ 开发者选项 ➡️ 调试-无线调试 ➡️ 开启 ➡️ 使用二维码配对设备
OTG模式
Oppo、Vivo
Oppo、Vivo想要调试USB设备,需要先打开OTG模式,否则手机无法识别外接USB设备。
设置 ➡️ 其他/更多设置 ➡️ OTG连接
华为、LG、Google等
大部分的厂商都是默认打开OTG的,可以直接插入USB设备访问
示例代码
Manifest配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.usb.demo">
<uses-feature android:name="android.hardware.usb.host" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UsbDemo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
</manifest>
Device Filter配置
资源文件保存在 res/xml/device_filter.xml 中,并指定应过滤具有指定属性的所有USB 设备
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device
product-id="1234"
vendor-id="5678" />
</resources>
监听USB设备添加/删除
使用BroadcastReceiver监听USB事件
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ACTION_USB_PERMISSION == intent.action) {
synchronized(this) {
Lg.i("Usb Action")
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
exchangeData(this)
}
} else {
Lg.i("permission denied for device ${device?.deviceName}")
}
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
device?.apply {
// call your method that cleans up and closes communication with the device
}
}
}
}
监听设备
Android手机一般只有一个USB接口,只需要获取到device就执行监听即可。
private fun enumerateDevices() {
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val deviceList = manager.deviceList
Lg.i("usb devices count=${deviceList.size}, list: ${deviceList.values}")
if (deviceList.size < 1) {
Lg.e("没有USB设备")
return
}
val filter = IntentFilter(ACTION_USB_PERMISSION)
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
registerReceiver(usbReceiver, filter)
device = deviceList.values.first()
Lg.i("product Name= ${device.productName}")
val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
manager.requestPermission(device, permissionIntent)
}
收发数据
使用Endpoint发送数据,一般Endpoint是成对出现的,一收一发。
private fun exchangeData(device: UsbDevice) {
val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
//一般为1个USB口
Lg.i("USB Interface count=${device.interfaceCount}")
device.getInterface(0).also { usbInterface ->
//一般为2个端点,一收一发
Lg.i("endpointCount=${usbInterface.endpointCount}")
usbManager.openDevice(device)?.apply {
usbInterface.getEndpoint(1).also { endpoint ->
Lg.i("endpointNumber=${endpoint.endpointNumber}")
claimInterface(usbInterface, forceClaim)
val bytes = byteArrayOf(0x01, 0x02, 0x03, 0x04)
//发送数据
bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) //do in another thread
}
usbInterface.getEndpoint(0).also { endpoint ->
//创建buffer接收数据
val responses = ByteArray(size = 64)
bulkTransfer(endpoint, responses, responses.size, TIMEOUT)
Lg.i("responses= ${responses.toHex()}")
}
}
}
}
fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
例子中用到的Const
companion object {
private const val TIMEOUT = 30
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
}