@yume-chan/adb 0.0.18 → 0.0.20
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 +54 -0
- package/CHANGELOG.md +25 -1
- package/README.md +220 -103
- package/esm/adb.d.ts +43 -60
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +68 -249
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts +17 -0
- package/esm/banner.d.ts.map +1 -0
- package/esm/banner.js +67 -0
- package/esm/banner.js.map +1 -0
- package/esm/commands/base.d.ts +6 -6
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js +8 -8
- package/esm/commands/framebuffer.d.ts +53 -53
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +46 -46
- package/esm/commands/framebuffer.js.map +1 -1
- package/esm/commands/index.d.ts +7 -8
- package/esm/commands/index.d.ts.map +1 -1
- package/esm/commands/index.js +7 -8
- package/esm/commands/index.js.map +1 -1
- package/esm/commands/power.d.ts +22 -22
- package/esm/commands/power.js +48 -48
- package/esm/commands/reverse.d.ts +40 -28
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +130 -100
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.d.ts +57 -51
- package/esm/commands/subprocess/command.d.ts.map +1 -1
- package/esm/commands/subprocess/command.js +86 -80
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/index.d.ts +3 -3
- package/esm/commands/subprocess/index.js +3 -3
- package/esm/commands/subprocess/protocols/index.d.ts +3 -3
- package/esm/commands/subprocess/protocols/index.js +3 -3
- package/esm/commands/subprocess/protocols/none.d.ts +30 -35
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +65 -55
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +33 -38
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +172 -162
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +48 -49
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/types.js +1 -1
- package/esm/commands/subprocess/utils.d.ts +1 -1
- package/esm/commands/subprocess/utils.js +18 -18
- package/esm/commands/sync/index.d.ts +8 -7
- package/esm/commands/sync/index.d.ts.map +1 -1
- package/esm/commands/sync/index.js +8 -7
- package/esm/commands/sync/index.js.map +1 -1
- package/esm/commands/sync/list.d.ts +42 -40
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +67 -47
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +13 -11
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +35 -37
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +57 -7
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +89 -22
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.d.ts +26 -23
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +40 -42
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +19 -19
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +54 -54
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +22 -0
- package/esm/commands/sync/socket.d.ts.map +1 -0
- package/esm/commands/sync/socket.js +69 -0
- package/esm/commands/sync/socket.js.map +1 -0
- package/esm/commands/sync/stat.d.ts +72 -72
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +116 -104
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +65 -60
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +123 -149
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +5 -5
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +20 -18
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/{auth.d.ts → daemon/auth.d.ts} +50 -51
- package/esm/daemon/auth.d.ts.map +1 -0
- package/esm/{auth.js → daemon/auth.js} +95 -96
- package/esm/daemon/auth.js.map +1 -0
- package/esm/daemon/connection.d.ts +11 -0
- package/esm/daemon/connection.d.ts.map +1 -0
- package/esm/daemon/connection.js +2 -0
- package/esm/daemon/connection.js.map +1 -0
- package/esm/{crypto.d.ts → daemon/crypto.d.ts} +36 -36
- package/esm/daemon/crypto.d.ts.map +1 -0
- package/esm/{crypto.js → daemon/crypto.js} +244 -248
- package/esm/daemon/crypto.js.map +1 -0
- package/esm/daemon/device.d.ts +9 -0
- package/esm/daemon/device.d.ts.map +1 -0
- package/esm/daemon/device.js +2 -0
- package/esm/daemon/device.js.map +1 -0
- package/esm/daemon/dispatcher.d.ts +42 -0
- package/esm/daemon/dispatcher.d.ts.map +1 -0
- package/esm/{socket → daemon}/dispatcher.js +225 -234
- package/esm/daemon/dispatcher.js.map +1 -0
- package/esm/daemon/index.d.ts +8 -0
- package/esm/daemon/index.d.ts.map +1 -0
- package/esm/daemon/index.js +8 -0
- package/esm/daemon/index.js.map +1 -0
- package/esm/{packet.d.ts → daemon/packet.d.ts} +45 -46
- package/esm/daemon/packet.d.ts.map +1 -0
- package/esm/{packet.js → daemon/packet.js} +43 -48
- package/esm/daemon/packet.js.map +1 -0
- package/esm/daemon/socket.d.ts +58 -0
- package/esm/daemon/socket.d.ts.map +1 -0
- package/esm/{socket → daemon}/socket.js +121 -118
- package/esm/daemon/socket.js.map +1 -0
- package/esm/daemon/transport.d.ts +45 -0
- package/esm/daemon/transport.d.ts.map +1 -0
- package/esm/daemon/transport.js +184 -0
- package/esm/daemon/transport.js.map +1 -0
- package/esm/features.d.ts +10 -7
- package/esm/features.d.ts.map +1 -1
- package/esm/features.js +13 -10
- package/esm/features.js.map +1 -1
- package/esm/index.d.ts +7 -9
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/server/client.d.ts +78 -0
- package/esm/server/client.d.ts.map +1 -0
- package/esm/server/client.js +329 -0
- package/esm/server/client.js.map +1 -0
- package/esm/server/index.d.ts +3 -0
- package/esm/server/index.d.ts.map +1 -0
- package/esm/server/index.js +3 -0
- package/esm/server/index.js.map +1 -0
- package/esm/server/transport.d.ts +19 -0
- package/esm/server/transport.d.ts.map +1 -0
- package/esm/server/transport.js +44 -0
- package/esm/server/transport.js.map +1 -0
- package/esm/utils/auto-reset-event.d.ts +8 -9
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js +33 -33
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/base64.d.ts +27 -27
- package/esm/utils/base64.js +246 -246
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts +8 -0
- package/esm/utils/conditional-variable.d.ts.map +1 -0
- package/esm/utils/conditional-variable.js +34 -0
- package/esm/utils/conditional-variable.js.map +1 -0
- package/esm/utils/hex.d.ts +3 -0
- package/esm/utils/hex.d.ts.map +1 -0
- package/esm/utils/hex.js +55 -0
- package/esm/utils/hex.js.map +1 -0
- package/esm/utils/index.d.ts +6 -3
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +6 -3
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/no-op.d.ts +7 -0
- package/esm/utils/no-op.d.ts.map +1 -0
- package/esm/utils/no-op.js +12 -0
- package/esm/utils/no-op.js.map +1 -0
- package/package.json +14 -12
- package/src/adb.ts +47 -248
- package/src/banner.ts +82 -0
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +4 -4
- package/src/commands/index.ts +7 -8
- package/src/commands/power.ts +2 -2
- package/src/commands/reverse.ts +90 -70
- package/src/commands/subprocess/command.ts +12 -4
- package/src/commands/subprocess/index.ts +3 -3
- package/src/commands/subprocess/protocols/index.ts +3 -3
- package/src/commands/subprocess/protocols/none.ts +36 -23
- package/src/commands/subprocess/protocols/shell.ts +94 -74
- package/src/commands/subprocess/protocols/types.ts +7 -7
- package/src/commands/subprocess/utils.ts +1 -1
- package/src/commands/sync/index.ts +8 -7
- package/src/commands/sync/list.ts +44 -25
- package/src/commands/sync/pull.ts +40 -45
- package/src/commands/sync/push.ts +158 -39
- package/src/commands/sync/request.ts +21 -16
- package/src/commands/sync/response.ts +10 -9
- package/src/commands/sync/socket.ts +99 -0
- package/src/commands/sync/stat.ts +47 -42
- package/src/commands/sync/sync.ts +73 -116
- package/src/commands/tcpip.ts +9 -7
- package/src/{auth.ts → daemon/auth.ts} +24 -23
- package/src/{crypto.ts → daemon/crypto.ts} +25 -33
- package/src/daemon/device.ts +14 -0
- package/src/{socket → daemon}/dispatcher.ts +95 -117
- package/src/daemon/index.ts +7 -0
- package/src/daemon/packet.ts +75 -0
- package/src/{socket → daemon}/socket.ts +71 -56
- package/src/daemon/transport.ts +269 -0
- package/src/features.ts +9 -6
- package/src/index.ts +3 -5
- package/src/server/client.ts +476 -0
- package/src/server/index.ts +2 -0
- package/src/server/transport.ts +77 -0
- package/src/utils/auto-reset-event.ts +14 -15
- package/src/utils/conditional-variable.ts +45 -0
- package/src/utils/hex.ts +58 -0
- package/src/utils/index.ts +6 -3
- package/src/utils/no-op.ts +12 -0
- package/tsconfig.build.json +12 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/auth.d.ts.map +0 -1
- package/esm/auth.js.map +0 -1
- package/esm/backend.d.ts +0 -9
- package/esm/backend.d.ts.map +0 -1
- package/esm/backend.js +0 -2
- package/esm/backend.js.map +0 -1
- package/esm/commands/install.d.ts +0 -4
- package/esm/commands/install.d.ts.map +0 -1
- package/esm/commands/install.js +0 -29
- package/esm/commands/install.js.map +0 -1
- package/esm/crypto.d.ts.map +0 -1
- package/esm/crypto.js.map +0 -1
- package/esm/packet.d.ts.map +0 -1
- package/esm/packet.js.map +0 -1
- package/esm/socket/dispatcher.d.ts +0 -60
- package/esm/socket/dispatcher.d.ts.map +0 -1
- package/esm/socket/dispatcher.js.map +0 -1
- package/esm/socket/index.d.ts +0 -3
- package/esm/socket/index.d.ts.map +0 -1
- package/esm/socket/index.js +0 -3
- package/esm/socket/index.js.map +0 -1
- package/esm/socket/socket.d.ts +0 -60
- package/esm/socket/socket.d.ts.map +0 -1
- package/esm/socket/socket.js.map +0 -1
- package/src/backend.ts +0 -14
- package/src/commands/install.ts +0 -40
- package/src/packet.ts +0 -90
- package/src/socket/index.ts +0 -2
package/src/banner.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { AdbFeature } from "./features.js";
|
|
2
|
+
|
|
3
|
+
export enum AdbBannerKey {
|
|
4
|
+
Product = "ro.product.name",
|
|
5
|
+
Model = "ro.product.model",
|
|
6
|
+
Device = "ro.product.device",
|
|
7
|
+
Features = "features",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class AdbBanner {
|
|
11
|
+
public static parse(banner: string) {
|
|
12
|
+
let product: string | undefined;
|
|
13
|
+
let model: string | undefined;
|
|
14
|
+
let device: string | undefined;
|
|
15
|
+
let features: AdbFeature[] = [];
|
|
16
|
+
|
|
17
|
+
const pieces = banner.split("::");
|
|
18
|
+
if (pieces.length > 1) {
|
|
19
|
+
const props = pieces[1]!;
|
|
20
|
+
for (const prop of props.split(";")) {
|
|
21
|
+
if (!prop) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const keyValue = prop.split("=");
|
|
26
|
+
if (keyValue.length !== 2) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [key, value] = keyValue;
|
|
31
|
+
switch (key) {
|
|
32
|
+
case AdbBannerKey.Product:
|
|
33
|
+
product = value;
|
|
34
|
+
break;
|
|
35
|
+
case AdbBannerKey.Model:
|
|
36
|
+
model = value;
|
|
37
|
+
break;
|
|
38
|
+
case AdbBannerKey.Device:
|
|
39
|
+
device = value;
|
|
40
|
+
break;
|
|
41
|
+
case AdbBannerKey.Features:
|
|
42
|
+
features = value!.split(",") as AdbFeature[];
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new AdbBanner(product, model, device, features);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#product: string | undefined;
|
|
52
|
+
public get product() {
|
|
53
|
+
return this.#product;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#model: string | undefined;
|
|
57
|
+
public get model() {
|
|
58
|
+
return this.#model;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#device: string | undefined;
|
|
62
|
+
public get device() {
|
|
63
|
+
return this.#device;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#features: AdbFeature[] = [];
|
|
67
|
+
public get features() {
|
|
68
|
+
return this.#features;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public constructor(
|
|
72
|
+
product: string | undefined,
|
|
73
|
+
model: string | undefined,
|
|
74
|
+
device: string | undefined,
|
|
75
|
+
features: AdbFeature[]
|
|
76
|
+
) {
|
|
77
|
+
this.#product = product;
|
|
78
|
+
this.#model = model;
|
|
79
|
+
this.#device = device;
|
|
80
|
+
this.#features = features;
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/commands/base.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BufferedReadableStream } from "@yume-chan/stream-extra";
|
|
2
2
|
import Struct from "@yume-chan/struct";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { Adb } from "../adb.js";
|
|
5
5
|
|
|
6
6
|
const Version = new Struct({ littleEndian: true }).uint32("version");
|
|
7
7
|
|
|
@@ -20,7 +20,7 @@ export const AdbFrameBufferV1 = new Struct({ littleEndian: true })
|
|
|
20
20
|
.uint32("alpha_length")
|
|
21
21
|
.uint8Array("data", { lengthField: "size" });
|
|
22
22
|
|
|
23
|
-
export type AdbFrameBufferV1 = typeof AdbFrameBufferV1["TDeserializeResult"];
|
|
23
|
+
export type AdbFrameBufferV1 = (typeof AdbFrameBufferV1)["TDeserializeResult"];
|
|
24
24
|
|
|
25
25
|
export const AdbFrameBufferV2 = new Struct({ littleEndian: true })
|
|
26
26
|
.uint32("bpp")
|
|
@@ -38,7 +38,7 @@ export const AdbFrameBufferV2 = new Struct({ littleEndian: true })
|
|
|
38
38
|
.uint32("alpha_length")
|
|
39
39
|
.uint8Array("data", { lengthField: "size" });
|
|
40
40
|
|
|
41
|
-
export type AdbFrameBufferV2 = typeof AdbFrameBufferV2["TDeserializeResult"];
|
|
41
|
+
export type AdbFrameBufferV2 = (typeof AdbFrameBufferV2)["TDeserializeResult"];
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* ADB uses 8 int32 fields to describe bit depths
|
|
@@ -69,6 +69,6 @@ export async function framebuffer(adb: Adb): Promise<AdbFrameBuffer> {
|
|
|
69
69
|
case 2:
|
|
70
70
|
return AdbFrameBufferV2.deserialize(stream);
|
|
71
71
|
default:
|
|
72
|
-
throw new Error("
|
|
72
|
+
throw new Error("Unsupported FrameBuffer version");
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/commands/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from './tcpip.js';
|
|
1
|
+
export * from "./base.js";
|
|
2
|
+
export * from "./framebuffer.js";
|
|
3
|
+
export * from "./power.js";
|
|
4
|
+
export * from "./reverse.js";
|
|
5
|
+
export * from "./subprocess/index.js";
|
|
6
|
+
export * from "./sync/index.js";
|
|
7
|
+
export * from "./tcpip.js";
|
package/src/commands/power.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import { AdbCommandBase } from "./base.js";
|
|
7
7
|
|
|
8
8
|
export class AdbPower extends AdbCommandBase {
|
|
9
|
-
public reboot(
|
|
10
|
-
return this.adb.createSocketAndWait(`reboot:${
|
|
9
|
+
public reboot(mode = "") {
|
|
10
|
+
return this.adb.createSocketAndWait(`reboot:${mode}`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
public bootloader() {
|
package/src/commands/reverse.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
// cspell: ignore killforward
|
|
2
2
|
|
|
3
3
|
import { AutoDisposable } from "@yume-chan/event";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
import { type Adb } from "../adb.js";
|
|
11
|
-
import {
|
|
12
|
-
type AdbIncomingSocketHandler,
|
|
13
|
-
type AdbSocket,
|
|
14
|
-
} from "../socket/index.js";
|
|
15
|
-
import { decodeUtf8 } from "../utils/index.js";
|
|
4
|
+
import { BufferedReadableStream } from "@yume-chan/stream-extra";
|
|
5
|
+
import Struct, { ExactReadableEndedError } from "@yume-chan/struct";
|
|
6
|
+
|
|
7
|
+
import type { Adb, AdbIncomingSocketHandler } from "../adb.js";
|
|
8
|
+
import { decodeUtf8, hexToNumber } from "../utils/index.js";
|
|
16
9
|
|
|
17
10
|
export interface AdbForwardListener {
|
|
18
11
|
deviceSerial: string;
|
|
@@ -26,48 +19,58 @@ const AdbReverseStringResponse = new Struct()
|
|
|
26
19
|
.string("length", { length: 4 })
|
|
27
20
|
.string("content", { lengthField: "length", lengthFieldRadix: 16 });
|
|
28
21
|
|
|
22
|
+
export class AdbReverseError extends Error {
|
|
23
|
+
public constructor(message: string) {
|
|
24
|
+
super(message);
|
|
25
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class AdbReverseNotSupportedError extends AdbReverseError {
|
|
30
|
+
public constructor() {
|
|
31
|
+
super(
|
|
32
|
+
"ADB reverse tunnel is not supported on this device when connected wirelessly."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
const AdbReverseErrorResponse = new Struct()
|
|
30
|
-
.
|
|
38
|
+
.concat(AdbReverseStringResponse)
|
|
31
39
|
.postDeserialize((value) => {
|
|
32
|
-
|
|
40
|
+
// https://issuetracker.google.com/issues/37066218
|
|
41
|
+
// ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over WiFi),
|
|
42
|
+
// and returns this confusing "more than one device/emulator" error.
|
|
43
|
+
if (value.content === "more than one device/emulator") {
|
|
44
|
+
throw new AdbReverseNotSupportedError();
|
|
45
|
+
} else {
|
|
46
|
+
throw new AdbReverseError(value.content);
|
|
47
|
+
}
|
|
33
48
|
});
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
>();
|
|
40
|
-
|
|
41
|
-
protected deviceAddressToLocalAddress = new Map<string, string>();
|
|
50
|
+
async function readString(stream: BufferedReadableStream, length: number) {
|
|
51
|
+
const buffer = await stream.readExactly(length);
|
|
52
|
+
return decodeUtf8(buffer);
|
|
53
|
+
}
|
|
42
54
|
|
|
55
|
+
export class AdbReverseCommand extends AutoDisposable {
|
|
43
56
|
protected adb: Adb;
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
readonly #deviceAddressToLocalAddress = new Map<string, string>();
|
|
46
59
|
|
|
47
60
|
public constructor(adb: Adb) {
|
|
48
61
|
super();
|
|
49
62
|
|
|
50
63
|
this.adb = adb;
|
|
51
|
-
this.addDisposable(
|
|
52
|
-
this.adb.onIncomingSocket(this.handleIncomingSocket)
|
|
53
|
-
);
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
protected
|
|
57
|
-
let address = socket.serviceString;
|
|
58
|
-
// ADB daemon appends `\0` to the service string
|
|
59
|
-
address = address.replace(/\0/g, "");
|
|
60
|
-
return !!(await this.localAddressToHandler.get(address)?.(socket));
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
private async createBufferedStream(service: string) {
|
|
66
|
+
protected async createBufferedStream(service: string) {
|
|
64
67
|
const socket = await this.adb.createSocket(service);
|
|
65
68
|
return new BufferedReadableStream(socket.readable);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
protected async sendRequest(service: string) {
|
|
69
72
|
const stream = await this.createBufferedStream(service);
|
|
70
|
-
const success =
|
|
73
|
+
const success = (await readString(stream, 4)) === "OKAY";
|
|
71
74
|
if (!success) {
|
|
72
75
|
await AdbReverseErrorResponse.deserialize(stream);
|
|
73
76
|
}
|
|
@@ -91,18 +94,12 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
/**
|
|
94
|
-
*
|
|
95
|
-
* @param
|
|
96
|
-
* @param
|
|
97
|
+
* Add an already existing reverse tunnel. Depends on the transport type, this may not do anything.
|
|
98
|
+
* @param deviceAddress The address to be listened on device by ADB daemon. Or `tcp:0` to choose an available TCP port.
|
|
99
|
+
* @param localAddress The address that listens on the local machine.
|
|
97
100
|
* @returns `tcp:{ACTUAL_LISTENING_PORT}`, If `deviceAddress` is `tcp:0`; otherwise, `deviceAddress`.
|
|
98
101
|
*/
|
|
99
|
-
public async
|
|
100
|
-
deviceAddress: string,
|
|
101
|
-
localAddress: string,
|
|
102
|
-
handler: AdbIncomingSocketHandler
|
|
103
|
-
): Promise<string> {
|
|
104
|
-
// TODO(reverse): handle ADB bug
|
|
105
|
-
// https://issuetracker.google.com/issues/37066218
|
|
102
|
+
public async addExternal(deviceAddress: string, localAddress: string) {
|
|
106
103
|
const stream = await this.sendRequest(
|
|
107
104
|
`reverse:forward:${deviceAddress};${localAddress}`
|
|
108
105
|
);
|
|
@@ -110,50 +107,73 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
110
107
|
// `tcp:0` tells the device to pick an available port.
|
|
111
108
|
// On Android >=8, device will respond with the selected port for all `tcp:` requests.
|
|
112
109
|
if (deviceAddress.startsWith("tcp:")) {
|
|
113
|
-
|
|
110
|
+
const position = stream.position;
|
|
114
111
|
try {
|
|
115
|
-
length =
|
|
112
|
+
const length = hexToNumber(await stream.readExactly(4));
|
|
113
|
+
const port = await readString(stream, length);
|
|
114
|
+
deviceAddress = `tcp:${Number.parseInt(port, 10)}`;
|
|
116
115
|
} catch (e) {
|
|
117
|
-
if (
|
|
116
|
+
if (
|
|
117
|
+
e instanceof ExactReadableEndedError &&
|
|
118
|
+
stream.position === position
|
|
119
|
+
) {
|
|
120
|
+
// Android <8 doesn't have this response.
|
|
121
|
+
// (the stream is closed now)
|
|
122
|
+
// Can be safely ignored.
|
|
123
|
+
} else {
|
|
118
124
|
throw e;
|
|
119
125
|
}
|
|
120
|
-
|
|
121
|
-
// Android <8 doesn't have this response.
|
|
122
|
-
// (the stream is closed now)
|
|
123
|
-
// Can be safely ignored.
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (length !== undefined) {
|
|
127
|
-
const port = decodeUtf8(await stream.read(length));
|
|
128
|
-
deviceAddress = `tcp:${Number.parseInt(port, 10)}`;
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
this.localAddressToHandler.set(localAddress, handler);
|
|
133
|
-
this.deviceAddressToLocalAddress.set(deviceAddress, localAddress);
|
|
134
129
|
return deviceAddress;
|
|
130
|
+
}
|
|
135
131
|
|
|
136
|
-
|
|
132
|
+
/**
|
|
133
|
+
* @param deviceAddress The address to be listened on device by ADB daemon. Or `tcp:0` to choose an available TCP port.
|
|
134
|
+
* @param handler A callback to handle incoming connections.
|
|
135
|
+
* @param localAddressThe The address that listens on the local machine. May be `undefined` to let the transport choose an appropriate one.
|
|
136
|
+
* @returns `tcp:{ACTUAL_LISTENING_PORT}`, If `deviceAddress` is `tcp:0`; otherwise, `deviceAddress`.
|
|
137
|
+
* @throws {AdbReverseNotSupportedError} If ADB reverse tunnel is not supported on this device when connected wirelessly.
|
|
138
|
+
* @throws {AdbReverseError} If ADB daemon returns an error.
|
|
139
|
+
*/
|
|
140
|
+
public async add(
|
|
141
|
+
deviceAddress: string,
|
|
142
|
+
handler: AdbIncomingSocketHandler,
|
|
143
|
+
localAddress?: string
|
|
144
|
+
): Promise<string> {
|
|
145
|
+
localAddress = await this.adb.transport.addReverseTunnel(
|
|
146
|
+
handler,
|
|
147
|
+
localAddress
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
deviceAddress = await this.addExternal(deviceAddress, localAddress);
|
|
152
|
+
this.#deviceAddressToLocalAddress.set(deviceAddress, localAddress);
|
|
153
|
+
return deviceAddress;
|
|
154
|
+
} catch (e) {
|
|
155
|
+
await this.adb.transport.removeReverseTunnel(localAddress);
|
|
156
|
+
throw e;
|
|
157
|
+
}
|
|
137
158
|
}
|
|
138
159
|
|
|
139
160
|
public async remove(deviceAddress: string): Promise<void> {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
this.
|
|
144
|
-
this.deviceAddressToLocalAddress.get(deviceAddress)!
|
|
145
|
-
);
|
|
146
|
-
this.deviceAddressToLocalAddress.delete(deviceAddress);
|
|
161
|
+
const localAddress =
|
|
162
|
+
this.#deviceAddressToLocalAddress.get(deviceAddress);
|
|
163
|
+
if (localAddress) {
|
|
164
|
+
await this.adb.transport.removeReverseTunnel(localAddress);
|
|
147
165
|
}
|
|
148
166
|
|
|
167
|
+
await this.sendRequest(`reverse:killforward:${deviceAddress}`);
|
|
168
|
+
|
|
149
169
|
// No need to close the stream, device will close it
|
|
150
170
|
}
|
|
151
171
|
|
|
152
172
|
public async removeAll(): Promise<void> {
|
|
153
|
-
await this.
|
|
173
|
+
await this.adb.transport.clearReverseTunnels();
|
|
174
|
+
this.#deviceAddressToLocalAddress.clear();
|
|
154
175
|
|
|
155
|
-
this.
|
|
156
|
-
this.localAddressToHandler.clear();
|
|
176
|
+
await this.sendRequest(`reverse:killforward-all`);
|
|
157
177
|
|
|
158
178
|
// No need to close the stream, device will close it
|
|
159
179
|
}
|
|
@@ -2,11 +2,13 @@ import { DecodeUtf8Stream, GatherStringStream } from "@yume-chan/stream-extra";
|
|
|
2
2
|
|
|
3
3
|
import { AdbCommandBase } from "../base.js";
|
|
4
4
|
|
|
5
|
+
import type {
|
|
6
|
+
AdbSubprocessProtocol,
|
|
7
|
+
AdbSubprocessProtocolConstructor,
|
|
8
|
+
} from "./protocols/index.js";
|
|
5
9
|
import {
|
|
6
10
|
AdbSubprocessNoneProtocol,
|
|
7
11
|
AdbSubprocessShellProtocol,
|
|
8
|
-
type AdbSubprocessProtocol,
|
|
9
|
-
type AdbSubprocessProtocolConstructor,
|
|
10
12
|
} from "./protocols/index.js";
|
|
11
13
|
|
|
12
14
|
export interface AdbSubprocessOptions {
|
|
@@ -65,7 +67,10 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/**
|
|
68
|
-
* Spawns an executable in PTY
|
|
70
|
+
* Spawns an executable in PTY mode.
|
|
71
|
+
*
|
|
72
|
+
* Redirection mode is enough for most simple commands, but PTY mode is required for
|
|
73
|
+
* commands that manipulate the terminal, such as `vi` and `less`.
|
|
69
74
|
* @param command The command to run. If omitted, the default shell will be spawned.
|
|
70
75
|
* @param options The options for creating the `AdbSubprocessProtocol`
|
|
71
76
|
* @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
|
|
@@ -78,7 +83,10 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
/**
|
|
81
|
-
* Spawns an executable and
|
|
86
|
+
* Spawns an executable and redirect the standard input/output stream.
|
|
87
|
+
*
|
|
88
|
+
* Redirection mode is enough for most simple commands, but PTY mode is required for
|
|
89
|
+
* commands that manipulate the terminal, such as `vi` and `less`.
|
|
82
90
|
* @param command The command to run, or an array of strings containing both command and args.
|
|
83
91
|
* @param options The options for creating the `AdbSubprocessProtocol`
|
|
84
92
|
* @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from "./command.js";
|
|
2
|
+
export * from "./protocols/index.js";
|
|
3
|
+
export * from "./utils.js";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from "./none.js";
|
|
2
|
+
export * from "./shell.js";
|
|
3
|
+
export * from "./types.js";
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { DuplexStreamFactory, ReadableStream } from
|
|
1
|
+
import { DuplexStreamFactory, ReadableStream } from "@yume-chan/stream-extra";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { type AdbSocket } from "../../../socket/index.js";
|
|
3
|
+
import type { Adb, AdbSocket } from "../../../adb.js";
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import type { AdbSubprocessProtocol } from "./types.js";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* The legacy shell
|
|
@@ -14,54 +13,68 @@ import { type AdbSubprocessProtocol } from "./types.js";
|
|
|
14
13
|
* * `resize`: No
|
|
15
14
|
*/
|
|
16
15
|
export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
17
|
-
public static isSupported() {
|
|
16
|
+
public static isSupported() {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
public static async pty(adb: Adb, command: string) {
|
|
20
|
-
return new AdbSubprocessNoneProtocol(
|
|
21
|
+
return new AdbSubprocessNoneProtocol(
|
|
22
|
+
await adb.createSocket(`shell:${command}`)
|
|
23
|
+
);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
public static async raw(adb: Adb, command: string) {
|
|
24
27
|
// `shell,raw:${command}` also triggers raw mode,
|
|
25
28
|
// But is not supported on Android version <7.
|
|
26
|
-
return new AdbSubprocessNoneProtocol(
|
|
29
|
+
return new AdbSubprocessNoneProtocol(
|
|
30
|
+
await adb.createSocket(`exec:${command}`)
|
|
31
|
+
);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
readonly #socket: AdbSocket;
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
readonly #duplex: DuplexStreamFactory<Uint8Array, Uint8Array>;
|
|
32
37
|
|
|
33
38
|
// Legacy shell forwards all data to stdin.
|
|
34
|
-
public get stdin() {
|
|
39
|
+
public get stdin() {
|
|
40
|
+
return this.#socket.writable;
|
|
41
|
+
}
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
#stdout: ReadableStream<Uint8Array>;
|
|
37
44
|
/**
|
|
38
45
|
* Legacy shell mixes stdout and stderr.
|
|
39
46
|
*/
|
|
40
|
-
public get stdout() {
|
|
47
|
+
public get stdout() {
|
|
48
|
+
return this.#stdout;
|
|
49
|
+
}
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
#stderr: ReadableStream<Uint8Array>;
|
|
43
52
|
/**
|
|
44
53
|
* `stderr` will always be empty.
|
|
45
54
|
*/
|
|
46
|
-
public get stderr() {
|
|
55
|
+
public get stderr() {
|
|
56
|
+
return this.#stderr;
|
|
57
|
+
}
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
public get exit() {
|
|
59
|
+
#exit: Promise<number>;
|
|
60
|
+
public get exit() {
|
|
61
|
+
return this.#exit;
|
|
62
|
+
}
|
|
50
63
|
|
|
51
64
|
public constructor(socket: AdbSocket) {
|
|
52
|
-
this
|
|
65
|
+
this.#socket = socket;
|
|
53
66
|
|
|
54
67
|
// Link `stdout`, `stderr` and `stdin` together,
|
|
55
68
|
// so closing any of them will close the others.
|
|
56
|
-
this
|
|
69
|
+
this.#duplex = new DuplexStreamFactory<Uint8Array, Uint8Array>({
|
|
57
70
|
close: async () => {
|
|
58
|
-
await this
|
|
71
|
+
await this.#socket.close();
|
|
59
72
|
},
|
|
60
73
|
});
|
|
61
74
|
|
|
62
|
-
this
|
|
63
|
-
this
|
|
64
|
-
this
|
|
75
|
+
this.#stdout = this.#duplex.wrapReadable(this.#socket.readable);
|
|
76
|
+
this.#stderr = this.#duplex.wrapReadable(new ReadableStream());
|
|
77
|
+
this.#exit = this.#duplex.closed.then(() => 0);
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
public resize() {
|
|
@@ -69,6 +82,6 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
public kill() {
|
|
72
|
-
return this
|
|
85
|
+
return this.#duplex.close();
|
|
73
86
|
}
|
|
74
87
|
}
|