@onekeyfe/hd-core 1.2.0-alpha.167 → 1.2.0-alpha.169
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.
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +6 -6
- package/__tests__/pro2Wallpaper.test.ts +66 -0
- package/__tests__/protocol-v2.test.ts +309 -19
- package/dist/api/FirmwareUpdateV4.d.ts +2 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +277 -72
- package/dist/utils/pro2Wallpaper.d.ts +3 -1
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +127 -17
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +12 -2
- package/src/core/index.ts +6 -100
- package/src/device/DeviceConnector.ts +8 -3
- package/src/utils/pro2Wallpaper.ts +197 -1
|
@@ -375,6 +375,18 @@ const isProtocolV2TargetStatusInProgress = (
|
|
|
375
375
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_PENDING ||
|
|
376
376
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS;
|
|
377
377
|
|
|
378
|
+
const getProtocolV2FirmwareStatusFingerprint = (
|
|
379
|
+
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[]
|
|
380
|
+
) =>
|
|
381
|
+
JSON.stringify(
|
|
382
|
+
statusTargets.map(target => ({
|
|
383
|
+
targetId: normalizeProtocolV2TargetId(target.target_id) ?? target.target_id,
|
|
384
|
+
status: normalizeProtocolV2TargetStatus(target.status) ?? target.status ?? null,
|
|
385
|
+
payloadVersion: target.payload_version ?? null,
|
|
386
|
+
path: target.path ?? null,
|
|
387
|
+
}))
|
|
388
|
+
);
|
|
389
|
+
|
|
378
390
|
const isProtocolV2TargetStatusFailed = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) => {
|
|
379
391
|
const normalizedStatus = normalizeProtocolV2TargetStatus(status);
|
|
380
392
|
return (
|
|
@@ -530,6 +542,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
530
542
|
|
|
531
543
|
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
532
544
|
|
|
545
|
+
private protocolV2InstallStatusBaseline?: ProtocolV2FirmwareUpdateStatusTarget[];
|
|
546
|
+
|
|
547
|
+
private protocolV2InstallNeedsBleReconnect = false;
|
|
548
|
+
|
|
533
549
|
private protocolV2LastRuntimeProbeFeatures?: Features;
|
|
534
550
|
|
|
535
551
|
private protocolV2LastTransferProgress?: number;
|
|
@@ -2383,23 +2399,34 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2383
2399
|
) {
|
|
2384
2400
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
2385
2401
|
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
2386
|
-
const
|
|
2402
|
+
const confirmationStartedAt = Date.now();
|
|
2403
|
+
let installStartedAt = requireCurrentInstallStatus ? undefined : confirmationStartedAt;
|
|
2404
|
+
const effectiveBaselineStatusTargets = this.protocolV2InstallStatusBaseline;
|
|
2405
|
+
const baselineStatusFingerprint = effectiveBaselineStatusTargets
|
|
2406
|
+
? getProtocolV2FirmwareStatusFingerprint(effectiveBaselineStatusTargets)
|
|
2407
|
+
: undefined;
|
|
2387
2408
|
let lastError: unknown;
|
|
2388
|
-
//
|
|
2389
|
-
//
|
|
2390
|
-
let shouldReconnect =
|
|
2409
|
+
// USB may keep the loader link usable. BLE can release it as installation starts, so its
|
|
2410
|
+
// recovery reconnects directly to status polling without generic Ping or DeviceInfo probes.
|
|
2411
|
+
let shouldReconnect = this.protocolV2InstallNeedsBleReconnect;
|
|
2412
|
+
this.protocolV2InstallNeedsBleReconnect = false;
|
|
2391
2413
|
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2414
|
+
let bleInstallLinkReady = false;
|
|
2392
2415
|
let installEvidenceObserved = false;
|
|
2393
2416
|
let currentInstallStatusObserved = false;
|
|
2394
2417
|
|
|
2395
|
-
while (Date.now() -
|
|
2418
|
+
while (Date.now() - (installStartedAt ?? confirmationStartedAt) < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
2396
2419
|
// A transport release caused by an explicit workflow cancellation must not
|
|
2397
2420
|
// be mistaken for the expected device reboot during installation.
|
|
2398
2421
|
this.throwIfAborted();
|
|
2399
2422
|
try {
|
|
2400
2423
|
if (shouldReconnect) {
|
|
2401
|
-
|
|
2402
|
-
|
|
2424
|
+
const isBleInstallReconnect = this.isBleReconnect();
|
|
2425
|
+
await this.reconnectProtocolV2Device({ skipBleProtocolProbe: isBleInstallReconnect });
|
|
2426
|
+
bleInstallLinkReady = isBleInstallReconnect;
|
|
2427
|
+
deviceInfo = isBleInstallReconnect
|
|
2428
|
+
? undefined
|
|
2429
|
+
: await this.verifyProtocolV2ReconnectIdentity();
|
|
2403
2430
|
shouldReconnect = false;
|
|
2404
2431
|
}
|
|
2405
2432
|
const currentDeviceInfo = deviceInfo;
|
|
@@ -2426,7 +2453,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2426
2453
|
statusResponse.type === 'DeviceFirmwareUpdateStatus'
|
|
2427
2454
|
? ((statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[])
|
|
2428
2455
|
: [];
|
|
2429
|
-
const
|
|
2456
|
+
const hasPendingOrInProgressTarget = statusTargets.some(target => {
|
|
2430
2457
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2431
2458
|
return (
|
|
2432
2459
|
targetId !== undefined &&
|
|
@@ -2434,8 +2461,25 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2434
2461
|
isProtocolV2TargetStatusInProgress(target.status)
|
|
2435
2462
|
);
|
|
2436
2463
|
});
|
|
2437
|
-
|
|
2464
|
+
const isExpectedStatusSnapshot =
|
|
2465
|
+
statusTargets.length === targets.length &&
|
|
2466
|
+
targets.every((expectedTarget, index) => {
|
|
2467
|
+
const statusTarget = statusTargets[index];
|
|
2468
|
+
return (
|
|
2469
|
+
statusTarget !== undefined &&
|
|
2470
|
+
normalizeProtocolV2TargetId(statusTarget.target_id) === expectedTarget.target_id &&
|
|
2471
|
+
statusTarget.path === expectedTarget.path
|
|
2472
|
+
);
|
|
2473
|
+
});
|
|
2474
|
+
const statusFingerprint = getProtocolV2FirmwareStatusFingerprint(statusTargets);
|
|
2475
|
+
const hasCurrentInstallTransition =
|
|
2476
|
+
baselineStatusFingerprint !== undefined
|
|
2477
|
+
? isExpectedStatusSnapshot && statusFingerprint !== baselineStatusFingerprint
|
|
2478
|
+
: hasPendingOrInProgressTarget;
|
|
2479
|
+
if (!currentInstallStatusObserved && hasCurrentInstallTransition) {
|
|
2438
2480
|
currentInstallStatusObserved = true;
|
|
2481
|
+
installStartedAt = Date.now();
|
|
2482
|
+
Log.log('[FirmwareUpdateV4] current firmware install records observed');
|
|
2439
2483
|
}
|
|
2440
2484
|
const hasMatchingTargetStatus = statusTargets.some(target => {
|
|
2441
2485
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
@@ -2514,12 +2558,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2514
2558
|
// missing endpoint as completion only after the runtime probe confirms App mode.
|
|
2515
2559
|
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
2516
2560
|
if (!currentDeviceInfo) {
|
|
2561
|
+
if (!bleInstallLinkReady) {
|
|
2562
|
+
throw ERRORS.TypedError(
|
|
2563
|
+
HardwareErrorCode.RuntimeError,
|
|
2564
|
+
'Protocol V2 device identity is unavailable during install polling'
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
2568
|
+
}
|
|
2569
|
+
const reconnectDeviceInfo = currentDeviceInfo ?? deviceInfo;
|
|
2570
|
+
if (!reconnectDeviceInfo) {
|
|
2517
2571
|
throw ERRORS.TypedError(
|
|
2518
2572
|
HardwareErrorCode.RuntimeError,
|
|
2519
2573
|
'Protocol V2 device identity is unavailable during install polling'
|
|
2520
2574
|
);
|
|
2521
2575
|
}
|
|
2522
|
-
const isNormalMode = await this.probeProtocolV2NormalMode(
|
|
2576
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(reconnectDeviceInfo);
|
|
2523
2577
|
if (
|
|
2524
2578
|
isNormalMode &&
|
|
2525
2579
|
(installEvidenceObserved ||
|
|
@@ -2544,6 +2598,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2544
2598
|
} else {
|
|
2545
2599
|
shouldReconnect = true;
|
|
2546
2600
|
deviceInfo = undefined;
|
|
2601
|
+
bleInstallLinkReady = false;
|
|
2547
2602
|
lastError = error;
|
|
2548
2603
|
Log.log(
|
|
2549
2604
|
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
@@ -2567,6 +2622,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2567
2622
|
}
|
|
2568
2623
|
shouldReconnect = true;
|
|
2569
2624
|
deviceInfo = undefined;
|
|
2625
|
+
bleInstallLinkReady = false;
|
|
2570
2626
|
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
2571
2627
|
}
|
|
2572
2628
|
await wait(1000);
|
|
@@ -2704,9 +2760,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2704
2760
|
);
|
|
2705
2761
|
}
|
|
2706
2762
|
|
|
2707
|
-
private async reconnectProtocolV2Device() {
|
|
2763
|
+
private async reconnectProtocolV2Device(options?: { skipBleProtocolProbe?: boolean }) {
|
|
2708
2764
|
if (this.isBleReconnect()) {
|
|
2709
|
-
await this.acquireProtocolV2BleDevice();
|
|
2765
|
+
await this.acquireProtocolV2BleDevice(options?.skipBleProtocolProbe);
|
|
2710
2766
|
return;
|
|
2711
2767
|
}
|
|
2712
2768
|
|
|
@@ -2834,13 +2890,31 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2834
2890
|
await wait(2000);
|
|
2835
2891
|
}
|
|
2836
2892
|
|
|
2837
|
-
private async acquireProtocolV2BleDevice() {
|
|
2838
|
-
|
|
2839
|
-
|
|
2893
|
+
private async acquireProtocolV2BleDevice(skipProtocolProbe = false) {
|
|
2894
|
+
const connector = this.device.deviceConnector;
|
|
2895
|
+
const expectedId = this.device.originalDescriptor.id;
|
|
2896
|
+
if (!skipProtocolProbe) {
|
|
2897
|
+
await connector?.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL);
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
const deviceDiff = await connector?.enumerate();
|
|
2901
|
+
const reconnectDescriptor = deviceDiff?.descriptors?.find(
|
|
2902
|
+
descriptor => descriptor.id === expectedId
|
|
2903
|
+
);
|
|
2904
|
+
if (!reconnectDescriptor) {
|
|
2905
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
2906
|
+
}
|
|
2907
|
+
await connector?.acquire(
|
|
2908
|
+
reconnectDescriptor.id,
|
|
2840
2909
|
null,
|
|
2841
2910
|
true,
|
|
2842
|
-
PROTOCOL_V2_CONNECT_PROTOCOL
|
|
2911
|
+
PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
2912
|
+
undefined,
|
|
2913
|
+
undefined,
|
|
2914
|
+
skipProtocolProbe
|
|
2843
2915
|
);
|
|
2916
|
+
this.device.commands.disposed = false;
|
|
2917
|
+
this.device.getCommands().mainId = reconnectDescriptor.id;
|
|
2844
2918
|
}
|
|
2845
2919
|
|
|
2846
2920
|
private async protocolV2StartFirmwareUpdate({
|
|
@@ -2849,8 +2923,31 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2849
2923
|
targets: Array<{ target_id: number; path: string }>;
|
|
2850
2924
|
}) {
|
|
2851
2925
|
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
2926
|
+
this.protocolV2InstallStatusBaseline = undefined;
|
|
2927
|
+
this.protocolV2InstallNeedsBleReconnect = false;
|
|
2852
2928
|
const commands = this.device.getCommands();
|
|
2853
2929
|
await commands.typedCall('DeviceFirmwareUpdateStage', 'Success', { targets });
|
|
2930
|
+
try {
|
|
2931
|
+
const baselineStatusResponse = await commands.typedCall(
|
|
2932
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2933
|
+
['DeviceFirmwareUpdateStatus', 'Success'],
|
|
2934
|
+
{
|
|
2935
|
+
fields: {
|
|
2936
|
+
status: true,
|
|
2937
|
+
payload_version: true,
|
|
2938
|
+
path: true,
|
|
2939
|
+
},
|
|
2940
|
+
},
|
|
2941
|
+
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2942
|
+
);
|
|
2943
|
+
this.protocolV2InstallStatusBaseline =
|
|
2944
|
+
baselineStatusResponse.type === 'DeviceFirmwareUpdateStatus'
|
|
2945
|
+
? ((baselineStatusResponse.message.records ??
|
|
2946
|
+
[]) as ProtocolV2FirmwareUpdateStatusTarget[])
|
|
2947
|
+
: [];
|
|
2948
|
+
} catch (error) {
|
|
2949
|
+
Log.log('[FirmwareUpdateV4] unable to capture pre-install firmware status: ', error);
|
|
2950
|
+
}
|
|
2854
2951
|
this.device.setCancelableAction(() => commands.cancelDevice());
|
|
2855
2952
|
const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
|
|
2856
2953
|
this.postMessage(
|
|
@@ -2864,7 +2961,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2864
2961
|
...(interaction ? { interaction } : {}),
|
|
2865
2962
|
})
|
|
2866
2963
|
);
|
|
2867
|
-
|
|
2964
|
+
try {
|
|
2965
|
+
await commands.call('DeviceFirmwareUpdateRequest', {});
|
|
2966
|
+
} catch (error) {
|
|
2967
|
+
this.throwIfAborted();
|
|
2968
|
+
if (!isProtocolV2DeviceDisconnectedError(error)) {
|
|
2969
|
+
throw error;
|
|
2970
|
+
}
|
|
2971
|
+
Log.log(
|
|
2972
|
+
'[FirmwareUpdateV4] BLE transport released after install request; continue status polling'
|
|
2973
|
+
);
|
|
2974
|
+
if (this.isBleReconnect()) {
|
|
2975
|
+
this.protocolV2InstallNeedsBleReconnect = true;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2868
2978
|
}
|
|
2869
2979
|
|
|
2870
2980
|
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
@@ -7,6 +7,7 @@ import { BaseMethod } from '../BaseMethod';
|
|
|
7
7
|
import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
|
|
8
8
|
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
9
9
|
import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
|
|
10
|
+
import { DataManager } from '../../data-manager';
|
|
10
11
|
import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
|
|
11
12
|
import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
|
|
12
13
|
import { LoggerNames, getLogger } from '../../utils';
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
PRO2_WALLPAPER_HEIGHT,
|
|
15
16
|
PRO2_WALLPAPER_WIDTH,
|
|
16
17
|
type Pro2WallpaperColorFormat,
|
|
18
|
+
type Pro2WallpaperEncoding,
|
|
17
19
|
encodePro2Wallpaper,
|
|
18
20
|
} from '../../utils/pro2Wallpaper';
|
|
19
21
|
|
|
@@ -21,6 +23,7 @@ export type DeviceUploadWallpaperParams = {
|
|
|
21
23
|
jpegBase64: string;
|
|
22
24
|
fileName?: string;
|
|
23
25
|
chunkSize?: number;
|
|
26
|
+
encoding?: Pro2WallpaperEncoding;
|
|
24
27
|
};
|
|
25
28
|
|
|
26
29
|
export type DeviceUploadWallpaperResponse = {
|
|
@@ -61,10 +64,16 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
61
64
|
private path = '';
|
|
62
65
|
|
|
63
66
|
init() {
|
|
64
|
-
const { jpegBase64, fileName, chunkSize } = this.payload;
|
|
67
|
+
const { jpegBase64, fileName, chunkSize, encoding } = this.payload;
|
|
65
68
|
if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
|
|
66
69
|
throw invalidParameter('Parameter [chunkSize] must be a positive integer.');
|
|
67
70
|
}
|
|
71
|
+
if (encoding !== undefined && encoding !== 'rgb565' && encoding !== 'i8-lz4') {
|
|
72
|
+
throw invalidParameter('Parameter [encoding] must be either rgb565 or i8-lz4.');
|
|
73
|
+
}
|
|
74
|
+
const env = DataManager.getSettings('env');
|
|
75
|
+
const resolvedEncoding: Pro2WallpaperEncoding =
|
|
76
|
+
encoding ?? (env && DataManager.isBleConnect(env) ? 'i8-lz4' : 'rgb565');
|
|
68
77
|
|
|
69
78
|
const decoded = decodeJpegBase64ToRgba({
|
|
70
79
|
jpegBase64,
|
|
@@ -76,9 +85,10 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
76
85
|
width: PRO2_WALLPAPER_WIDTH,
|
|
77
86
|
height: PRO2_WALLPAPER_HEIGHT,
|
|
78
87
|
rgba: decoded.data,
|
|
88
|
+
encoding: resolvedEncoding,
|
|
79
89
|
});
|
|
80
90
|
this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
|
|
81
|
-
this.params = { jpegBase64, fileName, chunkSize };
|
|
91
|
+
this.params = { jpegBase64, fileName, chunkSize, encoding: resolvedEncoding };
|
|
82
92
|
this.unlockPolicy = 'unlock-before-run';
|
|
83
93
|
// File writes and wallpaper apply require an unlocked device. Either PIN
|
|
84
94
|
// may authorize this device-management action.
|
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
|
|
|
@@ -961,60 +954,7 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
|
|
|
961
954
|
* If the Bluetooth connection times out, retry up to 6 times
|
|
962
955
|
* @param retryCount - Current retry count (default 0)
|
|
963
956
|
*/
|
|
964
|
-
|
|
965
|
-
// transport that never settles (field case: Electron main lost an IPC reply,
|
|
966
|
-
// "reply was never sent" after 5 minutes) hangs the call forever and cancel()
|
|
967
|
-
// only takes effect at poll checkpoints. Race acquire against a deadline and
|
|
968
|
-
// the caller's abort signal so the hang is bounded and cancel is immediate.
|
|
969
|
-
const BLE_ACQUIRE_DEADLINE_MS = 60 * 1000;
|
|
970
|
-
|
|
971
|
-
function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal): Promise<T> {
|
|
972
|
-
return new Promise<T>((resolve, reject) => {
|
|
973
|
-
let settled = false;
|
|
974
|
-
const settle = (fn: () => void) => {
|
|
975
|
-
if (settled) return;
|
|
976
|
-
settled = true;
|
|
977
|
-
clearTimeout(deadline);
|
|
978
|
-
abortSignal?.removeEventListener('abort', onAbort);
|
|
979
|
-
fn();
|
|
980
|
-
};
|
|
981
|
-
const onAbort = () =>
|
|
982
|
-
settle(() => reject(ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)));
|
|
983
|
-
const deadline = setTimeout(
|
|
984
|
-
() =>
|
|
985
|
-
settle(() =>
|
|
986
|
-
reject(
|
|
987
|
-
ERRORS.TypedError(
|
|
988
|
-
HardwareErrorCode.BleTimeoutError,
|
|
989
|
-
`BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`
|
|
990
|
-
)
|
|
991
|
-
)
|
|
992
|
-
),
|
|
993
|
-
BLE_ACQUIRE_DEADLINE_MS
|
|
994
|
-
);
|
|
995
|
-
// Attach before any early return so a late settlement of acquirePromise
|
|
996
|
-
// is always consumed — an abort or deadline must never leave the acquire
|
|
997
|
-
// rejection unhandled.
|
|
998
|
-
acquirePromise.then(
|
|
999
|
-
value => settle(() => resolve(value)),
|
|
1000
|
-
error => settle(() => reject(error))
|
|
1001
|
-
);
|
|
1002
|
-
if (abortSignal) {
|
|
1003
|
-
if (abortSignal.aborted) {
|
|
1004
|
-
onAbort();
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
abortSignal.addEventListener('abort', onAbort);
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
async function connectDeviceForBle(
|
|
1013
|
-
method: BaseMethod,
|
|
1014
|
-
device: Device,
|
|
1015
|
-
abortSignal?: AbortSignal,
|
|
1016
|
-
retryCount = 0
|
|
1017
|
-
) {
|
|
957
|
+
async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
|
|
1018
958
|
try {
|
|
1019
959
|
if (device.wasInterruptedByUser()) {
|
|
1020
960
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
@@ -1028,43 +968,9 @@ async function connectDeviceForBle(
|
|
|
1028
968
|
!device.commands ||
|
|
1029
969
|
device.commands.disposed;
|
|
1030
970
|
if (shouldAcquire) {
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
// legitimately block on a user-driven system bonding prompt for longer
|
|
1035
|
-
// than any sane deadline. Other envs keep the plain acquire unchanged.
|
|
1036
|
-
const useAcquireGuards = DataManager.getSettings('env') === 'desktop-web-ble';
|
|
1037
|
-
// A cancel landing during the retry backoff must not start a new acquire.
|
|
1038
|
-
if (useAcquireGuards && abortSignal?.aborted) {
|
|
1039
|
-
throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
|
|
1040
|
-
}
|
|
1041
|
-
if (!useAcquireGuards) {
|
|
1042
|
-
await device.acquire(method.payload.connectProtocol, {
|
|
1043
|
-
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1044
|
-
});
|
|
1045
|
-
} else {
|
|
1046
|
-
try {
|
|
1047
|
-
await raceBleAcquire(
|
|
1048
|
-
device.acquire(method.payload.connectProtocol, {
|
|
1049
|
-
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1050
|
-
}),
|
|
1051
|
-
abortSignal
|
|
1052
|
-
);
|
|
1053
|
-
} catch (err) {
|
|
1054
|
-
// A deadline hit means the transport is wedged mid-acquire; drop the
|
|
1055
|
-
// link before the retry so it cold-connects instead of stacking a
|
|
1056
|
-
// second connect onto the half-open one.
|
|
1057
|
-
if (
|
|
1058
|
-
err.errorCode === HardwareErrorCode.BleTimeoutError &&
|
|
1059
|
-
device.mainId &&
|
|
1060
|
-
device.deviceConnector
|
|
1061
|
-
) {
|
|
1062
|
-
await device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
|
|
1063
|
-
device.markTransportDisconnected();
|
|
1064
|
-
}
|
|
1065
|
-
throw err;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
971
|
+
await device.acquire(method.payload.connectProtocol, {
|
|
972
|
+
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
973
|
+
});
|
|
1068
974
|
}
|
|
1069
975
|
if (method.payload?.onlyConnectBleDevice) {
|
|
1070
976
|
if (shouldAcquire) {
|
|
@@ -1104,7 +1010,7 @@ async function connectDeviceForBle(
|
|
|
1104
1010
|
const nextRetry = retryCount + 1;
|
|
1105
1011
|
Log.debug(`Bluetooth connection will retry, retry count: ${nextRetry}`);
|
|
1106
1012
|
await wait(3000);
|
|
1107
|
-
await connectDeviceForBle(method, device,
|
|
1013
|
+
await connectDeviceForBle(method, device, nextRetry);
|
|
1108
1014
|
} else {
|
|
1109
1015
|
throw err;
|
|
1110
1016
|
}
|
|
@@ -1214,7 +1120,7 @@ const ensureConnected = async (
|
|
|
1214
1120
|
if (tryCount === 1) {
|
|
1215
1121
|
device.beginConnectionAttempt();
|
|
1216
1122
|
}
|
|
1217
|
-
await connectDeviceForBle(method, device
|
|
1123
|
+
await connectDeviceForBle(method, device);
|
|
1218
1124
|
}
|
|
1219
1125
|
resolve(device);
|
|
1220
1126
|
return;
|
|
@@ -96,7 +96,8 @@ export default class DeviceConnector {
|
|
|
96
96
|
forceCleanRunPromise?: boolean,
|
|
97
97
|
expectedProtocol?: HardwareConnectProtocol,
|
|
98
98
|
protocolHint?: HardwareConnectProtocol,
|
|
99
|
-
forceProtocolDetection?: boolean
|
|
99
|
+
forceProtocolDetection?: boolean,
|
|
100
|
+
skipProtocolProbe?: boolean
|
|
100
101
|
) {
|
|
101
102
|
Log.debug('acquire', path, session, expectedProtocol, protocolHint);
|
|
102
103
|
const env = DataManager.getSettings('env');
|
|
@@ -104,13 +105,17 @@ export default class DeviceConnector {
|
|
|
104
105
|
const transport = this.getActiveTransport();
|
|
105
106
|
let res;
|
|
106
107
|
if (DataManager.isBleConnect(env)) {
|
|
107
|
-
|
|
108
|
+
const acquireInput: Parameters<Transport['acquire']>[0] & {
|
|
109
|
+
skipProtocolProbe?: boolean;
|
|
110
|
+
} = {
|
|
108
111
|
uuid: path,
|
|
109
112
|
forceCleanRunPromise,
|
|
110
113
|
expectedProtocol,
|
|
111
114
|
protocolHint,
|
|
112
115
|
forceProtocolDetection,
|
|
113
|
-
|
|
116
|
+
skipProtocolProbe,
|
|
117
|
+
};
|
|
118
|
+
res = await transport.acquire(acquireInput);
|
|
114
119
|
} else {
|
|
115
120
|
res = await transport.acquire({
|
|
116
121
|
path,
|