@yume-chan/adb-daemon-webusb 0.0.20

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/CHANGELOG.json ADDED
@@ -0,0 +1,157 @@
1
+ {
2
+ "name": "@yume-chan/adb-daemon-webusb",
3
+ "entries": [
4
+ {
5
+ "version": "0.0.20",
6
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.20",
7
+ "date": "Mon, 05 Jun 2023 02:51:41 GMT",
8
+ "comments": {
9
+ "none": [
10
+ {
11
+ "comment": "Use ECMAScript private class fields syntax (supported by Chrome 74, Firefox 90, Safari 14.1 and Node.js 12.0.0)."
12
+ },
13
+ {
14
+ "comment": "Rename package to `@yume-chan/adb-daemon-webusb` following the renaming of `AdbDaemonTransport`."
15
+ },
16
+ {
17
+ "comment": "Rename `AdbWebUsbBackend` to `AdbDaemonWebUsbDevice` following the renaming of `AdbDaemonTransport`."
18
+ },
19
+ {
20
+ "comment": "Add Support for detecting device disconnects. It no longer throws an `NetworkError` when the device is disconnected."
21
+ },
22
+ {
23
+ "comment": "Add `filters` parameter to `AdbDaemonWebUsbDeviceManager#getDevices`. The filtration is manually implemented because WebUSB's `getDevice` API doesn't support filters."
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ {
29
+ "version": "0.0.19",
30
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.19",
31
+ "date": "Sun, 09 Apr 2023 05:55:33 GMT",
32
+ "comments": {
33
+ "none": [
34
+ {
35
+ "comment": "Remove stream queuing"
36
+ },
37
+ {
38
+ "comment": "Add the `AdbWebUsbBackendManager` class to simplify the usage with custom WebUSB implementations (for example the `usb` NPM package)."
39
+ }
40
+ ]
41
+ }
42
+ },
43
+ {
44
+ "version": "0.0.18",
45
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.18",
46
+ "date": "Wed, 25 Jan 2023 21:33:49 GMT",
47
+ "comments": {
48
+ "none": [
49
+ {
50
+ "comment": "Add an option to specify USB filters"
51
+ }
52
+ ]
53
+ }
54
+ },
55
+ {
56
+ "version": "0.0.17",
57
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.17",
58
+ "date": "Tue, 18 Oct 2022 09:32:30 GMT",
59
+ "comments": {
60
+ "none": [
61
+ {
62
+ "comment": "Update to use new stream util package"
63
+ }
64
+ ]
65
+ }
66
+ },
67
+ {
68
+ "version": "0.0.16",
69
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.16",
70
+ "date": "Sat, 28 May 2022 03:56:37 GMT",
71
+ "comments": {
72
+ "none": [
73
+ {
74
+ "comment": "Upgrade TypeScript to 4.7.2 to enable Node.js ESM"
75
+ }
76
+ ]
77
+ }
78
+ },
79
+ {
80
+ "version": "0.0.15",
81
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.15",
82
+ "date": "Mon, 02 May 2022 04:18:01 GMT",
83
+ "comments": {
84
+ "none": [
85
+ {
86
+ "comment": "Improve connection lifecycle handling"
87
+ }
88
+ ]
89
+ }
90
+ },
91
+ {
92
+ "version": "0.0.14",
93
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.14",
94
+ "date": "Sat, 30 Apr 2022 14:05:48 GMT",
95
+ "comments": {}
96
+ },
97
+ {
98
+ "version": "0.0.13",
99
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.13",
100
+ "date": "Thu, 28 Apr 2022 01:23:53 GMT",
101
+ "comments": {
102
+ "none": [
103
+ {
104
+ "comment": "Workaround an issue in Chrome where `transferIn` never returns `babble` on Windows"
105
+ }
106
+ ]
107
+ }
108
+ },
109
+ {
110
+ "version": "0.0.12",
111
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.12",
112
+ "date": "Sun, 03 Apr 2022 11:18:47 GMT",
113
+ "comments": {}
114
+ },
115
+ {
116
+ "version": "0.0.11",
117
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.11",
118
+ "date": "Sun, 03 Apr 2022 10:54:15 GMT",
119
+ "comments": {
120
+ "none": [
121
+ {
122
+ "comment": "Update to use Web Streams API"
123
+ },
124
+ {
125
+ "comment": "Improve compatibility with Node.js 12 ESM format"
126
+ },
127
+ {
128
+ "comment": "Update license year"
129
+ }
130
+ ],
131
+ "dependency": [
132
+ {
133
+ "comment": "Updating dependency \"@yume-chan/adb\" from `^0.0.10` to `^0.0.11`"
134
+ }
135
+ ]
136
+ }
137
+ },
138
+ {
139
+ "version": "0.0.10",
140
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.10",
141
+ "date": "Sun, 09 Jan 2022 15:52:20 GMT",
142
+ "comments": {
143
+ "none": [
144
+ {
145
+ "comment": "Remove `encodeUtf8()` and `decodeUtf8()` from `AdbBackend`"
146
+ }
147
+ ]
148
+ }
149
+ },
150
+ {
151
+ "version": "0.0.9",
152
+ "tag": "@yume-chan/adb-daemon-webusb_v0.0.9",
153
+ "date": "Sun, 09 Jan 2022 15:50:20 GMT",
154
+ "comments": {}
155
+ }
156
+ ]
157
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,89 @@
1
+ # Change Log - @yume-chan/adb-daemon-webusb
2
+
3
+ This log was last generated on Mon, 05 Jun 2023 02:51:41 GMT and should not be manually modified.
4
+
5
+ ## 0.0.20
6
+ Mon, 05 Jun 2023 02:51:41 GMT
7
+
8
+ ### Updates
9
+
10
+ - Use ECMAScript private class fields syntax (supported by Chrome 74, Firefox 90, Safari 14.1 and Node.js 12.0.0).
11
+ - Rename package to `@yume-chan/adb-daemon-webusb` following the renaming of `AdbDaemonTransport`.
12
+ - Rename `AdbWebUsbBackend` to `AdbDaemonWebUsbDevice` following the renaming of `AdbDaemonTransport`.
13
+ - Add Support for detecting device disconnects. It no longer throws an `NetworkError` when the device is disconnected.
14
+ - Add `filters` parameter to `AdbDaemonWebUsbDeviceManager#getDevices`. The filtration is manually implemented because WebUSB's `getDevice` API doesn't support filters.
15
+
16
+ ## 0.0.19
17
+ Sun, 09 Apr 2023 05:55:33 GMT
18
+
19
+ ### Updates
20
+
21
+ - Remove stream queuing
22
+ - Add the `AdbWebUsbBackendManager` class to simplify the usage with custom WebUSB implementations (for example the `usb` NPM package).
23
+
24
+ ## 0.0.18
25
+ Wed, 25 Jan 2023 21:33:49 GMT
26
+
27
+ ### Updates
28
+
29
+ - Add an option to specify USB filters
30
+
31
+ ## 0.0.17
32
+ Tue, 18 Oct 2022 09:32:30 GMT
33
+
34
+ ### Updates
35
+
36
+ - Update to use new stream util package
37
+
38
+ ## 0.0.16
39
+ Sat, 28 May 2022 03:56:37 GMT
40
+
41
+ ### Updates
42
+
43
+ - Upgrade TypeScript to 4.7.2 to enable Node.js ESM
44
+
45
+ ## 0.0.15
46
+ Mon, 02 May 2022 04:18:01 GMT
47
+
48
+ ### Updates
49
+
50
+ - Improve connection lifecycle handling
51
+
52
+ ## 0.0.14
53
+ Sat, 30 Apr 2022 14:05:48 GMT
54
+
55
+ _Version update only_
56
+
57
+ ## 0.0.13
58
+ Thu, 28 Apr 2022 01:23:53 GMT
59
+
60
+ ### Updates
61
+
62
+ - Workaround an issue in Chrome where `transferIn` never returns `babble` on Windows
63
+
64
+ ## 0.0.12
65
+ Sun, 03 Apr 2022 11:18:47 GMT
66
+
67
+ _Version update only_
68
+
69
+ ## 0.0.11
70
+ Sun, 03 Apr 2022 10:54:15 GMT
71
+
72
+ ### Updates
73
+
74
+ - Update to use Web Streams API
75
+ - Improve compatibility with Node.js 12 ESM format
76
+ - Update license year
77
+
78
+ ## 0.0.10
79
+ Sun, 09 Jan 2022 15:52:20 GMT
80
+
81
+ ### Updates
82
+
83
+ - Remove `encodeUtf8()` and `decodeUtf8()` from `AdbBackend`
84
+
85
+ ## 0.0.9
86
+ Sun, 09 Jan 2022 15:50:20 GMT
87
+
88
+ _Initial release_
89
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2023 Simon Chan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @yume-chan/adb-daemon-webusb
2
+
3
+ ADB daemon transport device for `@yume-chan/adb` using WebUSB ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/USB), [Spec](https://wicg.github.io/webusb)) API.
4
+
5
+ - [Use in browser](#use-in-browser)
6
+ - [Use in Node.js](#use-in-nodejs)
7
+ - [`AdbDaemonWebUsbDevice`](#adbdaemonwebusbdevice)
8
+ - [constructor](#constructor)
9
+ - [`raw`](#raw)
10
+ - [`connect`](#connect)
11
+ - [`AdbDaemonWebUsbDeviceManager`](#adbdaemonwebusbdevicemanager)
12
+ - [`BROWSER`](#browser)
13
+ - [constructor](#constructor-1)
14
+ - [`requestDevice`](#requestdevice)
15
+ - [`getDevices`](#getdevices)
16
+ - [Note on secure context](#note-on-secure-context)
17
+
18
+ ## Use in browser
19
+
20
+ | Chrome | Edge | Firefox | Internet Explorer | Safari |
21
+ | -------------- | -------------- | ------- | ----------------- | ------ |
22
+ | 61<sup>1</sup> | 79<sup>1</sup> | No | No | No |
23
+
24
+ <sup>1</sup>: Chrome for Android is supported, Chrome for iOS is NOT supported.
25
+
26
+ ## Use in Node.js
27
+
28
+ | Node.js | `usb` NPM Package |
29
+ | ------- | ----------------- |
30
+ | 10.5 | 2.8.1 |
31
+
32
+ Node.js doesn't have native support for WebUSB API, but the [`usb`](https://www.npmjs.com/package/usb) NPM package provides a WebUSB compatible API.
33
+
34
+ To use a custom WebUSB API implementation, pass it to the constructor of `AdbDaemonWebUsbDevice`, `AdbDaemonWebUsbDeviceManager` and `AdbDaemonWebUsbConnectionWatcher` via the `usbManager` parameter.
35
+
36
+ ## `AdbDaemonWebUsbDevice`
37
+
38
+ ### constructor
39
+
40
+ ```ts
41
+ public constructor(
42
+ device: USBDevice,
43
+ filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER]
44
+ usbManager: USB
45
+ );
46
+ ```
47
+
48
+ Create a new instance of `AdbDaemonWebUsbDevice` using a specified `USBDevice` instance.
49
+
50
+ `USBDevice` and `USB` types are from WebUSB API.
51
+
52
+ The `filters` parameter specifies the `classCode`, `subclassCode` and `protocolCode` to use when searching for ADB interface. The default value is `[{ classCode: 0xff, subclassCode: 0x42, protocolCode: 0x1 }]`, defined by Google.
53
+
54
+ ### `raw`
55
+
56
+ ```ts
57
+ public get raw(): USBDevice;
58
+ ```
59
+
60
+ Gets the raw `USBDevice` from the device. Allow sending/receiving USB packets to other interfaces/endpoints. For example can be used with `@yume-chan/aoa` package.
61
+
62
+ ### `connect`
63
+
64
+ ```ts
65
+ public async connect(): Promise<
66
+ ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>
67
+ >
68
+ ```
69
+
70
+ Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
71
+
72
+ ## `AdbDaemonWebUsbDeviceManager`
73
+
74
+ A helper class that wraps the WebUSB API.
75
+
76
+ ### `BROWSER`
77
+
78
+ ```ts
79
+ public static readonly BROWSER: AdbDaemonWebUsbDeviceManager | undefined;
80
+ ```
81
+
82
+ Gets the instance of `AdbDaemonWebUsbDeviceManager` using browser WebUSB implementation.
83
+
84
+ May be `undefined` if the browser does not support WebUSB.
85
+
86
+ ### constructor
87
+
88
+ ```ts
89
+ public constructor(usbManager: USB);
90
+ ```
91
+
92
+ Create a new instance of `AdbDaemonWebUsbDeviceManager` using the specified WebUSB API implementation.
93
+
94
+ ### `requestDevice`
95
+
96
+ ```ts
97
+ public async requestDevice(
98
+ filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER]
99
+ ): Promise<AdbDaemonWebUsbDevice | undefined>
100
+ ```
101
+
102
+ Request access to a connected device.
103
+ This is a convince method for `usb.requestDevice()`.
104
+
105
+ The `filters` parameter must have `classCode`, `subclassCode` and `protocolCode` fields for selecting the ADB interface. It can also have `vendorId`, `productId` or `serialNumber` fields to limit the displayed device list.
106
+
107
+ Returns an `AdbDaemonWebUsbDevice` instance, or `undefined` if the user cancelled the picker.
108
+
109
+ ### `getDevices`
110
+
111
+ ```ts
112
+ public async getDevices(
113
+ filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER]
114
+ ): Promise<AdbDaemonWebUsbDevice[]>
115
+ ```
116
+
117
+ Get all connected and authenticated devices.
118
+
119
+ This is a convince method for `usb.getDevices()`.
120
+
121
+ ## Note on secure context
122
+
123
+ WebUSB requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS).
124
+
125
+ `localhost` is considered secure, so local development works. But to access a self-hosted server running on another machine, either add a certificate, or add the domain name to the allowlist on each client machine:
126
+
127
+ 1. Open `chrome://flags/#unsafely-treat-insecure-origin-as-secure`
128
+ 2. Add the protocol and domain part of your url (e.g. `http://192.168.0.100:9000`) to the input box
129
+ 3. Choose `Enable` from the dropdown menu
130
+ 4. Restart browser
@@ -0,0 +1,49 @@
1
+ /// <reference types="w3c-web-usb" />
2
+ import type { AdbDaemonDevice, AdbPacketData, AdbPacketInit } from "@yume-chan/adb";
3
+ import type { Consumable, ReadableWritablePair, WritableStream } from "@yume-chan/stream-extra";
4
+ import { ReadableStream } from "@yume-chan/stream-extra";
5
+ /**
6
+ * `classCode`, `subclassCode` and `protocolCode` are required
7
+ * for selecting correct USB configuration and interface.
8
+ */
9
+ export type AdbDeviceFilter = USBDeviceFilter & Required<Pick<USBDeviceFilter, "classCode" | "subclassCode" | "protocolCode">>;
10
+ /**
11
+ * The default filter for ADB devices, as defined by Google.
12
+ */
13
+ export declare const ADB_DEFAULT_DEVICE_FILTER: {
14
+ readonly classCode: 255;
15
+ readonly subclassCode: 66;
16
+ readonly protocolCode: 1;
17
+ };
18
+ export declare class AdbDaemonWebUsbConnectionStreams implements ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>> {
19
+ #private;
20
+ get readable(): ReadableStream<AdbPacketData>;
21
+ get writable(): WritableStream<Consumable<{
22
+ command: number;
23
+ arg0: number;
24
+ arg1: number;
25
+ checksum: number;
26
+ magic: number;
27
+ payload: Uint8Array;
28
+ }>>;
29
+ constructor(device: USBDevice, inEndpoint: USBEndpoint, outEndpoint: USBEndpoint, usbManager: USB);
30
+ }
31
+ export declare class AdbDaemonWebUsbConnection implements AdbDaemonDevice {
32
+ #private;
33
+ get device(): USBDevice;
34
+ get serial(): string;
35
+ get name(): string;
36
+ /**
37
+ * Create a new instance of `AdbDaemonWebUsbConnection` using a specified `USBDevice` instance
38
+ *
39
+ * @param device The `USBDevice` instance obtained elsewhere.
40
+ * @param filters The filters to use when searching for ADB interface. Defaults to {@link ADB_DEFAULT_DEVICE_FILTER}.
41
+ */
42
+ constructor(device: USBDevice, filters: AdbDeviceFilter[] | undefined, usbManager: USB);
43
+ /**
44
+ * Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
45
+ * @returns The pair of `AdbPacket` streams.
46
+ */
47
+ connect(): Promise<ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>>;
48
+ }
49
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACR,eAAe,EACf,aAAa,EACb,aAAa,EAChB,MAAM,gBAAgB,CAAC;AAMxB,OAAO,KAAK,EACR,UAAU,EACV,oBAAoB,EACpB,cAAc,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGH,cAAc,EAEjB,MAAM,yBAAyB,CAAC;AAMjC;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,eAAe,GACzC,QAAQ,CACJ,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC,CACvE,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;CAIF,CAAC;AA6FrC,qBAAa,gCACT,YAAW,oBAAoB,CAAC,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;;IAGzE,IAAW,QAAQ,kCAElB;IAGD,IAAW,QAAQ;;;;;;;QAElB;gBAGG,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,WAAW,EACvB,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,GAAG;CAiItB;AAED,qBAAa,yBAA0B,YAAW,eAAe;;IAK7D,IAAW,MAAM,cAEhB;IAED,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;;;OAKG;gBAEC,MAAM,EAAE,SAAS,EACjB,OAAO,+BAAiD,EACxD,UAAU,EAAE,GAAG;IAOnB;;;OAGG;IACU,OAAO,IAAI,OAAO,CAC3B,oBAAoB,CAAC,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CACjE;CA0CJ"}
@@ -0,0 +1,230 @@
1
+ import { AdbPacketHeader, AdbPacketSerializeStream, unreachable, } from "@yume-chan/adb";
2
+ import { ConsumableWritableStream, DuplexStreamFactory, ReadableStream, pipeFrom, } from "@yume-chan/stream-extra";
3
+ import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
4
+ import { isErrorName } from "./utils.js";
5
+ /**
6
+ * The default filter for ADB devices, as defined by Google.
7
+ */
8
+ export const ADB_DEFAULT_DEVICE_FILTER = {
9
+ classCode: 0xff,
10
+ subclassCode: 0x42,
11
+ protocolCode: 1,
12
+ };
13
+ function alternateMatchesFilter(alternate, filters) {
14
+ return filters.some((filter) => alternate.interfaceClass === filter.classCode &&
15
+ alternate.interfaceSubclass === filter.subclassCode &&
16
+ alternate.interfaceProtocol === filter.protocolCode);
17
+ }
18
+ function findUsbAlternateInterface(device, filters) {
19
+ for (const configuration of device.configurations) {
20
+ for (const interface_ of configuration.interfaces) {
21
+ for (const alternate of interface_.alternates) {
22
+ if (alternateMatchesFilter(alternate, filters)) {
23
+ return { configuration, interface_, alternate };
24
+ }
25
+ }
26
+ }
27
+ }
28
+ throw new Error("No matched alternate interface found");
29
+ }
30
+ /**
31
+ * Find the first pair of input and output endpoints from an alternate interface.
32
+ *
33
+ * ADB interface only has two endpoints, one for input and one for output.
34
+ */
35
+ function findUsbEndpoints(endpoints) {
36
+ if (endpoints.length === 0) {
37
+ throw new Error("No endpoints given");
38
+ }
39
+ let inEndpoint;
40
+ let outEndpoint;
41
+ for (const endpoint of endpoints) {
42
+ switch (endpoint.direction) {
43
+ case "in":
44
+ inEndpoint = endpoint;
45
+ if (outEndpoint) {
46
+ return { inEndpoint, outEndpoint };
47
+ }
48
+ break;
49
+ case "out":
50
+ outEndpoint = endpoint;
51
+ if (inEndpoint) {
52
+ return { inEndpoint, outEndpoint };
53
+ }
54
+ break;
55
+ }
56
+ }
57
+ if (!inEndpoint) {
58
+ throw new Error("No input endpoint found.");
59
+ }
60
+ if (!outEndpoint) {
61
+ throw new Error("No output endpoint found.");
62
+ }
63
+ throw new Error("unreachable");
64
+ }
65
+ class Uint8ArrayExactReadable {
66
+ #data;
67
+ #position;
68
+ get position() {
69
+ return this.#position;
70
+ }
71
+ constructor(data) {
72
+ this.#data = data;
73
+ this.#position = 0;
74
+ }
75
+ readExactly(length) {
76
+ const result = this.#data.subarray(this.#position, this.#position + length);
77
+ this.#position += length;
78
+ return result;
79
+ }
80
+ }
81
+ export class AdbDaemonWebUsbConnectionStreams {
82
+ #readable;
83
+ get readable() {
84
+ return this.#readable;
85
+ }
86
+ #writable;
87
+ get writable() {
88
+ return this.#writable;
89
+ }
90
+ constructor(device, inEndpoint, outEndpoint, usbManager) {
91
+ let closed = false;
92
+ const duplex = new DuplexStreamFactory({
93
+ close: async () => {
94
+ try {
95
+ closed = true;
96
+ await device.close();
97
+ }
98
+ catch {
99
+ /* device may have already disconnected */
100
+ }
101
+ },
102
+ dispose: () => {
103
+ closed = true;
104
+ usbManager.removeEventListener("disconnect", handleUsbDisconnect);
105
+ },
106
+ });
107
+ function handleUsbDisconnect(e) {
108
+ if (e.device === device) {
109
+ duplex.dispose().catch(unreachable);
110
+ }
111
+ }
112
+ usbManager.addEventListener("disconnect", handleUsbDisconnect);
113
+ this.#readable = duplex.wrapReadable(new ReadableStream({
114
+ async pull(controller) {
115
+ try {
116
+ // The `length` argument in `transferIn` must not be smaller than what the device sent,
117
+ // otherwise it will return `babble` status without any data.
118
+ // ADB daemon sends each packet in two parts, the 24-byte header and the payload.
119
+ const result = await device.transferIn(inEndpoint.endpointNumber, 24);
120
+ // TODO: webusb: handle `babble` by discarding the data and receive again
121
+ // Per spec, the `result.data` always covers the whole `buffer`.
122
+ const buffer = new Uint8Array(result.data.buffer);
123
+ const stream = new Uint8ArrayExactReadable(buffer);
124
+ // Add `payload` field to its type, it's assigned below.
125
+ const packet = AdbPacketHeader.deserialize(stream);
126
+ if (packet.payloadLength !== 0) {
127
+ const result = await device.transferIn(inEndpoint.endpointNumber, packet.payloadLength);
128
+ packet.payload = new Uint8Array(result.data.buffer);
129
+ }
130
+ else {
131
+ packet.payload = EMPTY_UINT8_ARRAY;
132
+ }
133
+ controller.enqueue(packet);
134
+ }
135
+ catch (e) {
136
+ // On Windows, disconnecting the device will cause `NetworkError` to be thrown,
137
+ // even before the `disconnect` event is fired.
138
+ // We need to wait a little bit and check if the device is still connected.
139
+ // https://github.com/WICG/webusb/issues/219
140
+ if (isErrorName(e, "NetworkError")) {
141
+ await new Promise((resolve) => {
142
+ setTimeout(() => {
143
+ resolve();
144
+ }, 100);
145
+ });
146
+ if (closed) {
147
+ controller.close();
148
+ }
149
+ else {
150
+ throw e;
151
+ }
152
+ }
153
+ throw e;
154
+ }
155
+ },
156
+ }));
157
+ const zeroMask = outEndpoint.packetSize - 1;
158
+ this.#writable = pipeFrom(duplex.createWritable(new ConsumableWritableStream({
159
+ write: async (chunk) => {
160
+ try {
161
+ await device.transferOut(outEndpoint.endpointNumber, chunk);
162
+ // In USB protocol, a not-full packet indicates the end of a transfer.
163
+ // If the payload size is a multiple of the packet size,
164
+ // we need to send an empty packet to indicate the end,
165
+ // so the OS will send it to the device immediately.
166
+ if (zeroMask &&
167
+ (chunk.byteLength & zeroMask) === 0) {
168
+ await device.transferOut(outEndpoint.endpointNumber, EMPTY_UINT8_ARRAY);
169
+ }
170
+ }
171
+ catch (e) {
172
+ if (closed) {
173
+ return;
174
+ }
175
+ throw e;
176
+ }
177
+ },
178
+ })), new AdbPacketSerializeStream());
179
+ }
180
+ }
181
+ export class AdbDaemonWebUsbConnection {
182
+ #filters;
183
+ #usbManager;
184
+ #device;
185
+ get device() {
186
+ return this.#device;
187
+ }
188
+ get serial() {
189
+ return this.#device.serialNumber;
190
+ }
191
+ get name() {
192
+ return this.#device.productName;
193
+ }
194
+ /**
195
+ * Create a new instance of `AdbDaemonWebUsbConnection` using a specified `USBDevice` instance
196
+ *
197
+ * @param device The `USBDevice` instance obtained elsewhere.
198
+ * @param filters The filters to use when searching for ADB interface. Defaults to {@link ADB_DEFAULT_DEVICE_FILTER}.
199
+ */
200
+ constructor(device, filters = [ADB_DEFAULT_DEVICE_FILTER], usbManager) {
201
+ this.#device = device;
202
+ this.#filters = filters;
203
+ this.#usbManager = usbManager;
204
+ }
205
+ /**
206
+ * Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
207
+ * @returns The pair of `AdbPacket` streams.
208
+ */
209
+ async connect() {
210
+ if (!this.#device.opened) {
211
+ await this.#device.open();
212
+ }
213
+ const { configuration, interface_, alternate } = findUsbAlternateInterface(this.#device, this.#filters);
214
+ if (this.#device.configuration?.configurationValue !==
215
+ configuration.configurationValue) {
216
+ // Note: Switching configuration is not supported on Windows,
217
+ // but Android devices should always expose ADB function at the first (default) configuration.
218
+ await this.#device.selectConfiguration(configuration.configurationValue);
219
+ }
220
+ if (!interface_.claimed) {
221
+ await this.#device.claimInterface(interface_.interfaceNumber);
222
+ }
223
+ if (interface_.alternate.alternateSetting !== alternate.alternateSetting) {
224
+ await this.#device.selectAlternateInterface(interface_.interfaceNumber, alternate.alternateSetting);
225
+ }
226
+ const { inEndpoint, outEndpoint } = findUsbEndpoints(alternate.endpoints);
227
+ return new AdbDaemonWebUsbConnectionStreams(this.#device, inEndpoint, outEndpoint, this.#usbManager);
228
+ }
229
+ }
230
+ //# sourceMappingURL=connection.js.map