import type { BleService, BleCharacteristic, BleDataReceivedCallback, BleCharacteristicProperties, WriteCharacteristicOptions, ByteArray } from '../interface.uts'; import BluetoothGatt from "android.bluetooth.BluetoothGatt"; import BluetoothGattService from "android.bluetooth.BluetoothGattService"; import BluetoothGattCharacteristic from "android.bluetooth.BluetoothGattCharacteristic"; import BluetoothGattDescriptor from "android.bluetooth.BluetoothGattDescriptor"; import BluetoothGattCallback from "android.bluetooth.BluetoothGattCallback"; import UUID from "java.util.UUID"; import { DeviceManager } from './device_manager.uts'; import { AkBleErrorImpl, AkBluetoothErrorCode } from '../unierror.uts'; import { AutoDiscoverAllResult } from '../interface.uts'; // 补全UUID格式,将短格式转换为标准格式 function getFullUuid(shortUuid: string): string { return `0000${shortUuid}-0000-1000-8000-00805f9b34fb`; } const deviceWriteQueues = new Map>(); function enqueueDeviceWrite(deviceId: string, work: () => Promise): Promise { const previous = deviceWriteQueues.get(deviceId) ?? Promise.resolve(); const next = (async (): Promise => { try { await previous; } catch (e: any) { /* ignore previous rejection to keep queue alive */ } return await work(); })(); const queued = next.then(() => { }, () => { }); deviceWriteQueues.set(deviceId, queued); return next.finally(() => { if (deviceWriteQueues.get(deviceId) == queued) { deviceWriteQueues.delete(deviceId); } }); } function createCharProperties(props: number): BleCharacteristicProperties { const result: BleCharacteristicProperties = { read: false, write: false, notify: false, indicate: false, canRead: false, canWrite: false, canNotify: false, writeWithoutResponse: false }; result.read = (props & BluetoothGattCharacteristic.PROPERTY_READ) !== 0; result.write = (props & BluetoothGattCharacteristic.PROPERTY_WRITE) !== 0; result.notify = (props & BluetoothGattCharacteristic.PROPERTY_NOTIFY) !== 0; result.indicate = (props & BluetoothGattCharacteristic.PROPERTY_INDICATE) !== 0; result.writeWithoutResponse = (props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) !== 0; result.canRead = result.read; const writeWithoutResponse = result.writeWithoutResponse!; result.canWrite = (result.write != null && result.write) || (writeWithoutResponse != null && writeWithoutResponse); result.canNotify = result.notify; return result; } // 定义 PendingCallback 类型和实现类 interface PendingCallback { resolve: (data: any) => void; reject: (err?: any) => void; timer?: number; // Changed from any to number } class PendingCallbackImpl implements PendingCallback { override resolve: (data: any) => void; override reject: (err?: any) => void; override timer?: number; // Changed from any to number constructor(resolve: (data: any) => void, reject: (err?: any) => void, timer?: number) { this.resolve = resolve; this.reject = reject; this.timer = timer; } } // 全局回调管理(必须在类外部声明) let pendingCallbacks: Map; let notifyCallbacks: Map; // 在全局范围内初始化 pendingCallbacks = new Map(); notifyCallbacks = new Map(); // 服务发现等待队列:deviceId -> 回调数组 const serviceDiscoveryWaiters = new Map void)[]>(); // 服务发现状态:deviceId -> 是否已发现 const serviceDiscovered = new Map(); // 服务发现重试:deviceId -> 尝试次数 const serviceDiscoveryAttempts = new Map(); const SERVICE_DISCOVERY_MAX_RETRIES = 3; const SERVICE_DISCOVERY_RETRY_DELAY_MS = 600; // 特征发现等待队列:deviceId|serviceId -> 回调数组 const characteristicDiscoveryWaiters = new Map void)[]>(); class GattCallback extends BluetoothGattCallback { constructor() { super(); } override onServicesDiscovered(gatt: BluetoothGatt, status: Int): void { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:112', 'ak onServicesDiscovered',gatt); const deviceId = gatt.getDevice().getAddress(); if (status == BluetoothGatt.GATT_SUCCESS) { const attempt = serviceDiscoveryAttempts.get(deviceId) ?? 0; const services = gatt.getServices(); const result: BleService[] = []; let size = 0; if (services != null) { const servicesList = services; size = servicesList.size; for (let i = 0; i < size; i++) { const service = servicesList.get(i as Int); if (service != null) { const bleService: BleService = { uuid: service.getUuid().toString(), isPrimary: service.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY }; result.push(bleService); } } } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:133', '[ServiceManager] onServicesDiscovered size=', size, 'attempt=', attempt, 'device=', deviceId); if (result.length == 0 && attempt < SERVICE_DISCOVERY_MAX_RETRIES) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:135', '[ServiceManager] services empty after discovery, retrying', deviceId, 'nextAttempt=', attempt + 1); serviceDiscoveryAttempts.set(deviceId, attempt + 1); serviceDiscovered.delete(deviceId); setTimeout(() => { try { const dm = DeviceManager.getInstance(); const currentGatt = dm.getGattInstance(deviceId); const target = currentGatt != null ? currentGatt : gatt; if (target != null) { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:144', '[ServiceManager] retry discoverServices for', deviceId); target.discoverServices(); } } catch (e: any) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:148', '[ServiceManager] retry discoverServices failed', e); } }, SERVICE_DISCOVERY_RETRY_DELAY_MS); return; } let finalResult: BleService[] | null = result; if (result.length == 0) { const cached = ServiceManager.getInstance().getCachedServices(deviceId); if (cached != null && cached.length > 0) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:154', '[ServiceManager] discovery returned empty, using cached services for', deviceId, 'len=', cached.length); finalResult = cached; } else { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:154', '[ServiceManager] discovery returned empty after retries for', deviceId); serviceDiscoveryAttempts.delete(deviceId); const waitersFail = serviceDiscoveryWaiters.get(deviceId); if (waitersFail != null && waitersFail.length > 0) { for (let i = 0; i < waitersFail.length; i++) { const cb = waitersFail[i]; if (cb != null) { cb(null, new Error('服务发现返回空列表')); } } serviceDiscoveryWaiters.delete(deviceId); } return; } } serviceDiscoveryAttempts.delete(deviceId); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:154', `服务发现成功: ${deviceId}, services=${finalResult != null ? finalResult.length : 0}`); serviceDiscovered.set(deviceId, true); ServiceManager.getInstance().handleServicesDiscovered(deviceId, finalResult ?? []); const waiters = serviceDiscoveryWaiters.get(deviceId); if (waiters != null && waiters.length > 0) { for (let i = 0; i < waiters.length; i++) { const cb = waiters[i]; if (cb != null) { cb(finalResult ?? [], null); } } serviceDiscoveryWaiters.delete(deviceId); } } else { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:166', `服务发现失败: ${deviceId}, status: ${status}`); serviceDiscoveryAttempts.delete(deviceId); // 失败时也要通知等待队列 const waiters = serviceDiscoveryWaiters.get(deviceId); if (waiters != null && waiters.length > 0) { for (let i = 0; i < waiters.length; i++) { const cb = waiters[i]; if (cb != null) { cb(null, new Error('服务发现失败')); } } serviceDiscoveryWaiters.delete(deviceId); } } } override onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int): void { const deviceId = gatt.getDevice().getAddress(); if (newState == BluetoothGatt.STATE_CONNECTED) { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:182', `设备已连接: ${deviceId}`); ServiceManager.getInstance().resetDiscoveryState(deviceId); DeviceManager.handleConnectionStateChange(deviceId, 2, null); // 2 = STATE_CONNECTED } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:186', `设备已断开: ${deviceId}`); serviceDiscovered.delete(deviceId); serviceDiscoveryAttempts.delete(deviceId); ServiceManager.getInstance().handleDisconnected(deviceId); DeviceManager.handleConnectionStateChange(deviceId, 0, null); // 0 = STATE_DISCONNECTED } } override onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic): void { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:194', 'ak onCharacteristicChanged'); const deviceId = gatt.getDevice().getAddress(); const serviceId = characteristic.getService().getUuid().toString(); const charId = characteristic.getUuid().toString(); const key = `${deviceId}|${serviceId}|${charId}|notify`; const callback = notifyCallbacks.get(key); const value = characteristic.getValue(); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:201', '[onCharacteristicChanged]', key, value); // Check for PING packets (0xAA 0x04 0x00 ...) if (value != null && value.size >= 4) { const arr = new Uint8Array(value.size); for (let i = 0 as Int; i < value.size; i++) { const v = value[i as Int]; arr[i] = v != null ? v : 0; } if (arr[0] === 0xAA && arr[2] === 0x00) { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:201', '[BLE] PING packet detected:', Array.from(arr)); } } if (callback != null && value != null) { const valueLength = value.size; const arr = new Uint8Array(valueLength); for (let i = 0 as Int; i < valueLength; i++) { const v = value[i as Int]; arr[i] = v != null ? v : 0; } // 保存接收日志 __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:210', ` INSERT INTO ble_data_log (device_id, service_id, char_id, direction, data, timestamp) VALUES ('${deviceId}', '${serviceId}', '${charId}', 'recv', '${Array.from(arr).join(',')}', ${Date.now()}) `); callback(arr); } } override onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int): void { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:219', 'ak onCharacteristicRead', status); const deviceId = gatt.getDevice().getAddress(); const serviceId = characteristic.getService().getUuid().toString(); const charId = characteristic.getUuid().toString(); const key = `${deviceId}|${serviceId}|${charId}|read`; const pending = pendingCallbacks.get(key); const value = characteristic.getValue(); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:226', '[onCharacteristicRead]', key, 'status=', status, 'value=', value); if (pending != null) { try { const timer = pending.timer; if (timer != null) { clearTimeout(timer); pending.timer = null; } pendingCallbacks.delete(key); if (status == BluetoothGatt.GATT_SUCCESS && value != null) { const valueLength = value.size; const arr = new Uint8Array(valueLength); for (let i = 0 as Int; i < valueLength; i++) { const v = value[i as Int]; arr[i] = v != null ? v : 0; } // debug: log raw bytes and decoded string (helpful on Android native path) try { const hex = Array.from(arr).map((b): string => b.toString(16).padStart(2, '0')).join(' '); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:246', '[ServiceManager] onCharacteristicRead raw hex:', hex, 'len=', arr.length, 'key=', key); try { const decoded = new TextDecoder().decode(arr); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:249', '[ServiceManager] onCharacteristicRead decoded string:', decoded); } catch (e: any) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:250', '[ServiceManager] decode error', e); } } catch (e: any) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:251', '[ServiceManager] failed to log read buffer', e); } // resolve with ArrayBuffer pending.resolve(arr.buffer as ArrayBuffer); } else { pending.reject(new Error('Characteristic read failed')); } } catch (e: any) { try { pending.reject(e); } catch (e2: any) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:259', e2); } } } } override onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int): void { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:265', 'ak onCharacteristicWrite', status); const deviceId = gatt.getDevice().getAddress(); const serviceId = characteristic.getService().getUuid().toString(); const charId = characteristic.getUuid().toString(); const key = `${deviceId}|${serviceId}|${charId}|write`; const pending = pendingCallbacks.get(key); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:271', '[onCharacteristicWrite]', key, 'status=', status); if (pending != null) { try { const timer = pending.timer; if (timer != null) { clearTimeout(timer); } pendingCallbacks.delete(key); if (status == BluetoothGatt.GATT_SUCCESS) { pending.resolve('ok'); } else { pending.reject(new Error('Characteristic write failed')); } } catch (e: any) { try { pending.reject(e); } catch (e2: any) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:285', e2); } } } } } // 导出单例实例供外部使用 export const gattCallback = new GattCallback(); export class ServiceManager { private static instance: ServiceManager | null = null; private services = new Map(); private characteristics = new Map>(); private deviceManager = DeviceManager.getInstance(); private constructor() { } static getInstance(): ServiceManager { if (ServiceManager.instance == null) { ServiceManager.instance = new ServiceManager(); } return ServiceManager.instance!; } public resetDiscoveryState(deviceId: string, clearServiceCache: boolean = false): void { serviceDiscoveryAttempts.delete(deviceId); serviceDiscovered.delete(deviceId); serviceDiscoveryWaiters.delete(deviceId); if (clearServiceCache == true) { this.services.delete(deviceId); } } public handleServicesDiscovered(deviceId: string, services: BleService[]): void { this.services.set(deviceId, services); } public getCachedServices(deviceId: string): BleService[] | null { const cached = this.services.get(deviceId); return cached != null ? cached : null; } public handleDisconnected(deviceId: string): void { this.resetDiscoveryState(deviceId); const keysToRemove: string[] = []; this.characteristics.forEach((_value, key) => { if (key != null && key.indexOf(deviceId + '|') == 0) { keysToRemove.push(key); } }); for (let i = 0; i < keysToRemove.length; i++) { this.characteristics.delete(keysToRemove[i]); } } getServices(deviceId: string, callback?: (services: BleService[] | null, error?: Error) => void): any | Promise { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts', 'ak start getservice', deviceId); const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) { if (callback != null) { callback(null, new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", "")); } return Promise.reject(new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", "")); } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts', 'ak serviceDiscovered', gatt); // 如果服务已发现,直接返回 if (serviceDiscovered.get(deviceId) == true) { const services = gatt.getServices(); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:333', services); const result: BleService[] = []; if (services != null) { const servicesList = services; const size = servicesList.size; if (size > 0) { for (let i = 0 as Int; i < size; i++) { const service = servicesList != null ? servicesList.get(i) : servicesList[i]; if (service != null) { const bleService: BleService = { uuid: service.getUuid().toString(), isPrimary: service.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY }; result.push(bleService); if (bleService.uuid == getFullUuid('0001')) { const device = this.deviceManager.getDevice(deviceId); if (device != null) { device.serviceId = bleService.uuid; this.getCharacteristics(deviceId, device.serviceId!, (chars, err) => { if (err == null && chars != null) { const writeChar = chars.find((c): boolean => c.uuid == getFullUuid('0010')); const notifyChar = chars.find((c): boolean => c.uuid == getFullUuid('0011')); if (writeChar != null) device.writeCharId = writeChar.uuid; if (notifyChar != null) device.notifyCharId = notifyChar.uuid; } }); } } } } } } if (callback != null) { callback(result, null); } return Promise.resolve(result); } // 未发现则发起服务发现并加入等待队列 if (!serviceDiscoveryWaiters.has(deviceId)) { console.log('ak should start serviceDiscoveryWaiters') serviceDiscoveryWaiters.set(deviceId, []); gatt.discoverServices(); } return new Promise((resolve, reject) => { const cb = (services: BleService[] | null, error?: Error) => { if (error != null) reject(error); else resolve(services ?? []); if (callback != null) callback(services, error); }; const arr = serviceDiscoveryWaiters.get(deviceId); if (arr != null) arr.push(cb); }); } getCharacteristics(deviceId: string, serviceId: string, callback: (characteristics: BleCharacteristic[] | null, error?: Error) => void): void { const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) return callback(null, new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", "")); // 如果服务还没发现,等待服务发现后再查特征 if (serviceDiscovered.get(deviceId) !== true) { // 先注册到服务发现等待队列 this.getServices(deviceId, (services, err) => { if (err != null) { callback(null, err); } else { this.getCharacteristics(deviceId, serviceId, callback); } }); return; } // 服务已发现,正常获取特征 const service = gatt.getService(UUID.fromString(serviceId)); if (service == null) return callback(null, new AkBleErrorImpl(AkBluetoothErrorCode.ServiceNotFound, "Service not found", "")); const chars = service.getCharacteristics(); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:403', chars); const result: BleCharacteristic[] = []; if (chars != null) { const characteristicsList = chars; const size = characteristicsList.size; const bleService: BleService = { uuid: serviceId, isPrimary: service.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY }; for (let i = 0 as Int; i < size; i++) { const char = characteristicsList != null ? characteristicsList.get(i as Int) : characteristicsList[i]; if (char != null) { const props = char.getProperties(); try { const charUuid = char.getUuid() != null ? char.getUuid().toString() : ''; __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:418', '[ServiceManager] characteristic uuid=', charUuid); } catch (e: any) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:419', '[ServiceManager] failed to read char uuid', e); } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:420', props); const bleCharacteristic: BleCharacteristic = { uuid: char.getUuid().toString(), service: bleService, properties: createCharProperties(props) }; result.push(bleCharacteristic); } } } callback(result, null); } public async readCharacteristic(deviceId: string, serviceId: string, characteristicId: string): Promise { const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", ""); const service = gatt.getService(UUID.fromString(serviceId)); if (service == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.ServiceNotFound, "Service not found", ""); const char = service.getCharacteristic(UUID.fromString(characteristicId)); if (char == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.CharacteristicNotFound, "Characteristic not found", ""); const key = `${deviceId}|${serviceId}|${characteristicId}|read`; __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:441', key); return new Promise((resolve, reject) => { const timer = setTimeout(() => { pendingCallbacks.delete(key); reject(new AkBleErrorImpl(AkBluetoothErrorCode.ConnectionTimeout, "Connection timeout", "")); }, 5000); const resolveAdapter = (data: any) => { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:447', 'read resolve:', data); resolve(data as ArrayBuffer); }; const rejectAdapter = (err?: any) => { reject(new AkBleErrorImpl(AkBluetoothErrorCode.UnknownError, "Unknown error occurred", "")); }; pendingCallbacks.set(key, new PendingCallbackImpl(resolveAdapter, rejectAdapter, timer)); if (gatt.readCharacteristic(char) == false) { clearTimeout(timer); pendingCallbacks.delete(key); reject(new AkBleErrorImpl(AkBluetoothErrorCode.UnknownError, "Unknown error occurred", "")); } else { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:456', 'read should be succeed', key); } }); } public async writeCharacteristic(deviceId: string, serviceId: string, characteristicId: string, data: Uint8Array, options?: WriteCharacteristicOptions): Promise { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:462', '[writeCharacteristic] deviceId:', deviceId, 'serviceId:', serviceId, 'characteristicId:', characteristicId, 'data:', data); const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:465', '[writeCharacteristic] gatt is null'); throw new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", ""); } const service = gatt.getService(UUID.fromString(serviceId)); if (service == null) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:470', '[writeCharacteristic] service is null'); throw new AkBleErrorImpl(AkBluetoothErrorCode.ServiceNotFound, "Service not found", ""); } const char = service.getCharacteristic(UUID.fromString(characteristicId)); if (char == null) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:475', '[writeCharacteristic] characteristic is null'); throw new AkBleErrorImpl(AkBluetoothErrorCode.CharacteristicNotFound, "Characteristic not found", ""); } const key = `${deviceId}|${serviceId}|${characteristicId}|write`; const wantsNoResponse = options != null && options.waitForResponse == false; let retryMaxAttempts = 20; let retryDelay = 100; let giveupTimeout = 20000; if (options != null) { try { if (options.maxAttempts != null) { const parsedAttempts = Math.floor(options.maxAttempts as number); if (!isNaN(parsedAttempts) && parsedAttempts > 0) retryMaxAttempts = parsedAttempts; } } catch (e: any) { } try { if (options.retryDelayMs != null) { const parsedDelay = Math.floor(options.retryDelayMs as number); if (!isNaN(parsedDelay) && parsedDelay >= 0) retryDelay = parsedDelay; } } catch (e: any) { } try { if (options.giveupTimeoutMs != null) { const parsedGiveup = Math.floor(options.giveupTimeoutMs as number); if (!isNaN(parsedGiveup) && parsedGiveup > 0) giveupTimeout = parsedGiveup; } } catch (e: any) { } } const gattInstance = gatt; const executeWrite = (): Promise => { return new Promise((resolve, _reject) => { const initialTimeout = Math.max(giveupTimeout + 5000, 10000); let timer = setTimeout(() => { pendingCallbacks.delete(key); __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:510', '[writeCharacteristic] timeout'); resolve(false); }, initialTimeout); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:513', '[writeCharacteristic] initial timeout set to', initialTimeout, 'ms for', key); const resolveAdapter = (data: any) => { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:515', '[writeCharacteristic] resolveAdapter called'); resolve(true); }; const rejectAdapter = (err?: any) => { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:519', '[writeCharacteristic] rejectAdapter called', err); resolve(false); }; pendingCallbacks.set(key, new PendingCallbackImpl(resolveAdapter, rejectAdapter, timer)); const byteArray = new ByteArray(data.length as Int); for (let i = 0 as Int; i < data.length; i++) { byteArray[i] = data[i].toByte(); } const forceWriteTypeNoResponse = options != null && options.forceWriteTypeNoResponse == true; let usesNoResponse = forceWriteTypeNoResponse || wantsNoResponse; try { if (usesNoResponse == false) { const props = char.getProperties(); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:532', '[writeCharacteristic] characteristic properties mask=', props); usesNoResponse = (props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) !== 0; } if (usesNoResponse) { try { char.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); } catch (e: any) { } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:537', '[writeCharacteristic] using WRITE_TYPE_NO_RESPONSE'); } else { try { char.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); } catch (e: any) { } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:540', '[writeCharacteristic] using WRITE_TYPE_DEFAULT'); } } catch (e: any) { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:543', '[writeCharacteristic] failed to inspect/set write type', e); } const maxAttempts = retryMaxAttempts; function attemptWrite(att: Int): void { try { let setOk = true; try { const setRes = char.setValue(byteArray); if (typeof setRes == 'boolean' && setRes == false) { setOk = false; __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:553', '[writeCharacteristic] setValue returned false for', key, 'attempt', att); } } catch (e: any) { setOk = false; __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:557', '[writeCharacteristic] setValue threw for', key, 'attempt', att, e); } if (setOk == false) { if (att >= maxAttempts) { try { clearTimeout(timer); } catch (e: any) { } pendingCallbacks.delete(key); resolve(false); return; } setTimeout(() => { attemptWrite((att + 1) as Int); }, retryDelay); return; } try { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:570', '[writeCharacteristic] attempt', att, 'calling gatt.writeCharacteristic'); const r = gattInstance.writeCharacteristic(char); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:572', '[writeCharacteristic] attempt', att, 'result=', r); if (r == true) { if (usesNoResponse) { __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:575', '[writeCharacteristic] WRITE_TYPE_NO_RESPONSE success for', key); try { clearTimeout(timer); } catch (e: any) { } pendingCallbacks.delete(key); resolve(true); return; } try { clearTimeout(timer); } catch (e: any) { } const extra = 20000; timer = setTimeout(() => { pendingCallbacks.delete(key); __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:585', '[writeCharacteristic] timeout after write initiated'); resolve(false); }, extra); const pendingEntry = pendingCallbacks.get(key); if (pendingEntry != null) pendingEntry.timer = timer; return; } } catch (e: any) { __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:593', '[writeCharacteristic] attempt', att, 'exception when calling writeCharacteristic', e); } if (att < maxAttempts) { const nextAtt = (att + 1) as Int; setTimeout(() => { attemptWrite(nextAtt); }, retryDelay); return; } if (usesNoResponse) { try { clearTimeout(timer); } catch (e: any) { } pendingCallbacks.delete(key); __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:603', '[writeCharacteristic] all attempts failed with WRITE_NO_RESPONSE for', key); resolve(false); return; } try { clearTimeout(timer); } catch (e: any) { } const giveupTimeoutLocal = giveupTimeout; __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:609', '[writeCharacteristic] all attempts failed; waiting for late callback up to', giveupTimeoutLocal, 'ms for', key); const giveupTimer = setTimeout(() => { pendingCallbacks.delete(key); __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:612', '[writeCharacteristic] giveup timeout expired for', key); resolve(false); }, giveupTimeoutLocal); const pendingEntryAfter = pendingCallbacks.get(key); if (pendingEntryAfter != null) pendingEntryAfter.timer = giveupTimer; } catch (e: any) { clearTimeout(timer); pendingCallbacks.delete(key); __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:620', '[writeCharacteristic] Exception in attemptWrite', e); resolve(false); } } try { attemptWrite(1 as Int); } catch (e: any) { clearTimeout(timer); pendingCallbacks.delete(key); __f__('error', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:630', '[writeCharacteristic] Exception before attempting write', e); resolve(false); } }); }; return enqueueDeviceWrite(deviceId, executeWrite); } public async subscribeCharacteristic(deviceId: string, serviceId: string, characteristicId: string, onData: BleDataReceivedCallback): Promise { const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", ""); const service = gatt.getService(UUID.fromString(serviceId)); if (service == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.ServiceNotFound, "Service not found", ""); const char = service.getCharacteristic(UUID.fromString(characteristicId)); if (char == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.CharacteristicNotFound, "Characteristic not found", ""); const key = `${deviceId}|${serviceId}|${characteristicId}|notify`; notifyCallbacks.set(key, onData); if (gatt.setCharacteristicNotification(char, true) == false) { notifyCallbacks.delete(key); throw new AkBleErrorImpl(AkBluetoothErrorCode.UnknownError, "Failed to unsubscribe characteristic", ""); } else { // 写入 CCCD 描述符,启用 notify const descriptor = char.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (descriptor != null) { // 设置描述符值 const value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; descriptor.setValue(value); const writedescript = gatt.writeDescriptor(descriptor); __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:660', 'subscribeCharacteristic: CCCD written for notify', writedescript); } else { __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:662', 'subscribeCharacteristic: CCCD descriptor not found!'); } __f__('log', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:664', 'subscribeCharacteristic ok!!'); } } public async unsubscribeCharacteristic(deviceId: string, serviceId: string, characteristicId: string): Promise { const gatt = this.deviceManager.getGattInstance(deviceId); if (gatt == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.DeviceNotFound, "Device not found", ""); const service = gatt.getService(UUID.fromString(serviceId)); if (service == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.ServiceNotFound, "Service not found", ""); const char = service.getCharacteristic(UUID.fromString(characteristicId)); if (char == null) throw new AkBleErrorImpl(AkBluetoothErrorCode.CharacteristicNotFound, "Characteristic not found", ""); const key = `${deviceId}|${serviceId}|${characteristicId}|notify`; notifyCallbacks.delete(key); if (gatt.setCharacteristicNotification(char, false) == false) { throw new AkBleErrorImpl(AkBluetoothErrorCode.UnknownError, "Failed to unsubscribe characteristic", ""); } } // 自动发现所有服务和特征 public async autoDiscoverAll(deviceId: string): Promise { const services = await this.getServices(deviceId, null) as BleService[]; const allCharacteristics: BleCharacteristic[] = []; for (const service of services) { await new Promise((resolve, reject) => { this.getCharacteristics(deviceId, service.uuid, (chars, err) => { if (err != null) reject(err); else { if (chars != null) allCharacteristics.push(...chars); resolve(void 0); } }); }); } return { services, characteristics: allCharacteristics } as AutoDiscoverAllResult; } // 自动订阅所有支持 notify/indicate 的特征 public async subscribeAllNotifications(deviceId: string, onData: BleDataReceivedCallback): Promise { const { services, characteristics } = await this.autoDiscoverAll(deviceId); for (const char of characteristics) { if (char.properties.notify || char.properties.indicate) { try { await this.subscribeCharacteristic(deviceId, char.service.uuid, char.uuid, onData); } catch (e: any) { // 可以选择忽略单个特征订阅失败 __f__('warn', 'at uni_modules/ak-sbsrv/utssdk/app-android/service_manager.uts:710', `订阅特征 ${char.uuid} 失败:`, e); } } } } } //# sourceMappingURL=service_manager.uts.map