gpt4 book ai didi

java - Android USB 附件模式无法使用 libusb 与主机 PC 读取/写入

转载 作者:太空狗 更新时间:2023-10-29 13:52:05 28 4
gpt4 key购买 nike

我们希望克服的基本问题是通过 USB 将数据从充当主机和电源的外部源 (PC) 传输到 Android 设备。由于 PC 将能够供电,并使产品的最终消费者更容易使用,我们希望避免使用 OTG 电缆,因此在配件模式下运行 android 设备。

我已经设法让安卓设备进入配件模式,并识别出有一个 USB 主机连接到它,甚至能够读取主机的控制数据。但是我似乎无法弄清楚实际的批量数据传输。

除了简单地设置初始连接之外,官方 android 文档似乎对这个主题提供的内容很少。

我已经从涉及该主题的几个来源(主要来自 here)中尽可能多地在线获取代码。 , 还有其他地方。

在 Android 代码中,usbThread 启动,甚至可以访问配件并正确打印出其制造商、型号等。但是,从 inputStream 读取总是会导致 IOError。

在主机 PC 上的 C++ 代码中,一切运行顺利并连接起来,直到传输测试,其中第一个批量传输超时并且报告传输了零字节。

我总是在运行我的 C++ 代码之前终止 ADB。 android 文档说要发送一个 set_configuration(1),我试过了但没有用。他们还谈到 ADB 有第二个接口(interface),我在输出中没有看到。我确实看到了另一种接口(interface)配置,但设置和使用它根本不起作用。

是否需要任何额外的设置?我是否缺少一些基本的 USB 协议(protocol)? Android 代码是否应该简单地对 inputStream 进行连续轮询?今天这个时代还支持配件模式吗?

相关代码如下所示

主 Activity .java

//<company> contains actual reversed Internet domain name
private static final String ACTION_USB_PERMISSION = "<company>.USB_PERMISSION";
UsbManager usbManager;
usbThread thread;
boolean usbStarted = false;
boolean resumeRan = false;
boolean threadCreated = false;
UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;

private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
fileDescriptor = usbManager.openAccessory(accessory);
if (fileDescriptor != null)
{
usbStarted = true;
FileDescriptor fd = fileDescriptor.getFileDescriptor();
inputStream = new FileInputStream(fd);
outputStream = new FileOutputStream(fd);
thread = new usbThread();
thread.start();
}
}

private final BroadcastReceiver permissionReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action))
{
Log.d(TAG, "Asking for usb permission");
synchronized (this)
{
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
{
if((!usbStarted) && resumeRan)
{
resumeRan = false;
Log.d(TAG, "Model: " + accessory.getModel());
openAccessory();
}
}
else
{
Log.d(TAG, "permission denied for device ");
}
}
}
}
};

Handler usbHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
byte res = msg.getData().getByte("val");
Log.d(TAG, "Byte " + Byte.toString(res));
}
};

private class usbThread extends Thread
{
boolean bueno = true;
usbThread()
{
if(!threadCreated)
{
threadCreated = true;
}
else
{
bueno = false;
Log.d(TAG, "Tried to open second thread");
}
}
public void run()
{
while(usbStarted && bueno)
{
byte[] buffer = new byte[10];
int ret = 0;
try
{
int av = inputStream.available();
ret = inputStream.read(buffer, 0, av);
} catch (IOException e)
{
e.printStackTrace();
//Log.d("MMI_IO_READ", "IOException on buffer read.");
}

if(ret > 0)
{
//Log.d("MMI_IO_READ", "Ret = " +ret);
for(int i = 0; i < ret; i++)
{
Message m = usbHandler.obtainMessage();
Bundle b = new Bundle();
b.putByte("val", buffer[i]);
m.setData(b);
usbHandler.sendMessage(m);
}
}
}
Log.d(TAG, "Thread Shutdown");
}
}

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbDetachReceiver , filter);
filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(permissionReceiver, filter);
}

@Override
protected void onResume()
{
super.onResume();

UsbAccessory[] accessoryList = usbManager.getAccessoryList();
if (accessoryList != null)
{
if(!usbStarted)
{
Log.d(TAG, "Resume, usb not started");
accessory = accessoryList[0];
Log.d(TAG, "Manufacturer " + accessory.getManufacturer());
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(ACTION_USB_PERMISSION), 0);
resumeRan = true;
usbManager.requestPermission(accessory, mPermissionIntent);
}
}
}

请注意,正如 this thread 所建议的那样除其他外,以及我自己的经验,USB_ACCESSORY_ATTACH Intent 永远不会触发。因此,USB 检测依赖于不稳定的 onResume 检查。

主机USB.cpp

#include <stdio.h>
#include <libusb-1.0/libusb.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <iostream>


#define ACCESSORY_VID 0x18d1
#define ACCESSORY_PID 0x2D00
#define ACCESSORY_ADB_PID 0x2D01
#define INTERFACE 0


const char* manufacturer = "PCHost";
const char* modelName = "PCHost1";
const char* description = "Description";
const char* version = "1.0";
const char* uri = "http://example.com";
const char* serialNumber = "666";

//static

static void error(int code)
{
fprintf(stdout, "%s\n", libusb_strerror((libusb_error)code));
}

static int shutdown(libusb_device_handle *handle)
{
if(handle)
{
libusb_release_interface(handle, INTERFACE);
libusb_close(handle);
}
libusb_exit(NULL);
return 0;
}


static int init()
{
libusb_init(NULL);
libusb_set_debug(NULL, 3); //Verbose debugging level

return 0;
}

// Send AOA specified introdction control information.
static int androidIntroduction(libusb_device_handle *handle)
{
unsigned char ioBuffer[2];
int devVersion;
int response;
response = libusb_control_transfer(
handle, //handle
0xC0, //bmRequestType
51, //bRequest
0, //wValue
0, //wIndex
ioBuffer, //data
2, //wLength
100 //timeout
);
fprintf(stdout,"Sent getProtocol\n");

if(response < 0)
{error(response);return -1;}

fprintf(stdout,"Response \n");

devVersion = ioBuffer[1] << 8 | ioBuffer[0];
if(!(devVersion == 1 || devVersion==2))
return -1;
fprintf(stdout,"Version Code Device: %d\n", devVersion);

usleep(1000);//sometimes hangs on the next transfer :(

response = libusb_control_transfer(handle,0x40,52,0,0,(unsigned char*)manufacturer,strlen(manufacturer)+1,0);
if(response < 0)
{error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,1,(unsigned char*)modelName,strlen(modelName)+1,0);
if(response < 0)
{error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,2,(unsigned char*)description,strlen(description)+1,0);
if(response < 0)
{error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,3,(unsigned char*)version,strlen(version)+1,0);
if(response < 0)
{error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,4,(unsigned char*)uri,strlen(uri)+1,0);
if(response < 0)
{error(response);return -1;}
response = libusb_control_transfer(handle,0x40,52,0,5,(unsigned char*)serialNumber,strlen(serialNumber)+1,0);
if(response < 0)
{error(response);return -1;}

fprintf(stdout,"Accessory Identification sent\n");
return 1;
}

// Send introduction information to given handle, then try to put it into
// accessory mode and catch it once it reconnects.
static libusb_device_handle* setupAccessory(libusb_device_handle *handle)
{
int response;
response = androidIntroduction(handle);
if(response < 0)
return NULL;
response = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
if(response < 0){error(response);return NULL;}

fprintf(stdout,"Attempted to put device into accessory mode\n");

libusb_device_handle *androidHandle;

int tries = 4;
while(true)
{
tries--;
if((androidHandle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VID, ACCESSORY_ADB_PID)) == NULL)
{
if((androidHandle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VID, ACCESSORY_PID)) == NULL)
{
if(tries < 0)
{
std::cout << "Could not..." << '\n';
return NULL;
}
}
else
{
break;
}
}else
{
break;
}
usleep(1000000);
}

return androidHandle;
}

//Find the first Bulk OUT Enpoint of the given device.
uint8_t findBulkOut (libusb_device *device)
{
libusb_config_descriptor *con_desc;
libusb_get_active_config_descriptor(device, &con_desc);
const libusb_interface *interfaceList = con_desc->interface;
uint16_t numInterface = con_desc->bNumInterfaces;
for(int j = 0; j<numInterface; j++)
{
libusb_interface interface = interfaceList[j];
const libusb_interface_descriptor *intDescList = interface.altsetting;
int numAlt = interface.num_altsetting;
for(int p = 0; p < numAlt; p++)
{
libusb_interface_descriptor intDesc = intDescList[p];
uint8_t numEnd = intDesc.bNumEndpoints;
const libusb_endpoint_descriptor *ends = intDesc.endpoint;
for(int k = 0; k < numEnd; k++)
{
libusb_endpoint_descriptor endpoint = ends[k];
uint8_t type = 0x03 & endpoint.bmAttributes;
uint8_t address = endpoint.bEndpointAddress;
switch (type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
break;
case LIBUSB_TRANSFER_TYPE_BULK:
if(!(address & LIBUSB_ENDPOINT_IN)) //LIBUSB_ENPOINT_OUT is simply 0000, can't AND that...
{
return address;
}
break;
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
break;
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
break;
}
}
}
}
}

//Basically findBulkOut, but with output for EndPoints.
void printEnds (libusb_device *device)
{
libusb_config_descriptor *con_desc;
libusb_get_active_config_descriptor(device, &con_desc);
const libusb_interface *interfaceList = con_desc->interface;
uint16_t numInterface = con_desc->bNumInterfaces;
for(int j = 0; j<numInterface; j++)
{
libusb_interface interface = interfaceList[j];
const libusb_interface_descriptor *intDescList = interface.altsetting;
int numAlt = interface.num_altsetting;
for(int p = 0; p < numAlt; p++)
{
libusb_interface_descriptor intDesc = intDescList[p];
uint8_t numEnd = intDesc.bNumEndpoints;
const libusb_endpoint_descriptor *ends = intDesc.endpoint;
fprintf(stdout, "Interface %d. altSetting %d. Num of endpoints: %d\n", p, intDesc.bInterfaceNumber, numEnd);
for(int k = 0; k < numEnd; k++)
{
libusb_endpoint_descriptor endpoint = ends[k];
uint8_t type = 0x03 & endpoint.bmAttributes;
uint8_t address = endpoint.bEndpointAddress;
fprintf(stdout, "Endpoint type ");
switch (type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
std::cout << "Control";
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
std::cout << "Isochronus";
break;
case LIBUSB_TRANSFER_TYPE_BULK:
std::cout << "Bulk";
break;
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
std::cout << "Interupt";
break;
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
std::cout << "Bulk Stream";
break;
}
std::cout << " ";
std::cout << (address & LIBUSB_ENDPOINT_IN ? "IN" : "OUT");
std::cout << '\n';
fprintf(stdout, "Address %04X\n", address);
}
}
}
}

// Go through all connected devices. If they are do not have Google PID
// and VID, try to find out if they are Android devices. If they (most likely)
// are, try to put them in accessory mode. If successful, return that handle
libusb_device_handle* getAndroidHandle()
{
libusb_device **list;
ssize_t cnt = libusb_get_device_list(NULL, &list);
ssize_t i = 0;
int err = 0;
if (cnt < 0)
error(0);
for (i = 0; i < cnt; i++)
{
fprintf(stdout,"\nAttempted index %d\n", (int)i);
libusb_device *device = list[i];
libusb_device_descriptor dev_desc;
libusb_get_device_descriptor(device, &dev_desc);
uint16_t VID = dev_desc.idVendor;
uint16_t PID = dev_desc.idProduct;
fprintf(stdout, "VID: %04X. PID: %04X\n", VID, PID);

libusb_device_handle *handle;
int response = libusb_open(device, &handle);
if(response < 0)
{error(response);continue;}
libusb_set_auto_detach_kernel_driver(handle, 1);

libusb_device_handle *androidHandle;
if(VID == ACCESSORY_VID && (PID == ACCESSORY_PID || PID == ACCESSORY_ADB_PID))
{
int r = androidIntroduction(handle);
if (r != 1)
continue;
androidHandle = handle;
}
else
{
androidHandle = setupAccessory(handle);
libusb_close(handle);

}
if (androidHandle)
{
libusb_free_device_list(list, 1);
std::cout << "\n\nAndroid Found:" << '\n';
printEnds(libusb_get_device(androidHandle));
return androidHandle;
}
}
libusb_free_device_list(list, 1);
return NULL;
}

//Try to send data.
static int transferTest(libusb_device_handle *handle)
{
const static int PACKET_BULK_LEN=64;
const static int TIMEOUT=5000;
int r,i;
int transferred;
usleep(1000000); //1s

//libusb_set_configuration(handle, 1);
r = libusb_claim_interface(handle, INTERFACE);
if(r < 0)
{error(r); return -1;}
fprintf(stdout, "Interface claimed, ready to transfer data\n");
// TEST BULK IN/OUT
usleep(100000);// 0.1s
uint8_t outAddress = findBulkOut(libusb_get_device(handle));
fprintf(stdout, "Trying to write to %04X\n", outAddress );
char answer[PACKET_BULK_LEN];
char question[PACKET_BULK_LEN];
for (i=0;i<PACKET_BULK_LEN; i++) question[i]=i;

// ***TIMES OUT HERE***
r = libusb_bulk_transfer(handle, outAddress, (unsigned char*)question, PACKET_BULK_LEN,
&transferred,TIMEOUT);
if (r < 0)
{
fprintf(stderr, "Bulk write error %d\n", r);
error(r);
fprintf(stderr, "Number of bytes written %d\n", transferred);
return r;
}
fprintf(stdout, "Wrote %d bytes", transferred);

/*r = libusb_bulk_transfer(handle, ENDPOINT_BULK_IN, (unsigned char*)answer,PACKET_BULK_LEN,
&transferred, TIMEOUT);
if (r < 0)
{
fprintf(stderr, "Bulk read error %d\n", r);
error(r);
return r;
}
fprintf(stdout, "Read %d bytes", r);

if (transferred < PACKET_BULK_LEN)
{
fprintf(stderr, "Bulk transfer short read (%d)\n", r);
error(r);
return -1;
}
printf("Bulk Transfer Loop Test Result:\n");
// for (i=0;i< PACKET_BULK_LEN;i++) printf("%i, %i,\n ",question[i],answer[i]);
for(i = 0;i < PACKET_BULK_LEN; i++)
{
if(i%8 == 0)
printf("\n");
printf("%02x, %02x; ",question[i],answer[i]);
}
printf("\n\n");
*/return 0;
}

int main (int argc, char *argv[])
{
fprintf(stdout, "OUT flag %04X IN flag %04X\n", LIBUSB_ENDPOINT_OUT, LIBUSB_ENDPOINT_IN);
fprintf(stdout, "Shifted in %04X\n", LIBUSB_ENDPOINT_IN >> 7);
if(init() < 0)
return -1;

libusb_device_handle *handle = getAndroidHandle();
if(!handle)
{
fprintf(stdout, "\nError setting up accessory\n");
shutdown(NULL);
return -1;
};
if(transferTest(handle) < 0)
{
fprintf(stdout, "\nError in transferTest\n");
shutdown(handle);
return -1;
}
shutdown(handle);
fprintf(stdout, "\nFinished\n");
return 0;
}

提前感谢您的任何回复! :)

编辑

似乎是 inputStream.available() 是罪魁祸首,但似乎无法正常工作(我从一些随机谷歌搜索中发现)。无论设置是否正确,它都会抛出 IOException。我还取消了 libusb_set_configuration 的注释并设置了一个轻微的延迟以确保它正常工作。

旁注:我不必关闭 ADB 服务器即可使 USB 连接正常工作。如果设备以 0x2D01 作为 PID self 介绍,那么 ADB 日志记录就可以正常工作!

最佳答案

这是我的 Java 代码,也许它会对你有所帮助。

如果在 AOA 模式后添加 libusb_set_configuartion,上面的 C++ 代码也可以工作。

Java

import android.app.PendingIntent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;


import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;


import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//import java.util.logging.Logger;


public class MainActivity extends Activity implements Runnable
{

private static final String ACTION_USB_PERMISSION = "com.examples.accessory.controller.action.USB_PERMISSION";
private final String TAG = "_ITE";

private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;
private boolean mPermissionRequestPending;

UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbReceiver, filter);

setContentView(R.layout.activity_main);

//enableControls(false);
}

@Override
public void onResume()
{
super.onResume();

Intent intent = getIntent();
if (mInputStream != null && mOutputStream != null) {
return;
}

UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory accessory = (accessories == null ? null : accessories[0]);
if (accessory != null)
{
if (mUsbManager.hasPermission(accessory))
{
openAccessory(accessory);
}
else
{
synchronized (mUsbReceiver)
{
if (!mPermissionRequestPending)
{
mUsbManager.requestPermission(accessory,mPermissionIntent);
mPermissionRequestPending = true;
}
}
}
}
else
{
Log.d(TAG, "mAccessory is null");
}
}

@Override
public void onPause() {
super.onPause();
closeAccessory();
}

@Override
public void onDestroy() {
unregisterReceiver(mUsbReceiver);
super.onDestroy();
}



private void openAccessory(UsbAccessory accessory)
{
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
mAccessory = accessory;
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryController");
thread.start();
Log.d(TAG, "accessory opened");
//enableControls(true);
} else {
Log.d(TAG, "accessory open fail");
}
}

private void closeAccessory() {
//enableControls(false);

try {
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
} catch (IOException e) {
} finally {
mFileDescriptor = null;
mAccessory = null;
}
}

/*
* This receiver monitors for the event of a user granting permission to use
* the attached accessory. If the user has checked to always allow, this will
* be generated following attachment without further user interaction.
*/
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
} else {
Log.d(TAG, "permission denied for accessory "+ accessory);
}
mPermissionRequestPending = false;
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null && accessory.equals(mAccessory)) {
closeAccessory();
}
}
}
};

/*
* Runnable block that will poll the accessory data stream
* for regular updates, posting each message it finds to a
* Handler. This is run on a spawned background thread.
*/
public void run() {
int ret = 0;
byte[] buffer = new byte[16384];
int i;

while (ret >= 0)
{
try
{
ret = mInputStream.read(buffer);
} catch (IOException e) {
break;
}




}
}


}

list

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<uses-library android:name="com.android.future.usb.accessory" />
<activity android:name=".MainActivity">
<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_ACCESSORY_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
</intent-filter>

<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" >
</meta-data>
</activity>
</application>

accessory_filter.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>
<usb-accessory model="YourModel" manufacturer="yourname" version="0.0.1"/>

</resources>

关于java - Android USB 附件模式无法使用 libusb 与主机 PC 读取/写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45420126/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com