@yume-chan/adb 0.0.23 → 1.0.0
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.md +51 -2
- package/LICENSE +1 -1
- package/README.md +4 -1
- package/esm/adb.d.ts +15 -9
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +5 -2
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts +7 -6
- package/esm/banner.d.ts.map +1 -1
- package/esm/banner.js +6 -7
- package/esm/banner.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +63 -34
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +35 -33
- package/esm/commands/framebuffer.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 +12 -12
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +60 -39
- 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/none.js +3 -5
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +22 -14
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +21 -27
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +8 -8
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/sync/list.d.ts +49 -30
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +9 -11
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +7 -9
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +4 -6
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +25 -22
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +14 -21
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.d.ts +20 -23
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +26 -35
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +25 -19
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +49 -26
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +3 -3
- 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 +75 -54
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +73 -69
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +18 -17
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +8 -8
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +0 -1
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +9 -9
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.d.ts +8 -7
- package/esm/daemon/auth.d.ts.map +1 -1
- package/esm/daemon/auth.js +8 -9
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.d.ts +4 -3
- package/esm/daemon/crypto.d.ts.map +1 -1
- package/esm/daemon/crypto.js +57 -49
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/device.d.ts +2 -2
- package/esm/daemon/device.d.ts.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 +46 -33
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +48 -32
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +36 -32
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +11 -11
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +19 -40
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +60 -20
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +12 -15
- package/esm/daemon/transport.js.map +1 -1
- package/esm/device-observer.d.ts +10 -0
- package/esm/device-observer.d.ts.map +1 -0
- package/esm/device-observer.js +2 -0
- package/esm/device-observer.js.map +1 -0
- package/esm/features.d.ts +12 -11
- package/esm/features.d.ts.map +1 -1
- package/esm/features.js +11 -12
- package/esm/features.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/server/client.d.ts +140 -50
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +396 -180
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.d.ts +2 -2
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js +4 -6
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/base64.d.ts.map +1 -1
- package/esm/utils/base64.js +52 -38
- 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/no-op.d.ts.map +1 -1
- package/esm/utils/no-op.js +1 -0
- package/esm/utils/no-op.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 +14 -19
- package/src/adb.ts +20 -11
- package/src/banner.ts +8 -6
- package/src/commands/framebuffer.ts +44 -35
- package/src/commands/power.ts +7 -5
- package/src/commands/reverse.ts +78 -47
- package/src/commands/subprocess/command.ts +3 -3
- package/src/commands/subprocess/protocols/none.ts +5 -7
- package/src/commands/subprocess/protocols/shell.ts +33 -35
- package/src/commands/subprocess/protocols/types.ts +8 -8
- package/src/commands/sync/list.ts +20 -15
- package/src/commands/sync/pull.ts +9 -8
- package/src/commands/sync/push.ts +25 -24
- package/src/commands/sync/request.ts +37 -38
- package/src/commands/sync/response.ts +63 -37
- package/src/commands/sync/socket.ts +13 -8
- package/src/commands/sync/stat.ts +93 -73
- package/src/commands/sync/sync.ts +12 -11
- package/src/commands/tcpip.ts +9 -9
- package/src/daemon/auth.ts +12 -10
- package/src/daemon/crypto.ts +66 -55
- package/src/daemon/device.ts +2 -2
- package/src/daemon/dispatcher.ts +106 -44
- package/src/daemon/packet.ts +57 -38
- package/src/daemon/socket.ts +30 -50
- package/src/daemon/transport.ts +98 -50
- package/src/device-observer.ts +10 -0
- package/src/features.ts +13 -11
- package/src/index.ts +1 -0
- package/src/server/client.ts +505 -268
- package/src/server/transport.ts +7 -9
- package/src/utils/base64.ts +59 -40
- package/src/utils/hex.ts +16 -14
- package/src/utils/index.ts +1 -1
- package/src/utils/no-op.ts +1 -0
- package/src/utils/sequence-equal.ts +13 -0
- package/tsconfig.build.json +1 -12
- package/tsconfig.build.tsbuildinfo +1 -1
- package/CHANGELOG.json +0 -265
- 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
package/src/server/client.ts
CHANGED
|
@@ -1,221 +1,127 @@
|
|
|
1
1
|
// cspell:ignore tport
|
|
2
2
|
|
|
3
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
3
4
|
import { PromiseResolver } from "@yume-chan/async";
|
|
5
|
+
import type { Event } from "@yume-chan/event";
|
|
6
|
+
import { EventEmitter } from "@yume-chan/event";
|
|
7
|
+
import { getUint64LittleEndian } from "@yume-chan/no-data-view";
|
|
4
8
|
import type {
|
|
5
9
|
AbortSignal,
|
|
10
|
+
MaybeConsumable,
|
|
6
11
|
ReadableWritablePair,
|
|
7
12
|
WritableStreamDefaultWriter,
|
|
8
13
|
} from "@yume-chan/stream-extra";
|
|
9
14
|
import {
|
|
10
15
|
BufferedReadableStream,
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
tryCancel,
|
|
17
|
+
tryClose,
|
|
13
18
|
} from "@yume-chan/stream-extra";
|
|
14
|
-
import type {
|
|
15
|
-
AsyncExactReadable,
|
|
16
|
-
ExactReadable,
|
|
17
|
-
ValueOrPromise,
|
|
18
|
-
} from "@yume-chan/struct";
|
|
19
19
|
import {
|
|
20
|
-
|
|
21
|
-
EMPTY_UINT8_ARRAY,
|
|
22
|
-
SyncPromise,
|
|
20
|
+
bipedal,
|
|
23
21
|
decodeUtf8,
|
|
24
22
|
encodeUtf8,
|
|
23
|
+
TextDecoder,
|
|
25
24
|
} from "@yume-chan/struct";
|
|
26
25
|
|
|
27
26
|
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
|
28
27
|
import { AdbBanner } from "../banner.js";
|
|
28
|
+
import type { DeviceObserver as DeviceObserverBase } from "../device-observer.js";
|
|
29
29
|
import type { AdbFeature } from "../features.js";
|
|
30
|
-
import {
|
|
30
|
+
import { hexToNumber, sequenceEqual, write4HexDigits } from "../utils/index.js";
|
|
31
31
|
|
|
32
32
|
import { AdbServerTransport } from "./transport.js";
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
signal?: AbortSignal | undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface AdbServerConnection
|
|
40
|
-
extends ReadableWritablePair<Uint8Array, Uint8Array>,
|
|
41
|
-
Closeable {
|
|
42
|
-
get closed(): Promise<void>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface AdbServerConnector {
|
|
46
|
-
connect(
|
|
47
|
-
options?: AdbServerConnectionOptions,
|
|
48
|
-
): ValueOrPromise<AdbServerConnection>;
|
|
34
|
+
const OKAY = encodeUtf8("OKAY");
|
|
35
|
+
const FAIL = encodeUtf8("FAIL");
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
class AdbServerStream {
|
|
38
|
+
#connection: AdbServerClient.ServerConnection;
|
|
39
|
+
#buffered: BufferedReadableStream;
|
|
40
|
+
#writer: WritableStreamDefaultWriter<Uint8Array>;
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
export interface AdbServerSocket extends AdbSocket {
|
|
61
|
-
transportId: bigint;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type AdbServerDeviceSelector =
|
|
65
|
-
| { transportId: bigint }
|
|
66
|
-
| { serial: string }
|
|
67
|
-
| { usb: true }
|
|
68
|
-
| { tcp: true }
|
|
69
|
-
| undefined;
|
|
70
|
-
|
|
71
|
-
export interface AdbServerDevice {
|
|
72
|
-
serial: string;
|
|
73
|
-
product?: string | undefined;
|
|
74
|
-
model?: string | undefined;
|
|
75
|
-
device?: string | undefined;
|
|
76
|
-
transportId: bigint;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export class AdbServerClient {
|
|
80
|
-
static readonly VERSION = 41;
|
|
81
|
-
|
|
82
|
-
readonly connection: AdbServerConnector;
|
|
83
|
-
|
|
84
|
-
constructor(connection: AdbServerConnector) {
|
|
85
|
-
this.connection = connection;
|
|
42
|
+
constructor(connection: AdbServerClient.ServerConnection) {
|
|
43
|
+
this.#connection = connection;
|
|
44
|
+
this.#buffered = new BufferedReadableStream(connection.readable);
|
|
45
|
+
this.#writer = connection.writable.getWriter();
|
|
86
46
|
}
|
|
87
47
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
static readString(
|
|
91
|
-
stream: ExactReadable | AsyncExactReadable,
|
|
92
|
-
): string | PromiseLike<string> {
|
|
93
|
-
return SyncPromise.try(() => stream.readExactly(4))
|
|
94
|
-
.then((buffer) => {
|
|
95
|
-
const length = hexToNumber(buffer);
|
|
96
|
-
if (length === 0) {
|
|
97
|
-
return EMPTY_UINT8_ARRAY;
|
|
98
|
-
} else {
|
|
99
|
-
return stream.readExactly(length);
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
.then((valueBuffer) => {
|
|
103
|
-
return decodeUtf8(valueBuffer);
|
|
104
|
-
})
|
|
105
|
-
.valueOrPromise();
|
|
48
|
+
readExactly(length: number): MaybePromiseLike<Uint8Array> {
|
|
49
|
+
return this.#buffered.readExactly(length);
|
|
106
50
|
}
|
|
107
51
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
52
|
+
readString = bipedal(function* (this: AdbServerStream, then) {
|
|
53
|
+
const data = yield* then(this.readExactly(4));
|
|
54
|
+
const length = hexToNumber(data);
|
|
55
|
+
if (length === 0) {
|
|
56
|
+
return "";
|
|
57
|
+
} else {
|
|
58
|
+
const decoder = new TextDecoder();
|
|
59
|
+
let result = "";
|
|
60
|
+
const iterator = this.#buffered.iterateExactly(length);
|
|
61
|
+
while (true) {
|
|
62
|
+
const { done, value } = iterator.next();
|
|
63
|
+
if (done) {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
result += decoder.decode(yield* then(value), { stream: true });
|
|
67
|
+
}
|
|
68
|
+
result += decoder.decode();
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
async writeString(value: string): Promise<void> {
|
|
74
|
+
// TODO: investigate using `encodeUtf8("0000" + value)` then modifying the length
|
|
75
|
+
// That way allocates a new string (hopefully only a rope) instead of a new buffer
|
|
76
|
+
const encoded = encodeUtf8(value);
|
|
77
|
+
const buffer = new Uint8Array(4 + encoded.length);
|
|
78
|
+
write4HexDigits(buffer, 0, encoded.length);
|
|
79
|
+
buffer.set(encoded, 4);
|
|
80
|
+
await this.#writer.write(buffer);
|
|
117
81
|
}
|
|
118
82
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
83
|
+
async readOkay(): Promise<void> {
|
|
84
|
+
const response = await this.readExactly(4);
|
|
85
|
+
if (sequenceEqual(response, OKAY)) {
|
|
86
|
+
// `OKAY` is followed by data length and data
|
|
87
|
+
// But different services want to parse the data differently
|
|
88
|
+
// So don't read the data here
|
|
124
89
|
return;
|
|
125
90
|
}
|
|
126
91
|
|
|
127
|
-
if (response
|
|
128
|
-
const reason = await
|
|
92
|
+
if (sequenceEqual(response, FAIL)) {
|
|
93
|
+
const reason = await this.readString();
|
|
129
94
|
throw new Error(reason);
|
|
130
95
|
}
|
|
131
96
|
|
|
132
|
-
throw new Error(`Unexpected response: ${response}`);
|
|
97
|
+
throw new Error(`Unexpected response: ${decodeUtf8(response)}`);
|
|
133
98
|
}
|
|
134
99
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
await AdbServerClient.writeString(writer, request);
|
|
144
|
-
writer.releaseLock();
|
|
145
|
-
} catch (e) {
|
|
146
|
-
await connection.readable.cancel();
|
|
147
|
-
await connection.close();
|
|
148
|
-
throw e;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
152
|
-
try {
|
|
153
|
-
// `raceSignal` throws when the signal is aborted,
|
|
154
|
-
// so the `catch` block can close the connection.
|
|
155
|
-
await raceSignal(
|
|
156
|
-
() => AdbServerClient.readOkay(readable),
|
|
157
|
-
options?.signal,
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
readable: readable.release(),
|
|
162
|
-
writable: connection.writable,
|
|
163
|
-
get closed() {
|
|
164
|
-
return connection.closed;
|
|
165
|
-
},
|
|
166
|
-
async close() {
|
|
167
|
-
await connection.close();
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
} catch (e) {
|
|
171
|
-
await readable.cancel().catch(NOOP);
|
|
172
|
-
await connection.close();
|
|
173
|
-
throw e;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async getVersion(): Promise<number> {
|
|
178
|
-
const connection = await this.connect("host:version");
|
|
179
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
180
|
-
try {
|
|
181
|
-
const length = hexToNumber(await readable.readExactly(4));
|
|
182
|
-
const version = hexToNumber(await readable.readExactly(length));
|
|
183
|
-
return version;
|
|
184
|
-
} finally {
|
|
185
|
-
connection.writable.close().catch(NOOP);
|
|
186
|
-
readable.cancel().catch(NOOP);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async validateVersion() {
|
|
191
|
-
const version = await this.getVersion();
|
|
192
|
-
if (version !== AdbServerClient.VERSION) {
|
|
193
|
-
throw new Error(
|
|
194
|
-
`adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`,
|
|
195
|
-
);
|
|
196
|
-
}
|
|
100
|
+
release() {
|
|
101
|
+
this.#writer.releaseLock();
|
|
102
|
+
return {
|
|
103
|
+
readable: this.#buffered.release(),
|
|
104
|
+
writable: this.#connection.writable,
|
|
105
|
+
closed: this.#connection.closed,
|
|
106
|
+
close: () => this.#connection.close(),
|
|
107
|
+
};
|
|
197
108
|
}
|
|
198
109
|
|
|
199
|
-
async
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
connection.
|
|
110
|
+
async dispose() {
|
|
111
|
+
void tryCancel(this.#buffered);
|
|
112
|
+
void tryClose(this.#writer);
|
|
113
|
+
await this.#connection.close();
|
|
203
114
|
}
|
|
115
|
+
}
|
|
204
116
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return response.split(",") as AdbFeature[];
|
|
211
|
-
} finally {
|
|
212
|
-
connection.writable.close().catch(NOOP);
|
|
213
|
-
readable.cancel().catch(NOOP);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
117
|
+
/**
|
|
118
|
+
* Client for the ADB Server.
|
|
119
|
+
*/
|
|
120
|
+
export class AdbServerClient {
|
|
121
|
+
static readonly VERSION = 41;
|
|
216
122
|
|
|
217
|
-
parseDeviceList(value: string):
|
|
218
|
-
const devices:
|
|
123
|
+
static parseDeviceList(value: string): AdbServerClient.Device[] {
|
|
124
|
+
const devices: AdbServerClient.Device[] = [];
|
|
219
125
|
for (const line of value.split("\n")) {
|
|
220
126
|
if (!line) {
|
|
221
127
|
continue;
|
|
@@ -224,7 +130,7 @@ export class AdbServerClient {
|
|
|
224
130
|
const parts = line.split(" ").filter(Boolean);
|
|
225
131
|
const serial = parts[0]!;
|
|
226
132
|
const status = parts[1]!;
|
|
227
|
-
if (status !== "device") {
|
|
133
|
+
if (status !== "device" && status !== "unauthorized") {
|
|
228
134
|
continue;
|
|
229
135
|
}
|
|
230
136
|
|
|
@@ -254,6 +160,7 @@ export class AdbServerClient {
|
|
|
254
160
|
}
|
|
255
161
|
devices.push({
|
|
256
162
|
serial,
|
|
163
|
+
authenticating: status === "unauthorized",
|
|
257
164
|
product,
|
|
258
165
|
model,
|
|
259
166
|
device,
|
|
@@ -263,43 +170,10 @@ export class AdbServerClient {
|
|
|
263
170
|
return devices;
|
|
264
171
|
}
|
|
265
172
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const response = await AdbServerClient.readString(readable);
|
|
271
|
-
return this.parseDeviceList(response);
|
|
272
|
-
} finally {
|
|
273
|
-
connection.writable.close().catch(NOOP);
|
|
274
|
-
readable.cancel().catch(NOOP);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async trackDevices(
|
|
279
|
-
callback: (devices: AdbServerDevice[]) => void,
|
|
280
|
-
): Promise<() => void> {
|
|
281
|
-
const connection = await this.connect("host:track-devices-l");
|
|
282
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
283
|
-
let running = true;
|
|
284
|
-
(async () => {
|
|
285
|
-
try {
|
|
286
|
-
while (running) {
|
|
287
|
-
const response = await AdbServerClient.readString(readable);
|
|
288
|
-
const devices = this.parseDeviceList(response);
|
|
289
|
-
callback(devices);
|
|
290
|
-
}
|
|
291
|
-
} catch {
|
|
292
|
-
// ignore
|
|
293
|
-
}
|
|
294
|
-
})().catch(unreachable);
|
|
295
|
-
return () => {
|
|
296
|
-
running = false;
|
|
297
|
-
readable.cancel().catch(NOOP);
|
|
298
|
-
Promise.resolve(connection.close()).catch(NOOP);
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
formatDeviceService(device: AdbServerDeviceSelector, command: string) {
|
|
173
|
+
static formatDeviceService(
|
|
174
|
+
device: AdbServerClient.DeviceSelector,
|
|
175
|
+
command: string,
|
|
176
|
+
) {
|
|
303
177
|
if (!device) {
|
|
304
178
|
return `host:${command}`;
|
|
305
179
|
}
|
|
@@ -315,7 +189,176 @@ export class AdbServerClient {
|
|
|
315
189
|
if ("tcp" in device) {
|
|
316
190
|
return `host-local:${command}`;
|
|
317
191
|
}
|
|
318
|
-
throw new
|
|
192
|
+
throw new TypeError("Invalid device selector");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
readonly connector: AdbServerClient.ServerConnector;
|
|
196
|
+
|
|
197
|
+
readonly wireless = new AdbServerClient.WirelessCommands(this);
|
|
198
|
+
readonly mDns = new AdbServerClient.MDnsCommands(this);
|
|
199
|
+
|
|
200
|
+
constructor(connector: AdbServerClient.ServerConnector) {
|
|
201
|
+
this.connector = connector;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async createConnection(
|
|
205
|
+
request: string,
|
|
206
|
+
options?: AdbServerClient.ServerConnectionOptions,
|
|
207
|
+
): Promise<AdbServerStream> {
|
|
208
|
+
const connection = await this.connector.connect(options);
|
|
209
|
+
const stream = new AdbServerStream(connection);
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
await stream.writeString(request);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
await stream.dispose();
|
|
215
|
+
throw e;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// `raceSignal` throws when the signal is aborted,
|
|
220
|
+
// so the `catch` block can close the connection.
|
|
221
|
+
await raceSignal(() => stream.readOkay(), options?.signal);
|
|
222
|
+
return stream;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
await stream.dispose();
|
|
225
|
+
throw e;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* `adb version`
|
|
231
|
+
*/
|
|
232
|
+
async getVersion(): Promise<number> {
|
|
233
|
+
const connection = await this.createConnection("host:version");
|
|
234
|
+
try {
|
|
235
|
+
const length = hexToNumber(await connection.readExactly(4));
|
|
236
|
+
const version = hexToNumber(await connection.readExactly(length));
|
|
237
|
+
return version;
|
|
238
|
+
} finally {
|
|
239
|
+
await connection.dispose();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async validateVersion() {
|
|
244
|
+
const version = await this.getVersion();
|
|
245
|
+
if (version !== AdbServerClient.VERSION) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* `adb kill-server`
|
|
254
|
+
*/
|
|
255
|
+
async killServer(): Promise<void> {
|
|
256
|
+
const connection = await this.createConnection("host:kill");
|
|
257
|
+
await connection.dispose();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* `adb host-features`
|
|
262
|
+
*/
|
|
263
|
+
async getServerFeatures(): Promise<AdbFeature[]> {
|
|
264
|
+
const connection = await this.createConnection("host:host-features");
|
|
265
|
+
try {
|
|
266
|
+
const response = await connection.readString();
|
|
267
|
+
return response.split(",") as AdbFeature[];
|
|
268
|
+
} finally {
|
|
269
|
+
await connection.dispose();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get a list of connected devices from ADB Server.
|
|
275
|
+
*
|
|
276
|
+
* Equivalent ADB Command: `adb devices -l`
|
|
277
|
+
*/
|
|
278
|
+
async getDevices(): Promise<AdbServerClient.Device[]> {
|
|
279
|
+
const connection = await this.createConnection("host:devices-l");
|
|
280
|
+
try {
|
|
281
|
+
const response = await connection.readString();
|
|
282
|
+
return AdbServerClient.parseDeviceList(response);
|
|
283
|
+
} finally {
|
|
284
|
+
await connection.dispose();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Monitors device list changes.
|
|
290
|
+
*/
|
|
291
|
+
async trackDevices(): Promise<AdbServerClient.DeviceObserver> {
|
|
292
|
+
const connection = await this.createConnection("host:track-devices-l");
|
|
293
|
+
|
|
294
|
+
let current: AdbServerClient.Device[] = [];
|
|
295
|
+
const onError = new EventEmitter<Error>();
|
|
296
|
+
const onDeviceAdd = new EventEmitter<AdbServerClient.Device[]>();
|
|
297
|
+
const onDeviceRemove = new EventEmitter<AdbServerClient.Device[]>();
|
|
298
|
+
const onListChange = new EventEmitter<AdbServerClient.Device[]>();
|
|
299
|
+
|
|
300
|
+
void (async () => {
|
|
301
|
+
try {
|
|
302
|
+
while (true) {
|
|
303
|
+
const response = await connection.readString();
|
|
304
|
+
const next = AdbServerClient.parseDeviceList(response);
|
|
305
|
+
|
|
306
|
+
const added: AdbServerClient.Device[] = [];
|
|
307
|
+
for (const nextDevice of next) {
|
|
308
|
+
const index = current.findIndex(
|
|
309
|
+
(device) =>
|
|
310
|
+
device.transportId === nextDevice.transportId,
|
|
311
|
+
);
|
|
312
|
+
if (index === -1) {
|
|
313
|
+
added.push(nextDevice);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
current[index] = current[current.length - 1]!;
|
|
318
|
+
current.length -= 1;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (added.length) {
|
|
322
|
+
onDeviceAdd.fire(added);
|
|
323
|
+
}
|
|
324
|
+
if (current.length) {
|
|
325
|
+
onDeviceRemove.fire(current);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
current = next;
|
|
329
|
+
onListChange.fire(current);
|
|
330
|
+
}
|
|
331
|
+
} catch (e) {
|
|
332
|
+
onError.fire(e as Error);
|
|
333
|
+
}
|
|
334
|
+
})();
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
onError: onError.event,
|
|
338
|
+
onDeviceAdd: onDeviceAdd.event,
|
|
339
|
+
onDeviceRemove: onDeviceRemove.event,
|
|
340
|
+
onListChange: onListChange.event,
|
|
341
|
+
get current() {
|
|
342
|
+
return current;
|
|
343
|
+
},
|
|
344
|
+
stop: () => connection.dispose(),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* `adb -s <device> reconnect` or `adb reconnect offline`
|
|
350
|
+
*/
|
|
351
|
+
async reconnectDevice(device: AdbServerClient.DeviceSelector | "offline") {
|
|
352
|
+
const connection = await this.createConnection(
|
|
353
|
+
device === "offline"
|
|
354
|
+
? "host:reconnect-offline"
|
|
355
|
+
: AdbServerClient.formatDeviceService(device, "reconnect"),
|
|
356
|
+
);
|
|
357
|
+
try {
|
|
358
|
+
await connection.readString();
|
|
359
|
+
} finally {
|
|
360
|
+
await connection.dispose();
|
|
361
|
+
}
|
|
319
362
|
}
|
|
320
363
|
|
|
321
364
|
/**
|
|
@@ -326,23 +369,36 @@ export class AdbServerClient {
|
|
|
326
369
|
* @returns The transport ID of the selected device, and the features supported by the device.
|
|
327
370
|
*/
|
|
328
371
|
async getDeviceFeatures(
|
|
329
|
-
device:
|
|
372
|
+
device: AdbServerClient.DeviceSelector,
|
|
330
373
|
): Promise<{ transportId: bigint; features: AdbFeature[] }> {
|
|
331
|
-
//
|
|
332
|
-
// so
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
374
|
+
// On paper, `host:features` is a host service (device features are cached in host),
|
|
375
|
+
// so it shouldn't use `createDeviceConnection`,
|
|
376
|
+
// which is used to forward the service to the device.
|
|
377
|
+
//
|
|
378
|
+
// However, `createDeviceConnection` is a two step process:
|
|
379
|
+
//
|
|
380
|
+
// 1. Send a switch device service to host, to switch the connection to the device.
|
|
381
|
+
// 2. Send the actual service to host, let it forward the service to the device.
|
|
382
|
+
//
|
|
383
|
+
// In step 2, the host only forward the service to device if the service is unknown to host.
|
|
384
|
+
// If the service is a host service, it's still handled by host.
|
|
385
|
+
//
|
|
386
|
+
// Even better, if the service needs a device selector, but the selector is not provided,
|
|
387
|
+
// the service will be executed against the device selected by the switch device service.
|
|
388
|
+
// So we can use all device selector formats for the host service,
|
|
389
|
+
// and get the transport ID in the same time.
|
|
390
|
+
const connection = await this.createDeviceConnection(
|
|
391
|
+
device,
|
|
392
|
+
"host:features",
|
|
393
|
+
);
|
|
394
|
+
// Luckily `AdbServerClient.Socket` is compatible with `AdbServerClient.ServerConnection`
|
|
395
|
+
const stream = new AdbServerStream(connection);
|
|
339
396
|
try {
|
|
340
|
-
const
|
|
341
|
-
const featuresString = await AdbServerClient.readString(readable);
|
|
397
|
+
const featuresString = await stream.readString();
|
|
342
398
|
const features = featuresString.split(",") as AdbFeature[];
|
|
343
|
-
return { transportId:
|
|
399
|
+
return { transportId: connection.transportId, features };
|
|
344
400
|
} finally {
|
|
345
|
-
await
|
|
401
|
+
await stream.dispose();
|
|
346
402
|
}
|
|
347
403
|
}
|
|
348
404
|
|
|
@@ -350,12 +406,12 @@ export class AdbServerClient {
|
|
|
350
406
|
* Creates a connection that will forward the service to device.
|
|
351
407
|
* @param device The device selector
|
|
352
408
|
* @param service The service to forward
|
|
353
|
-
* @returns An `
|
|
409
|
+
* @returns An `AdbServerClient.Socket` that can be used to communicate with the service
|
|
354
410
|
*/
|
|
355
|
-
async
|
|
356
|
-
device:
|
|
411
|
+
async createDeviceConnection(
|
|
412
|
+
device: AdbServerClient.DeviceSelector,
|
|
357
413
|
service: string,
|
|
358
|
-
): Promise<
|
|
414
|
+
): Promise<AdbServerClient.Socket> {
|
|
359
415
|
await this.validateVersion();
|
|
360
416
|
|
|
361
417
|
let switchService: string;
|
|
@@ -372,68 +428,60 @@ export class AdbServerClient {
|
|
|
372
428
|
} else if ("tcp" in device) {
|
|
373
429
|
switchService = `host:tport:local`;
|
|
374
430
|
} else {
|
|
375
|
-
throw new
|
|
431
|
+
throw new TypeError("Invalid device selector");
|
|
376
432
|
}
|
|
377
433
|
|
|
378
|
-
const connection = await this.
|
|
434
|
+
const connection = await this.createConnection(switchService);
|
|
379
435
|
|
|
380
436
|
try {
|
|
381
|
-
|
|
382
|
-
await AdbServerClient.writeString(writer, service);
|
|
383
|
-
writer.releaseLock();
|
|
437
|
+
await connection.writeString(service);
|
|
384
438
|
} catch (e) {
|
|
385
|
-
await connection.
|
|
386
|
-
await connection.close();
|
|
439
|
+
await connection.dispose();
|
|
387
440
|
throw e;
|
|
388
441
|
}
|
|
389
442
|
|
|
390
|
-
const readable = new BufferedReadableStream(connection.readable);
|
|
391
443
|
try {
|
|
392
444
|
if (transportId === undefined) {
|
|
393
|
-
const array = await
|
|
394
|
-
|
|
395
|
-
const dataView = new DataView(
|
|
396
|
-
array.buffer,
|
|
397
|
-
array.byteOffset,
|
|
398
|
-
array.byteLength,
|
|
399
|
-
);
|
|
400
|
-
transportId = BigIntFieldType.Uint64.getter(dataView, 0, true);
|
|
445
|
+
const array = await connection.readExactly(8);
|
|
446
|
+
transportId = getUint64LittleEndian(array, 0);
|
|
401
447
|
}
|
|
402
448
|
|
|
403
|
-
await
|
|
449
|
+
await connection.readOkay();
|
|
450
|
+
|
|
451
|
+
const socket = connection.release();
|
|
404
452
|
|
|
405
453
|
return {
|
|
406
454
|
transportId,
|
|
407
455
|
service,
|
|
408
|
-
readable: readable
|
|
409
|
-
writable:
|
|
410
|
-
connection.writable,
|
|
411
|
-
).bePipedThroughFrom(new UnwrapConsumableStream()),
|
|
456
|
+
readable: socket.readable,
|
|
457
|
+
writable: socket.writable,
|
|
412
458
|
get closed() {
|
|
413
|
-
return
|
|
459
|
+
return socket.closed;
|
|
414
460
|
},
|
|
415
461
|
async close() {
|
|
416
|
-
await
|
|
462
|
+
await socket.close();
|
|
417
463
|
},
|
|
418
464
|
};
|
|
419
465
|
} catch (e) {
|
|
420
|
-
await
|
|
421
|
-
await connection.close();
|
|
466
|
+
await connection.dispose();
|
|
422
467
|
throw e;
|
|
423
468
|
}
|
|
424
469
|
}
|
|
425
470
|
|
|
426
471
|
/**
|
|
427
472
|
* Wait for a device to be connected or disconnected.
|
|
473
|
+
*
|
|
474
|
+
* `adb wait-for-<state>`
|
|
475
|
+
*
|
|
428
476
|
* @param device The device selector
|
|
429
477
|
* @param state The state to wait for
|
|
430
478
|
* @param options The options
|
|
431
479
|
* @returns A promise that resolves when the condition is met.
|
|
432
480
|
*/
|
|
433
481
|
async waitFor(
|
|
434
|
-
device:
|
|
482
|
+
device: AdbServerClient.DeviceSelector,
|
|
435
483
|
state: "device" | "disconnect",
|
|
436
|
-
options?:
|
|
484
|
+
options?: AdbServerClient.ServerConnectionOptions,
|
|
437
485
|
): Promise<void> {
|
|
438
486
|
let type: string;
|
|
439
487
|
if (!device) {
|
|
@@ -447,26 +495,29 @@ export class AdbServerClient {
|
|
|
447
495
|
} else if ("tcp" in device) {
|
|
448
496
|
type = "local";
|
|
449
497
|
} else {
|
|
450
|
-
throw new
|
|
498
|
+
throw new TypeError("Invalid device selector");
|
|
451
499
|
}
|
|
452
500
|
|
|
453
501
|
// `waitFor` can't use `connectDevice`, because the device
|
|
454
502
|
// might not be available yet.
|
|
455
|
-
const service =
|
|
503
|
+
const service = AdbServerClient.formatDeviceService(
|
|
456
504
|
device,
|
|
457
505
|
`wait-for-${type}-${state}`,
|
|
458
506
|
);
|
|
459
507
|
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
508
|
+
const connection = await this.createConnection(service, options);
|
|
509
|
+
try {
|
|
510
|
+
await connection.readOkay();
|
|
511
|
+
} finally {
|
|
512
|
+
await connection.dispose();
|
|
513
|
+
}
|
|
466
514
|
}
|
|
467
515
|
|
|
516
|
+
/**
|
|
517
|
+
* Creates an ADB Transport for the specified device.
|
|
518
|
+
*/
|
|
468
519
|
async createTransport(
|
|
469
|
-
device:
|
|
520
|
+
device: AdbServerClient.DeviceSelector,
|
|
470
521
|
): Promise<AdbServerTransport> {
|
|
471
522
|
const { transportId, features } = await this.getDeviceFeatures(device);
|
|
472
523
|
|
|
@@ -492,7 +543,7 @@ export class AdbServerClient {
|
|
|
492
543
|
}
|
|
493
544
|
|
|
494
545
|
export async function raceSignal<T>(
|
|
495
|
-
callback: () =>
|
|
546
|
+
callback: () => PromiseLike<T>,
|
|
496
547
|
...signals: (AbortSignal | undefined)[]
|
|
497
548
|
): Promise<T> {
|
|
498
549
|
const abortPromise = new PromiseResolver<never>();
|
|
@@ -521,3 +572,189 @@ export async function raceSignal<T>(
|
|
|
521
572
|
}
|
|
522
573
|
}
|
|
523
574
|
}
|
|
575
|
+
|
|
576
|
+
export namespace AdbServerClient {
|
|
577
|
+
export interface ServerConnectionOptions {
|
|
578
|
+
unref?: boolean | undefined;
|
|
579
|
+
signal?: AbortSignal | undefined;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export interface ServerConnection
|
|
583
|
+
extends ReadableWritablePair<Uint8Array, MaybeConsumable<Uint8Array>>,
|
|
584
|
+
Closeable {
|
|
585
|
+
get closed(): Promise<void>;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export interface ServerConnector {
|
|
589
|
+
connect(
|
|
590
|
+
options?: ServerConnectionOptions,
|
|
591
|
+
): MaybePromiseLike<ServerConnection>;
|
|
592
|
+
|
|
593
|
+
addReverseTunnel(
|
|
594
|
+
handler: AdbIncomingSocketHandler,
|
|
595
|
+
address?: string,
|
|
596
|
+
): MaybePromiseLike<string>;
|
|
597
|
+
|
|
598
|
+
removeReverseTunnel(address: string): MaybePromiseLike<void>;
|
|
599
|
+
|
|
600
|
+
clearReverseTunnels(): MaybePromiseLike<void>;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export interface Socket extends AdbSocket {
|
|
604
|
+
transportId: bigint;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* A union type for selecting a device.
|
|
609
|
+
*/
|
|
610
|
+
export type DeviceSelector =
|
|
611
|
+
| { transportId: bigint }
|
|
612
|
+
| { serial: string }
|
|
613
|
+
| { usb: true }
|
|
614
|
+
| { tcp: true }
|
|
615
|
+
| undefined;
|
|
616
|
+
|
|
617
|
+
export interface Device {
|
|
618
|
+
serial: string;
|
|
619
|
+
authenticating: boolean;
|
|
620
|
+
product?: string | undefined;
|
|
621
|
+
model?: string | undefined;
|
|
622
|
+
device?: string | undefined;
|
|
623
|
+
transportId: bigint;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export class NetworkError extends Error {
|
|
627
|
+
constructor(message: string) {
|
|
628
|
+
super(message);
|
|
629
|
+
this.name = "NetworkError";
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export class UnauthorizedError extends Error {
|
|
634
|
+
constructor(message: string) {
|
|
635
|
+
super(message);
|
|
636
|
+
this.name = "UnauthorizedError";
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export class AlreadyConnectedError extends Error {
|
|
641
|
+
constructor(message: string) {
|
|
642
|
+
super(message);
|
|
643
|
+
this.name = "AlreadyConnectedError";
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export class WirelessCommands {
|
|
648
|
+
#client: AdbServerClient;
|
|
649
|
+
|
|
650
|
+
constructor(client: AdbServerClient) {
|
|
651
|
+
this.#client = client;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* `adb pair <password> <address>`
|
|
656
|
+
*/
|
|
657
|
+
async pair(address: string, password: string): Promise<void> {
|
|
658
|
+
const connection = await this.#client.createConnection(
|
|
659
|
+
`host:pair:${password}:${address}`,
|
|
660
|
+
);
|
|
661
|
+
try {
|
|
662
|
+
const response = await connection.readExactly(4);
|
|
663
|
+
// `response` is either `FAIL`, or 4 hex digits for length of the string
|
|
664
|
+
if (sequenceEqual(response, FAIL)) {
|
|
665
|
+
throw new Error(await connection.readString());
|
|
666
|
+
}
|
|
667
|
+
const length = hexToNumber(response);
|
|
668
|
+
// Ignore the string as it's always `Successful ...`
|
|
669
|
+
await connection.readExactly(length);
|
|
670
|
+
} finally {
|
|
671
|
+
await connection.dispose();
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* `adb connect <address>`
|
|
677
|
+
*/
|
|
678
|
+
async connect(address: string): Promise<void> {
|
|
679
|
+
const connection = await this.#client.createConnection(
|
|
680
|
+
`host:connect:${address}`,
|
|
681
|
+
);
|
|
682
|
+
try {
|
|
683
|
+
const response = await connection.readString();
|
|
684
|
+
switch (response) {
|
|
685
|
+
case `already connected to ${address}`:
|
|
686
|
+
throw new AdbServerClient.AlreadyConnectedError(
|
|
687
|
+
response,
|
|
688
|
+
);
|
|
689
|
+
case `failed to connect to ${address}`: // `adb pair` mode not authorized
|
|
690
|
+
case `failed to authenticate to ${address}`: // `adb tcpip` mode not authorized
|
|
691
|
+
throw new AdbServerClient.UnauthorizedError(response);
|
|
692
|
+
case `connected to ${address}`:
|
|
693
|
+
return;
|
|
694
|
+
default:
|
|
695
|
+
throw new AdbServerClient.NetworkError(response);
|
|
696
|
+
}
|
|
697
|
+
} finally {
|
|
698
|
+
await connection.dispose();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* `adb disconnect <address>`
|
|
704
|
+
*/
|
|
705
|
+
async disconnect(address: string): Promise<void> {
|
|
706
|
+
const connection = await this.#client.createConnection(
|
|
707
|
+
`host:disconnect:${address}`,
|
|
708
|
+
);
|
|
709
|
+
try {
|
|
710
|
+
await connection.readString();
|
|
711
|
+
} finally {
|
|
712
|
+
await connection.dispose();
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
export class MDnsCommands {
|
|
718
|
+
#client: AdbServerClient;
|
|
719
|
+
|
|
720
|
+
constructor(client: AdbServerClient) {
|
|
721
|
+
this.#client = client;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async check() {
|
|
725
|
+
const connection =
|
|
726
|
+
await this.#client.createConnection("host:mdns:check");
|
|
727
|
+
try {
|
|
728
|
+
const response = await connection.readString();
|
|
729
|
+
return !response.startsWith("ERROR:");
|
|
730
|
+
} finally {
|
|
731
|
+
await connection.dispose();
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
async getServices() {
|
|
736
|
+
const connection =
|
|
737
|
+
await this.#client.createConnection("host:mdns:services");
|
|
738
|
+
try {
|
|
739
|
+
const response = await connection.readString();
|
|
740
|
+
return response
|
|
741
|
+
.split("\n")
|
|
742
|
+
.filter(Boolean)
|
|
743
|
+
.map((line) => {
|
|
744
|
+
const parts = line.split("\t");
|
|
745
|
+
return {
|
|
746
|
+
name: parts[0]!,
|
|
747
|
+
service: parts[1]!,
|
|
748
|
+
address: parts[2]!,
|
|
749
|
+
};
|
|
750
|
+
});
|
|
751
|
+
} finally {
|
|
752
|
+
await connection.dispose();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export interface DeviceObserver extends DeviceObserverBase<Device> {
|
|
758
|
+
onError: Event<Error>;
|
|
759
|
+
}
|
|
760
|
+
}
|