@onekeyfe/hd-core 1.2.0-alpha.170 → 1.2.0-alpha.172

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.
@@ -14,12 +14,12 @@ describe('FirmwareUpdateV4 install polling', () => {
14
14
  jest.restoreAllMocks();
15
15
  });
16
16
 
17
- test('uses Stage, waits for the empty Request response, then polls status', async () => {
17
+ test('keeps USB on Stage, Request Success, then status polling', async () => {
18
18
  const method = new FirmwareUpdateV4({
19
19
  id: 1,
20
20
  payload: {
21
21
  method: 'firmwareUpdateV4',
22
- connectId: 'pro2-ble',
22
+ connectId: 'pro2-usb',
23
23
  },
24
24
  });
25
25
  const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
@@ -43,7 +43,7 @@ describe('FirmwareUpdateV4 install polling', () => {
43
43
  method.device = {
44
44
  getCommands: () => ({ typedCall }),
45
45
  createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
46
- toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2-ble' }),
46
+ toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2-usb' }),
47
47
  setCancelableAction: jest.fn(),
48
48
  clearCancelableAction: jest.fn(),
49
49
  } as unknown as Device;
@@ -59,6 +59,7 @@ describe('FirmwareUpdateV4 install polling', () => {
59
59
  };
60
60
  firmwareUpdate.reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
61
61
  firmwareUpdate.verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue({});
62
+ (method as any).isBleReconnect = jest.fn(() => false);
62
63
 
63
64
  await firmwareUpdate.protocolV2StartFirmwareUpdate({ targets });
64
65
  await firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets);
@@ -72,6 +73,88 @@ describe('FirmwareUpdateV4 install polling', () => {
72
73
  );
73
74
  });
74
75
 
76
+ test('writes the BLE Request and completes only from target status polling', async () => {
77
+ const method = new FirmwareUpdateV4({
78
+ id: 1,
79
+ payload: {
80
+ method: 'firmwareUpdateV4',
81
+ connectId: 'pro2-ble',
82
+ },
83
+ });
84
+ const targets = [{ target_id: 6, path: 'vol0:/coprocessor.bin' }];
85
+ const typedCall = jest
86
+ .fn()
87
+ .mockResolvedValueOnce({ type: 'Success', message: {} })
88
+ .mockResolvedValueOnce({
89
+ type: 'DeviceFirmwareUpdateStatus',
90
+ message: {
91
+ records: [
92
+ {
93
+ target_id: 6,
94
+ status: 'FW_MGMT_UPDATER_TASK_STATUS_IN_PROGRESS',
95
+ path: 'vol0:/coprocessor.bin',
96
+ },
97
+ ],
98
+ },
99
+ })
100
+ .mockResolvedValueOnce({
101
+ type: 'DeviceFirmwareUpdateStatus',
102
+ message: {
103
+ records: [
104
+ {
105
+ target_id: 6,
106
+ status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED',
107
+ path: 'vol0:/coprocessor.bin',
108
+ },
109
+ ],
110
+ },
111
+ });
112
+ const call = jest.fn().mockResolvedValue({
113
+ type: 'WriteCompleted',
114
+ message: {},
115
+ });
116
+
117
+ method.device = {
118
+ getCommands: () => ({ typedCall, call }),
119
+ createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
120
+ toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2-ble' }),
121
+ setCancelableAction: jest.fn(),
122
+ clearCancelableAction: jest.fn(),
123
+ } as unknown as Device;
124
+ method.postMessage = jest.fn();
125
+ method.postProgressMessage = jest.fn();
126
+ (method as any).isBleReconnect = jest.fn(() => true);
127
+
128
+ const firmwareUpdate = method as unknown as {
129
+ protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<void>;
130
+ waitForProtocolV2FirmwareUpdateComplete: (
131
+ value: typeof targets,
132
+ requireCurrentInstallStatus: boolean
133
+ ) => Promise<void>;
134
+ };
135
+
136
+ await firmwareUpdate.protocolV2StartFirmwareUpdate({ targets });
137
+
138
+ expect(typedCall).toHaveBeenCalledTimes(1);
139
+ expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateStage', 'Success', { targets });
140
+ expect(call).toHaveBeenCalledWith(
141
+ 'DeviceFirmwareUpdateRequest',
142
+ {},
143
+ expect.objectContaining({
144
+ returnAfterWrite: true,
145
+ expectedTypes: ['Success'],
146
+ onResponseAfterWrite: expect.any(Function),
147
+ })
148
+ );
149
+ expect(method.postProgressMessage).not.toHaveBeenCalled();
150
+
151
+ await firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets, true);
152
+
153
+ expect(typedCall.mock.calls[1]?.[0]).toBe('DeviceFirmwareUpdateStatusGet');
154
+ expect(typedCall.mock.calls[1]?.[1]).toBe('DeviceFirmwareUpdateStatus');
155
+ expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
156
+ });
157
+
75
158
  test('does not send Request when Stage is rejected', async () => {
76
159
  const method = new FirmwareUpdateV4({
77
160
  id: 1,
@@ -1,48 +1,5 @@
1
1
  import { encodePro2Wallpaper } from '../src/utils/pro2Wallpaper';
2
2
 
3
- // The test decoder mirrors LZ4's bit-packed token and offset format.
4
- /* eslint-disable no-bitwise */
5
-
6
- function decodeLz4Block(data: Uint8Array, expectedLength: number) {
7
- const output = new Uint8Array(expectedLength);
8
- let inputOffset = 0;
9
- let outputOffset = 0;
10
-
11
- const readLength = (initialLength: number) => {
12
- let length = initialLength;
13
- if (length === 0x0f) {
14
- let extension = 0xff;
15
- while (extension === 0xff) {
16
- extension = data[inputOffset];
17
- inputOffset += 1;
18
- length += extension;
19
- }
20
- }
21
- return length;
22
- };
23
-
24
- while (inputOffset < data.byteLength) {
25
- const token = data[inputOffset];
26
- inputOffset += 1;
27
- const literalLength = readLength(token >> 4);
28
- output.set(data.subarray(inputOffset, inputOffset + literalLength), outputOffset);
29
- inputOffset += literalLength;
30
- outputOffset += literalLength;
31
- if (inputOffset >= data.byteLength) break;
32
-
33
- const matchOffset = data[inputOffset] | (data[inputOffset + 1] << 8);
34
- inputOffset += 2;
35
- const matchLength = readLength(token & 0x0f) + 4;
36
- for (let index = 0; index < matchLength; index += 1) {
37
- output[outputOffset] = output[outputOffset - matchOffset];
38
- outputOffset += 1;
39
- }
40
- }
41
-
42
- expect(outputOffset).toBe(expectedLength);
43
- return output;
44
- }
45
-
46
3
  describe('encodePro2Wallpaper', () => {
47
4
  test('encodes opaque pixels as aligned LVGL v9 RGB565 data', () => {
48
5
  const result = encodePro2Wallpaper({
@@ -71,29 +28,6 @@ describe('encodePro2Wallpaper', () => {
71
28
  expect(Array.from(result.data.slice(12))).toEqual([0x1f, 0x00, 0xff, 0xff, 128, 255]);
72
29
  });
73
30
 
74
- test('encodes an I8 palette and pixel indices as an LVGL LZ4 block', () => {
75
- const result = encodePro2Wallpaper({
76
- width: 2,
77
- height: 1,
78
- rgba: new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255]),
79
- encoding: 'i8-lz4',
80
- });
81
-
82
- expect(result.colorFormat).toBe('I8');
83
- expect(Array.from(result.data.slice(0, 12))).toEqual([
84
- 0x19, 0x0a, 0x08, 0, 2, 0, 1, 0, 2, 0, 0, 0,
85
- ]);
86
- const view = new DataView(result.data.buffer, result.data.byteOffset, result.data.byteLength);
87
- expect(view.getUint32(12, true)).toBe(2);
88
- expect(view.getUint32(16, true)).toBe(result.data.byteLength - 24);
89
- expect(view.getUint32(20, true)).toBe(1026);
90
-
91
- const rawData = decodeLz4Block(result.data.slice(24), 1026);
92
- expect(Array.from(rawData.slice(210 * 4, 210 * 4 + 4))).toEqual([0, 0, 255, 255]);
93
- expect(Array.from(rawData.slice(36 * 4, 36 * 4 + 4))).toEqual([0, 255, 0, 255]);
94
- expect(Array.from(rawData.slice(-2))).toEqual([210, 36]);
95
- });
96
-
97
31
  test('rejects invalid dimensions and RGBA byte length', () => {
98
32
  expect(() => encodePro2Wallpaper({ width: 0, height: 1, rgba: new Uint8Array() })).toThrow(
99
33
  'width'
@@ -194,13 +194,7 @@ describe('DeviceUploadWallpaper', () => {
194
194
  refreshProtocolV2SettingsAfterMutation: jest.fn().mockResolvedValue({}),
195
195
  });
196
196
 
197
- afterEach(() => {
198
- jest.restoreAllMocks();
199
- });
200
-
201
- test('uses I8 with LZ4 when uploading a Pro2 wallpaper over BLE', async () => {
202
- jest.spyOn(DataManager, 'getSettings').mockReturnValue('lowlevel');
203
- jest.spyOn(DataManager, 'isBleConnect').mockReturnValue(true);
197
+ test('encodes, uploads and applies a Pro2 wallpaper', async () => {
204
198
  const typedCall = jest.fn().mockImplementation((request, _response, params) => {
205
199
  if (request === 'FilesystemDirMake') return { message: { message: 'directory ready' } };
206
200
  if (request === 'FilesystemFileWrite') {
@@ -243,7 +237,7 @@ describe('DeviceUploadWallpaper', () => {
243
237
  });
244
238
  expect(device.refreshProtocolV2SettingsAfterMutation).toHaveBeenCalledTimes(1);
245
239
  expect(typedCall.mock.calls.some(call => call[0] === 'SetWallpaper')).toBe(false);
246
- expect(result).toMatchObject({ colorFormat: 'I8', message: 'wallpaper applied' });
240
+ expect(result).toMatchObject({ colorFormat: 'RGB565', message: 'wallpaper applied' });
247
241
  const fileWriteCallCount = typedCall.mock.calls.filter(
248
242
  call => call[0] === 'FilesystemFileWrite'
249
243
  ).length;
@@ -281,8 +275,6 @@ describe('DeviceUploadWallpaper', () => {
281
275
  });
282
276
 
283
277
  test('returns success when wallpaper read-back fails after apply', async () => {
284
- jest.spyOn(DataManager, 'getSettings').mockReturnValue('desktop-webusb');
285
- jest.spyOn(DataManager, 'isBleConnect').mockReturnValue(false);
286
278
  const typedCall = jest.fn().mockImplementation((request, _response, params) => {
287
279
  if (request === 'FilesystemDirMake') return { message: {} };
288
280
  if (request === 'FilesystemFileWrite') {
@@ -308,10 +300,7 @@ describe('DeviceUploadWallpaper', () => {
308
300
  (method as any).device = device;
309
301
  method.init();
310
302
 
311
- await expect(method.run()).resolves.toMatchObject({
312
- colorFormat: 'RGB565',
313
- message: 'wallpaper applied',
314
- });
303
+ await expect(method.run()).resolves.toMatchObject({ message: 'wallpaper applied' });
315
304
  expect(device.refreshProtocolV2SettingsAfterMutation).toHaveBeenCalledTimes(1);
316
305
  });
317
306
 
@@ -328,19 +317,6 @@ describe('DeviceUploadWallpaper', () => {
328
317
  expect(() => method.init()).toThrow('fileName');
329
318
  });
330
319
 
331
- test('rejects unsupported wallpaper encodings before device communication', () => {
332
- const method = new DeviceUploadWallpaper({
333
- id: 1,
334
- payload: {
335
- method: 'deviceUploadWallpaper',
336
- jpegBase64: createJpegBase64(604, 1024),
337
- encoding: 'gzip',
338
- } as any,
339
- });
340
-
341
- expect(() => method.init()).toThrow('encoding');
342
- });
343
-
344
320
  test('rejects unsupported firmware before creating or writing files', async () => {
345
321
  const typedCall = jest.fn();
346
322
  const method = new DeviceUploadWallpaper({
@@ -4978,7 +4954,7 @@ describe('Protocol V2 firmware update targets', () => {
4978
4954
  expect(commands.mainId).toBe('usb-path');
4979
4955
  });
4980
4956
 
4981
- test('scans and reacquires the same BLE peripheral without probing during install recovery', async () => {
4957
+ test('reacquires the same BLE peripheral by id without scanning during install recovery', async () => {
4982
4958
  const method = new FirmwareUpdateV4({
4983
4959
  id: 1,
4984
4960
  payload: {
@@ -5000,7 +4976,7 @@ describe('Protocol V2 firmware update targets', () => {
5000
4976
 
5001
4977
  await (method as any).reconnectProtocolV2Device({ skipProtocolProbe: true });
5002
4978
 
5003
- expect(enumerate).toHaveBeenCalledTimes(1);
4979
+ expect(enumerate).not.toHaveBeenCalled();
5004
4980
  expect(acquire).toHaveBeenCalledWith('ble-id', null, true, 'V2', undefined, undefined, true);
5005
4981
  expect(commands.disposed).toBe(false);
5006
4982
  expect(commands.mainId).toBe('ble-id');
@@ -5551,15 +5527,11 @@ describe('Protocol V2 firmware update targets', () => {
5551
5527
  method: 'firmwareUpdateV4',
5552
5528
  },
5553
5529
  });
5554
- const typedCall = jest.fn().mockImplementation((type: string) => {
5555
- if (type === 'DeviceFirmwareUpdateRequest') {
5556
- return Promise.reject(new Error('React Native BLE transport released'));
5557
- }
5558
- return Promise.resolve({ type: 'Success', message: {} });
5559
- });
5530
+ const typedCall = jest.fn().mockResolvedValue({ type: 'Success', message: {} });
5531
+ const call = jest.fn().mockRejectedValue(new Error('React Native BLE transport released'));
5560
5532
 
5561
5533
  (method as any).device = stubDevice({
5562
- getCommands: () => ({ typedCall }),
5534
+ getCommands: () => ({ typedCall, call }),
5563
5535
  createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
5564
5536
  toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
5565
5537
  });
@@ -5572,7 +5544,16 @@ describe('Protocol V2 firmware update targets', () => {
5572
5544
  })
5573
5545
  ).resolves.toBeUndefined();
5574
5546
 
5575
- expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
5547
+ expect(call).toHaveBeenCalledWith(
5548
+ 'DeviceFirmwareUpdateRequest',
5549
+ {},
5550
+ expect.objectContaining({
5551
+ returnAfterWrite: true,
5552
+ expectedTypes: ['Success'],
5553
+ onResponseAfterWrite: expect.any(Function),
5554
+ })
5555
+ );
5556
+ expect(typedCall).not.toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
5576
5557
  expect((method as any).protocolV2InstallNeedsBleReconnect).toBe(true);
5577
5558
  });
5578
5559
 
@@ -5678,7 +5659,7 @@ describe('Protocol V2 firmware update targets', () => {
5678
5659
  expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
5679
5660
  });
5680
5661
 
5681
- test('keeps polling after install ACK until the target finishes', async () => {
5662
+ test('keeps polling after an empty status response until the target finishes', async () => {
5682
5663
  const method = new FirmwareUpdateV4({
5683
5664
  id: 1,
5684
5665
  payload: {
@@ -5687,7 +5668,10 @@ describe('Protocol V2 firmware update targets', () => {
5687
5668
  });
5688
5669
  const typedCall = jest
5689
5670
  .fn()
5690
- .mockResolvedValueOnce({ type: 'Success', message: {} })
5671
+ .mockResolvedValueOnce({
5672
+ type: 'DeviceFirmwareUpdateStatus',
5673
+ message: { records: [] },
5674
+ })
5691
5675
  .mockResolvedValueOnce({
5692
5676
  type: 'DeviceFirmwareUpdateStatus',
5693
5677
  message: { records: [] },
@@ -5829,6 +5813,38 @@ describe('Protocol V2 firmware update targets', () => {
5829
5813
  expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
5830
5814
  });
5831
5815
 
5816
+ test('does not count a stale finished target when only another target was live', () => {
5817
+ const method = new FirmwareUpdateV4({
5818
+ id: 1,
5819
+ payload: {
5820
+ method: 'firmwareUpdateV4',
5821
+ },
5822
+ });
5823
+ const assertProtocolV2TargetStatus = (method as any).assertProtocolV2TargetStatus.bind(method);
5824
+ const expectedTargetIds = new Set([3, 4]);
5825
+ const expectedPaths = new Map([
5826
+ [3, 'vol0:/bootloader.bin'],
5827
+ [4, 'vol0:/application_p1.bin'],
5828
+ ]);
5829
+ const liveTargetIds = new Set([4]);
5830
+ method.postProgressMessage = jest.fn();
5831
+
5832
+ const completed = assertProtocolV2TargetStatus(
5833
+ [
5834
+ { target_id: 3, status: 2, path: 'vol0:/bootloader.bin' },
5835
+ { target_id: 4, status: 2, path: 'vol0:/application_p1.bin' },
5836
+ ],
5837
+ expectedTargetIds,
5838
+ expectedPaths,
5839
+ liveTargetIds
5840
+ );
5841
+
5842
+ expect(completed).toBe(false);
5843
+ expect((method as any).protocolV2CompletedTargetIds).toEqual(new Set([4]));
5844
+ expect(method.postProgressMessage).toHaveBeenCalledWith(50, 'installingFirmware');
5845
+ expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
5846
+ });
5847
+
5832
5848
  test('ignores stale failed records until the current install starts', async () => {
5833
5849
  const method = new FirmwareUpdateV4({
5834
5850
  id: 1,
@@ -6118,7 +6134,6 @@ describe('Protocol V2 firmware update targets', () => {
6118
6134
  });
6119
6135
  const typedCall = jest
6120
6136
  .fn()
6121
- .mockResolvedValueOnce({ type: 'Success', message: {} })
6122
6137
  .mockRejectedValueOnce(new Error('Pro2 is rebooting'))
6123
6138
  .mockResolvedValueOnce({
6124
6139
  type: 'DeviceFirmwareUpdateStatus',
@@ -6135,6 +6150,7 @@ describe('Protocol V2 firmware update targets', () => {
6135
6150
  (method as any).device = stubDevice({
6136
6151
  getCommands: () => ({ typedCall }),
6137
6152
  });
6153
+ (method as any).protocolV2InstallRequestConfirmed = true;
6138
6154
  (method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
6139
6155
  (method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
6140
6156
  (method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
@@ -6565,7 +6581,7 @@ describe('Protocol V2 firmware update targets', () => {
6565
6581
  expect(reconnectProtocolV2Device).not.toHaveBeenCalled();
6566
6582
  expect(typedCall).toHaveBeenCalledWith(
6567
6583
  'DeviceFirmwareUpdateStatusGet',
6568
- ['DeviceFirmwareUpdateStatus', 'Success'],
6584
+ 'DeviceFirmwareUpdateStatus',
6569
6585
  expect.anything(),
6570
6586
  expect.anything()
6571
6587
  );
@@ -6847,7 +6863,7 @@ describe('Protocol V2 firmware update targets', () => {
6847
6863
  });
6848
6864
  expect(typedCall).toHaveBeenCalledWith(
6849
6865
  'DeviceFirmwareUpdateStatusGet',
6850
- ['DeviceFirmwareUpdateStatus', 'Success'],
6866
+ 'DeviceFirmwareUpdateStatus',
6851
6867
  {
6852
6868
  fields: {
6853
6869
  status: true,
@@ -1 +1 @@
1
- {"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,9 @@
1
1
  import { BaseMethod } from '../BaseMethod';
2
- import { type Pro2WallpaperColorFormat, type Pro2WallpaperEncoding } from '../../utils/pro2Wallpaper';
2
+ import { type Pro2WallpaperColorFormat } from '../../utils/pro2Wallpaper';
3
3
  export type DeviceUploadWallpaperParams = {
4
4
  jpegBase64: string;
5
5
  fileName?: string;
6
6
  chunkSize?: number;
7
- encoding?: Pro2WallpaperEncoding;
8
7
  };
9
8
  export type DeviceUploadWallpaperResponse = {
10
9
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceUploadWallpaper.d.ts","sourceRoot":"","sources":["../../../src/api/protocol-v2/DeviceUploadWallpaper.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQ3C,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAE3B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,2BAA2B,GAAG;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,wBAAwB,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAmBF,MAAM,CAAC,OAAO,OAAO,qBAAsB,SAAQ,UAAU,CAAC,2BAA2B,CAAC;IACxF,qBAAqB;IAIrB,OAAO,CAAC,OAAO,CAAC,CAA8D;IAE9E,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,IAAI,CAAM;IAElB,IAAI;YAkCU,kBAAkB;YAgBlB,eAAe;YAaf,MAAM;IAwBd,GAAG,IAAI,OAAO,CAAC,6BAA6B,CAAC;CAyBpD"}
1
+ {"version":3,"file":"DeviceUploadWallpaper.d.ts","sourceRoot":"","sources":["../../../src/api/protocol-v2/DeviceUploadWallpaper.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAO3C,OAAO,EAGL,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,2BAA2B,GAAG;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,wBAAwB,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAmBF,MAAM,CAAC,OAAO,OAAO,qBAAsB,SAAQ,UAAU,CAAC,2BAA2B,CAAC;IACxF,qBAAqB;IAIrB,OAAO,CAAC,OAAO,CAAC,CAA8D;IAE9E,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,IAAI,CAAM;IAElB,IAAI;YA2BU,kBAAkB;YAgBlB,eAAe;YAaf,MAAM;IAwBd,GAAG,IAAI,OAAO,CAAC,6BAA6B,CAAC;CAyBpD"}
package/dist/index.d.ts CHANGED
@@ -1113,13 +1113,11 @@ declare const switchTransport: ({ env, Transport, plugin, }: {
1113
1113
 
1114
1114
  declare const PRO2_WALLPAPER_WIDTH = 604;
1115
1115
  declare const PRO2_WALLPAPER_HEIGHT = 1024;
1116
- type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8' | 'I8';
1117
- type Pro2WallpaperEncoding = 'rgb565' | 'i8-lz4';
1116
+ type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
1118
1117
  declare function encodePro2Wallpaper(options: {
1119
1118
  width: number;
1120
1119
  height: number;
1121
1120
  rgba: Uint8Array | ArrayBuffer;
1122
- encoding?: Pro2WallpaperEncoding;
1123
1121
  }): {
1124
1122
  data: Uint8Array;
1125
1123
  colorFormat: Pro2WallpaperColorFormat;
@@ -1129,7 +1127,6 @@ type DeviceUploadWallpaperParams = {
1129
1127
  jpegBase64: string;
1130
1128
  fileName?: string;
1131
1129
  chunkSize?: number;
1132
- encoding?: Pro2WallpaperEncoding;
1133
1130
  };
1134
1131
  type DeviceUploadWallpaperResponse = {
1135
1132
  path: string;
package/dist/index.js CHANGED
@@ -42476,18 +42476,6 @@ const PRO2_WALLPAPER_WIDTH = 604;
42476
42476
  const PRO2_WALLPAPER_HEIGHT = 1024;
42477
42477
  const COLOR_FORMAT_RGB565 = 0x12;
42478
42478
  const COLOR_FORMAT_RGB565A8 = 0x14;
42479
- const COLOR_FORMAT_I8 = 0x0a;
42480
- const IMAGE_FLAG_COMPRESSED = 0x0008;
42481
- const IMAGE_COMPRESSION_LZ4 = 0x00000002;
42482
- const I8_PALETTE_SIZE = 256 * 4;
42483
- const I8_RED_LEVELS = 6;
42484
- const I8_GREEN_LEVELS = 7;
42485
- const I8_BLUE_LEVELS = 6;
42486
- const LZ4_MIN_MATCH = 4;
42487
- const LZ4_LAST_LITERALS = 5;
42488
- const LZ4_MATCH_FIND_LIMIT = 12;
42489
- const LZ4_HASH_BITS = 16;
42490
- const LZ4_HASH_MULTIPLIER = -1640531535;
42491
42479
  const RED_THRESHOLD = [
42492
42480
  1, 7, 3, 5, 0, 8, 2, 6, 7, 1, 5, 3, 8, 0, 6, 2, 3, 5, 0, 8, 2, 6, 1, 7, 5, 3, 8, 0, 6, 2, 7, 1, 0,
42493
42481
  8, 2, 6, 1, 7, 3, 5, 8, 0, 6, 2, 7, 1, 5, 3, 2, 6, 1, 7, 3, 5, 0, 8, 6, 2, 7, 1, 5, 3, 8, 0,
@@ -42509,149 +42497,6 @@ function asBytes(rgba) {
42509
42497
  function align(value, boundary) {
42510
42498
  return Math.ceil(value / boundary) * boundary;
42511
42499
  }
42512
- function writeLz4Length(output, offset, length) {
42513
- let cursor = offset;
42514
- let remaining = length;
42515
- while (remaining >= 0xff) {
42516
- output[cursor] = 0xff;
42517
- cursor += 1;
42518
- remaining -= 0xff;
42519
- }
42520
- output[cursor] = remaining;
42521
- return cursor + 1;
42522
- }
42523
- function readUint32LittleEndian(data, offset) {
42524
- return (data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24));
42525
- }
42526
- function getLz4Hash(sequence) {
42527
- return (Math.imul(sequence, LZ4_HASH_MULTIPLIER) >>> (32 - LZ4_HASH_BITS)) & 0xffff;
42528
- }
42529
- function compressLz4Block(input) {
42530
- const output = new Uint8Array(input.byteLength + Math.floor(input.byteLength / 0xff) + 16);
42531
- const hashTable = new Int32Array(1 << LZ4_HASH_BITS);
42532
- hashTable.fill(-1);
42533
- let anchor = 0;
42534
- let inputOffset = 0;
42535
- let outputOffset = 0;
42536
- const matchFindEnd = input.byteLength - LZ4_MATCH_FIND_LIMIT;
42537
- const matchCopyEnd = input.byteLength - LZ4_LAST_LITERALS;
42538
- while (inputOffset <= matchFindEnd) {
42539
- const sequence = readUint32LittleEndian(input, inputOffset);
42540
- const hash = getLz4Hash(sequence);
42541
- const reference = hashTable[hash];
42542
- hashTable[hash] = inputOffset;
42543
- const matchOffset = inputOffset - reference;
42544
- const hasMatch = reference >= 0 &&
42545
- matchOffset <= 0xffff &&
42546
- readUint32LittleEndian(input, reference) === sequence;
42547
- if (hasMatch) {
42548
- let matchLength = LZ4_MIN_MATCH;
42549
- while (inputOffset + matchLength < matchCopyEnd &&
42550
- input[reference + matchLength] === input[inputOffset + matchLength]) {
42551
- matchLength += 1;
42552
- }
42553
- const literalLength = inputOffset - anchor;
42554
- const encodedMatchLength = matchLength - LZ4_MIN_MATCH;
42555
- const tokenOffset = outputOffset;
42556
- outputOffset += 1;
42557
- output[tokenOffset] =
42558
- (Math.min(literalLength, 0x0f) << 4) | Math.min(encodedMatchLength, 0x0f);
42559
- if (literalLength >= 0x0f) {
42560
- outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
42561
- }
42562
- output.set(input.subarray(anchor, inputOffset), outputOffset);
42563
- outputOffset += literalLength;
42564
- output[outputOffset] = matchOffset & 0xff;
42565
- output[outputOffset + 1] = matchOffset >> 8;
42566
- outputOffset += 2;
42567
- if (encodedMatchLength >= 0x0f) {
42568
- outputOffset = writeLz4Length(output, outputOffset, encodedMatchLength - 0x0f);
42569
- }
42570
- const matchStart = inputOffset;
42571
- inputOffset += matchLength;
42572
- anchor = inputOffset;
42573
- for (let cursor = Math.max(matchStart + 1, inputOffset - 2); cursor < inputOffset; cursor += 1) {
42574
- if (cursor <= matchFindEnd) {
42575
- hashTable[getLz4Hash(readUint32LittleEndian(input, cursor))] = cursor;
42576
- }
42577
- }
42578
- }
42579
- else {
42580
- inputOffset += 1;
42581
- }
42582
- }
42583
- const literalLength = input.byteLength - anchor;
42584
- const tokenOffset = outputOffset;
42585
- outputOffset += 1;
42586
- output[tokenOffset] = Math.min(literalLength, 0x0f) << 4;
42587
- if (literalLength >= 0x0f) {
42588
- outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
42589
- }
42590
- output.set(input.subarray(anchor), outputOffset);
42591
- outputOffset += literalLength;
42592
- return output.slice(0, outputOffset);
42593
- }
42594
- function quantizeChannel(value, levels) {
42595
- return Math.floor((value * (levels - 1) + 0x7f) / 0xff);
42596
- }
42597
- function expandChannel(value, levels) {
42598
- return Math.floor((value * 0xff + Math.floor((levels - 1) / 2)) / (levels - 1));
42599
- }
42600
- function encodePro2I8Lz4(options) {
42601
- const { width, height } = options;
42602
- if (!Number.isInteger(width) || width <= 0 || width > 0xffff) {
42603
- throw invalidParameter$2('Wallpaper width must be an integer between 1 and 65535.');
42604
- }
42605
- if (!Number.isInteger(height) || height <= 0 || height > 0xffff) {
42606
- throw invalidParameter$2('Wallpaper height must be an integer between 1 and 65535.');
42607
- }
42608
- const rgba = asBytes(options.rgba);
42609
- const expectedLength = width * height * 4;
42610
- if (rgba.byteLength !== expectedLength) {
42611
- throw invalidParameter$2(`Wallpaper RGBA data length must be ${expectedLength} bytes, received ${rgba.byteLength}.`);
42612
- }
42613
- const stride = width;
42614
- const rawData = new Uint8Array(I8_PALETTE_SIZE + stride * height);
42615
- for (let red = 0; red < I8_RED_LEVELS; red += 1) {
42616
- for (let green = 0; green < I8_GREEN_LEVELS; green += 1) {
42617
- for (let blue = 0; blue < I8_BLUE_LEVELS; blue += 1) {
42618
- const paletteIndex = (red * I8_GREEN_LEVELS + green) * I8_BLUE_LEVELS + blue;
42619
- const paletteOffset = paletteIndex * 4;
42620
- rawData[paletteOffset] = expandChannel(blue, I8_BLUE_LEVELS);
42621
- rawData[paletteOffset + 1] = expandChannel(green, I8_GREEN_LEVELS);
42622
- rawData[paletteOffset + 2] = expandChannel(red, I8_RED_LEVELS);
42623
- rawData[paletteOffset + 3] = 0xff;
42624
- }
42625
- }
42626
- }
42627
- for (let pixel = 0; pixel < width * height; pixel += 1) {
42628
- const sourceOffset = pixel * 4;
42629
- const alpha = rgba[sourceOffset + 3];
42630
- const red = Math.round((rgba[sourceOffset] * alpha) / 0xff);
42631
- const green = Math.round((rgba[sourceOffset + 1] * alpha) / 0xff);
42632
- const blue = Math.round((rgba[sourceOffset + 2] * alpha) / 0xff);
42633
- const paletteIndex = (quantizeChannel(red, I8_RED_LEVELS) * I8_GREEN_LEVELS +
42634
- quantizeChannel(green, I8_GREEN_LEVELS)) *
42635
- I8_BLUE_LEVELS +
42636
- quantizeChannel(blue, I8_BLUE_LEVELS);
42637
- rawData[I8_PALETTE_SIZE + pixel] = paletteIndex;
42638
- }
42639
- const compressed = compressLz4Block(rawData);
42640
- const data = new Uint8Array(24 + compressed.byteLength);
42641
- const view = new DataView(data.buffer);
42642
- data[0] = 0x19;
42643
- data[1] = COLOR_FORMAT_I8;
42644
- view.setUint16(2, IMAGE_FLAG_COMPRESSED, true);
42645
- view.setUint16(4, width, true);
42646
- view.setUint16(6, height, true);
42647
- view.setUint16(8, stride, true);
42648
- view.setUint16(10, 0, true);
42649
- view.setUint32(12, IMAGE_COMPRESSION_LZ4, true);
42650
- view.setUint32(16, compressed.byteLength, true);
42651
- view.setUint32(20, rawData.byteLength, true);
42652
- data.set(compressed, 24);
42653
- return { data, colorFormat: 'I8' };
42654
- }
42655
42500
  function encodePro2Image(options) {
42656
42501
  var _a;
42657
42502
  const { width, height } = options;
@@ -42719,9 +42564,6 @@ function encodePro2Image(options) {
42719
42564
  return { data, colorFormat };
42720
42565
  }
42721
42566
  function encodePro2Wallpaper(options) {
42722
- if (options.encoding === 'i8-lz4') {
42723
- return encodePro2I8Lz4(options);
42724
- }
42725
42567
  return encodePro2Image(options);
42726
42568
  }
42727
42569
 
@@ -53483,7 +53325,11 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53483
53325
  }
53484
53326
  seenTargetIds.add(targetId);
53485
53327
  }
53486
- const completedTargets = matchingTargets.filter(target => isProtocolV2TargetStatusFinished(target.status));
53328
+ const completedTargets = matchingTargets.filter(target => {
53329
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
53330
+ return (isProtocolV2TargetStatusFinished(target.status) &&
53331
+ (!liveTargetIds || (targetId !== undefined && liveTargetIds.has(targetId))));
53332
+ });
53487
53333
  const completedTargetIds = new Set();
53488
53334
  completedTargets.forEach(target => {
53489
53335
  const targetId = normalizeProtocolV2TargetId(target.target_id);
@@ -53592,20 +53438,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53592
53438
  }
53593
53439
  const currentDeviceInfo = deviceInfo;
53594
53440
  try {
53595
- const statusResponse = yield this.device.getCommands().typedCall('DeviceFirmwareUpdateStatusGet', ['DeviceFirmwareUpdateStatus', 'Success'], {
53441
+ const statusResponse = yield this.device.getCommands().typedCall('DeviceFirmwareUpdateStatusGet', 'DeviceFirmwareUpdateStatus', {
53596
53442
  fields: {
53597
53443
  status: true,
53598
53444
  payload_version: true,
53599
53445
  path: true,
53600
53446
  },
53601
53447
  }, { timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT });
53602
- if (statusResponse.type === 'Success') {
53448
+ if (this.protocolV2InstallRequestConfirmed) {
53603
53449
  installEvidenceObserved = true;
53604
- lastError = new Error('Protocol V2 firmware install acknowledged; waiting for target status');
53605
53450
  }
53606
- const statusTargets = statusResponse.type === 'DeviceFirmwareUpdateStatus'
53607
- ? ((_a = statusResponse.message.records) !== null && _a !== void 0 ? _a : [])
53608
- : [];
53451
+ const statusTargets = ((_a = statusResponse.message.records) !== null && _a !== void 0 ? _a : []);
53609
53452
  this.collectProtocolV2LiveTargetIds(statusTargets, expectedTargetIds, liveTargetIds);
53610
53453
  if (liveTargetIds.size > 0) {
53611
53454
  currentInstallStatusObserved = true;
@@ -53644,9 +53487,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53644
53487
  }
53645
53488
  }
53646
53489
  if (statusTargets.length === 0) {
53647
- if (statusResponse.type !== 'Success') {
53648
- lastError = new Error('Protocol V2 firmware update is waiting for user confirmation or target status');
53649
- }
53490
+ lastError = new Error('Protocol V2 firmware update is waiting for user confirmation or target status');
53650
53491
  }
53651
53492
  else {
53652
53493
  const missingTargetIds = this.getProtocolV2MissingTargetIds(statusTargets, expectedTargetIds);
@@ -53941,7 +53782,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53941
53782
  });
53942
53783
  }
53943
53784
  acquireProtocolV2BleDevice(skipProtocolProbe = false) {
53944
- var _a;
53945
53785
  return __awaiter(this, void 0, void 0, function* () {
53946
53786
  const connector = this.device.deviceConnector;
53947
53787
  const expectedId = this.device.originalDescriptor.id;
@@ -53949,14 +53789,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53949
53789
  yield (connector === null || connector === void 0 ? void 0 : connector.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL));
53950
53790
  return;
53951
53791
  }
53952
- const deviceDiff = yield (connector === null || connector === void 0 ? void 0 : connector.enumerate());
53953
- const reconnectDescriptor = (_a = deviceDiff === null || deviceDiff === void 0 ? void 0 : deviceDiff.descriptors) === null || _a === void 0 ? void 0 : _a.find(descriptor => descriptor.id === expectedId);
53954
- if (!reconnectDescriptor) {
53955
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
53956
- }
53957
- yield (connector === null || connector === void 0 ? void 0 : connector.acquire(reconnectDescriptor.id, null, true, PROTOCOL_V2_CONNECT_PROTOCOL, undefined, undefined, skipProtocolProbe));
53792
+ yield (connector === null || connector === void 0 ? void 0 : connector.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL, undefined, undefined, skipProtocolProbe));
53958
53793
  this.device.commands.disposed = false;
53959
- this.device.getCommands().mainId = reconnectDescriptor.id;
53794
+ this.device.getCommands().mainId = expectedId;
53960
53795
  });
53961
53796
  }
53962
53797
  protocolV2StartFirmwareUpdate({ targets, }) {
@@ -53969,6 +53804,32 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53969
53804
  this.device.setCancelableAction(() => commands.cancelDevice());
53970
53805
  const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
53971
53806
  this.postMessage(createUiMessage(UI_REQUEST.REQUEST_BUTTON, Object.assign({ device: this.device.toMessageObject(), source: 'method-lifecycle', reason: 'firmware-update', completion: 'operation-completed', deviceOnly: true, operation: this.name }, (interaction ? { interaction } : {}))));
53807
+ if (this.isBleReconnect()) {
53808
+ try {
53809
+ yield commands.call('DeviceFirmwareUpdateRequest', {}, {
53810
+ returnAfterWrite: true,
53811
+ expectedTypes: ['Success'],
53812
+ onResponseAfterWrite: response => {
53813
+ if (response.type !== 'Success')
53814
+ return;
53815
+ this.protocolV2InstallRequestConfirmed = true;
53816
+ this.device.clearCancelableAction();
53817
+ this.postProgressMessage(1, 'installingFirmware');
53818
+ Log$7.log('[FirmwareUpdateV4] BLE firmware install confirmed by device response');
53819
+ },
53820
+ });
53821
+ Log$7.log('[FirmwareUpdateV4] BLE firmware install request written; continue with status polling');
53822
+ }
53823
+ catch (error) {
53824
+ this.throwIfAborted();
53825
+ if (!isProtocolV2DeviceDisconnectedError(error)) {
53826
+ throw error;
53827
+ }
53828
+ Log$7.log('[FirmwareUpdateV4] BLE transport released while writing install request; continue status polling');
53829
+ this.protocolV2InstallNeedsBleReconnect = true;
53830
+ }
53831
+ return;
53832
+ }
53972
53833
  try {
53973
53834
  yield commands.typedCall('DeviceFirmwareUpdateRequest', 'Success', {});
53974
53835
  this.protocolV2InstallRequestConfirmed = true;
@@ -54472,15 +54333,10 @@ class DeviceUploadWallpaper extends BaseMethod {
54472
54333
  return ['V2'];
54473
54334
  }
54474
54335
  init() {
54475
- const { jpegBase64, fileName, chunkSize, encoding } = this.payload;
54336
+ const { jpegBase64, fileName, chunkSize } = this.payload;
54476
54337
  if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
54477
54338
  throw invalidParameter$1('Parameter [chunkSize] must be a positive integer.');
54478
54339
  }
54479
- if (encoding !== undefined && encoding !== 'rgb565' && encoding !== 'i8-lz4') {
54480
- throw invalidParameter$1('Parameter [encoding] must be either rgb565 or i8-lz4.');
54481
- }
54482
- const env = DataManager.getSettings('env');
54483
- const resolvedEncoding = encoding !== null && encoding !== void 0 ? encoding : (env && DataManager.isBleConnect(env) ? 'i8-lz4' : 'rgb565');
54484
54340
  const decoded = decodeJpegBase64ToRgba({
54485
54341
  jpegBase64,
54486
54342
  parameterName: 'jpegBase64',
@@ -54491,10 +54347,9 @@ class DeviceUploadWallpaper extends BaseMethod {
54491
54347
  width: PRO2_WALLPAPER_WIDTH,
54492
54348
  height: PRO2_WALLPAPER_HEIGHT,
54493
54349
  rgba: decoded.data,
54494
- encoding: resolvedEncoding,
54495
54350
  });
54496
54351
  this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
54497
- this.params = { jpegBase64, fileName, chunkSize, encoding: resolvedEncoding };
54352
+ this.params = { jpegBase64, fileName, chunkSize };
54498
54353
  this.unlockPolicy = 'unlock-before-run';
54499
54354
  this.protocolV2PreUnlockPinType = hdTransport.DeviceSessionPinType.Any;
54500
54355
  this.skipForceUpdateCheck = true;
@@ -1,7 +1,6 @@
1
1
  export declare const PRO2_WALLPAPER_WIDTH = 604;
2
2
  export declare const PRO2_WALLPAPER_HEIGHT = 1024;
3
- export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8' | 'I8';
4
- export type Pro2WallpaperEncoding = 'rgb565' | 'i8-lz4';
3
+ export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
5
4
  export type Pro2ImageAlphaMode = 'preserve' | 'black-background';
6
5
  export declare function encodePro2Image(options: {
7
6
  width: number;
@@ -16,7 +15,6 @@ export declare function encodePro2Wallpaper(options: {
16
15
  width: number;
17
16
  height: number;
18
17
  rgba: Uint8Array | ArrayBuffer;
19
- encoding?: Pro2WallpaperEncoding;
20
18
  }): {
21
19
  data: Uint8Array;
22
20
  colorFormat: Pro2WallpaperColorFormat;
@@ -1 +1 @@
1
- {"version":3,"file":"pro2Wallpaper.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Wallpaper.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAC;AAEpE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,kBAAkB,CAAC;AA4NjE,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;IAC/B,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,wBAAwB,CAAA;CAAE,CA4E9D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;IAC/B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,wBAAwB,CAAA;CAAE,CAK9D"}
1
+ {"version":3,"file":"pro2Wallpaper.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Wallpaper.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,kBAAkB,CAAC;AA8BjE,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;IAC/B,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,wBAAwB,CAAA;CAAE,CA4E9D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;CAChC,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,wBAAwB,CAAA;CAAE,CAE9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.0-alpha.170",
3
+ "version": "1.2.0-alpha.172",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.0-alpha.170",
29
- "@onekeyfe/hd-transport": "1.2.0-alpha.170",
28
+ "@onekeyfe/hd-shared": "1.2.0-alpha.172",
29
+ "@onekeyfe/hd-transport": "1.2.0-alpha.172",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "buffer": "^6.0.3",
@@ -46,5 +46,5 @@
46
46
  "@types/w3c-web-usb": "^1.0.10",
47
47
  "@types/web-bluetooth": "^0.0.21"
48
48
  },
49
- "gitHead": "d702db069d6b19f74cd5f1cf42277a7152beb3a8"
49
+ "gitHead": "4c6c4a4fcc52bcdd0b3ec9c36454b2074fa17a8c"
50
50
  }
@@ -2316,9 +2316,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2316
2316
  }
2317
2317
  seenTargetIds.add(targetId);
2318
2318
  }
2319
- const completedTargets = matchingTargets.filter(target =>
2320
- isProtocolV2TargetStatusFinished(target.status)
2321
- );
2319
+ const completedTargets = matchingTargets.filter(target => {
2320
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
2321
+ return (
2322
+ isProtocolV2TargetStatusFinished(target.status) &&
2323
+ (!liveTargetIds || (targetId !== undefined && liveTargetIds.has(targetId)))
2324
+ );
2325
+ });
2322
2326
  const completedTargetIds = new Set<number>();
2323
2327
  completedTargets.forEach(target => {
2324
2328
  const targetId = normalizeProtocolV2TargetId(target.target_id);
@@ -2465,7 +2469,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2465
2469
  try {
2466
2470
  const statusResponse = await this.device.getCommands().typedCall(
2467
2471
  'DeviceFirmwareUpdateStatusGet',
2468
- ['DeviceFirmwareUpdateStatus', 'Success'],
2472
+ 'DeviceFirmwareUpdateStatus',
2469
2473
  {
2470
2474
  fields: {
2471
2475
  status: true,
@@ -2475,16 +2479,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2475
2479
  },
2476
2480
  { timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
2477
2481
  );
2478
- if (statusResponse.type === 'Success') {
2482
+ if (this.protocolV2InstallRequestConfirmed) {
2479
2483
  installEvidenceObserved = true;
2480
- lastError = new Error(
2481
- 'Protocol V2 firmware install acknowledged; waiting for target status'
2482
- );
2483
2484
  }
2484
- const statusTargets =
2485
- statusResponse.type === 'DeviceFirmwareUpdateStatus'
2486
- ? ((statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[])
2487
- : [];
2485
+ const statusTargets = (statusResponse.message.records ??
2486
+ []) as ProtocolV2FirmwareUpdateStatusTarget[];
2488
2487
  this.collectProtocolV2LiveTargetIds(statusTargets, expectedTargetIds, liveTargetIds);
2489
2488
  if (liveTargetIds.size > 0) {
2490
2489
  currentInstallStatusObserved = true;
@@ -2544,11 +2543,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2544
2543
  }
2545
2544
 
2546
2545
  if (statusTargets.length === 0) {
2547
- if (statusResponse.type !== 'Success') {
2548
- lastError = new Error(
2549
- 'Protocol V2 firmware update is waiting for user confirmation or target status'
2550
- );
2551
- }
2546
+ lastError = new Error(
2547
+ 'Protocol V2 firmware update is waiting for user confirmation or target status'
2548
+ );
2552
2549
  } else {
2553
2550
  const missingTargetIds = this.getProtocolV2MissingTargetIds(
2554
2551
  statusTargets,
@@ -2944,15 +2941,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2944
2941
  await connector?.acquire(expectedId, null, true, PROTOCOL_V2_CONNECT_PROTOCOL);
2945
2942
  return;
2946
2943
  }
2947
- const deviceDiff = await connector?.enumerate();
2948
- const reconnectDescriptor = deviceDiff?.descriptors?.find(
2949
- descriptor => descriptor.id === expectedId
2950
- );
2951
- if (!reconnectDescriptor) {
2952
- throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
2953
- }
2954
2944
  await connector?.acquire(
2955
- reconnectDescriptor.id,
2945
+ expectedId,
2956
2946
  null,
2957
2947
  true,
2958
2948
  PROTOCOL_V2_CONNECT_PROTOCOL,
@@ -2961,7 +2951,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2961
2951
  skipProtocolProbe
2962
2952
  );
2963
2953
  this.device.commands.disposed = false;
2964
- this.device.getCommands().mainId = reconnectDescriptor.id;
2954
+ this.device.getCommands().mainId = expectedId;
2965
2955
  }
2966
2956
 
2967
2957
  private async protocolV2StartFirmwareUpdate({
@@ -2987,6 +2977,40 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2987
2977
  ...(interaction ? { interaction } : {}),
2988
2978
  })
2989
2979
  );
2980
+
2981
+ if (this.isBleReconnect()) {
2982
+ try {
2983
+ await commands.call(
2984
+ 'DeviceFirmwareUpdateRequest',
2985
+ {},
2986
+ {
2987
+ returnAfterWrite: true,
2988
+ expectedTypes: ['Success'],
2989
+ onResponseAfterWrite: response => {
2990
+ if (response.type !== 'Success') return;
2991
+ this.protocolV2InstallRequestConfirmed = true;
2992
+ this.device.clearCancelableAction();
2993
+ this.postProgressMessage(1, 'installingFirmware');
2994
+ Log.log('[FirmwareUpdateV4] BLE firmware install confirmed by device response');
2995
+ },
2996
+ }
2997
+ );
2998
+ Log.log(
2999
+ '[FirmwareUpdateV4] BLE firmware install request written; continue with status polling'
3000
+ );
3001
+ } catch (error) {
3002
+ this.throwIfAborted();
3003
+ if (!isProtocolV2DeviceDisconnectedError(error)) {
3004
+ throw error;
3005
+ }
3006
+ Log.log(
3007
+ '[FirmwareUpdateV4] BLE transport released while writing install request; continue status polling'
3008
+ );
3009
+ this.protocolV2InstallNeedsBleReconnect = true;
3010
+ }
3011
+ return;
3012
+ }
3013
+
2990
3014
  try {
2991
3015
  await commands.typedCall('DeviceFirmwareUpdateRequest', 'Success', {});
2992
3016
  this.protocolV2InstallRequestConfirmed = true;
@@ -7,7 +7,6 @@ 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';
11
10
  import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
12
11
  import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
13
12
  import { LoggerNames, getLogger } from '../../utils';
@@ -15,7 +14,6 @@ import {
15
14
  PRO2_WALLPAPER_HEIGHT,
16
15
  PRO2_WALLPAPER_WIDTH,
17
16
  type Pro2WallpaperColorFormat,
18
- type Pro2WallpaperEncoding,
19
17
  encodePro2Wallpaper,
20
18
  } from '../../utils/pro2Wallpaper';
21
19
 
@@ -23,7 +21,6 @@ export type DeviceUploadWallpaperParams = {
23
21
  jpegBase64: string;
24
22
  fileName?: string;
25
23
  chunkSize?: number;
26
- encoding?: Pro2WallpaperEncoding;
27
24
  };
28
25
 
29
26
  export type DeviceUploadWallpaperResponse = {
@@ -64,16 +61,10 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
64
61
  private path = '';
65
62
 
66
63
  init() {
67
- const { jpegBase64, fileName, chunkSize, encoding } = this.payload;
64
+ const { jpegBase64, fileName, chunkSize } = this.payload;
68
65
  if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
69
66
  throw invalidParameter('Parameter [chunkSize] must be a positive integer.');
70
67
  }
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');
77
68
 
78
69
  const decoded = decodeJpegBase64ToRgba({
79
70
  jpegBase64,
@@ -85,10 +76,9 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
85
76
  width: PRO2_WALLPAPER_WIDTH,
86
77
  height: PRO2_WALLPAPER_HEIGHT,
87
78
  rgba: decoded.data,
88
- encoding: resolvedEncoding,
89
79
  });
90
80
  this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
91
- this.params = { jpegBase64, fileName, chunkSize, encoding: resolvedEncoding };
81
+ this.params = { jpegBase64, fileName, chunkSize };
92
82
  this.unlockPolicy = 'unlock-before-run';
93
83
  // File writes and wallpaper apply require an unlocked device. Either PIN
94
84
  // may authorize this device-management action.
@@ -6,26 +6,12 @@ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
6
6
  export const PRO2_WALLPAPER_WIDTH = 604;
7
7
  export const PRO2_WALLPAPER_HEIGHT = 1024;
8
8
 
9
- export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8' | 'I8';
10
-
11
- export type Pro2WallpaperEncoding = 'rgb565' | 'i8-lz4';
9
+ export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
12
10
 
13
11
  export type Pro2ImageAlphaMode = 'preserve' | 'black-background';
14
12
 
15
13
  const COLOR_FORMAT_RGB565 = 0x12;
16
14
  const COLOR_FORMAT_RGB565A8 = 0x14;
17
- const COLOR_FORMAT_I8 = 0x0a;
18
- const IMAGE_FLAG_COMPRESSED = 0x0008;
19
- const IMAGE_COMPRESSION_LZ4 = 0x00000002;
20
- const I8_PALETTE_SIZE = 256 * 4;
21
- const I8_RED_LEVELS = 6;
22
- const I8_GREEN_LEVELS = 7;
23
- const I8_BLUE_LEVELS = 6;
24
- const LZ4_MIN_MATCH = 4;
25
- const LZ4_LAST_LITERALS = 5;
26
- const LZ4_MATCH_FIND_LIMIT = 12;
27
- const LZ4_HASH_BITS = 16;
28
- const LZ4_HASH_MULTIPLIER = -1640531535;
29
15
 
30
16
  const RED_THRESHOLD = [
31
17
  1, 7, 3, 5, 0, 8, 2, 6, 7, 1, 5, 3, 8, 0, 6, 2, 3, 5, 0, 8, 2, 6, 1, 7, 5, 3, 8, 0, 6, 2, 7, 1, 0,
@@ -52,184 +38,6 @@ function align(value: number, boundary: number): number {
52
38
  return Math.ceil(value / boundary) * boundary;
53
39
  }
54
40
 
55
- function writeLz4Length(output: Uint8Array, offset: number, length: number) {
56
- let cursor = offset;
57
- let remaining = length;
58
- while (remaining >= 0xff) {
59
- output[cursor] = 0xff;
60
- cursor += 1;
61
- remaining -= 0xff;
62
- }
63
- output[cursor] = remaining;
64
- return cursor + 1;
65
- }
66
-
67
- function readUint32LittleEndian(data: Uint8Array, offset: number) {
68
- return (
69
- data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)
70
- );
71
- }
72
-
73
- function getLz4Hash(sequence: number) {
74
- return (Math.imul(sequence, LZ4_HASH_MULTIPLIER) >>> (32 - LZ4_HASH_BITS)) & 0xffff;
75
- }
76
-
77
- function compressLz4Block(input: Uint8Array) {
78
- const output = new Uint8Array(input.byteLength + Math.floor(input.byteLength / 0xff) + 16);
79
- const hashTable = new Int32Array(1 << LZ4_HASH_BITS);
80
- hashTable.fill(-1);
81
-
82
- let anchor = 0;
83
- let inputOffset = 0;
84
- let outputOffset = 0;
85
- const matchFindEnd = input.byteLength - LZ4_MATCH_FIND_LIMIT;
86
- const matchCopyEnd = input.byteLength - LZ4_LAST_LITERALS;
87
-
88
- while (inputOffset <= matchFindEnd) {
89
- const sequence = readUint32LittleEndian(input, inputOffset);
90
- const hash = getLz4Hash(sequence);
91
- const reference = hashTable[hash];
92
- hashTable[hash] = inputOffset;
93
-
94
- const matchOffset = inputOffset - reference;
95
- const hasMatch =
96
- reference >= 0 &&
97
- matchOffset <= 0xffff &&
98
- readUint32LittleEndian(input, reference) === sequence;
99
- if (hasMatch) {
100
- let matchLength = LZ4_MIN_MATCH;
101
- while (
102
- inputOffset + matchLength < matchCopyEnd &&
103
- input[reference + matchLength] === input[inputOffset + matchLength]
104
- ) {
105
- matchLength += 1;
106
- }
107
-
108
- const literalLength = inputOffset - anchor;
109
- const encodedMatchLength = matchLength - LZ4_MIN_MATCH;
110
- const tokenOffset = outputOffset;
111
- outputOffset += 1;
112
- output[tokenOffset] =
113
- (Math.min(literalLength, 0x0f) << 4) | Math.min(encodedMatchLength, 0x0f);
114
-
115
- if (literalLength >= 0x0f) {
116
- outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
117
- }
118
- output.set(input.subarray(anchor, inputOffset), outputOffset);
119
- outputOffset += literalLength;
120
-
121
- output[outputOffset] = matchOffset & 0xff;
122
- output[outputOffset + 1] = matchOffset >> 8;
123
- outputOffset += 2;
124
- if (encodedMatchLength >= 0x0f) {
125
- outputOffset = writeLz4Length(output, outputOffset, encodedMatchLength - 0x0f);
126
- }
127
-
128
- const matchStart = inputOffset;
129
- inputOffset += matchLength;
130
- anchor = inputOffset;
131
- for (
132
- let cursor = Math.max(matchStart + 1, inputOffset - 2);
133
- cursor < inputOffset;
134
- cursor += 1
135
- ) {
136
- if (cursor <= matchFindEnd) {
137
- hashTable[getLz4Hash(readUint32LittleEndian(input, cursor))] = cursor;
138
- }
139
- }
140
- } else {
141
- inputOffset += 1;
142
- }
143
- }
144
-
145
- const literalLength = input.byteLength - anchor;
146
- const tokenOffset = outputOffset;
147
- outputOffset += 1;
148
- output[tokenOffset] = Math.min(literalLength, 0x0f) << 4;
149
- if (literalLength >= 0x0f) {
150
- outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
151
- }
152
- output.set(input.subarray(anchor), outputOffset);
153
- outputOffset += literalLength;
154
-
155
- return output.slice(0, outputOffset);
156
- }
157
-
158
- function quantizeChannel(value: number, levels: number) {
159
- return Math.floor((value * (levels - 1) + 0x7f) / 0xff);
160
- }
161
-
162
- function expandChannel(value: number, levels: number) {
163
- return Math.floor((value * 0xff + Math.floor((levels - 1) / 2)) / (levels - 1));
164
- }
165
-
166
- function encodePro2I8Lz4(options: {
167
- width: number;
168
- height: number;
169
- rgba: Uint8Array | ArrayBuffer;
170
- }): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
171
- const { width, height } = options;
172
- if (!Number.isInteger(width) || width <= 0 || width > 0xffff) {
173
- throw invalidParameter('Wallpaper width must be an integer between 1 and 65535.');
174
- }
175
- if (!Number.isInteger(height) || height <= 0 || height > 0xffff) {
176
- throw invalidParameter('Wallpaper height must be an integer between 1 and 65535.');
177
- }
178
- const rgba = asBytes(options.rgba);
179
- const expectedLength = width * height * 4;
180
- if (rgba.byteLength !== expectedLength) {
181
- throw invalidParameter(
182
- `Wallpaper RGBA data length must be ${expectedLength} bytes, received ${rgba.byteLength}.`
183
- );
184
- }
185
- const stride = width;
186
- const rawData = new Uint8Array(I8_PALETTE_SIZE + stride * height);
187
-
188
- for (let red = 0; red < I8_RED_LEVELS; red += 1) {
189
- for (let green = 0; green < I8_GREEN_LEVELS; green += 1) {
190
- for (let blue = 0; blue < I8_BLUE_LEVELS; blue += 1) {
191
- const paletteIndex = (red * I8_GREEN_LEVELS + green) * I8_BLUE_LEVELS + blue;
192
- const paletteOffset = paletteIndex * 4;
193
- // LVGL stores its ARGB8888 palette as B, G, R, A bytes on little-endian devices.
194
- rawData[paletteOffset] = expandChannel(blue, I8_BLUE_LEVELS);
195
- rawData[paletteOffset + 1] = expandChannel(green, I8_GREEN_LEVELS);
196
- rawData[paletteOffset + 2] = expandChannel(red, I8_RED_LEVELS);
197
- rawData[paletteOffset + 3] = 0xff;
198
- }
199
- }
200
- }
201
-
202
- for (let pixel = 0; pixel < width * height; pixel += 1) {
203
- const sourceOffset = pixel * 4;
204
- const alpha = rgba[sourceOffset + 3];
205
- const red = Math.round((rgba[sourceOffset] * alpha) / 0xff);
206
- const green = Math.round((rgba[sourceOffset + 1] * alpha) / 0xff);
207
- const blue = Math.round((rgba[sourceOffset + 2] * alpha) / 0xff);
208
- const paletteIndex =
209
- (quantizeChannel(red, I8_RED_LEVELS) * I8_GREEN_LEVELS +
210
- quantizeChannel(green, I8_GREEN_LEVELS)) *
211
- I8_BLUE_LEVELS +
212
- quantizeChannel(blue, I8_BLUE_LEVELS);
213
- rawData[I8_PALETTE_SIZE + pixel] = paletteIndex;
214
- }
215
-
216
- const compressed = compressLz4Block(rawData);
217
- const data = new Uint8Array(24 + compressed.byteLength);
218
- const view = new DataView(data.buffer);
219
- data[0] = 0x19;
220
- data[1] = COLOR_FORMAT_I8;
221
- view.setUint16(2, IMAGE_FLAG_COMPRESSED, true);
222
- view.setUint16(4, width, true);
223
- view.setUint16(6, height, true);
224
- view.setUint16(8, stride, true);
225
- view.setUint16(10, 0, true);
226
- view.setUint32(12, IMAGE_COMPRESSION_LZ4, true);
227
- view.setUint32(16, compressed.byteLength, true);
228
- view.setUint32(20, rawData.byteLength, true);
229
- data.set(compressed, 24);
230
- return { data, colorFormat: 'I8' };
231
- }
232
-
233
41
  export function encodePro2Image(options: {
234
42
  width: number;
235
43
  height: number;
@@ -317,10 +125,6 @@ export function encodePro2Wallpaper(options: {
317
125
  width: number;
318
126
  height: number;
319
127
  rgba: Uint8Array | ArrayBuffer;
320
- encoding?: Pro2WallpaperEncoding;
321
128
  }): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
322
- if (options.encoding === 'i8-lz4') {
323
- return encodePro2I8Lz4(options);
324
- }
325
129
  return encodePro2Image(options);
326
130
  }