Skip to content

Commit

Permalink
update module
Browse files Browse the repository at this point in the history
  • Loading branch information
alerighi committed Nov 3, 2023
1 parent b04c0b9 commit 0d85cef
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
public class BleException extends Exception {
public static final String ERROR_SCAN = "BleScanError";
public static final String ERROR_GENERIC = "BleGenericError";
public static final String ERROR_DEVICE_DISCONNECTED = "BleDeviceDisconnected";
public static final String ERROR_INVALID_STATE = "BleInvalidState";
public static final String ERROR_DEVICE_DISCONNECTED = "BleDeviceDisconnectedError";
public static final String ERROR_INVALID_STATE = "BleInvalidStateError";
public static final String ERROR_BLE_NOT_ENABLED = "BleNotEnabledError";
public static final String ERROR_BLE_NOT_SUPPORTED = "BleNotSupportedError";
public static final String ERROR_MISSING_PERMISSIONS = "BleMissingPermissionError";
Expand All @@ -16,7 +16,9 @@ public class BleException extends Exception {
public static final String ERROR_NOT_CONNECTED = "BleNotConnectedError";
public static final String ERROR_NOT_INITIALIZED = "BleNotInitializedError";
public static final String ERROR_MODULE_BUSY = "BleModuleBusyError";
public static final String ERROR_INVALID_ARGUMENTS = "ErrorInvalidArguments";
public static final String ERROR_INVALID_ARGUMENTS = "BleInvalidArgumentsError";
public static final String ERROR_DEVICE_NOT_FOUND = "BleDeviceNotFoundError";
public static final String ERROR_OPERATION_CANCELED = "BleOperationCanceledError";

private final WritableMap details;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public interface BleGatt {
void subscribe(String service, String characteristic) throws BleException;
void unsubscribe(String service, String characteristic) throws BleException;
void readRSSI(AsyncOperation operation) throws BleException;
void cancelPendingOperations() throws BleException;
void dispose();
}

Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public void connect(AsyncOperation operation, String id, int mtu) throws BleExce
context.setPendingGattOperation(new PendingGattConnect(emitter, operation, mtu, context));
gatt = device.connectGatt(appContext, false, callback);
if (gatt == null) {
throw new BleException(BleLibraryModule.ERROR_GATT, "gatt instance is null");
throw new BleException(BleException.ERROR_GATT, "gatt instance is null");
}
context.setConnectionState(ConnectionState.CONNECTING);
} catch (Exception exception) {
context.setPendingGattOperation(null);
throw exception;
throw new BleException(BleException.ERROR_DEVICE_NOT_FOUND, "specified device was not found");
}
}
}
Expand All @@ -54,7 +54,7 @@ public void connect(AsyncOperation operation, String id, int mtu) throws BleExce
public void disconnect(AsyncOperation operation) throws BleException {
if (context.getConnectionState() == ConnectionState.CONNECTED) {
if (gatt == null) {
throw new BleException(BleLibraryModule.ERROR_GATT, "gatt is undefined");
throw new BleException(BleException.ERROR_GATT, "gatt is undefined");
}
try {
context.setPendingGattOperation(new PendingGattDisconnect(emitter, operation));
Expand All @@ -71,7 +71,7 @@ public void disconnect(AsyncOperation operation) throws BleException {
@RequiresPermission(value = "android.permission.BLUETOOTH_CONNECT")
public void read(AsyncOperation operation, String serviceUuid, String characteristicUuid, int size) throws BleException {
if (context.getConnectionState() != ConnectionState.CONNECTED || gatt == null) {
throw new BleException(BleLibraryModule.ERROR_NOT_CONNECTED, "not connected");
throw new BleException(BleException.ERROR_NOT_CONNECTED, "not connected");
}
BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUuid, characteristicUuid);

Expand All @@ -89,7 +89,7 @@ public void read(AsyncOperation operation, String serviceUuid, String characteri
@RequiresPermission(value = "android.permission.BLUETOOTH_CONNECT")
public void write(AsyncOperation operation, String serviceUuid, String characteristicUuid, byte[] value, int chunkSize) throws BleException {
if (context.getConnectionState() != ConnectionState.CONNECTED || gatt == null) {
throw new BleException(BleLibraryModule.ERROR_NOT_CONNECTED, "not connected");
throw new BleException(BleException.ERROR_NOT_CONNECTED, "not connected");
}
BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUuid, characteristicUuid);

Expand All @@ -107,42 +107,50 @@ public void write(AsyncOperation operation, String serviceUuid, String character
@RequiresPermission(value = "android.permission.BLUETOOTH_CONNECT")
public void subscribe(String serviceUuid, String characteristicUuid) throws BleException {
if (context.getConnectionState() != ConnectionState.CONNECTED || gatt == null) {
throw new BleException(BleLibraryModule.ERROR_NOT_CONNECTED, "not connected");
throw new BleException(BleException.ERROR_NOT_CONNECTED, "not connected");
}
BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUuid, characteristicUuid);

boolean result = gatt.setCharacteristicNotification(characteristic, true);
if (!result) {
throw new BleException(BleLibraryModule.ERROR_GATT, "setCharacteristicNotification failed");
throw new BleException(BleException.ERROR_GATT, "setCharacteristicNotification failed");
}
}

@Override
@RequiresPermission(value = "android.permission.BLUETOOTH_CONNECT")
public void unsubscribe(String serviceUuid, String characteristicUuid) throws BleException {
if (context.getConnectionState() != ConnectionState.CONNECTED || gatt == null) {
throw new BleException(BleLibraryModule.ERROR_NOT_CONNECTED, "not connected");
throw new BleException(BleException.ERROR_NOT_CONNECTED, "not connected");
}
BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUuid, characteristicUuid);

boolean result = gatt.setCharacteristicNotification(characteristic, false);
if (!result) {
throw new BleException(BleLibraryModule.ERROR_GATT, "setCharacteristicNotification failed");
throw new BleException(BleException.ERROR_GATT, "setCharacteristicNotification failed");
}
}

@Override
@RequiresPermission(value = "android.permission.BLUETOOTH_CONNECT")
public void readRSSI(AsyncOperation operation) throws BleException {
if (context.getConnectionState() != ConnectionState.CONNECTED || gatt == null) {
throw new BleException(BleLibraryModule.ERROR_NOT_CONNECTED, "not connected");
throw new BleException(BleException.ERROR_NOT_CONNECTED, "not connected");
}

context.setPendingGattOperation(new PendingReadRssi(emitter, operation));

boolean result = gatt.readRemoteRssi();
if (!result) {
throw new BleException(BleLibraryModule.ERROR_GATT, "setCharacteristicNotification failed");
throw new BleException(BleException.ERROR_GATT, "setCharacteristicNotification failed");
}
}

@Override
public void cancelPendingOperations() throws BleException {
PendingGattOperation operation = context.getPendingGattOperation();
if (operation != null) {
operation.onCancel(gatt);
}
}

Expand All @@ -156,12 +164,12 @@ public void dispose() {
private BluetoothGattCharacteristic getCharacteristic(String serviceUuid, String characteristicUuid) throws BleException {
BluetoothGattService service = gatt.getService(UUID.fromString(serviceUuid));
if (service == null) {
throw new BleException(BleLibraryModule.ERROR_GATT, "service not found");
throw new BleException(BleException.ERROR_GATT, "service not found");
}

BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUuid));
if (characteristic == null) {
throw new BleException(BleLibraryModule.ERROR_GATT, "characteristic not found");
throw new BleException(BleException.ERROR_GATT, "characteristic not found");
}

return characteristic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ public void disposeModule(Promise promise) {
promise.resolve(null);
}

@ReactMethod
public void cancelPendingOperations(Promise promise) {
Log.d(NAME, "cancelPendingOperations()");



promise.resolve(null);
}

@ReactMethod
public void scanStart(ReadableArray filterUuid, Promise promise) {
Log.d(NAME, String.format("scanStart(%s)", filterUuid));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ void onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
operation.fail(new BleException(BleException.ERROR_INVALID_STATE, "unexpected charWrite"));
}

void onCancel(BluetoothGatt gatt) {
operation.fail(new BleException(BleException.ERROR_OPERATION_CANCELED, "current operation was canceled"));
}

boolean isPending() {
return operation.isPending();
}
Expand Down
21 changes: 18 additions & 3 deletions library/ios/BleLibrary.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

// error codes
static NSString *const ErrorGeneric = @"BleGenericError";
static NSString *const ErrorDeviceDisconnected = @"BleDeviceDisconnected";
static NSString *const ErrorInvalidState = @"BleInvalidState";
static NSString *const ErrorDeviceDisconnected = @"BleDeviceDisconnectedError";
static NSString *const ErrorInvalidState = @"BleInvalidStateError";
static NSString *const ErrorBleNotEnabled = @"BleNotEnabledError";
static NSString *const ErrorBleNotSupported = @"BleNotSupportedError";
static NSString *const ErrorMissingPermission = @"BleMissingPermissionError";
Expand All @@ -17,7 +17,9 @@
static NSString *const ErrorNotConnected = @"BleNotConnectedError";
static NSString *const ErrorNotInitialized = @"BleNotInitializedError";
static NSString *const ErrorModuleBusy = @"BleModuleBusyError";
static NSString *const ErrorInvalidArguments = @"ErrorInvalidArguments";
static NSString *const ErrorInvalidArguments = @"BleInvalidArgumentsError";
static NSString *const ErrorOperationCanceled = @"BleOperationCanceledError";
static NSString *const ErrorDeviceNotFound = @"BleDeviceNotFoundError";

// event types
static NSString *const EventInitDone = @"initDone";
Expand Down Expand Up @@ -148,6 +150,19 @@ -(void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
}
}

RCT_EXPORT_METHOD(cancelPendingOperations:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
NSLog(@"[BleLibrary] cancelPendingOperations()");

[self reject:ErrorOperationCanceled message:@"the current operation was canceles" error:nil];

self.write = nil;
self.read = nil;

resolve(nil);
}

#pragma mark - BLE scan

RCT_EXPORT_METHOD(scanStart:(NSArray<NSString *> *)serviceUuids
Expand Down
11 changes: 6 additions & 5 deletions library/src/BleError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@ export enum BleErrorCode {
BleNotSupportedError = 'BleNotSupportedError',
BleMissingPermissionError = 'BleMissingPermissionError',
BleNotEnabledError = 'BleNotEnabledError',
BleDeviceDisconnectedError = 'BleDeviceDisconnected',
BleInvalidStateError = 'BleInvalidState',
BleDeviceDisconnectedError = 'BleDeviceDisconnectedError',
BleInvalidStateError = 'BleInvalidStateError',
BleGATTError = 'BleGATTError',
BleConnectionError = 'BleConnectionError',
BleNotConnectedError = 'BleNotConnectedError',
BleModuleBusyError = 'BleModuleBusyError',
BleInvalidArgumentsError = 'ErrorInvalidArguments',
BleInvalidArgumentsError = 'BleInvalidArgumentsError',
BleCharacteristicNotFoundError = 'BleCharacteristicNotFoundError',
BleServiceNotFoundError = 'BleServiceNotFoundError',
BleScanError = 'BleScanError',
BleAlreadyConnectedError = 'BleAlreadyConnectedError',
BleDeviceNotFoundError = 'BleDeviceNotFoundError',
BleOperationNotAllowed = 'BleOperationNotAllowed',
BleOperationNotAllowed = 'BleOperationNotAllowedError',
BleOperationCanceled = 'BleOperationCanceledError',
}

export class BleError extends Error {
constructor(public readonly code: string, message = code) {
constructor(public readonly code: BleErrorCode, message: string = code) {
super(message)
this.name = 'BleError'
}
Expand Down
29 changes: 29 additions & 0 deletions library/src/CancelationToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Subscription } from './types'

export class CancelationToken {
private onCancel = new Set<() => void>()
private isCanceled = false

public addListener(callback: () => void): Subscription {
this.onCancel.add(callback)

return {
unsubscribe: () => {
this.onCancel.delete(callback)
},
}
}

public get canceled() {
return this.isCanceled
}

public cancel() {
if (!this.isCanceled) {
for (const callback of this.onCancel) {
callback()
}
}
this.isCanceled = true
}
}
5 changes: 5 additions & 0 deletions library/src/NativeBleInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface IBleNativeModule {
subscribe(service: string, characteristic: string): Promise<void>
unsubscribe(service: string, characteristic: string): Promise<void>
readRSSI(): Promise<number>
cancelPendingOperations(): Promise<void>
}

export interface INativeBleInterface extends IBleNativeModule {
Expand Down Expand Up @@ -111,6 +112,10 @@ export class NativeBleInterface implements INativeBleInterface {
return wrap(this.logger, 'readRSSI', this.nativeModule.readRSSI)
}

cancelPendingOperations(): Promise<void> {
return wrap(this.logger, 'cancelPendingOperations', this.nativeModule.cancelPendingOperations)
}

addListener(listener: Partial<IBleNativeEventListener>): Subscription {
this.logger?.debug('[NativeBleInterface] adding listeners')

Expand Down
33 changes: 27 additions & 6 deletions library/src/NativeBleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type ILogger,
type Subscription,
} from './types'
import type { CancelationToken } from './CancelationToken'

// as required by the standard
const MAX_BLE_CHAR_SIZE = 512
Expand Down Expand Up @@ -208,7 +209,8 @@ export class NativeBleManager implements BleManager {
service: string,
characteristic: string,
size?: number,
progress?: (current: number, total: number) => void
progress?: (current: number, total: number) => void,
cancelToken?: CancelationToken
): Promise<Buffer> {
service = service.toLowerCase()
characteristic = characteristic.toLowerCase()
Expand All @@ -225,6 +227,10 @@ export class NativeBleManager implements BleManager {

this.logger?.info(`[BleManager] execute read(${characteristic})`)

if (cancelToken?.canceled) {
throw new BleError(BleErrorCode.BleOperationCanceled, 'operation canceled')
}

let subscription: Subscription | undefined
if (progress !== undefined) {
subscription = this.nativeInterface!.addListener({
Expand All @@ -238,15 +244,20 @@ export class NativeBleManager implements BleManager {
})
}

const cancelSubscription = cancelToken?.addListener(() => {
this.nativeInterface!.cancelPendingOperations()
})

let result: string
try {
result = await this.nativeInterface!.read(service, characteristic, size ?? 0)
} catch (e: any) {
throw new BleError(e.code, e.message)
} finally {
subscription?.unsubscribe()
cancelSubscription?.unsubscribe()
}

subscription?.unsubscribe()

return Buffer.from(result, 'base64')
})
}
Expand All @@ -256,7 +267,8 @@ export class NativeBleManager implements BleManager {
characteristic: string,
value: Buffer,
chunkSize = MAX_BLE_CHAR_SIZE,
progress?: (current: number, total: number) => void
progress?: (current: number, total: number) => void,
cancelToken?: CancelationToken
): Promise<void> {
service = service.toLowerCase()
characteristic = characteristic.toLowerCase()
Expand All @@ -281,6 +293,10 @@ export class NativeBleManager implements BleManager {
})`
)

if (cancelToken?.canceled) {
throw new BleError(BleErrorCode.BleOperationCanceled, 'operation canceled')
}

let subscription: Subscription | undefined
if (progress !== undefined) {
subscription = this.nativeInterface!.addListener({
Expand All @@ -294,13 +310,18 @@ export class NativeBleManager implements BleManager {
})
}

const cancelSubscription = cancelToken?.addListener(() => {
this.nativeInterface!.cancelPendingOperations()
})

try {
await this.nativeInterface!.write(service, characteristic, value.toString('base64'), chunkSize)
} catch (e: any) {
throw new BleError(e.code, e.message)
} finally {
subscription?.unsubscribe()
cancelSubscription?.unsubscribe()
}

subscription?.unsubscribe()
})
}

Expand Down
Loading

0 comments on commit 0d85cef

Please sign in to comment.