diff --git a/lib/subutil/src/main/java/com/blankj/subutil/util/BtUtils.java b/lib/subutil/src/main/java/com/blankj/subutil/util/BtUtils.java new file mode 100644 index 0000000000..8940239408 --- /dev/null +++ b/lib/subutil/src/main/java/com/blankj/subutil/util/BtUtils.java @@ -0,0 +1,903 @@ +package com.blankj.subutil.util; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.le.ScanCallback; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.RequiresApi; +import android.text.TextUtils; + +import com.blankj.utilcode.util.ConvertUtils; + +import java.util.List; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SIG BT Util on Android platform。 + * + *
+ * author: bangbang.S + * time : 2020/12/11 + * desc : 蓝牙相关工具类 + *+ */ +public class BtUtils { + private static final String TAG = "BtUtils"; + + //https://www.bluetooth.com/specifications/assigned-numbers/service-discovery + public static final UUID BASE_UUID = toUuid("00000000-0000-1000-8000-00805F9B34FB"); + public static final long BASE_UUID_LEAST = BASE_UUID.getLeastSignificantBits(); + public static final long BASE_UUID_MOST = BASE_UUID.getMostSignificantBits(); + + private static final Pattern PATTERN_16_UUID = Pattern.compile("0000([0-9A-F]{4})-0000-1000-8000-00805F9B34FB"); + private static final Pattern PATTERN_32_UUID = Pattern.compile("([0-9A-F]{8})-0000-1000-8000-00805F9B34FB"); + + //https://www.bluetooth.com/specifications/gatt/descriptors + public static final UUID DESCRIPTOR_CHARA_USER_DESC = toUuid("2901"); + public static final UUID DESCRIPTOR_CHARA_CONFIG = toUuid("2902"); + + /** + * convert uuid string short (16bits or full form) to UUID + * @param uuid + * @return + * + * @see service-discovery + */ + static public final UUID toUuid(String uuid) { + return UUID.fromString(uuid.length() == 4 + ? "0000" + uuid + "-0000-1000-8000-00805f9b34fb" + : uuid); + } + + /** + * convert uuid to short form (16bits or 32bits) + * @param uuid + * @return short form uuid string if possible, other empty string, i.e. "". + */ + public static String toShortUuidStringOrEmpty(String uuid) { + uuid = uuid.toUpperCase(); + String shortUuid = ""; + Matcher m = PATTERN_16_UUID.matcher(uuid); + if (m.matches()) { + shortUuid = m.group(1); + } else { + m = PATTERN_32_UUID.matcher(uuid); + if (m.matches()) { + shortUuid = m.group(1); + } + } + + return shortUuid; + } + + /** + * convert uuid to short form (16bits or 32bits) + * @param uuid + * @return short form uuid string if possible, other original uuid string. + */ + public static String toShortUuidString(UUID uuid) { + return toShortUuidString(uuid.toString()); + } + + /** + * convert uuid to short form (16bits or 32bits) + * @param uuid + * @return short form uuid string if possible, other original uuid string. + */ + public static String toShortUuidString(String uuid) { + String shortUuid = toShortUuidStringOrEmpty(uuid); + if (TextUtils.isEmpty(shortUuid)) { + shortUuid = uuid; + } + + return shortUuid; + } + + /** + * + * convert status param in + * {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int) onConnectionStateChange} + * callback to String + * + * @param status + * @return + * + * @see gatt_api.h + * @See {@link #toGattOperateStatusString(int)} + */ + public static final String toGattConnectionStatusString(int status) { + String statusStr = null; + switch (status) { + case BluetoothGatt.GATT_SUCCESS: + statusStr = "GATT_SUCCESS"; + break; + + case 0x01: + statusStr = "GATT_CONN_L2C_FAILURE"; + break; + case 0x08: + statusStr = "GATT_CONN_TIMEOUT"; + break; + case 0x13: + statusStr = "GATT_CONN_TERMINATE_PEER_USER"; + break; + case 0x16: + statusStr = "GATT_CONN_TERMINATE_LOCAL_HOST"; + break; + case 0x003E: + statusStr = "GATT_CONN_FAIL_ESTABLISH"; + break; + case 0x0022: + statusStr = "GATT_CONN_LMP_TIMEOUT"; + break; + case 0x0085: + statusStr = "GATT_ERROR"; + break; + case 0x0100: + statusStr = "GATT_CONN_CANCEL"; + break; + default: + statusStr = "UNKNOWN_STATUS"; + } + + statusStr += "[" + status + " 0x" + String.format("%1$04X", status) + "]"; + + return statusStr; + } + + + + /** + * convert status param in + * all callbacks in {@link android.bluetooth.BluetoothGattCallback} except + * {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int) onConnectionStateChange} + * to String + * + * @param status + * @return + * + * @See {@link #toGattOperateStatusString(int)} + * @see gatt_api.h + * @see 错误码 + */ + public static final String toGattOperateStatusString(int status) { + String statusStr = null; + switch (status) { + case BluetoothGatt.GATT_SUCCESS: + statusStr = "GATT_SUCCESS"; + break; + case BluetoothGatt.GATT_CONNECTION_CONGESTED: + statusStr = "GATT_CONNECTION_CONGESTED"; + break; + case BluetoothGatt.GATT_FAILURE: + statusStr = "GATT_FAILURE"; + break; + case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION: + statusStr = "GATT_INSUFFICIENT_AUTHENTICATION"; + break; + case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION: + statusStr = "GATT_INSUFFICIENT_ENCRYPTION"; + break; + case BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH: + statusStr = "GATT_INVALID_ATTRIBUTE_LENGTH"; + break; + case BluetoothGatt.GATT_INVALID_OFFSET: + statusStr = "GATT_INVALID_OFFSET"; + break; + case BluetoothGatt.GATT_READ_NOT_PERMITTED: + statusStr = "GATT_READ_NOT_PERMITTED"; + break; + case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED: + statusStr = "GATT_SUCCESS"; + break; + case BluetoothGatt.GATT_WRITE_NOT_PERMITTED: + statusStr = "GATT_WRITE_NOT_PERMITTED"; + break; + + default: + statusStr += "UNKNOWN_STATUS[0x" + String.format("%1$04X", status) + "]"; + break; + } + + if (statusStr.contains("UNKNOWN")) { + switch (status) { + + //http://androidxref.com/4.4_r1/xref/external/bluetooth/bluedroid/stack/include/gatt_api.h + case 0x0001: + statusStr = "GATT_INVALID_HANDLE"; + break; + case 0x0002: + statusStr = "GATT_READ_NOT_PERMIT"; + break; + case 0x0003: + statusStr = "GATT_WRITE_NOT_PERMIT"; + break; + case 0x0004: + statusStr = "GATT_INVALID_PDU"; + break; + case 0x0005: + statusStr = "GATT_INSUF_AUTHENTICATION"; + break; + case 0x0006: + statusStr = "GATT_REQ_NOT_SUPPORTED"; + break; + case 0x0007: + statusStr = "GATT_INVALID_OFFSET"; + break; + case 0x0008: + statusStr = "GATT_INSUF_AUTHORIZATION"; + break; + case 0x0009: + statusStr = "GATT_PREPARE_Q_FULL"; + break; + case 0x000a: + statusStr = "GATT_NOT_FOUND"; + break; + case 0x000b: + statusStr = "GATT_NOT_LONG"; + break; + case 0x000C: + statusStr = "GATT_INSUF_KEY_SIZE"; + break; + case 0x000D: + statusStr = "GATT_INVALID_ATTR_LEN"; + break; + case 0x000E: + statusStr = "GATT_ERR_UNLIKELY"; + break; + case 0x000F: + statusStr = "GATT INSUF ENCRYPTION"; + break; + case 0x0010: + statusStr = "GATT_UNSUPPORT_GRP_TYPE"; + break; + case 0x0011: + statusStr = "GATT_INSUF_RESOURCE"; + break; + case 0x13: + statusStr = "GATT_CONN_TERMINATE_PEER_USER"; + break; + case 0x16: + statusStr = "GATT_CONN_TERMINATE_LOCAL_HOST"; + break; + + case 0x003A: + statusStr = "GATT CONTROLLER BUSY"; + break; + + case 0x0080: + statusStr = "GATT_NO_RESOURCES"; + break; + case 0x0081: + statusStr = "GATT_INTERNAL_ERROR"; + break; + case 0x0082: + statusStr = "GATT_WRONG_STATE"; + break; + case 0x0083: + statusStr = "GATT_DB_FULL"; + break; + case 0x0084: + statusStr = "GATT_BUSY"; + break; + case 0x0085: + statusStr = "GATT_ERROR"; + break; + case 0x0086: + statusStr = "GATT_CMD_STARTED"; + break; + case 0x0087: + statusStr = "GATT_ILLEGAL_PARAMETER"; + break; + case 0x0088: + statusStr = "GATT_PENDING"; + break; + case 0x0089: + statusStr = "GATT_AUTH_FAIL"; + break; + case 0x008a: + statusStr = "GATT_MORE"; + break; + case 0x008b: + statusStr = "GATT_INVALID_CFG"; + break; + case 0x008c: + statusStr = "GATT_SERVICE_STARTED"; + break; + case 0x008D: + statusStr = "GATT_ENCRYPED_NO_MITM"; + break; + case 0x008e: + statusStr = "GATT_NOT_ENCRYPTED"; + break; + case 0x008f: + statusStr = "GATT CONGESTED"; + break; + + case 0x003E: + statusStr = "GATT_CONN_FAIL_ESTABLISH"; + break; + case 0x0022: + statusStr = "GATT_CONN_LMP_TIMEOUT "; + break; + case 0x0100: + statusStr = "GATT_CONN_CANCEL"; + break; + case 0x0101: + statusStr = "TOO MANY OPEN CONNECTIONS"; + break; + } + statusStr += "[" + status + " 0x" + String.format("%1$04X", status) + "]"; +// statusStr += " see android source code gatt_api.h"; + } + + return statusStr; + } + + /** + * convert BluetoothDevice.DEVICE_TYPE_ to String + * @param type + * @return + */ + public static final String toBluetoothDeviceTypeString(int type) { + String typeStr = "unknown type[" + type + "]"; + switch (type) { + case BluetoothDevice.DEVICE_TYPE_CLASSIC: + typeStr = "DEVICE_TYPE_CLASSIC"; + break; + case BluetoothDevice.DEVICE_TYPE_LE: + typeStr = "DEVICE_TYPE_LE"; + break; + case BluetoothDevice.DEVICE_TYPE_DUAL: + typeStr = "DEVICE_TYPE_DUAL"; + break; + case BluetoothDevice.DEVICE_TYPE_UNKNOWN: + typeStr = "DEVICE_TYPE_UNKNOWN"; + break; + default: + typeStr = "UNKNOWN_DEVICE_TYPE[" + type + "]"; + } + + return typeStr; + } + + /** + * convert BluetoothGatt.CONNECTION_PRIORITY_ to String + * @param priority + * @return + */ + public static final String toConnectionPriorityString(int priority) { + String priorityStr = "unknown priority[" + priority + "]"; + switch (priority) { + case BluetoothGatt.CONNECTION_PRIORITY_BALANCED: + priorityStr = "CONNECTION_PRIORITY_BALANCED"; + break; + case BluetoothGatt.CONNECTION_PRIORITY_HIGH: + priorityStr = "CONNECTION_PRIORITY_HIGH"; + break; + case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER: + priorityStr = "CONNECTION_PRIORITY_LOW_POWER"; + break; + default: + priorityStr = "UNKNOWN_PRIORITY[" + priority + "]"; + } + return priorityStr; + } + + /** + * convert BluetoothDevice.BOND_ to String + * @param bondState + * @return + */ + public static final String toBluetoothDeviceBondStateString(int bondState) { + String bondStr = "unknown state[" + bondState + "]"; + switch (bondState) { + case BluetoothDevice.BOND_BONDED: + bondStr = "BOND_BONDED"; + break; + case BluetoothDevice.BOND_BONDING: + bondStr = "BOND_BONDING"; + break; + case BluetoothDevice.BOND_NONE: + bondStr = "BOND_NONE"; + break; + default: + bondStr = "UNKNOWN_BOND_TYPE[" + bondState + "]"; + } + return bondStr; + } + + /** + * convert BluetoothGattService.SERVICE_ to String + * @param serviceType + * @return + */ + public static final String toBluetoothGattServiceType(int serviceType) { + String typeStr = null; + switch (serviceType) { + case BluetoothGattService.SERVICE_TYPE_PRIMARY: + typeStr = "PRIMARY"; + break; + case BluetoothGattService.SERVICE_TYPE_SECONDARY: + typeStr = "SECONDARY"; + break; + default: + typeStr = "UNKNOWN_SERVICE_TYPE[" + serviceType + "]"; + } + + return typeStr; + } + + /** + * convert BluetoothGattCharacteristic.WRITE_TYPE_ to String + * @param writeType + * @return + */ + public static final String toCharacteristicWriteTypeStr(int writeType) { + String w = null; + switch (writeType) { + case BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT: + w = "WRITE_TYPE_DEFAULT"; + break; + case BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE: + w = "WRITE_TYPE_NO_RESPONSE"; + break; + case BluetoothGattCharacteristic.WRITE_TYPE_SIGNED: + w = "WRITE_TYPE_SIGNED"; + break; + default: + w = "UNKNOWN_WRITE_TYPE[" + writeType + "]"; + } + return w; + } + + /** + * convert BluetoothGattCharacteristic properties (man contain multi properties) to String + * @param properties + * @return + */ + public static final String toPropertiesStr(int properties) { + String p = ""; + int i = 0; + while (i < Integer.SIZE) { + String pro = toPropertyStr(properties & 1 << i); + if (!TextUtils.isEmpty(pro)) { + p += pro + " "; + } + + i++; + } + + p = p.trim(); + return p; + } + + /** + * convert BluetoothGattCharacteristic.PROPERTY_ to String + * @param property + * @return + */ + public static final String toPropertyStr(int property) { + String p = null; + switch (property) { + case BluetoothGattCharacteristic.PROPERTY_BROADCAST: + p = "BROADCAST"; + break; + case BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS: + p = "EXTENDED_PROPS"; + break; + case BluetoothGattCharacteristic.PROPERTY_INDICATE: + p = "INDICATE"; + break; + case BluetoothGattCharacteristic.PROPERTY_NOTIFY: + p = "NOTIFY"; + break; + case BluetoothGattCharacteristic.PROPERTY_READ: + p = "READ"; + break; + case BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE: + p = "SIGNED_WRITE"; + break; + case BluetoothGattCharacteristic.PROPERTY_WRITE: + p = "WRITE"; + break; + case BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE: + p = "WRITE_NO_RESPONSE"; + break; + default: + p = "UNKNOWN_PROPERTY[" + property + "]"; + } + return p; + } + + /** + * convert BluetoothGattDescriptor.PERMISSION_ to String + * @param permission + * @return + */ + public static final String toDescriptorPermissionStr(int permission) { + String p = null; + switch (permission) { + case BluetoothGattDescriptor.PERMISSION_READ: + p = "READ"; + break; + case BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED: + p = "READ_ENCRYPTED"; + break; + case BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED_MITM: + p = "READ_ENCRYPTED_MITM"; + break; + case BluetoothGattDescriptor.PERMISSION_WRITE: + p = "WRITE"; + break; + case BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED: + p = "WRITE_ENCRYPTED"; + break; + case BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED: + p = "WRITE_SIGNED"; + break; + case BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED_MITM: + p = "READ_ENCRYPTED_MITM"; + break; + case BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED_MITM: + p = "WRITE_SIGNED_MITM"; + break; + default: + p = "UNKNOWN_PERMISSION[" + permission + "]"; + } + + return p; + } + + /** + * convert BluetoothGattCharacteristic.PERMISSION_ to String + * @param permission + * @return + */ + public static final String toCharacterPermissionStr(int permission) { + String p = null; + switch (permission) { + case BluetoothGattCharacteristic.PERMISSION_READ: + p = "READ"; + break; + case BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED: + p = "READ_ENCRYPTED"; + break; + case BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM: + p = "READ_ENCRYPTED_MITM"; + break; + case BluetoothGattCharacteristic.PERMISSION_WRITE: + p = "WRITE"; + break; + case BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED: + p = "WRITE_ENCRYPTED"; + break; + case BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED: + p = "WRITE_SIGNED"; + break; + case BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM: + p = "PERMISSION_WRITE_ENCRYPTED_MITM"; + break; + case BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM: + p = "WRITE_SIGNED_MITM"; + break; + default: + p = "UNKNOWN_PERMISSION[" + permission + "]"; + } + + return p; + } + + private static final String makeIndent(int indent) { + String indentStr = ""; + for (int i = 0; i < indent; i++) { + indentStr += " "; + } + return indentStr; + } + + /** + * convert ScanCallback.SCAN_FAILED_ to String + * @param reason + * @return + */ + public static String toScanFailedReasonString(int reason){ + String reasonStr = null; + switch (reason) { + case ScanCallback.SCAN_FAILED_ALREADY_STARTED: + reasonStr = "SCAN_FAILED_ALREADY_STARTED"; + break; + case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED: + reasonStr = "SCAN_FAILED_APPLICATION_REGISTRATION_FAILED"; + break; + case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED: + reasonStr = "SCAN_FAILED_FEATURE_UNSUPPORTED"; + break; + case ScanCallback.SCAN_FAILED_INTERNAL_ERROR: + reasonStr = "SCAN_FAILED_INTERNAL_ERROR"; + break; + default: + reasonStr = "UNKNOWN_REASON[" + reason + "]"; + } + return reasonStr; + } + + /** + * convert BluetoothAdapter.STATE_ to String + * @param state + * @return + */ + public static String toBluetoothAdapterStateString(int state){ + String stateStr = null; + switch (state) { + case BluetoothAdapter.STATE_ON: + stateStr = "STATE_ON"; + break; + case BluetoothAdapter.STATE_OFF: + stateStr = "STATE_OFF"; + break; + case BluetoothAdapter.STATE_TURNING_OFF: + stateStr = "STATE_TURNING_OFF"; + break; + case BluetoothAdapter.STATE_TURNING_ON: + stateStr = "STATE_TURNING_ON"; + break; + case 14://BluetoothAdapter.STATE_BLE_TURNING_ON: + stateStr = "STATE_BLE_TURNING_ON"; + break; + case 15://BluetoothAdapter.STATE_BLE_ON: + stateStr = "STATE_BLE_ON"; + break; + case 16://BluetoothAdapter.STATE_BLE_TURNING_OFF: + stateStr = "STATE_BLE_TURNING_OFF"; + break; + default: + stateStr = "UNKNOWN_STATE[" + state + "]"; + break; + } + + return stateStr; + } + + /** + * convert BluetoothAdapter.STATE_ to String + * @param state + * @return + */ + public static String toBluetoothAdapterConnectionStateString(int state){ + String stateStr = null; + switch (state) { + case BluetoothAdapter.STATE_CONNECTED: + stateStr = "STATE_CONNECTED"; + break; + case BluetoothAdapter.STATE_CONNECTING: + stateStr = "STATE_CONNECTING"; + break; + case BluetoothAdapter.STATE_DISCONNECTING: + stateStr = "STATE_DISCONNECTING"; + break; + case BluetoothAdapter.STATE_DISCONNECTED: + stateStr = "STATE_DISCONNECTED"; + break; + default: + stateStr = "UNKNOWN_STATE[" + state + "]"; + } + return stateStr; + } + + /** + * convert BluetoothProfile.STATE_ to String + * + * @param state + * @return + */ + public static final String toBluetoothProfileStateString(int state) { + String stateStr = null; + switch (state) { + case BluetoothProfile.STATE_CONNECTED: + stateStr = "STATE_CONNECTED"; + break; + case BluetoothProfile.STATE_CONNECTING: + stateStr = "STATE_CONNECTING"; + break; + case BluetoothProfile.STATE_DISCONNECTED: + stateStr = "STATE_DISCONNECTED"; + break; + case BluetoothProfile.STATE_DISCONNECTING: + stateStr = "STATE_DISCONNECTING"; + break; + default: + stateStr = "UNKNOWN_STATE[" + state + "]"; + } + + return stateStr; + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static final void dump(StringBuilder builder, int indent, BluetoothGatt gatt) { + if (null == gatt) { + return; + } + + List