@yume-chan/adb 1.0.0 → 2.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 +26 -0
- package/LICENSE +1 -1
- package/esm/adb.d.ts +7 -6
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +22 -22
- package/esm/adb.js.map +1 -1
- package/esm/banner.js.map +1 -1
- package/esm/commands/base.d.ts +3 -2
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js +6 -3
- package/esm/commands/base.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +6 -6
- package/esm/commands/power.d.ts +2 -2
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +4 -4
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts +3 -4
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +5 -10
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/index.d.ts +3 -2
- package/esm/commands/subprocess/index.d.ts.map +1 -1
- package/esm/commands/subprocess/index.js +3 -2
- package/esm/commands/subprocess/index.js.map +1 -1
- package/esm/commands/subprocess/none/index.d.ts +5 -0
- package/esm/commands/subprocess/none/index.d.ts.map +1 -0
- package/esm/commands/subprocess/none/index.js +5 -0
- package/esm/commands/subprocess/none/index.js.map +1 -0
- package/esm/commands/subprocess/none/process.d.ts +13 -0
- package/esm/commands/subprocess/none/process.d.ts.map +1 -0
- package/esm/commands/subprocess/none/process.js +36 -0
- package/esm/commands/subprocess/none/process.js.map +1 -0
- package/esm/commands/subprocess/none/pty.d.ts +15 -0
- package/esm/commands/subprocess/none/pty.d.ts.map +1 -0
- package/esm/commands/subprocess/none/pty.js +29 -0
- package/esm/commands/subprocess/none/pty.js.map +1 -0
- package/esm/commands/subprocess/none/service.d.ts +10 -0
- package/esm/commands/subprocess/none/service.d.ts.map +1 -0
- package/esm/commands/subprocess/none/service.js +32 -0
- package/esm/commands/subprocess/none/service.js.map +1 -0
- package/esm/commands/subprocess/none/spawner.d.ts +19 -0
- package/esm/commands/subprocess/none/spawner.d.ts.map +1 -0
- package/esm/commands/subprocess/none/spawner.js +26 -0
- package/esm/commands/subprocess/none/spawner.js.map +1 -0
- package/esm/commands/subprocess/pty.d.ts +10 -0
- package/esm/commands/subprocess/pty.d.ts.map +1 -0
- package/esm/commands/subprocess/pty.js +2 -0
- package/esm/commands/subprocess/pty.js.map +1 -0
- package/esm/commands/subprocess/service.d.ts +11 -0
- package/esm/commands/subprocess/service.d.ts.map +1 -0
- package/esm/commands/subprocess/service.js +25 -0
- package/esm/commands/subprocess/service.js.map +1 -0
- package/esm/commands/subprocess/shell/index.d.ts +6 -0
- package/esm/commands/subprocess/shell/index.d.ts.map +1 -0
- package/esm/commands/subprocess/shell/index.js +6 -0
- package/esm/commands/subprocess/shell/index.js.map +1 -0
- package/esm/commands/subprocess/shell/process.d.ts +15 -0
- package/esm/commands/subprocess/shell/process.d.ts.map +1 -0
- package/esm/commands/subprocess/{protocols/shell.js → shell/process.js} +27 -54
- package/esm/commands/subprocess/shell/process.js.map +1 -0
- package/esm/commands/subprocess/shell/pty.d.ts +15 -0
- package/esm/commands/subprocess/shell/pty.d.ts.map +1 -0
- package/esm/commands/subprocess/shell/pty.js +76 -0
- package/esm/commands/subprocess/shell/pty.js.map +1 -0
- package/esm/commands/subprocess/shell/service.d.ts +14 -0
- package/esm/commands/subprocess/shell/service.d.ts.map +1 -0
- package/esm/commands/subprocess/shell/service.js +41 -0
- package/esm/commands/subprocess/shell/service.js.map +1 -0
- package/esm/commands/subprocess/shell/shared.d.ts +19 -0
- package/esm/commands/subprocess/shell/shared.d.ts.map +1 -0
- package/esm/commands/subprocess/shell/shared.js +15 -0
- package/esm/commands/subprocess/shell/shared.js.map +1 -0
- package/esm/commands/subprocess/shell/spawner.d.ts +24 -0
- package/esm/commands/subprocess/shell/spawner.d.ts.map +1 -0
- package/esm/commands/subprocess/shell/spawner.js +38 -0
- package/esm/commands/subprocess/shell/spawner.js.map +1 -0
- package/esm/commands/subprocess/utils.d.ts +1 -0
- package/esm/commands/subprocess/utils.d.ts.map +1 -1
- package/esm/commands/subprocess/utils.js +37 -0
- package/esm/commands/subprocess/utils.js.map +1 -1
- package/esm/commands/sync/list.d.ts +12 -8
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +5 -7
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +4 -4
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +2 -7
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +4 -4
- package/esm/commands/sync/request.d.ts +1 -1
- package/esm/commands/sync/response.d.ts +5 -5
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +2 -1
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/stat.d.ts +4 -4
- package/esm/commands/sync/sync.d.ts +2 -2
- package/esm/commands/sync/sync.js +1 -1
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +2 -2
- package/esm/commands/tcpip.js +2 -2
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +6 -4
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +3 -5
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +2 -2
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +1 -1
- package/esm/daemon/socket.js.map +1 -1
- package/esm/device-observer.d.ts +4 -4
- package/esm/device-observer.d.ts.map +1 -1
- package/esm/server/client.d.ts +15 -57
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +66 -263
- package/esm/server/client.js.map +1 -1
- package/esm/server/commands/index.d.ts +3 -0
- package/esm/server/commands/index.d.ts.map +1 -0
- package/esm/server/commands/index.js +3 -0
- package/esm/server/commands/index.js.map +1 -0
- package/esm/server/commands/m-dns.d.ts +12 -0
- package/esm/server/commands/m-dns.d.ts.map +1 -0
- package/esm/server/commands/m-dns.js +38 -0
- package/esm/server/commands/m-dns.js.map +1 -0
- package/esm/server/commands/wireless.d.ts +27 -0
- package/esm/server/commands/wireless.d.ts.map +1 -0
- package/esm/server/commands/wireless.js +82 -0
- package/esm/server/commands/wireless.js.map +1 -0
- package/esm/server/index.d.ts +2 -0
- package/esm/server/index.d.ts.map +1 -1
- package/esm/server/index.js +2 -0
- package/esm/server/index.js.map +1 -1
- package/esm/server/observer.d.ts +9 -0
- package/esm/server/observer.d.ts.map +1 -0
- package/esm/server/observer.js +128 -0
- package/esm/server/observer.js.map +1 -0
- package/esm/server/stream.d.ts +19 -0
- package/esm/server/stream.d.ts.map +1 -0
- package/esm/server/stream.js +77 -0
- package/esm/server/stream.js.map +1 -0
- package/esm/server/transport.d.ts +3 -4
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js +18 -16
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +1 -0
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/ref.d.ts +18 -0
- package/esm/utils/ref.d.ts.map +1 -0
- package/esm/utils/ref.js +29 -0
- package/esm/utils/ref.js.map +1 -0
- package/package.json +10 -10
- package/src/adb.ts +29 -28
- package/src/banner.ts +4 -4
- package/src/commands/base.ts +6 -3
- package/src/commands/power.ts +6 -6
- package/src/commands/reverse.ts +9 -12
- package/src/commands/subprocess/index.ts +3 -2
- package/src/commands/subprocess/none/index.ts +4 -0
- package/src/commands/subprocess/none/process.ts +56 -0
- package/src/commands/subprocess/none/pty.ts +45 -0
- package/src/commands/subprocess/none/service.ts +42 -0
- package/src/commands/subprocess/none/spawner.ts +68 -0
- package/src/commands/subprocess/pty.ts +15 -0
- package/src/commands/subprocess/service.ts +32 -0
- package/src/commands/subprocess/shell/index.ts +5 -0
- package/src/commands/subprocess/shell/process.ts +127 -0
- package/src/commands/subprocess/shell/pty.ts +112 -0
- package/src/commands/subprocess/shell/service.ts +58 -0
- package/src/commands/subprocess/shell/shared.ts +25 -0
- package/src/commands/subprocess/shell/spawner.ts +90 -0
- package/src/commands/subprocess/utils.ts +41 -0
- package/src/commands/sync/list.ts +7 -17
- package/src/commands/sync/pull.ts +2 -8
- package/src/commands/sync/response.ts +6 -4
- package/src/commands/sync/sync.ts +1 -1
- package/src/commands/tcpip.ts +2 -2
- package/src/daemon/dispatcher.ts +7 -4
- package/src/daemon/packet.ts +4 -8
- package/src/daemon/socket.ts +6 -6
- package/src/device-observer.ts +4 -4
- package/src/server/client.ts +100 -305
- package/src/server/commands/index.ts +2 -0
- package/src/server/commands/m-dns.ts +43 -0
- package/src/server/commands/wireless.ts +94 -0
- package/src/server/index.ts +2 -0
- package/src/server/observer.ts +172 -0
- package/src/server/stream.ts +100 -0
- package/src/server/transport.ts +38 -34
- package/src/utils/index.ts +1 -0
- package/src/utils/ref.ts +37 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/commands/subprocess/command.d.ts +0 -58
- package/esm/commands/subprocess/command.d.ts.map +0 -1
- package/esm/commands/subprocess/command.js +0 -89
- package/esm/commands/subprocess/command.js.map +0 -1
- package/esm/commands/subprocess/protocols/index.d.ts +0 -4
- package/esm/commands/subprocess/protocols/index.d.ts.map +0 -1
- package/esm/commands/subprocess/protocols/index.js +0 -4
- package/esm/commands/subprocess/protocols/index.js.map +0 -1
- package/esm/commands/subprocess/protocols/none.d.ts +0 -32
- package/esm/commands/subprocess/protocols/none.d.ts.map +0 -1
- package/esm/commands/subprocess/protocols/none.js +0 -61
- package/esm/commands/subprocess/protocols/none.js.map +0 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +0 -42
- package/esm/commands/subprocess/protocols/shell.d.ts.map +0 -1
- package/esm/commands/subprocess/protocols/shell.js.map +0 -1
- package/esm/commands/subprocess/protocols/types.d.ts +0 -49
- package/esm/commands/subprocess/protocols/types.d.ts.map +0 -1
- package/esm/commands/subprocess/protocols/types.js +0 -2
- package/esm/commands/subprocess/protocols/types.js.map +0 -1
- package/src/commands/subprocess/command.ts +0 -141
- package/src/commands/subprocess/protocols/index.ts +0 -3
- package/src/commands/subprocess/protocols/none.ts +0 -81
- package/src/commands/subprocess/protocols/shell.ts +0 -171
- package/src/commands/subprocess/protocols/types.ts +0 -63
package/src/commands/tcpip.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AdbServiceBase } from "./base.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ADB daemon checks for the following properties in the order of
|
|
@@ -27,7 +27,7 @@ function parsePort(value: string): number | undefined {
|
|
|
27
27
|
return Number.parseInt(value, 10);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export class
|
|
30
|
+
export class AdbTcpIpService extends AdbServiceBase {
|
|
31
31
|
async getListenAddresses(): Promise<AdbTcpIpListenAddresses> {
|
|
32
32
|
const serviceListenAddresses = await this.adb.getProp(
|
|
33
33
|
"service.adb.listen_addrs",
|
package/src/daemon/dispatcher.ts
CHANGED
|
@@ -101,18 +101,21 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
101
101
|
*/
|
|
102
102
|
readonly #sockets = new Map<number, AdbDaemonSocketController>();
|
|
103
103
|
|
|
104
|
-
#writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>;
|
|
104
|
+
readonly #writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>;
|
|
105
105
|
|
|
106
106
|
readonly options: AdbPacketDispatcherOptions;
|
|
107
107
|
|
|
108
108
|
#closed = false;
|
|
109
|
-
#disconnected = new PromiseResolver<void>();
|
|
109
|
+
readonly #disconnected = new PromiseResolver<void>();
|
|
110
110
|
get disconnected() {
|
|
111
111
|
return this.#disconnected.promise;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
#incomingSocketHandlers = new Map<
|
|
115
|
-
|
|
114
|
+
readonly #incomingSocketHandlers = new Map<
|
|
115
|
+
string,
|
|
116
|
+
AdbIncomingSocketHandler
|
|
117
|
+
>();
|
|
118
|
+
readonly #readAbortController = new AbortController();
|
|
116
119
|
|
|
117
120
|
constructor(
|
|
118
121
|
connection: ReadableWritablePair<
|
package/src/daemon/packet.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Consumable, TransformStream } from "@yume-chan/stream-extra";
|
|
2
2
|
import type { StructInit, StructValue } from "@yume-chan/struct";
|
|
3
|
-
import { buffer, s32, struct, u32 } from "@yume-chan/struct";
|
|
3
|
+
import { buffer, extend, s32, struct, u32 } from "@yume-chan/struct";
|
|
4
4
|
|
|
5
5
|
export const AdbCommand = {
|
|
6
6
|
Auth: 0x48545541, // 'AUTH'
|
|
@@ -29,13 +29,9 @@ export type AdbPacketHeader = StructValue<typeof AdbPacketHeader>;
|
|
|
29
29
|
|
|
30
30
|
type AdbPacketHeaderInit = StructInit<typeof AdbPacketHeader>;
|
|
31
31
|
|
|
32
|
-
export const AdbPacket =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
payload: buffer("payloadLength"),
|
|
36
|
-
}))(),
|
|
37
|
-
{ littleEndian: true },
|
|
38
|
-
);
|
|
32
|
+
export const AdbPacket = extend(AdbPacketHeader, {
|
|
33
|
+
payload: buffer("payloadLength"),
|
|
34
|
+
});
|
|
39
35
|
|
|
40
36
|
export type AdbPacket = StructValue<typeof AdbPacket>;
|
|
41
37
|
|
package/src/daemon/socket.ts
CHANGED
|
@@ -43,7 +43,7 @@ export class AdbDaemonSocketController
|
|
|
43
43
|
readonly localCreated!: boolean;
|
|
44
44
|
readonly service!: string;
|
|
45
45
|
|
|
46
|
-
#readable: ReadableStream<Uint8Array>;
|
|
46
|
+
readonly #readable: ReadableStream<Uint8Array>;
|
|
47
47
|
#readableController!: PushReadableStreamController<Uint8Array>;
|
|
48
48
|
get readable() {
|
|
49
49
|
return this.#readable;
|
|
@@ -54,12 +54,12 @@ export class AdbDaemonSocketController
|
|
|
54
54
|
|
|
55
55
|
#closed = false;
|
|
56
56
|
|
|
57
|
-
#closedPromise = new PromiseResolver<
|
|
57
|
+
readonly #closedPromise = new PromiseResolver<undefined>();
|
|
58
58
|
get closed() {
|
|
59
59
|
return this.#closedPromise.promise;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
#socket: AdbDaemonSocket;
|
|
62
|
+
readonly #socket: AdbDaemonSocket;
|
|
63
63
|
get socket() {
|
|
64
64
|
return this.#socket;
|
|
65
65
|
}
|
|
@@ -170,7 +170,7 @@ export class AdbDaemonSocketController
|
|
|
170
170
|
|
|
171
171
|
dispose() {
|
|
172
172
|
this.#readableController.close();
|
|
173
|
-
this.#closedPromise.resolve();
|
|
173
|
+
this.#closedPromise.resolve(undefined);
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -178,7 +178,7 @@ export class AdbDaemonSocketController
|
|
|
178
178
|
* A duplex stream representing a socket to ADB daemon.
|
|
179
179
|
*/
|
|
180
180
|
export class AdbDaemonSocket implements AdbDaemonSocketInfo, AdbSocket {
|
|
181
|
-
#controller: AdbDaemonSocketController;
|
|
181
|
+
readonly #controller: AdbDaemonSocketController;
|
|
182
182
|
|
|
183
183
|
get localId(): number {
|
|
184
184
|
return this.#controller.localId;
|
|
@@ -200,7 +200,7 @@ export class AdbDaemonSocket implements AdbDaemonSocketInfo, AdbSocket {
|
|
|
200
200
|
return this.#controller.writable;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
get closed(): Promise<
|
|
203
|
+
get closed(): Promise<undefined> {
|
|
204
204
|
return this.#controller.closed;
|
|
205
205
|
}
|
|
206
206
|
|
package/src/device-observer.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { MaybePromiseLike } from "@yume-chan/async";
|
|
|
2
2
|
import type { Event } from "@yume-chan/event";
|
|
3
3
|
|
|
4
4
|
export interface DeviceObserver<T> {
|
|
5
|
-
onDeviceAdd: Event<T[]>;
|
|
6
|
-
onDeviceRemove: Event<T[]>;
|
|
7
|
-
onListChange: Event<T[]>;
|
|
8
|
-
current: T[];
|
|
5
|
+
readonly onDeviceAdd: Event<readonly T[]>;
|
|
6
|
+
readonly onDeviceRemove: Event<readonly T[]>;
|
|
7
|
+
readonly onListChange: Event<readonly T[]>;
|
|
8
|
+
readonly current: readonly T[];
|
|
9
9
|
stop(): MaybePromiseLike<void>;
|
|
10
10
|
}
|
package/src/server/client.ts
CHANGED
|
@@ -3,122 +3,38 @@
|
|
|
3
3
|
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
4
4
|
import { PromiseResolver } from "@yume-chan/async";
|
|
5
5
|
import type { Event } from "@yume-chan/event";
|
|
6
|
-
import { EventEmitter } from "@yume-chan/event";
|
|
7
6
|
import { getUint64LittleEndian } from "@yume-chan/no-data-view";
|
|
8
7
|
import type {
|
|
9
8
|
AbortSignal,
|
|
10
9
|
MaybeConsumable,
|
|
11
10
|
ReadableWritablePair,
|
|
12
|
-
WritableStreamDefaultWriter,
|
|
13
11
|
} from "@yume-chan/stream-extra";
|
|
14
|
-
import {
|
|
15
|
-
BufferedReadableStream,
|
|
16
|
-
tryCancel,
|
|
17
|
-
tryClose,
|
|
18
|
-
} from "@yume-chan/stream-extra";
|
|
19
|
-
import {
|
|
20
|
-
bipedal,
|
|
21
|
-
decodeUtf8,
|
|
22
|
-
encodeUtf8,
|
|
23
|
-
TextDecoder,
|
|
24
|
-
} from "@yume-chan/struct";
|
|
12
|
+
import { AbortController } from "@yume-chan/stream-extra";
|
|
25
13
|
|
|
26
14
|
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
|
27
15
|
import { AdbBanner } from "../banner.js";
|
|
28
16
|
import type { DeviceObserver as DeviceObserverBase } from "../device-observer.js";
|
|
29
17
|
import type { AdbFeature } from "../features.js";
|
|
30
|
-
import { hexToNumber
|
|
18
|
+
import { hexToNumber } from "../utils/index.js";
|
|
31
19
|
|
|
20
|
+
import {
|
|
21
|
+
MDnsCommands,
|
|
22
|
+
WirelessCommands,
|
|
23
|
+
AlreadyConnectedError as _AlreadyConnectedError,
|
|
24
|
+
NetworkError as _NetworkError,
|
|
25
|
+
UnauthorizedError as _UnauthorizedError,
|
|
26
|
+
} from "./commands/index.js";
|
|
27
|
+
import { AdbServerDeviceObserverOwner } from "./observer.js";
|
|
28
|
+
import { AdbServerStream } from "./stream.js";
|
|
32
29
|
import { AdbServerTransport } from "./transport.js";
|
|
33
30
|
|
|
34
|
-
const OKAY = encodeUtf8("OKAY");
|
|
35
|
-
const FAIL = encodeUtf8("FAIL");
|
|
36
|
-
|
|
37
|
-
class AdbServerStream {
|
|
38
|
-
#connection: AdbServerClient.ServerConnection;
|
|
39
|
-
#buffered: BufferedReadableStream;
|
|
40
|
-
#writer: WritableStreamDefaultWriter<Uint8Array>;
|
|
41
|
-
|
|
42
|
-
constructor(connection: AdbServerClient.ServerConnection) {
|
|
43
|
-
this.#connection = connection;
|
|
44
|
-
this.#buffered = new BufferedReadableStream(connection.readable);
|
|
45
|
-
this.#writer = connection.writable.getWriter();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
readExactly(length: number): MaybePromiseLike<Uint8Array> {
|
|
49
|
-
return this.#buffered.readExactly(length);
|
|
50
|
-
}
|
|
51
|
-
|
|
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);
|
|
81
|
-
}
|
|
82
|
-
|
|
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
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (sequenceEqual(response, FAIL)) {
|
|
93
|
-
const reason = await this.readString();
|
|
94
|
-
throw new Error(reason);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
throw new Error(`Unexpected response: ${decodeUtf8(response)}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
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
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async dispose() {
|
|
111
|
-
void tryCancel(this.#buffered);
|
|
112
|
-
void tryClose(this.#writer);
|
|
113
|
-
await this.#connection.close();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
31
|
/**
|
|
118
32
|
* Client for the ADB Server.
|
|
119
33
|
*/
|
|
120
34
|
export class AdbServerClient {
|
|
121
|
-
static
|
|
35
|
+
static NetworkError = _NetworkError;
|
|
36
|
+
static UnauthorizedError = _UnauthorizedError;
|
|
37
|
+
static AlreadyConnectedError = _AlreadyConnectedError;
|
|
122
38
|
|
|
123
39
|
static parseDeviceList(value: string): AdbServerClient.Device[] {
|
|
124
40
|
const devices: AdbServerClient.Device[] = [];
|
|
@@ -194,8 +110,9 @@ export class AdbServerClient {
|
|
|
194
110
|
|
|
195
111
|
readonly connector: AdbServerClient.ServerConnector;
|
|
196
112
|
|
|
197
|
-
readonly wireless = new
|
|
198
|
-
readonly mDns = new
|
|
113
|
+
readonly wireless = new WirelessCommands(this);
|
|
114
|
+
readonly mDns = new MDnsCommands(this);
|
|
115
|
+
readonly #observerOwner = new AdbServerDeviceObserverOwner(this);
|
|
199
116
|
|
|
200
117
|
constructor(connector: AdbServerClient.ServerConnector) {
|
|
201
118
|
this.connector = connector;
|
|
@@ -240,11 +157,11 @@ export class AdbServerClient {
|
|
|
240
157
|
}
|
|
241
158
|
}
|
|
242
159
|
|
|
243
|
-
async validateVersion() {
|
|
160
|
+
async validateVersion(minimalVersion: number) {
|
|
244
161
|
const version = await this.getVersion();
|
|
245
|
-
if (version
|
|
162
|
+
if (version < minimalVersion) {
|
|
246
163
|
throw new Error(
|
|
247
|
-
`adb server version (${version}) doesn't match this client (${
|
|
164
|
+
`adb server version (${version}) doesn't match this client (${minimalVersion})`,
|
|
248
165
|
);
|
|
249
166
|
}
|
|
250
167
|
}
|
|
@@ -288,61 +205,10 @@ export class AdbServerClient {
|
|
|
288
205
|
/**
|
|
289
206
|
* Monitors device list changes.
|
|
290
207
|
*/
|
|
291
|
-
async trackDevices(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
};
|
|
208
|
+
async trackDevices(
|
|
209
|
+
options?: AdbServerClient.ServerConnectionOptions,
|
|
210
|
+
): Promise<AdbServerClient.DeviceObserver> {
|
|
211
|
+
return this.#observerOwner.createObserver(options);
|
|
346
212
|
}
|
|
347
213
|
|
|
348
214
|
/**
|
|
@@ -412,20 +278,22 @@ export class AdbServerClient {
|
|
|
412
278
|
device: AdbServerClient.DeviceSelector,
|
|
413
279
|
service: string,
|
|
414
280
|
): Promise<AdbServerClient.Socket> {
|
|
415
|
-
await this.validateVersion();
|
|
416
|
-
|
|
417
281
|
let switchService: string;
|
|
418
282
|
let transportId: bigint | undefined;
|
|
419
283
|
if (!device) {
|
|
284
|
+
await this.validateVersion(41);
|
|
420
285
|
switchService = `host:tport:any`;
|
|
421
286
|
} else if ("transportId" in device) {
|
|
422
287
|
switchService = `host:transport-id:${device.transportId}`;
|
|
423
288
|
transportId = device.transportId;
|
|
424
289
|
} else if ("serial" in device) {
|
|
290
|
+
await this.validateVersion(41);
|
|
425
291
|
switchService = `host:tport:serial:${device.serial}`;
|
|
426
292
|
} else if ("usb" in device) {
|
|
293
|
+
await this.validateVersion(41);
|
|
427
294
|
switchService = `host:tport:usb`;
|
|
428
295
|
} else if ("tcp" in device) {
|
|
296
|
+
await this.validateVersion(41);
|
|
429
297
|
switchService = `host:tport:local`;
|
|
430
298
|
} else {
|
|
431
299
|
throw new TypeError("Invalid device selector");
|
|
@@ -467,18 +335,7 @@ export class AdbServerClient {
|
|
|
467
335
|
throw e;
|
|
468
336
|
}
|
|
469
337
|
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Wait for a device to be connected or disconnected.
|
|
473
|
-
*
|
|
474
|
-
* `adb wait-for-<state>`
|
|
475
|
-
*
|
|
476
|
-
* @param device The device selector
|
|
477
|
-
* @param state The state to wait for
|
|
478
|
-
* @param options The options
|
|
479
|
-
* @returns A promise that resolves when the condition is met.
|
|
480
|
-
*/
|
|
481
|
-
async waitFor(
|
|
338
|
+
async #waitForUnchecked(
|
|
482
339
|
device: AdbServerClient.DeviceSelector,
|
|
483
340
|
state: "device" | "disconnect",
|
|
484
341
|
options?: AdbServerClient.ServerConnectionOptions,
|
|
@@ -513,6 +370,60 @@ export class AdbServerClient {
|
|
|
513
370
|
}
|
|
514
371
|
}
|
|
515
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Wait for a device to be connected or disconnected.
|
|
375
|
+
*
|
|
376
|
+
* `adb wait-for-<state>`
|
|
377
|
+
*
|
|
378
|
+
* @param device The device selector
|
|
379
|
+
* @param state The state to wait for
|
|
380
|
+
* @param options The options
|
|
381
|
+
* @returns A promise that resolves when the condition is met.
|
|
382
|
+
*/
|
|
383
|
+
async waitFor(
|
|
384
|
+
device: AdbServerClient.DeviceSelector,
|
|
385
|
+
state: "device" | "disconnect",
|
|
386
|
+
options?: AdbServerClient.ServerConnectionOptions,
|
|
387
|
+
): Promise<void> {
|
|
388
|
+
if (state === "disconnect") {
|
|
389
|
+
await this.validateVersion(41);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return this.#waitForUnchecked(device, state, options);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async waitForDisconnect(
|
|
396
|
+
transportId: bigint,
|
|
397
|
+
options?: AdbServerClient.ServerConnectionOptions,
|
|
398
|
+
): Promise<void> {
|
|
399
|
+
const serverVersion = await this.getVersion();
|
|
400
|
+
if (serverVersion >= 41) {
|
|
401
|
+
return this.#waitForUnchecked(
|
|
402
|
+
{ transportId },
|
|
403
|
+
"disconnect",
|
|
404
|
+
options,
|
|
405
|
+
);
|
|
406
|
+
} else {
|
|
407
|
+
const observer = await this.trackDevices(options);
|
|
408
|
+
return new Promise<void>((resolve, reject) => {
|
|
409
|
+
observer.onDeviceRemove((devices) => {
|
|
410
|
+
if (
|
|
411
|
+
devices.some(
|
|
412
|
+
(device) => device.transportId === transportId,
|
|
413
|
+
)
|
|
414
|
+
) {
|
|
415
|
+
observer.stop();
|
|
416
|
+
resolve();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
observer.onError((e) => {
|
|
420
|
+
observer.stop();
|
|
421
|
+
reject(e);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
516
427
|
/**
|
|
517
428
|
* Creates an ADB Transport for the specified device.
|
|
518
429
|
*/
|
|
@@ -533,12 +444,23 @@ export class AdbServerClient {
|
|
|
533
444
|
features,
|
|
534
445
|
);
|
|
535
446
|
|
|
536
|
-
|
|
447
|
+
const waitAbortController = new AbortController();
|
|
448
|
+
const disconnected = this.waitForDisconnect(transportId, {
|
|
449
|
+
unref: true,
|
|
450
|
+
signal: waitAbortController.signal,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const transport = new AdbServerTransport(
|
|
537
454
|
this,
|
|
538
455
|
info?.serial ?? "",
|
|
539
456
|
banner,
|
|
540
457
|
transportId,
|
|
458
|
+
disconnected,
|
|
541
459
|
);
|
|
460
|
+
|
|
461
|
+
void transport.disconnected.finally(() => waitAbortController.abort());
|
|
462
|
+
|
|
463
|
+
return transport;
|
|
542
464
|
}
|
|
543
465
|
}
|
|
544
466
|
|
|
@@ -582,7 +504,7 @@ export namespace AdbServerClient {
|
|
|
582
504
|
export interface ServerConnection
|
|
583
505
|
extends ReadableWritablePair<Uint8Array, MaybeConsumable<Uint8Array>>,
|
|
584
506
|
Closeable {
|
|
585
|
-
get closed(): Promise<
|
|
507
|
+
get closed(): Promise<undefined>;
|
|
586
508
|
}
|
|
587
509
|
|
|
588
510
|
export interface ServerConnector {
|
|
@@ -623,138 +545,11 @@ export namespace AdbServerClient {
|
|
|
623
545
|
transportId: bigint;
|
|
624
546
|
}
|
|
625
547
|
|
|
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
548
|
export interface DeviceObserver extends DeviceObserverBase<Device> {
|
|
758
549
|
onError: Event<Error>;
|
|
759
550
|
}
|
|
551
|
+
|
|
552
|
+
export type NetworkError = _NetworkError;
|
|
553
|
+
export type UnauthorizedError = _UnauthorizedError;
|
|
554
|
+
export type AlreadyConnectedError = _AlreadyConnectedError;
|
|
760
555
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// cspell:ignore mdns
|
|
2
|
+
|
|
3
|
+
import type { AdbServerClient } from "../client.js";
|
|
4
|
+
|
|
5
|
+
export class MDnsCommands {
|
|
6
|
+
readonly #client: AdbServerClient;
|
|
7
|
+
|
|
8
|
+
constructor(client: AdbServerClient) {
|
|
9
|
+
this.#client = client;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async check() {
|
|
13
|
+
const connection =
|
|
14
|
+
await this.#client.createConnection("host:mdns:check");
|
|
15
|
+
try {
|
|
16
|
+
const response = await connection.readString();
|
|
17
|
+
return !response.startsWith("ERROR:");
|
|
18
|
+
} finally {
|
|
19
|
+
await connection.dispose();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async getServices() {
|
|
24
|
+
const connection =
|
|
25
|
+
await this.#client.createConnection("host:mdns:services");
|
|
26
|
+
try {
|
|
27
|
+
const response = await connection.readString();
|
|
28
|
+
return response
|
|
29
|
+
.split("\n")
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((line) => {
|
|
32
|
+
const parts = line.split("\t");
|
|
33
|
+
return {
|
|
34
|
+
name: parts[0]!,
|
|
35
|
+
service: parts[1]!,
|
|
36
|
+
address: parts[2]!,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
} finally {
|
|
40
|
+
await connection.dispose();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|