Skip to content

Commit d23d32f

Browse files
authored
fix: fix writeWithRetry (#405)
* feat: optimize ble transport v1 * fix: fix writeWithRetry * fix: delete wait 10s * fix: only retry write in emmcFileWrite * fix: only wait 3s with pro & touch * Rollback firmware submodule to c0494cc
1 parent cad00ee commit d23d32f

File tree

6 files changed

+148
-58
lines changed

6 files changed

+148
-58
lines changed

packages/core/src/api/FirmwareUpdateV2.ts

+10
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,18 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
101101
Log.log('FirmwareUpdateV2 [checkDeviceToBootloader] isBleReconnect: ', isBleReconnect);
102102

103103
// check device goto bootloader mode
104+
let isFirstCheck = true;
105+
const isTouchOrProDevice =
106+
getDeviceType(this?.device?.features) === 'touch' ||
107+
getDeviceType(this?.device?.features) === 'pro';
104108
const intervalTimer: ReturnType<typeof setInterval> | undefined = setInterval(
105109
async () => {
110+
Log.log('FirmwareUpdateV2 [checkDeviceToBootloader] isFirstCheck: ', isFirstCheck);
111+
if (isTouchOrProDevice && isFirstCheck) {
112+
isFirstCheck = false;
113+
Log.log('FirmwareUpdateV2 [checkDeviceToBootloader] wait 3000ms');
114+
await wait(3000);
115+
}
106116
if (isBleReconnect) {
107117
try {
108118
await this.device.deviceConnector?.acquire(

packages/hd-transport-react-native/src/BleTransport.ts

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { Device, Characteristic } from '@onekeyfe/react-native-ble-plx';
1+
import { Device, Characteristic, BleErrorCode } from '@onekeyfe/react-native-ble-plx';
2+
import { getLogger, LoggerNames, wait } from '@onekeyfe/hd-core';
3+
// import { wait } from '@onekeyfe/hd-core/src/utils';
4+
5+
const Log = getLogger(LoggerNames.HdBleTransport);
26

37
export default class BleTransport {
48
id: string;
@@ -13,6 +17,10 @@ export default class BleTransport {
1317

1418
nofitySubscription?: () => void;
1519

20+
static MAX_RETRIES = 10;
21+
22+
static RETRY_DELAY = 2000;
23+
1624
constructor(
1725
device: Device,
1826
writeCharacteristic: Characteristic,
@@ -24,4 +32,38 @@ export default class BleTransport {
2432
this.notifyCharacteristic = notifyCharacteristic;
2533
console.log(`BleTransport(${String(this.id)}) new instance`);
2634
}
35+
36+
/**
37+
* @description only for pro / touch , while upgrade firmware
38+
* @param data
39+
* @param retryCount
40+
* @returns
41+
*/
42+
async writeWithRetry(data: string, retryCount = BleTransport.MAX_RETRIES): Promise<void> {
43+
try {
44+
await this.writeCharacteristic.writeWithoutResponse(data);
45+
} catch (error) {
46+
Log?.debug(
47+
`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`
48+
);
49+
if (retryCount > 0) {
50+
await wait(BleTransport.RETRY_DELAY);
51+
if (
52+
error.errorCode === BleErrorCode.DeviceDisconnected ||
53+
error.errorCode === BleErrorCode.CharacteristicNotFound
54+
) {
55+
try {
56+
await this.device.connect();
57+
await this.device.discoverAllServicesAndCharacteristics();
58+
} catch (e) {
59+
Log?.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
60+
}
61+
} else {
62+
Log?.debug(`writeCharacteristic error: ${error}`);
63+
}
64+
return this.writeWithRetry(data, retryCount - 1);
65+
}
66+
throw error;
67+
}
68+
}
2769
}

packages/hd-transport-react-native/src/index.ts

+89-53
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,21 @@ export default class ReactNativeBleTransport {
374374
});
375375

376376
const disconnectSubscription = device.onDisconnected(() => {
377-
this.Log.debug('device disconnect: ', device?.id);
378-
this.emitter?.emit('device-disconnect', {
379-
name: device?.name,
380-
id: device?.id,
381-
connectId: device?.id,
382-
});
383-
this.release(uuid);
384-
disconnectSubscription?.remove();
377+
try {
378+
this.Log.debug('device disconnect: ', device?.id);
379+
this.emitter?.emit('device-disconnect', {
380+
name: device?.name,
381+
id: device?.id,
382+
connectId: device?.id,
383+
});
384+
if (this.runPromise) {
385+
this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError));
386+
}
387+
} finally {
388+
this.runPromise = null;
389+
this.release(uuid);
390+
disconnectSubscription?.remove();
391+
}
385392
});
386393

387394
return { uuid };
@@ -392,41 +399,45 @@ export default class ReactNativeBleTransport {
392399
let buffer: any[] = [];
393400
const subscription = characteristic.monitor((error, c) => {
394401
if (error) {
395-
this.Log.debug(
396-
`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${
397-
error as unknown as string
398-
}`
399-
);
400-
if (this.runPromise) {
401-
let ERROR:
402-
| typeof HardwareErrorCode.BleDeviceBondError
403-
| typeof HardwareErrorCode.BleCharacteristicNotifyError
404-
| typeof HardwareErrorCode.BleTimeoutError =
405-
HardwareErrorCode.BleCharacteristicNotifyError;
406-
if (error.reason?.includes('The connection has timed out unexpectedly')) {
407-
ERROR = HardwareErrorCode.BleTimeoutError;
408-
}
409-
if (error.reason?.includes('Encryption is insufficient')) {
410-
ERROR = HardwareErrorCode.BleDeviceBondError;
411-
}
412-
if (
413-
error.reason?.includes('Cannot write client characteristic config descriptor') ||
414-
error.reason?.includes('Cannot find client characteristic config descriptor') ||
415-
error.reason?.includes('The handle is invalid')
416-
) {
417-
this.runPromise.reject(
418-
ERRORS.TypedError(
419-
HardwareErrorCode.BleCharacteristicNotifyChangeFailure,
420-
error.message ?? error.reason
421-
)
422-
);
423-
this.Log.debug(
424-
`${HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`
425-
);
426-
return;
402+
try {
403+
this.Log.debug(
404+
`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${
405+
error as unknown as string
406+
}`
407+
);
408+
if (this.runPromise) {
409+
let ERROR:
410+
| typeof HardwareErrorCode.BleDeviceBondError
411+
| typeof HardwareErrorCode.BleCharacteristicNotifyError
412+
| typeof HardwareErrorCode.BleTimeoutError =
413+
HardwareErrorCode.BleCharacteristicNotifyError;
414+
if (error.reason?.includes('The connection has timed out unexpectedly')) {
415+
ERROR = HardwareErrorCode.BleTimeoutError;
416+
}
417+
if (error.reason?.includes('Encryption is insufficient')) {
418+
ERROR = HardwareErrorCode.BleDeviceBondError;
419+
}
420+
if (
421+
error.reason?.includes('Cannot write client characteristic config descriptor') ||
422+
error.reason?.includes('Cannot find client characteristic config descriptor') ||
423+
error.reason?.includes('The handle is invalid')
424+
) {
425+
this.runPromise.reject(
426+
ERRORS.TypedError(
427+
HardwareErrorCode.BleCharacteristicNotifyChangeFailure,
428+
error.message ?? error.reason
429+
)
430+
);
431+
this.Log.debug(
432+
`${HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`
433+
);
434+
return;
435+
}
436+
this.runPromise.reject(ERRORS.TypedError(ERROR, error.reason ?? error.message));
437+
this.Log.debug(': monitor notify error, and has unreleased Promise');
427438
}
428-
this.runPromise.reject(ERRORS.TypedError(ERROR, error.reason ?? error.message));
429-
this.Log.debug(': monitor notify error, and has unreleased Promise');
439+
} finally {
440+
this.runPromise = null;
430441
}
431442
return;
432443
}
@@ -516,30 +527,55 @@ export default class ReactNativeBleTransport {
516527
this.Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
517528
}
518529

519-
const buffers = buildBuffers(messages, name, data);
530+
const buffers = buildBuffers(messages, name, data) as Array<ByteBuffer>;
520531

521-
if (name === 'FirmwareUpload' || name === 'EmmcFileWrite') {
532+
async function writeChunkedData(
533+
buffers: ByteBuffer[],
534+
writeFunction: (data: string) => Promise<void>,
535+
onError: (e: any) => void
536+
) {
522537
const packetCapacity = Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
523538
let index = 0;
524539
let chunk = ByteBuffer.allocate(packetCapacity);
540+
525541
while (index < buffers.length) {
526542
const buffer = buffers[index].toBuffer();
527543
chunk.append(buffer);
528544
index += 1;
545+
529546
if (chunk.offset === packetCapacity || index >= buffers.length) {
530547
chunk.reset();
531-
// Upgrading Packet Logs, Too much content to ignore
532-
// this.Log.debug('send more packet hex strting: ', chunk.toString('hex'));
533548
try {
534-
await transport.writeCharacteristic.writeWithoutResponse(chunk.toString('base64'));
549+
await writeFunction(chunk.toString('base64'));
535550
chunk = ByteBuffer.allocate(packetCapacity);
536551
} catch (e) {
537-
this.runPromise = null;
538-
this.Log.error('writeCharacteristic write error: ', e);
539-
return;
552+
onError(e);
553+
throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
540554
}
541555
}
542556
}
557+
}
558+
559+
if (name === 'EmmcFileWrite') {
560+
await writeChunkedData(
561+
buffers,
562+
data => transport.writeWithRetry(data),
563+
e => {
564+
this.runPromise = null;
565+
this.Log.error('writeCharacteristic write error: ', e);
566+
}
567+
);
568+
} else if (name === 'FirmwareUpload') {
569+
await writeChunkedData(
570+
buffers,
571+
async data => {
572+
await transport.writeCharacteristic.writeWithoutResponse(data);
573+
},
574+
e => {
575+
this.runPromise = null;
576+
this.Log.error('writeCharacteristic write error: ', e);
577+
}
578+
);
543579
} else {
544580
for (const o of buffers) {
545581
const outData = o.toString('base64');
@@ -552,11 +588,11 @@ export default class ReactNativeBleTransport {
552588
this.runPromise = null;
553589
if (e.errorCode === BleErrorCode.DeviceDisconnected) {
554590
throw ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded);
555-
}
556-
if (e.errorCode === BleErrorCode.OperationStartFailed) {
591+
} else if (e.errorCode === BleErrorCode.OperationStartFailed) {
557592
throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, e.reason);
593+
} else {
594+
throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
558595
}
559-
return;
560596
}
561597
}
562598
}

packages/hd-transport/src/serialization/protobuf/decode.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import { isPrimitiveField } from '../../utils/protobuf';
44

55
const transform = (field: Field, value: any) => {
66
// [compatibility]: optional undefined keys should be null. Example: Features.fw_major.
7-
if (field.optional && typeof value === 'undefined') {
7+
if (field?.optional && typeof value === 'undefined') {
88
return null;
99
}
1010

11-
if (field.type === 'bytes') {
11+
if (field?.type === 'bytes') {
1212
return ByteBuffer.wrap(value).toString('hex');
1313
// return value.toString('hex');
1414
}
1515

1616
// [compatibility]
1717
// it is likely that we can remove this right away because trezor-connect tests don't ever trigger this condition
1818
// we should probably make sure that trezor-connect treats following protobuf types as strings: int64, uint64, sint64, fixed64, sfixed64
19-
if (field.long) {
19+
if (field?.long) {
2020
if (Number.isSafeInteger(value.toNumber())) {
2121
// old trezor-link behavior https://github.com/trezor/trezor-link/blob/9c200cc5608976cff0542484525e98c753ba1888/src/lowlevel/protobuf/message_decoder.js#L80
2222
return value.toNumber();

packages/hd-transport/src/serialization/protobuf/encode.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function patch(Message: Type, payload: any) {
3737
return;
3838
}
3939
// primitive type
40-
if (isPrimitiveField(field.type)) {
40+
if (field && isPrimitiveField(field.type)) {
4141
if (field.repeated) {
4242
patched[key] = value.map((v: any) => transform(field.type, v));
4343
} else {

packages/shared/src/HardwareError.ts

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ export const HardwareErrorCode = {
285285
BleForceCleanRunPromise: 714,
286286
BleDeviceBondError: 715,
287287
BleCharacteristicNotifyChangeFailure: 716,
288+
BleTransportCallCanceled: 717,
288289

289290
/**
290291
* Hardware runtiome errors
@@ -476,6 +477,7 @@ export const HardwareErrorCodeMessage: HardwareErrorCodeMessageMapping = {
476477
[HardwareErrorCode.BleForceCleanRunPromise]: 'Force clean Bluetooth run promise',
477478
[HardwareErrorCode.BleDeviceBondError]: 'Bluetooth pairing failed',
478479
[HardwareErrorCode.BleCharacteristicNotifyChangeFailure]: 'Characteristic Notify Change Failure',
480+
[HardwareErrorCode.BleTransportCallCanceled]: 'Ble Transport call canceled',
479481

480482
/**
481483
* Runtime Error

0 commit comments

Comments
 (0)