@yume-chan/adb 0.0.20 → 0.0.21
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 +24 -0
- package/CHANGELOG.md +12 -1
- package/README.md +5 -3
- package/esm/adb.d.ts +4 -1
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +21 -11
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts.map +1 -1
- package/esm/banner.js +1 -1
- package/esm/banner.js.map +1 -1
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +9 -0
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +27 -3
- package/esm/commands/framebuffer.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.d.ts +1 -1
- package/esm/commands/subprocess/command.d.ts.map +1 -1
- package/esm/commands/subprocess/command.js +15 -13
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +15 -15
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/sync/push.js +1 -1
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +1 -1
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +2 -0
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +4 -2
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +0 -1
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +3 -3
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.js +2 -2
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +15 -0
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +17 -2
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.d.ts +11 -9
- package/esm/daemon/auth.d.ts.map +1 -1
- package/esm/daemon/auth.js +21 -12
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.js +2 -2
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/dispatcher.d.ts +4 -4
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js +16 -16
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.js +1 -1
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +0 -2
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +12 -8
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +10 -2
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +5 -3
- package/esm/daemon/transport.js.map +1 -1
- package/esm/features.js +1 -1
- package/esm/features.js.map +1 -1
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +1 -2
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts.map +1 -1
- package/esm/utils/conditional-variable.js.map +1 -1
- package/package.json +12 -13
- package/src/adb.ts +42 -30
- package/src/banner.ts +7 -7
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +31 -3
- package/src/commands/power.ts +9 -9
- package/src/commands/reverse.ts +13 -13
- package/src/commands/subprocess/command.ts +24 -25
- package/src/commands/subprocess/protocols/none.ts +12 -12
- package/src/commands/subprocess/protocols/shell.ts +35 -35
- package/src/commands/sync/list.ts +5 -5
- package/src/commands/sync/pull.ts +4 -4
- package/src/commands/sync/push.ts +6 -6
- package/src/commands/sync/request.ts +3 -3
- package/src/commands/sync/response.ts +7 -5
- package/src/commands/sync/socket.ts +14 -14
- package/src/commands/sync/stat.ts +5 -5
- package/src/commands/sync/sync.ts +32 -17
- package/src/commands/tcpip.ts +2 -2
- package/src/daemon/auth.ts +49 -30
- package/src/daemon/crypto.ts +9 -9
- package/src/daemon/dispatcher.ts +40 -39
- package/src/daemon/packet.ts +1 -1
- package/src/daemon/socket.ts +34 -30
- package/src/daemon/transport.ts +32 -20
- package/src/server/client.ts +38 -41
- package/src/server/transport.ts +13 -13
- package/src/utils/auto-reset-event.ts +4 -4
- package/src/utils/base64.ts +5 -5
- package/src/utils/conditional-variable.ts +4 -4
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/daemon/connection.d.ts +0 -11
- package/esm/daemon/connection.d.ts.map +0 -1
- package/esm/daemon/connection.js +0 -2
- package/esm/daemon/connection.js.map +0 -1
|
@@ -26,14 +26,14 @@ export interface AdbSyncPushV1Options {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const AdbSyncOkResponse = new Struct({ littleEndian: true }).uint32(
|
|
29
|
-
"unused"
|
|
29
|
+
"unused",
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
async function pipeFileData(
|
|
33
33
|
locked: AdbSyncSocketLocked,
|
|
34
34
|
file: ReadableStream<Consumable<Uint8Array>>,
|
|
35
35
|
packetSize: number,
|
|
36
|
-
mtime: number
|
|
36
|
+
mtime: number,
|
|
37
37
|
) {
|
|
38
38
|
// Read and write in parallel,
|
|
39
39
|
// allow error response to abort the write.
|
|
@@ -45,11 +45,11 @@ async function pipeFileData(
|
|
|
45
45
|
await adbSyncWriteRequest(
|
|
46
46
|
locked,
|
|
47
47
|
AdbSyncRequestId.Data,
|
|
48
|
-
chunk
|
|
48
|
+
chunk,
|
|
49
49
|
);
|
|
50
50
|
},
|
|
51
51
|
}),
|
|
52
|
-
{ signal: abortController.signal }
|
|
52
|
+
{ signal: abortController.signal },
|
|
53
53
|
)
|
|
54
54
|
.then(async () => {
|
|
55
55
|
await adbSyncWriteRequest(locked, AdbSyncRequestId.Done, mtime);
|
|
@@ -59,7 +59,7 @@ async function pipeFileData(
|
|
|
59
59
|
await adbSyncReadResponse(
|
|
60
60
|
locked,
|
|
61
61
|
AdbSyncResponseId.Ok,
|
|
62
|
-
AdbSyncOkResponse
|
|
62
|
+
AdbSyncOkResponse,
|
|
63
63
|
).catch((e) => {
|
|
64
64
|
abortController.abort();
|
|
65
65
|
throw e;
|
|
@@ -142,7 +142,7 @@ export async function adbSyncPushV2({
|
|
|
142
142
|
id: AdbSyncRequestId.SendV2,
|
|
143
143
|
mode,
|
|
144
144
|
flags,
|
|
145
|
-
})
|
|
145
|
+
}),
|
|
146
146
|
);
|
|
147
147
|
|
|
148
148
|
await pipeFileData(locked, file, packetSize, mtime);
|
|
@@ -30,7 +30,7 @@ export interface AdbSyncWritable {
|
|
|
30
30
|
export async function adbSyncWriteRequest(
|
|
31
31
|
writable: AdbSyncWritable,
|
|
32
32
|
id: AdbSyncRequestId | string,
|
|
33
|
-
value: number | string | Uint8Array
|
|
33
|
+
value: number | string | Uint8Array,
|
|
34
34
|
): Promise<void> {
|
|
35
35
|
if (typeof value === "number") {
|
|
36
36
|
const buffer = AdbSyncNumberRequest.serialize({
|
|
@@ -42,12 +42,12 @@ export async function adbSyncWriteRequest(
|
|
|
42
42
|
// Let `writable` buffer writes
|
|
43
43
|
const buffer = encodeUtf8(value);
|
|
44
44
|
await writable.write(
|
|
45
|
-
AdbSyncNumberRequest.serialize({ id, arg: buffer.byteLength })
|
|
45
|
+
AdbSyncNumberRequest.serialize({ id, arg: buffer.byteLength }),
|
|
46
46
|
);
|
|
47
47
|
await writable.write(buffer);
|
|
48
48
|
} else {
|
|
49
49
|
await writable.write(
|
|
50
|
-
AdbSyncNumberRequest.serialize({ id, arg: value.byteLength })
|
|
50
|
+
AdbSyncNumberRequest.serialize({ id, arg: value.byteLength }),
|
|
51
51
|
);
|
|
52
52
|
await writable.write(value);
|
|
53
53
|
}
|
|
@@ -19,17 +19,19 @@ export enum AdbSyncResponseId {
|
|
|
19
19
|
Fail = "FAIL",
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export class AdbSyncError extends Error {}
|
|
23
|
+
|
|
22
24
|
export const AdbSyncFailResponse = new Struct({ littleEndian: true })
|
|
23
25
|
.uint32("messageLength")
|
|
24
26
|
.string("message", { lengthField: "messageLength" })
|
|
25
27
|
.postDeserialize((object) => {
|
|
26
|
-
throw new
|
|
28
|
+
throw new AdbSyncError(object.message);
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
export async function adbSyncReadResponse<T>(
|
|
30
32
|
stream: AsyncExactReadable,
|
|
31
33
|
id: AdbSyncResponseId,
|
|
32
|
-
type: StructLike<T
|
|
34
|
+
type: StructLike<T>,
|
|
33
35
|
): Promise<T> {
|
|
34
36
|
const actualId = decodeUtf8(await stream.readExactly(4));
|
|
35
37
|
switch (actualId) {
|
|
@@ -44,11 +46,11 @@ export async function adbSyncReadResponse<T>(
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export async function* adbSyncReadResponses<
|
|
47
|
-
T extends Struct<object, PropertyKey, object, any
|
|
49
|
+
T extends Struct<object, PropertyKey, object, any>,
|
|
48
50
|
>(
|
|
49
51
|
stream: AsyncExactReadable,
|
|
50
52
|
id: AdbSyncResponseId,
|
|
51
|
-
type: T
|
|
53
|
+
type: T,
|
|
52
54
|
): AsyncGenerator<StructValueType<T>, void, void> {
|
|
53
55
|
while (true) {
|
|
54
56
|
const actualId = decodeUtf8(await stream.readExactly(4));
|
|
@@ -68,7 +70,7 @@ export async function* adbSyncReadResponses<
|
|
|
68
70
|
break;
|
|
69
71
|
default:
|
|
70
72
|
throw new Error(
|
|
71
|
-
`Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${actualId}'
|
|
73
|
+
`Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${actualId}'`,
|
|
72
74
|
);
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -19,15 +19,15 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
|
|
|
19
19
|
readonly #writeLock = new AutoResetEvent();
|
|
20
20
|
readonly #combiner: BufferCombiner;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
get position() {
|
|
23
23
|
return this.#readable.position;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
constructor(
|
|
27
27
|
writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>,
|
|
28
28
|
readable: BufferedReadableStream,
|
|
29
29
|
bufferSize: number,
|
|
30
|
-
lock: AutoResetEvent
|
|
30
|
+
lock: AutoResetEvent,
|
|
31
31
|
) {
|
|
32
32
|
this.#writer = writer;
|
|
33
33
|
this.#readable = readable;
|
|
@@ -35,39 +35,39 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
|
|
|
35
35
|
this.#combiner = new BufferCombiner(bufferSize);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
async #writeInnerStream(buffer: Uint8Array) {
|
|
39
39
|
await ConsumableWritableStream.write(this.#writer, buffer);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
async flush() {
|
|
43
43
|
try {
|
|
44
44
|
await this.#writeLock.wait();
|
|
45
45
|
const buffer = this.#combiner.flush();
|
|
46
46
|
if (buffer) {
|
|
47
|
-
await this
|
|
47
|
+
await this.#writeInnerStream(buffer);
|
|
48
48
|
}
|
|
49
49
|
} finally {
|
|
50
50
|
this.#writeLock.notifyOne();
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
async write(data: Uint8Array) {
|
|
55
55
|
try {
|
|
56
56
|
await this.#writeLock.wait();
|
|
57
57
|
for (const buffer of this.#combiner.push(data)) {
|
|
58
|
-
await this
|
|
58
|
+
await this.#writeInnerStream(buffer);
|
|
59
59
|
}
|
|
60
60
|
} finally {
|
|
61
61
|
this.#writeLock.notifyOne();
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
async readExactly(length: number) {
|
|
66
66
|
await this.flush();
|
|
67
67
|
return await this.#readable.readExactly(length);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
release(): void {
|
|
71
71
|
this.#combiner.flush();
|
|
72
72
|
this.#socketLock.notifyOne();
|
|
73
73
|
}
|
|
@@ -78,22 +78,22 @@ export class AdbSyncSocket {
|
|
|
78
78
|
readonly #socket: AdbSocket;
|
|
79
79
|
readonly #locked: AdbSyncSocketLocked;
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
constructor(socket: AdbSocket, bufferSize: number) {
|
|
82
82
|
this.#socket = socket;
|
|
83
83
|
this.#locked = new AdbSyncSocketLocked(
|
|
84
84
|
socket.writable.getWriter(),
|
|
85
85
|
new BufferedReadableStream(socket.readable),
|
|
86
86
|
bufferSize,
|
|
87
|
-
this.#lock
|
|
87
|
+
this.#lock,
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
async lock() {
|
|
92
92
|
await this.#lock.wait();
|
|
93
93
|
return this.#locked;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
async close() {
|
|
97
97
|
await this.#socket.close();
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -103,7 +103,7 @@ export type AdbSyncStatResponse =
|
|
|
103
103
|
export async function adbSyncLstat(
|
|
104
104
|
socket: AdbSyncSocket,
|
|
105
105
|
path: string,
|
|
106
|
-
v2: boolean
|
|
106
|
+
v2: boolean,
|
|
107
107
|
): Promise<AdbSyncStat> {
|
|
108
108
|
const locked = await socket.lock();
|
|
109
109
|
try {
|
|
@@ -112,14 +112,14 @@ export async function adbSyncLstat(
|
|
|
112
112
|
return await adbSyncReadResponse(
|
|
113
113
|
locked,
|
|
114
114
|
AdbSyncResponseId.Lstat2,
|
|
115
|
-
AdbSyncStatResponse
|
|
115
|
+
AdbSyncStatResponse,
|
|
116
116
|
);
|
|
117
117
|
} else {
|
|
118
118
|
await adbSyncWriteRequest(locked, AdbSyncRequestId.Lstat, path);
|
|
119
119
|
const response = await adbSyncReadResponse(
|
|
120
120
|
locked,
|
|
121
121
|
AdbSyncResponseId.Lstat,
|
|
122
|
-
AdbSyncLstatResponse
|
|
122
|
+
AdbSyncLstatResponse,
|
|
123
123
|
);
|
|
124
124
|
return {
|
|
125
125
|
mode: response.mode,
|
|
@@ -141,7 +141,7 @@ export async function adbSyncLstat(
|
|
|
141
141
|
|
|
142
142
|
export async function adbSyncStat(
|
|
143
143
|
socket: AdbSyncSocket,
|
|
144
|
-
path: string
|
|
144
|
+
path: string,
|
|
145
145
|
): Promise<AdbSyncStatResponse> {
|
|
146
146
|
const locked = await socket.lock();
|
|
147
147
|
try {
|
|
@@ -149,7 +149,7 @@ export async function adbSyncStat(
|
|
|
149
149
|
return await adbSyncReadResponse(
|
|
150
150
|
locked,
|
|
151
151
|
AdbSyncResponseId.Stat,
|
|
152
|
-
AdbSyncStatResponse
|
|
152
|
+
AdbSyncStatResponse,
|
|
153
153
|
);
|
|
154
154
|
} finally {
|
|
155
155
|
locked.release();
|
|
@@ -48,27 +48,27 @@ export class AdbSync extends AutoDisposable {
|
|
|
48
48
|
readonly #supportsSendReceiveV2: boolean;
|
|
49
49
|
readonly #needPushMkdirWorkaround: boolean;
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
get supportsStat(): boolean {
|
|
52
52
|
return this.#supportsStat;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
get supportsListV2(): boolean {
|
|
56
56
|
return this.#supportsListV2;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
get fixedPushMkdir(): boolean {
|
|
60
60
|
return this.#fixedPushMkdir;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
get supportsSendReceiveV2(): boolean {
|
|
64
64
|
return this.#supportsSendReceiveV2;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
get needPushMkdirWorkaround(): boolean {
|
|
68
68
|
return this.#needPushMkdirWorkaround;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
constructor(adb: Adb, socket: AdbSocket) {
|
|
72
72
|
super();
|
|
73
73
|
|
|
74
74
|
this._adb = adb;
|
|
@@ -78,7 +78,7 @@ export class AdbSync extends AutoDisposable {
|
|
|
78
78
|
this.#supportsListV2 = adb.supportsFeature(AdbFeature.ListV2);
|
|
79
79
|
this.#fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir);
|
|
80
80
|
this.#supportsSendReceiveV2 = adb.supportsFeature(
|
|
81
|
-
AdbFeature.SendReceiveV2
|
|
81
|
+
AdbFeature.SendReceiveV2,
|
|
82
82
|
);
|
|
83
83
|
// https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
|
|
84
84
|
this.#needPushMkdirWorkaround =
|
|
@@ -86,19 +86,34 @@ export class AdbSync extends AutoDisposable {
|
|
|
86
86
|
!this.fixedPushMkdir;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Gets information of a file or folder.
|
|
91
|
+
*
|
|
92
|
+
* If `path` points to a symbolic link, the returned information is about the link itself (with `type` being `LinuxFileType.Link`).
|
|
93
|
+
*/
|
|
94
|
+
async lstat(path: string): Promise<AdbSyncStat> {
|
|
95
|
+
return await adbSyncLstat(this._socket, path, this.#supportsStat);
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Gets the information of a file or folder.
|
|
100
|
+
*
|
|
101
|
+
* If `path` points to a symbolic link, it will be resolved and the returned information is about the target (with `type` being `LinuxFileType.File` or `LinuxFileType.Directory`).
|
|
102
|
+
*/
|
|
103
|
+
async stat(path: string) {
|
|
104
|
+
if (!this.#supportsStat) {
|
|
95
105
|
throw new Error("Not supported");
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
return await adbSyncStat(this._socket, path);
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Checks if `path` is a directory, or a symbolic link to a directory.
|
|
113
|
+
*
|
|
114
|
+
* This uses `lstat` internally, thus works on all Android versions.
|
|
115
|
+
*/
|
|
116
|
+
async isDirectory(path: string): Promise<boolean> {
|
|
102
117
|
try {
|
|
103
118
|
await this.lstat(path + "/");
|
|
104
119
|
return true;
|
|
@@ -107,11 +122,11 @@ export class AdbSync extends AutoDisposable {
|
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
|
|
110
|
-
|
|
125
|
+
opendir(path: string): AsyncGenerator<AdbSyncEntry, void, void> {
|
|
111
126
|
return adbSyncOpenDir(this._socket, path, this.supportsListV2);
|
|
112
127
|
}
|
|
113
128
|
|
|
114
|
-
|
|
129
|
+
async readdir(path: string) {
|
|
115
130
|
const results: AdbSyncEntry[] = [];
|
|
116
131
|
for await (const entry of this.opendir(path)) {
|
|
117
132
|
results.push(entry);
|
|
@@ -125,7 +140,7 @@ export class AdbSync extends AutoDisposable {
|
|
|
125
140
|
* @param filename The full path of the file on device to read.
|
|
126
141
|
* @returns A `ReadableStream` that contains the file content.
|
|
127
142
|
*/
|
|
128
|
-
|
|
143
|
+
read(filename: string): ReadableStream<Uint8Array> {
|
|
129
144
|
return adbSyncPull(this._socket, filename);
|
|
130
145
|
}
|
|
131
146
|
|
|
@@ -134,7 +149,7 @@ export class AdbSync extends AutoDisposable {
|
|
|
134
149
|
*
|
|
135
150
|
* @param options The content and options of the file to write.
|
|
136
151
|
*/
|
|
137
|
-
|
|
152
|
+
async write(options: AdbSyncWriteOptions): Promise<void> {
|
|
138
153
|
if (this.needPushMkdirWorkaround) {
|
|
139
154
|
// It may fail if the path is already existed.
|
|
140
155
|
// Ignore the result.
|
|
@@ -153,7 +168,7 @@ export class AdbSync extends AutoDisposable {
|
|
|
153
168
|
});
|
|
154
169
|
}
|
|
155
170
|
|
|
156
|
-
|
|
171
|
+
override async dispose() {
|
|
157
172
|
super.dispose();
|
|
158
173
|
await this._socket.close();
|
|
159
174
|
}
|
package/src/commands/tcpip.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AdbCommandBase } from "./base.js";
|
|
2
2
|
|
|
3
3
|
export class AdbTcpIpCommand extends AdbCommandBase {
|
|
4
|
-
|
|
4
|
+
async setPort(port: number): Promise<string> {
|
|
5
5
|
if (port <= 0) {
|
|
6
6
|
throw new Error(`Invalid port ${port}`);
|
|
7
7
|
}
|
|
@@ -13,7 +13,7 @@ export class AdbTcpIpCommand extends AdbCommandBase {
|
|
|
13
13
|
return output;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
async disable(): Promise<string> {
|
|
17
17
|
const output = await this.adb.createSocketAndWait("usb:");
|
|
18
18
|
if (output !== "restarting in USB mode\n") {
|
|
19
19
|
throw new Error(output);
|
package/src/daemon/auth.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
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
|
+
import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
calculateBase64EncodedLength,
|
|
8
|
+
encodeBase64,
|
|
9
|
+
encodeUtf8,
|
|
10
|
+
} from "../utils/index.js";
|
|
6
11
|
|
|
7
12
|
import {
|
|
8
13
|
adbGeneratePublicKey,
|
|
@@ -12,18 +17,26 @@ import {
|
|
|
12
17
|
import type { AdbPacketData } from "./packet.js";
|
|
13
18
|
import { AdbCommand } from "./packet.js";
|
|
14
19
|
|
|
15
|
-
export
|
|
20
|
+
export interface AdbPrivateKey {
|
|
21
|
+
/**
|
|
22
|
+
* The private key in PKCS #8 format.
|
|
23
|
+
*/
|
|
24
|
+
buffer: Uint8Array;
|
|
25
|
+
name?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type AdbKeyIterable =
|
|
29
|
+
| Iterable<AdbPrivateKey>
|
|
30
|
+
| AsyncIterable<AdbPrivateKey>;
|
|
16
31
|
|
|
17
32
|
export interface AdbCredentialStore {
|
|
18
33
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* The returned `Uint8Array` is the private key in PKCS #8 format.
|
|
34
|
+
* Generates and stores a RSA private key with modulus length `2048` and public exponent `65537`.
|
|
22
35
|
*/
|
|
23
|
-
generateKey(): ValueOrPromise<
|
|
36
|
+
generateKey(): ValueOrPromise<AdbPrivateKey>;
|
|
24
37
|
|
|
25
38
|
/**
|
|
26
|
-
* Synchronously or asynchronously
|
|
39
|
+
* Synchronously or asynchronously iterates through all stored RSA private keys.
|
|
27
40
|
*
|
|
28
41
|
* Each call to `iterateKeys` must return a different iterator that iterate through all stored keys.
|
|
29
42
|
*/
|
|
@@ -50,13 +63,13 @@ export interface AdbAuthenticator {
|
|
|
50
63
|
*/
|
|
51
64
|
(
|
|
52
65
|
credentialStore: AdbCredentialStore,
|
|
53
|
-
getNextRequest: () => Promise<AdbPacketData
|
|
66
|
+
getNextRequest: () => Promise<AdbPacketData>,
|
|
54
67
|
): AsyncIterable<AdbPacketData>;
|
|
55
68
|
}
|
|
56
69
|
|
|
57
70
|
export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
|
|
58
71
|
credentialStore: AdbCredentialStore,
|
|
59
|
-
getNextRequest: () => Promise<AdbPacketData
|
|
72
|
+
getNextRequest: () => Promise<AdbPacketData>,
|
|
60
73
|
): AsyncIterable<AdbPacketData> {
|
|
61
74
|
for await (const key of credentialStore.iterateKeys()) {
|
|
62
75
|
const packet = await getNextRequest();
|
|
@@ -65,7 +78,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
|
|
|
65
78
|
return;
|
|
66
79
|
}
|
|
67
80
|
|
|
68
|
-
const signature = rsaSign(key, packet.payload);
|
|
81
|
+
const signature = rsaSign(key.buffer, packet.payload);
|
|
69
82
|
yield {
|
|
70
83
|
command: AdbCommand.Auth,
|
|
71
84
|
arg0: AdbAuthType.Signature,
|
|
@@ -77,7 +90,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
|
|
|
77
90
|
|
|
78
91
|
export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
79
92
|
credentialStore: AdbCredentialStore,
|
|
80
|
-
getNextRequest: () => Promise<AdbPacketData
|
|
93
|
+
getNextRequest: () => Promise<AdbPacketData>,
|
|
81
94
|
): AsyncIterable<AdbPacketData> {
|
|
82
95
|
const packet = await getNextRequest();
|
|
83
96
|
|
|
@@ -85,7 +98,7 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
|
85
98
|
return;
|
|
86
99
|
}
|
|
87
100
|
|
|
88
|
-
let privateKey:
|
|
101
|
+
let privateKey: AdbPrivateKey | undefined;
|
|
89
102
|
for await (const key of credentialStore.iterateKeys()) {
|
|
90
103
|
privateKey = key;
|
|
91
104
|
break;
|
|
@@ -99,13 +112,23 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
|
|
99
112
|
const [publicKeyBase64Length] =
|
|
100
113
|
calculateBase64EncodedLength(publicKeyLength);
|
|
101
114
|
|
|
115
|
+
const nameBuffer = privateKey.name?.length
|
|
116
|
+
? encodeUtf8(privateKey.name)
|
|
117
|
+
: EMPTY_UINT8_ARRAY;
|
|
102
118
|
const publicKeyBuffer = new Uint8Array(
|
|
103
|
-
publicKeyBase64Length +
|
|
119
|
+
publicKeyBase64Length +
|
|
120
|
+
(nameBuffer.length ? nameBuffer.length + 1 : 0) + // Space character + name
|
|
121
|
+
1, // Null character
|
|
104
122
|
);
|
|
105
123
|
|
|
106
|
-
adbGeneratePublicKey(privateKey, publicKeyBuffer);
|
|
124
|
+
adbGeneratePublicKey(privateKey.buffer, publicKeyBuffer);
|
|
107
125
|
encodeBase64(publicKeyBuffer.subarray(0, publicKeyLength), publicKeyBuffer);
|
|
108
126
|
|
|
127
|
+
if (nameBuffer.length) {
|
|
128
|
+
publicKeyBuffer[publicKeyBase64Length] = 0x20;
|
|
129
|
+
publicKeyBuffer.set(nameBuffer, publicKeyBase64Length + 1);
|
|
130
|
+
}
|
|
131
|
+
|
|
109
132
|
yield {
|
|
110
133
|
command: AdbCommand.Auth,
|
|
111
134
|
arg0: AdbAuthType.PublicKey,
|
|
@@ -120,34 +143,30 @@ export const ADB_DEFAULT_AUTHENTICATORS: AdbAuthenticator[] = [
|
|
|
120
143
|
];
|
|
121
144
|
|
|
122
145
|
export class AdbAuthenticationProcessor implements Disposable {
|
|
123
|
-
|
|
146
|
+
readonly authenticators: readonly AdbAuthenticator[];
|
|
124
147
|
|
|
125
|
-
|
|
148
|
+
readonly #credentialStore: AdbCredentialStore;
|
|
126
149
|
|
|
127
150
|
#pendingRequest = new PromiseResolver<AdbPacketData>();
|
|
128
151
|
#iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
|
|
129
152
|
|
|
130
|
-
|
|
153
|
+
constructor(
|
|
131
154
|
authenticators: readonly AdbAuthenticator[],
|
|
132
|
-
credentialStore: AdbCredentialStore
|
|
155
|
+
credentialStore: AdbCredentialStore,
|
|
133
156
|
) {
|
|
134
157
|
this.authenticators = authenticators;
|
|
135
|
-
this
|
|
158
|
+
this.#credentialStore = credentialStore;
|
|
136
159
|
}
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
#getNextRequest = (): Promise<AdbPacketData> => {
|
|
139
162
|
return this.#pendingRequest.promise;
|
|
140
163
|
};
|
|
141
164
|
|
|
142
|
-
|
|
143
|
-
AdbPacketData,
|
|
144
|
-
void,
|
|
145
|
-
void
|
|
146
|
-
> {
|
|
165
|
+
async *#invokeAuthenticator(): AsyncGenerator<AdbPacketData, void, void> {
|
|
147
166
|
for (const authenticator of this.authenticators) {
|
|
148
167
|
for await (const packet of authenticator(
|
|
149
|
-
this
|
|
150
|
-
this
|
|
168
|
+
this.#credentialStore,
|
|
169
|
+
this.#getNextRequest,
|
|
151
170
|
)) {
|
|
152
171
|
// If the authenticator yielded a response
|
|
153
172
|
// Prepare `nextRequest` for next authentication request
|
|
@@ -162,9 +181,9 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
|
|
165
|
-
|
|
184
|
+
async process(packet: AdbPacketData): Promise<AdbPacketData> {
|
|
166
185
|
if (!this.#iterator) {
|
|
167
|
-
this.#iterator = this
|
|
186
|
+
this.#iterator = this.#invokeAuthenticator();
|
|
168
187
|
}
|
|
169
188
|
|
|
170
189
|
this.#pendingRequest.resolve(packet);
|
|
@@ -177,7 +196,7 @@ export class AdbAuthenticationProcessor implements Disposable {
|
|
|
177
196
|
return result.value;
|
|
178
197
|
}
|
|
179
198
|
|
|
180
|
-
|
|
199
|
+
dispose() {
|
|
181
200
|
void this.#iterator?.return?.();
|
|
182
201
|
}
|
|
183
202
|
}
|
package/src/daemon/crypto.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
export function getBigUint(
|
|
14
14
|
dataView: DataView,
|
|
15
15
|
byteOffset: number,
|
|
16
|
-
length: number
|
|
16
|
+
length: number,
|
|
17
17
|
): bigint {
|
|
18
18
|
let result = 0n;
|
|
19
19
|
|
|
@@ -40,7 +40,7 @@ export function setBigUint(
|
|
|
40
40
|
dataView: DataView,
|
|
41
41
|
byteOffset: number,
|
|
42
42
|
value: bigint,
|
|
43
|
-
littleEndian?: boolean
|
|
43
|
+
littleEndian?: boolean,
|
|
44
44
|
) {
|
|
45
45
|
const start = byteOffset;
|
|
46
46
|
|
|
@@ -135,11 +135,11 @@ export function adbGetPublicKeySize() {
|
|
|
135
135
|
export function adbGeneratePublicKey(privateKey: Uint8Array): Uint8Array;
|
|
136
136
|
export function adbGeneratePublicKey(
|
|
137
137
|
privateKey: Uint8Array,
|
|
138
|
-
output: Uint8Array
|
|
138
|
+
output: Uint8Array,
|
|
139
139
|
): number;
|
|
140
140
|
export function adbGeneratePublicKey(
|
|
141
141
|
privateKey: Uint8Array,
|
|
142
|
-
output?: Uint8Array
|
|
142
|
+
output?: Uint8Array,
|
|
143
143
|
): Uint8Array | number {
|
|
144
144
|
// Android has its own public key generation algorithm
|
|
145
145
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
|
|
@@ -158,9 +158,6 @@ export function adbGeneratePublicKey(
|
|
|
158
158
|
// (All in little endian)
|
|
159
159
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38
|
|
160
160
|
|
|
161
|
-
// extract `n` from private key
|
|
162
|
-
const [n] = rsaParsePrivateKey(privateKey);
|
|
163
|
-
|
|
164
161
|
let outputType: "Uint8Array" | "number";
|
|
165
162
|
const outputLength = adbGetPublicKeySize();
|
|
166
163
|
if (!output) {
|
|
@@ -177,7 +174,7 @@ export function adbGeneratePublicKey(
|
|
|
177
174
|
const outputView = new DataView(
|
|
178
175
|
output.buffer,
|
|
179
176
|
output.byteOffset,
|
|
180
|
-
output.byteLength
|
|
177
|
+
output.byteLength,
|
|
181
178
|
);
|
|
182
179
|
let outputOffset = 0;
|
|
183
180
|
|
|
@@ -185,6 +182,9 @@ export function adbGeneratePublicKey(
|
|
|
185
182
|
outputView.setUint32(outputOffset, 2048 / 8 / 4, true);
|
|
186
183
|
outputOffset += 4;
|
|
187
184
|
|
|
185
|
+
// extract `n` from private key
|
|
186
|
+
const [n] = rsaParsePrivateKey(privateKey);
|
|
187
|
+
|
|
188
188
|
// Calculate `n0inv`
|
|
189
189
|
// Don't know why need to multiply by -1
|
|
190
190
|
// Didn't exist in Android codebase
|
|
@@ -221,7 +221,7 @@ export function adbGeneratePublicKey(
|
|
|
221
221
|
export function powMod(
|
|
222
222
|
base: bigint,
|
|
223
223
|
exponent: bigint,
|
|
224
|
-
modulus: bigint
|
|
224
|
+
modulus: bigint,
|
|
225
225
|
): bigint {
|
|
226
226
|
if (modulus === 1n) {
|
|
227
227
|
return 0n;
|