【问题标题】:Android USB Accessory mode can't read/write with host PC using libusbAndroid USB 附件模式无法使用 libusb 与主机 PC 进行读/写
【发布时间】:2017-07-31 15:41:57
【问题描述】:

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

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

除了简单地设置初始连接之外,官方的 android 文档似乎提供的很少。

我从涉及该主题的少数资源中尽可能多地在线挽救了代码,主要来自here,但也来自其他地方。

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

在主机 PC 上的 c++ 代码中,一切运行顺利并连接到 transferTest,第一次批量传输超时并且报告传输零字节。

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

是否需要任何额外的设置?我是否缺少一些基本的 USB 协议? Android 代码是否应该简单地对 inputStream 进行连续轮询?在这个时代,配件模式是否仍然受到支持?

相关代码如下所示

MainActivity.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 意图永远不会触发。因此,USB 检测依赖于不稳定的 onResume 检查。

HostUSB.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 进行自我介绍,则 ADB 日志记录工作正常!

【问题讨论】:

    标签: java android c++ usb libusb


    【解决方案1】:

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

    如果您在 AOA 模式后添加 libusb_set_configuation,上述 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;
                }
    
    
    
    
            }
        }
    
    
    }
    

    清单

    <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>
    

    【讨论】:

    • 感谢您的回复!我在周末之前让它工作了。即使连接设置正确,似乎“inputStream.avaliable()”也会引发 IOException。显然该功能不起作用?我确实也添加了 libusb_set_configuration,这可能也有帮助!你知道关于 ATTACH/DETACH 事件的任何事情吗? DETACH 似乎也没有开火……
    【解决方案2】:

    这不是代码问题,您无法在没有 OTG 的情况下进行批量数据传输。以 10/15 美元的价格购买一个 OTG 适配器,然后您就可以做到这一点,但这取决于设备及其使用年限。一般来说,如果没有 adb 和 OTG 适配器/电缆,它们将不会充当主机。我从未听说过另一种方法,我认为您最终会得到一个不起作用的代码,但如果您确实找到了方法,请告诉我。

    【讨论】:

    • 这是不正确的,因为我的代码现在似乎可以使用手机附带的标准电缆正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 2012-12-15
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 2013-02-10
    • 2014-04-01
    相关资源
    最近更新 更多