@yume-chan/adb 0.0.23 → 0.0.24
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 +33 -0
- package/CHANGELOG.md +15 -1
- package/LICENSE +1 -1
- package/esm/adb.d.ts +2 -2
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +2 -2
- package/esm/adb.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +6 -5
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +22 -10
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.js +3 -3
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +2 -2
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +3 -3
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +5 -6
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +2 -2
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/sync/list.d.ts +2 -5
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -4
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +1 -4
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +2 -2
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +4 -7
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +3 -6
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.d.ts +13 -18
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +23 -28
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +18 -12
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +39 -17
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +2 -2
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +10 -5
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.d.ts +0 -3
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +0 -2
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +4 -3
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +4 -1
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.js +1 -1
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.js +1 -1
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.d.ts +3 -2
- package/esm/daemon/crypto.d.ts.map +1 -1
- package/esm/daemon/crypto.js +56 -49
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/dispatcher.d.ts +35 -10
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js +44 -31
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +2 -2
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +13 -12
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +10 -5
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +17 -15
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +55 -15
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +9 -10
- package/esm/daemon/transport.js.map +1 -1
- package/esm/server/client.d.ts +128 -50
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +325 -124
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.js +4 -4
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/base64.d.ts.map +1 -1
- package/esm/utils/base64.js +33 -22
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/hex.d.ts +1 -1
- package/esm/utils/hex.d.ts.map +1 -1
- package/esm/utils/hex.js +12 -14
- package/esm/utils/hex.js.map +1 -1
- package/esm/utils/index.d.ts +1 -1
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +1 -1
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/sequence-equal.d.ts +2 -0
- package/esm/utils/sequence-equal.d.ts.map +1 -0
- package/esm/utils/sequence-equal.js +12 -0
- package/esm/utils/sequence-equal.js.map +1 -0
- package/package.json +12 -14
- package/src/adb.ts +7 -4
- package/src/commands/power.ts +7 -5
- package/src/commands/reverse.ts +30 -17
- package/src/commands/subprocess/command.ts +3 -3
- package/src/commands/subprocess/protocols/none.ts +2 -2
- package/src/commands/subprocess/protocols/shell.ts +7 -11
- package/src/commands/subprocess/protocols/types.ts +2 -2
- package/src/commands/sync/list.ts +2 -4
- package/src/commands/sync/pull.ts +2 -2
- package/src/commands/sync/push.ts +7 -10
- package/src/commands/sync/request.ts +32 -32
- package/src/commands/sync/response.ts +47 -20
- package/src/commands/sync/socket.ts +13 -8
- package/src/commands/sync/stat.ts +0 -2
- package/src/commands/sync/sync.ts +8 -3
- package/src/commands/tcpip.ts +1 -1
- package/src/daemon/auth.ts +1 -1
- package/src/daemon/crypto.ts +64 -55
- package/src/daemon/dispatcher.ts +104 -42
- package/src/daemon/packet.ts +21 -14
- package/src/daemon/socket.ts +28 -25
- package/src/daemon/transport.ts +71 -24
- package/src/server/client.ts +420 -208
- package/src/server/transport.ts +4 -4
- package/src/utils/base64.ts +33 -22
- package/src/utils/hex.ts +16 -14
- package/src/utils/index.ts +1 -1
- package/src/utils/sequence-equal.ts +13 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/utils/conditional-variable.d.ts +0 -8
- package/esm/utils/conditional-variable.d.ts.map +0 -1
- package/esm/utils/conditional-variable.js +0 -34
- package/esm/utils/conditional-variable.js.map +0 -1
- package/src/utils/conditional-variable.ts +0 -45
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"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"}
|
package/esm/server/client.js
CHANGED
|
@@ -1,95 +1,144 @@
|
|
|
1
1
|
// cspell:ignore tport
|
|
2
2
|
import { PromiseResolver } from "@yume-chan/async";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getUint64LittleEndian } from "@yume-chan/no-data-view";
|
|
4
|
+
import { BufferedReadableStream, MaybeConsumable, WrapWritableStream, } from "@yume-chan/stream-extra";
|
|
5
|
+
import { EMPTY_UINT8_ARRAY, SyncPromise, decodeUtf8, encodeUtf8, } from "@yume-chan/struct";
|
|
5
6
|
import { AdbBanner } from "../banner.js";
|
|
6
|
-
import { NOOP, hexToNumber,
|
|
7
|
+
import { NOOP, hexToNumber, sequenceEqual, write4HexDigits, } from "../utils/index.js";
|
|
7
8
|
import { AdbServerTransport } from "./transport.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const OKAY = encodeUtf8("OKAY");
|
|
10
|
+
const FAIL = encodeUtf8("FAIL");
|
|
11
|
+
class AdbServerStream {
|
|
12
|
+
#connection;
|
|
13
|
+
#buffered;
|
|
14
|
+
#writer;
|
|
11
15
|
constructor(connection) {
|
|
12
|
-
this
|
|
16
|
+
this.#connection = connection;
|
|
17
|
+
this.#buffered = new BufferedReadableStream(connection.readable);
|
|
18
|
+
this.#writer = connection.writable.getWriter();
|
|
19
|
+
}
|
|
20
|
+
readExactly(length) {
|
|
21
|
+
return this.#buffered.readExactly(length);
|
|
13
22
|
}
|
|
14
|
-
|
|
15
|
-
return SyncPromise.try(() =>
|
|
23
|
+
readString() {
|
|
24
|
+
return SyncPromise.try(() => this.readExactly(4))
|
|
16
25
|
.then((buffer) => {
|
|
17
26
|
const length = hexToNumber(buffer);
|
|
18
27
|
if (length === 0) {
|
|
19
28
|
return EMPTY_UINT8_ARRAY;
|
|
20
29
|
}
|
|
21
30
|
else {
|
|
22
|
-
return
|
|
31
|
+
return this.readExactly(length);
|
|
23
32
|
}
|
|
24
33
|
})
|
|
25
|
-
.then((
|
|
26
|
-
|
|
34
|
+
.then((buffer) => {
|
|
35
|
+
// TODO: Investigate using stream mode `TextDecoder` for long strings.
|
|
36
|
+
// Because concatenating strings uses rope data structure,
|
|
37
|
+
// which only points to the original strings and doesn't copy the data,
|
|
38
|
+
// it's more efficient than concatenating `Uint8Array`s.
|
|
39
|
+
//
|
|
40
|
+
// ```
|
|
41
|
+
// const decoder = new TextDecoder();
|
|
42
|
+
// let result = '';
|
|
43
|
+
// for await (const chunk of stream.iterateExactly(length)) {
|
|
44
|
+
// result += decoder.decode(chunk, { stream: true });
|
|
45
|
+
// }
|
|
46
|
+
// result += decoder.decode();
|
|
47
|
+
// return result;
|
|
48
|
+
// ```
|
|
49
|
+
//
|
|
50
|
+
// Although, it will be super complex to use `SyncPromise` with async iterator,
|
|
51
|
+
// `stream.iterateExactly` need to return an
|
|
52
|
+
// `Iterator<Uint8Array | Promise<Uint8Array>>` instead of a true async iterator.
|
|
53
|
+
// Maybe `SyncPromise` should support async iterators directly.
|
|
54
|
+
return decodeUtf8(buffer);
|
|
27
55
|
})
|
|
28
56
|
.valueOrPromise();
|
|
29
57
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
buffer
|
|
35
|
-
|
|
58
|
+
async writeString(value) {
|
|
59
|
+
// TODO: investigate using `encodeUtf8("0000" + value)` then modifying the length
|
|
60
|
+
// That way allocates a new string (hopefully only a rope) instead of a new buffer
|
|
61
|
+
const encoded = encodeUtf8(value);
|
|
62
|
+
const buffer = new Uint8Array(4 + encoded.length);
|
|
63
|
+
write4HexDigits(buffer, 0, encoded.length);
|
|
64
|
+
buffer.set(encoded, 4);
|
|
65
|
+
await this.#writer.write(buffer);
|
|
36
66
|
}
|
|
37
|
-
|
|
38
|
-
const response =
|
|
39
|
-
if (response
|
|
67
|
+
async readOkay() {
|
|
68
|
+
const response = await this.readExactly(4);
|
|
69
|
+
if (sequenceEqual(response, OKAY)) {
|
|
70
|
+
// `OKAY` is followed by data length and data
|
|
71
|
+
// But different services want to parse the data differently
|
|
72
|
+
// So don't read the data here
|
|
40
73
|
return;
|
|
41
74
|
}
|
|
42
|
-
if (response
|
|
43
|
-
const reason = await
|
|
75
|
+
if (sequenceEqual(response, FAIL)) {
|
|
76
|
+
const reason = await this.readString();
|
|
44
77
|
throw new Error(reason);
|
|
45
78
|
}
|
|
46
|
-
throw new Error(`Unexpected response: ${response}`);
|
|
79
|
+
throw new Error(`Unexpected response: ${decodeUtf8(response)}`);
|
|
80
|
+
}
|
|
81
|
+
release() {
|
|
82
|
+
this.#writer.releaseLock();
|
|
83
|
+
return {
|
|
84
|
+
readable: this.#buffered.release(),
|
|
85
|
+
writable: this.#connection.writable,
|
|
86
|
+
closed: this.#connection.closed,
|
|
87
|
+
close: () => this.#connection.close(),
|
|
88
|
+
};
|
|
47
89
|
}
|
|
48
|
-
async
|
|
49
|
-
|
|
90
|
+
async dispose() {
|
|
91
|
+
await this.#buffered.cancel().catch(NOOP);
|
|
92
|
+
await this.#writer.close().catch(NOOP);
|
|
50
93
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
94
|
+
await this.#connection.close();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// ignore
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export class AdbServerClient {
|
|
102
|
+
static VERSION = 41;
|
|
103
|
+
connector;
|
|
104
|
+
wireless = new AdbServerClient.WirelessCommands(this);
|
|
105
|
+
mDns = new AdbServerClient.MDnsCommands(this);
|
|
106
|
+
constructor(connection) {
|
|
107
|
+
this.connector = connection;
|
|
108
|
+
}
|
|
109
|
+
async createConnection(request, options) {
|
|
110
|
+
const connection = await this.connector.connect(options);
|
|
111
|
+
const stream = new AdbServerStream(connection);
|
|
112
|
+
try {
|
|
113
|
+
await stream.writeString(request);
|
|
54
114
|
}
|
|
55
115
|
catch (e) {
|
|
56
|
-
await
|
|
57
|
-
await connection.close();
|
|
116
|
+
await stream.dispose();
|
|
58
117
|
throw e;
|
|
59
118
|
}
|
|
60
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
61
119
|
try {
|
|
62
120
|
// `raceSignal` throws when the signal is aborted,
|
|
63
121
|
// so the `catch` block can close the connection.
|
|
64
|
-
await raceSignal(() =>
|
|
65
|
-
return
|
|
66
|
-
readable: readable.release(),
|
|
67
|
-
writable: connection.writable,
|
|
68
|
-
get closed() {
|
|
69
|
-
return connection.closed;
|
|
70
|
-
},
|
|
71
|
-
async close() {
|
|
72
|
-
await connection.close();
|
|
73
|
-
},
|
|
74
|
-
};
|
|
122
|
+
await raceSignal(() => stream.readOkay(), options?.signal);
|
|
123
|
+
return stream;
|
|
75
124
|
}
|
|
76
125
|
catch (e) {
|
|
77
|
-
await
|
|
78
|
-
await connection.close();
|
|
126
|
+
await stream.dispose();
|
|
79
127
|
throw e;
|
|
80
128
|
}
|
|
81
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* `adb version`
|
|
132
|
+
*/
|
|
82
133
|
async getVersion() {
|
|
83
|
-
const connection = await this.
|
|
84
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
134
|
+
const connection = await this.createConnection("host:version");
|
|
85
135
|
try {
|
|
86
|
-
const length = hexToNumber(await
|
|
87
|
-
const version = hexToNumber(await
|
|
136
|
+
const length = hexToNumber(await connection.readExactly(4));
|
|
137
|
+
const version = hexToNumber(await connection.readExactly(length));
|
|
88
138
|
return version;
|
|
89
139
|
}
|
|
90
140
|
finally {
|
|
91
|
-
connection.
|
|
92
|
-
readable.cancel().catch(NOOP);
|
|
141
|
+
await connection.dispose();
|
|
93
142
|
}
|
|
94
143
|
}
|
|
95
144
|
async validateVersion() {
|
|
@@ -98,21 +147,24 @@ export class AdbServerClient {
|
|
|
98
147
|
throw new Error(`adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`);
|
|
99
148
|
}
|
|
100
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* `adb kill-server`
|
|
152
|
+
*/
|
|
101
153
|
async killServer() {
|
|
102
|
-
const connection = await this.
|
|
103
|
-
connection.
|
|
104
|
-
connection.readable.cancel().catch(NOOP);
|
|
154
|
+
const connection = await this.createConnection("host:kill");
|
|
155
|
+
await connection.dispose();
|
|
105
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* `adb host-features`
|
|
159
|
+
*/
|
|
106
160
|
async getServerFeatures() {
|
|
107
|
-
const connection = await this.
|
|
108
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
161
|
+
const connection = await this.createConnection("host:host-features");
|
|
109
162
|
try {
|
|
110
|
-
const response = await
|
|
163
|
+
const response = await connection.readString();
|
|
111
164
|
return response.split(",");
|
|
112
165
|
}
|
|
113
166
|
finally {
|
|
114
|
-
connection.
|
|
115
|
-
readable.cancel().catch(NOOP);
|
|
167
|
+
await connection.dispose();
|
|
116
168
|
}
|
|
117
169
|
}
|
|
118
170
|
parseDeviceList(value) {
|
|
@@ -124,7 +176,7 @@ export class AdbServerClient {
|
|
|
124
176
|
const parts = line.split(" ").filter(Boolean);
|
|
125
177
|
const serial = parts[0];
|
|
126
178
|
const status = parts[1];
|
|
127
|
-
if (status !== "device") {
|
|
179
|
+
if (status !== "device" && status !== "unauthorized") {
|
|
128
180
|
continue;
|
|
129
181
|
}
|
|
130
182
|
let product;
|
|
@@ -153,6 +205,7 @@ export class AdbServerClient {
|
|
|
153
205
|
}
|
|
154
206
|
devices.push({
|
|
155
207
|
serial,
|
|
208
|
+
authenticating: status === "unauthorized",
|
|
156
209
|
product,
|
|
157
210
|
model,
|
|
158
211
|
device,
|
|
@@ -161,39 +214,43 @@ export class AdbServerClient {
|
|
|
161
214
|
}
|
|
162
215
|
return devices;
|
|
163
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* `adb devices -l`
|
|
219
|
+
*/
|
|
164
220
|
async getDevices() {
|
|
165
|
-
const connection = await this.
|
|
166
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
221
|
+
const connection = await this.createConnection("host:devices-l");
|
|
167
222
|
try {
|
|
168
|
-
const response = await
|
|
223
|
+
const response = await connection.readString();
|
|
169
224
|
return this.parseDeviceList(response);
|
|
170
225
|
}
|
|
171
226
|
finally {
|
|
172
|
-
connection.
|
|
173
|
-
readable.cancel().catch(NOOP);
|
|
227
|
+
await connection.dispose();
|
|
174
228
|
}
|
|
175
229
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Track the device list.
|
|
232
|
+
*
|
|
233
|
+
* @param signal An optional `AbortSignal` to stop tracking
|
|
234
|
+
*
|
|
235
|
+
* When `signal` is aborted, `trackDevices` will return normally, instead of throwing `signal.reason`.
|
|
236
|
+
*/
|
|
237
|
+
async *trackDevices(signal) {
|
|
238
|
+
const connection = await this.createConnection("host:track-devices-l");
|
|
239
|
+
try {
|
|
240
|
+
while (true) {
|
|
241
|
+
const response = await raceSignal(async () => await connection.readString(), signal);
|
|
242
|
+
const devices = this.parseDeviceList(response);
|
|
243
|
+
yield devices;
|
|
187
244
|
}
|
|
188
|
-
|
|
189
|
-
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
if (e === signal?.reason) {
|
|
248
|
+
return;
|
|
190
249
|
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Promise.resolve(connection.close()).catch(NOOP);
|
|
196
|
-
};
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
await connection.dispose();
|
|
253
|
+
}
|
|
197
254
|
}
|
|
198
255
|
formatDeviceService(device, command) {
|
|
199
256
|
if (!device) {
|
|
@@ -211,7 +268,21 @@ export class AdbServerClient {
|
|
|
211
268
|
if ("tcp" in device) {
|
|
212
269
|
return `host-local:${command}`;
|
|
213
270
|
}
|
|
214
|
-
throw new
|
|
271
|
+
throw new TypeError("Invalid device selector");
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* `adb -s <device> reconnect` or `adb reconnect offline`
|
|
275
|
+
*/
|
|
276
|
+
async reconnectDevice(device) {
|
|
277
|
+
const connection = await this.createConnection(device === "offline"
|
|
278
|
+
? "host:reconnect-offline"
|
|
279
|
+
: this.formatDeviceService(device, "reconnect"));
|
|
280
|
+
try {
|
|
281
|
+
await connection.readString();
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
await connection.dispose();
|
|
285
|
+
}
|
|
215
286
|
}
|
|
216
287
|
/**
|
|
217
288
|
* Gets the features supported by the device.
|
|
@@ -221,31 +292,41 @@ export class AdbServerClient {
|
|
|
221
292
|
* @returns The transport ID of the selected device, and the features supported by the device.
|
|
222
293
|
*/
|
|
223
294
|
async getDeviceFeatures(device) {
|
|
224
|
-
//
|
|
225
|
-
// so
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
|
|
295
|
+
// On paper, `host:features` is a host service (device features are cached in host),
|
|
296
|
+
// so it shouldn't use `createDeviceConnection`,
|
|
297
|
+
// which is used to forward the service to the device.
|
|
298
|
+
//
|
|
299
|
+
// However, `createDeviceConnection` is a two step process:
|
|
300
|
+
//
|
|
301
|
+
// 1. Send a switch device service to host, to switch the connection to the device.
|
|
302
|
+
// 2. Send the actual service to host, let it forward the service to the device.
|
|
303
|
+
//
|
|
304
|
+
// In step 2, the host only forward the service to device if the service is unknown to host.
|
|
305
|
+
// If the service is a host service, it's still handled by host.
|
|
306
|
+
//
|
|
307
|
+
// Even better, if the service needs a device selector, but the selector is not provided,
|
|
308
|
+
// the service will be executed against the device selected by the switch device service.
|
|
309
|
+
// So we can use all device selector formats for the host service,
|
|
310
|
+
// and get the transport ID in the same time.
|
|
311
|
+
const connection = await this.createDeviceConnection(device, "host:features");
|
|
312
|
+
// Luckily `AdbServerClient.Socket` is compatible with `AdbServerClient.ServerConnection`
|
|
313
|
+
const stream = new AdbServerStream(connection);
|
|
232
314
|
try {
|
|
233
|
-
const
|
|
234
|
-
const featuresString = await AdbServerClient.readString(readable);
|
|
315
|
+
const featuresString = await stream.readString();
|
|
235
316
|
const features = featuresString.split(",");
|
|
236
|
-
return { transportId:
|
|
317
|
+
return { transportId: connection.transportId, features };
|
|
237
318
|
}
|
|
238
319
|
finally {
|
|
239
|
-
await
|
|
320
|
+
await stream.dispose();
|
|
240
321
|
}
|
|
241
322
|
}
|
|
242
323
|
/**
|
|
243
324
|
* Creates a connection that will forward the service to device.
|
|
244
325
|
* @param device The device selector
|
|
245
326
|
* @param service The service to forward
|
|
246
|
-
* @returns An `
|
|
327
|
+
* @returns An `AdbServerClient.Socket` that can be used to communicate with the service
|
|
247
328
|
*/
|
|
248
|
-
async
|
|
329
|
+
async createDeviceConnection(device, service) {
|
|
249
330
|
await this.validateVersion();
|
|
250
331
|
let switchService;
|
|
251
332
|
let transportId;
|
|
@@ -266,49 +347,46 @@ export class AdbServerClient {
|
|
|
266
347
|
switchService = `host:tport:local`;
|
|
267
348
|
}
|
|
268
349
|
else {
|
|
269
|
-
throw new
|
|
350
|
+
throw new TypeError("Invalid device selector");
|
|
270
351
|
}
|
|
271
|
-
const connection = await this.
|
|
352
|
+
const connection = await this.createConnection(switchService);
|
|
272
353
|
try {
|
|
273
|
-
|
|
274
|
-
await AdbServerClient.writeString(writer, service);
|
|
275
|
-
writer.releaseLock();
|
|
354
|
+
await connection.writeString(service);
|
|
276
355
|
}
|
|
277
356
|
catch (e) {
|
|
278
|
-
await connection.
|
|
279
|
-
await connection.close();
|
|
357
|
+
await connection.dispose();
|
|
280
358
|
throw e;
|
|
281
359
|
}
|
|
282
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
283
360
|
try {
|
|
284
361
|
if (transportId === undefined) {
|
|
285
|
-
const array = await
|
|
286
|
-
|
|
287
|
-
const dataView = new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
288
|
-
transportId = BigIntFieldType.Uint64.getter(dataView, 0, true);
|
|
362
|
+
const array = await connection.readExactly(8);
|
|
363
|
+
transportId = getUint64LittleEndian(array, 0);
|
|
289
364
|
}
|
|
290
|
-
await
|
|
365
|
+
await connection.readOkay();
|
|
366
|
+
const socket = connection.release();
|
|
291
367
|
return {
|
|
292
368
|
transportId,
|
|
293
369
|
service,
|
|
294
|
-
readable: readable
|
|
295
|
-
writable: new WrapWritableStream(
|
|
370
|
+
readable: socket.readable,
|
|
371
|
+
writable: new WrapWritableStream(socket.writable).bePipedThroughFrom(new MaybeConsumable.UnwrapStream()),
|
|
296
372
|
get closed() {
|
|
297
|
-
return
|
|
373
|
+
return socket.closed;
|
|
298
374
|
},
|
|
299
375
|
async close() {
|
|
300
|
-
await
|
|
376
|
+
await socket.close();
|
|
301
377
|
},
|
|
302
378
|
};
|
|
303
379
|
}
|
|
304
380
|
catch (e) {
|
|
305
|
-
await
|
|
306
|
-
await connection.close();
|
|
381
|
+
await connection.dispose();
|
|
307
382
|
throw e;
|
|
308
383
|
}
|
|
309
384
|
}
|
|
310
385
|
/**
|
|
311
386
|
* Wait for a device to be connected or disconnected.
|
|
387
|
+
*
|
|
388
|
+
* `adb wait-for-<state>`
|
|
389
|
+
*
|
|
312
390
|
* @param device The device selector
|
|
313
391
|
* @param state The state to wait for
|
|
314
392
|
* @param options The options
|
|
@@ -332,16 +410,18 @@ export class AdbServerClient {
|
|
|
332
410
|
type = "local";
|
|
333
411
|
}
|
|
334
412
|
else {
|
|
335
|
-
throw new
|
|
413
|
+
throw new TypeError("Invalid device selector");
|
|
336
414
|
}
|
|
337
415
|
// `waitFor` can't use `connectDevice`, because the device
|
|
338
416
|
// might not be available yet.
|
|
339
417
|
const service = this.formatDeviceService(device, `wait-for-${type}-${state}`);
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
418
|
+
const connection = await this.createConnection(service, options);
|
|
419
|
+
try {
|
|
420
|
+
await connection.readOkay();
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
await connection.dispose();
|
|
424
|
+
}
|
|
345
425
|
}
|
|
346
426
|
async createTransport(device) {
|
|
347
427
|
const { transportId, features } = await this.getDeviceFeatures(device);
|
|
@@ -377,4 +457,125 @@ export async function raceSignal(callback, ...signals) {
|
|
|
377
457
|
}
|
|
378
458
|
}
|
|
379
459
|
}
|
|
460
|
+
(function (AdbServerClient) {
|
|
461
|
+
class NetworkError extends Error {
|
|
462
|
+
constructor(message) {
|
|
463
|
+
super(message);
|
|
464
|
+
this.name = "NetworkError";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
AdbServerClient.NetworkError = NetworkError;
|
|
468
|
+
class UnauthorizedError extends Error {
|
|
469
|
+
constructor(message) {
|
|
470
|
+
super(message);
|
|
471
|
+
this.name = "UnauthorizedError";
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
AdbServerClient.UnauthorizedError = UnauthorizedError;
|
|
475
|
+
class AlreadyConnectedError extends Error {
|
|
476
|
+
constructor(message) {
|
|
477
|
+
super(message);
|
|
478
|
+
this.name = "AlreadyConnectedError";
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
AdbServerClient.AlreadyConnectedError = AlreadyConnectedError;
|
|
482
|
+
class WirelessCommands {
|
|
483
|
+
#client;
|
|
484
|
+
constructor(client) {
|
|
485
|
+
this.#client = client;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* `adb pair <password> <address>`
|
|
489
|
+
*/
|
|
490
|
+
async pair(address, password) {
|
|
491
|
+
const connection = await this.#client.createConnection(`host:pair:${password}:${address}`);
|
|
492
|
+
try {
|
|
493
|
+
const response = await connection.readExactly(4);
|
|
494
|
+
// `response` is either `FAIL`, or 4 hex digits for length of the string
|
|
495
|
+
if (sequenceEqual(response, FAIL)) {
|
|
496
|
+
throw new Error(await connection.readString());
|
|
497
|
+
}
|
|
498
|
+
const length = hexToNumber(response);
|
|
499
|
+
// Ignore the string as it's always `Successful ...`
|
|
500
|
+
await connection.readExactly(length);
|
|
501
|
+
}
|
|
502
|
+
finally {
|
|
503
|
+
await connection.dispose();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* `adb connect <address>`
|
|
508
|
+
*/
|
|
509
|
+
async connect(address) {
|
|
510
|
+
const connection = await this.#client.createConnection(`host:connect:${address}`);
|
|
511
|
+
try {
|
|
512
|
+
const response = await connection.readString();
|
|
513
|
+
switch (response) {
|
|
514
|
+
case `already connected to ${address}`:
|
|
515
|
+
throw new AdbServerClient.AlreadyConnectedError(response);
|
|
516
|
+
case `failed to connect to ${address}`: // `adb pair` mode not authorized
|
|
517
|
+
case `failed to authenticate to ${address}`: // `adb tcpip` mode not authorized
|
|
518
|
+
throw new AdbServerClient.UnauthorizedError(response);
|
|
519
|
+
case `connected to ${address}`:
|
|
520
|
+
return;
|
|
521
|
+
default:
|
|
522
|
+
throw new AdbServerClient.NetworkError(response);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
finally {
|
|
526
|
+
await connection.dispose();
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* `adb disconnect <address>`
|
|
531
|
+
*/
|
|
532
|
+
async disconnect(address) {
|
|
533
|
+
const connection = await this.#client.createConnection(`host:disconnect:${address}`);
|
|
534
|
+
try {
|
|
535
|
+
await connection.readString();
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
await connection.dispose();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
AdbServerClient.WirelessCommands = WirelessCommands;
|
|
543
|
+
class MDnsCommands {
|
|
544
|
+
#client;
|
|
545
|
+
constructor(client) {
|
|
546
|
+
this.#client = client;
|
|
547
|
+
}
|
|
548
|
+
async check() {
|
|
549
|
+
const connection = await this.#client.createConnection("host:mdns:check");
|
|
550
|
+
try {
|
|
551
|
+
const response = await connection.readString();
|
|
552
|
+
return !response.startsWith("ERROR:");
|
|
553
|
+
}
|
|
554
|
+
finally {
|
|
555
|
+
await connection.dispose();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async getServices() {
|
|
559
|
+
const connection = await this.#client.createConnection("host:mdns:services");
|
|
560
|
+
try {
|
|
561
|
+
const response = await connection.readString();
|
|
562
|
+
return response
|
|
563
|
+
.split("\n")
|
|
564
|
+
.filter(Boolean)
|
|
565
|
+
.map((line) => {
|
|
566
|
+
const parts = line.split("\t");
|
|
567
|
+
return {
|
|
568
|
+
name: parts[0],
|
|
569
|
+
service: parts[1],
|
|
570
|
+
address: parts[2],
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
finally {
|
|
575
|
+
await connection.dispose();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
AdbServerClient.MDnsCommands = MDnsCommands;
|
|
580
|
+
})(AdbServerClient || (AdbServerClient = {}));
|
|
380
581
|
//# sourceMappingURL=client.js.map
|