Android USB Host使用详解之三:U盘操作实例

U盘命令传输流程参看:U盘bulk-only传输文档

Android中,在USB Host Mode下,U盘可以使用的传输数据(或命令)的函数有

bulkTransfer(UsbEndpoint endpoint, byte[] buffer,int length, int timeout)controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout) 
 bulkTransfer()用于传输CBW命令(bulk-out端点),主设备传输数据到从设备(bulk-out端点),从设备传输数据到主设备(bulk-in端点),从设备发送的CSW响应(bulk-in端点)。   

controlTransfer()用于传输控制命令(控制端点),包括reset和get max lnu等命令。

下面U盘操作实例进行讲解:

1)布局文件

    
第一个按钮发送reset命令,第二个按钮接收max lnu结果,第三个按钮发送read format capacities的CBW命令,并将获取的结果在文本视图中显示。

2)AndroidManifest.xml和device_filter.xml

<?xml version="1.0" encoding="utf-8"?>                                                                                                                                       
<?xml version="1.0" encoding="utf-8"?>        
这个就不用解释了。

3)成员变量和控件初始化

private final String TAG = "++MainActivity++";private static final String ACTION_USB_PERMISSION =    "com.android.example.USB_PERMISSION";private Button mBtnReset;private Button mBtnGetMaxLnu;private Button mBtnSendCommand;private TextView mTvInfo;private UsbManager mUsbManager;private UsbDevice mUsbDevice;private UsbEndpoint mEndpointIn;private UsbEndpoint mEndpointOut;private UsbDeviceConnection mConnection = null;private final int mVendorID = 3544;private final int mProductID = 8199;private boolean mDetachedRegistered = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                mBtnReset = (Button)findViewById(R.id.btn_reset);        mBtnGetMaxLnu = (Button)findViewById(R.id.btn_get_max_lnu);        mBtnSendCommand = (Button)findViewById(R.id.btn_send_command);        mTvInfo = (TextView)findViewById(R.id.tv_info);                mBtnReset.setOnClickListener(this);        mBtnGetMaxLnu.setOnClickListener(this);        mBtnSendCommand.setOnClickListener(this);                mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);    }
   4)找到相应的设备并建立连接   

@Overrideprotected void onResume() {super.onResume();// 获取启动Activity的USB设备Intent intent = getIntent();String action = intent.getAction();             mUsbDevice = null;if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {mUsbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);if(mVendorID != mUsbDevice.getVendorId() || mProductID != mUsbDevice.getProductId()) {mUsbDevice = null;}} if(mUsbDevice == null) {        refreshDevice();        }if(mUsbDevice == null) { // 插入设备自动启动应用程序,自动获取获取permissionLog.d(TAG, "Please insert USB flash disk!");// 手机请使用ToastToast.makeText(this, "Please insert USB flash disk!", Toast.LENGTH_SHORT).show();finish();return;} // 判断是否拥有权限if(!mUsbManager.hasPermission(mUsbDevice)) {PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);        registerReceiver(mPermissionReceiver, filter);        mUsbManager.requestPermission(mUsbDevice, permissionIntent);                } else {        Log.d(TAG, "Correct device!");            Toast.makeText(MainActivity.this, "Correct device!", Toast.LENGTH_SHORT).show();        makeConnection();        }        registerReceiver(usbDetachedReceiver, usbDetachedFilter); // 注册弹出通知     mDetachedRegistered = true;}
refreshDevice()是手动打开软件是获取设备的方法;然后是获取权限,即通信前必须获得使用者的允许;makeConnection()是初始化bulk-in、bulk-out端点,并建立连接;最后是注册U盘弹出接收器。

5)refreshDevice()

// 启动程序前已经插入了设备,需要从设备列表中获取private void refreshDevice() {HashMap deviceList = mUsbManager.getDeviceList();Iterator deviceIterator = deviceList.values().iterator();while(deviceIterator.hasNext()){    mUsbDevice = deviceIterator.next();    if(mVendorID == mUsbDevice.getVendorId() && mProductID == mUsbDevice.getProductId()) {        break;        } else {        mUsbDevice = null;        }}}
6)注册弹出通知,弹出设备时关闭程序

private IntentFilter usbDetachedFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);        private BroadcastReceiver usbDetachedReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);if(device != null) {// 确保弹出的设备为指定的if(mVendorID == device.getVendorId() && mProductID == device.getProductId()) {        mUsbDevice = null;        finish();}}}    };
7)建立连接:makeConnection()

    private void makeConnection() {    if(mUsbDevice == null) {Log.d(TAG, "Please insert USB flash disk!");Toast.makeText(this, "Please insert USB flash disk!", Toast.LENGTH_SHORT).show();        finish();return;}    // U盘接口个数为1if(mUsbDevice.getInterfaceCount() != 1) {Log.d(TAG, "Not a USB flash disk!");Toast.makeText(this, "Not a USB flash disk!", Toast.LENGTH_SHORT).show();finish();return;}UsbInterface intf = mUsbDevice.getInterface(0);// U盘接口0可获取的端点数为2if(intf.getEndpointCount() != 2) {Log.d(TAG, "Not a USB flash disk!");Toast.makeText(this, "Not a USB flash disk!", Toast.LENGTH_SHORT).show();finish();return;} else {mEndpointIn = intf.getEndpoint(0); // Bulk-In端点mEndpointOut = intf.getEndpoint(1); // Bulk_Out端点}if (mUsbDevice != null) {            UsbDeviceConnection connection = mUsbManager.openDevice(mUsbDevice);            if (connection != null && connection.claimInterface(intf, true)) {            Log.d(TAG, "Make connection succeeded!");            Toast.makeText(this, "Make connection succeeded!", Toast.LENGTH_SHORT).show();                mConnection = connection;            } else {            Log.d(TAG, "Make connection failed!");            Toast.makeText(this, "Make connection failed!", Toast.LENGTH_SHORT).show();                mConnection = null;                finish();            }         }}
8)各按键的命令处理

@Overridepublic void onClick(View v) {switch(v.getId()) {case R.id.btn_reset :reset();break;case R.id.btn_get_max_lnu :getMaxLnu();break;case R.id.btn_send_command :sendCommand();break;default :break;}}
9)reset()发送reset命令

private void reset() {synchronized (this) {            if (mConnection != null) {            String str = mTvInfo.getText().toString();                        // 复位命令的设置有USB Mass Storage的定义文档给出            int result = mConnection.controlTransfer(0x21, 0xFF, 0x00, 0x00, null, 0, 1000);                if(result < 0) { // result<0说明发送失败                Log.d(TAG, "Send reset command failed!");                str += "Send reset command failed!\n";    } else {     Log.d(TAG, "Send reset command succeeded!");    str += "Send reset command succeeded!\n";    }                    mTvInfo.setText(str);            }        }}
reset命令通过控制端点发送,如果发送成功,result的值是大于或等于0的。

10)getMaxLnu()获取最大的LNU

private void getMaxLnu() {synchronized (this) {            if (mConnection != null) {            String str = mTvInfo.getText().toString();                        // 接收的数据只有1个字节            byte[] message = new byte[1];            // 获取最大LUN命令的设置由USB Mass Storage的定义文档给出                int result = mConnection.controlTransfer(0xA1, 0xFE, 0x00, 0x00, message, 1, 1000);                if(result < 0) {                Log.d(TAG,  "Get max lnu failed!");    str += "Get max lnu failed!\n";    } else {    Log.d(TAG, "Get max lnu succeeded!");        str += "Get max lnu succeeded!\nMax LNU : ";    for(int i=0; i
LNU是什么东西自己百度,LNU用于后面的bulk传输的参数设置。

11)sendCommand()发送read format capacities命令

private void sendCommand() {String str = mTvInfo.getText().toString();byte[] cmd = new byte[] {(byte) 0x55, (byte) 0x53, (byte) 0x42, (byte) 0x43, // 固定值(byte) 0x28, (byte) 0xe8, (byte) 0x3e, (byte) 0xfe, // 自定义,与返回的CSW中的值是一样的(byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, // 传输数据长度为512字节(byte) 0x80, // 传入数据(byte) 0x00, // LNU为0,则设为0(byte) 0x01, // 命令长度为1(byte) 0x23, (byte) 0x00, (byte) 0x00, (byte) 0x00, // READ FORMAT CAPACITIES,后面的0x00皆被忽略(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};int result = mConnection.bulkTransfer(mEndpointOut, cmd, cmd.length, 1000);if(result < 0) {        Log.d(TAG,  "Send command failed!");str += "Send command failed!\n";} else {Log.d(TAG, "Send command succeeded!");str += "Send command succeeded!\n";}byte[] message = new byte[24];//  需要足够的长度接收数据result = mConnection.bulkTransfer(mEndpointIn, message, message.length, 1000);if(result < 0) {        Log.d(TAG,  "Receive message failed!");str += "Receive message failed!\n";} else {Log.d(TAG, "Receive message succeeded!");str += "Receive message succeeded!\nFormat capacities : \n";for(int i=0; i
注:上面这段代码直接嵌入在UI线程中的,发送和读取时都会阻塞UI线程,请自行开辟线程
到这里就不得不提一下U盘发送命令的顺序

以Android手机和U盘举例,首先准备工作做好(已建立连接,bulk-in和bulk-out端点),然后Android手机发送一个CBW命令给U盘,告诉U盘要做什么:

      (1)如果是发送数据给U盘,那么U盘准备好接收数据,紧接着Android手机发送数据,U盘接收数据后,返回一个CSW给Android手机,告诉接收数据是否成功,这种情况,对于开发者来说,首先发送CBW命令,判断是否发送成功,如果发送成功,紧接着发送数据(注意时间),发送数据后接收CSW,判断是否成功.......

      (2)如果是要从U盘获取数据,那么U盘准备好数据发送给Android手机,Android手机从bulk-in端点接收数据,然后接收U盘发送CSW。

      (3)如果是纯命令,即不用发数据,那么Android手机就接收CSW响应。

上面为先发送一个read format capacities的命令,然后接收format capacities,最后接收CSW。(没讲清楚,敬请原谅!)

最后来看一下结果吧:

接收的数据皆以16进制的形式给出,至于format capacities结果怎么计算的,我也没搞明白,我的U盘是4GB的(如果大神知道,告诉小弟呗),最后的CSW的值的前八个字节与CBW的前八个一样的,后面的请看文章开头给出的参考文档。



更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. 如何在后台运行Linux命令?
  3. No.11 使用firewall配置的防火墙策略的生效模式
  4. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  5. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  6. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  7. Android(安卓)简单GreenDao增删改查
  8. DesiredCapabilities参数大全
  9. Android(安卓)ListView分页显示

随机推荐

  1. Android 带小圆圈的倒计时圆形进度条
  2. Android 如何更换屏幕上锁界面背景图片
  3. 从菜鸟到Android资深工程师的进阶之路
  4. android使用Navigation实现Fragment之间
  5. Android开发学习笔记——四大组件之Conte
  6. android模拟器安装及优化(集锦)
  7. Android的前景
  8. 利用android studio LinearLayout线性布
  9. Android中进行单元测试
  10. android中用jsonObject解析json数据