package com.blakequ.bluetooth_manager_lib.connect; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Looper; import com.blakequ.bluetooth_manager_lib.util.BluetoothUtils; import com.blankj.utilcode.util.LogUtils; import java.lang.reflect.Field; import java.util.List; import java.util.Queue; import java.util.UUID; /** * Copyright (C) BlakeQu All Rights Reserved *

* Licensed under the blakequ.com License, Version 1.0 (the "License"); * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *

* author : quhao
* date : 2016/8/19 11:16
* last modify author :
* version : 1.0
* description:bluetooth connect interface */ public abstract class BluetoothConnectInterface { protected static final String TAG = "BluetoothConnectInterface"; protected Context context; private BluetoothOperatorQueue mOpratorQueue; private Handler mHandler = new Handler(Looper.getMainLooper()); public BluetoothConnectInterface(Context context){ this.context = context; mOpratorQueue = new BluetoothOperatorQueue(); } /** * release resource */ public abstract void release(); /** * get gatt connect callback * @return */ protected abstract BluetoothGattCallback getBluetoothGattCallback(); protected abstract String getServiceUUID(); /** * get device gatt service, if not will return null * @param address * @return null if not find gatt service */ public abstract BluetoothGatt getBluetoothGatt(String address); /** * get the list of subscribe * @return */ protected abstract Queue getSubscribeDataQueue(); /** * invoke when bluetooth disconnect * @param gatt */ protected abstract void onDeviceDisconnect(BluetoothGatt gatt, int errorState); /** * invoke when bluetooth connected * @param gatt */ protected abstract void onDeviceConnected(BluetoothGatt gatt); /** * invoke when fail to discover service * @param gatt */ protected abstract void onDiscoverServicesFail(BluetoothGatt gatt); /** * invoke when success to discover service * @param gatt */ protected abstract void onDiscoverServicesSuccess(BluetoothGatt gatt); /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != Looper.getMainLooper().getThread()) { mHandler.post(action); } else { action.run(); } } protected Handler getMainLooperHandler(){ return mHandler; } protected BluetoothGattCallback gattCallback = new BluetoothGattCallback() { @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onCharacteristicChanged(gatt, characteristic); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); LogUtils.i("onCharacteristicRead data status:" + GattError.parseConnectionError(status) + " " + characteristic.getUuid().toString()); mOpratorQueue.nextOperator(); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onCharacteristicRead(gatt, characteristic, status); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); LogUtils.i("onCharacteristicWrite write status:" + GattError.parseConnectionError(status)); mOpratorQueue.nextOperator(); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onCharacteristicWrite(gatt, characteristic, status); } @Override public void onConnectionStateChange(final BluetoothGatt gatt, int status, final int newState) { super.onConnectionStateChange(gatt, status, newState); //status=133是GATT_ERROR错误http://stackoverflow.com/questions/25330938/android-bluetoothgatt-status-133-register-callback //http://www.loverobots.cn/android-ble-connection-solution-bluetoothgatt-status-133.html LogUtils.i("onConnectionStateChange gattStatus=" + GattError.parseConnectionError(status) + " newStatus=" + (newState == BluetoothProfile.STATE_CONNECTED ? "CONNECTED" : "DISCONNECTED")); //不同的手机当蓝牙关闭,设备断开(重启,远离)返回的状态不一样,newState都一样是DISCONNECTED,设备切换不会产生影响 if (status == BluetoothGatt.GATT_SUCCESS) { if (newState == BluetoothProfile.STATE_CONNECTED) {//调用connect会调用 LogUtils.i("Connected to GATT server"); // Attempts to discover services after successful connection. runOnUiThread(new Runnable() { @Override public void run() { onDeviceConnected(gatt); if (gatt != null && !gatt.discoverServices()) { LogUtils.e("onConnectionStateChange start service discovery fail! Thread:" + Thread.currentThread()); } } }); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//调用disconnect会调用,设备断开或蓝牙关闭会进入 runOnUiThread(new Runnable() { @Override public void run() { onDeviceDisconnect(gatt, newState); } }); } } else{ //调用connect和disconnect出错后会进入,设备断开或蓝牙关闭会进入 runOnUiThread(new Runnable() { @Override public void run() { onDeviceDisconnect(gatt, newState); } }); } if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onConnectionStateChange(gatt, status, newState); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); LogUtils.i("onDescriptorRead status=" + GattError.parseConnectionError(status)); mOpratorQueue.nextOperator(); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); LogUtils.i("onDescriptorWrite status=" + GattError.parseConnectionError(status)); mOpratorQueue.nextOperator(); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onDescriptorWrite(gatt, descriptor, status); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onMtuChanged(gatt, mtu, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onReadRemoteRssi(gatt, rssi, status); } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onReliableWriteCompleted(gatt, status); } @Override public void onServicesDiscovered(final BluetoothGatt gatt, int status) { LogUtils.i("onServicesDiscovered status=" + GattError.parseConnectionError(status)); if (status == BluetoothGatt.GATT_SUCCESS) { //start subscribe data runOnUiThread(new Runnable() { @Override public void run() { onDiscoverServicesSuccess(gatt); if (gatt != null){ startSubscribe(gatt); } } }); }else { LogUtils.e("onServicesDiscovered fail!"); runOnUiThread(new Runnable() { @Override public void run() { onDiscoverServicesFail(gatt); } }); } if (getBluetoothGattCallback() != null) getBluetoothGattCallback().onServicesDiscovered(gatt, status); } }; /** * start subscribe data, add data to subscribe list before invoke this method *

You should invoke this method after onServicesDiscovered, otherwise can not find service

* @param bluetoothGatt * @return boolean is success start read or write character */ public boolean startSubscribe(BluetoothGatt bluetoothGatt){ if (bluetoothGatt == null){ LogUtils.e("Fail to subscribe, BluetoothGatt is null"); return false; } boolean isSuccess = subscribe(bluetoothGatt.getDevice().getAddress()); mOpratorQueue.start(bluetoothGatt); return isSuccess; } /** * 订阅蓝牙设备通知及读写数据 * @return */ protected boolean subscribe(String address){ BluetoothGatt mBluetoothGatt = getBluetoothGatt(address); if (mBluetoothGatt == null){ LogUtils.e("can not subscribe to ble device info "+address); return false; } mOpratorQueue.clean(); if (isEmpty(getServiceUUID())){ LogUtils.e("Service UUID is null"); return false; } //check subscribe list if (getSubscribeDataQueue() == null && getSubscribeDataQueue().size() > 0){ LogUtils.e("Subscribe BLE data is null, you must invoke addBluetoothSubscribeData to add data"); return false; } BluetoothGattService gattService = mBluetoothGatt.getService(UUID.fromString(getServiceUUID())); if (gattService != null){ for (BluetoothSubScribeData data:getSubscribeDataQueue()){ final BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(data.getCharacteristicUUID()); if (characteristic != null){ switch (data.getOperatorType()){ case CHAR_WIRTE: if (BluetoothUtils.isCharacteristicWrite(characteristic.getProperties())){ characteristic.setValue(data.getCharacteristicValue()); mOpratorQueue.addOperator(characteristic, true); }else{ LogUtils.e("Fail to write characteristic, not have write property , uuid:"+characteristic.getUuid()+" ,property:"+characteristic.getProperties()); } break; case CHAR_READ: //bug fix:samsung phone bug, can not read value if (checkIsSamsung()){ setProperty(characteristic); } if(BluetoothUtils.isCharacteristicRead(characteristic.getProperties())){ mOpratorQueue.addOperator(characteristic, false); }else{ LogUtils.e("Fail to read characteristic, not have read property , uuid:" + characteristic.getUuid() + " ,property:" + characteristic.getProperties()); } break; case DESC_READ: BluetoothGattDescriptor descriptor = characteristic.getDescriptor(data.getDescriptorUUID()); if (descriptor != null){ mOpratorQueue.addOperator(descriptor, false); }else { LogUtils.e("Fail to get descriptor read uuid:"+data.getDescriptorUUID()); } break; case DESC_WRITE: BluetoothGattDescriptor descriptor2 = characteristic.getDescriptor(data.getDescriptorUUID()); if (descriptor2 != null){ descriptor2.setValue(data.getDescriptorValue()); mOpratorQueue.addOperator(descriptor2, true); }else { LogUtils.e("Fail to get descriptor write uuid:"+data.getDescriptorUUID()); } break; case NOTIFY: if(BluetoothUtils.isCharacteristicNotify(characteristic.getProperties())){ mBluetoothGatt.setCharacteristicNotification(characteristic, true); BluetoothGattDescriptor descriptor3 = characteristic.getDescriptor(data.getDescriptorUUID()); if (descriptor3 != null){ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0){ descriptor3.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); }else if((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0){ //两个都是通知的意思,notify和indication的区别在于,notify只是将你要发的数据发送给手机,没有确认机制, //不会保证数据发送是否到达。而indication的方式在手机收到数据时会主动回一个ack回来。即有确认机制,只有收 //到这个ack你才能继续发送下一个数据。这保证了数据的正确到达,也起到了流控的作用。所以在打开通知的时候,需要设置一下。 descriptor3.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); } mOpratorQueue.addOperator(descriptor3, true); }else { LogUtils.e("Fail to get notify descriptor uuid:"+data.getDescriptorUUID()); } }else{ LogUtils.e("Fail to notify characteristic, not have notify property , uuid:" + characteristic.getUuid() + " ,property:" + characteristic.getProperties()); } break; } }else { LogUtils.e("Fail to get characteristic service uuid:"+data.getCharacteristicUUID()); } } }else { LogUtils.e("Can not get gatt service uuid:"+getServiceUUID()); return false; } return true; } /** * 设置属性,设置读权限 * @param flagReadChar */ @SuppressLint("SoonBlockedPrivateApi") private void setProperty(BluetoothGattCharacteristic flagReadChar){ Field properField = null; try { properField = flagReadChar.getClass().getDeclaredField("mProperties"); properField.setAccessible(true); properField.set(flagReadChar, 10); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * 判断手机类型 * @return */ protected boolean checkIsSamsung() { String brand = android.os.Build.BRAND; if (brand.toLowerCase().equals("samsung")) { return true; } return false; } public boolean isEmpty(String str) { return str == null || str.length() == 0; } public boolean isEmpty(List sourceList) { return sourceList == null || sourceList.size() == 0; } }