diff --git a/packages/connect-examples/electron-example/package.json b/packages/connect-examples/electron-example/package.json index 553bc875a..f3c880766 100644 --- a/packages/connect-examples/electron-example/package.json +++ b/packages/connect-examples/electron-example/package.json @@ -2,7 +2,7 @@ "name": "hardware-example", "productName": "HardwareExample", "executableName": "onekey-hardware-example", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "description": "End-to-end encrypted workspaces for teams", "main": "dist/index.js", diff --git a/packages/connect-examples/expo-example/package.json b/packages/connect-examples/expo-example/package.json index b3796078e..519e0cc63 100644 --- a/packages/connect-examples/expo-example/package.json +++ b/packages/connect-examples/expo-example/package.json @@ -1,6 +1,6 @@ { "name": "expo-example", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "scripts": { "start": "cross-env CONNECT_SRC=https://localhost:8087/ yarn expo start --dev-client", "android": "yarn expo run:android", @@ -19,10 +19,10 @@ "@noble/ed25519": "^2.1.0", "@noble/hashes": "^1.3.3", "@noble/secp256k1": "^1.7.1", - "@onekeyfe/hd-ble-sdk": "1.1.4-alpha.4", - "@onekeyfe/hd-common-connect-sdk": "1.1.4-alpha.4", - "@onekeyfe/hd-core": "1.1.4-alpha.4", - "@onekeyfe/hd-web-sdk": "1.1.4-alpha.4", + "@onekeyfe/hd-ble-sdk": "1.1.5", + "@onekeyfe/hd-common-connect-sdk": "1.1.5", + "@onekeyfe/hd-core": "1.1.5", + "@onekeyfe/hd-web-sdk": "1.1.5", "@onekeyfe/react-native-ble-utils": "^0.1.3", "@polkadot/util-crypto": "13.1.1", "@react-native-async-storage/async-storage": "1.21.0", diff --git a/packages/connect-examples/expo-playground/package.json b/packages/connect-examples/expo-playground/package.json index 9839f16e9..ee5fb378a 100644 --- a/packages/connect-examples/expo-playground/package.json +++ b/packages/connect-examples/expo-playground/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/onekey-hardware-playground", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "private": true, "sideEffects": [ "app/utils/shim.js", @@ -17,9 +17,9 @@ }, "dependencies": { "@noble/hashes": "^1.8.0", - "@onekeyfe/hd-core": "1.1.4-alpha.4", - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-web-sdk": "1.1.4-alpha.4", + "@onekeyfe/hd-core": "1.1.5", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-web-sdk": "1.1.5", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", diff --git a/packages/core/package.json b/packages/core/package.json index 0c6829c79..120b92ff3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-core", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "description": "> TODO: description", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", @@ -25,8 +25,8 @@ "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5", "axios": "^0.27.2", "bignumber.js": "^9.0.2", "bytebuffer": "^5.0.1", diff --git a/packages/hd-ble-sdk/package.json b/packages/hd-ble-sdk/package.json index 4904c34c3..69fc9f64d 100644 --- a/packages/hd-ble-sdk/package.json +++ b/packages/hd-ble-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-ble-sdk", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "ISC", @@ -20,8 +20,8 @@ "lint:fix": "eslint . --fix" }, "dependencies": { - "@onekeyfe/hd-core": "1.1.4-alpha.4", - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-react-native": "1.1.4-alpha.4" + "@onekeyfe/hd-core": "1.1.5", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport-react-native": "1.1.5" } } diff --git a/packages/hd-common-connect-sdk/package.json b/packages/hd-common-connect-sdk/package.json index c845ce260..32211181f 100644 --- a/packages/hd-common-connect-sdk/package.json +++ b/packages/hd-common-connect-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-common-connect-sdk", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "ISC", @@ -20,11 +20,11 @@ "lint:fix": "eslint . --fix" }, "dependencies": { - "@onekeyfe/hd-core": "1.1.4-alpha.4", - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-emulator": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-http": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-lowlevel": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-web-device": "1.1.4-alpha.4" + "@onekeyfe/hd-core": "1.1.5", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport-emulator": "1.1.5", + "@onekeyfe/hd-transport-http": "1.1.5", + "@onekeyfe/hd-transport-lowlevel": "1.1.5", + "@onekeyfe/hd-transport-web-device": "1.1.5" } } diff --git a/packages/hd-transport-electron/package.json b/packages/hd-transport-electron/package.json index 2945d456a..222a0a5fc 100644 --- a/packages/hd-transport-electron/package.json +++ b/packages/hd-transport-electron/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-electron", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "MIT", @@ -25,7 +25,7 @@ "electron-log": ">=4.0.0" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4" + "@onekeyfe/hd-shared": "1.1.5" }, "devDependencies": { "@types/web-bluetooth": "^0.0.17", diff --git a/packages/hd-transport-electron/src/noble-ble-handler.ts b/packages/hd-transport-electron/src/noble-ble-handler.ts index c73777e1e..4b6c99cac 100644 --- a/packages/hd-transport-electron/src/noble-ble-handler.ts +++ b/packages/hd-transport-electron/src/noble-ble-handler.ts @@ -19,6 +19,7 @@ import { import { COMMON_HEADER_SIZE } from '@onekeyfe/hd-transport'; import type { WebContents, IpcMainInvokeEvent } from 'electron'; import type { Peripheral, Service, Characteristic } from '@abandonware/noble'; +import pRetry from 'p-retry'; import type { NobleModule, Logger, DeviceInfo, CharacteristicPair } from './types/noble-extended'; import { safeLog } from './types/noble-extended'; @@ -250,6 +251,9 @@ async function initializeNoble(): Promise { return; } + // Setup persistent state listener before initialization + setupPersistentStateListener(); + const timeout = setTimeout(() => { reject( ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout') @@ -289,9 +293,6 @@ async function initializeNoble(): Promise { handleDeviceDiscovered(peripheral); }); - // Setup persistent state listener after initialization - setupPersistentStateListener(); - logger?.info('[NobleBLE] Noble initialized successfully'); } catch (error) { logger?.error('[NobleBLE] Failed to initialize Noble:', error); @@ -563,7 +564,7 @@ function getDevice(deviceId: string): DeviceInfo | null { }; } -// Discover services and characteristics for a connected device +// Core service discovery function (single attempt) async function discoverServicesAndCharacteristics( peripheral: Peripheral ): Promise { @@ -642,6 +643,168 @@ async function discoverServicesAndCharacteristics( }); } +// Force reconnect to clear potential connection state issues +async function forceReconnectPeripheral(peripheral: Peripheral, deviceId: string): Promise { + logger?.info('[NobleBLE] Forcing connection reset for device:', deviceId); + + // Step 1: Force disconnect if connected + if (peripheral.state === 'connected') { + await new Promise(resolve => { + peripheral.disconnect(() => { + logger?.info('[NobleBLE] Force disconnect completed'); + resolve(); + }); + }); + + // Wait for complete disconnection + await wait(1000); + } + + // Step 2: Clear device state + connectedDevices.delete(deviceId); + deviceCharacteristics.delete(deviceId); + devicePacketStates.delete(deviceId); + subscribedDevices.delete(deviceId); + subscriptionOperations.delete(deviceId); + + // Step 3: Re-establish connection + await new Promise((resolve, reject) => { + peripheral.connect((error: string) => { + if (error) { + logger?.error('[NobleBLE] Force reconnect failed:', error); + reject(new Error(`Force reconnect failed: ${error}`)); + } else { + logger?.info('[NobleBLE] Force reconnect successful'); + connectedDevices.set(deviceId, peripheral); + resolve(); + } + }); + }); + + // Wait for connection to stabilize + await wait(500); +} + +// Enhanced connection with fresh peripheral rescan as last resort +async function connectAndDiscoverWithFreshScan(deviceId: string): Promise { + logger?.info('[NobleBLE] Attempting connection with fresh peripheral scan as fallback'); + + const currentPeripheral = discoveredDevices.get(deviceId); + + // First attempt with existing peripheral + if (currentPeripheral) { + try { + return await discoverServicesAndCharacteristicsWithRetry(currentPeripheral, deviceId); + } catch (error) { + logger?.error( + '[NobleBLE] Service discovery failed with existing peripheral, attempting fresh scan...' + ); + } + } + + // Last resort: Fresh scan to get new peripheral object + logger?.info( + '[NobleBLE] Performing fresh scan to get new peripheral object for device:', + deviceId + ); + + try { + const freshPeripheral = await performTargetedScan(deviceId); + if (!freshPeripheral) { + throw new Error(`Device ${deviceId} not found in fresh scan`); + } + + // Update device maps with fresh peripheral + discoveredDevices.set(deviceId, freshPeripheral); + + // Connect to fresh peripheral + await new Promise((resolve, reject) => { + freshPeripheral.connect((error: string) => { + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${error}`)); + } else { + connectedDevices.set(deviceId, freshPeripheral); + resolve(); + } + }); + }); + + // Attempt service discovery with fresh peripheral (single attempt) + logger?.info('[NobleBLE] Attempting service discovery with fresh peripheral'); + await wait(1000); // Give fresh connection more time to stabilize + + return await discoverServicesAndCharacteristics(freshPeripheral); + } catch (error) { + logger?.error('[NobleBLE] Fresh scan and connection failed:', error); + throw error; + } +} + +// Enhanced service discovery with p-retry for robust BLE connection +async function discoverServicesAndCharacteristicsWithRetry( + peripheral: Peripheral, + deviceId: string +): Promise { + return pRetry( + async attemptNumber => { + logger?.info('[NobleBLE] Starting service discovery:', { + deviceId, + peripheralState: peripheral.state, + attempt: attemptNumber, + maxRetries: 5, + targetUUIDs: ONEKEY_SERVICE_UUIDS, + }); + + // Strategy: Force reconnect on 3rd attempt to clear potential state issues + if (attemptNumber === 3) { + logger?.info('[NobleBLE] Attempting force reconnect to clear connection state...'); + try { + await forceReconnectPeripheral(peripheral, deviceId); + } catch (error) { + logger?.error('[NobleBLE] Force reconnect failed:', error); + throw error; + } + } + + // Progressive delay strategy - handled by p-retry, but add extra wait for higher attempts + if (attemptNumber > 1) { + logger?.info(`[NobleBLE] Service discovery retry attempt ${attemptNumber}/5`); + } + + // Verify connection state before attempting service discovery + if (peripheral.state !== 'connected') { + throw new Error(`Device not connected: ${peripheral.state}`); + } + + try { + return await discoverServicesAndCharacteristics(peripheral); + } catch (error) { + logger?.error(`[NobleBLE] No services found (attempt ${attemptNumber}/5)`); + + if (attemptNumber < 5) { + logger?.error(`[NobleBLE] Will retry service discovery (attempt ${attemptNumber + 1}/5)`); + } + + throw error; // p-retry will handle the retry logic + } + }, + { + retries: 4, // Total 5 attempts (initial + 4 retries) + factor: 1.5, // Exponential backoff: 1000ms → 1500ms → 2250ms → 3000ms + minTimeout: 1000, // Start with 1 second delay + maxTimeout: 3000, // Maximum 3 seconds delay + onFailedAttempt: error => { + // This runs after each failed attempt + logger?.error(`[NobleBLE] Service discovery attempt ${error.attemptNumber} failed:`, { + message: error.message, + retriesLeft: error.retriesLeft, + nextRetryIn: `${Math.min(1000 * 1.5 ** error.attemptNumber, 3000)}ms`, + }); + }, + } + ); +} + // Connect to device - supports both discovered and direct connection modes async function connectDevice(deviceId: string, webContents: WebContents): Promise { logger?.info('[NobleBLE] Connect device request:', { @@ -746,14 +909,17 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis // Continue to re-setup the connection properly } - // Discover services and characteristics + // Discover services and characteristics with enhanced retry including fresh scan try { - const characteristics = await discoverServicesAndCharacteristics(peripheral); + const characteristics = await connectAndDiscoverWithFreshScan(deviceId); deviceCharacteristics.set(deviceId, characteristics); logger?.info('[NobleBLE] Device ready for communication:', deviceId); return; } catch (error) { - logger?.error('[NobleBLE] Service/characteristic discovery failed:', error); + logger?.error( + '[NobleBLE] Service/characteristic discovery failed after all attempts:', + error + ); throw error; } } @@ -780,15 +946,18 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis // Set up unified disconnect listener setupDisconnectListener(connectedPeripheral, deviceId, webContents); - // Discover services and characteristics - discoverServicesAndCharacteristics(connectedPeripheral) + // Discover services and characteristics with enhanced retry including fresh scan + connectAndDiscoverWithFreshScan(deviceId) .then(characteristics => { deviceCharacteristics.set(deviceId, characteristics); logger?.info('[NobleBLE] Device ready for communication:', deviceId); resolve(); }) .catch(error => { - logger?.error('[NobleBLE] Service/characteristic discovery failed:', error); + logger?.error( + '[NobleBLE] Service/characteristic discovery failed after all attempts:', + error + ); // Disconnect on failure connectedPeripheral.disconnect(); reject(error); @@ -1053,6 +1222,7 @@ async function unsubscribeNotifications(deviceId: string): Promise { // Setup IPC handlers export function setupNobleBleHandlers(webContents: WebContents): void { try { + console.log('NOBLE_VERSION_771'); // @ts-ignore – electron-log is only available at runtime // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require logger = require('electron-log') as Logger; diff --git a/packages/hd-transport-emulator/package.json b/packages/hd-transport-emulator/package.json index 10276dd6c..4711949dd 100644 --- a/packages/hd-transport-emulator/package.json +++ b/packages/hd-transport-emulator/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-emulator", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "description": "hardware emulator transport", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", @@ -24,8 +24,8 @@ "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5", "axios": "^0.27.2", "secure-json-parse": "^4.0.0" } diff --git a/packages/hd-transport-http/package.json b/packages/hd-transport-http/package.json index 16a36643e..f45b3a217 100644 --- a/packages/hd-transport-http/package.json +++ b/packages/hd-transport-http/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-http", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "description": "hardware http transport", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", @@ -24,8 +24,8 @@ "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5", "axios": "^0.27.2", "secure-json-parse": "^4.0.0" } diff --git a/packages/hd-transport-lowlevel/package.json b/packages/hd-transport-lowlevel/package.json index 7890c91c8..488699a05 100644 --- a/packages/hd-transport-lowlevel/package.json +++ b/packages/hd-transport-lowlevel/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-lowlevel", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "MIT", "main": "dist/index.js", @@ -19,7 +19,7 @@ "lint:fix": "eslint . --fix" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4" + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5" } } diff --git a/packages/hd-transport-react-native/package.json b/packages/hd-transport-react-native/package.json index a79d7e5ac..3ce73aa34 100644 --- a/packages/hd-transport-react-native/package.json +++ b/packages/hd-transport-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-react-native", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "MIT", "main": "dist/index.js", @@ -19,8 +19,8 @@ "lint:fix": "eslint . --fix" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" } diff --git a/packages/hd-transport-web-device/package.json b/packages/hd-transport-web-device/package.json index 4dfb2cf9e..c3725f320 100644 --- a/packages/hd-transport-web-device/package.json +++ b/packages/hd-transport-web-device/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport-web-device", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "MIT", @@ -20,11 +20,11 @@ "lint:fix": "eslint . --fix" }, "dependencies": { - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport": "1.1.4-alpha.4" + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport": "1.1.5" }, "devDependencies": { - "@onekeyfe/hd-transport-electron": "1.1.4-alpha.4", + "@onekeyfe/hd-transport-electron": "1.1.5", "@types/w3c-web-usb": "^1.0.6", "@types/web-bluetooth": "^0.0.17" } diff --git a/packages/hd-transport/package.json b/packages/hd-transport/package.json index 7d5429640..e78095419 100644 --- a/packages/hd-transport/package.json +++ b/packages/hd-transport/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-transport", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "description": "> TODO: description", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", diff --git a/packages/hd-web-sdk/package.json b/packages/hd-web-sdk/package.json index 6171ffedd..fdeea6935 100644 --- a/packages/hd-web-sdk/package.json +++ b/packages/hd-web-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-web-sdk", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "author": "OneKey", "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme", "license": "ISC", @@ -21,10 +21,10 @@ }, "dependencies": { "@onekeyfe/cross-inpage-provider-core": "^0.0.17", - "@onekeyfe/hd-core": "1.1.4-alpha.4", - "@onekeyfe/hd-shared": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-http": "1.1.4-alpha.4", - "@onekeyfe/hd-transport-web-device": "1.1.4-alpha.4" + "@onekeyfe/hd-core": "1.1.5", + "@onekeyfe/hd-shared": "1.1.5", + "@onekeyfe/hd-transport-http": "1.1.5", + "@onekeyfe/hd-transport-web-device": "1.1.5" }, "devDependencies": { "@babel/plugin-proposal-optional-chaining": "^7.17.12", diff --git a/packages/shared/package.json b/packages/shared/package.json index 329d9e9f6..950262763 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/hd-shared", - "version": "1.1.4-alpha.4", + "version": "1.1.5", "description": "Hardware SDK's shared tool library", "keywords": [ "Hardware-SDK", diff --git a/submodules/firmware b/submodules/firmware index 4fb782f04..0ae92cc77 160000 --- a/submodules/firmware +++ b/submodules/firmware @@ -1 +1 @@ -Subproject commit 4fb782f04dc465e5b9114922600234b50326b17b +Subproject commit 0ae92cc777ed276b8b2f75a155fd8f13605973b5