@yume-chan/adb 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 +45 -0
- package/CHANGELOG.md +22 -1
- package/README.md +46 -364
- package/esm/adb.d.ts +9 -5
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +21 -11
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts.map +1 -1
- package/esm/banner.js +1 -1
- package/esm/banner.js.map +1 -1
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +9 -0
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +27 -3
- package/esm/commands/framebuffer.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.d.ts +1 -1
- package/esm/commands/subprocess/command.d.ts.map +1 -1
- package/esm/commands/subprocess/command.js +15 -13
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +2 -1
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +11 -14
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +21 -17
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/utils.js.map +1 -1
- package/esm/commands/sync/list.d.ts +2 -1
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -2
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +4 -0
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.js +1 -1
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +1 -1
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +3 -1
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +5 -3
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +1 -1
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +7 -3
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.js +2 -2
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +15 -0
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +17 -2
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +21 -0
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +18 -0
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.d.ts +11 -9
- package/esm/daemon/auth.d.ts.map +1 -1
- package/esm/daemon/auth.js +21 -12
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.js +2 -2
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/dispatcher.d.ts +6 -5
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js +69 -58
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +5 -5
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +2 -2
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +7 -14
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +58 -46
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +16 -4
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +11 -7
- package/esm/daemon/transport.js.map +1 -1
- package/esm/features.js +1 -1
- package/esm/features.js.map +1 -1
- package/esm/server/client.d.ts +12 -8
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +41 -22
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts.map +1 -1
- package/esm/utils/conditional-variable.js.map +1 -1
- package/esm/utils/hex.js.map +1 -1
- package/package.json +13 -14
- package/src/adb.ts +49 -35
- package/src/banner.ts +7 -7
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +31 -3
- package/src/commands/power.ts +9 -9
- package/src/commands/reverse.ts +13 -13
- package/src/commands/subprocess/command.ts +24 -25
- package/src/commands/subprocess/protocols/none.ts +23 -27
- package/src/commands/subprocess/protocols/shell.ts +42 -37
- package/src/commands/sync/list.ts +11 -7
- package/src/commands/sync/pull.ts +7 -4
- package/src/commands/sync/push.ts +6 -6
- package/src/commands/sync/request.ts +3 -3
- package/src/commands/sync/response.ts +8 -6
- package/src/commands/sync/socket.ts +19 -14
- package/src/commands/sync/stat.ts +5 -5
- package/src/commands/sync/sync.ts +32 -17
- package/src/commands/tcpip.ts +46 -2
- package/src/daemon/auth.ts +49 -30
- package/src/daemon/crypto.ts +9 -9
- package/src/daemon/dispatcher.ts +104 -88
- package/src/daemon/packet.ts +2 -2
- package/src/daemon/socket.ts +106 -115
- package/src/daemon/transport.ts +49 -29
- package/src/server/client.ts +88 -77
- package/src/server/transport.ts +13 -13
- package/src/utils/auto-reset-event.ts +4 -4
- package/src/utils/base64.ts +5 -5
- package/src/utils/conditional-variable.ts +4 -4
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/daemon/connection.d.ts +0 -11
- package/esm/daemon/connection.d.ts.map +0 -1
- package/esm/daemon/connection.js +0 -2
- package/esm/daemon/connection.js.map +0 -1
package/src/daemon/transport.ts
CHANGED
|
@@ -27,19 +27,34 @@ import { AdbCommand, calculateChecksum } from "./packet.js";
|
|
|
27
27
|
|
|
28
28
|
export const ADB_DAEMON_VERSION_OMIT_CHECKSUM = 0x01000001;
|
|
29
29
|
|
|
30
|
+
export type AdbDaemonConnection = ReadableWritablePair<
|
|
31
|
+
AdbPacketData,
|
|
32
|
+
Consumable<AdbPacketInit>
|
|
33
|
+
>;
|
|
34
|
+
|
|
30
35
|
interface AdbDaemonAuthenticationOptions {
|
|
31
36
|
serial: string;
|
|
32
|
-
connection:
|
|
37
|
+
connection: AdbDaemonConnection;
|
|
33
38
|
credentialStore: AdbCredentialStore;
|
|
34
39
|
authenticators?: AdbAuthenticator[];
|
|
40
|
+
/**
|
|
41
|
+
* Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
|
|
42
|
+
*/
|
|
43
|
+
preserveConnection?: boolean | undefined;
|
|
44
|
+
debugSlowRead?: boolean | undefined;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
interface AdbDaemonSocketConnectorConstructionOptions {
|
|
38
48
|
serial: string;
|
|
39
|
-
connection:
|
|
49
|
+
connection: AdbDaemonConnection;
|
|
40
50
|
version: number;
|
|
41
51
|
maxPayloadSize: number;
|
|
42
52
|
banner: string;
|
|
53
|
+
/**
|
|
54
|
+
* Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
|
|
55
|
+
*/
|
|
56
|
+
preserveConnection?: boolean | undefined;
|
|
57
|
+
debugSlowRead?: boolean | undefined;
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
export class AdbDaemonTransport implements AdbTransport {
|
|
@@ -51,11 +66,12 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
51
66
|
* on the same connection. Because every time the device receives a `CNXN` packet,
|
|
52
67
|
* it resets all internal state, and starts a new authentication process.
|
|
53
68
|
*/
|
|
54
|
-
|
|
69
|
+
static async authenticate({
|
|
55
70
|
serial,
|
|
56
71
|
connection,
|
|
57
72
|
credentialStore,
|
|
58
73
|
authenticators = ADB_DEFAULT_AUTHENTICATORS,
|
|
74
|
+
...options
|
|
59
75
|
}: AdbDaemonAuthenticationOptions): Promise<AdbDaemonTransport> {
|
|
60
76
|
// Initially, set to highest-supported version and payload size.
|
|
61
77
|
let version = 0x01000001;
|
|
@@ -65,7 +81,7 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
65
81
|
const resolver = new PromiseResolver<string>();
|
|
66
82
|
const authProcessor = new AdbAuthenticationProcessor(
|
|
67
83
|
authenticators,
|
|
68
|
-
credentialStore
|
|
84
|
+
credentialStore,
|
|
69
85
|
);
|
|
70
86
|
|
|
71
87
|
// Here is similar to `AdbPacketDispatcher`,
|
|
@@ -80,14 +96,13 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
80
96
|
version = Math.min(version, packet.arg0);
|
|
81
97
|
maxPayloadSize = Math.min(
|
|
82
98
|
maxPayloadSize,
|
|
83
|
-
packet.arg1
|
|
99
|
+
packet.arg1,
|
|
84
100
|
);
|
|
85
101
|
resolver.resolve(decodeUtf8(packet.payload));
|
|
86
102
|
break;
|
|
87
103
|
case AdbCommand.Auth: {
|
|
88
|
-
const response =
|
|
89
|
-
packet
|
|
90
|
-
);
|
|
104
|
+
const response =
|
|
105
|
+
await authProcessor.process(packet);
|
|
91
106
|
await sendPacket(response);
|
|
92
107
|
break;
|
|
93
108
|
}
|
|
@@ -105,19 +120,19 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
105
120
|
// Don't cancel the source ReadableStream on AbortSignal abort.
|
|
106
121
|
preventCancel: true,
|
|
107
122
|
signal: abortController.signal,
|
|
108
|
-
}
|
|
123
|
+
},
|
|
109
124
|
)
|
|
110
125
|
.then(
|
|
111
126
|
() => {
|
|
112
127
|
if (resolver.state === "running") {
|
|
113
128
|
resolver.reject(
|
|
114
|
-
new Error("Connection closed unexpectedly")
|
|
129
|
+
new Error("Connection closed unexpectedly"),
|
|
115
130
|
);
|
|
116
131
|
}
|
|
117
132
|
},
|
|
118
133
|
(e) => {
|
|
119
134
|
resolver.reject(e);
|
|
120
|
-
}
|
|
135
|
+
},
|
|
121
136
|
);
|
|
122
137
|
|
|
123
138
|
const writer = connection.writable.getWriter();
|
|
@@ -147,7 +162,7 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
147
162
|
AdbFeature.AbbExec,
|
|
148
163
|
"remount_shell",
|
|
149
164
|
"track_app",
|
|
150
|
-
|
|
165
|
+
AdbFeature.SendReceiveV2,
|
|
151
166
|
"sendrecv_v2_brotli",
|
|
152
167
|
"sendrecv_v2_lz4",
|
|
153
168
|
"sendrecv_v2_zstd",
|
|
@@ -180,43 +195,49 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
180
195
|
version,
|
|
181
196
|
maxPayloadSize,
|
|
182
197
|
banner,
|
|
198
|
+
...options,
|
|
183
199
|
});
|
|
184
200
|
}
|
|
185
201
|
|
|
202
|
+
#connection: AdbDaemonConnection;
|
|
203
|
+
get connection() {
|
|
204
|
+
return this.#connection;
|
|
205
|
+
}
|
|
206
|
+
|
|
186
207
|
readonly #dispatcher: AdbPacketDispatcher;
|
|
187
208
|
|
|
188
209
|
#serial: string;
|
|
189
|
-
|
|
210
|
+
get serial() {
|
|
190
211
|
return this.#serial;
|
|
191
212
|
}
|
|
192
213
|
|
|
193
214
|
#protocolVersion: number;
|
|
194
|
-
|
|
215
|
+
get protocolVersion() {
|
|
195
216
|
return this.#protocolVersion;
|
|
196
217
|
}
|
|
197
218
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return this.#maxPayloadSize;
|
|
219
|
+
get maxPayloadSize() {
|
|
220
|
+
return this.#dispatcher.options.maxPayloadSize;
|
|
201
221
|
}
|
|
202
222
|
|
|
203
223
|
#banner: AdbBanner;
|
|
204
|
-
|
|
224
|
+
get banner() {
|
|
205
225
|
return this.#banner;
|
|
206
226
|
}
|
|
207
227
|
|
|
208
|
-
|
|
228
|
+
get disconnected() {
|
|
209
229
|
return this.#dispatcher.disconnected;
|
|
210
230
|
}
|
|
211
231
|
|
|
212
|
-
|
|
232
|
+
constructor({
|
|
213
233
|
serial,
|
|
214
234
|
connection,
|
|
215
235
|
version,
|
|
216
|
-
maxPayloadSize,
|
|
217
236
|
banner,
|
|
237
|
+
...options
|
|
218
238
|
}: AdbDaemonSocketConnectorConstructionOptions) {
|
|
219
239
|
this.#serial = serial;
|
|
240
|
+
this.#connection = connection;
|
|
220
241
|
this.#banner = AdbBanner.parse(banner);
|
|
221
242
|
|
|
222
243
|
let calculateChecksum: boolean;
|
|
@@ -232,20 +253,19 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
232
253
|
this.#dispatcher = new AdbPacketDispatcher(connection, {
|
|
233
254
|
calculateChecksum,
|
|
234
255
|
appendNullToServiceString,
|
|
235
|
-
|
|
256
|
+
...options,
|
|
236
257
|
});
|
|
237
258
|
|
|
238
259
|
this.#protocolVersion = version;
|
|
239
|
-
this.#maxPayloadSize = maxPayloadSize;
|
|
240
260
|
}
|
|
241
261
|
|
|
242
|
-
|
|
262
|
+
connect(service: string): ValueOrPromise<AdbSocket> {
|
|
243
263
|
return this.#dispatcher.createSocket(service);
|
|
244
264
|
}
|
|
245
265
|
|
|
246
|
-
|
|
266
|
+
addReverseTunnel(
|
|
247
267
|
handler: AdbIncomingSocketHandler,
|
|
248
|
-
address?: string
|
|
268
|
+
address?: string,
|
|
249
269
|
): string {
|
|
250
270
|
if (!address) {
|
|
251
271
|
const id = Math.random().toString().substring(2);
|
|
@@ -255,15 +275,15 @@ export class AdbDaemonTransport implements AdbTransport {
|
|
|
255
275
|
return address;
|
|
256
276
|
}
|
|
257
277
|
|
|
258
|
-
|
|
278
|
+
removeReverseTunnel(address: string): void {
|
|
259
279
|
this.#dispatcher.removeReverseTunnel(address);
|
|
260
280
|
}
|
|
261
281
|
|
|
262
|
-
|
|
282
|
+
clearReverseTunnels(): void {
|
|
263
283
|
this.#dispatcher.clearReverseTunnels();
|
|
264
284
|
}
|
|
265
285
|
|
|
266
|
-
|
|
286
|
+
close(): ValueOrPromise<void> {
|
|
267
287
|
return this.#dispatcher.close();
|
|
268
288
|
}
|
|
269
289
|
}
|
package/src/server/client.ts
CHANGED
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
import { PromiseResolver } from "@yume-chan/async";
|
|
4
4
|
import type {
|
|
5
5
|
AbortSignal,
|
|
6
|
-
Consumable,
|
|
7
6
|
ReadableWritablePair,
|
|
8
7
|
WritableStreamDefaultWriter,
|
|
9
8
|
} from "@yume-chan/stream-extra";
|
|
10
9
|
import {
|
|
11
10
|
BufferedReadableStream,
|
|
12
|
-
DuplexStreamFactory,
|
|
13
11
|
UnwrapConsumableStream,
|
|
14
12
|
WrapWritableStream,
|
|
15
13
|
} from "@yume-chan/stream-extra";
|
|
@@ -25,7 +23,7 @@ import {
|
|
|
25
23
|
encodeUtf8,
|
|
26
24
|
} from "@yume-chan/struct";
|
|
27
25
|
|
|
28
|
-
import type { AdbIncomingSocketHandler, AdbSocket } from "../adb.js";
|
|
26
|
+
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
|
29
27
|
import { AdbBanner } from "../banner.js";
|
|
30
28
|
import type { AdbFeature } from "../features.js";
|
|
31
29
|
import { NOOP, hexToNumber, numberToHex } from "../utils/index.js";
|
|
@@ -37,14 +35,20 @@ export interface AdbServerConnectionOptions {
|
|
|
37
35
|
signal?: AbortSignal | undefined;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
export interface AdbServerConnection
|
|
38
|
+
export interface AdbServerConnection
|
|
39
|
+
extends ReadableWritablePair<Uint8Array, Uint8Array>,
|
|
40
|
+
Closeable {
|
|
41
|
+
get closed(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface AdbServerConnector {
|
|
41
45
|
connect(
|
|
42
|
-
options?: AdbServerConnectionOptions
|
|
43
|
-
): ValueOrPromise<
|
|
46
|
+
options?: AdbServerConnectionOptions,
|
|
47
|
+
): ValueOrPromise<AdbServerConnection>;
|
|
44
48
|
|
|
45
49
|
addReverseTunnel(
|
|
46
50
|
handler: AdbIncomingSocketHandler,
|
|
47
|
-
address?: string
|
|
51
|
+
address?: string,
|
|
48
52
|
): ValueOrPromise<string>;
|
|
49
53
|
|
|
50
54
|
removeReverseTunnel(address: string): ValueOrPromise<void>;
|
|
@@ -57,10 +61,8 @@ export interface AdbServerSocket extends AdbSocket {
|
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
export type AdbServerDeviceSelector =
|
|
60
|
-
| {
|
|
61
|
-
serial: string;
|
|
62
|
-
}
|
|
63
64
|
| { transportId: bigint }
|
|
65
|
+
| { serial: string }
|
|
64
66
|
| { usb: true }
|
|
65
67
|
| { tcp: true }
|
|
66
68
|
| undefined;
|
|
@@ -74,18 +76,18 @@ export interface AdbServerDevice {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export class AdbServerClient {
|
|
77
|
-
|
|
79
|
+
static readonly VERSION = 41;
|
|
78
80
|
|
|
79
|
-
|
|
81
|
+
readonly connection: AdbServerConnector;
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
constructor(connection: AdbServerConnector) {
|
|
82
84
|
this.connection = connection;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
stream: ExactReadable | AsyncExactReadable
|
|
87
|
+
static readString(stream: ExactReadable): string;
|
|
88
|
+
static readString(stream: AsyncExactReadable): PromiseLike<string>;
|
|
89
|
+
static readString(
|
|
90
|
+
stream: ExactReadable | AsyncExactReadable,
|
|
89
91
|
): string | PromiseLike<string> {
|
|
90
92
|
return SyncPromise.try(() => stream.readExactly(4))
|
|
91
93
|
.then((buffer) => {
|
|
@@ -98,9 +100,9 @@ export class AdbServerClient {
|
|
|
98
100
|
.valueOrPromise();
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
static async writeString(
|
|
102
104
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
|
103
|
-
value: string
|
|
105
|
+
value: string,
|
|
104
106
|
): Promise<void> {
|
|
105
107
|
const valueBuffer = encodeUtf8(value);
|
|
106
108
|
const buffer = new Uint8Array(4 + valueBuffer.length);
|
|
@@ -109,8 +111,8 @@ export class AdbServerClient {
|
|
|
109
111
|
await writer.write(buffer);
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
stream: ExactReadable | AsyncExactReadable
|
|
114
|
+
static async readOkay(
|
|
115
|
+
stream: ExactReadable | AsyncExactReadable,
|
|
114
116
|
): Promise<void> {
|
|
115
117
|
const response = decodeUtf8(await stream.readExactly(4));
|
|
116
118
|
if (response === "OKAY") {
|
|
@@ -125,38 +127,49 @@ export class AdbServerClient {
|
|
|
125
127
|
throw new Error(`Unexpected response: ${response}`);
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
async connect(
|
|
129
131
|
request: string,
|
|
130
|
-
options?: AdbServerConnectionOptions
|
|
131
|
-
): Promise<
|
|
132
|
+
options?: AdbServerConnectionOptions,
|
|
133
|
+
): Promise<AdbServerConnection> {
|
|
132
134
|
const connection = await this.connection.connect(options);
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
try {
|
|
137
|
+
const writer = connection.writable.getWriter();
|
|
138
|
+
await AdbServerClient.writeString(writer, request);
|
|
139
|
+
writer.releaseLock();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
await connection.readable.cancel();
|
|
142
|
+
await connection.close();
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
136
145
|
|
|
137
146
|
const readable = new BufferedReadableStream(connection.readable);
|
|
138
|
-
|
|
139
147
|
try {
|
|
140
|
-
// `raceSignal` throws
|
|
148
|
+
// `raceSignal` throws when the signal is aborted,
|
|
141
149
|
// so the `catch` block can close the connection.
|
|
142
150
|
await raceSignal(
|
|
143
151
|
() => AdbServerClient.readOkay(readable),
|
|
144
|
-
options?.signal
|
|
152
|
+
options?.signal,
|
|
145
153
|
);
|
|
146
154
|
|
|
147
|
-
writer.releaseLock();
|
|
148
155
|
return {
|
|
149
156
|
readable: readable.release(),
|
|
150
157
|
writable: connection.writable,
|
|
158
|
+
get closed() {
|
|
159
|
+
return connection.closed;
|
|
160
|
+
},
|
|
161
|
+
async close() {
|
|
162
|
+
await connection.close();
|
|
163
|
+
},
|
|
151
164
|
};
|
|
152
165
|
} catch (e) {
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
await readable.cancel().catch(NOOP);
|
|
167
|
+
await connection.close();
|
|
155
168
|
throw e;
|
|
156
169
|
}
|
|
157
170
|
}
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
async getVersion(): Promise<number> {
|
|
160
173
|
const connection = await this.connect("host:version");
|
|
161
174
|
const readable = new BufferedReadableStream(connection.readable);
|
|
162
175
|
try {
|
|
@@ -169,22 +182,22 @@ export class AdbServerClient {
|
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
|
|
172
|
-
|
|
185
|
+
async validateVersion() {
|
|
173
186
|
const version = await this.getVersion();
|
|
174
187
|
if (version !== AdbServerClient.VERSION) {
|
|
175
188
|
throw new Error(
|
|
176
|
-
`adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})
|
|
189
|
+
`adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`,
|
|
177
190
|
);
|
|
178
191
|
}
|
|
179
192
|
}
|
|
180
193
|
|
|
181
|
-
|
|
194
|
+
async killServer(): Promise<void> {
|
|
182
195
|
const connection = await this.connect("host:kill");
|
|
183
196
|
connection.writable.close().catch(NOOP);
|
|
184
197
|
connection.readable.cancel().catch(NOOP);
|
|
185
198
|
}
|
|
186
199
|
|
|
187
|
-
|
|
200
|
+
async getServerFeatures(): Promise<AdbFeature[]> {
|
|
188
201
|
const connection = await this.connect("host:host-features");
|
|
189
202
|
const readable = new BufferedReadableStream(connection.readable);
|
|
190
203
|
try {
|
|
@@ -196,7 +209,7 @@ export class AdbServerClient {
|
|
|
196
209
|
}
|
|
197
210
|
}
|
|
198
211
|
|
|
199
|
-
|
|
212
|
+
async getDevices(): Promise<AdbServerDevice[]> {
|
|
200
213
|
const connection = await this.connect("host:devices-l");
|
|
201
214
|
const readable = new BufferedReadableStream(connection.readable);
|
|
202
215
|
try {
|
|
@@ -253,10 +266,7 @@ export class AdbServerClient {
|
|
|
253
266
|
}
|
|
254
267
|
}
|
|
255
268
|
|
|
256
|
-
|
|
257
|
-
device: AdbServerDeviceSelector,
|
|
258
|
-
command: string
|
|
259
|
-
) {
|
|
269
|
+
formatDeviceService(device: AdbServerDeviceSelector, command: string) {
|
|
260
270
|
if (!device) {
|
|
261
271
|
return `host:${command}`;
|
|
262
272
|
}
|
|
@@ -282,8 +292,8 @@ export class AdbServerClient {
|
|
|
282
292
|
* @param device The device selector
|
|
283
293
|
* @returns The transport ID of the selected device, and the features supported by the device.
|
|
284
294
|
*/
|
|
285
|
-
|
|
286
|
-
device: AdbServerDeviceSelector
|
|
295
|
+
async getDeviceFeatures(
|
|
296
|
+
device: AdbServerDeviceSelector,
|
|
287
297
|
): Promise<{ transportId: bigint; features: AdbFeature[] }> {
|
|
288
298
|
// Usually the client sends a device command using `connectDevice`,
|
|
289
299
|
// so the command got forwarded and handled by ADB daemon.
|
|
@@ -309,9 +319,9 @@ export class AdbServerClient {
|
|
|
309
319
|
* @param service The service to forward
|
|
310
320
|
* @returns An `AdbServerSocket` that can be used to communicate with the service
|
|
311
321
|
*/
|
|
312
|
-
|
|
322
|
+
async connectDevice(
|
|
313
323
|
device: AdbServerDeviceSelector,
|
|
314
|
-
service: string
|
|
324
|
+
service: string,
|
|
315
325
|
): Promise<AdbServerSocket> {
|
|
316
326
|
await this.validateVersion();
|
|
317
327
|
|
|
@@ -333,8 +343,18 @@ export class AdbServerClient {
|
|
|
333
343
|
}
|
|
334
344
|
|
|
335
345
|
const connection = await this.connect(switchService);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const writer = connection.writable.getWriter();
|
|
349
|
+
await AdbServerClient.writeString(writer, service);
|
|
350
|
+
writer.releaseLock();
|
|
351
|
+
} catch (e) {
|
|
352
|
+
await connection.readable.cancel();
|
|
353
|
+
await connection.close();
|
|
354
|
+
throw e;
|
|
355
|
+
}
|
|
356
|
+
|
|
336
357
|
const readable = new BufferedReadableStream(connection.readable);
|
|
337
|
-
const writer = connection.writable.getWriter();
|
|
338
358
|
try {
|
|
339
359
|
if (transportId === undefined) {
|
|
340
360
|
const array = await readable.readExactly(8);
|
|
@@ -342,39 +362,30 @@ export class AdbServerClient {
|
|
|
342
362
|
const dataView = new DataView(
|
|
343
363
|
array.buffer,
|
|
344
364
|
array.byteOffset,
|
|
345
|
-
array.byteLength
|
|
365
|
+
array.byteLength,
|
|
346
366
|
);
|
|
347
367
|
transportId = BigIntFieldType.Uint64.getter(dataView, 0, true);
|
|
348
368
|
}
|
|
349
369
|
|
|
350
|
-
await AdbServerClient.writeString(writer, service);
|
|
351
370
|
await AdbServerClient.readOkay(readable);
|
|
352
371
|
|
|
353
|
-
writer.releaseLock();
|
|
354
|
-
|
|
355
|
-
const duplex = new DuplexStreamFactory<
|
|
356
|
-
Uint8Array,
|
|
357
|
-
Consumable<Uint8Array>
|
|
358
|
-
>();
|
|
359
|
-
const wrapReadable = duplex.wrapReadable(readable.release());
|
|
360
|
-
const wrapWritable = duplex.createWritable(
|
|
361
|
-
new WrapWritableStream(connection.writable).bePipedThroughFrom(
|
|
362
|
-
new UnwrapConsumableStream()
|
|
363
|
-
)
|
|
364
|
-
);
|
|
365
|
-
|
|
366
372
|
return {
|
|
367
373
|
transportId,
|
|
368
374
|
service,
|
|
369
|
-
readable:
|
|
370
|
-
writable:
|
|
371
|
-
|
|
372
|
-
|
|
375
|
+
readable: readable.release(),
|
|
376
|
+
writable: new WrapWritableStream(
|
|
377
|
+
connection.writable,
|
|
378
|
+
).bePipedThroughFrom(new UnwrapConsumableStream()),
|
|
379
|
+
get closed() {
|
|
380
|
+
return connection.closed;
|
|
381
|
+
},
|
|
382
|
+
async close() {
|
|
383
|
+
await connection.close();
|
|
373
384
|
},
|
|
374
385
|
};
|
|
375
386
|
} catch (e) {
|
|
376
|
-
|
|
377
|
-
|
|
387
|
+
await readable.cancel().catch(NOOP);
|
|
388
|
+
await connection.close();
|
|
378
389
|
throw e;
|
|
379
390
|
}
|
|
380
391
|
}
|
|
@@ -386,10 +397,10 @@ export class AdbServerClient {
|
|
|
386
397
|
* @param options The options
|
|
387
398
|
* @returns A promise that resolves when the condition is met.
|
|
388
399
|
*/
|
|
389
|
-
|
|
400
|
+
async waitFor(
|
|
390
401
|
device: AdbServerDeviceSelector,
|
|
391
402
|
state: "device" | "disconnect",
|
|
392
|
-
options?: AdbServerConnectionOptions
|
|
403
|
+
options?: AdbServerConnectionOptions,
|
|
393
404
|
): Promise<void> {
|
|
394
405
|
let type: string;
|
|
395
406
|
if (!device) {
|
|
@@ -410,7 +421,7 @@ export class AdbServerClient {
|
|
|
410
421
|
// might not be available yet.
|
|
411
422
|
const service = this.formatDeviceService(
|
|
412
423
|
device,
|
|
413
|
-
`wait-for-${type}-${state}
|
|
424
|
+
`wait-for-${type}-${state}`,
|
|
414
425
|
);
|
|
415
426
|
|
|
416
427
|
// `connect` resolves when server writes `OKAY`,
|
|
@@ -418,33 +429,33 @@ export class AdbServerClient {
|
|
|
418
429
|
await this.connect(service, options);
|
|
419
430
|
}
|
|
420
431
|
|
|
421
|
-
|
|
422
|
-
device: AdbServerDeviceSelector
|
|
432
|
+
async createTransport(
|
|
433
|
+
device: AdbServerDeviceSelector,
|
|
423
434
|
): Promise<AdbServerTransport> {
|
|
424
435
|
const { transportId, features } = await this.getDeviceFeatures(device);
|
|
425
436
|
|
|
426
437
|
const devices = await this.getDevices();
|
|
427
438
|
const info = devices.find(
|
|
428
|
-
(device) => device.transportId === transportId
|
|
439
|
+
(device) => device.transportId === transportId,
|
|
429
440
|
);
|
|
430
441
|
|
|
431
442
|
const banner = new AdbBanner(
|
|
432
443
|
info?.product,
|
|
433
444
|
info?.model,
|
|
434
445
|
info?.device,
|
|
435
|
-
features
|
|
446
|
+
features,
|
|
436
447
|
);
|
|
437
448
|
|
|
438
449
|
return new AdbServerTransport(
|
|
439
450
|
this,
|
|
440
451
|
info?.serial ?? "",
|
|
441
452
|
banner,
|
|
442
|
-
transportId
|
|
453
|
+
transportId,
|
|
443
454
|
);
|
|
444
455
|
}
|
|
445
456
|
}
|
|
446
457
|
|
|
447
|
-
async function raceSignal<T>(
|
|
458
|
+
export async function raceSignal<T>(
|
|
448
459
|
callback: () => Promise<T>,
|
|
449
460
|
...signals: (AbortSignal | undefined)[]
|
|
450
461
|
): Promise<T> {
|
package/src/server/transport.ts
CHANGED
|
@@ -14,23 +14,23 @@ import type { AdbServerClient } from "./client.js";
|
|
|
14
14
|
export class AdbServerTransport implements AdbTransport {
|
|
15
15
|
#client: AdbServerClient;
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
readonly serial: string;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
readonly transportId: bigint;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
readonly maxPayloadSize: number = 1 * 1024 * 1024;
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
readonly banner: AdbBanner;
|
|
24
24
|
|
|
25
25
|
#closed = new PromiseResolver<void>();
|
|
26
26
|
#waitAbortController = new AbortController();
|
|
27
|
-
|
|
27
|
+
readonly disconnected: Promise<void>;
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
constructor(
|
|
30
30
|
client: AdbServerClient,
|
|
31
31
|
serial: string,
|
|
32
32
|
banner: AdbBanner,
|
|
33
|
-
transportId: bigint
|
|
33
|
+
transportId: bigint,
|
|
34
34
|
) {
|
|
35
35
|
this.#client = client;
|
|
36
36
|
this.serial = serial;
|
|
@@ -46,27 +46,27 @@ export class AdbServerTransport implements AdbTransport {
|
|
|
46
46
|
]);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
async connect(service: string): Promise<AdbSocket> {
|
|
50
50
|
return await this.#client.connectDevice(
|
|
51
51
|
{
|
|
52
52
|
transportId: this.transportId,
|
|
53
53
|
},
|
|
54
|
-
service
|
|
54
|
+
service,
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
async addReverseTunnel(
|
|
59
59
|
handler: AdbIncomingSocketHandler,
|
|
60
|
-
address?: string
|
|
60
|
+
address?: string,
|
|
61
61
|
): Promise<string> {
|
|
62
62
|
return await this.#client.connection.addReverseTunnel(handler, address);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
async removeReverseTunnel(address: string): Promise<void> {
|
|
66
66
|
await this.#client.connection.removeReverseTunnel(address);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
async clearReverseTunnels(): Promise<void> {
|
|
70
70
|
await this.#client.connection.clearReverseTunnels();
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -5,11 +5,11 @@ export class AutoResetEvent implements Disposable {
|
|
|
5
5
|
#set: boolean;
|
|
6
6
|
readonly #queue: PromiseResolver<void>[] = [];
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
constructor(initialSet = false) {
|
|
9
9
|
this.#set = initialSet;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
wait(): Promise<void> {
|
|
13
13
|
if (!this.#set) {
|
|
14
14
|
this.#set = true;
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ export class AutoResetEvent implements Disposable {
|
|
|
23
23
|
return resolver.promise;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
notifyOne() {
|
|
27
27
|
if (this.#queue.length !== 0) {
|
|
28
28
|
this.#queue.pop()!.resolve();
|
|
29
29
|
} else {
|
|
@@ -31,7 +31,7 @@ export class AutoResetEvent implements Disposable {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
dispose() {
|
|
35
35
|
for (const item of this.#queue) {
|
|
36
36
|
item.reject(new Error("The AutoResetEvent has been disposed"));
|
|
37
37
|
}
|