@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
@@ -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
  };
@@ -375,12 +360,11 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
375
360
  };
376
361
 
377
362
  const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
378
- isBleStaleBondHardwareError(error) ||
379
- (error instanceof HardwareError &&
380
- (error.errorCode === HardwareErrorCode.ActionCancelled ||
381
- error.errorCode === HardwareErrorCode.FirmwareError ||
382
- error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
383
- 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);
384
368
 
385
369
  const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
386
370
  normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
@@ -417,18 +401,6 @@ const normalizeProtocolV2TargetStatus = (
417
401
  return undefined;
418
402
  };
419
403
 
420
- const PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE = new Map<
421
- number | string,
422
- 'prepare' | 'install' | 'verify'
423
- >([
424
- [0, 'prepare'],
425
- [1, 'install'],
426
- [2, 'verify'],
427
- ['FW_MGMT_UPDATER_PHASE_PREPARE', 'prepare'],
428
- ['FW_MGMT_UPDATER_PHASE_INSTALL', 'install'],
429
- ['FW_MGMT_UPDATER_PHASE_VERIFY', 'verify'],
430
- ]);
431
-
432
404
  const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
433
405
 
434
406
  const versionArrayToNumber = (version?: IVersionArray) => {
@@ -486,11 +458,6 @@ const toProtocolV2FiniteNumber = (value: unknown) => {
486
458
  return undefined;
487
459
  };
488
460
 
489
- const normalizeProtocolV2ProgressPercent = (value: unknown) => {
490
- const progress = toProtocolV2FiniteNumber(value);
491
- return progress === undefined ? undefined : Math.min(100, Math.max(0, progress));
492
- };
493
-
494
461
  export const isProtocolV2FirmwareFingerprintValid = (
495
462
  binary: ArrayBuffer | Uint8Array,
496
463
  fingerprint: string | undefined
@@ -563,10 +530,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
563
530
 
564
531
  private protocolV2InstallBaselineVersions = new Map<number, string>();
565
532
 
566
- private protocolV2InstallNeedsReconnect = false;
567
-
568
- private protocolV2InstallTerminalSuccessObserved = false;
569
-
570
533
  private protocolV2LastRuntimeProbeFeatures?: Features;
571
534
 
572
535
  private protocolV2LastTransferProgress?: number;
@@ -1845,9 +1808,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1845
1808
  if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
1846
1809
  return true;
1847
1810
  } catch (error) {
1848
- if (isBleStaleBondHardwareError(error)) {
1849
- throw error;
1850
- }
1851
1811
  Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
1852
1812
  return false;
1853
1813
  }
@@ -1941,7 +1901,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1941
1901
  }
1942
1902
  lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
1943
1903
  } catch (error) {
1944
- if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
1904
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1945
1905
  throw error;
1946
1906
  }
1947
1907
  shouldReconnect = true;
@@ -2223,9 +2183,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2223
2183
  });
2224
2184
  return processedSize + source.size;
2225
2185
  } catch (error) {
2226
- if (isBleStaleBondHardwareError(error)) {
2227
- throw error;
2228
- }
2229
2186
  lastError = error;
2230
2187
  if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
2231
2188
  await this.recoverProtocolV2FileTransfer();
@@ -2255,41 +2212,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2255
2212
  }
2256
2213
  }
2257
2214
 
2258
- private collectProtocolV2LiveTargetIds(
2259
- statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2260
- expectedTargetIds: Set<number>,
2261
- liveTargetIds: Set<number>
2262
- ) {
2263
- statusTargets.forEach(target => {
2264
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2265
- if (
2266
- targetId !== undefined &&
2267
- expectedTargetIds.has(targetId) &&
2268
- isProtocolV2TargetStatusInProgress(target.status)
2269
- ) {
2270
- liveTargetIds.add(targetId);
2271
- }
2272
- });
2273
- }
2274
-
2275
2215
  private assertProtocolV2TargetStatus(
2276
2216
  statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2277
2217
  expectedTargetIds: Set<number>,
2278
- expectedPaths?: Map<number, string>,
2279
- liveTargetIds?: Set<number>
2218
+ expectedPaths = new Map<number, string>()
2280
2219
  ) {
2281
- const resolvedExpectedPaths = expectedPaths ?? new Map<number, string>();
2282
2220
  Log.log(
2283
2221
  `[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
2284
2222
  );
2285
- const failedTarget = statusTargets.find(target => {
2286
- const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2287
- return (
2288
- expectedTargetIds.has(targetId) &&
2289
- isProtocolV2TargetStatusFailed(target.status) &&
2290
- (!liveTargetIds || liveTargetIds.has(targetId))
2291
- );
2292
- });
2223
+ const failedTarget = statusTargets.find(
2224
+ target =>
2225
+ expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
2226
+ isProtocolV2TargetStatusFailed(target.status)
2227
+ );
2293
2228
  if (failedTarget) {
2294
2229
  const failedTargetDetails = JSON.stringify({
2295
2230
  targetId: failedTarget.target_id,
@@ -2306,27 +2241,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2306
2241
  }
2307
2242
  );
2308
2243
  }
2309
- if (liveTargetIds) {
2310
- const staleFailedTargets = statusTargets.filter(target => {
2311
- const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2312
- return (
2313
- expectedTargetIds.has(targetId) &&
2314
- isProtocolV2TargetStatusFailed(target.status) &&
2315
- !liveTargetIds.has(targetId)
2316
- );
2317
- });
2318
- if (staleFailedTargets.length > 0) {
2319
- Log.log(
2320
- `[FirmwareUpdateV4] ignoring stale failed firmware status until those targets start: ${JSON.stringify(
2321
- staleFailedTargets.map(target => ({
2322
- targetId: target.target_id,
2323
- status: target.status,
2324
- path: target.path,
2325
- }))
2326
- )}`
2327
- );
2328
- }
2329
- }
2330
2244
 
2331
2245
  const matchingTargets = statusTargets.filter(target =>
2332
2246
  expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
@@ -2334,16 +2248,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2334
2248
  const seenTargetIds = new Set<number>();
2335
2249
  for (const target of matchingTargets) {
2336
2250
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2337
- const pathConflictsWithCurrentInstall =
2338
- targetId !== undefined &&
2339
- Boolean(target.path) &&
2340
- Boolean(resolvedExpectedPaths.get(targetId)) &&
2341
- target.path !== resolvedExpectedPaths.get(targetId) &&
2342
- (!liveTargetIds || liveTargetIds.has(targetId));
2343
2251
  if (
2344
2252
  targetId === undefined ||
2345
2253
  seenTargetIds.has(targetId) ||
2346
- pathConflictsWithCurrentInstall
2254
+ (target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
2347
2255
  ) {
2348
2256
  Log.error(`[FirmwareUpdateV4] install status conflicts with target=${target.target_id}`);
2349
2257
  throw ERRORS.TypedError(
@@ -2356,13 +2264,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2356
2264
  }
2357
2265
  seenTargetIds.add(targetId);
2358
2266
  }
2359
- const completedTargets = matchingTargets.filter(target => {
2360
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2361
- return (
2362
- isProtocolV2TargetStatusFinished(target.status) &&
2363
- (!liveTargetIds || (targetId !== undefined && liveTargetIds.has(targetId)))
2364
- );
2365
- });
2267
+ const completedTargets = matchingTargets.filter(target =>
2268
+ isProtocolV2TargetStatusFinished(target.status)
2269
+ );
2366
2270
  const completedTargetIds = new Set<number>();
2367
2271
  completedTargets.forEach(target => {
2368
2272
  const targetId = normalizeProtocolV2TargetId(target.target_id);
@@ -2399,54 +2303,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2399
2303
  }
2400
2304
 
2401
2305
  if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
2402
- let detailedProgress = completedTargetIds.size * 100;
2403
- let installProgressMetadata:
2404
- | {
2405
- installTargetId: number;
2406
- installPhase: 'prepare' | 'install' | 'verify';
2407
- installPhaseProgress: number;
2408
- }
2409
- | undefined;
2410
- let hasInProgressTarget = false;
2411
- matchingTargets.forEach(target => {
2412
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2413
- if (
2414
- targetId === undefined ||
2415
- completedTargetIds.has(targetId) ||
2416
- !isProtocolV2TargetStatusInProgress(target.status) ||
2417
- (liveTargetIds && !liveTargetIds.has(targetId))
2418
- ) {
2419
- return;
2420
- }
2421
- hasInProgressTarget = true;
2422
- detailedProgress += normalizeProtocolV2ProgressPercent(target.progress_percent) ?? 0;
2423
- const installPhase = target.phase_info
2424
- ? PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE.get(target.phase_info.phase)
2425
- : undefined;
2426
- const installPhaseProgress = normalizeProtocolV2ProgressPercent(
2427
- target.phase_info?.progress_percent
2428
- );
2429
- if (installPhase && installPhaseProgress !== undefined && !installProgressMetadata) {
2430
- installProgressMetadata = {
2431
- installTargetId: targetId,
2432
- installPhase,
2433
- installPhaseProgress,
2434
- };
2435
- }
2436
- });
2306
+ const hasInProgressTarget = matchingTargets.some(target =>
2307
+ isProtocolV2TargetStatusInProgress(target.status)
2308
+ );
2437
2309
  const completedProgress = Math.floor(
2438
2310
  (completedTargetIds.size / expectedTargetIds.size) * 100
2439
2311
  );
2440
- const reportedProgress = Math.floor(detailedProgress / expectedTargetIds.size);
2441
- const progress = Math.min(
2442
- 99,
2443
- Math.max(completedProgress, reportedProgress, hasInProgressTarget ? 1 : 0)
2444
- );
2445
- if (installProgressMetadata) {
2446
- this.postProgressMessage(progress, 'installingFirmware', installProgressMetadata);
2447
- } else {
2448
- this.postProgressMessage(progress, 'installingFirmware');
2449
- }
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');
2450
2316
  }
2451
2317
 
2452
2318
  return false;
@@ -2517,25 +2383,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2517
2383
  ) {
2518
2384
  const expectedTargetIds = new Set(targets.map(target => target.target_id));
2519
2385
  const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
2520
- const isBleCoprocessorInstall =
2521
- this.isBleReconnect() &&
2522
- expectedTargetIds.has(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR);
2523
2386
  const startTime = Date.now();
2524
2387
  let lastError: unknown;
2525
- // The loader may release either USB or BLE as installation starts. Recovery reconnects
2526
- // directly to status polling without generic Ping or DeviceInfo probes.
2527
- let shouldReconnect = this.protocolV2InstallNeedsReconnect;
2528
- 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;
2529
2391
  let deviceInfo: ProtocolV2DeviceInfo | undefined;
2530
- let bleInstallLinkReady = false;
2531
- let installStatusDisconnectObserved = false;
2532
- let installReconnectObserved = false;
2533
- let finishedStatusSnapshotKey: string | undefined;
2534
- let finishedStatusSnapshotPolls = 0;
2535
- let fastFinishedInstallProgressReported = false;
2536
- let installEvidenceObserved = this.protocolV2InstallTerminalSuccessObserved;
2392
+ let installEvidenceObserved = false;
2537
2393
  let currentInstallStatusObserved = false;
2538
- const liveTargetIds = new Set<number>();
2539
2394
 
2540
2395
  while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
2541
2396
  // A transport release caused by an explicit workflow cancellation must not
@@ -2543,40 +2398,43 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2543
2398
  this.throwIfAborted();
2544
2399
  try {
2545
2400
  if (shouldReconnect) {
2546
- const isBleInstallReconnect = this.isBleReconnect();
2547
- await this.reconnectProtocolV2Device({ skipProtocolProbe: true });
2548
- bleInstallLinkReady = isBleInstallReconnect;
2549
- deviceInfo = isBleInstallReconnect
2550
- ? undefined
2551
- : await this.verifyProtocolV2ReconnectIdentity();
2552
- if (isBleInstallReconnect && installStatusDisconnectObserved) {
2553
- installReconnectObserved = true;
2554
- }
2401
+ await this.reconnectProtocolV2Device();
2402
+ deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2555
2403
  shouldReconnect = false;
2556
2404
  }
2557
- let currentDeviceInfo = deviceInfo;
2405
+ const currentDeviceInfo = deviceInfo;
2558
2406
  try {
2559
2407
  const statusResponse = await this.device.getCommands().typedCall(
2560
2408
  'DeviceFirmwareUpdateStatusGet',
2561
- 'DeviceFirmwareUpdateStatus',
2409
+ ['DeviceFirmwareUpdateStatus', 'Success'],
2562
2410
  {
2563
2411
  fields: {
2564
2412
  status: true,
2565
- progress_percent: true,
2566
- phase_info: true,
2567
2413
  payload_version: true,
2568
2414
  path: true,
2569
2415
  },
2570
2416
  },
2571
2417
  { timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
2572
2418
  );
2573
- if (this.protocolV2InstallTerminalSuccessObserved) {
2419
+ if (statusResponse.type === 'Success') {
2574
2420
  installEvidenceObserved = true;
2421
+ lastError = new Error(
2422
+ 'Protocol V2 firmware install acknowledged; waiting for target status'
2423
+ );
2575
2424
  }
2576
- const statusTargets = (statusResponse.message.records ??
2577
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
2578
- this.collectProtocolV2LiveTargetIds(statusTargets, expectedTargetIds, liveTargetIds);
2579
- 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) {
2580
2438
  currentInstallStatusObserved = true;
2581
2439
  }
2582
2440
  const hasMatchingTargetStatus = statusTargets.some(target => {
@@ -2593,101 +2451,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2593
2451
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2594
2452
  return targetId !== undefined && expectedTargetIds.has(targetId);
2595
2453
  });
2596
- const matchingFinishedTargetIds = new Set<number>();
2597
- const matchingFinishedStatus = matchingStatusTargets
2598
- .map(target => {
2599
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2600
- const expectedPath = targetId === undefined ? undefined : expectedPaths.get(targetId);
2601
- const expectedVersion =
2602
- targetId === undefined
2603
- ? undefined
2604
- : this.getExpectedProtocolV2TargetVersion(targetId);
2605
- const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
2606
- if (
2607
- targetId === undefined ||
2608
- matchingFinishedTargetIds.has(targetId) ||
2609
- !isProtocolV2TargetStatusFinished(target.status) ||
2610
- (Boolean(target.path) && Boolean(expectedPath) && target.path !== expectedPath) ||
2611
- (expectedVersion !== undefined &&
2612
- payloadVersion !== undefined &&
2613
- payloadVersion !== expectedVersion)
2614
- ) {
2615
- return undefined;
2616
- }
2617
- matchingFinishedTargetIds.add(targetId);
2618
- return [targetId, target.path ?? null, payloadVersion ?? null] as const;
2619
- })
2620
- .filter((target): target is readonly [number, string | null, number | null] => !!target)
2621
- .sort(([leftTargetId], [rightTargetId]) => leftTargetId - rightTargetId);
2622
- const hasCompleteMatchingFinishedStatus =
2623
- matchingFinishedStatus.length === expectedTargetIds.size &&
2624
- matchingFinishedTargetIds.size === expectedTargetIds.size &&
2625
- expectedTargetIds.size > 0;
2626
- if (
2627
- requireCurrentInstallStatus &&
2628
- installReconnectObserved &&
2629
- hasCompleteMatchingFinishedStatus
2630
- ) {
2631
- const nextSnapshotKey = JSON.stringify(matchingFinishedStatus);
2632
- if (nextSnapshotKey === finishedStatusSnapshotKey) {
2633
- finishedStatusSnapshotPolls += 1;
2634
- } else {
2635
- finishedStatusSnapshotKey = nextSnapshotKey;
2636
- finishedStatusSnapshotPolls = 1;
2637
- }
2638
- } else {
2639
- finishedStatusSnapshotKey = undefined;
2640
- finishedStatusSnapshotPolls = 0;
2641
- }
2642
- if (
2643
- requireCurrentInstallStatus &&
2644
- installReconnectObserved &&
2645
- hasCompleteMatchingFinishedStatus &&
2646
- !currentInstallStatusObserved &&
2647
- !this.protocolV2InstallTerminalSuccessObserved &&
2648
- !fastFinishedInstallProgressReported
2649
- ) {
2650
- // A fast BLE install may finish while disconnected, so IN_PROGRESS can be hidden.
2651
- // A status response timeout alone is not evidence that the install was accepted.
2652
- this.postProgressMessage(1, 'installingFirmware');
2653
- fastFinishedInstallProgressReported = true;
2654
- }
2655
- const finishedAfterDisconnectObserved =
2656
- finishedStatusSnapshotPolls >= PROTOCOL_V2_INSTALL_FINISHED_AFTER_DISCONNECT_POLLS;
2657
- const hasCurrentInstallEvidence =
2658
- currentInstallStatusObserved ||
2659
- this.protocolV2InstallTerminalSuccessObserved ||
2660
- finishedAfterDisconnectObserved;
2661
- if (hasCurrentInstallEvidence) {
2662
- installEvidenceObserved = true;
2663
- }
2664
2454
  const shouldVerifyTargetCompletion =
2665
- !requireCurrentInstallStatus || hasCurrentInstallEvidence;
2455
+ !requireCurrentInstallStatus || currentInstallStatusObserved;
2666
2456
  if (
2667
2457
  shouldVerifyTargetCompletion &&
2668
- this.assertProtocolV2TargetStatus(
2669
- statusTargets,
2670
- expectedTargetIds,
2671
- expectedPaths,
2672
- requireCurrentInstallStatus &&
2673
- !this.protocolV2InstallTerminalSuccessObserved &&
2674
- !finishedAfterDisconnectObserved
2675
- ? liveTargetIds
2676
- : undefined
2677
- )
2458
+ this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)
2678
2459
  ) {
2679
2460
  return;
2680
2461
  }
2681
2462
 
2682
- if (!hasCurrentInstallEvidence) {
2683
- // A successful status poll clears DeviceCommands' cancel action. Keep cancellation
2684
- // available while UpdateRequest is still waiting for confirmation on the device.
2685
- this.device.setCancelableAction(() => this.device.getCommands().cancelDevice());
2686
- }
2687
-
2688
2463
  if (
2689
2464
  requireCurrentInstallStatus &&
2690
- !hasCurrentInstallEvidence &&
2465
+ !currentInstallStatusObserved &&
2691
2466
  matchingStatusTargets.length > 0
2692
2467
  ) {
2693
2468
  lastError = new Error(
@@ -2695,18 +2470,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2695
2470
  );
2696
2471
  }
2697
2472
 
2698
- if (
2699
- statusTargets.length === 0 &&
2700
- !currentDeviceInfo &&
2701
- bleInstallLinkReady &&
2702
- installEvidenceObserved
2703
- ) {
2704
- // BLE install reconnect skips generic probes because loaders may not answer Ping.
2705
- // Restore the verified identity only after current-install evidence disappears.
2706
- currentDeviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2707
- deviceInfo = currentDeviceInfo;
2708
- }
2709
-
2710
2473
  if (statusTargets.length === 0 && currentDeviceInfo) {
2711
2474
  const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
2712
2475
  if (
@@ -2724,9 +2487,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2724
2487
  }
2725
2488
 
2726
2489
  if (statusTargets.length === 0) {
2727
- lastError = new Error(
2728
- 'Protocol V2 firmware update is waiting for user confirmation or target status'
2729
- );
2490
+ if (statusResponse.type !== 'Success') {
2491
+ lastError = new Error(
2492
+ 'Protocol V2 firmware update is waiting for user confirmation or target status'
2493
+ );
2494
+ }
2730
2495
  } else {
2731
2496
  const missingTargetIds = this.getProtocolV2MissingTargetIds(
2732
2497
  statusTargets,
@@ -2749,22 +2514,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2749
2514
  // missing endpoint as completion only after the runtime probe confirms App mode.
2750
2515
  if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
2751
2516
  if (!currentDeviceInfo) {
2752
- if (!bleInstallLinkReady) {
2753
- throw ERRORS.TypedError(
2754
- HardwareErrorCode.RuntimeError,
2755
- 'Protocol V2 device identity is unavailable during install polling'
2756
- );
2757
- }
2758
- deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
2759
- }
2760
- const reconnectDeviceInfo = currentDeviceInfo ?? deviceInfo;
2761
- if (!reconnectDeviceInfo) {
2762
2517
  throw ERRORS.TypedError(
2763
2518
  HardwareErrorCode.RuntimeError,
2764
2519
  'Protocol V2 device identity is unavailable during install polling'
2765
2520
  );
2766
2521
  }
2767
- const isNormalMode = await this.probeProtocolV2NormalMode(reconnectDeviceInfo);
2522
+ const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
2768
2523
  if (
2769
2524
  isNormalMode &&
2770
2525
  (installEvidenceObserved ||
@@ -2787,18 +2542,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2787
2542
  );
2788
2543
  }
2789
2544
  } else {
2790
- if (
2791
- isBleCoprocessorInstall &&
2792
- isProtocolV2DeviceDisconnectedError(error) &&
2793
- !isProtocolV2ResponseTimeout(error)
2794
- ) {
2795
- installStatusDisconnectObserved = true;
2796
- finishedStatusSnapshotKey = undefined;
2797
- finishedStatusSnapshotPolls = 0;
2798
- }
2799
2545
  shouldReconnect = true;
2800
2546
  deviceInfo = undefined;
2801
- bleInstallLinkReady = false;
2802
2547
  lastError = error;
2803
2548
  Log.log(
2804
2549
  '[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
@@ -2822,7 +2567,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2822
2567
  }
2823
2568
  shouldReconnect = true;
2824
2569
  deviceInfo = undefined;
2825
- bleInstallLinkReady = false;
2826
2570
  Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
2827
2571
  }
2828
2572
  await wait(1000);
@@ -2851,9 +2595,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2851
2595
  return;
2852
2596
  }
2853
2597
  } catch (error) {
2854
- if (isBleStaleBondHardwareError(error)) {
2855
- throw error;
2856
- }
2857
2598
  Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
2858
2599
  }
2859
2600
  await this.protocolV2Reboot(DeviceRebootType.Normal);
@@ -2945,7 +2686,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2945
2686
  'Protocol V2 device is still in bootloader mode'
2946
2687
  );
2947
2688
  } catch (error) {
2948
- if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
2689
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
2949
2690
  throw error;
2950
2691
  }
2951
2692
  shouldReconnect = true;
@@ -2963,53 +2704,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2963
2704
  );
2964
2705
  }
2965
2706
 
2966
- private async reconnectProtocolV2Device(options?: { skipProtocolProbe?: boolean }) {
2707
+ private async reconnectProtocolV2Device() {
2967
2708
  if (this.isBleReconnect()) {
2968
- await this.acquireProtocolV2BleDevice(options?.skipProtocolProbe);
2709
+ await this.acquireProtocolV2BleDevice();
2969
2710
  return;
2970
2711
  }
2971
2712
 
2972
2713
  const deviceDiff = await this.device.deviceConnector?.enumerate();
2973
2714
  const devicesDescriptor = deviceDiff?.descriptors ?? [];
2974
- const env = DataManager.getSettings('env');
2975
- const isWebUsb = DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env);
2976
-
2977
- if (isWebUsb && options?.skipProtocolProbe) {
2978
- const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
2979
- const expectedPath = this.protocolV2ExpectedPath?.trim();
2980
- const reconnectDescriptor = devicesDescriptor.find(descriptor => {
2981
- const descriptorPath = descriptor.path?.trim();
2982
- return (
2983
- (!!expectedSerialNumber && descriptorPath === expectedSerialNumber) ||
2984
- (!!expectedPath && descriptorPath === expectedPath)
2985
- );
2986
- });
2987
- if (!reconnectDescriptor?.path) {
2988
- throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
2989
- }
2990
- this.device.updateDescriptor(
2991
- {
2992
- ...reconnectDescriptor,
2993
- protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
2994
- },
2995
- true
2996
- );
2997
- const mainId = await this.device.deviceConnector?.acquire(
2998
- reconnectDescriptor.path,
2999
- null,
3000
- undefined,
3001
- PROTOCOL_V2_CONNECT_PROTOCOL,
3002
- undefined,
3003
- undefined,
3004
- true
3005
- );
3006
- this.device.mainId = typeof mainId === 'string' ? mainId : reconnectDescriptor.path;
3007
- this.device.commands.disposed = false;
3008
- this.device.getCommands().mainId = this.device.mainId;
3009
- return;
3010
- }
3011
2715
 
3012
- if (isWebUsb && devicesDescriptor.length === 1) {
2716
+ if (
2717
+ DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
2718
+ devicesDescriptor.length === 1
2719
+ ) {
3013
2720
  const descriptor = devicesDescriptor[0];
3014
2721
  if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
3015
2722
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
@@ -3127,24 +2834,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3127
2834
  await wait(2000);
3128
2835
  }
3129
2836
 
3130
- private async acquireProtocolV2BleDevice(skipProtocolProbe = false) {
3131
- const connector = this.device.deviceConnector;
3132
- const expectedId = this.device.originalDescriptor.id;
3133
- if (!skipProtocolProbe) {
3134
- await connector?.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL);
3135
- return;
3136
- }
3137
- await connector?.acquire(
3138
- expectedId,
2837
+ private async acquireProtocolV2BleDevice() {
2838
+ await this.device.deviceConnector?.acquire(
2839
+ this.device.originalDescriptor.id,
3139
2840
  null,
3140
2841
  true,
3141
- PROTOCOL_V2_CONNECT_PROTOCOL,
3142
- undefined,
3143
- undefined,
3144
- skipProtocolProbe
2842
+ PROTOCOL_V2_CONNECT_PROTOCOL
3145
2843
  );
3146
- this.device.commands.disposed = false;
3147
- this.device.getCommands().mainId = expectedId;
3148
2844
  }
3149
2845
 
3150
2846
  private async protocolV2StartFirmwareUpdate({
@@ -3153,11 +2849,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3153
2849
  targets: Array<{ target_id: number; path: string }>;
3154
2850
  }) {
3155
2851
  this.protocolV2LastRuntimeProbeFeatures = undefined;
3156
- this.protocolV2InstallNeedsReconnect = false;
3157
- this.protocolV2InstallTerminalSuccessObserved = false;
3158
2852
  const commands = this.device.getCommands();
3159
2853
  await commands.typedCall('DeviceFirmwareUpdateStage', 'Success', { targets });
3160
- this.device.setCancelableAction(() => this.device.getCommands().cancelDevice());
2854
+ this.device.setCancelableAction(() => commands.cancelDevice());
3161
2855
  const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
3162
2856
  this.postMessage(
3163
2857
  createUiMessage(UI_REQUEST.REQUEST_BUTTON, {
@@ -3170,54 +2864,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3170
2864
  ...(interaction ? { interaction } : {}),
3171
2865
  })
3172
2866
  );
3173
-
3174
- try {
3175
- await commands.call(
3176
- 'DeviceFirmwareUpdateRequest',
3177
- {},
3178
- {
3179
- returnAfterWrite: true,
3180
- expectedTypes: ['Success'],
3181
- onResponseAfterWrite: response => {
3182
- if (response.type !== 'Success') return;
3183
- this.protocolV2InstallTerminalSuccessObserved = true;
3184
- this.device.clearCancelableAction();
3185
- Log.log('[FirmwareUpdateV4] firmware install completed by device response');
3186
- },
3187
- }
3188
- );
3189
- Log.log('[FirmwareUpdateV4] firmware install request written; continue with status polling');
3190
- } catch (error) {
3191
- this.throwIfAborted();
3192
- if (!isProtocolV2DeviceDisconnectedError(error)) {
3193
- throw error;
3194
- }
3195
- Log.log(
3196
- '[FirmwareUpdateV4] transport released while writing install request; continue status polling'
3197
- );
3198
- this.protocolV2InstallNeedsReconnect = true;
3199
- }
2867
+ await commands.call('DeviceFirmwareUpdateRequest', {}, { returnAfterWrite: true });
3200
2868
  }
3201
2869
 
3202
2870
  private async protocolV2Reboot(rebootType: DeviceRebootType) {
3203
2871
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
3204
2872
  try {
3205
- const res = await typedCall(
3206
- 'DeviceReboot',
3207
- 'Success',
3208
- {
3209
- reboot_type: rebootType,
3210
- },
3211
- { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
3212
- );
2873
+ const res = await typedCall('DeviceReboot', 'Success', {
2874
+ reboot_type: rebootType,
2875
+ });
3213
2876
  this.device.markProtocolV2Reboot(rebootType);
3214
2877
  return res.message;
3215
2878
  } catch (error) {
3216
- if (
3217
- isProtocolV2DeviceDisconnectedError(error) ||
3218
- isProtocolV2ReconnectProbeError(error) ||
3219
- isProtocolV2ResponseTimeout(error)
3220
- ) {
2879
+ if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
3221
2880
  this.device.markProtocolV2Reboot(rebootType);
3222
2881
  return { message: 'Device rebooted successfully' };
3223
2882
  }