Skip to content

Commit c39dd3b

Browse files
committed
feat: alternative to discoverAll: serviceUUIDs. Will discover only those services and no chars(faster)
`discoverServices` now supports `serviceUUIDs ` on android
1 parent 49fe431 commit c39dd3b

File tree

2 files changed

+127
-93
lines changed

2 files changed

+127
-93
lines changed

src/bluetooth.android.ts

Lines changed: 107 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,29 @@ export enum Phy {
129129
LE_CODED, // = android.bluetooth.BluetoothDevice.PHY_LE_CODED,
130130
LE_ALL_SUPPORTED, // = android.bluetooth.le.ScanSettings.PHY_LE_ALL_SUPPORTED
131131
}
132-
function androidPhy(mode: Phy) {
133-
switch (mode) {
134-
case Phy.LE_1M:
135-
return android.bluetooth.BluetoothDevice.PHY_LE_1M;
136-
case Phy.LE_CODED:
137-
return android.bluetooth.BluetoothDevice.PHY_LE_CODED;
138-
default:
139-
// PHY_LE_ALL_SUPPORTED
140-
return android.bluetooth.le.ScanSettings.PHY_LE_ALL_SUPPORTED;
141-
}
142-
}
143-
144-
export function uuidToString(uuid) {
145-
// uuid is returned lowercase
132+
// function androidPhy(mode: Phy) {
133+
// switch (mode) {
134+
// case Phy.LE_1M:
135+
// return android.bluetooth.BluetoothDevice.PHY_LE_1M;
136+
// case Phy.LE_CODED:
137+
// return android.bluetooth.BluetoothDevice.PHY_LE_CODED;
138+
// default:
139+
// // PHY_LE_ALL_SUPPORTED
140+
// return android.bluetooth.le.ScanSettings.PHY_LE_ALL_SUPPORTED;
141+
// }
142+
// }
143+
144+
const uuidRegexp = new RegExp('0000(.{4})-0000-1000-8000-00805f9b34fb');
145+
export function uuidToString(uuid: android.os.ParcelUuid | string | java.util.UUID) {
146146
const uuidStr = uuid.toString();
147-
const pattern = java.util.regex.Pattern.compile('0000(.{4})-0000-1000-8000-00805f9b34fb', 2);
148-
const matcher = pattern.matcher(uuidStr);
149-
return matcher.matches() ? matcher.group(1) : uuidStr;
147+
if (uuidStr.length !== 4) {
148+
const match = uuidRegexp.exec(uuidStr);
149+
if (match){
150+
return match[1];
151+
}
152+
}
153+
return uuidStr;
150154
}
151-
152155
// val must be a Uint8Array or Uint16Array or a string like '0x01' or '0x007F' or '0x01,0x02', or '0x007F,'0x006F''
153156
export function arrayToNativeByteArray(val) {
154157
const length = val.length;
@@ -984,87 +987,99 @@ declare interface WrapperResult {
984987
bluetoothGattService: android.bluetooth.BluetoothGattService;
985988
}
986989

987-
function getGattDeviceServiceInfo(gatt: android.bluetooth.BluetoothGatt) {
990+
function getGattDeviceServiceInfo(gatt: android.bluetooth.BluetoothGatt, args?: DiscoverServicesOptions & {all?: boolean}) {
988991
const services = gatt.getServices();
989992
const servicesJs = [];
990993
const BluetoothGattCharacteristic = android.bluetooth.BluetoothGattCharacteristic;
994+
const serviceUUIDs = args.serviceUUIDs;
995+
const all = args.all;
991996
for (let i = 0; i < services.size(); i++) {
992-
const service = services.get(i);
993-
const characteristics = service.getCharacteristics();
994-
const characteristicsJs = [];
995-
for (let j = 0; j < characteristics.size(); j++) {
996-
const characteristic = characteristics.get(j);
997-
const props = characteristic.getProperties();
998-
const descriptors = characteristic.getDescriptors();
999-
const descriptorsJs = [];
1000-
for (let k = 0; k < descriptors.size(); k++) {
1001-
const descriptor = descriptors.get(k);
1002-
const descriptorJs = {
1003-
UUID: uuidToString(descriptor.getUuid()),
1004-
value: descriptor.getValue(), // always empty btw
997+
const service: android.bluetooth.BluetoothGattService = services.get(i);
998+
const serviceUUID = uuidToString(service.getUuid());
999+
console.log('getGattDeviceServiceInfo', service.getUuid().toString(),serviceUUID, serviceUUIDs);
1000+
if (serviceUUIDs && serviceUUIDs.indexOf(serviceUUID) === -1) {
1001+
continue;
1002+
}
1003+
let characteristicsJs;
1004+
if (all === true) {
1005+
const characteristics = service.getCharacteristics();
1006+
characteristicsJs = [];
1007+
for (let j = 0; j < characteristics.size(); j++) {
1008+
const characteristic: android.bluetooth.BluetoothGattCharacteristic = characteristics.get(j);
1009+
const characteristicUUID = uuidToString(characteristic.getUuid());
1010+
const props = characteristic.getProperties();
1011+
const descriptors = characteristic.getDescriptors();
1012+
const descriptorsJs = [];
1013+
for (let k = 0; k < descriptors.size(); k++) {
1014+
const descriptor: android.bluetooth.BluetoothGattDescriptor = descriptors.get(k);
1015+
const descriptorJs = {
1016+
UUID: uuidToString(descriptor.getUuid()),
1017+
value: descriptor.getValue(), // always empty btw
1018+
permissions: null,
1019+
};
1020+
const descPerms = descriptor.getPermissions();
1021+
if (descPerms > 0) {
1022+
descriptorJs.permissions = {
1023+
read: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ) !== 0,
1024+
readEncrypted: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED) !== 0,
1025+
readEncryptedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM) !== 0,
1026+
write: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE) !== 0,
1027+
writeEncrypted: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED) !== 0,
1028+
writeEncryptedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM) !== 0,
1029+
writeSigned: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED) !== 0,
1030+
writeSignedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM) !== 0,
1031+
};
1032+
}
1033+
1034+
if (Trace.isEnabled()) {
1035+
CLog(CLogTypes.info, `TNS_BluetoothGattCallback.onServicesDiscovered ---- pushing descriptor: ${descriptor}`);
1036+
}
1037+
descriptorsJs.push(descriptorJs);
1038+
}
1039+
1040+
const characteristicJs = {
1041+
serviceUUID,
1042+
UUID: characteristicUUID,
1043+
name: characteristicUUID, // there's no sep field on Android
1044+
properties: {
1045+
read: (props & BluetoothGattCharacteristic.PROPERTY_READ) !== 0,
1046+
write: (props & BluetoothGattCharacteristic.PROPERTY_WRITE) !== 0,
1047+
writeWithoutResponse: (props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) !== 0,
1048+
notify: (props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) !== 0,
1049+
indicate: (props & BluetoothGattCharacteristic.PROPERTY_INDICATE) !== 0,
1050+
broadcast: (props & BluetoothGattCharacteristic.PROPERTY_BROADCAST) !== 0,
1051+
authenticatedSignedWrites: (props & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE) !== 0,
1052+
extendedProperties: (props & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS) !== 0,
1053+
},
1054+
descriptors: descriptorsJs,
10051055
permissions: null,
10061056
};
1007-
const descPerms = descriptor.getPermissions();
1008-
if (descPerms > 0) {
1009-
descriptorJs.permissions = {
1010-
read: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ) !== 0,
1011-
readEncrypted: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED) !== 0,
1012-
readEncryptedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM) !== 0,
1013-
write: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE) !== 0,
1014-
writeEncrypted: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED) !== 0,
1015-
writeEncryptedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM) !== 0,
1016-
writeSigned: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED) !== 0,
1017-
writeSignedMitm: (descPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM) !== 0,
1057+
1058+
// permissions are usually not provided, so let's not return them in that case
1059+
const charPerms = characteristic.getPermissions();
1060+
if (charPerms > 0) {
1061+
characteristicJs.permissions = {
1062+
read: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ) !== 0,
1063+
readEncrypted: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED) !== 0,
1064+
readEncryptedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM) !== 0,
1065+
write: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE) !== 0,
1066+
writeEncrypted: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED) !== 0,
1067+
writeEncryptedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM) !== 0,
1068+
writeSigned: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED) !== 0,
1069+
writeSignedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM) !== 0,
10181070
};
10191071
}
10201072

10211073
if (Trace.isEnabled()) {
1022-
CLog(CLogTypes.info, `TNS_BluetoothGattCallback.onServicesDiscovered ---- pushing descriptor: ${descriptor}`);
1074+
CLog(CLogTypes.info, `TNS_BluetoothGattCallback.onServicesDiscovered ---- pushing characteristic: ${JSON.stringify(characteristicJs)} for service:${serviceUUID}`);
10231075
}
1024-
descriptorsJs.push(descriptorJs);
1025-
}
1026-
1027-
const characteristicJs = {
1028-
serviceUUID: uuidToString(service.getUuid()),
1029-
UUID: uuidToString(characteristic.getUuid()),
1030-
name: uuidToString(characteristic.getUuid()), // there's no sep field on Android
1031-
properties: {
1032-
read: (props & BluetoothGattCharacteristic.PROPERTY_READ) !== 0,
1033-
write: (props & BluetoothGattCharacteristic.PROPERTY_WRITE) !== 0,
1034-
writeWithoutResponse: (props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) !== 0,
1035-
notify: (props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) !== 0,
1036-
indicate: (props & BluetoothGattCharacteristic.PROPERTY_INDICATE) !== 0,
1037-
broadcast: (props & BluetoothGattCharacteristic.PROPERTY_BROADCAST) !== 0,
1038-
authenticatedSignedWrites: (props & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE) !== 0,
1039-
extendedProperties: (props & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS) !== 0,
1040-
},
1041-
descriptors: descriptorsJs,
1042-
permissions: null,
1043-
};
1044-
1045-
// permissions are usually not provided, so let's not return them in that case
1046-
const charPerms = characteristic.getPermissions();
1047-
if (charPerms > 0) {
1048-
characteristicJs.permissions = {
1049-
read: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ) !== 0,
1050-
readEncrypted: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED) !== 0,
1051-
readEncryptedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM) !== 0,
1052-
write: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE) !== 0,
1053-
writeEncrypted: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED) !== 0,
1054-
writeEncryptedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM) !== 0,
1055-
writeSigned: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED) !== 0,
1056-
writeSignedMitm: (charPerms & BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM) !== 0,
1057-
};
1058-
}
1059-
1060-
if (Trace.isEnabled()) {
1061-
CLog(CLogTypes.info, `TNS_BluetoothGattCallback.onServicesDiscovered ---- pushing characteristic: ${JSON.stringify(characteristicJs)}`);
1076+
characteristicsJs.push(characteristicJs);
10621077
}
1063-
characteristicsJs.push(characteristicJs);
10641078
}
10651079

1080+
10661081
servicesJs.push({
1067-
UUID: uuidToString(service.getUuid()),
1082+
UUID: serviceUUID,
10681083
characteristics: characteristicsJs,
10691084
});
10701085
}
@@ -1732,8 +1747,10 @@ export class Bluetooth extends BluetoothCommon {
17321747
});
17331748
});
17341749
let services, mtu;
1735-
if(args.autoDiscoverAll !== false) {
1750+
if(args.autoDiscoverAll === true) {
17361751
services = (await this.discoverAll({ peripheralUUID: pUUID }))?.services;
1752+
} else if(args.serviceUUIDs) {
1753+
services = (await this.discoverServices({ peripheralUUID: pUUID, serviceUUIDs:args.serviceUUIDs }))?.services;
17371754
}
17381755
if (!!args.auto2MegPhy) {
17391756
await this.select2MegPhy({ peripheralUUID: pUUID }) ;
@@ -1864,7 +1881,7 @@ export class Bluetooth extends BluetoothCommon {
18641881
if (Trace.isEnabled()) {
18651882
CLog(
18661883
CLogTypes.info,
1867-
`${methodName} ---- got result peripheralUUID:${args.peripheralUUID} serviceUUID:${args.serviceUUID} characteristicUUID:${args.characteristicUUID} status:${status}`
1884+
`${methodName} ---- got result peripheralUUID:${pUUID} serviceUUID:${sUUID} characteristicUUID:${cUUID} status:${status}`
18681885
);
18691886
}
18701887
if (UUID === pUUID && cUUID === args.characteristicUUID && sUUID === args.serviceUUID) {
@@ -2453,7 +2470,7 @@ export class Bluetooth extends BluetoothCommon {
24532470
}
24542471

24552472
@prepareArgs
2456-
public discoverServices(args: DiscoverServicesOptions): Promise<{ services: Service[] }> {
2473+
public discoverServices(args: DiscoverServicesOptions & {all?: boolean}): Promise<{ services: Service[] }> {
24572474
const methodName = 'discoverServices';
24582475
if (Trace.isEnabled()) {
24592476
CLog(CLogTypes.info, methodName, args);
@@ -2480,6 +2497,7 @@ export class Bluetooth extends BluetoothCommon {
24802497
if (Trace.isEnabled()) {
24812498
CLog(CLogTypes.info, methodName, pUUID, stateObject);
24822499
}
2500+
const serviceUUIDs = args.serviceUUIDs;
24832501
return this.addToGattQueue(
24842502
() =>
24852503
new Promise((resolve, reject) => {
@@ -2499,7 +2517,7 @@ export class Bluetooth extends BluetoothCommon {
24992517
}
25002518
if (UUID === pUUID) {
25012519
if (status === GATT_SUCCESS) {
2502-
resolve(getGattDeviceServiceInfo(gatt));
2520+
resolve(getGattDeviceServiceInfo(gatt, args));
25032521
clearListeners();
25042522
} else {
25052523
onError(
@@ -2579,7 +2597,7 @@ export class Bluetooth extends BluetoothCommon {
25792597
}
25802598

25812599
public discoverAll(args: DiscoverOptions) {
2582-
return this.discoverServices(args);
2600+
return this.discoverServices({...args, all:true});
25832601
}
25842602

25852603
private disconnectListeners: DisconnectListener[] = [];

src/bluetooth.common.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,18 @@ export function prepareArgs(target: Object, propertyKey: string, descriptor: Typ
6161
descriptor.value = function (...args: any[]) {
6262
const paramsToCheck = args[0];
6363
if (paramsToCheck.hasOwnProperty) {
64-
['serviceUUID', 'characteristicUUID'].forEach(function (k) {
65-
if (paramsToCheck[k]) {
66-
const matcher = (paramsToCheck[k] as string).match(pattern);
67-
paramsToCheck[k] = (matcher && matcher.length > 0 ? matcher[1] : paramsToCheck[k]).toLowerCase();
64+
['serviceUUIDs', 'serviceUUID', 'characteristicUUID'].forEach(function (k) {
65+
const value = paramsToCheck[k];
66+
if (value) {
67+
if (Array.isArray(value)) {
68+
paramsToCheck[k] = paramsToCheck[k].map(v=>{
69+
const matcher = (v as string).match(pattern);
70+
return (matcher && matcher.length > 0 ? matcher[1] : v).toLowerCase();
71+
});
72+
} else {
73+
const matcher = (paramsToCheck[k] as string).match(pattern);
74+
paramsToCheck[k] = (matcher && matcher.length > 0 ? matcher[1] : paramsToCheck[k]).toLowerCase();
75+
}
6876
}
6977
});
7078
}
@@ -342,8 +350,16 @@ export interface ConnectOptions {
342350
*/
343351
onDisconnected?: (data: { UUID; name: string }) => void;
344352

353+
/**
354+
* Discover all services on connection
355+
*/
345356
autoDiscoverAll?: boolean;
346357

358+
/**
359+
* Discover specified services on connection
360+
*/
361+
serviceUUIDs?: string[];
362+
347363
/**
348364
* Selects 2M PHY when available (Android only)
349365
*/

0 commit comments

Comments
 (0)