Android 獲取USB OTG插入狀置的資訊
至從Android支援USB Host(Android USB OTG)之後,陸續的支援很多USB接外設備,例如:Audio、USB Stick、Keyboard、Mouse…等周邊,有些插入後就自動支援,有些可能不支援內建的驅動,但可以經過UsbDeviceConnection進行USB控制硬體也能達到效果,但該周邊功能僅能給自建的APP使用。

USB Manager
先取得USB Manager物件的控制:
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
之後可以利用它來取得已知的USB訊息,利如列出已有的USB週邊:
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Log.d(TAG, deviceList.size()+" USB device(s) found");
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next();
Log.d("1",device);
}
將上面的程式直接至放於onCreate並且預先插入USB週邊再啟動APP後就能得到USB裝置的訊息。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Log.d("1", deviceList.size()+" USB device(s) found");
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next();
Log.d("1","" + device);
}
}
輸出結果:
1 USB device(s) found
UsbDevice[mName=/dev/bus/usb/001/002,mVendorId=2965,mProductId=5920,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@41818b98]
USB Plug 訊息接收
拔除裝置
USB 周邊在插入(attached)與拔除(deattached)時必需要經過插入裝置的IntentFilter設定過濾資訊並註冊Receiver來取得當前的資訊,拔除時要過濾的action為UsbManager.ACTION_USB_ACCESSORY_DETACHED,程式如下:
IntentFilter filterAttached_and_Detached = null;
filterAttached_and_Detached = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
//
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_DEVICE_DETACHED.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
Log.d("1","DEATTCHED-" + device);
}
}
}
};
//
registerReceiver(mUsbReceiver, filterAttached_and_Detached);
插入裝置
插入裝置的IntentFilter過濾名稱為UsbManager.ACTION_USB_DEVICE_ATTACHED,並且當BroadcastReceiver廣播通知啟動時也會連帶的將濾名稱填入Intent物件中,其中BroadcastReceiver功能本章不做介紹,還未了解的請至官網了解。
IntentFilter filterAttached_and_Detached = null;
filterAttached_and_Detached = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
//
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_DEVICE_ATTACHED.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
Log.d("1","ATTACHED-" + device);
}
}
}
};
//
registerReceiver(mUsbReceiver, filterAttached_and_Detached);
USB 權限請求
當你插入USB裝置時會引發UsbManager.ACTION_USB_DEVICE_ATTACHED後必需要檢查權限是否同意被使用,至於權限會包在Intent之中並於BroadcastReceiver中取得,所以在插入USB通知時必需要先取得包在Intent資訊中的UsbManager.EXTRA_PERMISSION_GRANTED檢查布林值:
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
:
//false 不需要請求
:
}
如果需要權限請求時可以使用USB Manager的requestPermission:
PendingIntent mPermissionIntent;
mPermissionIntent = PendingIntent.getBroadcast(MainActivity.this, 0,
new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_ONE_SHOT);
//
mUsbManager.requestPermission(device, mPermissionIntent);
請求過程結束後會依照PendingIntent指定的action內容ACTION_USB_PERMISSION發播廣通知,所以收到播廣通知後判斷是否為ACTION_USB_PERMISSION就能知道權限的請求狀態,程式內容會改成:
if (ACTION_USB_DEVICE_ATTACHED.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
//
Log.d("1","ATTACHED-" + device);
}
}
else {
PendingIntent mPermissionIntent;
mPermissionIntent = PendingIntent.getBroadcast(MainActivity.this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_ONE_SHOT);
mUsbManager.requestPermission(device, mPermissionIntent);
}
}
}
ACTION_USB_PERMISSION的內容可以自訂,但內容必需要唯一值,例子中使用的內容為:
private static final String ACTION_USB_PERMISSION = "tw.g35gtwcms.android.test.list_usb_otg.USB_PERMISSION";
當需要權限請求設定的對話框出現後,

完成選擇時會依照宣告Intent的action發廣播通知,所以接收廣播通知的程式要另外加入:
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
//
Log.d("1","PERMISSION-" + device);
}
}
}
}
權限請求完成後,收到通知並且確認不需要權限請求時就將接上USB的裝置資訊印出,像下面的輸出結果:
PERMISSION-UsbDevice[mName=/dev/bus/usb/001/010,mVendorId=2965,mProductId=5920,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@4182b720]
範例程式
此次範例程式使用前必要注意一點,USB會因為裝置佔用而無法使用USB傳輪線來取得LogCat的內容,所以在這之前必需要將你的除錯模式的輸出改為tcpip的方式,也就是經由網路輸出,要何操作請參考站內文章ADB使用WiFi進行除錯 – ADB Over WiFi。

以上都完成後就可以執行範例程式,執行後再接上你的OTG線並且插入USB裝置並且觀察LogCat的變化,輸出結果如下:

輸出結果文字版:
PERMISSION-UsbDevice[mName=/dev/bus/usb/001/010,mVendorId=2965,mProductId=5920,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@4182b720]
DETACHED-UsbDevice[mName=/dev/bus/usb/001/010,mVendorId=2965,mProductId=5920,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@4182d7c8]
DETACHED-UsbDevice[mName=/dev/bus/usb/001/010,mVendorId=2965,mProductId=5920,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@4182e5a8]
DETACHED-UsbDevice[mName=/dev/bus/usb/001/001,mVendorId=7531,mProductId=2,mClass=9,mSubclass=0,mProtocol=1,mInterfaces=[Landroid.os.Parcelable;@4182f340]
測試完成後如果不使用請務必再將除錯模式更改為usb模式。