From 31c0a9c8c1f803b786b960813a8fb036110b5c92 Mon Sep 17 00:00:00 2001 From: DXL <64101226@qq.com> Date: Thu, 27 Aug 2020 12:24:21 +0800 Subject: [PATCH] Add files via upload --- appmain/devices/BleSerialControl.java | 667 ++++++++++++++++++++++++++ appmain/devices/DevCallback.java | 14 + appmain/devices/Device.java | 12 + appmain/devices/DriverInterface.java | 40 ++ appmain/devices/EmptyDevice.java | 15 + appmain/devices/UsbSerialControl.java | 367 ++++++++++++++ 6 files changed, 1115 insertions(+) create mode 100644 appmain/devices/BleSerialControl.java create mode 100644 appmain/devices/DevCallback.java create mode 100644 appmain/devices/Device.java create mode 100644 appmain/devices/DriverInterface.java create mode 100644 appmain/devices/EmptyDevice.java create mode 100644 appmain/devices/UsbSerialControl.java diff --git a/appmain/devices/BleSerialControl.java b/appmain/devices/BleSerialControl.java new file mode 100644 index 0000000..9d1e17d --- /dev/null +++ b/appmain/devices/BleSerialControl.java @@ -0,0 +1,667 @@ +package com.proxgrind.devices; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; + +import com.proxgrind.chameleon.exceptions.DataInvalidException; +import com.proxgrind.chameleon.javabean.DevBean; +import com.proxgrind.chameleon.packets.DataPackets; +import com.proxgrind.chameleon.utils.device.ble.BLERawUtils; +import com.proxgrind.chameleon.utils.device.ble.ClsUtils; +import com.proxgrind.chameleon.utils.tools.HexUtil; +import com.proxgrind.chameleon.utils.system.LogUtils; +import com.proxgrind.chameleon.utils.system.SystemUtils; +import com.proxgrind.chameleon.callback.ConnectCallback; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Queue; +import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @author DXL + * @see com.proxgrind.devices.DriverInterface + * 封装用于BLE通讯的实例! + */ +public class BleSerialControl extends BluetoothGattCallback + implements DriverInterface { + // 日志标签! + public static final String TAG = "BleSerialControl"; + // MTU上限 + public static final int MTU = 244; + // UART 服务! + public static final UUID UART_SERVICE_UUID = UUID.fromString("51510001-7969-6473-6f40-6b6f6c6c6957"); + // UART 写特征 + public static final UUID SEND_CHARACT_UUID = UUID.fromString("51510002-7969-6473-6f40-6b6f6c6c6957"); + // UART 读特征 + public static final UUID RECV_CHARACT_UUID = UUID.fromString("51510003-7969-6473-6f40-6b6f6c6c6957"); + // 控制点! + public static final UUID CTRL_CHARACT_UUID = UUID.fromString("51510004-7969-6473-6f40-6b6f6c6c6957"); + // UART读特征句柄! + public static final UUID RECV_DESC_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + // Chameleon MINI UUID特征码! + public static byte[] UUID_RAW_MINI = HexUtil.hexStringToByteArray("57696C6C6F6B406F7364697901005151"); + // Chameleon MINI DFU UUID特征码! + public static byte[] UUID_RAW_MINI_DFU = HexUtil.hexStringToByteArray("5252"); + // Chameleon Tiny 广播UUID + public static UUID UUID_COMPLETE_TINY = UUID.fromString("51510004-7969-6473-6f40-6b6f6c6c6957"); + // Chameleon Tiny UUID特征码 + public static byte[] UUID_RAW_TINY = HexUtil.hexStringToByteArray("57696C6C6F6B406F7364697904005151"); + // 解包后保存的数据的队列,此缓冲区大多数是用于串口数据的!! + private static final Queue BUFFER_SERIAL = new ConcurrentLinkedQueue<>(); + + // 蓝牙相关 + private DevCallback devCallback; + private List gattCallbacks = new ArrayList<>(); + private BluetoothDevice device; + private BluetoothAdapter bluetoothAdapter; + private BluetoothGatt gatt; + private BroadcastReceiver bondReceiver; + private DevBean devBean; + // 当前设备是否连接 + private volatile boolean isConnected = false; + // 是否允许数据同步到缓冲区 + private volatile boolean pushDataToBuffer = true; + // 当前的BLE同步状态 + private volatile int asyncSyncStatus = 0; + // 绑定广播同步状态 + private volatile int bondAsyncSyncStatus = 0; + // 最后的一个应答帧 + private volatile byte[] dataFrame; + // 帧锁 + private static final Object LOCK_DATA_FRAME = new Object(); + // 单例 + private static final BleSerialControl thiz = new BleSerialControl(); + // 数据回调 + private OnDataReceiveListener onDataReceiveListener; + + private BleSerialControl() { + synchronized (BleSerialControl.class) { + // 添加一个默认连接用的回调! + gattCallbacks.add(new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (status == 19) { // 状态码19是绑定信息丢失引起的! + LogUtils.d("状态码发现配对异常,需要重新进行连接配对。"); + asyncSyncStatus = -1; + closeNoException(); + return; + } + if (newState == BluetoothProfile.STATE_CONNECTED) {//状态变为 已连接 + LogUtils.d("已连接,开始发现服务!"); + // 默认打开高性能传输 + requestIntervalHigh(); + // 尝试寻找服务! + if (!gatt.discoverServices()) { + asyncSyncStatus = -1; + } + } + if (newState == BluetoothGatt.STATE_DISCONNECTED) { //状态变为 未连接 + isConnected = false; + closeNoException(); + LogUtils.w("设备断开了链接!"); + if (devCallback != null) { + // 通知一下设备断开! + devCallback.onDetach(gatt.getDevice()); + } + asyncSyncStatus = -1; + } + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (asyncSyncStatus == -1) return; + if (status == BluetoothGatt.GATT_SUCCESS) { + LogUtils.d("已发现服务,开始对指定句柄开启写通知!"); + enableNotifyOnUARTService(gatt, RECV_CHARACT_UUID); + } else { + asyncSyncStatus = -1; + } + } + + @Override + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + if (asyncSyncStatus == -1) return; + // 如果是MINI,则不存在04的控制特征 + UUID currentCharacteristicUUID = descriptor.getCharacteristic().getUuid(); + if (isCtrlCharacteristicExists() && !currentCharacteristicUUID.equals(CTRL_CHARACT_UUID)) { + // 如果当前设备存在控制点,则判断当前是否需要启用控制点! + enableNotifyOnUARTService(gatt, CTRL_CHARACT_UUID); + return; + } + UUID finalResultUUid; + if (isCtrlCharacteristicExists()) { + finalResultUUid = CTRL_CHARACT_UUID; + } else { + finalResultUUid = RECV_CHARACT_UUID; + } + if (currentCharacteristicUUID.equals(finalResultUUid)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + LogUtils.d("写通知开启成功,BLE连接成功!"); + asyncSyncStatus = 1; + } else { + asyncSyncStatus = -1; + } + } + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + byte[] finalDatas = characteristic.getValue(); + if (finalDatas != null) { + try { + // 我们需要进行解包! + finalDatas = DataPackets.getData(finalDatas, false); + } catch (DataInvalidException die) { + // die.printStackTrace(); + } + if (onDataReceiveListener != null) { + onDataReceiveListener.onReceive(finalDatas); + } + if (pushDataToBuffer) { + for (Byte b : finalDatas) { + if (!BUFFER_SERIAL.offer(b)) { + // clear and retry add data to queue! + BUFFER_SERIAL.clear(); + BUFFER_SERIAL.offer(b); + } + } + } + dataFrame = finalDatas; + } + } + }); + bondReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // LogUtils.d("在广播中进行绑定状态的处理!"); + String action = intent.getAction(); //得到action + if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) return; + BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (btDevice == null) return; + int status = btDevice.getBondState(); + if (status == BluetoothDevice.BOND_BONDED) { + bondAsyncSyncStatus = 1; + } + } + }; + } + } + + public static BleSerialControl get() { + return thiz; + } + + @Override + public void register(DevCallback callback) { + devCallback = callback; + BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + if (bluetoothManager == null) return; + bluetoothAdapter = bluetoothManager.getAdapter(); + context.registerReceiver(bondReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)); + } + + @Override + public void connect(DevBean t, ConnectCallback callback) { + new Thread(new Runnable() { + @Override + public void run() { + BluetoothDevice device = bluetoothAdapter.getRemoteDevice(t.getMacAddress()); + setDevice(device); + devBean = t; + if (connectNoCallback(t, device)) { + isConnected = true; + callback.onConnectSucces(); + } else { + isConnected = false; + callback.onConnectFail(); + } + } + }).start(); + } + + @Override + public boolean isDeviceConnected() { + return isConnected; + } + + @Override + public BluetoothAdapter getAdapter() { + return bluetoothAdapter; + } + + @Override + public DevBean getDevice() { + if (devBean != null) return devBean; + return new DevBean(device.getName(), device.getAddress()); + } + + @Override + public void disconnect() { + try { + closeNoException(); + isConnected = false; + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public int getUniqueId() { + return 0x06; + } + + @Override + public void unregister() { + devCallback = null; + try { + context.unregisterReceiver(bondReceiver); + } catch (Exception ignored) { + } + } + + @Override + public int write(byte[] buffer, int offset, int length, int timeout) throws IOException { + return write(buffer, offset, length, timeout, SEND_CHARACT_UUID); + } + + public int write(byte[] buffer, int offset, int length, int timeout, UUID sendUUID) throws IOException { + synchronized (LOCK_DATA_FRAME) { + dataFrame = null; + } + if (length - offset > MTU) return -1; + if (gatt == null) { + LogUtils.e("Gatt对象异常!"); + disconnect(); + return -1; + } + BluetoothGattService txService = gatt.getService(UART_SERVICE_UUID); + if (txService == null) { + LogUtils.e("没有这个BluetoothGattService: " + UART_SERVICE_UUID); + return -1; + } + BluetoothGattCharacteristic characteristic = txService.getCharacteristic(sendUUID); + if (characteristic == null) { + LogUtils.e("没有这个BluetoothGattCharacteristic: " + sendUUID); + return -1; + } + LogUtils.d("发送的最终值: " + HexUtil.toHexString(buffer)); + characteristic.setValue(buffer); + long startTime = System.currentTimeMillis(); + while (gatt != null && !gatt.writeCharacteristic(characteristic)) { + if (SystemUtils.isTimeout(startTime, timeout)) { + LogUtils.e("write超时!"); + return -1; + } + } + return length - offset; + } + + @Override + public int read(byte[] buffer, int offset, int length, int timeout) throws IOException { + if (gatt == null) { + LogUtils.e("Gatt对象异常!"); + disconnect(); + return -1; + } + long startTime = System.currentTimeMillis(); + if (length > 0) { + while (BUFFER_SERIAL.size() == 0) { + if (SystemUtils.isTimeout(startTime, timeout)) { + return -1; + } + } + // 需要重新开始计时 + startTime = System.currentTimeMillis(); + //从轮询缓冲队列中取出对应长度的数据 + for (int i = offset; i < length; ++i) { + //判断轮询缓冲区的元素是否可用 + if (BUFFER_SERIAL.peek() != null) { + Byte b = BUFFER_SERIAL.poll(); + if (b != null) { + buffer[i] = b; + } else { + if (SystemUtils.isTimeout(startTime, timeout)) { + //LogUtils.d( "read超时!"); + return i - offset; + } + } + } + } + } else { + synchronized (LOCK_DATA_FRAME) { + while (dataFrame == null) { + if (SystemUtils.isTimeout(startTime, timeout)) { + return -1; + } + } + System.arraycopy(dataFrame, 0, buffer, 0, dataFrame.length); + int len = dataFrame.length; + LogUtils.d("接收到的数据帧: " + HexUtil.toHexString(dataFrame)); + dataFrame = null; + return len; + } + } + //Log.d(TAG, "最终的接收字节转换: " + new String(buffer)); + //TODO 返回的是当前读取到的缓冲区的数据的长度(实际长度)! + return length - offset; + } + + @Override + public void flush() throws IOException { + BUFFER_SERIAL.clear(); + } + + @Override + public void close() throws IOException { + synchronized (thiz) { + if (gatt != null) { + gatt.disconnect(); + gatt.close(); + ClsUtils.refreshDeviceCache(gatt); + gatt = null; + isConnected = false; + LogUtils.d("关闭Gatt执行完成!"); + } + } + } + + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + for (BluetoothGattCallback callback : gattCallbacks) { + callback.onConnectionStateChange(gatt, status, newState); + } + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + for (BluetoothGattCallback callback : gattCallbacks) { + callback.onServicesDiscovered(gatt, status); + } + } + + @Override + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + for (BluetoothGattCallback callback : gattCallbacks) { + callback.onDescriptorWrite(gatt, descriptor, status); + } + } + + @Override + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + for (BluetoothGattCallback callback : gattCallbacks) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + callback.onMtuChanged(gatt, mtu, status); + } + } + } + + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + for (BluetoothGattCallback callback : gattCallbacks) { + callback.onCharacteristicWrite(gatt, characteristic, status); + } + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + for (BluetoothGattCallback callback : gattCallbacks) { + callback.onCharacteristicChanged(gatt, characteristic); + } + } + + public boolean isChameleonMini(byte[] raw) { + BLERawUtils.Details details = BLERawUtils.find(7, raw); + if (details != null) { // 查询成功,发现有效的包! + byte[] bytes = details.getValue(); + if (bytes != null) { + // 匹配服务的UUID! + return Arrays.equals(BleSerialControl.UUID_RAW_MINI, bytes); + } + } + return false; + } + + public boolean isChameleonTiny(byte[] raw) { + return !isChameleonMini(raw); + } + + public boolean isDeviceNoBond(String address) { + return bluetoothAdapter + .getRemoteDevice(address) + .getBondState() == BluetoothDevice.BOND_NONE; + } + + public boolean isDeviceNoBond(DevBean devBean) { + return isDeviceNoBond(devBean.getMacAddress()); + } + + public boolean connectNoCallback(DevBean t, BluetoothDevice device) { + synchronized (thiz) { + if (isConnected) { + LogUtils.d("isConnected是TRUE状态,可能设备并未断开连接!"); + return true; + } + closeNoException(); + if (t == null) { + return false; + } + // 结束搜索! + bluetoothAdapter.cancelDiscovery(); + // 重置链接任务状态! + resetTaskStatus(); + // 尝试绑定,如果返回True则是进入绑定进程 + boolean isMINI = isChameleonMini((byte[]) t.getObject()); + if (!isMINI && ClsUtils.createBond(t.getClass(), device)) { + // 此时,我们需要在广播中继续链接GATT + // 需要只等待绑定完成,而不是直接链接 + LogUtils.d("配对开始,将在等待广播结果!"); + if (isBondSuccessful()) { + connectGatt(device); + } else { + resetTaskStatus(); + return false; + } + } else { + // 不可以进入绑定,可能是绑定过了,或者是MINI,直接链接。 + connectGatt(device); + LogUtils.d("无法配对,可能是绑定过了,或者是MINI, 将直接链接尝试!"); + } + LogUtils.d("开始等待连接任务执行完成(BLE异步回调初始化)"); + // 等待连接结果! + boolean ret = isTaskExeSuccessful(isMINI ? 1000 * 6 : 1000 * 16); + resetTaskStatus(); + if (ret) { + return true; + } else { + // 连接失败,关闭蓝牙链接! + closeNoException(); + return false; + } + } + } + + /** + * Connect to gatt + */ + private void connectGatt(BluetoothDevice t) { + gatt = t.connectGatt(context, false, this); + } + + /** + * Reset some params + */ + private void resetTaskStatus() { + asyncSyncStatus = 0; + bondAsyncSyncStatus = 0; + } + + /** + * Check connect task is successful. + */ + private boolean isTaskExeSuccessful(int timeout) { + long startTime = System.currentTimeMillis(); + while (asyncSyncStatus == 0) { + if (SystemUtils.isTimeout(startTime, timeout)) { + closeNoException(); + asyncSyncStatus = 0; + LogUtils.d("isTaskExeSuccessful(),连接任务超时。"); + return false; + } + } + // 如果结果值是 1,则成功! + return asyncSyncStatus == 1; + } + + /** + * Check bond task is successful. + */ + private boolean isBondSuccessful() { + long startTime = System.currentTimeMillis(); + int timeout = 1000 * 18; + while (bondAsyncSyncStatus == 0) { + if (asyncSyncStatus == -1) { + LogUtils.d("连接任务出现问题,提前终止绑定过程!"); + return false; + } + if (SystemUtils.isTimeout(startTime, timeout)) { + closeNoException(); + bondAsyncSyncStatus = 0; + LogUtils.d("isBondSuccessful(),连接任务超时。"); + return false; + } + } + // 如果结果值是 1,则成功! + return bondAsyncSyncStatus == 1; + } + + /** + * Get on data receive listener! + */ + public OnDataReceiveListener getOnDataReceiveListener() { + return onDataReceiveListener; + } + + public void setOnDataReceiveListener(OnDataReceiveListener onDataReceiveListener) { + this.onDataReceiveListener = onDataReceiveListener; + } + + /** + * Close device and no exception throw. + */ + private void closeNoException() { + try { + close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Enable Notification + */ + private void enableNotifyOnUARTService(BluetoothGatt gatt, UUID uuid) { + BluetoothGattService service = gatt.getService(UART_SERVICE_UUID); + if (service == null) { + return; + } + BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid); + if (characteristic == null) { + return; + } + gatt.setCharacteristicNotification(characteristic, true); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(RECV_DESC_UUID); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + long startTime = System.currentTimeMillis(); + while (!gatt.writeDescriptor(descriptor)) { + if (SystemUtils.isTimeout(startTime, 2000)) { + asyncSyncStatus = -1; + LogUtils.d("enableNotify()超时。"); + return; + } + } + // Log.d(TAG, "将会尝试开启接收来自设备的广播"); + } + + /** + * The chameleon tiny pro is have ctrl characteristic exists. + */ + public boolean isCtrlCharacteristicExists() { + if (gatt == null) return false; + BluetoothGattService service = gatt.getService(UART_SERVICE_UUID); + if (service == null) return false; + return service.getCharacteristic(CTRL_CHARACT_UUID) != null; + } + + /** + * The new device + */ + public void setDevice(BluetoothDevice device) { + this.device = device; + } + + public boolean isPushDataToBuffer() { + return pushDataToBuffer; + } + + public void setPushDataToBuffer(boolean pushDataToBuffer) { + this.pushDataToBuffer = pushDataToBuffer; + } + + public void addGattCallback(BluetoothGattCallback callback) { + if (!gattCallbacks.contains(callback)) + gattCallbacks.add(callback); + } + + public void removeGattCallback(BluetoothGattCallback callback) { + gattCallbacks.remove(callback); + } + + private boolean requestInterval(int v) { + if (gatt == null) return false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return gatt.requestConnectionPriority(v); + } + return false; + } + + public boolean requestIntervalHigh() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return requestInterval(BluetoothGatt.CONNECTION_PRIORITY_HIGH); + } + return false; + } + + public boolean requestIntervalBalanced() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return requestInterval(BluetoothGatt.CONNECTION_PRIORITY_BALANCED); + } + return false; + } + + public boolean requestIntervalLowPower() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return requestInterval(BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER); + } + return false; + } + + public interface OnDataReceiveListener { + void onReceive(byte[] frame); + } +} diff --git a/appmain/devices/DevCallback.java b/appmain/devices/DevCallback.java new file mode 100644 index 0000000..2e5d7e3 --- /dev/null +++ b/appmain/devices/DevCallback.java @@ -0,0 +1,14 @@ +package com.proxgrind.devices; + +import java.io.Serializable; + +public interface DevCallback extends Serializable { + // 是否符合设备预期 + boolean isDev(T dev); + + //新设备发现回调 + void onAttach(T dev); + + //设备移除回调 + void onDetach(T dev); +} diff --git a/appmain/devices/Device.java b/appmain/devices/Device.java new file mode 100644 index 0000000..97d2822 --- /dev/null +++ b/appmain/devices/Device.java @@ -0,0 +1,12 @@ +package com.proxgrind.devices; + +import java.io.IOException; +import java.io.Serializable; + +public interface Device extends Serializable { + //设备测试连通性! + boolean working() throws IOException; + + //设备关闭! + boolean close() throws IOException; +} diff --git a/appmain/devices/DriverInterface.java b/appmain/devices/DriverInterface.java new file mode 100644 index 0000000..de74afb --- /dev/null +++ b/appmain/devices/DriverInterface.java @@ -0,0 +1,40 @@ +package com.proxgrind.devices; + +import android.app.Application; + +import com.proxgrind.chameleon.posixio.PosixCom; +import com.proxgrind.chameleon.utils.system.AppContextUtils; +import com.proxgrind.chameleon.callback.ConnectCallback; + +/* + * 驱动程序类 + * 泛型1 -> 设备实体类 + * 泛型2 -> 适配器类 + */ +public interface DriverInterface extends PosixCom { + Application context = AppContextUtils.app; + + //注册广播之类的事件 + void register(DevCallback callback); + + //链接到设备 + void connect(Device t, ConnectCallback callback); + + // 设备是否连接! + boolean isDeviceConnected(); + + //得到当前的驱动程序适配器类 + Adapter getAdapter(); + + //得到当前的驱动程序的设备类 + Device getDevice(); + + //断开与设备的链接(在某些设备上不一定是立刻生效的) + void disconnect(); + + //获得驱动的ID! + int getUniqueId(); + + //解注册广播之类的 + void unregister(); +} \ No newline at end of file diff --git a/appmain/devices/EmptyDevice.java b/appmain/devices/EmptyDevice.java new file mode 100644 index 0000000..ec5a1e6 --- /dev/null +++ b/appmain/devices/EmptyDevice.java @@ -0,0 +1,15 @@ +package com.proxgrind.devices; + +import java.io.IOException; + +public class EmptyDevice implements Device { + @Override + public boolean working() throws IOException { + return true; + } + + @Override + public boolean close() throws IOException { + return true; + } +} diff --git a/appmain/devices/UsbSerialControl.java b/appmain/devices/UsbSerialControl.java new file mode 100644 index 0000000..7cdcb54 --- /dev/null +++ b/appmain/devices/UsbSerialControl.java @@ -0,0 +1,367 @@ +package com.proxgrind.devices; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; +import android.util.Log; + +import com.felhr.usbserial.UsbSerialDevice; +import com.felhr.usbserial.UsbSerialInterface; +import com.proxgrind.chameleon.R; +import com.proxgrind.chameleon.callback.ConnectCallback; +import com.proxgrind.chameleon.utils.system.LogUtils; +import com.proxgrind.chameleon.utils.system.SystemUtils; +import com.proxgrind.chameleon.utils.tools.Commons; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +/* + * Usb 2 uart Serial implements + */ +public class UsbSerialControl implements DriverInterface { + + //日志标签 + private static final String LOG_TAG = UsbSerialControl.class.getSimpleName(); + private final int UNIQUE_ID = 0x04; + //广播名称 + private final String USB_PERMISSION_ACTION = "cn.rrg.nfctools.UsbSerialPer"; + //设备名称! + private final String usbName = context.getString(R.string.usb_name); + //串口对象 + private UsbSerialDevice mPort = null; + //单例模式 + private static UsbSerialControl mThiz = null; + //回调接口 + private DevCallback mCallback = null; + //广播接收,由于是单例,因此实际上广播接收也可以设置为单例! + private BroadcastReceiver usbReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + //取到广播的意图 + String action = intent.getAction(); + //对比意图,根据意图做出回调选择 + if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action) || + UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action) || + "cn.rrg.devices.usb_attach_uart".equals(action)) { + Log.d(LOG_TAG, "收到UsbSerial设备寻找的广播!"); + if (mCallback != null) { + if (initUsbSerial(context)) { + //初始化成功则回调串口设备加入方法 + mCallback.onAttach(usbName); + } else { + //不成则打印到LOG + Log.e(LOG_TAG, "no usb permission!"); + } + } + } + + //在申请权限的时候如果成功那么应当进行设备的初始化 + if (USB_PERMISSION_ACTION.equals(action)) { + //get permission success + if (initUsbSerial(context)) { + //初始化成功则回调串口设备加入方法 + mCallback.onAttach(usbName); + } else { + //不成则打印到LOG + Log.e(LOG_TAG, "no usb permission!"); + } + } + + //在设备移除时应当释放USB设备 + if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + //判断并且释放USB串口 + if (mThiz != null) { + try { + mThiz.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //回调设备移除接口 + if (mPort != null) + if (mCallback != null) { + try { + mCallback.onDetach(usbName); + } catch (Exception e) { + e.printStackTrace(); + } + } + isConnected = false; + LogUtils.d("设备移除了。"); + } + } + }; + ; + //注册状态 + private static volatile boolean isRegister = false; + //轮询队列 + private static final Queue recvBufQueue = new ConcurrentLinkedQueue<>(); + // 数据回调! + private static final UsbSerialInterface.UsbReadCallback readCallback = new UsbSerialInterface.UsbReadCallback() { + @Override + public void onReceivedData(byte[] bytes) { + //进行加锁,提高数据吞吐稳定性 + for (byte b : bytes) { + recvBufQueue.add(b); + } + //LogUtils.d("USB自动接收到的数据: " + new String(bytes)); + } + }; + private volatile boolean isConnected = false; + + /*私有化构造方法,懒汉单例模式*/ + private UsbSerialControl() { + //you can't invoke this constructor + //beacause this class is single-instance! + } + + public static UsbSerialControl get() { + synchronized (LOG_TAG) { + if (mThiz != null) { + return mThiz; + } else { + mThiz = new UsbSerialControl(); + } + return mThiz; + } + } + + //串口备初始化函数 + private boolean initUsbSerial(Context context) { + //得到Usb管理器 + UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + if (usbManager == null) return false; + //尝试取出所有可用的列表 + if (usbManager.getDeviceList() == null || usbManager.getDeviceList().size() <= 0) { + Log.d(LOG_TAG, "initUsbSerial() 未发现设备!"); + return false; + } + //迭代集合里面的设备对象 + List devList = new ArrayList<>(usbManager.getDeviceList().values()); + //取出第一个USB对象 + UsbDevice usbDevice = devList.get(0); + //判断设备是否支持 + if (!UsbSerialDevice.isSupported(usbDevice)) { + Log.d(LOG_TAG, "UsbSerial支持检测结果: false"); + return false; + } + //如果对于这个设备没有权限! + if (!usbManager.hasPermission(usbDevice)) { + //发送广播申请权限 + PendingIntent intent = + PendingIntent.getBroadcast(context, + 0, new Intent(USB_PERMISSION_ACTION), 0); + //Log.d(LOG_TAG, "尝试获得USB权限!"); + usbManager.requestPermission(usbDevice, intent); + //当没有权限的时候应当直接返回 + return false; + } + //一切正常返回true! + return connect(); + } + + @Override + public int write(byte[] buffer, int offset, int length, int timeout) throws IOException { + //TODO 注释防止外泄 + if (mPort == null) { + //Log.e(LOG_TAG, "port is null"); + return -1; + } + //构建一个可用字节的缓冲区 + byte[] tmpBuf = new byte[length - offset]; + //将可用字节灌装到定义的缓冲区 + System.arraycopy(buffer, offset, tmpBuf, 0, tmpBuf.length); + //在同步块中进行提交操作! + mPort.write(tmpBuf); + //Log.d(LOG_TAG, "send: " + new String(buffer) + ", len" + buffer.length); + //Log.d(LOG_TAG, "发送的字节: " + HexUtil.toHexString(tmpBuf, 0, length - offset)); + return length - offset; + } + + @Override + public int read(byte[] buffer, int offset, int length, int timeout) throws IOException { + if (mPort == null) { + //Log.e(LOG_TAG, "port is null"); + return -1; + } + long startTime = System.currentTimeMillis(); + //Log.d(LOG_TAG, "数据缓冲区内长度正常,开始拷贝..."); + while (recvBufQueue.size() < length) { + if (SystemUtils.isTimeout(startTime, timeout)) { + return -1; + } + } + int len = 0; + //从轮询缓冲队列中取出对应长度的数据 + for (int i = offset; i < length; ++i) { + //判断轮询缓冲区的元素是否可用 + if (recvBufQueue.peek() != null) { + Byte b = recvBufQueue.poll(); + if (b != null) { + buffer[i] = b; + ++len; + } + } + } + //Log.d(LOG_TAG, "接收到的数据: " + HexUtil.toHexString(buffer, offset, length)); + //TODO 返回的是当前读取到的缓冲区的数据的长度(实际长度)! + return len; + } + + @Override + public void flush() throws IOException { + //don't support flush + } + + @Override + public void close() throws IOException { + isConnected = false; + if (mPort == null) { + Log.e(LOG_TAG, "port is null"); + return; + } + mPort.close(); + } + + @Override + public void register(DevCallback callback) { + mCallback = callback; + if (isRegister) { + LogUtils.d("USB驱动可能已经注册过了。"); + return; + } + IntentFilter filter = new IntentFilter(USB_PERMISSION_ACTION); + filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction("cn.rrg.devices.usb_attach_uart"); + try { + context.registerReceiver(usbReceiver, filter); + LogUtils.d("USB注册完成!"); + } catch (Exception e) { + e.printStackTrace(); + } + isRegister = true; + } + + @Override + public void connect(String t, ConnectCallback callback) { + if (connect()) { + isConnected = true; + callback.onConnectSucces(); + } else { + isConnected = false; + callback.onConnectFail(); + } + } + + @Override + public boolean isDeviceConnected() { + return mPort != null && isConnected; + } + + public boolean connect() { + if (mPort != null) { + mPort.close(); + mPort = null; + } + isConnected = false; + //得到Usb管理器 + UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + if (usbManager == null) { + return false; + } + //迭代集合里面的设备对象 + List devList = new ArrayList<>(usbManager.getDeviceList().values()); + if (devList.size() <= 0) { + return false; + } + //取出第一个USB对象 + UsbDevice usbDevice = devList.get(0); + //USB链接! + UsbDeviceConnection connection = usbManager.openDevice(usbDevice); + //判断是否非空,为空证明没有权限 + if (connection == null) { + //发送广播申请权限 + PendingIntent intent = PendingIntent.getBroadcast(context, 0, new Intent(USB_PERMISSION_ACTION), 0); + //Log.d(LOG_TAG, "尝试获得USB权限!"); + usbManager.requestPermission(usbDevice, intent); + //当没有权限的时候应当直接返回 + return false; + } + Log.d(LOG_TAG, "开始尝试打开设备!"); + //得到串口端口对象 + mPort = UsbSerialDevice.createUsbSerialDevice(usbDevice, connection); + //尝试打开串口 + if (mPort.open()) { + //设置波特率 + mPort.setBaudRate(115200); + //设置数据位 + mPort.setDataBits(UsbSerialDevice.DATA_BITS_8); + //设置停止位 + mPort.setStopBits(UsbSerialDevice.STOP_BITS_1); + //奇偶校验值 + mPort.setParity(UsbSerialDevice.PARITY_NONE); + //数据流控制 + mPort.setFlowControl(UsbSerialDevice.FLOW_CONTROL_OFF); + //新数据回调 + mPort.read(readCallback); + Log.d(LOG_TAG, "Usb链接成功,通信创建成功!!"); + return true; + } + return false; + } + + public boolean connectAndSetConnected() { + return isConnected = connect(); + } + + @Override + public UsbManager getAdapter() { + return (UsbManager) context.getSystemService(Context.USB_SERVICE); + } + + @Override + public String getDevice() { + return mPort != null ? mPort.getClass().getSimpleName() : null; + } + + @Override + public void disconnect() { + //TODO 暂时不做处理 + try { + close(); + } catch (IOException e) { + e.printStackTrace(); + } + isConnected = false; + } + + @Override + public int getUniqueId() { + return UNIQUE_ID; + } + + @Override + public void unregister() { + //广播解注册 + if (isRegister) { + try { + context.unregisterReceiver(usbReceiver); + isRegister = false; + LogUtils.d("解注册USB完成!"); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +}