USB OTG: USB On-The-Go的简称,是USB 2.0规定的补充标准,它可以使手机等作为USB主机,和其他USB设备连接通信,例如U盘,另外的手机,USB Hub等。
Android对Usb的支持是从3.1开始的,显然是加强Android平板的对外扩展能力。而对Usb使用更多的,是Android在工业中的使用。Android工业板子一般都会提供多个U口和多个串口,它们是连接外设的手段与桥梁。
USB OTG是通过OTG线连接通信,OTG线一头是公头连接手机USB HOST,一头母头接外设。如图一连接通信。
Android USB代码分析
在路径/frameworks/base/core/java/android/hardware/usb/下,这个包内包含了USB相关的类。
1,USBManager:定义了USB的action和参数,获取USB的状态,与连接的USB设备通信。
方法getDeviceList获取到所有连接的USB device的HashMap
方法openDevice 与USB设备通信
当有USB设备连接时,会发送广播ACTION_USB_DEVICE_ATTACHED。
public static final StringACTION_USB_DEVICE_ATTACHED =
"android.hardware.usb.action.USB_DEVICE_ATTACHED";
当有USB设备移除时,会发送广播ACTION_USB_DEVICE_DETACHED。
publicstatic final String ACTION_USB_DEVICE_DETACHED =
"android.hardware.usb.action.USB_DEVICE_DETACHED";
2, UsbDevice:Usb设备的抽象,它包含一个或多个UsbInterface,而每个UsbInterface包含多个UsbEndpoint。Host与其通信,先打开UsbDeviceConnection,使用UsbRequest在一个端点(endpoint)发送和接收数据。
3,UsbInterface:定义了设备的功能集,一个UsbDevice包含多个UsbInterface,每个Interface都是独立的。
4,UsbEndpoint:endpoint是interface的通信通道。
5,UsbDeviceConnection:host与device建立的连接,并在endpoint传输数据。
6,UsbRequest:USB 请求包。可以在UsbDeviceConnection上异步传输数据。注意是只在异步通信时才会用到它。
7,UsbConstants:USB常量的定义。
Settings中USB界面
Settings界面代码主要由UsbModeChooserActivity和UsbBackend显示USB选项,
UsbBackend中通过方法setUsbFunction调用UsbManager中setCurrentFunction,设置不同的USB连接模式,如
mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true);
设置USB模式为MTP,同理也可设置为PTP,MIDI等。这个方法第二参数true/false是设置是否可以通过USB连接传输数据,当为true时,就可以传输数据,当为false时,USB连接仅为充电,无法访问手机上文件。
通过方法getUsbDataMode()可以获取当前USB连接模式,UsbModeChooserActivity通过这个方法显示当前的USB连接模式。
方法isModeSupported判断这种USB模式是否支持,只有支持的模式才会在选项中显示。
UsbModeChooserActivity弹出的USB选项Dialog,包含支持的USB连接模式。
3.3 USB OTG界面
手机通过OTG线连接U盘之类的外设,会通过SystemUI中StorageNotification,添加了对Storage的监听,
当volume状态发生变化时,会在onVolumeStateChangedInternal做对应提示。
private final StorageEventListener mListener = newStorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo
vol, int oldState, intnewState) {
onVolumeStateChangedInternal(vol);
}
刚通过OTG线连接U盘,调用onVolumeChecking显示checking U盘的提示。
privateNotificationonVolumeChecking(VolumeInfo vol) {
finalDiskInfo disk = vol.getDisk();
final CharSequence title = mContext.getString(
R.string.ext_media_checking_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_checking_notification_message, disk.getDescription());
returnbuildNotificationBuilder(vol, title, text)
.setCategory(Notification.CATEGORY_PROGRESS)
.setOngoing(true)
.build();
}
当U盘状态为MOUNTED时,调用onVolumeMounted更新U盘提示,当U盘移除时,调用onVolumeRemoved或者onVolumeBadRemoval,提示已经移除U盘。
这个通过OTG线连接U盘,显示的提示和插入SD卡类似,都可以点击Notification进入DocumentsUI,浏览和操作U盘中的文件。
手机通过OTG线与另外手机连接时,在MtpDocumentsProvider这个应用中有ReceiverActivity有监听
android.hardware.usb.action.USB_DEVICE_ATTACHED 这个广播,当主机通过OTG线连接另一台手机时,
接收到这个广播,从广播中获取到连接设备信息,创建相应的Uri,把Uri传给启动DocumentsUI的intent,
在启动DocumentsUI后,另外一台手机的USB连接模式未MTP,PTP时,主机就能查看和操作从机的文件。当是charging only状态,主机无法访问从机文件。
public class ReceiverActivity extends Activity {
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
try{
finalMtpDocumentsProvider provider =MtpDocumentsProvider.getInstance();
provider.openDevice(device.getDeviceId());
finalString deviceRootId =provider.getDeviceDocumentId(device.getDeviceId());
finalUri uri = DocumentsContract.buildRootUri(
MtpDocumentsProvider.AUTHORITY,deviceRootId);
final Intent intent = newIntent(Intent.ACTION_VIEW);
intent.setDataAndType(uri,DocumentsContract.Root.MIME_TYPE_ITEM);
intent.addCategory(Intent.CATEGORY_DEFAULT);
this.startActivity(intent);
}catch(IOException exception) {
Log.e(MtpDocumentsProvider.TAG,"Failed to open
device", exception);
}
}
finish();
}
}
同时也是在MtpDocumentsProvider的UsbIntentReceiver,这个receiver会监听OTG插拔两个广播
android.hardware.usb.action.USB_DEVICE_ATTACHED和android.hardware.usb.action.USB_DEVICE_DETACHED。
public class UsbIntentReceiver extendsBroadcastReceiver {
@Override
public voidonReceive(Context context, Intent intent) {
final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE);
switch(intent.getAction()) {
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
MtpDocumentsProvider.getInstance().resumeRootScanner();
break;
case UsbManager.ACTION_USB_DEVICE_DETACHED:
try{
MtpDocumentsProvider.getInstance().closeDevice(device.getDeviceId());
}catch(IOException | InterruptedException e) {
Log.e(MtpDocumentsProvider.TAG,"Failed to close
device", e);
}
break;
}
}
}
通过在MtpDocumentsProvider中初始化ServiceIntentSender,通过调用sendUpdateNotificationIntent显示手机连接的提示。
USB和底层交互
在android中很多地方使用到了UEvent机制,如图:像HDMI,Battery,USB相关等;当我们需要接受底层的UEvent的时候,我们就需要注册一个UEventObserver。Kernel层和framework层交互通过UeventObserver。在USB插拔时,在UsbDeviceManager中有注册UEventObserver,监听USB的uevent,当有uevent事件上传,获取对应的USB状态,和连接的设备之类。
/*
* Listensfor uevent messages from the kernel to monitor the USB state
*/
private final UEventObserver mUEventObserver = newUEventObserver() {
@Override
public voidonUEvent(UEventObserver.UEvent event) {
if (DEBUG) Slog.v(TAG, "USB UEVENT:
" + event.toString());
Stringstate = event.get("USB_STATE");
Stringaccessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
}else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got
accessory start");
startAccessoryMode();
}
}
};
mUEventObserver通过 startObserving加入想要监听的uevent,当对应事件上传,就会在对应onUEvent作出对应处理。
// Watch for USB configuration
changes
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
startObserving这个方法转到UEventThread中,UEventThread是UeventObserver的内部类,run中一个死循环,不断监听底层的uevent,然后发送uevent。
@Override
public voidrun() {
nativeSetup();
while (true) {
Stringmessage =nativeWaitForNextEvent();
if (message != null) {
if (DEBUG) {
Log.d(TAG, message);
}
sendEvent(message);
}
}
}
USB插拔的后续处理就根据uevent上传的状态在updateState方法中赋值connected,configured,通过Handler发送Message:MSG_UPDATE_STATE,包含connected,configured,在UsbHandler中handleMessage中更新USB,adb的Notification,上层界面显示。
public void updateState(String state){
int connected, configured;
if ("DISCONNECTED".equals(state)) {
connected= 0;
configured= 0;
}else if ("CONNECTED".equals(state)) {
connected= 1;
configured= 0;
}else if ("CONFIGURED".equals(state)) {
connected= 1;
configured= 1;
}else{
Slog.e(TAG, "unknown
state "+ state);
return;
}
removeMessages(MSG_UPDATE_STATE);
Messagemsg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1= connected;
msg.arg2= configured;
// debounce disconnects to avoid
problems bringing up USB tethering
sendMessageDelayed(msg,(connected == 0) ?UPDATE_DELAY: 0);
}
UsbHostManager中从jni回调beginUsbDeviceAdded-》addUsbConfiguration-》addUsbInterface-》addUsbEndpoint-》endUsbDeviceAdded
通过这些方法把USB OTG device挂载上,手机当成USB主机,可以通过USB连接模式,传输数据等。