@yume-chan/adb 0.0.19 → 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 +33 -0
- package/CHANGELOG.md +15 -1
- package/README.md +220 -103
- package/esm/adb.d.ts +24 -44
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +14 -203
- 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/power.d.ts +1 -1
- package/esm/commands/power.js +2 -2
- package/esm/commands/reverse.d.ts +17 -18
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +53 -48
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +3 -8
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +16 -16
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +3 -9
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +48 -42
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +1 -2
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -2
- package/esm/commands/sync/push.d.ts +14 -3
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +8 -8
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +1 -1
- package/esm/commands/sync/response.d.ts +3 -3
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +3 -3
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +8 -13
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +32 -29
- 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 +11 -18
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +19 -23
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +2 -2
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +4 -2
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/{auth.d.ts → daemon/auth.d.ts} +1 -2
- package/esm/daemon/auth.d.ts.map +1 -0
- package/esm/{auth.js → daemon/auth.js} +14 -14
- 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} +5 -5
- package/esm/daemon/crypto.d.ts.map +1 -0
- package/esm/{crypto.js → daemon/crypto.js} +23 -27
- package/esm/daemon/crypto.js.map +1 -0
- package/esm/{backend.d.ts → daemon/device.d.ts} +2 -2
- 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/{socket → daemon}/dispatcher.d.ts +8 -26
- package/esm/daemon/dispatcher.d.ts.map +1 -0
- package/esm/{socket → daemon}/dispatcher.js +65 -63
- 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} +4 -4
- package/esm/daemon/packet.d.ts.map +1 -0
- package/esm/{packet.js → daemon/packet.js} +1 -1
- package/esm/daemon/packet.js.map +1 -0
- package/esm/{socket → daemon}/socket.d.ts +15 -17
- package/esm/daemon/socket.d.ts.map +1 -0
- package/esm/{socket → daemon}/socket.js +38 -35
- 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/index.d.ts +3 -5
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -5
- 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 +1 -2
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js +12 -12
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/base64.js +5 -5
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts +1 -2
- package/esm/utils/conditional-variable.d.ts.map +1 -1
- package/esm/utils/conditional-variable.js +10 -10
- package/esm/utils/conditional-variable.js.map +1 -1
- 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 +2 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +2 -0
- 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 +11 -10
- package/src/adb.ts +41 -252
- package/src/banner.ts +82 -0
- package/src/commands/power.ts +2 -2
- package/src/commands/reverse.ts +68 -73
- package/src/commands/subprocess/protocols/none.ts +17 -18
- package/src/commands/subprocess/protocols/shell.ts +54 -56
- package/src/commands/subprocess/protocols/types.ts +1 -2
- package/src/commands/sync/list.ts +2 -2
- package/src/commands/sync/push.ts +22 -10
- package/src/commands/sync/request.ts +1 -1
- package/src/commands/sync/response.ts +6 -6
- package/src/commands/sync/socket.ts +37 -35
- package/src/commands/sync/sync.ts +25 -28
- package/src/commands/tcpip.ts +6 -4
- package/src/{auth.ts → daemon/auth.ts} +17 -17
- package/src/{crypto.ts → daemon/crypto.ts} +25 -33
- package/src/{backend.ts → daemon/device.ts} +1 -1
- package/src/{socket → daemon}/dispatcher.ts +71 -78
- package/src/daemon/index.ts +7 -0
- package/src/{packet.ts → daemon/packet.ts} +5 -5
- package/src/{socket → daemon}/socket.ts +50 -44
- package/src/daemon/transport.ts +269 -0
- 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 +12 -12
- package/src/utils/conditional-variable.ts +10 -10
- package/src/utils/hex.ts +58 -0
- package/src/utils/index.ts +2 -0
- 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.map +0 -1
- package/esm/backend.js +0 -2
- package/esm/backend.js.map +0 -1
- package/esm/commands/install.d.ts +0 -10
- 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.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.map +0 -1
- package/esm/socket/socket.js.map +0 -1
- package/src/socket/index.ts +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
AsyncExactReadable,
|
|
3
3
|
StructLike,
|
|
4
4
|
StructValueType,
|
|
5
5
|
} from "@yume-chan/struct";
|
|
@@ -27,11 +27,11 @@ export const AdbSyncFailResponse = new Struct({ littleEndian: true })
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
export async function adbSyncReadResponse<T>(
|
|
30
|
-
stream:
|
|
30
|
+
stream: AsyncExactReadable,
|
|
31
31
|
id: AdbSyncResponseId,
|
|
32
32
|
type: StructLike<T>
|
|
33
33
|
): Promise<T> {
|
|
34
|
-
const actualId = decodeUtf8(await stream.
|
|
34
|
+
const actualId = decodeUtf8(await stream.readExactly(4));
|
|
35
35
|
switch (actualId) {
|
|
36
36
|
case AdbSyncResponseId.Fail:
|
|
37
37
|
await AdbSyncFailResponse.deserialize(stream);
|
|
@@ -46,12 +46,12 @@ export async function adbSyncReadResponse<T>(
|
|
|
46
46
|
export async function* adbSyncReadResponses<
|
|
47
47
|
T extends Struct<object, PropertyKey, object, any>
|
|
48
48
|
>(
|
|
49
|
-
stream:
|
|
49
|
+
stream: AsyncExactReadable,
|
|
50
50
|
id: AdbSyncResponseId,
|
|
51
51
|
type: T
|
|
52
52
|
): AsyncGenerator<StructValueType<T>, void, void> {
|
|
53
53
|
while (true) {
|
|
54
|
-
const actualId = decodeUtf8(await stream.
|
|
54
|
+
const actualId = decodeUtf8(await stream.readExactly(4));
|
|
55
55
|
switch (actualId) {
|
|
56
56
|
case AdbSyncResponseId.Fail:
|
|
57
57
|
await AdbSyncFailResponse.deserialize(stream);
|
|
@@ -61,7 +61,7 @@ export async function* adbSyncReadResponses<
|
|
|
61
61
|
//
|
|
62
62
|
// For example, `DONE` responses for `LIST` requests are 16 bytes (same as `DENT` responses),
|
|
63
63
|
// but `DONE` responses for `STAT` requests are 12 bytes (same as `STAT` responses).
|
|
64
|
-
await stream.
|
|
64
|
+
await stream.readExactly(type.size);
|
|
65
65
|
return;
|
|
66
66
|
case id:
|
|
67
67
|
yield await type.deserialize(stream);
|
|
@@ -7,19 +7,21 @@ import {
|
|
|
7
7
|
BufferedReadableStream,
|
|
8
8
|
ConsumableWritableStream,
|
|
9
9
|
} from "@yume-chan/stream-extra";
|
|
10
|
-
import type {
|
|
10
|
+
import type { AsyncExactReadable } from "@yume-chan/struct";
|
|
11
11
|
|
|
12
|
-
import type { AdbSocket } from "../../
|
|
13
|
-
import { AutoResetEvent } from "../../index.js";
|
|
12
|
+
import type { AdbSocket } from "../../adb.js";
|
|
13
|
+
import { AutoResetEvent } from "../../utils/index.js";
|
|
14
14
|
|
|
15
|
-
export class AdbSyncSocketLocked implements
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
export class AdbSyncSocketLocked implements AsyncExactReadable {
|
|
16
|
+
readonly #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
|
|
17
|
+
readonly #readable: BufferedReadableStream;
|
|
18
|
+
readonly #socketLock: AutoResetEvent;
|
|
19
|
+
readonly #writeLock = new AutoResetEvent();
|
|
20
|
+
readonly #combiner: BufferCombiner;
|
|
21
|
+
|
|
22
|
+
public get position() {
|
|
23
|
+
return this.#readable.position;
|
|
24
|
+
}
|
|
23
25
|
|
|
24
26
|
public constructor(
|
|
25
27
|
writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>,
|
|
@@ -27,71 +29,71 @@ export class AdbSyncSocketLocked implements StructAsyncDeserializeStream {
|
|
|
27
29
|
bufferSize: number,
|
|
28
30
|
lock: AutoResetEvent
|
|
29
31
|
) {
|
|
30
|
-
this
|
|
31
|
-
this
|
|
32
|
-
this
|
|
33
|
-
this
|
|
32
|
+
this.#writer = writer;
|
|
33
|
+
this.#readable = readable;
|
|
34
|
+
this.#socketLock = lock;
|
|
35
|
+
this.#combiner = new BufferCombiner(bufferSize);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
private async writeInnerStream(buffer: Uint8Array) {
|
|
37
|
-
await ConsumableWritableStream.write(this
|
|
39
|
+
await ConsumableWritableStream.write(this.#writer, buffer);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
public async flush() {
|
|
41
43
|
try {
|
|
42
|
-
await this.
|
|
43
|
-
const buffer = this.
|
|
44
|
+
await this.#writeLock.wait();
|
|
45
|
+
const buffer = this.#combiner.flush();
|
|
44
46
|
if (buffer) {
|
|
45
47
|
await this.writeInnerStream(buffer);
|
|
46
48
|
}
|
|
47
49
|
} finally {
|
|
48
|
-
this.
|
|
50
|
+
this.#writeLock.notifyOne();
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
public async write(data: Uint8Array) {
|
|
53
55
|
try {
|
|
54
|
-
await this.
|
|
55
|
-
for (const buffer of this.
|
|
56
|
+
await this.#writeLock.wait();
|
|
57
|
+
for (const buffer of this.#combiner.push(data)) {
|
|
56
58
|
await this.writeInnerStream(buffer);
|
|
57
59
|
}
|
|
58
60
|
} finally {
|
|
59
|
-
this.
|
|
61
|
+
this.#writeLock.notifyOne();
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
public async
|
|
65
|
+
public async readExactly(length: number) {
|
|
64
66
|
await this.flush();
|
|
65
|
-
return await this.
|
|
67
|
+
return await this.#readable.readExactly(length);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
public release(): void {
|
|
69
|
-
this.
|
|
70
|
-
this.
|
|
71
|
+
this.#combiner.flush();
|
|
72
|
+
this.#socketLock.notifyOne();
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export class AdbSyncSocket {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
readonly #lock = new AutoResetEvent();
|
|
78
|
+
readonly #socket: AdbSocket;
|
|
79
|
+
readonly #locked: AdbSyncSocketLocked;
|
|
78
80
|
|
|
79
81
|
public constructor(socket: AdbSocket, bufferSize: number) {
|
|
80
|
-
this
|
|
81
|
-
this
|
|
82
|
+
this.#socket = socket;
|
|
83
|
+
this.#locked = new AdbSyncSocketLocked(
|
|
82
84
|
socket.writable.getWriter(),
|
|
83
85
|
new BufferedReadableStream(socket.readable),
|
|
84
86
|
bufferSize,
|
|
85
|
-
this
|
|
87
|
+
this.#lock
|
|
86
88
|
);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
public async lock() {
|
|
90
|
-
await this.
|
|
91
|
-
return this
|
|
92
|
+
await this.#lock.wait();
|
|
93
|
+
return this.#locked;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
public async close() {
|
|
95
|
-
await this.
|
|
97
|
+
await this.#socket.close();
|
|
96
98
|
}
|
|
97
99
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { AutoDisposable } from "@yume-chan/event";
|
|
2
2
|
import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
|
|
3
3
|
|
|
4
|
-
import type { Adb } from "../../adb.js";
|
|
4
|
+
import type { Adb, AdbSocket } from "../../adb.js";
|
|
5
5
|
import { AdbFeature } from "../../features.js";
|
|
6
|
-
import type { AdbSocket } from "../../socket/index.js";
|
|
7
6
|
import { escapeArg } from "../subprocess/index.js";
|
|
8
7
|
|
|
9
8
|
import type { AdbSyncEntry } from "./list.js";
|
|
@@ -11,6 +10,7 @@ import { adbSyncOpenDir } from "./list.js";
|
|
|
11
10
|
import { adbSyncPull } from "./pull.js";
|
|
12
11
|
import { adbSyncPush } from "./push.js";
|
|
13
12
|
import { AdbSyncSocket } from "./socket.js";
|
|
13
|
+
import type { AdbSyncStat, LinuxFileType } from "./stat.js";
|
|
14
14
|
import { adbSyncLstat, adbSyncStat } from "./stat.js";
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -32,7 +32,8 @@ export function dirname(path: string): string {
|
|
|
32
32
|
export interface AdbSyncWriteOptions {
|
|
33
33
|
filename: string;
|
|
34
34
|
file: ReadableStream<Consumable<Uint8Array>>;
|
|
35
|
-
|
|
35
|
+
type?: LinuxFileType;
|
|
36
|
+
permission?: number;
|
|
36
37
|
mtime?: number;
|
|
37
38
|
dryRun?: boolean;
|
|
38
39
|
}
|
|
@@ -41,30 +42,30 @@ export class AdbSync extends AutoDisposable {
|
|
|
41
42
|
protected _adb: Adb;
|
|
42
43
|
protected _socket: AdbSyncSocket;
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
readonly #supportsStat: boolean;
|
|
46
|
+
readonly #supportsListV2: boolean;
|
|
47
|
+
readonly #fixedPushMkdir: boolean;
|
|
48
|
+
readonly #supportsSendReceiveV2: boolean;
|
|
49
|
+
readonly #needPushMkdirWorkaround: boolean;
|
|
49
50
|
|
|
50
51
|
public get supportsStat(): boolean {
|
|
51
|
-
return this
|
|
52
|
+
return this.#supportsStat;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
public get supportsListV2(): boolean {
|
|
55
|
-
return this
|
|
56
|
+
return this.#supportsListV2;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
public get fixedPushMkdir(): boolean {
|
|
59
|
-
return this
|
|
60
|
+
return this.#fixedPushMkdir;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
public get supportsSendReceiveV2(): boolean {
|
|
63
|
-
return this
|
|
64
|
+
return this.#supportsSendReceiveV2;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
public get needPushMkdirWorkaround(): boolean {
|
|
67
|
-
return this
|
|
68
|
+
return this.#needPushMkdirWorkaround;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
public constructor(adb: Adb, socket: AdbSocket) {
|
|
@@ -73,19 +74,19 @@ export class AdbSync extends AutoDisposable {
|
|
|
73
74
|
this._adb = adb;
|
|
74
75
|
this._socket = new AdbSyncSocket(socket, adb.maxPayloadSize);
|
|
75
76
|
|
|
76
|
-
this
|
|
77
|
-
this
|
|
78
|
-
this
|
|
79
|
-
this
|
|
77
|
+
this.#supportsStat = adb.supportsFeature(AdbFeature.StatV2);
|
|
78
|
+
this.#supportsListV2 = adb.supportsFeature(AdbFeature.ListV2);
|
|
79
|
+
this.#fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir);
|
|
80
|
+
this.#supportsSendReceiveV2 = adb.supportsFeature(
|
|
80
81
|
AdbFeature.SendReceiveV2
|
|
81
82
|
);
|
|
82
83
|
// https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
|
|
83
|
-
this
|
|
84
|
+
this.#needPushMkdirWorkaround =
|
|
84
85
|
this._adb.supportsFeature(AdbFeature.ShellV2) &&
|
|
85
86
|
!this.fixedPushMkdir;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
public async lstat(path: string) {
|
|
89
|
+
public async lstat(path: string): Promise<AdbSyncStat> {
|
|
89
90
|
return await adbSyncLstat(this._socket, path, this.supportsStat);
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -119,25 +120,21 @@ export class AdbSync extends AutoDisposable {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
|
-
*
|
|
123
|
+
* Reads the content of a file on device.
|
|
123
124
|
*
|
|
124
125
|
* @param filename The full path of the file on device to read.
|
|
125
|
-
* @returns A `ReadableStream` that
|
|
126
|
+
* @returns A `ReadableStream` that contains the file content.
|
|
126
127
|
*/
|
|
127
128
|
public read(filename: string): ReadableStream<Uint8Array> {
|
|
128
129
|
return adbSyncPull(this._socket, filename);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
|
-
*
|
|
133
|
+
* Writes a file on device. If the file name already exists, it will be overwritten.
|
|
133
134
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @param file The content to write.
|
|
136
|
-
* @param mode The unix permissions of the file.
|
|
137
|
-
* @param mtime The modified time of the file.
|
|
138
|
-
* @returns A `WritableStream` that writes to the file.
|
|
135
|
+
* @param options The content and options of the file to write.
|
|
139
136
|
*/
|
|
140
|
-
public async write(options: AdbSyncWriteOptions) {
|
|
137
|
+
public async write(options: AdbSyncWriteOptions): Promise<void> {
|
|
141
138
|
if (this.needPushMkdirWorkaround) {
|
|
142
139
|
// It may fail if the path is already existed.
|
|
143
140
|
// Ignore the result.
|
package/src/commands/tcpip.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { AdbCommandBase } from "./base.js";
|
|
2
2
|
|
|
3
3
|
export class AdbTcpIpCommand extends AdbCommandBase {
|
|
4
|
-
public async setPort(port: number): Promise<
|
|
4
|
+
public async setPort(port: number): Promise<string> {
|
|
5
5
|
if (port <= 0) {
|
|
6
6
|
throw new Error(`Invalid port ${port}`);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const output = await this.adb.createSocketAndWait(`tcpip:${port}`);
|
|
10
10
|
if (output !== `restarting in TCP mode port: ${port}\n`) {
|
|
11
|
-
throw new Error(
|
|
11
|
+
throw new Error(output);
|
|
12
12
|
}
|
|
13
|
+
return output;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
public async disable(): Promise<
|
|
16
|
+
public async disable(): Promise<string> {
|
|
16
17
|
const output = await this.adb.createSocketAndWait("usb:");
|
|
17
18
|
if (output !== "restarting in USB mode\n") {
|
|
18
|
-
throw new Error(
|
|
19
|
+
throw new Error(output);
|
|
19
20
|
}
|
|
21
|
+
return output;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -2,14 +2,15 @@ import { PromiseResolver } from "@yume-chan/async";
|
|
|
2
2
|
import type { Disposable } from "@yume-chan/event";
|
|
3
3
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
4
4
|
|
|
5
|
+
import { calculateBase64EncodedLength, encodeBase64 } from "../utils/index.js";
|
|
6
|
+
|
|
5
7
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
adbGeneratePublicKey,
|
|
9
|
+
adbGetPublicKeySize,
|
|
10
|
+
rsaSign,
|
|
9
11
|
} from "./crypto.js";
|
|
10
12
|
import type { AdbPacketData } from "./packet.js";
|
|
11
13
|
import { AdbCommand } from "./packet.js";
|
|
12
|
-
import { calculateBase64EncodedLength, encodeBase64 } from "./utils/index.js";
|
|
13
14
|
|
|
14
15
|
export type AdbKeyIterable = Iterable<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
15
16
|
|
|
@@ -64,7 +65,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
|
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
const signature =
|
|
68
|
+
const signature = rsaSign(key, packet.payload);
|
|
68
69
|
yield {
|
|
69
70
|
command: AdbCommand.Auth,
|
|
70
71
|
arg0: AdbAuthType.Signature,
|
|
@@ -94,7 +95,7 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
|
94
95
|
privateKey = await credentialStore.generateKey();
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
const publicKeyLength =
|
|
98
|
+
const publicKeyLength = adbGetPublicKeySize();
|
|
98
99
|
const [publicKeyBase64Length] =
|
|
99
100
|
calculateBase64EncodedLength(publicKeyLength);
|
|
100
101
|
|
|
@@ -102,7 +103,7 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
|
102
103
|
publicKeyBase64Length + 1 // Null character
|
|
103
104
|
);
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
adbGeneratePublicKey(privateKey, publicKeyBuffer);
|
|
106
107
|
encodeBase64(publicKeyBuffer.subarray(0, publicKeyLength), publicKeyBuffer);
|
|
107
108
|
|
|
108
109
|
yield {
|
|
@@ -123,9 +124,8 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
123
124
|
|
|
124
125
|
private readonly credentialStore: AdbCredentialStore;
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
private iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
|
|
127
|
+
#pendingRequest = new PromiseResolver<AdbPacketData>();
|
|
128
|
+
#iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
|
|
129
129
|
|
|
130
130
|
public constructor(
|
|
131
131
|
authenticators: readonly AdbAuthenticator[],
|
|
@@ -136,7 +136,7 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
private getNextRequest = (): Promise<AdbPacketData> => {
|
|
139
|
-
return this
|
|
139
|
+
return this.#pendingRequest.promise;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
142
|
private async *invokeAuthenticator(): AsyncGenerator<
|
|
@@ -151,7 +151,7 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
151
151
|
)) {
|
|
152
152
|
// If the authenticator yielded a response
|
|
153
153
|
// Prepare `nextRequest` for next authentication request
|
|
154
|
-
this
|
|
154
|
+
this.#pendingRequest = new PromiseResolver();
|
|
155
155
|
|
|
156
156
|
// Yield the response to outer layer
|
|
157
157
|
yield packet;
|
|
@@ -163,13 +163,13 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
public async process(packet: AdbPacketData): Promise<AdbPacketData> {
|
|
166
|
-
if (!this
|
|
167
|
-
this
|
|
166
|
+
if (!this.#iterator) {
|
|
167
|
+
this.#iterator = this.invokeAuthenticator();
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
this
|
|
170
|
+
this.#pendingRequest.resolve(packet);
|
|
171
171
|
|
|
172
|
-
const result = await this
|
|
172
|
+
const result = await this.#iterator.next();
|
|
173
173
|
if (result.done) {
|
|
174
174
|
throw new Error("No authenticator can handle the request");
|
|
175
175
|
}
|
|
@@ -178,6 +178,6 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
public dispose() {
|
|
181
|
-
void this
|
|
181
|
+
void this.#iterator?.return?.();
|
|
182
182
|
}
|
|
183
183
|
}
|
|
@@ -3,11 +3,6 @@ import {
|
|
|
3
3
|
setBigUint64,
|
|
4
4
|
} from "@yume-chan/dataview-bigint-polyfill/esm/fallback.js";
|
|
5
5
|
|
|
6
|
-
const BigInt0 = BigInt(0);
|
|
7
|
-
const BigInt1 = BigInt(1);
|
|
8
|
-
const BigInt2 = BigInt(2);
|
|
9
|
-
const BigInt64 = BigInt(64);
|
|
10
|
-
|
|
11
6
|
/**
|
|
12
7
|
* Gets the `BigInt` value at the specified byte offset and length from the start of the view. There is
|
|
13
8
|
* no alignment constraint; multi-byte values may be fetched from any offset.
|
|
@@ -20,13 +15,13 @@ export function getBigUint(
|
|
|
20
15
|
byteOffset: number,
|
|
21
16
|
length: number
|
|
22
17
|
): bigint {
|
|
23
|
-
let result =
|
|
18
|
+
let result = 0n;
|
|
24
19
|
|
|
25
20
|
// Currently `length` must be a multiplication of 8
|
|
26
21
|
// Support for arbitrary length can be easily added
|
|
27
22
|
|
|
28
23
|
for (let i = byteOffset; i < byteOffset + length; i += 8) {
|
|
29
|
-
result <<=
|
|
24
|
+
result <<= 64n;
|
|
30
25
|
const value = getBigUint64(dataView, i, false);
|
|
31
26
|
result += value;
|
|
32
27
|
}
|
|
@@ -50,18 +45,18 @@ export function setBigUint(
|
|
|
50
45
|
const start = byteOffset;
|
|
51
46
|
|
|
52
47
|
if (littleEndian) {
|
|
53
|
-
while (value >
|
|
48
|
+
while (value > 0n) {
|
|
54
49
|
setBigUint64(dataView, byteOffset, value, true);
|
|
55
50
|
byteOffset += 8;
|
|
56
|
-
value >>=
|
|
51
|
+
value >>= 64n;
|
|
57
52
|
}
|
|
58
53
|
} else {
|
|
59
54
|
// Because we don't know how long (in bits) the `value` is,
|
|
60
55
|
// Convert it to an array of `uint64` first.
|
|
61
56
|
const uint64Array: bigint[] = [];
|
|
62
|
-
while (value >
|
|
57
|
+
while (value > 0n) {
|
|
63
58
|
uint64Array.push(BigInt.asUintN(64, value));
|
|
64
|
-
value >>=
|
|
59
|
+
value >>= 64n;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
for (let i = uint64Array.length - 1; i >= 0; i -= 1) {
|
|
@@ -99,7 +94,7 @@ const RsaPrivateKeyNLength = 2048 / 8;
|
|
|
99
94
|
const RsaPrivateKeyDOffset = 303;
|
|
100
95
|
const RsaPrivateKeyDLength = 2048 / 8;
|
|
101
96
|
|
|
102
|
-
export function
|
|
97
|
+
export function rsaParsePrivateKey(key: Uint8Array): [n: bigint, d: bigint] {
|
|
103
98
|
const view = new DataView(key.buffer, key.byteOffset, key.byteLength);
|
|
104
99
|
const n = getBigUint(view, RsaPrivateKeyNOffset, RsaPrivateKeyNLength);
|
|
105
100
|
const d = getBigUint(view, RsaPrivateKeyDOffset, RsaPrivateKeyDLength);
|
|
@@ -133,16 +128,16 @@ export function modInverse(a: number, m: number) {
|
|
|
133
128
|
return ((y % m) + m) % m;
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
export function
|
|
131
|
+
export function adbGetPublicKeySize() {
|
|
137
132
|
return 4 + 4 + 2048 / 8 + 2048 / 8 + 4;
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
export function
|
|
141
|
-
export function
|
|
135
|
+
export function adbGeneratePublicKey(privateKey: Uint8Array): Uint8Array;
|
|
136
|
+
export function adbGeneratePublicKey(
|
|
142
137
|
privateKey: Uint8Array,
|
|
143
138
|
output: Uint8Array
|
|
144
139
|
): number;
|
|
145
|
-
export function
|
|
140
|
+
export function adbGeneratePublicKey(
|
|
146
141
|
privateKey: Uint8Array,
|
|
147
142
|
output?: Uint8Array
|
|
148
143
|
): Uint8Array | number {
|
|
@@ -164,10 +159,10 @@ export function calculatePublicKey(
|
|
|
164
159
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38
|
|
165
160
|
|
|
166
161
|
// extract `n` from private key
|
|
167
|
-
const [n] =
|
|
162
|
+
const [n] = rsaParsePrivateKey(privateKey);
|
|
168
163
|
|
|
169
164
|
let outputType: "Uint8Array" | "number";
|
|
170
|
-
const outputLength =
|
|
165
|
+
const outputLength = adbGetPublicKeySize();
|
|
171
166
|
if (!output) {
|
|
172
167
|
output = new Uint8Array(outputLength);
|
|
173
168
|
outputType = "Uint8Array";
|
|
@@ -191,12 +186,9 @@ export function calculatePublicKey(
|
|
|
191
186
|
outputOffset += 4;
|
|
192
187
|
|
|
193
188
|
// Calculate `n0inv`
|
|
194
|
-
// Don't know why need to
|
|
189
|
+
// Don't know why need to multiply by -1
|
|
195
190
|
// Didn't exist in Android codebase
|
|
196
|
-
const n0inv = modInverse(
|
|
197
|
-
Number(BigInt.asUintN(32, n) * BigInt(-1)),
|
|
198
|
-
2 ** 32
|
|
199
|
-
);
|
|
191
|
+
const n0inv = modInverse(-Number(BigInt.asUintN(32, n)), 2 ** 32);
|
|
200
192
|
outputView.setUint32(outputOffset, n0inv, true);
|
|
201
193
|
outputOffset += 4;
|
|
202
194
|
|
|
@@ -205,7 +197,7 @@ export function calculatePublicKey(
|
|
|
205
197
|
outputOffset += 256;
|
|
206
198
|
|
|
207
199
|
// Calculate rr = (2^(rsa_size)) ^ 2 mod n
|
|
208
|
-
const rr =
|
|
200
|
+
const rr = 2n ** 4096n % n;
|
|
209
201
|
outputOffset += setBigUint(outputView, outputOffset, rr, true);
|
|
210
202
|
|
|
211
203
|
// exponent
|
|
@@ -231,20 +223,20 @@ export function powMod(
|
|
|
231
223
|
exponent: bigint,
|
|
232
224
|
modulus: bigint
|
|
233
225
|
): bigint {
|
|
234
|
-
if (modulus ===
|
|
235
|
-
return
|
|
226
|
+
if (modulus === 1n) {
|
|
227
|
+
return 0n;
|
|
236
228
|
}
|
|
237
229
|
|
|
238
|
-
let r =
|
|
230
|
+
let r = 1n;
|
|
239
231
|
base = base % modulus;
|
|
240
232
|
|
|
241
|
-
while (exponent >
|
|
242
|
-
if (BigInt.asUintN(1, exponent) ===
|
|
233
|
+
while (exponent > 0n) {
|
|
234
|
+
if (BigInt.asUintN(1, exponent) === 1n) {
|
|
243
235
|
r = (r * base) % modulus;
|
|
244
236
|
}
|
|
245
237
|
|
|
246
|
-
|
|
247
|
-
|
|
238
|
+
base = (base * base) % modulus;
|
|
239
|
+
exponent >>= 1n;
|
|
248
240
|
}
|
|
249
241
|
|
|
250
242
|
return r;
|
|
@@ -283,8 +275,8 @@ export const SHA1_DIGEST_INFO = new Uint8Array([
|
|
|
283
275
|
// encrypt the given data with its private key)
|
|
284
276
|
// However SubtileCrypto.encrypt() doesn't accept 'RSASSA-PKCS1-v1_5' algorithm
|
|
285
277
|
// So we need to implement the encryption by ourself
|
|
286
|
-
export function
|
|
287
|
-
const [n, d] =
|
|
278
|
+
export function rsaSign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
|
|
279
|
+
const [n, d] = rsaParsePrivateKey(privateKey);
|
|
288
280
|
|
|
289
281
|
// PKCS#1 padding
|
|
290
282
|
const padded = new Uint8Array(256);
|