@onekeyfe/hd-transport-react-native 1.2.2-alpha.100 → 1.2.2-alpha.11
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/dist/bleStaleBond.d.ts +5 -0
- package/dist/bleStaleBond.d.ts.map +1 -0
- package/dist/bleStrategy.d.ts +1 -1
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -64
- package/package.json +5 -5
- package/src/__tests__/bleStaleBond.test.ts +39 -0
- package/src/__tests__/bleStrategy.test.ts +2 -3
- package/src/__tests__/protocolV2Link.test.ts +158 -16
- package/src/bleStaleBond.ts +61 -0
- package/src/bleStrategy.ts +1 -3
- package/src/index.ts +197 -68
|
@@ -109,9 +109,11 @@ const schemas = {
|
|
|
109
109
|
const createHarness = ({
|
|
110
110
|
deviceName = 'OneKey Pro 2',
|
|
111
111
|
isWritableWithResponse = true,
|
|
112
|
+
monitorError,
|
|
112
113
|
}: {
|
|
113
114
|
deviceName?: string;
|
|
114
115
|
isWritableWithResponse?: boolean;
|
|
116
|
+
monitorError?: (Error & { reason?: string; attErrorCode?: number; iosErrorCode?: number }) | null;
|
|
115
117
|
} = {}) => {
|
|
116
118
|
const uuid = 'rn-pro2-id';
|
|
117
119
|
const sentSeqs: number[] = [];
|
|
@@ -130,6 +132,9 @@ const createHarness = ({
|
|
|
130
132
|
isNotifiable: true,
|
|
131
133
|
monitor: jest.fn(callback => {
|
|
132
134
|
notifyCallback = callback;
|
|
135
|
+
if (monitorError) {
|
|
136
|
+
queueMicrotask(() => callback(monitorError, null));
|
|
137
|
+
}
|
|
133
138
|
return { remove: jest.fn() };
|
|
134
139
|
}),
|
|
135
140
|
};
|
|
@@ -405,6 +410,22 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
405
410
|
await transport.release(uuid, true);
|
|
406
411
|
});
|
|
407
412
|
|
|
413
|
+
test('keeps native stale-bond write mapping out of Protocol V1 calls', async () => {
|
|
414
|
+
const { transport, uuid, writeCharacteristic } = createV1Harness();
|
|
415
|
+
const nativeError = Object.assign(new Error('Encryption is insufficient'), {
|
|
416
|
+
attErrorCode: 15,
|
|
417
|
+
reason: 'Encryption is insufficient',
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
await transport.acquire({ uuid, expectedProtocol: 'V1' });
|
|
421
|
+
writeCharacteristic.writeWithResponse.mockRejectedValueOnce(nativeError);
|
|
422
|
+
|
|
423
|
+
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 50 })).rejects.toMatchObject({
|
|
424
|
+
errorCode: HardwareErrorCode.BleWriteCharacteristicError,
|
|
425
|
+
});
|
|
426
|
+
await transport.release(uuid, true);
|
|
427
|
+
});
|
|
428
|
+
|
|
408
429
|
test('detects Protocol V2 on iOS without using the BLE name as a protocol hint', async () => {
|
|
409
430
|
const { transport, uuid, device, sentSeqs, writeCharacteristic } = createHarness({
|
|
410
431
|
deviceName: 'Pro2 6E9E',
|
|
@@ -415,7 +436,8 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
415
436
|
protocolType: 'V2',
|
|
416
437
|
});
|
|
417
438
|
expect(device.requestMTU).toHaveBeenCalledWith(247);
|
|
418
|
-
expect(writeCharacteristic.writeWithResponse
|
|
439
|
+
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
|
|
440
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalled();
|
|
419
441
|
|
|
420
442
|
await expect(
|
|
421
443
|
transport.call(uuid, 'Ping', { message: 'first-core-command' })
|
|
@@ -424,6 +446,93 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
424
446
|
await transport.release(uuid, true);
|
|
425
447
|
});
|
|
426
448
|
|
|
449
|
+
test('physically refreshes an uncached Protocol V2 firmware install link without Ping', async () => {
|
|
450
|
+
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
451
|
+
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
452
|
+
(transport as any).sessionProtocols.set(uuid, 'V2');
|
|
453
|
+
device.connect = jest.fn().mockResolvedValue(device);
|
|
454
|
+
device.isConnected.mockResolvedValueOnce(false);
|
|
455
|
+
|
|
456
|
+
await expect(
|
|
457
|
+
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
458
|
+
).resolves.toEqual({
|
|
459
|
+
uuid,
|
|
460
|
+
protocolType: 'V2',
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(uuid);
|
|
464
|
+
expect(device.cancelConnection).not.toHaveBeenCalled();
|
|
465
|
+
expect(device.connect).toHaveBeenCalled();
|
|
466
|
+
expect(probeProtocolV2).not.toHaveBeenCalled();
|
|
467
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
468
|
+
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
469
|
+
expect(transport.getProtocolType(uuid)).toBe('V2');
|
|
470
|
+
await transport.release(uuid, true);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test('refreshes a cached firmware install connection instead of trusting GATT state', async () => {
|
|
474
|
+
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
475
|
+
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
476
|
+
|
|
477
|
+
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
478
|
+
device.connect = jest.fn().mockResolvedValue(device);
|
|
479
|
+
device.isConnected.mockResolvedValueOnce(false);
|
|
480
|
+
writeCharacteristic.writeWithResponse.mockClear();
|
|
481
|
+
writeCharacteristic.writeWithoutResponse.mockClear();
|
|
482
|
+
|
|
483
|
+
await expect(
|
|
484
|
+
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
485
|
+
).resolves.toEqual({
|
|
486
|
+
uuid,
|
|
487
|
+
protocolType: 'V2',
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(uuid);
|
|
491
|
+
expect(device.cancelConnection).toHaveBeenCalled();
|
|
492
|
+
expect(device.connect).toHaveBeenCalled();
|
|
493
|
+
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
494
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
495
|
+
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
496
|
+
await transport.release(uuid, true);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test('rejects no-probe acquire before the BLE endpoint has confirmed a protocol', async () => {
|
|
500
|
+
const { transport, uuid } = createHarness();
|
|
501
|
+
|
|
502
|
+
await expect(
|
|
503
|
+
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
504
|
+
).rejects.toThrow('previously confirmed protocol');
|
|
505
|
+
|
|
506
|
+
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test('keeps confirmed V2 authorization across a BLE manager reset', async () => {
|
|
510
|
+
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
511
|
+
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
512
|
+
|
|
513
|
+
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
514
|
+
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
515
|
+
|
|
516
|
+
(transport as any).resetPlxManager();
|
|
517
|
+
transport.blePlxManager = bleManager as any;
|
|
518
|
+
device.connect = jest.fn().mockResolvedValue(device);
|
|
519
|
+
device.isConnected.mockResolvedValueOnce(false);
|
|
520
|
+
writeCharacteristic.writeWithResponse.mockClear();
|
|
521
|
+
writeCharacteristic.writeWithoutResponse.mockClear();
|
|
522
|
+
|
|
523
|
+
await expect(
|
|
524
|
+
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
525
|
+
).resolves.toEqual({
|
|
526
|
+
uuid,
|
|
527
|
+
protocolType: 'V2',
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
531
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
532
|
+
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
533
|
+
await transport.release(uuid, true);
|
|
534
|
+
});
|
|
535
|
+
|
|
427
536
|
test.each(['ios', 'android'] as const)(
|
|
428
537
|
'keeps a first expected Protocol V2 probe miss retryable on %s',
|
|
429
538
|
async platform => {
|
|
@@ -459,8 +568,37 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
459
568
|
}
|
|
460
569
|
);
|
|
461
570
|
|
|
571
|
+
test.each([
|
|
572
|
+
[
|
|
573
|
+
'Encryption is insufficient',
|
|
574
|
+
{ reason: 'Encryption is insufficient', attErrorCode: 15 },
|
|
575
|
+
HardwareErrorCode.BleDeviceBondError,
|
|
576
|
+
],
|
|
577
|
+
[
|
|
578
|
+
'Peer removed pairing information',
|
|
579
|
+
{ reason: 'Peer removed pairing information', iosErrorCode: 14 },
|
|
580
|
+
HardwareErrorCode.BlePeerRemovedPairingInformation,
|
|
581
|
+
],
|
|
582
|
+
] as const)(
|
|
583
|
+
'fails Protocol V2 acquire immediately on %s instead of waiting for Ping',
|
|
584
|
+
async (_label, nativeError, errorCode) => {
|
|
585
|
+
const { transport, uuid, device } = createHarness({
|
|
586
|
+
monitorError: Object.assign(new Error(nativeError.reason), nativeError),
|
|
587
|
+
});
|
|
588
|
+
const probe = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
589
|
+
|
|
590
|
+
await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
|
|
591
|
+
errorCode,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
expect(probe).not.toHaveBeenCalled();
|
|
595
|
+
expect(device.cancelConnection).toHaveBeenCalled();
|
|
596
|
+
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
|
|
462
600
|
test.each(['ios', 'android'] as const)(
|
|
463
|
-
'
|
|
601
|
+
'keeps a confirmed Protocol V2 probe miss retryable on %s without native bond evidence',
|
|
464
602
|
async platform => {
|
|
465
603
|
setPlatformOS(platform);
|
|
466
604
|
const { transport, uuid, device } = createHarness();
|
|
@@ -469,10 +607,10 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
469
607
|
jest.spyOn(transport as any, 'probeProtocolV2').mockResolvedValue(false);
|
|
470
608
|
|
|
471
609
|
await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
|
|
472
|
-
errorCode: HardwareErrorCode.
|
|
610
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
473
611
|
});
|
|
474
612
|
|
|
475
|
-
expect(device.cancelConnection).toHaveBeenCalled();
|
|
613
|
+
expect(device.cancelConnection).not.toHaveBeenCalled();
|
|
476
614
|
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
477
615
|
}
|
|
478
616
|
);
|
|
@@ -658,7 +796,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
658
796
|
});
|
|
659
797
|
expect(device.requestMTU).toHaveBeenCalledTimes(3);
|
|
660
798
|
expect((transport as any).getCachedTransport(uuid).mtuSize).toBeUndefined();
|
|
661
|
-
expect(writeCharacteristic.
|
|
799
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalled();
|
|
662
800
|
await transport.release(uuid, true);
|
|
663
801
|
});
|
|
664
802
|
|
|
@@ -667,6 +805,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
667
805
|
device.mtu = undefined;
|
|
668
806
|
|
|
669
807
|
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
808
|
+
const writesBeforeFileWrite = writeCharacteristic.writeWithoutResponse.mock.calls.length;
|
|
670
809
|
device.requestMTU.mockImplementationOnce(() => {
|
|
671
810
|
device.mtu = 247;
|
|
672
811
|
return Promise.resolve(device);
|
|
@@ -674,7 +813,9 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
674
813
|
|
|
675
814
|
await expect(transport.call(uuid, 'FileWrite', {})).resolves.toBeDefined();
|
|
676
815
|
expect(device.requestMTU).toHaveBeenCalledTimes(4);
|
|
677
|
-
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(
|
|
816
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(
|
|
817
|
+
writesBeforeFileWrite + 1
|
|
818
|
+
);
|
|
678
819
|
await transport.release(uuid, true);
|
|
679
820
|
});
|
|
680
821
|
|
|
@@ -683,12 +824,13 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
683
824
|
device.mtu = undefined;
|
|
684
825
|
|
|
685
826
|
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
827
|
+
const writesBeforeFileWrite = writeCharacteristic.writeWithoutResponse.mock.calls.length;
|
|
686
828
|
|
|
687
829
|
await expect(transport.call(uuid, 'FileWrite', {})).rejects.toMatchObject({
|
|
688
830
|
errorCode: HardwareErrorCode.BleConnectedError,
|
|
689
831
|
});
|
|
690
832
|
expect(device.requestMTU).toHaveBeenCalledTimes(4);
|
|
691
|
-
expect(writeCharacteristic.writeWithoutResponse).
|
|
833
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(writesBeforeFileWrite);
|
|
692
834
|
await transport.release(uuid, true);
|
|
693
835
|
});
|
|
694
836
|
|
|
@@ -808,19 +950,19 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
808
950
|
await transport.release(uuid, true);
|
|
809
951
|
});
|
|
810
952
|
|
|
811
|
-
test('uses
|
|
953
|
+
test('uses withoutResponse for consecutive iOS Protocol V2 control calls without releasing', async () => {
|
|
812
954
|
const { transport, uuid, writeCharacteristic } = createHarness();
|
|
813
955
|
|
|
814
956
|
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
815
957
|
const releaseNative = jest.spyOn(transport as any, 'releaseNative');
|
|
816
|
-
expect(writeCharacteristic.writeWithoutResponse).
|
|
817
|
-
expect(writeCharacteristic.writeWithResponse).
|
|
958
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
959
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
818
960
|
|
|
819
961
|
await transport.call(uuid, 'DeviceInfoGet', {});
|
|
820
962
|
await transport.call(uuid, 'ProtocolInfoRequest', {});
|
|
821
963
|
|
|
822
|
-
expect(writeCharacteristic.
|
|
823
|
-
expect(writeCharacteristic.
|
|
964
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
965
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
824
966
|
expect(releaseNative).not.toHaveBeenCalled();
|
|
825
967
|
|
|
826
968
|
await transport.release(uuid, true);
|
|
@@ -833,8 +975,8 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
833
975
|
|
|
834
976
|
await transport.call(uuid, 'FileWrite', {});
|
|
835
977
|
await transport.call(uuid, 'FileWrite', {});
|
|
836
|
-
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(
|
|
837
|
-
expect(writeCharacteristic.writeWithResponse).
|
|
978
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
979
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
838
980
|
expect(
|
|
839
981
|
logger.debug.mock.calls.filter(
|
|
840
982
|
([message]) =>
|
|
@@ -867,8 +1009,8 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
867
1009
|
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
868
1010
|
|
|
869
1011
|
await transport.call(uuid, 'FileWrite', {}, { writeWithResponse: true });
|
|
870
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(
|
|
871
|
-
expect(writeCharacteristic.writeWithoutResponse).
|
|
1012
|
+
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
|
|
1013
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
872
1014
|
await transport.release(uuid, true);
|
|
873
1015
|
});
|
|
874
1016
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ERRORS,
|
|
3
|
+
HardwareErrorCode,
|
|
4
|
+
isBleStaleBondErrorText,
|
|
5
|
+
isBleStaleBondHardwareError,
|
|
6
|
+
} from '@onekeyfe/hd-shared';
|
|
7
|
+
|
|
8
|
+
export { isBleStaleBondHardwareError };
|
|
9
|
+
|
|
10
|
+
const ATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
11
|
+
const ATT_INSUFFICIENT_ENCRYPTION = 15;
|
|
12
|
+
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
13
|
+
|
|
14
|
+
type NativeBleErrorFields = {
|
|
15
|
+
attErrorCode?: unknown;
|
|
16
|
+
iosErrorCode?: unknown;
|
|
17
|
+
reason?: unknown;
|
|
18
|
+
message?: unknown;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const nativeErrorText = (error: NativeBleErrorFields) =>
|
|
22
|
+
[error.reason, error.message]
|
|
23
|
+
.filter((value): value is string => typeof value === 'string')
|
|
24
|
+
.join(' ');
|
|
25
|
+
|
|
26
|
+
export const isNativeBleStaleBondError = (error: unknown): boolean => {
|
|
27
|
+
if (!error || typeof error !== 'object') {
|
|
28
|
+
return typeof error === 'string' ? isBleStaleBondErrorText(error) : false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nativeError = error as NativeBleErrorFields;
|
|
32
|
+
if (
|
|
33
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
|
|
34
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
|
|
35
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION
|
|
36
|
+
) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return isBleStaleBondErrorText(nativeErrorText(nativeError));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const toBleStaleBondHardwareError = (error: unknown) => {
|
|
44
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
45
|
+
return error as Error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const nativeError = (error ?? {}) as NativeBleErrorFields;
|
|
49
|
+
const text = nativeErrorText(nativeError);
|
|
50
|
+
const normalizedText = text.toLowerCase();
|
|
51
|
+
const peerRemoved =
|
|
52
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
53
|
+
normalizedText.includes('peer removed pairing information');
|
|
54
|
+
|
|
55
|
+
return ERRORS.TypedError(
|
|
56
|
+
peerRemoved
|
|
57
|
+
? HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
58
|
+
: HardwareErrorCode.BleDeviceBondError,
|
|
59
|
+
text || undefined
|
|
60
|
+
);
|
|
61
|
+
};
|
package/src/bleStrategy.ts
CHANGED
|
@@ -34,8 +34,6 @@ export function shouldRefreshNegotiatedMtu(mtu?: number | null) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function shouldWriteProtocolV2WithResponse({
|
|
37
|
-
platform,
|
|
38
|
-
highThroughput,
|
|
39
37
|
requestedWithResponse,
|
|
40
38
|
characteristic,
|
|
41
39
|
}: {
|
|
@@ -46,5 +44,5 @@ export function shouldWriteProtocolV2WithResponse({
|
|
|
46
44
|
}) {
|
|
47
45
|
if (!characteristic.isWritableWithResponse) return false;
|
|
48
46
|
if (!characteristic.isWritableWithoutResponse) return true;
|
|
49
|
-
return requestedWithResponse === true
|
|
47
|
+
return requestedWithResponse === true;
|
|
50
48
|
}
|