@onekeyfe/hd-core 1.2.2-alpha.0 → 1.2.2-alpha.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +4 -20
  2. package/__tests__/DeviceCommands.test.ts +0 -13
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +2 -0
  4. package/__tests__/check-firmware-release-protocol-v2.test.ts +2 -0
  5. package/__tests__/device-lifecycle-events.test.ts +0 -19
  6. package/__tests__/deviceUploadNft.test.ts +0 -41
  7. package/__tests__/evmSignTypedData.test.ts +0 -42
  8. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +12 -524
  9. package/__tests__/open-wallet-session.test.ts +1 -127
  10. package/__tests__/protocol-v2-resources.test.ts +4 -7
  11. package/__tests__/protocol-v2-unlock-policy.test.ts +1 -35
  12. package/__tests__/protocol-v2.test.ts +27 -673
  13. package/dist/api/FirmwareUpdateV4.d.ts +0 -3
  14. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  15. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  16. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  17. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  18. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  19. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  20. package/dist/api/firmware/getBinary.d.ts +2 -2
  21. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  22. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  23. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  24. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  25. package/dist/core/index.d.ts +0 -1
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/device/Device.d.ts +1 -1
  28. package/dist/device/Device.d.ts.map +1 -1
  29. package/dist/device/DeviceConnector.d.ts +1 -1
  30. package/dist/device/DeviceConnector.d.ts.map +1 -1
  31. package/dist/events/ui-request.d.ts +0 -3
  32. package/dist/events/ui-request.d.ts.map +1 -1
  33. package/dist/index.d.ts +5 -10
  34. package/dist/index.js +398 -1116
  35. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  36. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -2
  37. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  38. package/dist/protocols/protocol-v2/walletSession.d.ts +0 -3
  39. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  40. package/dist/types/settings.d.ts +2 -2
  41. package/dist/types/settings.d.ts.map +1 -1
  42. package/dist/utils/deviceFeaturesUtils.d.ts +0 -3
  43. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  44. package/dist/utils/patch.d.ts +1 -1
  45. package/dist/utils/patch.d.ts.map +1 -1
  46. package/package.json +4 -4
  47. package/src/api/FirmwareUpdateV4.ts +75 -416
  48. package/src/api/OpenWalletSession.ts +9 -39
  49. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +1 -2
  50. package/src/api/evm/EVMSignTypedData.ts +0 -1
  51. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -9
  52. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +1 -7
  53. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -28
  54. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -19
  55. package/src/api/protocol-v2/helpers.ts +0 -8
  56. package/src/core/index.ts +10 -121
  57. package/src/data/messages/messages-protocol-v2.json +1 -111
  58. package/src/device/Device.ts +1 -3
  59. package/src/device/DeviceConnector.ts +3 -7
  60. package/src/events/ui-request.ts +0 -3
  61. package/src/protocols/protocol-v2/resources.ts +1 -9
  62. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +1 -8
  63. package/src/protocols/protocol-v2/walletSession.ts +22 -65
  64. package/src/types/settings.ts +2 -4
  65. package/src/utils/deviceFeaturesUtils.ts +0 -4
  66. package/__tests__/pro2HostAssetPackage.test.ts +0 -58
  67. package/dist/utils/pro2HostAssetPackage.d.ts +0 -8
  68. package/dist/utils/pro2HostAssetPackage.d.ts.map +0 -1
  69. package/src/utils/pro2HostAssetPackage.ts +0 -315
@@ -29,12 +29,6 @@ const wasResumed = (session: unknown) =>
29
29
  'resumed' in session &&
30
30
  (session as { resumed?: unknown }).resumed === true;
31
31
 
32
- const wasWalletStatusRefreshed = (session: unknown) =>
33
- !!session &&
34
- typeof session === 'object' &&
35
- 'walletStatusRefreshed' in session &&
36
- (session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
37
-
38
32
  const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
39
33
  if (!session.passphraseState) {
40
34
  throw ERRORS.TypedError(
@@ -93,20 +87,12 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
93
87
  this.unlockPolicy = 'unlock-before-run';
94
88
  this.skipForceUpdateCheck = true;
95
89
  this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
96
- this.protocolV2PreUnlockPinType =
97
- this.params.mode === OpenWalletSessionMode.SelectHidden
98
- ? DeviceSessionPinType.Any
99
- : DeviceSessionPinType.Main;
100
90
  this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
101
91
  }
102
92
 
103
93
  async run(): Promise<OpenWalletSessionPayload> {
104
94
  const isProtocolV2 = this.device.isProtocolV2();
105
- const reusePreflightStatus =
106
- isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
107
- let state = reusePreflightStatus
108
- ? await this.device.getDeviceState()
109
- : await this.device.getDeviceState({ refreshSections: ['status'] });
95
+ let state = await this.device.getDeviceState({ refreshSections: ['status'] });
110
96
  let currentDeviceId = state.identity.deviceId;
111
97
  const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
112
98
  candidate.status.unlocked === true &&
@@ -129,25 +115,14 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
129
115
  currentDeviceId = state.identity.deviceId;
130
116
  return state;
131
117
  };
132
- const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
133
- if (!wasWalletStatusRefreshed(session)) {
134
- return refreshProtocolV2DeviceState();
135
- }
136
- state = await this.device.getDeviceState();
137
- currentDeviceId = state.identity.deviceId;
138
- return state;
139
- };
140
118
  const ensureProtocolV2WalletStatus = async () => {
141
119
  if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
142
- await this.device.unlockDevice(
143
- this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
144
- {
145
- source: 'unlock-coordinator',
146
- reason: 'device-locked',
147
- deviceOnly: true,
148
- method: 'openWalletSession',
149
- }
150
- );
120
+ await this.device.unlockDevice(DeviceSessionPinType.Main, {
121
+ source: 'unlock-coordinator',
122
+ reason: 'device-locked',
123
+ deviceOnly: true,
124
+ method: 'openWalletSession',
125
+ });
151
126
  requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
152
127
  }
153
128
  return state;
@@ -164,16 +139,13 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
164
139
  selectMainWalletBeforeRestore:
165
140
  !hasAuthoritativeProtocolV2WalletStatus(state) ||
166
141
  state.status.unlockedAttachPin === true,
167
- mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
168
142
  })
169
143
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
170
144
  onlyMainPin: true,
171
145
  initSession: this.payload.initSession,
172
146
  });
173
147
  const refreshedState = isProtocolV2
174
- ? requireAuthoritativeProtocolV2WalletStatus(
175
- await resolveProtocolV2DeviceStateAfterSession(session)
176
- )
148
+ ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
177
149
  : state;
178
150
  const deviceId = requireDeviceId();
179
151
  if (
@@ -261,9 +233,7 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
261
233
  })
262
234
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
263
235
  const refreshedState = isProtocolV2
264
- ? requireAuthoritativeProtocolV2WalletStatus(
265
- await resolveProtocolV2DeviceStateAfterSession(session)
266
- )
236
+ ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
267
237
  : state;
268
238
  const deviceId = requireDeviceId();
269
239
  if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
@@ -400,8 +400,7 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
400
400
  this.payload.passphraseState,
401
401
  useEmptyPassphrase,
402
402
  this.payload.skipPassphraseCheck,
403
- deriveCardano,
404
- this.protocolV2UnlockContext?.preflightMainPinSelected
403
+ deriveCardano
405
404
  );
406
405
  if (!passphraseStateSafety) {
407
406
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
@@ -360,7 +360,6 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
360
360
 
361
361
  const supportBiggerData =
362
362
  DeviceModelToTypes.model_classic1s.includes(currentDeviceType) ||
363
- DeviceModelToTypes.model_pro2.includes(currentDeviceType) ||
364
363
  (DeviceModelToTypes.model_touch.includes(currentDeviceType) &&
365
364
  semver.gte(currentVersion, supportBiggerDataVersion));
366
365
 
@@ -41,15 +41,9 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
41
41
  */
42
42
  export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
43
43
 
44
- type FirmwareProgressMetadata = Pick<
44
+ type FirmwareTransferMetrics = Pick<
45
45
  FirmwareProgress['payload'],
46
- | 'transferredBytes'
47
- | 'totalBytes'
48
- | 'rateBytesPerSecond'
49
- | 'elapsedMs'
50
- | 'installTargetId'
51
- | 'installPhase'
52
- | 'installPhaseProgress'
46
+ 'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
53
47
  >;
54
48
 
55
49
  const isDeviceDisconnectedError = (error: unknown) => {
@@ -109,7 +103,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
109
103
  postProgressMessage = (
110
104
  progress: number,
111
105
  progressType: IFirmwareUpdateProgressType,
112
- metrics?: FirmwareProgressMetadata
106
+ metrics?: FirmwareTransferMetrics
113
107
  ) => {
114
108
  this.postMessage(
115
109
  createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
@@ -4,13 +4,7 @@ import { invalidParameter } from '../helpers/filesystemValidation';
4
4
  import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
5
5
  import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
6
6
 
7
- const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
8
- 'status',
9
- 'progress_percent',
10
- 'phase_info',
11
- 'payload_version',
12
- 'path',
13
- ] as const;
7
+ const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = ['status', 'payload_version', 'path'] as const;
14
8
 
15
9
  function normalizeStatusFields(
16
10
  fields: DeviceFirmwareUpdateStatusGetParams['fields']
@@ -20,10 +20,6 @@ import {
20
20
  getCompletePro2NftBasenames,
21
21
  } from '../../utils/pro2Nft';
22
22
  import { encodePro2Image } from '../../utils/pro2Wallpaper';
23
- import {
24
- buildPro2HostAssetPackage,
25
- supportsPro2HostAssetPackage,
26
- } from '../../utils/pro2HostAssetPackage';
27
23
  import { BaseMethod } from '../BaseMethod';
28
24
  import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
29
25
  import { invalidParameter } from '../helpers/filesystemValidation';
@@ -142,7 +138,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
142
138
  this.useDevicePassphraseState = false;
143
139
  }
144
140
 
145
- private async assertCapabilities(useHostAssetPackage: boolean) {
141
+ private async assertCapabilities() {
146
142
  const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
147
143
  const hasFileWrite = supportsProtocolV2Message(
148
144
  protocolInfo,
@@ -154,7 +150,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
154
150
  );
155
151
  const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
156
152
  const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
157
- if (!hasFileWrite || !hasNftUpdate || (!useHostAssetPackage && (!hasPathInfo || !hasDirList))) {
153
+ if (!hasFileWrite || !hasPathInfo || !hasDirList || !hasNftUpdate) {
158
154
  throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
159
155
  }
160
156
  }
@@ -196,32 +192,16 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
196
192
  const { bundle } = this;
197
193
  if (!bundle) throw invalidParameter('NFT data has not been initialized.');
198
194
 
199
- const useHostAssetPackage = supportsPro2HostAssetPackage(
200
- this.device.getCurrentFirmwareVersionString()
201
- );
202
- await this.assertCapabilities(useHostAssetPackage);
195
+ await this.assertCapabilities();
203
196
  this.throwIfAborted();
204
- if (!useHostAssetPackage) await this.assertStorageCapacity(bundle.basename);
197
+ await this.assertStorageCapacity(bundle.basename);
205
198
  this.throwIfAborted();
206
199
 
207
- const extractedFiles = [
200
+ const files = [
208
201
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
209
202
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
210
203
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
211
204
  ];
212
- const files = useHostAssetPackage
213
- ? [
214
- {
215
- path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.okpkg`,
216
- data: buildPro2HostAssetPackage(
217
- extractedFiles.map(file => ({
218
- name: file.path.slice(`${PRO2_NFT_DIRECTORY}/`.length),
219
- data: file.data,
220
- }))
221
- ),
222
- },
223
- ]
224
- : extractedFiles;
225
205
  const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
226
206
  let transferredBeforeFile = 0;
227
207
 
@@ -258,9 +238,9 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
258
238
  const response = await this.updateNft(bundle.basename);
259
239
  return {
260
240
  basename: bundle.basename,
261
- imagePath: extractedFiles[0].path,
262
- thumbnailPath: extractedFiles[1].path,
263
- metadataPath: extractedFiles[2].path,
241
+ imagePath: files[0].path,
242
+ thumbnailPath: files[1].path,
243
+ metadataPath: files[2].path,
264
244
  totalSize,
265
245
  nftUpdated: true,
266
246
  message: response.message?.message,
@@ -16,10 +16,6 @@ import {
16
16
  type Pro2WallpaperColorFormat,
17
17
  encodePro2Wallpaper,
18
18
  } from '../../utils/pro2Wallpaper';
19
- import {
20
- buildPro2HostAssetPackage,
21
- supportsPro2HostAssetPackage,
22
- } from '../../utils/pro2HostAssetPackage';
23
19
 
24
20
  export type DeviceUploadWallpaperParams = {
25
21
  jpegBase64: string;
@@ -35,8 +31,6 @@ export type DeviceUploadWallpaperResponse = {
35
31
  };
36
32
 
37
33
  const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
38
- const WALLPAPER_PACKAGE_PATH = `${WALLPAPER_DIRECTORY}/wallpaper.okpkg`;
39
- const WALLPAPER_PACKAGE_ENTRY = 'wallpaper.bin';
40
34
  const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
41
35
  const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
42
36
  const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
@@ -122,14 +116,16 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
122
116
  this.directoryReady = true;
123
117
  }
124
118
 
125
- private async upload(path: string, data: Uint8Array) {
119
+ private async upload() {
126
120
  if (this.uploaded) return;
121
+ const { encoded } = this;
122
+ if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
127
123
 
128
124
  await writeProtocolV2File({
129
125
  commands: this.device.commands,
130
- path,
131
- data,
132
- totalSize: data.byteLength,
126
+ path: this.path,
127
+ data: encoded.data,
128
+ totalSize: encoded.data.byteLength,
133
129
  chunkSize: this.params.chunkSize,
134
130
  maxChunkRetries: 3,
135
131
  overwrite: true,
@@ -149,14 +145,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
149
145
  if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
150
146
  await this.assertCapabilities();
151
147
  await this.ensureDirectory();
152
- const useHostAssetPackage = supportsPro2HostAssetPackage(
153
- this.device.getCurrentFirmwareVersionString()
154
- );
155
- const data = useHostAssetPackage
156
- ? buildPro2HostAssetPackage([{ name: WALLPAPER_PACKAGE_ENTRY, data: encoded.data }])
157
- : encoded.data;
158
- if (useHostAssetPackage) this.path = WALLPAPER_PACKAGE_PATH;
159
- await this.upload(this.path, data);
148
+ await this.upload();
160
149
  const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
161
150
  settings: { wallpaper_path: this.path },
162
151
  });
@@ -171,7 +160,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
171
160
  }
172
161
  return {
173
162
  path: this.path,
174
- size: data.byteLength,
163
+ size: encoded.data.byteLength,
175
164
  colorFormat: encoded.colorFormat,
176
165
  message: response.message?.message,
177
166
  };
@@ -1,5 +1,4 @@
1
1
  import { DeviceRebootType } from '@onekeyfe/hd-transport';
2
- import { HardwareErrorCode } from '@onekeyfe/hd-shared';
3
2
 
4
3
  import { invalidParameter, validateNonEmptyString } from '../helpers/filesystemValidation';
5
4
  import { ProtocolV2FirmwareTargetType } from '../../protocols/protocol-v2/firmware';
@@ -104,13 +103,6 @@ export const getProtocolV2UnknownErrorText = (error: unknown) => {
104
103
  };
105
104
 
106
105
  export const isProtocolV2DeviceDisconnectedError = (error: unknown) => {
107
- const errorCode =
108
- error && typeof error === 'object'
109
- ? (error as { errorCode?: number; code?: number }).errorCode ??
110
- (error as { errorCode?: number; code?: number }).code
111
- : undefined;
112
- if (errorCode === HardwareErrorCode.BleDeviceDisconnected) return true;
113
-
114
106
  const message = getProtocolV2UnknownErrorText(error).toLowerCase();
115
107
  const compactMessage = message.replace(/\s+/g, '');
116
108
  return (
package/src/core/index.ts CHANGED
@@ -415,13 +415,6 @@ const onCallDevice = async (
415
415
  if (method.payload?.onlyConnectBleDevice) {
416
416
  preWarmCallbackTask?.resolve();
417
417
  Log.debug('Call API - only connect ble device: ', device?.mainId);
418
- // This early return bypasses the normal-path bookkeeping at the end of the
419
- // call. Without it the task leaks and haunts every later queue snapshot
420
- // and cancel sweep (field log: a completed task lingered for 6 minutes),
421
- // and the request stays in the active maps, so repeated preconnects pile
422
- // up phantom work in diagnostics.
423
- completeMethodRequestContext(method);
424
- requestQueue.releaseTask(method.responseID);
425
418
  return createResponseMessage(method.responseID, true, null);
426
419
  }
427
420
 
@@ -651,8 +644,7 @@ const onCallDevice = async (
651
644
  method.payload?.passphraseState,
652
645
  method.payload?.useEmptyPassphrase,
653
646
  method.payload?.skipPassphraseCheck,
654
- hasDeriveCardano(method),
655
- method.protocolV2UnlockContext?.preflightMainPinSelected
647
+ hasDeriveCardano(method)
656
648
  );
657
649
 
658
650
  // Double check, handles the special case of Touch/Pro
@@ -958,72 +950,11 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
958
950
  );
959
951
  }
960
952
 
961
- export function isProtocolV2PeerRemovedPairingError(method: BaseMethod, error: unknown) {
962
- return (
963
- method.payload.connectProtocol === 'V2' &&
964
- (error as { errorCode?: unknown })?.errorCode ===
965
- HardwareErrorCode.BlePeerRemovedPairingInformation
966
- );
967
- }
968
-
969
953
  /**
970
954
  * If the Bluetooth connection times out, retry up to 6 times
971
955
  * @param retryCount - Current retry count (default 0)
972
956
  */
973
- // device.acquire awaits a transport reply with no deadline of its own; a
974
- // transport that never settles (field case: Electron main lost an IPC reply,
975
- // "reply was never sent" after 5 minutes) hangs the call forever and cancel()
976
- // only takes effect at poll checkpoints. Race acquire against a deadline and
977
- // the caller's abort signal so the hang is bounded and cancel is immediate.
978
- const BLE_ACQUIRE_DEADLINE_MS = 60 * 1000;
979
-
980
- function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal): Promise<T> {
981
- return new Promise<T>((resolve, reject) => {
982
- let settled = false;
983
- const settle = (fn: () => void) => {
984
- if (settled) return;
985
- settled = true;
986
- clearTimeout(deadline);
987
- abortSignal?.removeEventListener('abort', onAbort);
988
- fn();
989
- };
990
- const onAbort = () =>
991
- settle(() => reject(ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)));
992
- const deadline = setTimeout(
993
- () =>
994
- settle(() =>
995
- reject(
996
- ERRORS.TypedError(
997
- HardwareErrorCode.BleTimeoutError,
998
- `BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`
999
- )
1000
- )
1001
- ),
1002
- BLE_ACQUIRE_DEADLINE_MS
1003
- );
1004
- // Attach before any early return so a late settlement of acquirePromise
1005
- // is always consumed — an abort or deadline must never leave the acquire
1006
- // rejection unhandled.
1007
- acquirePromise.then(
1008
- value => settle(() => resolve(value)),
1009
- error => settle(() => reject(error))
1010
- );
1011
- if (abortSignal) {
1012
- if (abortSignal.aborted) {
1013
- onAbort();
1014
- return;
1015
- }
1016
- abortSignal.addEventListener('abort', onAbort);
1017
- }
1018
- });
1019
- }
1020
-
1021
- async function connectDeviceForBle(
1022
- method: BaseMethod,
1023
- device: Device,
1024
- abortSignal?: AbortSignal,
1025
- retryCount = 0
1026
- ) {
957
+ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
1027
958
  try {
1028
959
  if (device.wasInterruptedByUser()) {
1029
960
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
@@ -1037,43 +968,9 @@ async function connectDeviceForBle(
1037
968
  !device.commands ||
1038
969
  device.commands.disposed;
1039
970
  if (shouldAcquire) {
1040
- // The deadline/abort guards are scoped to the desktop electron
1041
- // transport: its IPC acquire is the only path with a proven
1042
- // never-settling failure mode, while react-native/lowlevel acquire may
1043
- // legitimately block on a user-driven system bonding prompt for longer
1044
- // than any sane deadline. Other envs keep the plain acquire unchanged.
1045
- const useAcquireGuards = DataManager.getSettings('env') === 'desktop-web-ble';
1046
- // A cancel landing during the retry backoff must not start a new acquire.
1047
- if (useAcquireGuards && abortSignal?.aborted) {
1048
- throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
1049
- }
1050
- if (!useAcquireGuards) {
1051
- await device.acquire(method.payload.connectProtocol, {
1052
- forceProtocolDetection: method.payload.forceProtocolDetection,
1053
- });
1054
- } else {
1055
- try {
1056
- await raceBleAcquire(
1057
- device.acquire(method.payload.connectProtocol, {
1058
- forceProtocolDetection: method.payload.forceProtocolDetection,
1059
- }),
1060
- abortSignal
1061
- );
1062
- } catch (err) {
1063
- // A deadline hit means the transport is wedged mid-acquire; drop the
1064
- // link before the retry so it cold-connects instead of stacking a
1065
- // second connect onto the half-open one.
1066
- if (
1067
- err.errorCode === HardwareErrorCode.BleTimeoutError &&
1068
- device.mainId &&
1069
- device.deviceConnector
1070
- ) {
1071
- await device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
1072
- device.markTransportDisconnected();
1073
- }
1074
- throw err;
1075
- }
1076
- }
971
+ await device.acquire(method.payload.connectProtocol, {
972
+ forceProtocolDetection: method.payload.forceProtocolDetection,
973
+ });
1077
974
  }
1078
975
  if (method.payload?.onlyConnectBleDevice) {
1079
976
  if (shouldAcquire) {
@@ -1113,7 +1010,7 @@ async function connectDeviceForBle(
1113
1010
  const nextRetry = retryCount + 1;
1114
1011
  Log.debug(`Bluetooth connection will retry, retry count: ${nextRetry}`);
1115
1012
  await wait(3000);
1116
- await connectDeviceForBle(method, device, abortSignal, nextRetry);
1013
+ await connectDeviceForBle(method, device, nextRetry);
1117
1014
  } else {
1118
1015
  throw err;
1119
1016
  }
@@ -1223,7 +1120,7 @@ const ensureConnected = async (
1223
1120
  if (tryCount === 1) {
1224
1121
  device.beginConnectionAttempt();
1225
1122
  }
1226
- await connectDeviceForBle(method, device, abortSignal);
1123
+ await connectDeviceForBle(method, device);
1227
1124
  }
1228
1125
  resolve(device);
1229
1126
  return;
@@ -1267,8 +1164,7 @@ const ensureConnected = async (
1267
1164
  HardwareErrorCode.BridgeNeedsPermission,
1268
1165
  HardwareErrorCode.DeviceInterruptedFromUser,
1269
1166
  HardwareErrorCode.CallQueueActionCancelled,
1270
- ].includes(error.errorCode) ||
1271
- isProtocolV2PeerRemovedPairingError(method, error)
1167
+ ].includes(error.errorCode)
1272
1168
  ) {
1273
1169
  reject(error);
1274
1170
  return;
@@ -1324,9 +1220,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1324
1220
  ','
1325
1221
  )}`
1326
1222
  );
1327
- // Abort before rejecting: rejectRequest releases the task and would make
1328
- // its AbortController unreachable to an in-flight method loop.
1329
- requestQueue.abortRequestsByConnectId(connectId);
1330
1223
  const canceledDevices: Device[] = [];
1331
1224
  const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
1332
1225
  if (!device || canceledDevices.includes(device)) {
@@ -1351,6 +1244,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1351
1244
  }
1352
1245
  }
1353
1246
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1247
+ requestQueue.abortRequestsByConnectId(connectId);
1354
1248
  pollingManager.stop(connectId);
1355
1249
  } catch (e) {
1356
1250
  Log.error('Cancel API Error: ', e);
@@ -1359,8 +1253,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1359
1253
  const env = DataManager.getSettings('env');
1360
1254
  if (DataManager.isBleConnect(env)) {
1361
1255
  Log.debug('Cancel Api all _deviceList: ');
1362
- // Keep method abort signals observable until every active task is rejected.
1363
- requestQueue.abortAllRequests();
1364
1256
  const canceledDevices: Device[] = [];
1365
1257
  const interruptDevice = (device?: Device) => {
1366
1258
  if (!device || canceledDevices.includes(device)) {
@@ -1385,6 +1277,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1385
1277
  }
1386
1278
  }
1387
1279
  deviceCacheMap.forEach(interruptDevice);
1280
+ requestQueue.abortAllRequests();
1388
1281
  pollingManager.stopAll();
1389
1282
  } else {
1390
1283
  _deviceList?.allDevices().forEach(device => {
@@ -1435,10 +1328,6 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
1435
1328
  const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
1436
1329
  if (!method.useDevicePassphraseState) return false;
1437
1330
 
1438
- if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
1439
- return true;
1440
- }
1441
-
1442
1331
  return device.hasUsePassphrase();
1443
1332
  };
1444
1333