Files
2020-08-27 12:24:21 +08:00

668 lines
25 KiB
Java

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<DevBean, BluetoothAdapter> {
// 日志标签!
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<Byte> BUFFER_SERIAL = new ConcurrentLinkedQueue<>();
// 蓝牙相关
private DevCallback<BluetoothDevice> devCallback;
private List<BluetoothGattCallback> 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);
}
}