@yume-chan/adb 0.0.18 → 0.0.19
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 +21 -0
- package/CHANGELOG.md +11 -1
- package/esm/adb.d.ts +63 -60
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +257 -249
- package/esm/adb.js.map +1 -1
- package/esm/auth.d.ts +51 -51
- package/esm/auth.d.ts.map +1 -1
- package/esm/auth.js +95 -96
- package/esm/auth.js.map +1 -1
- package/esm/backend.d.ts +8 -8
- package/esm/backend.d.ts.map +1 -1
- package/esm/backend.js +1 -1
- 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/install.d.ts +9 -3
- package/esm/commands/install.d.ts.map +1 -1
- package/esm/commands/install.js +26 -26
- package/esm/commands/install.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 +41 -28
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +125 -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 +35 -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 +39 -38
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +166 -162
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +49 -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 +46 -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 +27 -0
- package/esm/commands/sync/socket.d.ts.map +1 -0
- package/esm/commands/sync/socket.js +66 -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 +72 -60
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +127 -149
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +5 -5
- package/esm/commands/tcpip.js +18 -18
- package/esm/crypto.d.ts +36 -36
- package/esm/crypto.js +248 -248
- 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 +9 -9
- package/esm/index.js +9 -9
- package/esm/packet.d.ts +45 -46
- package/esm/packet.d.ts.map +1 -1
- package/esm/packet.js +43 -48
- package/esm/packet.js.map +1 -1
- package/esm/socket/dispatcher.d.ts +59 -59
- package/esm/socket/dispatcher.d.ts.map +1 -1
- package/esm/socket/dispatcher.js +223 -234
- package/esm/socket/dispatcher.js.map +1 -1
- package/esm/socket/index.d.ts +2 -2
- package/esm/socket/index.js +2 -2
- package/esm/socket/socket.d.ts +59 -59
- package/esm/socket/socket.d.ts.map +1 -1
- package/esm/socket/socket.js +118 -118
- package/esm/socket/socket.js.map +1 -1
- package/esm/utils/auto-reset-event.d.ts +9 -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/conditional-variable.d.ts +9 -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/index.d.ts +4 -3
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +4 -3
- package/esm/utils/index.js.map +1 -1
- package/package.json +11 -10
- package/src/adb.ts +47 -37
- package/src/auth.ts +7 -6
- package/src/backend.ts +4 -4
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +4 -4
- package/src/commands/index.ts +7 -8
- package/src/commands/reverse.ts +35 -10
- 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 +25 -11
- package/src/commands/subprocess/protocols/shell.ts +50 -28
- package/src/commands/subprocess/protocols/types.ts +8 -7
- package/src/commands/subprocess/utils.ts +1 -1
- package/src/commands/sync/index.ts +8 -7
- package/src/commands/sync/list.ts +42 -23
- package/src/commands/sync/pull.ts +40 -45
- package/src/commands/sync/push.ts +146 -39
- package/src/commands/sync/request.ts +20 -15
- package/src/commands/sync/response.ts +7 -6
- package/src/commands/sync/socket.ts +97 -0
- package/src/commands/sync/stat.ts +47 -42
- package/src/commands/sync/sync.ts +68 -108
- package/src/commands/tcpip.ts +5 -5
- package/src/features.ts +9 -6
- package/src/packet.ts +19 -34
- package/src/socket/dispatcher.ts +32 -47
- package/src/socket/index.ts +2 -2
- package/src/socket/socket.ts +26 -17
- package/src/utils/auto-reset-event.ts +14 -15
- package/src/utils/conditional-variable.ts +45 -0
- package/src/utils/index.ts +4 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/src/commands/install.ts +0 -40
package/src/adb.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
// cspell: ignore libusb
|
|
2
|
-
|
|
3
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import type { Consumable, ReadableWritablePair } from "@yume-chan/stream-extra";
|
|
4
3
|
import {
|
|
5
4
|
AbortController,
|
|
5
|
+
ConsumableWritableStream,
|
|
6
6
|
DecodeUtf8Stream,
|
|
7
7
|
GatherStringStream,
|
|
8
8
|
WritableStream,
|
|
9
|
-
type ReadableWritablePair,
|
|
10
9
|
} from "@yume-chan/stream-extra";
|
|
11
10
|
|
|
11
|
+
import type { AdbCredentialStore } from "./auth.js";
|
|
12
12
|
import {
|
|
13
13
|
ADB_DEFAULT_AUTHENTICATORS,
|
|
14
14
|
AdbAuthenticationProcessor,
|
|
15
|
-
type AdbCredentialStore,
|
|
16
15
|
} from "./auth.js";
|
|
16
|
+
import type { AdbFrameBuffer } from "./commands/index.js";
|
|
17
17
|
import {
|
|
18
18
|
AdbPower,
|
|
19
19
|
AdbReverseCommand,
|
|
@@ -22,22 +22,16 @@ import {
|
|
|
22
22
|
AdbTcpIpCommand,
|
|
23
23
|
escapeArg,
|
|
24
24
|
framebuffer,
|
|
25
|
-
install,
|
|
26
|
-
type AdbFrameBuffer,
|
|
27
25
|
} from "./commands/index.js";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import {
|
|
36
|
-
AdbPacketDispatcher,
|
|
37
|
-
type AdbIncomingSocketHandler,
|
|
38
|
-
type AdbSocket,
|
|
39
|
-
type Closeable,
|
|
26
|
+
import { AdbFeature } from "./features.js";
|
|
27
|
+
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
|
28
|
+
import { AdbCommand, calculateChecksum } from "./packet.js";
|
|
29
|
+
import type {
|
|
30
|
+
AdbIncomingSocketHandler,
|
|
31
|
+
AdbSocket,
|
|
32
|
+
Closeable,
|
|
40
33
|
} from "./socket/index.js";
|
|
34
|
+
import { AdbPacketDispatcher } from "./socket/index.js";
|
|
41
35
|
import { decodeUtf8, encodeUtf8 } from "./utils/index.js";
|
|
42
36
|
|
|
43
37
|
export enum AdbPropKey {
|
|
@@ -56,13 +50,17 @@ export class Adb implements Closeable {
|
|
|
56
50
|
* and starts a new authentication process.
|
|
57
51
|
*/
|
|
58
52
|
public static async authenticate(
|
|
59
|
-
connection: ReadableWritablePair<
|
|
53
|
+
connection: ReadableWritablePair<
|
|
54
|
+
AdbPacketData,
|
|
55
|
+
Consumable<AdbPacketInit>
|
|
56
|
+
>,
|
|
60
57
|
credentialStore: AdbCredentialStore,
|
|
61
58
|
authenticators = ADB_DEFAULT_AUTHENTICATORS
|
|
62
59
|
): Promise<Adb> {
|
|
63
60
|
// Initially, set to highest-supported version and payload size.
|
|
64
61
|
let version = 0x01000001;
|
|
65
|
-
|
|
62
|
+
// Android 4: 4K, Android 7: 256K, Android 9: 1M
|
|
63
|
+
let maxPayloadSize = 1024 * 1024;
|
|
66
64
|
|
|
67
65
|
const resolver = new PromiseResolver<string>();
|
|
68
66
|
const authProcessor = new AdbAuthenticationProcessor(
|
|
@@ -117,7 +115,9 @@ export class Adb implements Closeable {
|
|
|
117
115
|
async function sendPacket(init: AdbPacketData) {
|
|
118
116
|
// Always send checksum in auth steps
|
|
119
117
|
// Because we don't know if the device needs it or not.
|
|
120
|
-
|
|
118
|
+
(init as AdbPacketInit).checksum = calculateChecksum(init.payload);
|
|
119
|
+
(init as AdbPacketInit).magic = init.command ^ 0xffffffff;
|
|
120
|
+
await ConsumableWritableStream.write(writer, init as AdbPacketInit);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
let banner: string;
|
|
@@ -125,17 +125,17 @@ export class Adb implements Closeable {
|
|
|
125
125
|
// https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
|
|
126
126
|
// There are some other feature constants, but some of them are only used by ADB server, not devices (daemons).
|
|
127
127
|
const features = [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
AdbFeature.ShellV2,
|
|
129
|
+
AdbFeature.Cmd,
|
|
130
|
+
AdbFeature.StatV2,
|
|
131
|
+
AdbFeature.ListV2,
|
|
132
|
+
AdbFeature.FixedPushMkdir,
|
|
133
133
|
"apex",
|
|
134
|
-
|
|
134
|
+
AdbFeature.Abb,
|
|
135
135
|
// only tells the client the symlink timestamp issue in `adb push --sync` has been fixed.
|
|
136
136
|
// No special handling required.
|
|
137
137
|
"fixed_push_symlink_timestamp",
|
|
138
|
-
|
|
138
|
+
AdbFeature.AbbExec,
|
|
139
139
|
"remount_shell",
|
|
140
140
|
"track_app",
|
|
141
141
|
"sendrecv_v2",
|
|
@@ -174,11 +174,16 @@ export class Adb implements Closeable {
|
|
|
174
174
|
return this.dispatcher.disconnected;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
private _protocolVersion: number
|
|
177
|
+
private _protocolVersion: number;
|
|
178
178
|
public get protocolVersion() {
|
|
179
179
|
return this._protocolVersion;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
private _maxPayloadSize: number;
|
|
183
|
+
public get maxPayloadSize() {
|
|
184
|
+
return this._maxPayloadSize;
|
|
185
|
+
}
|
|
186
|
+
|
|
182
187
|
private _product: string | undefined;
|
|
183
188
|
public get product() {
|
|
184
189
|
return this._product;
|
|
@@ -194,7 +199,7 @@ export class Adb implements Closeable {
|
|
|
194
199
|
return this._device;
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
private _features:
|
|
202
|
+
private _features: AdbFeature[] = [];
|
|
198
203
|
public get features() {
|
|
199
204
|
return this._features;
|
|
200
205
|
}
|
|
@@ -205,7 +210,10 @@ export class Adb implements Closeable {
|
|
|
205
210
|
public readonly tcpip: AdbTcpIpCommand;
|
|
206
211
|
|
|
207
212
|
public constructor(
|
|
208
|
-
connection: ReadableWritablePair<
|
|
213
|
+
connection: ReadableWritablePair<
|
|
214
|
+
AdbPacketData,
|
|
215
|
+
Consumable<AdbPacketInit>
|
|
216
|
+
>,
|
|
209
217
|
version: number,
|
|
210
218
|
maxPayloadSize: number,
|
|
211
219
|
banner: string
|
|
@@ -229,6 +237,7 @@ export class Adb implements Closeable {
|
|
|
229
237
|
});
|
|
230
238
|
|
|
231
239
|
this._protocolVersion = version;
|
|
240
|
+
this._maxPayloadSize = maxPayloadSize;
|
|
232
241
|
|
|
233
242
|
this.subprocess = new AdbSubprocess(this);
|
|
234
243
|
this.power = new AdbPower(this);
|
|
@@ -262,13 +271,17 @@ export class Adb implements Closeable {
|
|
|
262
271
|
this._device = value;
|
|
263
272
|
break;
|
|
264
273
|
case AdbPropKey.Features:
|
|
265
|
-
this._features = value!.split(",") as
|
|
274
|
+
this._features = value!.split(",") as AdbFeature[];
|
|
266
275
|
break;
|
|
267
276
|
}
|
|
268
277
|
}
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
|
|
281
|
+
public supportsFeature(feature: AdbFeature): boolean {
|
|
282
|
+
return this._features.includes(feature);
|
|
283
|
+
}
|
|
284
|
+
|
|
272
285
|
/**
|
|
273
286
|
* Add a handler for incoming socket.
|
|
274
287
|
* @param handler A function to call with new incoming sockets. It must return `true` if it accepts the socket.
|
|
@@ -300,18 +313,15 @@ export class Adb implements Closeable {
|
|
|
300
313
|
}
|
|
301
314
|
|
|
302
315
|
public async rm(...filenames: string[]): Promise<string> {
|
|
316
|
+
// https://android.googlesource.com/platform/packages/modules/adb/+/1a0fb8846d4e6b671c8aa7f137a8c21d7b248716/client/adb_install.cpp#984
|
|
303
317
|
const stdout = await this.subprocess.spawnAndWaitLegacy([
|
|
304
318
|
"rm",
|
|
305
|
-
"-rf",
|
|
306
319
|
...filenames.map((arg) => escapeArg(arg)),
|
|
320
|
+
"</dev/null",
|
|
307
321
|
]);
|
|
308
322
|
return stdout;
|
|
309
323
|
}
|
|
310
324
|
|
|
311
|
-
public install() {
|
|
312
|
-
return install(this);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
325
|
public async sync(): Promise<AdbSync> {
|
|
316
326
|
const socket = await this.createSocket("sync:");
|
|
317
327
|
return new AdbSync(this, socket);
|
package/src/auth.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { Disposable } from "@yume-chan/event";
|
|
3
|
+
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
calculatePublicKey,
|
|
7
7
|
calculatePublicKeyLength,
|
|
8
8
|
sign,
|
|
9
9
|
} from "./crypto.js";
|
|
10
|
-
import {
|
|
10
|
+
import type { AdbPacketData } from "./packet.js";
|
|
11
|
+
import { AdbCommand } from "./packet.js";
|
|
11
12
|
import { calculateBase64EncodedLength, encodeBase64 } from "./utils/index.js";
|
|
12
13
|
|
|
13
14
|
export type AdbKeyIterable = Iterable<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
@@ -97,9 +98,9 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
|
97
98
|
const [publicKeyBase64Length] =
|
|
98
99
|
calculateBase64EncodedLength(publicKeyLength);
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
const publicKeyBuffer = new Uint8Array(
|
|
102
|
+
publicKeyBase64Length + 1 // Null character
|
|
103
|
+
);
|
|
103
104
|
|
|
104
105
|
calculatePublicKey(privateKey, publicKeyBuffer);
|
|
105
106
|
encodeBase64(publicKeyBuffer.subarray(0, publicKeyLength), publicKeyBuffer);
|
package/src/backend.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { Consumable, ReadableWritablePair } from "@yume-chan/stream-extra";
|
|
2
|
+
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
|
5
5
|
|
|
6
6
|
export interface AdbBackend {
|
|
7
7
|
readonly serial: string;
|
|
@@ -9,6 +9,6 @@ export interface AdbBackend {
|
|
|
9
9
|
readonly name: string | undefined;
|
|
10
10
|
|
|
11
11
|
connect(): ValueOrPromise<
|
|
12
|
-
ReadableWritablePair<AdbPacketData, AdbPacketInit
|
|
12
|
+
ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>
|
|
13
13
|
>;
|
|
14
14
|
}
|
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/reverse.ts
CHANGED
|
@@ -7,11 +7,8 @@ import {
|
|
|
7
7
|
} from "@yume-chan/stream-extra";
|
|
8
8
|
import Struct from "@yume-chan/struct";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
type AdbIncomingSocketHandler,
|
|
13
|
-
type AdbSocket,
|
|
14
|
-
} from "../socket/index.js";
|
|
10
|
+
import type { Adb } from "../adb.js";
|
|
11
|
+
import type { AdbIncomingSocketHandler, AdbSocket } from "../socket/index.js";
|
|
15
12
|
import { decodeUtf8 } from "../utils/index.js";
|
|
16
13
|
|
|
17
14
|
export interface AdbForwardListener {
|
|
@@ -26,10 +23,33 @@ const AdbReverseStringResponse = new Struct()
|
|
|
26
23
|
.string("length", { length: 4 })
|
|
27
24
|
.string("content", { lengthField: "length", lengthFieldRadix: 16 });
|
|
28
25
|
|
|
26
|
+
export class AdbReverseError extends Error {
|
|
27
|
+
public constructor(message: string) {
|
|
28
|
+
super(message);
|
|
29
|
+
Object.setPrototypeOf(this, AdbReverseError.prototype);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class AdbReverseNotSupportedError extends Error {
|
|
34
|
+
public constructor() {
|
|
35
|
+
super(
|
|
36
|
+
"ADB reverse tunnel is not supported on this device when connected wirelessly."
|
|
37
|
+
);
|
|
38
|
+
Object.setPrototypeOf(this, AdbReverseNotSupportedError.prototype);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
const AdbReverseErrorResponse = new Struct()
|
|
30
43
|
.fields(AdbReverseStringResponse)
|
|
31
44
|
.postDeserialize((value) => {
|
|
32
|
-
|
|
45
|
+
// https://issuetracker.google.com/issues/37066218
|
|
46
|
+
// ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over WiFi),
|
|
47
|
+
// and returns this confusing "more than one device/emulator" error.
|
|
48
|
+
if (value.content === "more than one device/emulator") {
|
|
49
|
+
throw new AdbReverseNotSupportedError();
|
|
50
|
+
} else {
|
|
51
|
+
throw new AdbReverseError(value.content);
|
|
52
|
+
}
|
|
33
53
|
});
|
|
34
54
|
|
|
35
55
|
export class AdbReverseCommand extends AutoDisposable {
|
|
@@ -91,18 +111,23 @@ export class AdbReverseCommand extends AutoDisposable {
|
|
|
91
111
|
}
|
|
92
112
|
|
|
93
113
|
/**
|
|
94
|
-
* @param deviceAddress
|
|
95
|
-
*
|
|
114
|
+
* @param deviceAddress
|
|
115
|
+
* The address to be listened on device by ADB daemon. Or `tcp:0` to choose an available TCP port.
|
|
116
|
+
* @param localAddress
|
|
117
|
+
* An identifier for the reverse tunnel.
|
|
118
|
+
*
|
|
119
|
+
* When a socket wants to connect to {@link deviceAddress}, native ADB client will forward that connection to {@link localAddress}.
|
|
120
|
+
* However in this library, the {@link handler} is invoked instead. So this parameter is only used to identify the reverse tunnel.
|
|
96
121
|
* @param handler A callback to handle incoming connections. It must return `true` if it accepts the connection.
|
|
97
122
|
* @returns `tcp:{ACTUAL_LISTENING_PORT}`, If `deviceAddress` is `tcp:0`; otherwise, `deviceAddress`.
|
|
123
|
+
* @throws {AdbReverseNotSupportedError} If ADB reverse tunnel is not supported on this device when connected wirelessly.
|
|
124
|
+
* @throws {AdbReverseError} If ADB daemon returns an error.
|
|
98
125
|
*/
|
|
99
126
|
public async add(
|
|
100
127
|
deviceAddress: string,
|
|
101
128
|
localAddress: string,
|
|
102
129
|
handler: AdbIncomingSocketHandler
|
|
103
130
|
): Promise<string> {
|
|
104
|
-
// TODO(reverse): handle ADB bug
|
|
105
|
-
// https://issuetracker.google.com/issues/37066218
|
|
106
131
|
const stream = await this.sendRequest(
|
|
107
132
|
`reverse:forward:${deviceAddress};${localAddress}`
|
|
108
133
|
);
|
|
@@ -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,9 @@
|
|
|
1
|
-
import { DuplexStreamFactory, ReadableStream } from
|
|
1
|
+
import { DuplexStreamFactory, ReadableStream } from "@yume-chan/stream-extra";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import type { Adb } from "../../../adb.js";
|
|
4
|
+
import type { AdbSocket } from "../../../socket/index.js";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import type { AdbSubprocessProtocol } from "./types.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The legacy shell
|
|
@@ -14,16 +14,22 @@ import { type AdbSubprocessProtocol } from "./types.js";
|
|
|
14
14
|
* * `resize`: No
|
|
15
15
|
*/
|
|
16
16
|
export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
17
|
-
public static isSupported() {
|
|
17
|
+
public static isSupported() {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
18
20
|
|
|
19
21
|
public static async pty(adb: Adb, command: string) {
|
|
20
|
-
return new AdbSubprocessNoneProtocol(
|
|
22
|
+
return new AdbSubprocessNoneProtocol(
|
|
23
|
+
await adb.createSocket(`shell:${command}`)
|
|
24
|
+
);
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
public static async raw(adb: Adb, command: string) {
|
|
24
28
|
// `shell,raw:${command}` also triggers raw mode,
|
|
25
29
|
// But is not supported on Android version <7.
|
|
26
|
-
return new AdbSubprocessNoneProtocol(
|
|
30
|
+
return new AdbSubprocessNoneProtocol(
|
|
31
|
+
await adb.createSocket(`exec:${command}`)
|
|
32
|
+
);
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
private readonly socket: AdbSocket;
|
|
@@ -31,22 +37,30 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|
|
31
37
|
private readonly duplex: DuplexStreamFactory<Uint8Array, Uint8Array>;
|
|
32
38
|
|
|
33
39
|
// Legacy shell forwards all data to stdin.
|
|
34
|
-
public get stdin() {
|
|
40
|
+
public get stdin() {
|
|
41
|
+
return this.socket.writable;
|
|
42
|
+
}
|
|
35
43
|
|
|
36
44
|
private _stdout: ReadableStream<Uint8Array>;
|
|
37
45
|
/**
|
|
38
46
|
* Legacy shell mixes stdout and stderr.
|
|
39
47
|
*/
|
|
40
|
-
public get stdout() {
|
|
48
|
+
public get stdout() {
|
|
49
|
+
return this._stdout;
|
|
50
|
+
}
|
|
41
51
|
|
|
42
52
|
private _stderr: ReadableStream<Uint8Array>;
|
|
43
53
|
/**
|
|
44
54
|
* `stderr` will always be empty.
|
|
45
55
|
*/
|
|
46
|
-
public get stderr() {
|
|
56
|
+
public get stderr() {
|
|
57
|
+
return this._stderr;
|
|
58
|
+
}
|
|
47
59
|
|
|
48
60
|
private _exit: Promise<number>;
|
|
49
|
-
public get exit() {
|
|
61
|
+
public get exit() {
|
|
62
|
+
return this._exit;
|
|
63
|
+
}
|
|
50
64
|
|
|
51
65
|
public constructor(socket: AdbSocket) {
|
|
52
66
|
this.socket = socket;
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { PromiseResolver } from
|
|
1
|
+
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import type {
|
|
3
|
+
Consumable,
|
|
4
|
+
PushReadableStreamController,
|
|
5
|
+
ReadableStream,
|
|
6
|
+
WritableStreamDefaultWriter,
|
|
7
|
+
} from "@yume-chan/stream-extra";
|
|
2
8
|
import {
|
|
9
|
+
ConsumableTransformStream,
|
|
10
|
+
ConsumableWritableStream,
|
|
3
11
|
PushReadableStream,
|
|
4
12
|
StructDeserializeStream,
|
|
5
|
-
StructSerializeStream,
|
|
6
13
|
TransformStream,
|
|
7
14
|
WritableStream,
|
|
8
15
|
pipeFrom,
|
|
9
|
-
type PushReadableStreamController,
|
|
10
|
-
type ReadableStream,
|
|
11
|
-
type WritableStreamDefaultWriter,
|
|
12
16
|
} from "@yume-chan/stream-extra";
|
|
13
|
-
import
|
|
17
|
+
import type { StructValueType } from "@yume-chan/struct";
|
|
18
|
+
import Struct, { placeholder } from "@yume-chan/struct";
|
|
14
19
|
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { encodeUtf8 } from
|
|
20
|
+
import type { Adb } from "../../../adb.js";
|
|
21
|
+
import { AdbFeature } from "../../../features.js";
|
|
22
|
+
import type { AdbSocket } from "../../../socket/index.js";
|
|
23
|
+
import { encodeUtf8 } from "../../../utils/index.js";
|
|
19
24
|
|
|
20
|
-
import {
|
|
25
|
+
import type { AdbSubprocessProtocol } from "./types.js";
|
|
21
26
|
|
|
22
27
|
export enum AdbShellProtocolId {
|
|
23
28
|
Stdin,
|
|
@@ -29,33 +34,38 @@ export enum AdbShellProtocolId {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
// This packet format is used in both direction.
|
|
32
|
-
const AdbShellProtocolPacket =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.uint8Array('data', { lengthField: 'length' });
|
|
37
|
+
const AdbShellProtocolPacket = new Struct({ littleEndian: true })
|
|
38
|
+
.uint8("id", placeholder<AdbShellProtocolId>())
|
|
39
|
+
.uint32("length")
|
|
40
|
+
.uint8Array("data", { lengthField: "length" });
|
|
37
41
|
|
|
38
|
-
type AdbShellProtocolPacketInit = typeof AdbShellProtocolPacket[
|
|
42
|
+
type AdbShellProtocolPacketInit = (typeof AdbShellProtocolPacket)["TInit"];
|
|
39
43
|
|
|
40
44
|
type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>;
|
|
41
45
|
|
|
42
|
-
class StdinSerializeStream extends
|
|
46
|
+
class StdinSerializeStream extends ConsumableTransformStream<
|
|
47
|
+
Uint8Array,
|
|
48
|
+
AdbShellProtocolPacketInit
|
|
49
|
+
> {
|
|
43
50
|
constructor() {
|
|
44
51
|
super({
|
|
45
|
-
transform(chunk, controller) {
|
|
46
|
-
controller.enqueue({
|
|
52
|
+
async transform(chunk, controller) {
|
|
53
|
+
await controller.enqueue({
|
|
47
54
|
id: AdbShellProtocolId.Stdin,
|
|
48
55
|
data: chunk,
|
|
49
56
|
});
|
|
50
57
|
},
|
|
51
58
|
flush() {
|
|
52
59
|
// TODO: AdbShellSubprocessProtocol: support closing stdin
|
|
53
|
-
}
|
|
60
|
+
},
|
|
54
61
|
});
|
|
55
62
|
}
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
class StdoutDeserializeStream extends TransformStream<
|
|
65
|
+
class StdoutDeserializeStream extends TransformStream<
|
|
66
|
+
AdbShellProtocolPacket,
|
|
67
|
+
Uint8Array
|
|
68
|
+
> {
|
|
59
69
|
constructor(type: AdbShellProtocolId.Stdout | AdbShellProtocolId.Stderr) {
|
|
60
70
|
super({
|
|
61
71
|
transform(chunk, controller) {
|
|
@@ -116,7 +126,7 @@ class MultiplexStream<T> {
|
|
|
116
126
|
*/
|
|
117
127
|
export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
118
128
|
public static isSupported(adb: Adb) {
|
|
119
|
-
return adb.
|
|
129
|
+
return adb.supportsFeature(AdbFeature.ShellV2);
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
public static async pty(adb: Adb, command: string) {
|
|
@@ -133,9 +143,11 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
private readonly _socket: AdbSocket;
|
|
136
|
-
private _socketWriter: WritableStreamDefaultWriter<
|
|
146
|
+
private _socketWriter: WritableStreamDefaultWriter<
|
|
147
|
+
Consumable<AdbShellProtocolPacketInit>
|
|
148
|
+
>;
|
|
137
149
|
|
|
138
|
-
private _stdin: WritableStream<Uint8Array
|
|
150
|
+
private _stdin: WritableStream<Consumable<Uint8Array>>;
|
|
139
151
|
public get stdin() {
|
|
140
152
|
return this._stdin;
|
|
141
153
|
}
|
|
@@ -192,9 +204,19 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
192
204
|
new StdoutDeserializeStream(AdbShellProtocolId.Stderr)
|
|
193
205
|
);
|
|
194
206
|
|
|
195
|
-
const multiplexer = new MultiplexStream<
|
|
207
|
+
const multiplexer = new MultiplexStream<
|
|
208
|
+
Consumable<AdbShellProtocolPacketInit>
|
|
209
|
+
>();
|
|
196
210
|
void multiplexer.readable
|
|
197
|
-
.pipeThrough(
|
|
211
|
+
.pipeThrough(
|
|
212
|
+
new ConsumableTransformStream({
|
|
213
|
+
async transform(chunk, controller) {
|
|
214
|
+
await controller.enqueue(
|
|
215
|
+
AdbShellProtocolPacket.serialize(chunk)
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
})
|
|
219
|
+
)
|
|
198
220
|
.pipeTo(socket.writable);
|
|
199
221
|
|
|
200
222
|
this._stdin = pipeFrom(
|
|
@@ -206,7 +228,7 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
|
|
|
206
228
|
}
|
|
207
229
|
|
|
208
230
|
public async resize(rows: number, cols: number) {
|
|
209
|
-
await this._socketWriter
|
|
231
|
+
await ConsumableWritableStream.write(this._socketWriter, {
|
|
210
232
|
id: AdbShellProtocolId.WindowSizeChange,
|
|
211
233
|
data: encodeUtf8(
|
|
212
234
|
// The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}`
|