@yume-chan/adb 0.0.22 → 0.0.24
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 +51 -0
- package/CHANGELOG.md +24 -1
- package/LICENSE +1 -1
- package/esm/adb.d.ts +6 -3
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +11 -4
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts.map +1 -1
- package/esm/banner.js +1 -0
- package/esm/banner.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +6 -5
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +23 -11
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.js +3 -3
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +2 -2
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +3 -3
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +14 -69
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +2 -2
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/subprocess/utils.js +1 -1
- package/esm/commands/subprocess/utils.js.map +1 -1
- package/esm/commands/sync/list.d.ts +2 -5
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -4
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +1 -4
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +2 -2
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +4 -7
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +3 -6
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.d.ts +13 -18
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +23 -28
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +18 -12
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +39 -17
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +2 -2
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +10 -5
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.d.ts +0 -3
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +0 -2
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +4 -3
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +9 -7
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.js +1 -1
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.js +1 -1
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.d.ts +3 -2
- package/esm/daemon/crypto.d.ts.map +1 -1
- package/esm/daemon/crypto.js +56 -49
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/dispatcher.d.ts +38 -6
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js +86 -28
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +2 -2
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +13 -12
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +11 -6
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +42 -12
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +75 -11
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +60 -33
- package/esm/daemon/transport.js.map +1 -1
- package/esm/features.d.ts +2 -1
- package/esm/features.d.ts.map +1 -1
- package/esm/features.js +2 -1
- package/esm/features.js.map +1 -1
- package/esm/server/client.d.ts +128 -48
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +378 -145
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.d.ts +3 -0
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js +30 -4
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/base64.d.ts.map +1 -1
- package/esm/utils/base64.js +33 -22
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/hex.d.ts +1 -1
- package/esm/utils/hex.d.ts.map +1 -1
- package/esm/utils/hex.js +12 -14
- package/esm/utils/hex.js.map +1 -1
- package/esm/utils/index.d.ts +1 -1
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +1 -1
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/sequence-equal.d.ts +2 -0
- package/esm/utils/sequence-equal.d.ts.map +1 -0
- package/esm/utils/sequence-equal.js +12 -0
- package/esm/utils/sequence-equal.js.map +1 -0
- package/package.json +13 -15
- package/src/adb.ts +22 -6
- package/src/banner.ts +1 -0
- package/src/commands/power.ts +7 -5
- package/src/commands/reverse.ts +31 -18
- package/src/commands/subprocess/command.ts +3 -3
- package/src/commands/subprocess/protocols/none.ts +2 -2
- package/src/commands/subprocess/protocols/shell.ts +21 -100
- package/src/commands/subprocess/protocols/types.ts +2 -2
- package/src/commands/subprocess/utils.ts +1 -1
- package/src/commands/sync/list.ts +2 -4
- package/src/commands/sync/pull.ts +2 -2
- package/src/commands/sync/push.ts +7 -10
- package/src/commands/sync/request.ts +32 -32
- package/src/commands/sync/response.ts +47 -20
- package/src/commands/sync/socket.ts +13 -8
- package/src/commands/sync/stat.ts +0 -2
- package/src/commands/sync/sync.ts +13 -9
- package/src/commands/tcpip.ts +1 -1
- package/src/daemon/auth.ts +1 -1
- package/src/daemon/crypto.ts +64 -55
- package/src/daemon/dispatcher.ts +175 -37
- package/src/daemon/packet.ts +21 -14
- package/src/daemon/socket.ts +62 -28
- package/src/daemon/transport.ts +145 -40
- package/src/features.ts +2 -1
- package/src/server/client.ts +472 -224
- package/src/server/transport.ts +32 -4
- package/src/utils/base64.ts +33 -22
- package/src/utils/hex.ts +16 -14
- package/src/utils/index.ts +1 -1
- package/src/utils/sequence-equal.ts +13 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/utils/conditional-variable.d.ts +0 -8
- package/esm/utils/conditional-variable.d.ts.map +0 -1
- package/esm/utils/conditional-variable.js +0 -34
- package/esm/utils/conditional-variable.js.map +0 -1
- package/src/utils/conditional-variable.ts +0 -45
package/src/commands/reverse.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { AutoDisposable } from "@yume-chan/event";
|
|
4
4
|
import { BufferedReadableStream } from "@yume-chan/stream-extra";
|
|
5
|
-
import Struct, { ExactReadableEndedError } from "@yume-chan/struct";
|
|
5
|
+
import Struct, { ExactReadableEndedError, encodeUtf8 } from "@yume-chan/struct";
|
|
6
6
|
|
|
7
7
|
import type { Adb, AdbIncomingSocketHandler } from "../adb.js";
|
|
8
|
-
import {
|
|
8
|
+
import { hexToNumber, sequenceEqual } from "../utils/index.js";
|
|
9
9
|
|
|
10
10
|
export interface AdbForwardListener {
|
|
11
11
|
deviceSerial: string;
|
|
@@ -38,7 +38,7 @@ const AdbReverseErrorResponse = new Struct()
|
|
|
38
38
|
.concat(AdbReverseStringResponse)
|
|
39
39
|
.postDeserialize((value) => {
|
|
40
40
|
// https://issuetracker.google.com/issues/37066218
|
|
41
|
-
// ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over
|
|
41
|
+
// ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over Wi-Fi),
|
|
42
42
|
// and returns this confusing "more than one device/emulator" error.
|
|
43
43
|
if (value.content === "more than one device/emulator") {
|
|
44
44
|
throw new AdbReverseNotSupportedError();
|
|
@@ -47,11 +47,21 @@ const AdbReverseErrorResponse = new Struct()
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
// Like `hexToNumber`, it's much faster than first converting `buffer` to a string
|
|
51
|
+
function decimalToNumber(buffer: Uint8Array) {
|
|
52
|
+
let value = 0;
|
|
53
|
+
for (const byte of buffer) {
|
|
54
|
+
// Like `parseInt`, return when it encounters a non-digit character
|
|
55
|
+
if (byte < 48 || byte > 57) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
value = value * 10 + byte - 48;
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
const OKAY = encodeUtf8("OKAY");
|
|
64
|
+
|
|
55
65
|
export class AdbReverseCommand extends AutoDisposable {
|
|
56
66
|
protected adb: Adb;
|
|
57
67
|
|
|
@@ -70,10 +80,12 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
70
80
|
|
|
71
81
|
protected async sendRequest(service: string) {
|
|
72
82
|
const stream = await this.createBufferedStream(service);
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
|
|
84
|
+
const response = await stream.readExactly(4);
|
|
85
|
+
if (!sequenceEqual(response, OKAY)) {
|
|
75
86
|
await AdbReverseErrorResponse.deserialize(stream);
|
|
76
87
|
}
|
|
88
|
+
|
|
77
89
|
return stream;
|
|
78
90
|
}
|
|
79
91
|
|
|
@@ -81,14 +93,15 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
81
93
|
const stream = await this.createBufferedStream("reverse:list-forward");
|
|
82
94
|
|
|
83
95
|
const response = await AdbReverseStringResponse.deserialize(stream);
|
|
84
|
-
return response.content
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
return response.content
|
|
97
|
+
.split("\n")
|
|
98
|
+
.filter((line) => !!line)
|
|
99
|
+
.map((line) => {
|
|
100
|
+
const [deviceSerial, localName, remoteName] = line.split(
|
|
101
|
+
" ",
|
|
102
|
+
) as [string, string, string];
|
|
103
|
+
return { deviceSerial, localName, remoteName };
|
|
104
|
+
});
|
|
92
105
|
|
|
93
106
|
// No need to close the stream, device will close it
|
|
94
107
|
}
|
|
@@ -110,8 +123,8 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
110
123
|
const position = stream.position;
|
|
111
124
|
try {
|
|
112
125
|
const length = hexToNumber(await stream.readExactly(4));
|
|
113
|
-
const port = await
|
|
114
|
-
deviceAddress = `tcp:${
|
|
126
|
+
const port = decimalToNumber(await stream.readExactly(length));
|
|
127
|
+
deviceAddress = `tcp:${port}`;
|
|
115
128
|
} catch (e) {
|
|
116
129
|
if (
|
|
117
130
|
e instanceof ExactReadableEndedError &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConcatStringStream,
|
|
1
|
+
import { ConcatStringStream, TextDecoderStream } from "@yume-chan/stream-extra";
|
|
2
2
|
|
|
3
3
|
import { AdbCommandBase } from "../base.js";
|
|
4
4
|
|
|
@@ -112,10 +112,10 @@ export class AdbSubprocess extends AdbCommandBase {
|
|
|
112
112
|
|
|
113
113
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
114
114
|
process.stdout
|
|
115
|
-
.pipeThrough(new
|
|
115
|
+
.pipeThrough(new TextDecoderStream())
|
|
116
116
|
.pipeThrough(new ConcatStringStream()),
|
|
117
117
|
process.stderr
|
|
118
|
-
.pipeThrough(new
|
|
118
|
+
.pipeThrough(new TextDecoderStream())
|
|
119
119
|
.pipeThrough(new ConcatStringStream()),
|
|
120
120
|
process.exit,
|
|
121
121
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MaybeConsumable, WritableStream } from "@yume-chan/stream-extra";
|
|
2
2
|
import { ReadableStream } from "@yume-chan/stream-extra";
|
|
3
3
|
|
|
4
4
|
import type { Adb, AdbSocket } from "../../../adb.js";
|
|
@@ -36,7 +36,7 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
|
36
36
|
readonly #socket: AdbSocket;
|
|
37
37
|
|
|
38
38
|
// Legacy shell forwards all data to stdin.
|
|
39
|
-
get stdin(): WritableStream<
|
|
39
|
+
get stdin(): WritableStream<MaybeConsumable<Uint8Array>> {
|
|
40
40
|
return this.#socket.writable;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
2
|
import type {
|
|
3
|
-
Consumable,
|
|
4
3
|
PushReadableStreamController,
|
|
5
4
|
ReadableStream,
|
|
6
5
|
WritableStreamDefaultWriter,
|
|
7
6
|
} from "@yume-chan/stream-extra";
|
|
8
7
|
import {
|
|
9
|
-
|
|
10
|
-
ConsumableWritableStream,
|
|
8
|
+
MaybeConsumable,
|
|
11
9
|
PushReadableStream,
|
|
12
10
|
StructDeserializeStream,
|
|
13
11
|
WritableStream,
|
|
14
|
-
pipeFrom,
|
|
15
12
|
} from "@yume-chan/stream-extra";
|
|
16
13
|
import type { StructValueType } from "@yume-chan/struct";
|
|
17
14
|
import Struct, { placeholder } from "@yume-chan/struct";
|
|
@@ -37,68 +34,8 @@ const AdbShellProtocolPacket = new Struct({ littleEndian: true })
|
|
|
37
34
|
.uint32("length")
|
|
38
35
|
.uint8Array("data", { lengthField: "length" });
|
|
39
36
|
|
|
40
|
-
type AdbShellProtocolPacketInit = (typeof AdbShellProtocolPacket)["TInit"];
|
|
41
|
-
|
|
42
37
|
type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>;
|
|
43
38
|
|
|
44
|
-
class StdinSerializeStream extends ConsumableTransformStream<
|
|
45
|
-
Uint8Array,
|
|
46
|
-
AdbShellProtocolPacketInit
|
|
47
|
-
> {
|
|
48
|
-
constructor() {
|
|
49
|
-
super({
|
|
50
|
-
async transform(chunk, controller) {
|
|
51
|
-
await controller.enqueue({
|
|
52
|
-
id: AdbShellProtocolId.Stdin,
|
|
53
|
-
data: chunk,
|
|
54
|
-
});
|
|
55
|
-
},
|
|
56
|
-
flush() {
|
|
57
|
-
// TODO: AdbShellSubprocessProtocol: support closing stdin
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
class MultiplexStream<T> {
|
|
64
|
-
#readable: PushReadableStream<T>;
|
|
65
|
-
#readableController!: PushReadableStreamController<T>;
|
|
66
|
-
get readable() {
|
|
67
|
-
return this.#readable;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
#activeCount = 0;
|
|
71
|
-
|
|
72
|
-
constructor() {
|
|
73
|
-
this.#readable = new PushReadableStream((controller) => {
|
|
74
|
-
this.#readableController = controller;
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
createWriteable() {
|
|
79
|
-
return new WritableStream<T>({
|
|
80
|
-
start: () => {
|
|
81
|
-
this.#activeCount += 1;
|
|
82
|
-
},
|
|
83
|
-
write: async (chunk) => {
|
|
84
|
-
await this.#readableController.enqueue(chunk);
|
|
85
|
-
},
|
|
86
|
-
abort: () => {
|
|
87
|
-
this.#activeCount -= 1;
|
|
88
|
-
if (this.#activeCount === 0) {
|
|
89
|
-
this.#readableController.close();
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
close: () => {
|
|
93
|
-
this.#activeCount -= 1;
|
|
94
|
-
if (this.#activeCount === 0) {
|
|
95
|
-
this.#readableController.close();
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
39
|
/**
|
|
103
40
|
* Shell v2 a.k.a Shell Protocol
|
|
104
41
|
*
|
|
@@ -109,7 +46,7 @@ class MultiplexStream<T> {
|
|
|
109
46
|
*/
|
|
110
47
|
export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
111
48
|
static isSupported(adb: Adb) {
|
|
112
|
-
return adb.
|
|
49
|
+
return adb.canUseFeature(AdbFeature.ShellV2);
|
|
113
50
|
}
|
|
114
51
|
|
|
115
52
|
static async pty(adb: Adb, command: string) {
|
|
@@ -126,11 +63,9 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
126
63
|
}
|
|
127
64
|
|
|
128
65
|
readonly #socket: AdbSocket;
|
|
129
|
-
#
|
|
130
|
-
Consumable<AdbShellProtocolPacketInit>
|
|
131
|
-
>;
|
|
66
|
+
#writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>;
|
|
132
67
|
|
|
133
|
-
#stdin: WritableStream<
|
|
68
|
+
#stdin: WritableStream<MaybeConsumable<Uint8Array>>;
|
|
134
69
|
get stdin() {
|
|
135
70
|
return this.#stdin;
|
|
136
71
|
}
|
|
@@ -153,13 +88,8 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
153
88
|
constructor(socket: AdbSocket) {
|
|
154
89
|
this.#socket = socket;
|
|
155
90
|
|
|
156
|
-
// Check this image to help you understand the stream graph
|
|
157
|
-
// cspell: disable-next-line
|
|
158
|
-
// https://www.plantuml.com/plantuml/png/bL91QiCm4Bpx5SAdv90lb1JISmiw5XzaQKf5PIkiLZIqzEyLSg8ks13gYtOykpFhiOw93N6UGjVDqK7rZsxKqNw0U_NTgVAy4empOy2mm4_olC0VEVEE47GUpnGjKdgXoD76q4GIEpyFhOwP_m28hW0NNzxNUig1_JdW0bA7muFIJDco1daJ_1SAX9bgvoPJPyIkSekhNYctvIGXrCH6tIsPL5fs-s6J5yc9BpWXhKtNdF2LgVYPGM_6GlMwfhWUsIt4lbScANrwlgVVUifPSVi__t44qStnwPvZwobdSmHHlL57p2vFuHS0
|
|
159
|
-
|
|
160
91
|
let stdoutController!: PushReadableStreamController<Uint8Array>;
|
|
161
92
|
let stderrController!: PushReadableStreamController<Uint8Array>;
|
|
162
|
-
|
|
163
93
|
this.#stdout = new PushReadableStream<Uint8Array>((controller) => {
|
|
164
94
|
stdoutController = controller;
|
|
165
95
|
});
|
|
@@ -207,39 +137,30 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
207
137
|
},
|
|
208
138
|
);
|
|
209
139
|
|
|
210
|
-
|
|
211
|
-
Consumable<AdbShellProtocolPacketInit>
|
|
212
|
-
>();
|
|
213
|
-
void multiplexer.readable
|
|
214
|
-
.pipeThrough(
|
|
215
|
-
new ConsumableTransformStream({
|
|
216
|
-
async transform(chunk, controller) {
|
|
217
|
-
await controller.enqueue(
|
|
218
|
-
AdbShellProtocolPacket.serialize(chunk),
|
|
219
|
-
);
|
|
220
|
-
},
|
|
221
|
-
}),
|
|
222
|
-
)
|
|
223
|
-
.pipeTo(socket.writable);
|
|
224
|
-
|
|
225
|
-
this.#stdin = pipeFrom(
|
|
226
|
-
multiplexer.createWriteable(),
|
|
227
|
-
new StdinSerializeStream(),
|
|
228
|
-
);
|
|
140
|
+
this.#writer = this.#socket.writable.getWriter();
|
|
229
141
|
|
|
230
|
-
this.#
|
|
142
|
+
this.#stdin = new MaybeConsumable.WritableStream<Uint8Array>({
|
|
143
|
+
write: async (chunk) => {
|
|
144
|
+
await this.#writer.write(
|
|
145
|
+
AdbShellProtocolPacket.serialize({
|
|
146
|
+
id: AdbShellProtocolId.Stdin,
|
|
147
|
+
data: chunk,
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
});
|
|
231
152
|
}
|
|
232
153
|
|
|
233
154
|
async resize(rows: number, cols: number) {
|
|
234
|
-
await
|
|
235
|
-
|
|
236
|
-
|
|
155
|
+
await this.#writer.write(
|
|
156
|
+
AdbShellProtocolPacket.serialize({
|
|
157
|
+
id: AdbShellProtocolId.WindowSizeChange,
|
|
237
158
|
// The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}`
|
|
238
159
|
// However, according to https://linux.die.net/man/4/tty_ioctl
|
|
239
160
|
// `x_pixels` and `y_pixels` are unused, so always sending `0` should be fine.
|
|
240
|
-
`${rows}x${cols},0x0\0
|
|
241
|
-
),
|
|
242
|
-
|
|
161
|
+
data: encodeUtf8(`${rows}x${cols},0x0\0`),
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
243
164
|
}
|
|
244
165
|
|
|
245
166
|
kill() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
MaybeConsumable,
|
|
3
3
|
ReadableStream,
|
|
4
4
|
WritableStream,
|
|
5
5
|
} from "@yume-chan/stream-extra";
|
|
@@ -11,7 +11,7 @@ export interface AdbSubprocessProtocol {
|
|
|
11
11
|
/**
|
|
12
12
|
* A WritableStream that writes to the `stdin` stream.
|
|
13
13
|
*/
|
|
14
|
-
readonly stdin: WritableStream<
|
|
14
|
+
readonly stdin: WritableStream<MaybeConsumable<Uint8Array>>;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* The `stdout` stream of the process.
|
|
@@ -17,8 +17,7 @@ export interface AdbSyncEntry extends AdbSyncStat {
|
|
|
17
17
|
export const AdbSyncEntryResponse = new Struct({ littleEndian: true })
|
|
18
18
|
.concat(AdbSyncLstatResponse)
|
|
19
19
|
.uint32("nameLength")
|
|
20
|
-
.string("name", { lengthField: "nameLength" })
|
|
21
|
-
.extra({ id: AdbSyncResponseId.Entry as const });
|
|
20
|
+
.string("name", { lengthField: "nameLength" });
|
|
22
21
|
|
|
23
22
|
export type AdbSyncEntryResponse =
|
|
24
23
|
(typeof AdbSyncEntryResponse)["TDeserializeResult"];
|
|
@@ -26,8 +25,7 @@ export type AdbSyncEntryResponse =
|
|
|
26
25
|
export const AdbSyncEntry2Response = new Struct({ littleEndian: true })
|
|
27
26
|
.concat(AdbSyncStatResponse)
|
|
28
27
|
.uint32("nameLength")
|
|
29
|
-
.string("name", { lengthField: "nameLength" })
|
|
30
|
-
.extra({ id: AdbSyncResponseId.Entry2 as const });
|
|
28
|
+
.string("name", { lengthField: "nameLength" });
|
|
31
29
|
|
|
32
30
|
export type AdbSyncEntry2Response =
|
|
33
31
|
(typeof AdbSyncEntry2Response)["TDeserializeResult"];
|
|
@@ -8,8 +8,7 @@ import type { AdbSyncSocket } from "./socket.js";
|
|
|
8
8
|
|
|
9
9
|
export const AdbSyncDataResponse = new Struct({ littleEndian: true })
|
|
10
10
|
.uint32("dataLength")
|
|
11
|
-
.uint8Array("data", { lengthField: "dataLength" })
|
|
12
|
-
.extra({ id: AdbSyncResponseId.Data as const });
|
|
11
|
+
.uint8Array("data", { lengthField: "dataLength" });
|
|
13
12
|
|
|
14
13
|
export type AdbSyncDataResponse =
|
|
15
14
|
(typeof AdbSyncDataResponse)["TDeserializeResult"];
|
|
@@ -52,6 +51,7 @@ export function adbSyncPull(
|
|
|
52
51
|
socket: AdbSyncSocket,
|
|
53
52
|
path: string,
|
|
54
53
|
): ReadableStream<Uint8Array> {
|
|
54
|
+
// TODO: use `ReadableStream.from` when it's supported
|
|
55
55
|
return new PushReadableStream(async (controller) => {
|
|
56
56
|
for await (const data of adbSyncPullGenerator(socket, path)) {
|
|
57
57
|
await controller.enqueue(data);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadableStream } from "@yume-chan/stream-extra";
|
|
2
2
|
import {
|
|
3
3
|
AbortController,
|
|
4
|
-
ConsumableWritableStream,
|
|
5
4
|
DistributionStream,
|
|
5
|
+
MaybeConsumable,
|
|
6
6
|
} from "@yume-chan/stream-extra";
|
|
7
7
|
import Struct, { placeholder } from "@yume-chan/struct";
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ export const ADB_SYNC_MAX_PACKET_SIZE = 64 * 1024;
|
|
|
18
18
|
export interface AdbSyncPushV1Options {
|
|
19
19
|
socket: AdbSyncSocket;
|
|
20
20
|
filename: string;
|
|
21
|
-
file: ReadableStream<
|
|
21
|
+
file: ReadableStream<MaybeConsumable<Uint8Array>>;
|
|
22
22
|
type?: LinuxFileType;
|
|
23
23
|
permission?: number;
|
|
24
24
|
mtime?: number;
|
|
@@ -31,7 +31,7 @@ export const AdbSyncOkResponse = new Struct({ littleEndian: true }).uint32(
|
|
|
31
31
|
|
|
32
32
|
async function pipeFileData(
|
|
33
33
|
locked: AdbSyncSocketLocked,
|
|
34
|
-
file: ReadableStream<
|
|
34
|
+
file: ReadableStream<MaybeConsumable<Uint8Array>>,
|
|
35
35
|
packetSize: number,
|
|
36
36
|
mtime: number,
|
|
37
37
|
) {
|
|
@@ -40,7 +40,7 @@ async function pipeFileData(
|
|
|
40
40
|
const abortController = new AbortController();
|
|
41
41
|
file.pipeThrough(new DistributionStream(packetSize, true))
|
|
42
42
|
.pipeTo(
|
|
43
|
-
new
|
|
43
|
+
new MaybeConsumable.WritableStream({
|
|
44
44
|
write: async (chunk) => {
|
|
45
45
|
await adbSyncWriteRequest(
|
|
46
46
|
locked,
|
|
@@ -97,10 +97,7 @@ export enum AdbSyncSendV2Flags {
|
|
|
97
97
|
* 4
|
|
98
98
|
*/
|
|
99
99
|
Zstd = 1 << 2,
|
|
100
|
-
|
|
101
|
-
* 0x80000000
|
|
102
|
-
*/
|
|
103
|
-
DryRun = (1 << 31) >>> 0,
|
|
100
|
+
DryRun = 0x80000000,
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
|
|
@@ -114,7 +111,7 @@ export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
|
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
export const AdbSyncSendV2Request = new Struct({ littleEndian: true })
|
|
117
|
-
.uint32("id"
|
|
114
|
+
.uint32("id")
|
|
118
115
|
.uint32("mode")
|
|
119
116
|
.uint32("flags", placeholder<AdbSyncSendV2Flags>());
|
|
120
117
|
|
|
@@ -2,53 +2,53 @@ import Struct from "@yume-chan/struct";
|
|
|
2
2
|
|
|
3
3
|
import { encodeUtf8 } from "../../utils/index.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
import { adbSyncEncodeId } from "./response.js";
|
|
6
|
+
|
|
7
|
+
export namespace AdbSyncRequestId {
|
|
8
|
+
export const List = adbSyncEncodeId("LIST");
|
|
9
|
+
export const ListV2 = adbSyncEncodeId("LIS2");
|
|
10
|
+
export const Send = adbSyncEncodeId("SEND");
|
|
11
|
+
export const SendV2 = adbSyncEncodeId("SND2");
|
|
12
|
+
export const Lstat = adbSyncEncodeId("STAT");
|
|
13
|
+
export const Stat = adbSyncEncodeId("STA2");
|
|
14
|
+
export const LstatV2 = adbSyncEncodeId("LST2");
|
|
15
|
+
export const Data = adbSyncEncodeId("DATA");
|
|
16
|
+
export const Done = adbSyncEncodeId("DONE");
|
|
17
|
+
export const Receive = adbSyncEncodeId("RECV");
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export const AdbSyncNumberRequest = new Struct({ littleEndian: true })
|
|
19
|
-
.
|
|
21
|
+
.uint32("id")
|
|
20
22
|
.uint32("arg");
|
|
21
23
|
|
|
22
|
-
export const AdbSyncDataRequest = new Struct({ littleEndian: true })
|
|
23
|
-
.concat(AdbSyncNumberRequest)
|
|
24
|
-
.uint8Array("data", { lengthField: "arg" });
|
|
25
|
-
|
|
26
24
|
export interface AdbSyncWritable {
|
|
27
25
|
write(buffer: Uint8Array): Promise<void>;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
export async function adbSyncWriteRequest(
|
|
31
29
|
writable: AdbSyncWritable,
|
|
32
|
-
id:
|
|
30
|
+
id: number | string,
|
|
33
31
|
value: number | string | Uint8Array,
|
|
34
32
|
): Promise<void> {
|
|
33
|
+
if (typeof id === "string") {
|
|
34
|
+
id = adbSyncEncodeId(id);
|
|
35
|
+
}
|
|
36
|
+
|
|
35
37
|
if (typeof value === "number") {
|
|
36
|
-
const buffer = AdbSyncNumberRequest.serialize({
|
|
37
|
-
id,
|
|
38
|
-
arg: value,
|
|
39
|
-
});
|
|
40
|
-
await writable.write(buffer);
|
|
41
|
-
} else if (typeof value === "string") {
|
|
42
|
-
// Let `writable` buffer writes
|
|
43
|
-
const buffer = encodeUtf8(value);
|
|
44
38
|
await writable.write(
|
|
45
|
-
AdbSyncNumberRequest.serialize({ id, arg:
|
|
39
|
+
AdbSyncNumberRequest.serialize({ id, arg: value }),
|
|
46
40
|
);
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
52
|
-
await writable.write(value);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
value = encodeUtf8(value);
|
|
53
46
|
}
|
|
47
|
+
|
|
48
|
+
// `writable` is buffered, it copies inputs to an internal buffer,
|
|
49
|
+
// so don't concatenate headers and data here, that will be an unnecessary copy.
|
|
50
|
+
await writable.write(
|
|
51
|
+
AdbSyncNumberRequest.serialize({ id, arg: value.length }),
|
|
52
|
+
);
|
|
53
|
+
await writable.write(value);
|
|
54
54
|
}
|
|
@@ -1,22 +1,39 @@
|
|
|
1
|
+
import { getUint32LittleEndian } from "@yume-chan/no-data-view";
|
|
1
2
|
import type {
|
|
2
3
|
AsyncExactReadable,
|
|
3
4
|
StructLike,
|
|
4
5
|
StructValueType,
|
|
5
6
|
} from "@yume-chan/struct";
|
|
6
|
-
import Struct from "@yume-chan/struct";
|
|
7
|
+
import Struct, { decodeUtf8 } from "@yume-chan/struct";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
function encodeAsciiUnchecked(value: string): Uint8Array {
|
|
10
|
+
const result = new Uint8Array(value.length);
|
|
11
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
12
|
+
result[i] = value.charCodeAt(i);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Encode ID to numbers for faster comparison
|
|
19
|
+
* @param value A 4-character string
|
|
20
|
+
* @returns A 32-bit integer by encoding the string as little-endian
|
|
21
|
+
*/
|
|
22
|
+
export function adbSyncEncodeId(value: string): number {
|
|
23
|
+
const buffer = encodeAsciiUnchecked(value);
|
|
24
|
+
return getUint32LittleEndian(buffer, 0);
|
|
25
|
+
}
|
|
9
26
|
|
|
10
|
-
export
|
|
11
|
-
Entry = "DENT"
|
|
12
|
-
Entry2 = "DNT2"
|
|
13
|
-
Lstat = "STAT"
|
|
14
|
-
Stat = "STA2"
|
|
15
|
-
Lstat2 = "LST2"
|
|
16
|
-
Done = "DONE"
|
|
17
|
-
Data = "DATA"
|
|
18
|
-
Ok = "OKAY"
|
|
19
|
-
Fail = "FAIL"
|
|
27
|
+
export namespace AdbSyncResponseId {
|
|
28
|
+
export const Entry = adbSyncEncodeId("DENT");
|
|
29
|
+
export const Entry2 = adbSyncEncodeId("DNT2");
|
|
30
|
+
export const Lstat = adbSyncEncodeId("STAT");
|
|
31
|
+
export const Stat = adbSyncEncodeId("STA2");
|
|
32
|
+
export const Lstat2 = adbSyncEncodeId("LST2");
|
|
33
|
+
export const Done = adbSyncEncodeId("DONE");
|
|
34
|
+
export const Data = adbSyncEncodeId("DATA");
|
|
35
|
+
export const Ok = adbSyncEncodeId("OKAY");
|
|
36
|
+
export const Fail = adbSyncEncodeId("FAIL");
|
|
20
37
|
}
|
|
21
38
|
|
|
22
39
|
export class AdbSyncError extends Error {}
|
|
@@ -30,18 +47,24 @@ export const AdbSyncFailResponse = new Struct({ littleEndian: true })
|
|
|
30
47
|
|
|
31
48
|
export async function adbSyncReadResponse<T>(
|
|
32
49
|
stream: AsyncExactReadable,
|
|
33
|
-
id:
|
|
50
|
+
id: number | string,
|
|
34
51
|
type: StructLike<T>,
|
|
35
52
|
): Promise<T> {
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
if (typeof id === "string") {
|
|
54
|
+
id = adbSyncEncodeId(id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const buffer = await stream.readExactly(4);
|
|
58
|
+
switch (getUint32LittleEndian(buffer, 0)) {
|
|
38
59
|
case AdbSyncResponseId.Fail:
|
|
39
60
|
await AdbSyncFailResponse.deserialize(stream);
|
|
40
61
|
throw new Error("Unreachable");
|
|
41
62
|
case id:
|
|
42
63
|
return await type.deserialize(stream);
|
|
43
64
|
default:
|
|
44
|
-
throw new Error(
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Expected '${id}', but got '${decodeUtf8(buffer)}'`,
|
|
67
|
+
);
|
|
45
68
|
}
|
|
46
69
|
}
|
|
47
70
|
|
|
@@ -49,12 +72,16 @@ export async function* adbSyncReadResponses<
|
|
|
49
72
|
T extends Struct<object, PropertyKey, object, unknown>,
|
|
50
73
|
>(
|
|
51
74
|
stream: AsyncExactReadable,
|
|
52
|
-
id:
|
|
75
|
+
id: number | string,
|
|
53
76
|
type: T,
|
|
54
77
|
): AsyncGenerator<StructValueType<T>, void, void> {
|
|
78
|
+
if (typeof id === "string") {
|
|
79
|
+
id = adbSyncEncodeId(id);
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
while (true) {
|
|
56
|
-
const
|
|
57
|
-
switch (
|
|
83
|
+
const buffer = await stream.readExactly(4);
|
|
84
|
+
switch (getUint32LittleEndian(buffer, 0)) {
|
|
58
85
|
case AdbSyncResponseId.Fail:
|
|
59
86
|
await AdbSyncFailResponse.deserialize(stream);
|
|
60
87
|
throw new Error("Unreachable");
|
|
@@ -70,7 +97,7 @@ export async function* adbSyncReadResponses<
|
|
|
70
97
|
break;
|
|
71
98
|
default:
|
|
72
99
|
throw new Error(
|
|
73
|
-
`Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${
|
|
100
|
+
`Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${decodeUtf8(buffer)}'`,
|
|
74
101
|
);
|
|
75
102
|
}
|
|
76
103
|
}
|