Android USB通訊(完整版)
阿新 • • 發佈:2018-12-31
1.Host端程式碼:
package com.tcl.navigator.hostchart.activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.tcl.navigator.hostchart.R;
import com.tcl.navigator.hostchart.base.MyApplication;
import com.tcl.navigator.hostchart.receiver.OpenDevicesReceiver;
import com.tcl.navigator.hostchart.receiver.UsbDetachedReceiver;
import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
*/
public class MainActivity extends AppCompatActivity implements UsbDetachedReceiver.UsbDetachedListener, OpenDevicesReceiver.OpenDevicesListener, View.OnClickListener {
private static final int CONNECTED_SUCCESS = 0;
private static final int RECEIVER_MESSAGE_SUCCESS = 1;
private static final int SEND_MESSAGE_SUCCESS = 2;
private static final String USB_ACTION = "com.tcl.navigator.hostchart";
private TextView mLog;
private EditText mMessage;
private UsbDetachedReceiver mUsbDetachedReceiver;
private ExecutorService mThreadPool;
private UsbManager mUsbManager;
private OpenDevicesReceiver mOpenDevicesReceiver;
private TextView mError;
private UsbDeviceConnection mUsbDeviceConnection;
private UsbEndpoint mUsbEndpointOut;
private UsbEndpoint mUsbEndpointIn;
private boolean mToggle = true;
private Button mSendMessage;
private boolean isDetached = false;
private byte[] mBytes = new byte[1024];
private boolean isReceiverMessage = true;
private UsbInterface mUsbInterface;
private StringBuffer mStringBuffer = new StringBuffer();
private Context mContext;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CONNECTED_SUCCESS://車機和手機連線成功
mError.setText("");
mSendMessage.setEnabled(true);
loopReceiverMessage();
break;
case RECEIVER_MESSAGE_SUCCESS://成功接受到資料
mLog.setText(mStringBuffer.toString());
break;
case SEND_MESSAGE_SUCCESS://成功傳送資料
mMessage.setText("");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
initView();
initListener();
initData();
}
private void initView() {
mLog = (TextView) findViewById(R.id.log);
mError = (TextView) findViewById(R.id.error);
mMessage = (EditText) findViewById(R.id.message);
mSendMessage = (Button) findViewById(R.id.sendmessage);
}
private void initListener() {
mSendMessage.setOnClickListener(this);
}
private void initData() {
mContext = getApplicationContext();
mSendMessage.setEnabled(false);
mUsbDetachedReceiver = new UsbDetachedReceiver(this);
IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbDetachedReceiver, intentFilter);
mThreadPool = Executors.newFixedThreadPool(5);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
openDevices();
}
/**
* 開啟裝置 , 讓車機和手機端連起來
*/
private void openDevices() {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(USB_ACTION), 0);
IntentFilter intentFilter = new IntentFilter(USB_ACTION);
mOpenDevicesReceiver = new OpenDevicesReceiver(this);
registerReceiver(mOpenDevicesReceiver, intentFilter);
//列舉裝置(手機)
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
if (deviceList != null) {
for (UsbDevice usbDevice : deviceList.values()) {
int productId = usbDevice.getProductId();
if (productId != 377 && productId != 7205) {
//這裡說明下,這裡的377 , 7205是我這臺機子上獨有,因為我這上面有多臺裝置,所以我在這裡
//判斷了下.只有一臺裝置的無需這一步.
if (mUsbManager.hasPermission(usbDevice)) {
initAccessory(usbDevice);
} else {
mUsbManager.requestPermission(usbDevice, pendingIntent);
}
}
}
} else {
mError.setText("請連線USB");
}
}
/**
* 傳送命令 , 讓手機進入Accessory模式
*
* @param usbDevice
*/
private void initAccessory(UsbDevice usbDevice) {
UsbDeviceConnection usbDeviceConnection = mUsbManager.openDevice(usbDevice);
if (usbDeviceConnection == null) {
mError.setText("請連線USB");
return;
}
//根據AOA協議開啟Accessory模式
initStringControlTransfer(usbDeviceConnection, 0, "Google, Inc."); // MANUFACTURER
initStringControlTransfer(usbDeviceConnection, 1, "AccessoryChat"); // MODEL
initStringControlTransfer(usbDeviceConnection, 2, "Accessory Chat"); // DESCRIPTION
initStringControlTransfer(usbDeviceConnection, 3, "1.0"); // VERSION
initStringControlTransfer(usbDeviceConnection, 4, "http://www.android.com"); // URI
initStringControlTransfer(usbDeviceConnection, 5, "0123456789"); // SERIAL
usbDeviceConnection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, 100);
usbDeviceConnection.close();
MyApplication.printLogDebug("initAccessory success");
initDevice();
}
private void initStringControlTransfer(UsbDeviceConnection deviceConnection, int index, String string) {
deviceConnection.controlTransfer(0x40, 52, 0, index, string.getBytes(), string.length(), 100);
}
/**
* 初始化裝置(手機) , 當手機進入Accessory模式後 , 手機的PID會變為Google定義的2個常量值其中的一個 ,
*/
private void initDevice() {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
while (mToggle) {
SystemClock.sleep(1000);
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Collection<UsbDevice> values = deviceList.values();
if (!values.isEmpty()) {
for (UsbDevice usbDevice : values) {
int productId = usbDevice.getProductId();
if (productId == 0x2D00 || productId == 0x2D01) {
if (mUsbManager.hasPermission(usbDevice)) {
mUsbDeviceConnection = mUsbManager.openDevice(usbDevice);
if (mUsbDeviceConnection != null) {
mUsbInterface = usbDevice.getInterface(0);
int endpointCount = mUsbInterface.getEndpointCount();
for (int i = 0; i < endpointCount; i++) {
UsbEndpoint usbEndpoint = mUsbInterface.getEndpoint(i);
if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
mUsbEndpointOut = usbEndpoint;
} else if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
mUsbEndpointIn = usbEndpoint;
}
}
}
if (mUsbEndpointOut != null && mUsbEndpointIn != null) {
MyApplication.printLogDebug("connected success");
mHandler.sendEmptyMessage(CONNECTED_SUCCESS);
mToggle = false;
isDetached = true;
}
}
} else {
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(""), 0));
}
}
}
} else {
finish();
}
}
}
});
}
/**
* 接受訊息執行緒 , 此執行緒在裝置(手機)初始化完成後 , 就一直迴圈接受訊息
*/
private void loopReceiverMessage() {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
while (isReceiverMessage) {
/**
* 迴圈接受資料的地方 , 只接受byte資料型別的資料
*/
if (mUsbDeviceConnection != null && mUsbEndpointIn != null) {
int i = mUsbDeviceConnection.bulkTransfer(mUsbEndpointIn, mBytes, mBytes.length, 3000);
MyApplication.printLogDebug(i + "");
if (i > 0) {
mStringBuffer.append(new String(mBytes, 0, i) + "\n");
mHandler.sendEmptyMessage(RECEIVER_MESSAGE_SUCCESS);
}
}
}
}
});
}
@Override
public void usbDetached() {
if (isDetached) {
finish();
}
}
@Override
public void openAccessoryModel(UsbDevice usbDevice) {
initAccessory(usbDevice);
}
@Override
public void openDevicesError() {
mError.setText("USB連線錯誤");
}
@Override
public void onClick(View v) {
final String messageContent = mMessage.getText().toString();
if (!TextUtils.isEmpty(messageContent)) {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
/**
* 傳送資料的地方 , 只接受byte資料型別的資料
*/
int i = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, messageContent.getBytes(), messageContent.getBytes().length, 3000);
if (i > 0) {
mHandler.sendEmptyMessage(SEND_MESSAGE_SUCCESS);
}
}
});
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mUsbDeviceConnection != null) {
mUsbDeviceConnection.releaseInterface(mUsbInterface);
mUsbDeviceConnection.close();
mUsbDeviceConnection = null;
}
mUsbEndpointIn = null;
mUsbEndpointOut = null;
mToggle = false;
isReceiverMessage = false;
mThreadPool.shutdownNow();
unregisterReceiver(mUsbDetachedReceiver);
unregisterReceiver(mOpenDevicesReceiver);
}
}
OpenDevicesReceiver廣播程式碼:
package com.tcl.navigator.hostchart.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
/**
* Created by yaohui on 2017/3/3.
*/
public class OpenDevicesReceiver extends BroadcastReceiver {
private OpenDevicesListener mOpenDevicesListener;
public OpenDevicesReceiver(OpenDevicesListener openDevicesListener) {
mOpenDevicesListener = openDevicesListener;
}
@Override
public void onReceive(Context context, Intent intent) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (usbDevice != null) {
mOpenDevicesListener.openAccessoryModel(usbDevice);
} else {
mOpenDevicesListener.openDevicesError();
}
} else {
mOpenDevicesListener.openDevicesError();
}
}
public interface OpenDevicesListener {
/**
* 開啟Accessory模式
*
* @param usbDevice
*/
void openAccessoryModel(UsbDevice usbDevice);
/**
* 開啟裝置(手機)失敗
*/
void openDevicesError();
}
}
UsbDetachedReceiver廣播程式碼:
package com.tcl.navigator.hostchart.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class UsbDetachedReceiver extends BroadcastReceiver {
private UsbDetachedListener mUsbDetachedListener;
public UsbDetachedReceiver(UsbDetachedListener usbDetachedListener) {
mUsbDetachedListener = usbDetachedListener;
}
@Override
public void onReceive(Context context, Intent intent) {
mUsbDetachedListener.usbDetached();
}
public interface UsbDetachedListener {
/**
* usb斷開連線
*/
void usbDetached();
}
}
以上就是Host端程式碼了.
2.Accessory端程式碼:
package com.tcl.navigator.accessorychart.activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.tcl.navigator.accessorychart.R;
import com.tcl.navigator.accessorychart.receiver.OpenAccessoryReceiver;
import com.tcl.navigator.accessorychart.receiver.UsbDetachedReceiver;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity implements OpenAccessoryReceiver.OpenAccessoryListener, View.OnClickListener, UsbDetachedReceiver.UsbDetachedListener {
private static final int SEND_MESSAGE_SUCCESS = 0;
private static final int RECEIVER_MESSAGE_SUCCESS = 1;
private static final String USB_ACTION = "com.tcl.navigator.accessorychart";
private TextView mLog;
private EditText mMessage;
private Button mSend;
private UsbManager mUsbManager;
private OpenAccessoryReceiver mOpenAccessoryReceiver;
private ParcelFileDescriptor mParcelFileDescriptor;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
private ExecutorService mThreadPool;
private byte[] mBytes = new byte[1024];
private StringBuffer mStringBuffer = new StringBuffer();
private UsbDetachedReceiver mUsbDetachedReceiver;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SEND_MESSAGE_SUCCESS:
mMessage.setText("");
mMessage.clearComposingText();
break;
case RECEIVER_MESSAGE_SUCCESS:
mLog.setText(mStringBuffer.toString());
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
initView();
initListener();
initData();
}
private void initView() {
mLog = (TextView) findViewById(R.id.log);
mMessage = (EditText) findViewById(R.id.message);
mSend = (Button) findViewById(R.id.send);
}
private void initListener() {
mSend.setOnClickListener(this);
}
private void initData() {
mSend.setEnabled(false);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mThreadPool = Executors.newFixedThreadPool(3);
mUsbDetachedReceiver = new UsbDetachedReceiver(this);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbDetachedReceiver, filter);
mOpenAccessoryReceiver = new OpenAccessoryReceiver(this);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(USB_ACTION), 0);
IntentFilter intentFilter = new IntentFilter(USB_ACTION);
registerReceiver(mOpenAccessoryReceiver, intentFilter);
UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory usbAccessory = (accessories == null ? null : accessories[0]);
if (usbAccessory != null) {
if (mUsbManager.hasPermission(usbAccessory)) {
openAccessory(usbAccessory);
} else {
mUsbManager.requestPermission(usbAccessory, pendingIntent);
}
}
}
/**
* 開啟Accessory模式
*
* @param usbAccessory
*/
private void openAccessory(UsbAccessory usbAccessory) {
mParcelFileDescriptor = mUsbManager.openAccessory(usbAccessory);
if (mParcelFileDescriptor != null) {
FileDescriptor fileDescriptor = mParcelFileDescriptor.getFileDescriptor();
mFileInputStream = new FileInputStream(fileDescriptor);
mFileOutputStream = new FileOutputStream(fileDescriptor);
mSend.setEnabled(true);
mThreadPool.execute(new Runnable() {
@Override
public void run() {
int i = 0;
while (i >= 0) {
try {
i = mFileInputStream.read(mBytes);
} catch (IOException e) {
e.printStackTrace();
break;
}
if (i > 0) {
mStringBuffer.append(new String(mBytes, 0, i) + "\n");
mHandler.sendEmptyMessage(RECEIVER_MESSAGE_SUCCESS);
}
}
}
});
}
}
@Override
public void openAccessoryModel(UsbAccessory usbAccessory) {
openAccessory(usbAccessory);
}
@Override
public void openAccessoryError() {
}
@Override
public void onClick(View v) {
final String mMessageContent = mMessage.getText().toString();
if (!TextUtils.isEmpty(mMessageContent)) {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
mFileOutputStream.write(mMessageContent.getBytes());
mHandler.sendEmptyMessage(SEND_MESSAGE_SUCCESS);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void usbDetached() {
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mOpenAccessoryReceiver);
unregisterReceiver(mUsbDetachedReceiver);
if (mParcelFileDescriptor != null) {
try {
mParcelFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mFileInputStream != null) {
try {
mFileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mFileOutputStream != null) {
try {
mFileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
OpenAccessoryReceiver廣播程式碼:
package com.tcl.navigator.accessorychart.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
public class OpenAccessoryReceiver extends BroadcastReceiver {
private OpenAccessoryListener mOpenAccessoryListener;
public OpenAccessoryReceiver(OpenAccessoryListener openAccessoryListener) {
mOpenAccessoryListener = openAccessoryListener;
}
@Override
public void onReceive(Context context, Intent intent) {
UsbAccessory usbAccessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (usbAccessory != null) {
mOpenAccessoryListener.openAccessoryModel(usbAccessory);
} else {
mOpenAccessoryListener.openAccessoryError();
}
} else {
mOpenAccessoryListener.openAccessoryError();
}
}
public interface OpenAccessoryListener {
/**
* 開啟Accessory模式
*
* @param usbAccessory
*/
void openAccessoryModel(UsbAccessory usbAccessory);
/**
* 開啟裝置(手機)失敗
*/
void openAccessoryError();
}
}
UsbDetachedReceiver廣播程式碼:
package com.tcl.navigator.accessorychart.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class UsbDetachedReceiver extends BroadcastReceiver {
private UsbDetachedListener mUsbDetachedListener;
public UsbDetachedReceiver(UsbDetachedListener usbDetachedListener) {
mUsbDetachedListener = usbDetachedListener;
}
@Override
public void onReceive(Context context, Intent intent) {
mUsbDetachedListener.usbDetached();
}
public interface UsbDetachedListener {
/**
* usb斷開連線
*/
void usbDetached();
}
}
最後在AndroidManifest裡面有些小改動:
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter"/>
</activity>
其中USB通訊是需要使用者手動賦予許可權的,所以2個手機都會有提示的.
到此基本上就完了,我寫的這2個app執行在2個安卓手機上,通過資料線是可以進行聊天的.
不懂的請給我留言,我看到了都會回覆的,再見啦! ! !
原始碼下載地址:HostChart,AccessoryChart