@yume-chan/adb-daemon-webusb 0.0.20 → 0.0.22
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 +24 -0
- package/CHANGELOG.md +15 -1
- package/README.md +46 -130
- package/esm/device.d.ts +5 -2
- package/esm/device.d.ts.map +1 -1
- package/esm/device.js +93 -50
- package/esm/device.js.map +1 -1
- package/esm/manager.d.ts +7 -1
- package/esm/manager.d.ts.map +1 -1
- package/esm/manager.js +13 -7
- package/esm/manager.js.map +1 -1
- package/esm/utils.js.map +1 -1
- package/esm/watcher.d.ts +0 -2
- package/esm/watcher.d.ts.map +1 -1
- package/esm/watcher.js +6 -6
- package/esm/watcher.js.map +1 -1
- package/package.json +9 -9
- package/src/device.ts +138 -90
- package/src/manager.ts +36 -11
- package/src/utils.ts +3 -3
- package/src/watcher.ts +8 -8
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/connection.d.ts +0 -49
- package/esm/connection.d.ts.map +0 -1
- package/esm/connection.js +0 -230
- package/esm/connection.js.map +0 -1
package/esm/watcher.js
CHANGED
|
@@ -4,17 +4,17 @@ export class AdbDaemonWebUsbDeviceWatcher {
|
|
|
4
4
|
constructor(callback, usb) {
|
|
5
5
|
this.#callback = callback;
|
|
6
6
|
this.#usbManager = usb;
|
|
7
|
-
this.#usbManager.addEventListener("connect", this
|
|
8
|
-
this.#usbManager.addEventListener("disconnect", this
|
|
7
|
+
this.#usbManager.addEventListener("connect", this.#handleConnect);
|
|
8
|
+
this.#usbManager.addEventListener("disconnect", this.#handleDisconnect);
|
|
9
9
|
}
|
|
10
10
|
dispose() {
|
|
11
|
-
this.#usbManager.removeEventListener("connect", this
|
|
12
|
-
this.#usbManager.removeEventListener("disconnect", this
|
|
11
|
+
this.#usbManager.removeEventListener("connect", this.#handleConnect);
|
|
12
|
+
this.#usbManager.removeEventListener("disconnect", this.#handleDisconnect);
|
|
13
13
|
}
|
|
14
|
-
handleConnect = (e) => {
|
|
14
|
+
#handleConnect = (e) => {
|
|
15
15
|
this.#callback(e.device.serialNumber);
|
|
16
16
|
};
|
|
17
|
-
handleDisconnect = () => {
|
|
17
|
+
#handleDisconnect = () => {
|
|
18
18
|
this.#callback();
|
|
19
19
|
};
|
|
20
20
|
}
|
package/esm/watcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,4BAA4B;IACrC,SAAS,CAAqC;IAC9C,WAAW,CAAM;IAEjB,
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,4BAA4B;IACrC,SAAS,CAAqC;IAC9C,WAAW,CAAM;IAEjB,YAAY,QAA4C,EAAE,GAAQ;QAC9D,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QAEvB,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO;QACH,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACrE,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAChC,YAAY,EACZ,IAAI,CAAC,iBAAiB,CACzB,CAAC;IACN,CAAC;IAED,cAAc,GAAG,CAAC,CAAqB,EAAE,EAAE;QACvC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,iBAAiB,GAAG,GAAG,EAAE;QACrB,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC,CAAC;CACL"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yume-chan/adb-daemon-webusb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "Adb daemon transport connection for `@yume-chan/adb` using WebUSB API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webusb",
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"main": "esm/index.js",
|
|
26
26
|
"types": "esm/index.d.ts",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@types/w3c-web-usb": "^1.0.
|
|
29
|
-
"@yume-chan/adb": "^0.0.
|
|
30
|
-
"@yume-chan/stream-extra": "^0.0.
|
|
31
|
-
"@yume-chan/struct": "^0.0.
|
|
32
|
-
"tslib": "^2.
|
|
28
|
+
"@types/w3c-web-usb": "^1.0.10",
|
|
29
|
+
"@yume-chan/adb": "^0.0.22",
|
|
30
|
+
"@yume-chan/stream-extra": "^0.0.22",
|
|
31
|
+
"@yume-chan/struct": "^0.0.22",
|
|
32
|
+
"tslib": "^2.6.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@yume-chan/eslint-config": "^1.0.0",
|
|
36
36
|
"@yume-chan/tsconfig": "^1.0.0",
|
|
37
|
-
"eslint": "^8.
|
|
38
|
-
"prettier": "^
|
|
39
|
-
"typescript": "^5.
|
|
37
|
+
"eslint": "^8.55.0",
|
|
38
|
+
"prettier": "^3.1.0",
|
|
39
|
+
"typescript": "^5.3.2"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -b tsconfig.build.json",
|
package/src/device.ts
CHANGED
|
@@ -77,19 +77,19 @@ class Uint8ArrayExactReadable implements ExactReadable {
|
|
|
77
77
|
#data: Uint8Array;
|
|
78
78
|
#position: number;
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
get position() {
|
|
81
81
|
return this.#position;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
constructor(data: Uint8Array) {
|
|
85
85
|
this.#data = data;
|
|
86
86
|
this.#position = 0;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
readExactly(length: number): Uint8Array {
|
|
90
90
|
const result = this.#data.subarray(
|
|
91
91
|
this.#position,
|
|
92
|
-
this.#position + length
|
|
92
|
+
this.#position + length,
|
|
93
93
|
);
|
|
94
94
|
this.#position += length;
|
|
95
95
|
return result;
|
|
@@ -99,22 +99,41 @@ class Uint8ArrayExactReadable implements ExactReadable {
|
|
|
99
99
|
export class AdbDaemonWebUsbConnection
|
|
100
100
|
implements ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>
|
|
101
101
|
{
|
|
102
|
+
#device: AdbDaemonWebUsbDevice;
|
|
103
|
+
get device() {
|
|
104
|
+
return this.#device;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#inEndpoint: USBEndpoint;
|
|
108
|
+
get inEndpoint() {
|
|
109
|
+
return this.#inEndpoint;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#outEndpoint: USBEndpoint;
|
|
113
|
+
get outEndpoint() {
|
|
114
|
+
return this.#outEndpoint;
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
#readable: ReadableStream<AdbPacketData>;
|
|
103
|
-
|
|
118
|
+
get readable() {
|
|
104
119
|
return this.#readable;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
#writable: WritableStream<Consumable<AdbPacketInit>>;
|
|
108
|
-
|
|
123
|
+
get writable() {
|
|
109
124
|
return this.#writable;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
|
|
113
|
-
device:
|
|
127
|
+
constructor(
|
|
128
|
+
device: AdbDaemonWebUsbDevice,
|
|
114
129
|
inEndpoint: USBEndpoint,
|
|
115
130
|
outEndpoint: USBEndpoint,
|
|
116
|
-
usbManager: USB
|
|
131
|
+
usbManager: USB,
|
|
117
132
|
) {
|
|
133
|
+
this.#device = device;
|
|
134
|
+
this.#inEndpoint = inEndpoint;
|
|
135
|
+
this.#outEndpoint = outEndpoint;
|
|
136
|
+
|
|
118
137
|
let closed = false;
|
|
119
138
|
|
|
120
139
|
const duplex = new DuplexStreamFactory<
|
|
@@ -124,7 +143,7 @@ export class AdbDaemonWebUsbConnection
|
|
|
124
143
|
close: async () => {
|
|
125
144
|
try {
|
|
126
145
|
closed = true;
|
|
127
|
-
await device.close();
|
|
146
|
+
await device.raw.close();
|
|
128
147
|
} catch {
|
|
129
148
|
/* device may have already disconnected */
|
|
130
149
|
}
|
|
@@ -133,13 +152,13 @@ export class AdbDaemonWebUsbConnection
|
|
|
133
152
|
closed = true;
|
|
134
153
|
usbManager.removeEventListener(
|
|
135
154
|
"disconnect",
|
|
136
|
-
handleUsbDisconnect
|
|
155
|
+
handleUsbDisconnect,
|
|
137
156
|
);
|
|
138
157
|
},
|
|
139
158
|
});
|
|
140
159
|
|
|
141
160
|
function handleUsbDisconnect(e: USBConnectionEvent) {
|
|
142
|
-
if (e.device === device) {
|
|
161
|
+
if (e.device === device.raw) {
|
|
143
162
|
duplex.dispose().catch(unreachable);
|
|
144
163
|
}
|
|
145
164
|
}
|
|
@@ -147,63 +166,19 @@ export class AdbDaemonWebUsbConnection
|
|
|
147
166
|
usbManager.addEventListener("disconnect", handleUsbDisconnect);
|
|
148
167
|
|
|
149
168
|
this.#readable = duplex.wrapReadable(
|
|
150
|
-
new ReadableStream<AdbPacketData>(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const result = await device.transferIn(
|
|
157
|
-
inEndpoint.endpointNumber,
|
|
158
|
-
24
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
// TODO: webusb: handle `babble` by discarding the data and receive again
|
|
162
|
-
|
|
163
|
-
// Per spec, the `result.data` always covers the whole `buffer`.
|
|
164
|
-
const buffer = new Uint8Array(result.data!.buffer);
|
|
165
|
-
const stream = new Uint8ArrayExactReadable(buffer);
|
|
166
|
-
|
|
167
|
-
// Add `payload` field to its type, it's assigned below.
|
|
168
|
-
const packet = AdbPacketHeader.deserialize(
|
|
169
|
-
stream
|
|
170
|
-
) as AdbPacketHeader & { payload: Uint8Array };
|
|
171
|
-
if (packet.payloadLength !== 0) {
|
|
172
|
-
const result = await device.transferIn(
|
|
173
|
-
inEndpoint.endpointNumber,
|
|
174
|
-
packet.payloadLength
|
|
175
|
-
);
|
|
176
|
-
packet.payload = new Uint8Array(
|
|
177
|
-
result.data!.buffer
|
|
178
|
-
);
|
|
169
|
+
new ReadableStream<AdbPacketData>(
|
|
170
|
+
{
|
|
171
|
+
pull: async (controller) => {
|
|
172
|
+
const packet = await this.#transferIn();
|
|
173
|
+
if (packet) {
|
|
174
|
+
controller.enqueue(packet);
|
|
179
175
|
} else {
|
|
180
|
-
|
|
176
|
+
controller.close();
|
|
181
177
|
}
|
|
182
|
-
|
|
183
|
-
controller.enqueue(packet);
|
|
184
|
-
} catch (e) {
|
|
185
|
-
// On Windows, disconnecting the device will cause `NetworkError` to be thrown,
|
|
186
|
-
// even before the `disconnect` event is fired.
|
|
187
|
-
// We need to wait a little bit and check if the device is still connected.
|
|
188
|
-
// https://github.com/WICG/webusb/issues/219
|
|
189
|
-
if (isErrorName(e, "NetworkError")) {
|
|
190
|
-
await new Promise<void>((resolve) => {
|
|
191
|
-
setTimeout(() => {
|
|
192
|
-
resolve();
|
|
193
|
-
}, 100);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
if (closed) {
|
|
197
|
-
controller.close();
|
|
198
|
-
} else {
|
|
199
|
-
throw e;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
throw e;
|
|
204
|
-
}
|
|
178
|
+
},
|
|
205
179
|
},
|
|
206
|
-
|
|
180
|
+
{ highWaterMark: 0 },
|
|
181
|
+
),
|
|
207
182
|
);
|
|
208
183
|
|
|
209
184
|
const zeroMask = outEndpoint.packetSize - 1;
|
|
@@ -212,9 +187,9 @@ export class AdbDaemonWebUsbConnection
|
|
|
212
187
|
new ConsumableWritableStream({
|
|
213
188
|
write: async (chunk) => {
|
|
214
189
|
try {
|
|
215
|
-
await device.transferOut(
|
|
190
|
+
await device.raw.transferOut(
|
|
216
191
|
outEndpoint.endpointNumber,
|
|
217
|
-
chunk
|
|
192
|
+
chunk,
|
|
218
193
|
);
|
|
219
194
|
|
|
220
195
|
// In USB protocol, a not-full packet indicates the end of a transfer.
|
|
@@ -225,9 +200,9 @@ export class AdbDaemonWebUsbConnection
|
|
|
225
200
|
zeroMask &&
|
|
226
201
|
(chunk.byteLength & zeroMask) === 0
|
|
227
202
|
) {
|
|
228
|
-
await device.transferOut(
|
|
203
|
+
await device.raw.transferOut(
|
|
229
204
|
outEndpoint.endpointNumber,
|
|
230
|
-
EMPTY_UINT8_ARRAY
|
|
205
|
+
EMPTY_UINT8_ARRAY,
|
|
231
206
|
);
|
|
232
207
|
}
|
|
233
208
|
} catch (e) {
|
|
@@ -237,11 +212,72 @@ export class AdbDaemonWebUsbConnection
|
|
|
237
212
|
throw e;
|
|
238
213
|
}
|
|
239
214
|
},
|
|
240
|
-
})
|
|
215
|
+
}),
|
|
241
216
|
),
|
|
242
|
-
new AdbPacketSerializeStream()
|
|
217
|
+
new AdbPacketSerializeStream(),
|
|
243
218
|
);
|
|
244
219
|
}
|
|
220
|
+
|
|
221
|
+
async #transferIn(): Promise<AdbPacketData | undefined> {
|
|
222
|
+
try {
|
|
223
|
+
while (true) {
|
|
224
|
+
// ADB daemon sends each packet in two parts, the 24-byte header and the payload.
|
|
225
|
+
const result = await this.#device.raw.transferIn(
|
|
226
|
+
this.#inEndpoint.endpointNumber,
|
|
227
|
+
this.#inEndpoint.packetSize,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (result.data!.byteLength !== 24) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Per spec, the `result.data` always covers the whole `buffer`.
|
|
235
|
+
const buffer = new Uint8Array(result.data!.buffer);
|
|
236
|
+
const stream = new Uint8ArrayExactReadable(buffer);
|
|
237
|
+
|
|
238
|
+
// Add `payload` field to its type, it's assigned below.
|
|
239
|
+
const packet = AdbPacketHeader.deserialize(
|
|
240
|
+
stream,
|
|
241
|
+
) as AdbPacketHeader & { payload: Uint8Array };
|
|
242
|
+
|
|
243
|
+
if (packet.magic !== (packet.command ^ 0xffffffff)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (packet.payloadLength !== 0) {
|
|
248
|
+
const result = await this.#device.raw.transferIn(
|
|
249
|
+
this.#inEndpoint.endpointNumber,
|
|
250
|
+
packet.payloadLength,
|
|
251
|
+
);
|
|
252
|
+
packet.payload = new Uint8Array(result.data!.buffer);
|
|
253
|
+
} else {
|
|
254
|
+
packet.payload = EMPTY_UINT8_ARRAY;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return packet;
|
|
258
|
+
}
|
|
259
|
+
} catch (e) {
|
|
260
|
+
// On Windows, disconnecting the device will cause `NetworkError` to be thrown,
|
|
261
|
+
// even before the `disconnect` event is fired.
|
|
262
|
+
// We need to wait a little bit and check if the device is still connected.
|
|
263
|
+
// https://github.com/WICG/webusb/issues/219
|
|
264
|
+
if (isErrorName(e, "NetworkError")) {
|
|
265
|
+
await new Promise<void>((resolve) => {
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
resolve();
|
|
268
|
+
}, 100);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (closed) {
|
|
272
|
+
return undefined;
|
|
273
|
+
} else {
|
|
274
|
+
throw e;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
throw e;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
245
281
|
}
|
|
246
282
|
|
|
247
283
|
export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
|
|
@@ -249,15 +285,16 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
|
|
|
249
285
|
#usbManager: USB;
|
|
250
286
|
|
|
251
287
|
#raw: USBDevice;
|
|
252
|
-
|
|
288
|
+
get raw() {
|
|
253
289
|
return this.#raw;
|
|
254
290
|
}
|
|
255
291
|
|
|
256
|
-
|
|
257
|
-
|
|
292
|
+
#serial: string;
|
|
293
|
+
get serial(): string {
|
|
294
|
+
return this.#serial;
|
|
258
295
|
}
|
|
259
296
|
|
|
260
|
-
|
|
297
|
+
get name(): string {
|
|
261
298
|
return this.#raw.productName!;
|
|
262
299
|
}
|
|
263
300
|
|
|
@@ -267,23 +304,25 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
|
|
|
267
304
|
* @param device The `USBDevice` instance obtained elsewhere.
|
|
268
305
|
* @param filters The filters to use when searching for ADB interface. Defaults to {@link ADB_DEFAULT_DEVICE_FILTER}.
|
|
269
306
|
*/
|
|
270
|
-
|
|
307
|
+
constructor(
|
|
271
308
|
device: USBDevice,
|
|
272
309
|
filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER],
|
|
273
|
-
usbManager: USB
|
|
310
|
+
usbManager: USB,
|
|
274
311
|
) {
|
|
275
312
|
this.#raw = device;
|
|
313
|
+
if (device.serialNumber) {
|
|
314
|
+
this.#serial = device.serialNumber;
|
|
315
|
+
} else {
|
|
316
|
+
this.#serial =
|
|
317
|
+
device.vendorId.toString(16).padStart(4, "0") +
|
|
318
|
+
"x" +
|
|
319
|
+
device.productId.toString(16).padStart(4, "0");
|
|
320
|
+
}
|
|
276
321
|
this.#filters = filters;
|
|
277
322
|
this.#usbManager = usbManager;
|
|
278
323
|
}
|
|
279
324
|
|
|
280
|
-
|
|
281
|
-
* Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
|
|
282
|
-
* @returns The pair of `AdbPacket` streams.
|
|
283
|
-
*/
|
|
284
|
-
public async connect(): Promise<
|
|
285
|
-
ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>
|
|
286
|
-
> {
|
|
325
|
+
async #claimInterface(): Promise<[USBEndpoint, USBEndpoint]> {
|
|
287
326
|
if (!this.#raw.opened) {
|
|
288
327
|
await this.#raw.open();
|
|
289
328
|
}
|
|
@@ -298,7 +337,7 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
|
|
|
298
337
|
// Note: Switching configuration is not supported on Windows,
|
|
299
338
|
// but Android devices should always expose ADB function at the first (default) configuration.
|
|
300
339
|
await this.#raw.selectConfiguration(
|
|
301
|
-
configuration.configurationValue
|
|
340
|
+
configuration.configurationValue,
|
|
302
341
|
);
|
|
303
342
|
}
|
|
304
343
|
|
|
@@ -311,18 +350,27 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
|
|
|
311
350
|
) {
|
|
312
351
|
await this.#raw.selectAlternateInterface(
|
|
313
352
|
interface_.interfaceNumber,
|
|
314
|
-
alternate.alternateSetting
|
|
353
|
+
alternate.alternateSetting,
|
|
315
354
|
);
|
|
316
355
|
}
|
|
317
356
|
|
|
318
357
|
const { inEndpoint, outEndpoint } = findUsbEndpoints(
|
|
319
|
-
alternate.endpoints
|
|
358
|
+
alternate.endpoints,
|
|
320
359
|
);
|
|
360
|
+
return [inEndpoint, outEndpoint];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
|
|
365
|
+
* @returns The pair of `AdbPacket` streams.
|
|
366
|
+
*/
|
|
367
|
+
async connect(): Promise<AdbDaemonWebUsbConnection> {
|
|
368
|
+
const [inEndpoint, outEndpoint] = await this.#claimInterface();
|
|
321
369
|
return new AdbDaemonWebUsbConnection(
|
|
322
|
-
this
|
|
370
|
+
this,
|
|
323
371
|
inEndpoint,
|
|
324
372
|
outEndpoint,
|
|
325
|
-
this.#usbManager
|
|
373
|
+
this.#usbManager,
|
|
326
374
|
);
|
|
327
375
|
}
|
|
328
376
|
}
|
package/src/manager.ts
CHANGED
|
@@ -2,13 +2,20 @@ import { ADB_DEFAULT_DEVICE_FILTER, AdbDaemonWebUsbDevice } from "./device.js";
|
|
|
2
2
|
import type { AdbDeviceFilter } from "./utils.js";
|
|
3
3
|
import { findUsbAlternateInterface, isErrorName } from "./utils.js";
|
|
4
4
|
|
|
5
|
+
export namespace AdbDaemonWebUsbDeviceManager {
|
|
6
|
+
export interface RequestDeviceOptions {
|
|
7
|
+
filters?: AdbDeviceFilter[] | undefined;
|
|
8
|
+
exclusionFilters?: USBDeviceFilter[] | undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
export class AdbDaemonWebUsbDeviceManager {
|
|
6
13
|
/**
|
|
7
14
|
* Gets the instance of {@link AdbDaemonWebUsbDeviceManager} using browser WebUSB implementation.
|
|
8
15
|
*
|
|
9
16
|
* May be `undefined` if current runtime does not support WebUSB.
|
|
10
17
|
*/
|
|
11
|
-
|
|
18
|
+
static readonly BROWSER =
|
|
12
19
|
typeof globalThis.navigator !== "undefined" &&
|
|
13
20
|
!!globalThis.navigator.usb
|
|
14
21
|
? new AdbDaemonWebUsbDeviceManager(globalThis.navigator.usb)
|
|
@@ -20,7 +27,7 @@ export class AdbDaemonWebUsbDeviceManager {
|
|
|
20
27
|
* Create a new instance of {@link AdbDaemonWebUsbDeviceManager} using the specified WebUSB implementation.
|
|
21
28
|
* @param usbManager A WebUSB compatible interface.
|
|
22
29
|
*/
|
|
23
|
-
|
|
30
|
+
constructor(usbManager: USB) {
|
|
24
31
|
this.#usbManager = usbManager;
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -37,14 +44,24 @@ export class AdbDaemonWebUsbDeviceManager {
|
|
|
37
44
|
* @returns An {@link AdbDaemonWebUsbDevice} instance if the user selected a device,
|
|
38
45
|
* or `undefined` if the user cancelled the device picker.
|
|
39
46
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
async requestDevice(
|
|
48
|
+
options: AdbDaemonWebUsbDeviceManager.RequestDeviceOptions = {},
|
|
42
49
|
): Promise<AdbDaemonWebUsbDevice | undefined> {
|
|
50
|
+
if (!options.filters) {
|
|
51
|
+
options.filters = [ADB_DEFAULT_DEVICE_FILTER];
|
|
52
|
+
} else if (options.filters.length === 0) {
|
|
53
|
+
throw new TypeError("filters must not be empty");
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
try {
|
|
44
|
-
const device = await this.#usbManager.requestDevice(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return new AdbDaemonWebUsbDevice(
|
|
57
|
+
const device = await this.#usbManager.requestDevice(
|
|
58
|
+
options as USBDeviceRequestOptions,
|
|
59
|
+
);
|
|
60
|
+
return new AdbDaemonWebUsbDevice(
|
|
61
|
+
device,
|
|
62
|
+
options.filters,
|
|
63
|
+
this.#usbManager,
|
|
64
|
+
);
|
|
48
65
|
} catch (e) {
|
|
49
66
|
// No device selected
|
|
50
67
|
if (isErrorName(e, "NotFoundError")) {
|
|
@@ -67,9 +84,13 @@ export class AdbDaemonWebUsbDeviceManager {
|
|
|
67
84
|
* Defaults to {@link ADB_DEFAULT_DEVICE_FILTER}.
|
|
68
85
|
* @returns An array of {@link AdbDaemonWebUsbDevice} instances for all connected and authenticated devices.
|
|
69
86
|
*/
|
|
70
|
-
|
|
71
|
-
filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER]
|
|
87
|
+
async getDevices(
|
|
88
|
+
filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER],
|
|
72
89
|
): Promise<AdbDaemonWebUsbDevice[]> {
|
|
90
|
+
if (filters.length === 0) {
|
|
91
|
+
throw new TypeError("filters must not be empty");
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
const devices = await this.#usbManager.getDevices();
|
|
74
95
|
return devices
|
|
75
96
|
.filter((device) => {
|
|
@@ -104,7 +125,11 @@ export class AdbDaemonWebUsbDeviceManager {
|
|
|
104
125
|
})
|
|
105
126
|
.map(
|
|
106
127
|
(device) =>
|
|
107
|
-
new AdbDaemonWebUsbDevice(
|
|
128
|
+
new AdbDaemonWebUsbDevice(
|
|
129
|
+
device,
|
|
130
|
+
filters,
|
|
131
|
+
this.#usbManager,
|
|
132
|
+
),
|
|
108
133
|
);
|
|
109
134
|
}
|
|
110
135
|
}
|
package/src/utils.ts
CHANGED
|
@@ -18,19 +18,19 @@ export type AdbDeviceFilter = USBDeviceFilter &
|
|
|
18
18
|
|
|
19
19
|
function alternateMatchesFilter(
|
|
20
20
|
alternate: USBAlternateInterface,
|
|
21
|
-
filters: AdbDeviceFilter[]
|
|
21
|
+
filters: AdbDeviceFilter[],
|
|
22
22
|
) {
|
|
23
23
|
return filters.some(
|
|
24
24
|
(filter) =>
|
|
25
25
|
alternate.interfaceClass === filter.classCode &&
|
|
26
26
|
alternate.interfaceSubclass === filter.subclassCode &&
|
|
27
|
-
alternate.interfaceProtocol === filter.protocolCode
|
|
27
|
+
alternate.interfaceProtocol === filter.protocolCode,
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export function findUsbAlternateInterface(
|
|
32
32
|
device: USBDevice,
|
|
33
|
-
filters: AdbDeviceFilter[]
|
|
33
|
+
filters: AdbDeviceFilter[],
|
|
34
34
|
) {
|
|
35
35
|
for (const configuration of device.configurations) {
|
|
36
36
|
for (const interface_ of configuration.interfaces) {
|
package/src/watcher.ts
CHANGED
|
@@ -2,27 +2,27 @@ export class AdbDaemonWebUsbDeviceWatcher {
|
|
|
2
2
|
#callback: (newDeviceSerial?: string) => void;
|
|
3
3
|
#usbManager: USB;
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
constructor(callback: (newDeviceSerial?: string) => void, usb: USB) {
|
|
6
6
|
this.#callback = callback;
|
|
7
7
|
this.#usbManager = usb;
|
|
8
8
|
|
|
9
|
-
this.#usbManager.addEventListener("connect", this
|
|
10
|
-
this.#usbManager.addEventListener("disconnect", this
|
|
9
|
+
this.#usbManager.addEventListener("connect", this.#handleConnect);
|
|
10
|
+
this.#usbManager.addEventListener("disconnect", this.#handleDisconnect);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
this.#usbManager.removeEventListener("connect", this
|
|
13
|
+
dispose(): void {
|
|
14
|
+
this.#usbManager.removeEventListener("connect", this.#handleConnect);
|
|
15
15
|
this.#usbManager.removeEventListener(
|
|
16
16
|
"disconnect",
|
|
17
|
-
this
|
|
17
|
+
this.#handleDisconnect,
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
#handleConnect = (e: USBConnectionEvent) => {
|
|
22
22
|
this.#callback(e.device.serialNumber);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
#handleDisconnect = () => {
|
|
26
26
|
this.#callback();
|
|
27
27
|
};
|
|
28
28
|
}
|