@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/adb.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Consumable, ReadableWritablePair } from "@yume-chan/stream-extra";
|
|
2
|
-
import {
|
|
2
|
+
import { ConcatStringStream, DecodeUtf8Stream } from "@yume-chan/stream-extra";
|
|
3
3
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
4
4
|
|
|
5
5
|
import type { AdbBanner } from "./banner.js";
|
|
@@ -15,20 +15,22 @@ import {
|
|
|
15
15
|
} from "./commands/index.js";
|
|
16
16
|
import type { AdbFeature } from "./features.js";
|
|
17
17
|
|
|
18
|
+
export interface Closeable {
|
|
19
|
+
close(): ValueOrPromise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
export interface AdbSocket
|
|
19
23
|
extends ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>,
|
|
20
24
|
Closeable {
|
|
21
|
-
|
|
25
|
+
get service(): string;
|
|
26
|
+
|
|
27
|
+
get closed(): Promise<void>;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
export type AdbIncomingSocketHandler = (
|
|
25
|
-
socket: AdbSocket
|
|
31
|
+
socket: AdbSocket,
|
|
26
32
|
) => ValueOrPromise<void>;
|
|
27
33
|
|
|
28
|
-
export interface Closeable {
|
|
29
|
-
close(): ValueOrPromise<void>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
34
|
export interface AdbTransport extends Closeable {
|
|
33
35
|
readonly serial: string;
|
|
34
36
|
|
|
@@ -42,7 +44,7 @@ export interface AdbTransport extends Closeable {
|
|
|
42
44
|
|
|
43
45
|
addReverseTunnel(
|
|
44
46
|
handler: AdbIncomingSocketHandler,
|
|
45
|
-
address?: string
|
|
47
|
+
address?: string,
|
|
46
48
|
): ValueOrPromise<string>;
|
|
47
49
|
|
|
48
50
|
removeReverseTunnel(address: string): ValueOrPromise<void>;
|
|
@@ -51,30 +53,30 @@ export interface AdbTransport extends Closeable {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export class Adb implements Closeable {
|
|
54
|
-
|
|
56
|
+
readonly transport: AdbTransport;
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
get serial() {
|
|
57
59
|
return this.transport.serial;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
get maxPayloadSize() {
|
|
61
63
|
return this.transport.maxPayloadSize;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
get banner() {
|
|
65
67
|
return this.transport.banner;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
get disconnected() {
|
|
69
71
|
return this.transport.disconnected;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
readonly subprocess: AdbSubprocess;
|
|
75
|
+
readonly power: AdbPower;
|
|
76
|
+
readonly reverse: AdbReverseCommand;
|
|
77
|
+
readonly tcpip: AdbTcpIpCommand;
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
constructor(transport: AdbTransport) {
|
|
78
80
|
this.transport = transport;
|
|
79
81
|
|
|
80
82
|
this.subprocess = new AdbSubprocess(this);
|
|
@@ -83,24 +85,22 @@ export class Adb implements Closeable {
|
|
|
83
85
|
this.tcpip = new AdbTcpIpCommand(this);
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
supportsFeature(feature: AdbFeature): boolean {
|
|
87
89
|
return this.banner.features.includes(feature);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
async createSocket(service: string): Promise<AdbSocket> {
|
|
91
93
|
return this.transport.connect(service);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
async createSocketAndWait(service: string): Promise<string> {
|
|
95
97
|
const socket = await this.createSocket(service);
|
|
96
|
-
|
|
97
|
-
await socket.readable
|
|
98
|
+
return await socket.readable
|
|
98
99
|
.pipeThrough(new DecodeUtf8Stream())
|
|
99
|
-
.
|
|
100
|
-
return gatherStream.result;
|
|
100
|
+
.pipeThrough(new ConcatStringStream());
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
async getProp(key: string): Promise<string> {
|
|
104
104
|
const stdout = await this.subprocess.spawnAndWaitLegacy([
|
|
105
105
|
"getprop",
|
|
106
106
|
key,
|
|
@@ -108,26 +108,40 @@ export class Adb implements Closeable {
|
|
|
108
108
|
return stdout.trim();
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
async rm(
|
|
112
|
+
filenames: string | string[],
|
|
113
|
+
options?: { recursive?: boolean; force?: boolean },
|
|
114
|
+
): Promise<string> {
|
|
115
|
+
const args = ["rm"];
|
|
116
|
+
if (options?.recursive) {
|
|
117
|
+
args.push("-r");
|
|
118
|
+
}
|
|
119
|
+
if (options?.force) {
|
|
120
|
+
args.push("-f");
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(filenames)) {
|
|
123
|
+
for (const filename of filenames) {
|
|
124
|
+
args.push(escapeArg(filename));
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
args.push(escapeArg(filenames));
|
|
128
|
+
}
|
|
112
129
|
// https://android.googlesource.com/platform/packages/modules/adb/+/1a0fb8846d4e6b671c8aa7f137a8c21d7b248716/client/adb_install.cpp#984
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
...filenames.map((arg) => escapeArg(arg)),
|
|
116
|
-
"</dev/null",
|
|
117
|
-
]);
|
|
130
|
+
args.push("</dev/null");
|
|
131
|
+
const stdout = await this.subprocess.spawnAndWaitLegacy(args);
|
|
118
132
|
return stdout;
|
|
119
133
|
}
|
|
120
134
|
|
|
121
|
-
|
|
135
|
+
async sync(): Promise<AdbSync> {
|
|
122
136
|
const socket = await this.createSocket("sync:");
|
|
123
137
|
return new AdbSync(this, socket);
|
|
124
138
|
}
|
|
125
139
|
|
|
126
|
-
|
|
140
|
+
async framebuffer(): Promise<AdbFrameBuffer> {
|
|
127
141
|
return framebuffer(this);
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
|
|
144
|
+
async close(): Promise<void> {
|
|
131
145
|
await this.transport.close();
|
|
132
146
|
}
|
|
133
147
|
}
|
package/src/banner.ts
CHANGED
|
@@ -8,7 +8,7 @@ export enum AdbBannerKey {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export class AdbBanner {
|
|
11
|
-
|
|
11
|
+
static parse(banner: string) {
|
|
12
12
|
let product: string | undefined;
|
|
13
13
|
let model: string | undefined;
|
|
14
14
|
let device: string | undefined;
|
|
@@ -49,30 +49,30 @@ export class AdbBanner {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
#product: string | undefined;
|
|
52
|
-
|
|
52
|
+
get product() {
|
|
53
53
|
return this.#product;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
#model: string | undefined;
|
|
57
|
-
|
|
57
|
+
get model() {
|
|
58
58
|
return this.#model;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
#device: string | undefined;
|
|
62
|
-
|
|
62
|
+
get device() {
|
|
63
63
|
return this.#device;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
#features: AdbFeature[] = [];
|
|
67
|
-
|
|
67
|
+
get features() {
|
|
68
68
|
return this.#features;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
constructor(
|
|
72
72
|
product: string | undefined,
|
|
73
73
|
model: string | undefined,
|
|
74
74
|
device: string | undefined,
|
|
75
|
-
features: AdbFeature[]
|
|
75
|
+
features: AdbFeature[],
|
|
76
76
|
) {
|
|
77
77
|
this.#product = product;
|
|
78
78
|
this.#model = model;
|
package/src/commands/base.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BufferedReadableStream } from "@yume-chan/stream-extra";
|
|
2
|
-
import Struct from "@yume-chan/struct";
|
|
2
|
+
import Struct, { StructEmptyError } from "@yume-chan/struct";
|
|
3
3
|
|
|
4
4
|
import type { Adb } from "../adb.js";
|
|
5
5
|
|
|
@@ -58,10 +58,38 @@ export type AdbFrameBufferV2 = (typeof AdbFrameBufferV2)["TDeserializeResult"];
|
|
|
58
58
|
*/
|
|
59
59
|
export type AdbFrameBuffer = AdbFrameBufferV1 | AdbFrameBufferV2;
|
|
60
60
|
|
|
61
|
+
export abstract class AdbFrameBufferError extends Error {
|
|
62
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
63
|
+
super(message, options);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class AdbFrameBufferUnsupportedVersionError extends AdbFrameBufferError {
|
|
68
|
+
constructor(version: number) {
|
|
69
|
+
super(`Unsupported FrameBuffer version ${version}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class AdbFrameBufferForbiddenError extends AdbFrameBufferError {
|
|
74
|
+
constructor() {
|
|
75
|
+
super("FrameBuffer is disabled by current app");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
export async function framebuffer(adb: Adb): Promise<AdbFrameBuffer> {
|
|
62
80
|
const socket = await adb.createSocket("framebuffer:");
|
|
63
81
|
const stream = new BufferedReadableStream(socket.readable);
|
|
64
|
-
|
|
82
|
+
|
|
83
|
+
let version: number;
|
|
84
|
+
try {
|
|
85
|
+
({ version } = await Version.deserialize(stream));
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (e instanceof StructEmptyError) {
|
|
88
|
+
throw new AdbFrameBufferForbiddenError();
|
|
89
|
+
}
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
|
|
65
93
|
switch (version) {
|
|
66
94
|
case 1:
|
|
67
95
|
// TODO: AdbFrameBuffer: does all v1 responses uses the same color space? Add it so the command returns same format for all versions.
|
|
@@ -69,6 +97,6 @@ export async function framebuffer(adb: Adb): Promise<AdbFrameBuffer> {
|
|
|
69
97
|
case 2:
|
|
70
98
|
return AdbFrameBufferV2.deserialize(stream);
|
|
71
99
|
default:
|
|
72
|
-
throw new
|
|
100
|
+
throw new AdbFrameBufferUnsupportedVersionError(version);
|
|
73
101
|
}
|
|
74
102
|
}
|
package/src/commands/power.ts
CHANGED
|
@@ -6,23 +6,23 @@
|
|
|
6
6
|
import { AdbCommandBase } from "./base.js";
|
|
7
7
|
|
|
8
8
|
export class AdbPower extends AdbCommandBase {
|
|
9
|
-
|
|
9
|
+
reboot(mode = "") {
|
|
10
10
|
return this.adb.createSocketAndWait(`reboot:${mode}`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
bootloader() {
|
|
14
14
|
return this.reboot("bootloader");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
fastboot() {
|
|
18
18
|
return this.reboot("fastboot");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
recovery() {
|
|
22
22
|
return this.reboot("recovery");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
sideload() {
|
|
26
26
|
return this.reboot("sideload");
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -31,15 +31,15 @@ export class AdbPower extends AdbCommandBase {
|
|
|
31
31
|
*
|
|
32
32
|
* Only works on some Qualcomm devices.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
qualcommEdlMode() {
|
|
35
35
|
return this.reboot("edl");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
powerOff() {
|
|
39
39
|
return this.adb.subprocess.spawnAndWaitLegacy(["reboot", "-p"]);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
powerButton(longPress = false) {
|
|
43
43
|
return this.adb.subprocess.spawnAndWaitLegacy([
|
|
44
44
|
"input",
|
|
45
45
|
"keyevent",
|
|
@@ -52,7 +52,7 @@ export class AdbPower extends AdbCommandBase {
|
|
|
52
52
|
*
|
|
53
53
|
* Only works on Samsung devices.
|
|
54
54
|
*/
|
|
55
|
-
|
|
55
|
+
samsungOdin() {
|
|
56
56
|
return this.reboot("download");
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/commands/reverse.ts
CHANGED
|
@@ -20,16 +20,16 @@ const AdbReverseStringResponse = new Struct()
|
|
|
20
20
|
.string("content", { lengthField: "length", lengthFieldRadix: 16 });
|
|
21
21
|
|
|
22
22
|
export class AdbReverseError extends Error {
|
|
23
|
-
|
|
23
|
+
constructor(message: string) {
|
|
24
24
|
super(message);
|
|
25
25
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export class AdbReverseNotSupportedError extends AdbReverseError {
|
|
30
|
-
|
|
30
|
+
constructor() {
|
|
31
31
|
super(
|
|
32
|
-
"ADB reverse tunnel is not supported on this device when connected wirelessly."
|
|
32
|
+
"ADB reverse tunnel is not supported on this device when connected wirelessly.",
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -57,7 +57,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
57
57
|
|
|
58
58
|
readonly #deviceAddressToLocalAddress = new Map<string, string>();
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
constructor(adb: Adb) {
|
|
61
61
|
super();
|
|
62
62
|
|
|
63
63
|
this.adb = adb;
|
|
@@ -77,7 +77,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
77
77
|
return stream;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
async list(): Promise<AdbForwardListener[]> {
|
|
81
81
|
const stream = await this.createBufferedStream("reverse:list-forward");
|
|
82
82
|
|
|
83
83
|
const response = await AdbReverseStringResponse.deserialize(stream);
|
|
@@ -85,7 +85,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
85
85
|
const [deviceSerial, localName, remoteName] = line.split(" ") as [
|
|
86
86
|
string,
|
|
87
87
|
string,
|
|
88
|
-
string
|
|
88
|
+
string,
|
|
89
89
|
];
|
|
90
90
|
return { deviceSerial, localName, remoteName };
|
|
91
91
|
});
|
|
@@ -99,9 +99,9 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
99
99
|
* @param localAddress The address that listens on the local machine.
|
|
100
100
|
* @returns `tcp:{ACTUAL_LISTENING_PORT}`, If `deviceAddress` is `tcp:0`; otherwise, `deviceAddress`.
|
|
101
101
|
*/
|
|
102
|
-
|
|
102
|
+
async addExternal(deviceAddress: string, localAddress: string) {
|
|
103
103
|
const stream = await this.sendRequest(
|
|
104
|
-
`reverse:forward:${deviceAddress};${localAddress}
|
|
104
|
+
`reverse:forward:${deviceAddress};${localAddress}`,
|
|
105
105
|
);
|
|
106
106
|
|
|
107
107
|
// `tcp:0` tells the device to pick an available port.
|
|
@@ -137,14 +137,14 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
137
137
|
* @throws {AdbReverseNotSupportedError} If ADB reverse tunnel is not supported on this device when connected wirelessly.
|
|
138
138
|
* @throws {AdbReverseError} If ADB daemon returns an error.
|
|
139
139
|
*/
|
|
140
|
-
|
|
140
|
+
async add(
|
|
141
141
|
deviceAddress: string,
|
|
142
142
|
handler: AdbIncomingSocketHandler,
|
|
143
|
-
localAddress?: string
|
|
143
|
+
localAddress?: string,
|
|
144
144
|
): Promise<string> {
|
|
145
145
|
localAddress = await this.adb.transport.addReverseTunnel(
|
|
146
146
|
handler,
|
|
147
|
-
localAddress
|
|
147
|
+
localAddress,
|
|
148
148
|
);
|
|
149
149
|
|
|
150
150
|
try {
|
|
@@ -157,7 +157,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
async remove(deviceAddress: string): Promise<void> {
|
|
161
161
|
const localAddress =
|
|
162
162
|
this.#deviceAddressToLocalAddress.get(deviceAddress);
|
|
163
163
|
if (localAddress) {
|
|
@@ -169,7 +169,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
169
169
|
// No need to close the stream, device will close it
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
async removeAll(): Promise<void> {
|
|
173
173
|
await this.adb.transport.clearReverseTunnels();
|
|
174
174
|
this.#deviceAddressToLocalAddress.clear();
|
|
175
175
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConcatStringStream, DecodeUtf8Stream } from "@yume-chan/stream-extra";
|
|
2
2
|
|
|
3
3
|
import { AdbCommandBase } from "../base.js";
|
|
4
4
|
|
|
@@ -37,10 +37,10 @@ export interface AdbSubprocessWaitResult {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export class AdbSubprocess extends AdbCommandBase {
|
|
40
|
-
|
|
40
|
+
async #createProtocol(
|
|
41
41
|
mode: "pty" | "raw",
|
|
42
42
|
command?: string | string[],
|
|
43
|
-
options?: Partial<AdbSubprocessOptions
|
|
43
|
+
options?: Partial<AdbSubprocessOptions>,
|
|
44
44
|
): Promise<AdbSubprocessProtocol> {
|
|
45
45
|
const { protocols } = { ...DEFAULT_OPTIONS, ...options };
|
|
46
46
|
|
|
@@ -75,11 +75,11 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
75
75
|
* @param options The options for creating the `AdbSubprocessProtocol`
|
|
76
76
|
* @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
shell(
|
|
79
79
|
command?: string | string[],
|
|
80
|
-
options?: Partial<AdbSubprocessOptions
|
|
80
|
+
options?: Partial<AdbSubprocessOptions>,
|
|
81
81
|
): Promise<AdbSubprocessProtocol> {
|
|
82
|
-
return this
|
|
82
|
+
return this.#createProtocol("pty", command, options);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -91,11 +91,11 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
91
91
|
* @param options The options for creating the `AdbSubprocessProtocol`
|
|
92
92
|
* @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
|
|
93
93
|
*/
|
|
94
|
-
|
|
94
|
+
spawn(
|
|
95
95
|
command: string | string[],
|
|
96
|
-
options?: Partial<AdbSubprocessOptions
|
|
96
|
+
options?: Partial<AdbSubprocessOptions>,
|
|
97
97
|
): Promise<AdbSubprocessProtocol> {
|
|
98
|
-
return this
|
|
98
|
+
return this.#createProtocol("raw", command, options);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
@@ -104,24 +104,25 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
104
104
|
* @param options The options for creating the `AdbSubprocessProtocol`
|
|
105
105
|
* @returns The entire output of the command
|
|
106
106
|
*/
|
|
107
|
-
|
|
107
|
+
async spawnAndWait(
|
|
108
108
|
command: string | string[],
|
|
109
|
-
options?: Partial<AdbSubprocessOptions
|
|
109
|
+
options?: Partial<AdbSubprocessOptions>,
|
|
110
110
|
): Promise<AdbSubprocessWaitResult> {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
const stdout =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
const process = await this.spawn(command, options);
|
|
112
|
+
|
|
113
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
114
|
+
process.stdout
|
|
115
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
116
|
+
.pipeThrough(new ConcatStringStream()),
|
|
117
|
+
process.stderr
|
|
118
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
119
|
+
.pipeThrough(new ConcatStringStream()),
|
|
120
|
+
process.exit,
|
|
120
121
|
]);
|
|
121
122
|
|
|
122
123
|
return {
|
|
123
|
-
stdout
|
|
124
|
-
stderr
|
|
124
|
+
stdout,
|
|
125
|
+
stderr,
|
|
125
126
|
exitCode,
|
|
126
127
|
};
|
|
127
128
|
}
|
|
@@ -131,9 +132,7 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
131
132
|
* @param command The command to run
|
|
132
133
|
* @returns The entire output of the command
|
|
133
134
|
*/
|
|
134
|
-
|
|
135
|
-
command: string | string[]
|
|
136
|
-
): Promise<string> {
|
|
135
|
+
async spawnAndWaitLegacy(command: string | string[]): Promise<string> {
|
|
137
136
|
const { stdout } = await this.spawnAndWait(command, {
|
|
138
137
|
protocols: [AdbSubprocessNoneProtocol],
|
|
139
138
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Consumable, WritableStream } from "@yume-chan/stream-extra";
|
|
2
|
+
import { ReadableStream } from "@yume-chan/stream-extra";
|
|
2
3
|
|
|
3
4
|
import type { Adb, AdbSocket } from "../../../adb.js";
|
|
5
|
+
import { unreachable } from "../../../utils/index.js";
|
|
4
6
|
|
|
5
7
|
import type { AdbSubprocessProtocol } from "./types.js";
|
|
6
8
|
|
|
@@ -13,75 +15,69 @@ import type { AdbSubprocessProtocol } from "./types.js";
|
|
|
13
15
|
* * `resize`: No
|
|
14
16
|
*/
|
|
15
17
|
export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
16
|
-
|
|
18
|
+
static isSupported() {
|
|
17
19
|
return true;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
static async pty(adb: Adb, command: string) {
|
|
21
23
|
return new AdbSubprocessNoneProtocol(
|
|
22
|
-
await adb.createSocket(`shell:${command}`)
|
|
24
|
+
await adb.createSocket(`shell:${command}`),
|
|
23
25
|
);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
static async raw(adb: Adb, command: string) {
|
|
27
29
|
// `shell,raw:${command}` also triggers raw mode,
|
|
28
30
|
// But is not supported on Android version <7.
|
|
29
31
|
return new AdbSubprocessNoneProtocol(
|
|
30
|
-
await adb.createSocket(`exec:${command}`)
|
|
32
|
+
await adb.createSocket(`exec:${command}`),
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
readonly #socket: AdbSocket;
|
|
35
37
|
|
|
36
|
-
readonly #duplex: DuplexStreamFactory<Uint8Array, Uint8Array>;
|
|
37
|
-
|
|
38
38
|
// Legacy shell forwards all data to stdin.
|
|
39
|
-
|
|
39
|
+
get stdin(): WritableStream<Consumable<Uint8Array>> {
|
|
40
40
|
return this.#socket.writable;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
#stdout: ReadableStream<Uint8Array>;
|
|
44
43
|
/**
|
|
45
44
|
* Legacy shell mixes stdout and stderr.
|
|
46
45
|
*/
|
|
47
|
-
|
|
48
|
-
return this.#
|
|
46
|
+
get stdout(): ReadableStream<Uint8Array> {
|
|
47
|
+
return this.#socket.readable;
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
#stderr: ReadableStream<Uint8Array>;
|
|
52
51
|
/**
|
|
53
52
|
* `stderr` will always be empty.
|
|
54
53
|
*/
|
|
55
|
-
|
|
54
|
+
get stderr(): ReadableStream<Uint8Array> {
|
|
56
55
|
return this.#stderr;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
#exit: Promise<number>;
|
|
60
|
-
|
|
59
|
+
get exit() {
|
|
61
60
|
return this.#exit;
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
constructor(socket: AdbSocket) {
|
|
65
64
|
this.#socket = socket;
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
this.#stderr = new ReadableStream({
|
|
67
|
+
start: (controller) => {
|
|
68
|
+
this.#socket.closed
|
|
69
|
+
.then(() => controller.close())
|
|
70
|
+
.catch(unreachable);
|
|
72
71
|
},
|
|
73
72
|
});
|
|
74
|
-
|
|
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);
|
|
73
|
+
this.#exit = socket.closed.then(() => 0);
|
|
78
74
|
}
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
resize() {
|
|
81
77
|
// Not supported, but don't throw.
|
|
82
78
|
}
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
async kill() {
|
|
81
|
+
await this.#socket.close();
|
|
86
82
|
}
|
|
87
83
|
}
|