@onekeyfe/hd-core 1.2.2-alpha.1 → 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 -68
  7. package/__tests__/evmSignTypedData.test.ts +0 -42
  8. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +12 -825
  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 -710
  13. package/dist/api/FirmwareUpdateV4.d.ts +0 -4
  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 -21
  34. package/dist/index.js +402 -1136
  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 -434
  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 -31
  54. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -27
  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 -354
@@ -1,11 +1,4 @@
1
- import {
2
- EDeviceType,
3
- ERRORS,
4
- HardwareError,
5
- HardwareErrorCode,
6
- isBleStaleBondHardwareError,
7
- wait,
8
- } from '@onekeyfe/hd-shared';
1
+ import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
9
2
  import JSZip from 'jszip';
10
3
  import {
11
4
  DeviceRebootType,
@@ -51,7 +44,6 @@ import {
51
44
  getProtocolV2UnknownErrorText,
52
45
  isProtocolV2DeviceDisconnectedError,
53
46
  } from './protocol-v2/helpers';
54
- import { isProtocolV2ResponseTimeout } from './helpers/protocolV2FileWrite';
55
47
  import {
56
48
  openFirmwareByteSource,
57
49
  readFirmwareByteSourceFully,
@@ -63,7 +55,6 @@ import {
63
55
  assertFirmwareUpdatePreparedPlanDeviceIdentity,
64
56
  validateFirmwareUpdatePreparedPlan,
65
57
  } from './firmware/FirmwareUpdatePreparedPlan';
66
-
67
58
  import type {
68
59
  FirmwareArtifactReference,
69
60
  FirmwareUpdateV4Params,
@@ -93,7 +84,6 @@ const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
93
84
  const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
94
85
  const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
95
86
  const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
96
- const PROTOCOL_V2_INSTALL_FINISHED_AFTER_DISCONNECT_POLLS = 4;
97
87
  const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
98
88
  const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
99
89
  const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
@@ -192,11 +182,6 @@ const getProtocolV2DeviceTransferProgress = (
192
182
  type ProtocolV2FirmwareUpdateStatusTarget = {
193
183
  target_id: number | string;
194
184
  status?: number | string;
195
- progress_percent?: number;
196
- phase_info?: {
197
- phase: number | string;
198
- progress_percent: number;
199
- };
200
185
  payload_version?: number;
201
186
  path?: string;
202
187
  };
@@ -374,22 +359,12 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
374
359
  );
375
360
  };
376
361
 
377
- const isProtocolV2InstallResponseTimeout = (error: unknown) => {
378
- if (!error || typeof error !== 'object') return false;
379
- const { code } = error as { code?: number | string };
380
- return (
381
- code === 'response-timeout' ||
382
- /\bresponse timeout\b/i.test(getProtocolV2UnknownErrorText(error))
383
- );
384
- };
385
-
386
362
  const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
387
- isBleStaleBondHardwareError(error) ||
388
- (error instanceof HardwareError &&
389
- (error.errorCode === HardwareErrorCode.ActionCancelled ||
390
- error.errorCode === HardwareErrorCode.FirmwareError ||
391
- error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
392
- error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE));
363
+ error instanceof HardwareError &&
364
+ (error.errorCode === HardwareErrorCode.ActionCancelled ||
365
+ error.errorCode === HardwareErrorCode.FirmwareError ||
366
+ error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
367
+ error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
393
368
 
394
369
  const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
395
370
  normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
@@ -426,18 +401,6 @@ const normalizeProtocolV2TargetStatus = (
426
401
  return undefined;
427
402
  };
428
403
 
429
- const PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE = new Map<
430
- number | string,
431
- 'prepare' | 'install' | 'verify'
432
- >([
433
- [0, 'prepare'],
434
- [1, 'install'],
435
- [2, 'verify'],
436
- ['FW_MGMT_UPDATER_PHASE_PREPARE', 'prepare'],
437
- ['FW_MGMT_UPDATER_PHASE_INSTALL', 'install'],
438
- ['FW_MGMT_UPDATER_PHASE_VERIFY', 'verify'],
439
- ]);
440
-
441
404
  const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
442
405
 
443
406
  const versionArrayToNumber = (version?: IVersionArray) => {
@@ -495,11 +458,6 @@ const toProtocolV2FiniteNumber = (value: unknown) => {
495
458
  return undefined;
496
459
  };
497
460
 
498
- const normalizeProtocolV2ProgressPercent = (value: unknown) => {
499
- const progress = toProtocolV2FiniteNumber(value);
500
- return progress === undefined ? undefined : Math.min(100, Math.max(0, progress));
501
- };
502
-
503
461
  export const isProtocolV2FirmwareFingerprintValid = (
504
462
  binary: ArrayBuffer | Uint8Array,
505
463
  fingerprint: string | undefined
@@ -572,12 +530,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
572
530
 
573
531
  private protocolV2InstallBaselineVersions = new Map<number, string>();
574
532
 
575
- private protocolV2InstallNeedsReconnect = false;
576
-
577
- private protocolV2InstallDisconnectObserved = false;
578
-
579
- private protocolV2InstallTerminalSuccessObserved = false;
580
-
581
533
  private protocolV2LastRuntimeProbeFeatures?: Features;
582
534
 
583
535
  private protocolV2LastTransferProgress?: number;
@@ -1856,9 +1808,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1856
1808
  if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
1857
1809
  return true;
1858
1810
  } catch (error) {
1859
- if (isBleStaleBondHardwareError(error)) {
1860
- throw error;
1861
- }
1862
1811
  Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
1863
1812
  return false;
1864
1813
  }
@@ -1952,7 +1901,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1952
1901
  }
1953
1902
  lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
1954
1903
  } catch (error) {
1955
- if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
1904
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1956
1905
  throw error;
1957
1906
  }
1958
1907
  shouldReconnect = true;
@@ -2234,9 +2183,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2234
2183
  });
2235
2184
  return processedSize + source.size;
2236
2185
  } catch (error) {
2237
- if (isBleStaleBondHardwareError(error)) {
2238
- throw error;
2239
- }
2240
2186
  lastError = error;
2241
2187
  if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
2242
2188
  await this.recoverProtocolV2FileTransfer();
@@ -2266,41 +2212,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2266
2212
  }
2267
2213
  }
2268
2214
 
2269
- private collectProtocolV2LiveTargetIds(
2270
- statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2271
- expectedTargetIds: Set<number>,
2272
- liveTargetIds: Set<number>
2273
- ) {
2274
- statusTargets.forEach(target => {
2275
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2276
- if (
2277
- targetId !== undefined &&
2278
- expectedTargetIds.has(targetId) &&
2279
- isProtocolV2TargetStatusInProgress(target.status)
2280
- ) {
2281
- liveTargetIds.add(targetId);
2282
- }
2283
- });
2284
- }
2285
-
2286
2215
  private assertProtocolV2TargetStatus(
2287
2216
  statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2288
2217
  expectedTargetIds: Set<number>,
2289
- expectedPaths?: Map<number, string>,
2290
- liveTargetIds?: Set<number>
2218
+ expectedPaths = new Map<number, string>()
2291
2219
  ) {
2292
- const resolvedExpectedPaths = expectedPaths ?? new Map<number, string>();
2293
2220
  Log.log(
2294
2221
  `[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
2295
2222
  );
2296
- const failedTarget = statusTargets.find(target => {
2297
- const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2298
- return (
2299
- expectedTargetIds.has(targetId) &&
2300
- isProtocolV2TargetStatusFailed(target.status) &&
2301
- (!liveTargetIds || liveTargetIds.has(targetId))
2302
- );
2303
- });
2223
+ const failedTarget = statusTargets.find(
2224
+ target =>
2225
+ expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
2226
+ isProtocolV2TargetStatusFailed(target.status)
2227
+ );
2304
2228
  if (failedTarget) {
2305
2229
  const failedTargetDetails = JSON.stringify({
2306
2230
  targetId: failedTarget.target_id,
@@ -2317,27 +2241,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2317
2241
  }
2318
2242
  );
2319
2243
  }
2320
- if (liveTargetIds) {
2321
- const staleFailedTargets = statusTargets.filter(target => {
2322
- const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2323
- return (
2324
- expectedTargetIds.has(targetId) &&
2325
- isProtocolV2TargetStatusFailed(target.status) &&
2326
- !liveTargetIds.has(targetId)
2327
- );
2328
- });
2329
- if (staleFailedTargets.length > 0) {
2330
- Log.log(
2331
- `[FirmwareUpdateV4] ignoring stale failed firmware status until those targets start: ${JSON.stringify(
2332
- staleFailedTargets.map(target => ({
2333
- targetId: target.target_id,
2334
- status: target.status,
2335
- path: target.path,
2336
- }))
2337
- )}`
2338
- );
2339
- }
2340
- }
2341
2244
 
2342
2245
  const matchingTargets = statusTargets.filter(target =>
2343
2246
  expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
@@ -2345,16 +2248,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2345
2248
  const seenTargetIds = new Set<number>();
2346
2249
  for (const target of matchingTargets) {
2347
2250
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2348
- const pathConflictsWithCurrentInstall =
2349
- targetId !== undefined &&
2350
- Boolean(target.path) &&
2351
- Boolean(resolvedExpectedPaths.get(targetId)) &&
2352
- target.path !== resolvedExpectedPaths.get(targetId) &&
2353
- (!liveTargetIds || liveTargetIds.has(targetId));
2354
2251
  if (
2355
2252
  targetId === undefined ||
2356
2253
  seenTargetIds.has(targetId) ||
2357
- pathConflictsWithCurrentInstall
2254
+ (target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
2358
2255
  ) {
2359
2256
  Log.error(`[FirmwareUpdateV4] install status conflicts with target=${target.target_id}`);
2360
2257
  throw ERRORS.TypedError(
@@ -2367,13 +2264,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2367
2264
  }
2368
2265
  seenTargetIds.add(targetId);
2369
2266
  }
2370
- const completedTargets = matchingTargets.filter(target => {
2371
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2372
- return (
2373
- isProtocolV2TargetStatusFinished(target.status) &&
2374
- (!liveTargetIds || (targetId !== undefined && liveTargetIds.has(targetId)))
2375
- );
2376
- });
2267
+ const completedTargets = matchingTargets.filter(target =>
2268
+ isProtocolV2TargetStatusFinished(target.status)
2269
+ );
2377
2270
  const completedTargetIds = new Set<number>();
2378
2271
  completedTargets.forEach(target => {
2379
2272
  const targetId = normalizeProtocolV2TargetId(target.target_id);
@@ -2410,54 +2303,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2410
2303
  }
2411
2304
 
2412
2305
  if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
2413
- let detailedProgress = completedTargetIds.size * 100;
2414
- let installProgressMetadata:
2415
- | {
2416
- installTargetId: number;
2417
- installPhase: 'prepare' | 'install' | 'verify';
2418
- installPhaseProgress: number;
2419
- }
2420
- | undefined;
2421
- let hasInProgressTarget = false;
2422
- matchingTargets.forEach(target => {
2423
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2424
- if (
2425
- targetId === undefined ||
2426
- completedTargetIds.has(targetId) ||
2427
- !isProtocolV2TargetStatusInProgress(target.status) ||
2428
- (liveTargetIds && !liveTargetIds.has(targetId))
2429
- ) {
2430
- return;
2431
- }
2432
- hasInProgressTarget = true;
2433
- detailedProgress += normalizeProtocolV2ProgressPercent(target.progress_percent) ?? 0;
2434
- const installPhase = target.phase_info
2435
- ? PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE.get(target.phase_info.phase)
2436
- : undefined;
2437
- const installPhaseProgress = normalizeProtocolV2ProgressPercent(
2438
- target.phase_info?.progress_percent
2439
- );
2440
- if (installPhase && installPhaseProgress !== undefined && !installProgressMetadata) {
2441
- installProgressMetadata = {
2442
- installTargetId: targetId,
2443
- installPhase,
2444
- installPhaseProgress,
2445
- };
2446
- }
2447
- });
2306
+ const hasInProgressTarget = matchingTargets.some(target =>
2307
+ isProtocolV2TargetStatusInProgress(target.status)
2308
+ );
2448
2309
  const completedProgress = Math.floor(
2449
2310
  (completedTargetIds.size / expectedTargetIds.size) * 100
2450
2311
  );
2451
- const reportedProgress = Math.floor(detailedProgress / expectedTargetIds.size);
2452
- const progress = Math.min(
2453
- 99,
2454
- Math.max(completedProgress, reportedProgress, hasInProgressTarget ? 1 : 0)
2455
- );
2456
- if (installProgressMetadata) {
2457
- this.postProgressMessage(progress, 'installingFirmware', installProgressMetadata);
2458
- } else {
2459
- this.postProgressMessage(progress, 'installingFirmware');
2460
- }
2312
+ // The protocol exposes no per-target percentage, so report coarse progress by
2313
+ // completed targets and use 1% once work starts to keep the UI responsive.
2314
+ const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
2315
+ this.postProgressMessage(progress, 'installingFirmware');
2461
2316
  }
2462
2317
 
2463
2318
  return false;
@@ -2528,37 +2383,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2528
2383
  ) {
2529
2384
  const expectedTargetIds = new Set(targets.map(target => target.target_id));
2530
2385
  const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
2531
- const isBleInstall = this.isBleReconnect();
2532
2386
  const startTime = Date.now();
2533
2387
  let lastError: unknown;
2534
- // The loader may release either USB or BLE as installation starts. Recovery reconnects
2535
- // directly to status polling without generic Ping or DeviceInfo probes.
2536
- let shouldReconnect = this.protocolV2InstallNeedsReconnect;
2537
- this.protocolV2InstallNeedsReconnect = false;
2388
+ // DeviceFirmwareUpdateRequest leaves the current loader link usable for status polling.
2389
+ // Reconnecting here probes DeviceInfo, which loaders reject while installation is active.
2390
+ let shouldReconnect = false;
2538
2391
  let deviceInfo: ProtocolV2DeviceInfo | undefined;
2539
- let bleInstallLinkReady = false;
2540
- let installStatusDisconnectObserved = isBleInstall && this.protocolV2InstallDisconnectObserved;
2541
- this.protocolV2InstallDisconnectObserved = false;
2542
- let installReconnectObserved = false;
2543
- let finishedStatusSnapshotKey: string | undefined;
2544
- let finishedStatusSnapshotPolls = 0;
2545
- let fastFinishedInstallProgressReported = false;
2546
- let installEvidenceObserved = this.protocolV2InstallTerminalSuccessObserved;
2392
+ let installEvidenceObserved = false;
2547
2393
  let currentInstallStatusObserved = false;
2548
- const liveTargetIds = new Set<number>();
2549
- const recordBleInstallDisconnect = (error: unknown) => {
2550
- if (
2551
- !isBleInstall ||
2552
- !isProtocolV2DeviceDisconnectedError(error) ||
2553
- isProtocolV2InstallResponseTimeout(error)
2554
- ) {
2555
- return;
2556
- }
2557
- installStatusDisconnectObserved = true;
2558
- installReconnectObserved = false;
2559
- finishedStatusSnapshotKey = undefined;
2560
- finishedStatusSnapshotPolls = 0;
2561
- };
2562
2394
 
2563
2395
  while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
2564
2396
  // A transport release caused by an explicit workflow cancellation must not
@@ -2566,40 +2398,43 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2566
2398
  this.throwIfAborted();
2567
2399
  try {
2568
2400
  if (shouldReconnect) {
2569
- const isBleInstallReconnect = this.isBleReconnect();
2570
- await this.reconnectProtocolV2Device({ skipProtocolProbe: true });
2571
- bleInstallLinkReady = isBleInstallReconnect;
2572
- deviceInfo = isBleInstallReconnect
2573
- ? undefined
2574
- : await this.verifyProtocolV2ReconnectIdentity();
2575
- if (isBleInstallReconnect && installStatusDisconnectObserved) {
2576
- installReconnectObserved = true;
2577
- }
2401
+ await this.reconnectProtocolV2Device();
2402
+ deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2578
2403
  shouldReconnect = false;
2579
2404
  }
2580
- let currentDeviceInfo = deviceInfo;
2405
+ const currentDeviceInfo = deviceInfo;
2581
2406
  try {
2582
2407
  const statusResponse = await this.device.getCommands().typedCall(
2583
2408
  'DeviceFirmwareUpdateStatusGet',
2584
- 'DeviceFirmwareUpdateStatus',
2409
+ ['DeviceFirmwareUpdateStatus', 'Success'],
2585
2410
  {
2586
2411
  fields: {
2587
2412
  status: true,
2588
- progress_percent: true,
2589
- phase_info: true,
2590
2413
  payload_version: true,
2591
2414
  path: true,
2592
2415
  },
2593
2416
  },
2594
2417
  { timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
2595
2418
  );
2596
- if (this.protocolV2InstallTerminalSuccessObserved) {
2419
+ if (statusResponse.type === 'Success') {
2597
2420
  installEvidenceObserved = true;
2421
+ lastError = new Error(
2422
+ 'Protocol V2 firmware install acknowledged; waiting for target status'
2423
+ );
2598
2424
  }
2599
- const statusTargets = (statusResponse.message.records ??
2600
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
2601
- this.collectProtocolV2LiveTargetIds(statusTargets, expectedTargetIds, liveTargetIds);
2602
- if (liveTargetIds.size > 0) {
2425
+ const statusTargets =
2426
+ statusResponse.type === 'DeviceFirmwareUpdateStatus'
2427
+ ? ((statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[])
2428
+ : [];
2429
+ const hasInProgressTarget = statusTargets.some(target => {
2430
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
2431
+ return (
2432
+ targetId !== undefined &&
2433
+ expectedTargetIds.has(targetId) &&
2434
+ isProtocolV2TargetStatusInProgress(target.status)
2435
+ );
2436
+ });
2437
+ if (hasInProgressTarget) {
2603
2438
  currentInstallStatusObserved = true;
2604
2439
  }
2605
2440
  const hasMatchingTargetStatus = statusTargets.some(target => {
@@ -2616,101 +2451,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2616
2451
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2617
2452
  return targetId !== undefined && expectedTargetIds.has(targetId);
2618
2453
  });
2619
- const matchingFinishedTargetIds = new Set<number>();
2620
- const matchingFinishedStatus = matchingStatusTargets
2621
- .map(target => {
2622
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2623
- const expectedPath = targetId === undefined ? undefined : expectedPaths.get(targetId);
2624
- const expectedVersion =
2625
- targetId === undefined
2626
- ? undefined
2627
- : this.getExpectedProtocolV2TargetVersion(targetId);
2628
- const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
2629
- if (
2630
- targetId === undefined ||
2631
- matchingFinishedTargetIds.has(targetId) ||
2632
- !isProtocolV2TargetStatusFinished(target.status) ||
2633
- (Boolean(target.path) && Boolean(expectedPath) && target.path !== expectedPath) ||
2634
- (expectedVersion !== undefined &&
2635
- payloadVersion !== undefined &&
2636
- payloadVersion !== expectedVersion)
2637
- ) {
2638
- return undefined;
2639
- }
2640
- matchingFinishedTargetIds.add(targetId);
2641
- return [targetId, target.path ?? null, payloadVersion ?? null] as const;
2642
- })
2643
- .filter((target): target is readonly [number, string | null, number | null] => !!target)
2644
- .sort(([leftTargetId], [rightTargetId]) => leftTargetId - rightTargetId);
2645
- const hasCompleteMatchingFinishedStatus =
2646
- matchingFinishedStatus.length === expectedTargetIds.size &&
2647
- matchingFinishedTargetIds.size === expectedTargetIds.size &&
2648
- expectedTargetIds.size > 0;
2649
- if (
2650
- requireCurrentInstallStatus &&
2651
- installReconnectObserved &&
2652
- hasCompleteMatchingFinishedStatus
2653
- ) {
2654
- const nextSnapshotKey = JSON.stringify(matchingFinishedStatus);
2655
- if (nextSnapshotKey === finishedStatusSnapshotKey) {
2656
- finishedStatusSnapshotPolls += 1;
2657
- } else {
2658
- finishedStatusSnapshotKey = nextSnapshotKey;
2659
- finishedStatusSnapshotPolls = 1;
2660
- }
2661
- } else {
2662
- finishedStatusSnapshotKey = undefined;
2663
- finishedStatusSnapshotPolls = 0;
2664
- }
2665
- if (
2666
- requireCurrentInstallStatus &&
2667
- installReconnectObserved &&
2668
- hasCompleteMatchingFinishedStatus &&
2669
- !currentInstallStatusObserved &&
2670
- !this.protocolV2InstallTerminalSuccessObserved &&
2671
- !fastFinishedInstallProgressReported
2672
- ) {
2673
- // A fast BLE install may finish while disconnected, so IN_PROGRESS can be hidden.
2674
- // A status response timeout alone is not evidence that the install was accepted.
2675
- this.postProgressMessage(1, 'installingFirmware');
2676
- fastFinishedInstallProgressReported = true;
2677
- }
2678
- const finishedAfterDisconnectObserved =
2679
- finishedStatusSnapshotPolls >= PROTOCOL_V2_INSTALL_FINISHED_AFTER_DISCONNECT_POLLS;
2680
- const hasCurrentInstallEvidence =
2681
- currentInstallStatusObserved ||
2682
- this.protocolV2InstallTerminalSuccessObserved ||
2683
- finishedAfterDisconnectObserved;
2684
- if (hasCurrentInstallEvidence) {
2685
- installEvidenceObserved = true;
2686
- }
2687
2454
  const shouldVerifyTargetCompletion =
2688
- !requireCurrentInstallStatus || hasCurrentInstallEvidence;
2455
+ !requireCurrentInstallStatus || currentInstallStatusObserved;
2689
2456
  if (
2690
2457
  shouldVerifyTargetCompletion &&
2691
- this.assertProtocolV2TargetStatus(
2692
- statusTargets,
2693
- expectedTargetIds,
2694
- expectedPaths,
2695
- requireCurrentInstallStatus &&
2696
- !this.protocolV2InstallTerminalSuccessObserved &&
2697
- !finishedAfterDisconnectObserved
2698
- ? liveTargetIds
2699
- : undefined
2700
- )
2458
+ this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)
2701
2459
  ) {
2702
2460
  return;
2703
2461
  }
2704
2462
 
2705
- if (!hasCurrentInstallEvidence) {
2706
- // A successful status poll clears DeviceCommands' cancel action. Keep cancellation
2707
- // available while UpdateRequest is still waiting for confirmation on the device.
2708
- this.device.setCancelableAction(() => this.device.getCommands().cancelDevice());
2709
- }
2710
-
2711
2463
  if (
2712
2464
  requireCurrentInstallStatus &&
2713
- !hasCurrentInstallEvidence &&
2465
+ !currentInstallStatusObserved &&
2714
2466
  matchingStatusTargets.length > 0
2715
2467
  ) {
2716
2468
  lastError = new Error(
@@ -2718,18 +2470,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2718
2470
  );
2719
2471
  }
2720
2472
 
2721
- if (
2722
- statusTargets.length === 0 &&
2723
- !currentDeviceInfo &&
2724
- bleInstallLinkReady &&
2725
- installEvidenceObserved
2726
- ) {
2727
- // BLE install reconnect skips generic probes because loaders may not answer Ping.
2728
- // Restore the verified identity only after current-install evidence disappears.
2729
- currentDeviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2730
- deviceInfo = currentDeviceInfo;
2731
- }
2732
-
2733
2473
  if (statusTargets.length === 0 && currentDeviceInfo) {
2734
2474
  const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
2735
2475
  if (
@@ -2747,9 +2487,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2747
2487
  }
2748
2488
 
2749
2489
  if (statusTargets.length === 0) {
2750
- lastError = new Error(
2751
- 'Protocol V2 firmware update is waiting for user confirmation or target status'
2752
- );
2490
+ if (statusResponse.type !== 'Success') {
2491
+ lastError = new Error(
2492
+ 'Protocol V2 firmware update is waiting for user confirmation or target status'
2493
+ );
2494
+ }
2753
2495
  } else {
2754
2496
  const missingTargetIds = this.getProtocolV2MissingTargetIds(
2755
2497
  statusTargets,
@@ -2772,22 +2514,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2772
2514
  // missing endpoint as completion only after the runtime probe confirms App mode.
2773
2515
  if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
2774
2516
  if (!currentDeviceInfo) {
2775
- if (!bleInstallLinkReady) {
2776
- throw ERRORS.TypedError(
2777
- HardwareErrorCode.RuntimeError,
2778
- 'Protocol V2 device identity is unavailable during install polling'
2779
- );
2780
- }
2781
- deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2782
- }
2783
- const reconnectDeviceInfo = currentDeviceInfo ?? deviceInfo;
2784
- if (!reconnectDeviceInfo) {
2785
2517
  throw ERRORS.TypedError(
2786
2518
  HardwareErrorCode.RuntimeError,
2787
2519
  'Protocol V2 device identity is unavailable during install polling'
2788
2520
  );
2789
2521
  }
2790
- const isNormalMode = await this.probeProtocolV2NormalMode(reconnectDeviceInfo);
2522
+ const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
2791
2523
  if (
2792
2524
  isNormalMode &&
2793
2525
  (installEvidenceObserved ||
@@ -2810,10 +2542,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2810
2542
  );
2811
2543
  }
2812
2544
  } else {
2813
- recordBleInstallDisconnect(error);
2814
2545
  shouldReconnect = true;
2815
2546
  deviceInfo = undefined;
2816
- bleInstallLinkReady = false;
2817
2547
  lastError = error;
2818
2548
  Log.log(
2819
2549
  '[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
@@ -2835,10 +2565,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2835
2565
  ) {
2836
2566
  throw error;
2837
2567
  }
2838
- recordBleInstallDisconnect(error);
2839
2568
  shouldReconnect = true;
2840
2569
  deviceInfo = undefined;
2841
- bleInstallLinkReady = false;
2842
2570
  Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
2843
2571
  }
2844
2572
  await wait(1000);
@@ -2867,9 +2595,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2867
2595
  return;
2868
2596
  }
2869
2597
  } catch (error) {
2870
- if (isBleStaleBondHardwareError(error)) {
2871
- throw error;
2872
- }
2873
2598
  Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
2874
2599
  }
2875
2600
  await this.protocolV2Reboot(DeviceRebootType.Normal);
@@ -2961,7 +2686,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2961
2686
  'Protocol V2 device is still in bootloader mode'
2962
2687
  );
2963
2688
  } catch (error) {
2964
- if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
2689
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
2965
2690
  throw error;
2966
2691
  }
2967
2692
  shouldReconnect = true;
@@ -2979,53 +2704,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2979
2704
  );
2980
2705
  }
2981
2706
 
2982
- private async reconnectProtocolV2Device(options?: { skipProtocolProbe?: boolean }) {
2707
+ private async reconnectProtocolV2Device() {
2983
2708
  if (this.isBleReconnect()) {
2984
- await this.acquireProtocolV2BleDevice(options?.skipProtocolProbe);
2709
+ await this.acquireProtocolV2BleDevice();
2985
2710
  return;
2986
2711
  }
2987
2712
 
2988
2713
  const deviceDiff = await this.device.deviceConnector?.enumerate();
2989
2714
  const devicesDescriptor = deviceDiff?.descriptors ?? [];
2990
- const env = DataManager.getSettings('env');
2991
- const isWebUsb = DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env);
2992
-
2993
- if (isWebUsb && options?.skipProtocolProbe) {
2994
- const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
2995
- const expectedPath = this.protocolV2ExpectedPath?.trim();
2996
- const reconnectDescriptor = devicesDescriptor.find(descriptor => {
2997
- const descriptorPath = descriptor.path?.trim();
2998
- return (
2999
- (!!expectedSerialNumber && descriptorPath === expectedSerialNumber) ||
3000
- (!!expectedPath && descriptorPath === expectedPath)
3001
- );
3002
- });
3003
- if (!reconnectDescriptor?.path) {
3004
- throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
3005
- }
3006
- this.device.updateDescriptor(
3007
- {
3008
- ...reconnectDescriptor,
3009
- protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
3010
- },
3011
- true
3012
- );
3013
- const mainId = await this.device.deviceConnector?.acquire(
3014
- reconnectDescriptor.path,
3015
- null,
3016
- undefined,
3017
- PROTOCOL_V2_CONNECT_PROTOCOL,
3018
- undefined,
3019
- undefined,
3020
- true
3021
- );
3022
- this.device.mainId = typeof mainId === 'string' ? mainId : reconnectDescriptor.path;
3023
- this.device.commands.disposed = false;
3024
- this.device.getCommands().mainId = this.device.mainId;
3025
- return;
3026
- }
3027
2715
 
3028
- if (isWebUsb && devicesDescriptor.length === 1) {
2716
+ if (
2717
+ DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
2718
+ devicesDescriptor.length === 1
2719
+ ) {
3029
2720
  const descriptor = devicesDescriptor[0];
3030
2721
  if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
3031
2722
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
@@ -3143,24 +2834,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3143
2834
  await wait(2000);
3144
2835
  }
3145
2836
 
3146
- private async acquireProtocolV2BleDevice(skipProtocolProbe = false) {
3147
- const connector = this.device.deviceConnector;
3148
- const expectedId = this.device.originalDescriptor.id;
3149
- if (!skipProtocolProbe) {
3150
- await connector?.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL);
3151
- return;
3152
- }
3153
- await connector?.acquire(
3154
- expectedId,
2837
+ private async acquireProtocolV2BleDevice() {
2838
+ await this.device.deviceConnector?.acquire(
2839
+ this.device.originalDescriptor.id,
3155
2840
  null,
3156
2841
  true,
3157
- PROTOCOL_V2_CONNECT_PROTOCOL,
3158
- undefined,
3159
- undefined,
3160
- skipProtocolProbe
2842
+ PROTOCOL_V2_CONNECT_PROTOCOL
3161
2843
  );
3162
- this.device.commands.disposed = false;
3163
- this.device.getCommands().mainId = expectedId;
3164
2844
  }
3165
2845
 
3166
2846
  private async protocolV2StartFirmwareUpdate({
@@ -3169,12 +2849,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3169
2849
  targets: Array<{ target_id: number; path: string }>;
3170
2850
  }) {
3171
2851
  this.protocolV2LastRuntimeProbeFeatures = undefined;
3172
- this.protocolV2InstallNeedsReconnect = false;
3173
- this.protocolV2InstallDisconnectObserved = false;
3174
- this.protocolV2InstallTerminalSuccessObserved = false;
3175
2852
  const commands = this.device.getCommands();
3176
2853
  await commands.typedCall('DeviceFirmwareUpdateStage', 'Success', { targets });
3177
- this.device.setCancelableAction(() => this.device.getCommands().cancelDevice());
2854
+ this.device.setCancelableAction(() => commands.cancelDevice());
3178
2855
  const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
3179
2856
  this.postMessage(
3180
2857
  createUiMessage(UI_REQUEST.REQUEST_BUTTON, {
@@ -3187,55 +2864,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3187
2864
  ...(interaction ? { interaction } : {}),
3188
2865
  })
3189
2866
  );
3190
-
3191
- try {
3192
- await commands.call(
3193
- 'DeviceFirmwareUpdateRequest',
3194
- {},
3195
- {
3196
- returnAfterWrite: true,
3197
- expectedTypes: ['Success'],
3198
- onResponseAfterWrite: response => {
3199
- if (response.type !== 'Success') return;
3200
- this.protocolV2InstallTerminalSuccessObserved = true;
3201
- this.device.clearCancelableAction();
3202
- Log.log('[FirmwareUpdateV4] firmware install completed by device response');
3203
- },
3204
- }
3205
- );
3206
- Log.log('[FirmwareUpdateV4] firmware install request written; continue with status polling');
3207
- } catch (error) {
3208
- this.throwIfAborted();
3209
- if (!isProtocolV2DeviceDisconnectedError(error)) {
3210
- throw error;
3211
- }
3212
- Log.log(
3213
- '[FirmwareUpdateV4] transport released while writing install request; continue status polling'
3214
- );
3215
- this.protocolV2InstallNeedsReconnect = true;
3216
- this.protocolV2InstallDisconnectObserved = !isProtocolV2InstallResponseTimeout(error);
3217
- }
2867
+ await commands.call('DeviceFirmwareUpdateRequest', {}, { returnAfterWrite: true });
3218
2868
  }
3219
2869
 
3220
2870
  private async protocolV2Reboot(rebootType: DeviceRebootType) {
3221
2871
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
3222
2872
  try {
3223
- const res = await typedCall(
3224
- 'DeviceReboot',
3225
- 'Success',
3226
- {
3227
- reboot_type: rebootType,
3228
- },
3229
- { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
3230
- );
2873
+ const res = await typedCall('DeviceReboot', 'Success', {
2874
+ reboot_type: rebootType,
2875
+ });
3231
2876
  this.device.markProtocolV2Reboot(rebootType);
3232
2877
  return res.message;
3233
2878
  } catch (error) {
3234
- if (
3235
- isProtocolV2DeviceDisconnectedError(error) ||
3236
- isProtocolV2ReconnectProbeError(error) ||
3237
- isProtocolV2ResponseTimeout(error)
3238
- ) {
2879
+ if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
3239
2880
  this.device.markProtocolV2Reboot(rebootType);
3240
2881
  return { message: 'Device rebooted successfully' };
3241
2882
  }