@yume-chan/adb 0.0.19 → 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 +57 -0
- package/CHANGELOG.md +26 -1
- package/README.md +222 -103
- package/esm/adb.d.ts +28 -45
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +34 -213
- 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/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 +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +2 -2
- package/esm/commands/power.js.map +1 -1
- 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/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 +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 +63 -57
- 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 +9 -9
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +2 -2
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +5 -3
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +7 -5
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +8 -14
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +35 -32
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.js +4 -4
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +26 -18
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +36 -25
- 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} +12 -11
- package/esm/daemon/auth.d.ts.map +1 -0
- package/esm/{auth.js → daemon/auth.js} +31 -22
- package/esm/daemon/auth.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} +24 -28
- 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 +12 -30
- package/esm/daemon/dispatcher.d.ts.map +1 -0
- package/esm/{socket → daemon}/dispatcher.js +80 -78
- 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} +2 -2
- package/esm/{packet.js.map → daemon/packet.js.map} +1 -1
- package/esm/{socket → daemon}/socket.d.ts +15 -19
- package/esm/daemon/socket.d.ts.map +1 -0
- package/esm/{socket → daemon}/socket.js +49 -42
- package/esm/daemon/socket.js.map +1 -0
- package/esm/daemon/transport.d.ts +53 -0
- package/esm/daemon/transport.d.ts.map +1 -0
- package/esm/daemon/transport.js +186 -0
- package/esm/daemon/transport.js.map +1 -0
- package/esm/features.js +1 -1
- package/esm/features.js.map +1 -1
- 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 +328 -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 +12 -12
- package/src/adb.ts +75 -274
- package/src/banner.ts +82 -0
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +31 -3
- package/src/commands/power.ts +10 -10
- package/src/commands/reverse.ts +77 -82
- package/src/commands/subprocess/command.ts +24 -25
- package/src/commands/subprocess/protocols/none.ts +29 -30
- package/src/commands/subprocess/protocols/shell.ts +87 -89
- package/src/commands/subprocess/protocols/types.ts +1 -2
- package/src/commands/sync/list.ts +7 -7
- package/src/commands/sync/pull.ts +4 -4
- package/src/commands/sync/push.ts +28 -16
- package/src/commands/sync/request.ts +4 -4
- package/src/commands/sync/response.ts +13 -11
- package/src/commands/sync/socket.ts +48 -46
- package/src/commands/sync/stat.ts +5 -5
- package/src/commands/sync/sync.ts +55 -43
- package/src/commands/tcpip.ts +6 -4
- package/src/{auth.ts → daemon/auth.ts} +62 -43
- package/src/{crypto.ts → daemon/crypto.ts} +33 -41
- package/src/{backend.ts → daemon/device.ts} +1 -1
- package/src/{socket → daemon}/dispatcher.ts +103 -109
- package/src/daemon/index.ts +7 -0
- package/src/{packet.ts → daemon/packet.ts} +6 -6
- package/src/{socket → daemon}/socket.ts +77 -67
- package/src/daemon/transport.ts +281 -0
- package/src/index.ts +3 -5
- package/src/server/client.ts +473 -0
- package/src/server/index.ts +2 -0
- package/src/server/transport.ts +77 -0
- package/src/utils/auto-reset-event.ts +16 -16
- package/src/utils/base64.ts +5 -5
- package/src/utils/conditional-variable.ts +14 -14
- 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/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
|
@@ -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.
|
|
@@ -18,15 +13,15 @@ const BigInt64 = BigInt(64);
|
|
|
18
13
|
export function getBigUint(
|
|
19
14
|
dataView: DataView,
|
|
20
15
|
byteOffset: number,
|
|
21
|
-
length: number
|
|
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
|
}
|
|
@@ -45,23 +40,23 @@ export function setBigUint(
|
|
|
45
40
|
dataView: DataView,
|
|
46
41
|
byteOffset: number,
|
|
47
42
|
value: bigint,
|
|
48
|
-
littleEndian?: boolean
|
|
43
|
+
littleEndian?: boolean,
|
|
49
44
|
) {
|
|
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,18 +128,18 @@ 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
|
-
output: Uint8Array
|
|
138
|
+
output: Uint8Array,
|
|
144
139
|
): number;
|
|
145
|
-
export function
|
|
140
|
+
export function adbGeneratePublicKey(
|
|
146
141
|
privateKey: Uint8Array,
|
|
147
|
-
output?: Uint8Array
|
|
142
|
+
output?: Uint8Array,
|
|
148
143
|
): Uint8Array | number {
|
|
149
144
|
// Android has its own public key generation algorithm
|
|
150
145
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
|
|
@@ -163,11 +158,8 @@ export function calculatePublicKey(
|
|
|
163
158
|
// (All in little endian)
|
|
164
159
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38
|
|
165
160
|
|
|
166
|
-
// extract `n` from private key
|
|
167
|
-
const [n] = parsePrivateKey(privateKey);
|
|
168
|
-
|
|
169
161
|
let outputType: "Uint8Array" | "number";
|
|
170
|
-
const outputLength =
|
|
162
|
+
const outputLength = adbGetPublicKeySize();
|
|
171
163
|
if (!output) {
|
|
172
164
|
output = new Uint8Array(outputLength);
|
|
173
165
|
outputType = "Uint8Array";
|
|
@@ -182,7 +174,7 @@ export function calculatePublicKey(
|
|
|
182
174
|
const outputView = new DataView(
|
|
183
175
|
output.buffer,
|
|
184
176
|
output.byteOffset,
|
|
185
|
-
output.byteLength
|
|
177
|
+
output.byteLength,
|
|
186
178
|
);
|
|
187
179
|
let outputOffset = 0;
|
|
188
180
|
|
|
@@ -190,13 +182,13 @@ export function calculatePublicKey(
|
|
|
190
182
|
outputView.setUint32(outputOffset, 2048 / 8 / 4, true);
|
|
191
183
|
outputOffset += 4;
|
|
192
184
|
|
|
185
|
+
// extract `n` from private key
|
|
186
|
+
const [n] = rsaParsePrivateKey(privateKey);
|
|
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
|
|
@@ -229,22 +221,22 @@ export function calculatePublicKey(
|
|
|
229
221
|
export function powMod(
|
|
230
222
|
base: bigint,
|
|
231
223
|
exponent: bigint,
|
|
232
|
-
modulus: bigint
|
|
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);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AsyncOperationManager, PromiseResolver } from "@yume-chan/async";
|
|
2
|
-
import type { RemoveEventListener } from "@yume-chan/event";
|
|
3
2
|
import type {
|
|
4
3
|
Consumable,
|
|
5
4
|
ReadableWritablePair,
|
|
@@ -10,15 +9,14 @@ import {
|
|
|
10
9
|
ConsumableWritableStream,
|
|
11
10
|
WritableStream,
|
|
12
11
|
} from "@yume-chan/stream-extra";
|
|
13
|
-
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
14
12
|
import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
|
|
15
13
|
|
|
16
|
-
import type {
|
|
17
|
-
import {
|
|
18
|
-
import { decodeUtf8, encodeUtf8 } from "../utils/index.js";
|
|
14
|
+
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
|
15
|
+
import { decodeUtf8, encodeUtf8, unreachable } from "../utils/index.js";
|
|
19
16
|
|
|
20
|
-
import type {
|
|
21
|
-
import {
|
|
17
|
+
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
|
18
|
+
import { AdbCommand, calculateChecksum } from "./packet.js";
|
|
19
|
+
import { AdbDaemonSocketController } from "./socket.js";
|
|
22
20
|
|
|
23
21
|
export interface AdbPacketDispatcherOptions {
|
|
24
22
|
calculateChecksum: boolean;
|
|
@@ -30,14 +28,10 @@ export interface AdbPacketDispatcherOptions {
|
|
|
30
28
|
*/
|
|
31
29
|
appendNullToServiceString: boolean;
|
|
32
30
|
maxPayloadSize: number;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
) => ValueOrPromise<boolean>;
|
|
38
|
-
|
|
39
|
-
export interface Closeable {
|
|
40
|
-
close(): ValueOrPromise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to preserve the connection open after the `AdbPacketDispatcher` is closed.
|
|
33
|
+
*/
|
|
34
|
+
preserveConnection?: boolean | undefined;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
/**
|
|
@@ -53,32 +47,31 @@ export interface Closeable {
|
|
|
53
47
|
export class AdbPacketDispatcher implements Closeable {
|
|
54
48
|
// ADB socket id starts from 1
|
|
55
49
|
// (0 means open failed)
|
|
56
|
-
|
|
50
|
+
readonly #initializers = new AsyncOperationManager(1);
|
|
57
51
|
/**
|
|
58
52
|
* Socket local ID to the socket controller.
|
|
59
53
|
*/
|
|
60
|
-
|
|
54
|
+
readonly #sockets = new Map<number, AdbDaemonSocketController>();
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
#writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>;
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
readonly options: AdbPacketDispatcherOptions;
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return this.
|
|
60
|
+
#closed = false;
|
|
61
|
+
#disconnected = new PromiseResolver<void>();
|
|
62
|
+
get disconnected() {
|
|
63
|
+
return this.#disconnected.promise;
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
private _abortController = new AbortController();
|
|
66
|
+
#incomingSocketHandlers = new Map<string, AdbIncomingSocketHandler>();
|
|
67
|
+
#readAbortController = new AbortController();
|
|
75
68
|
|
|
76
|
-
|
|
69
|
+
constructor(
|
|
77
70
|
connection: ReadableWritablePair<
|
|
78
71
|
AdbPacketData,
|
|
79
72
|
Consumable<AdbPacketInit>
|
|
80
73
|
>,
|
|
81
|
-
options: AdbPacketDispatcherOptions
|
|
74
|
+
options: AdbPacketDispatcherOptions,
|
|
82
75
|
) {
|
|
83
76
|
this.options = options;
|
|
84
77
|
|
|
@@ -88,28 +81,28 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
88
81
|
write: async (packet) => {
|
|
89
82
|
switch (packet.command) {
|
|
90
83
|
case AdbCommand.OK:
|
|
91
|
-
this
|
|
84
|
+
this.#handleOk(packet);
|
|
92
85
|
break;
|
|
93
86
|
case AdbCommand.Close:
|
|
94
|
-
await this
|
|
87
|
+
await this.#handleClose(packet);
|
|
95
88
|
break;
|
|
96
89
|
case AdbCommand.Write:
|
|
97
|
-
if (this
|
|
98
|
-
await this
|
|
90
|
+
if (this.#sockets.has(packet.arg1)) {
|
|
91
|
+
await this.#sockets
|
|
99
92
|
.get(packet.arg1)!
|
|
100
93
|
.enqueue(packet.payload);
|
|
101
94
|
await this.sendPacket(
|
|
102
95
|
AdbCommand.OK,
|
|
103
96
|
packet.arg1,
|
|
104
|
-
packet.arg0
|
|
97
|
+
packet.arg0,
|
|
105
98
|
);
|
|
106
99
|
break;
|
|
107
100
|
}
|
|
108
101
|
throw new Error(
|
|
109
|
-
`Unknown local socket id: ${packet.arg1}
|
|
102
|
+
`Unknown local socket id: ${packet.arg1}`,
|
|
110
103
|
);
|
|
111
104
|
case AdbCommand.Open:
|
|
112
|
-
await this
|
|
105
|
+
await this.#handleOpen(packet);
|
|
113
106
|
break;
|
|
114
107
|
default:
|
|
115
108
|
// Junk data may only appear in the authentication phase,
|
|
@@ -118,44 +111,39 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
118
111
|
// (although it's possible that Adb added new commands in the future)
|
|
119
112
|
throw new Error(
|
|
120
113
|
`Unknown command: ${packet.command.toString(
|
|
121
|
-
16
|
|
122
|
-
)}
|
|
114
|
+
16,
|
|
115
|
+
)}`,
|
|
123
116
|
);
|
|
124
117
|
}
|
|
125
118
|
},
|
|
126
119
|
}),
|
|
127
120
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// it's still possible to create another ADB connection.
|
|
132
|
-
// So don't close `readable` here.
|
|
133
|
-
preventCancel: true,
|
|
134
|
-
signal: this._abortController.signal,
|
|
135
|
-
}
|
|
121
|
+
preventCancel: options.preserveConnection ?? false,
|
|
122
|
+
signal: this.#readAbortController.signal,
|
|
123
|
+
},
|
|
136
124
|
)
|
|
137
125
|
.then(
|
|
138
126
|
() => {
|
|
139
|
-
this
|
|
127
|
+
this.#dispose();
|
|
140
128
|
},
|
|
141
129
|
(e) => {
|
|
142
|
-
if (!this
|
|
143
|
-
this.
|
|
130
|
+
if (!this.#closed) {
|
|
131
|
+
this.#disconnected.reject(e);
|
|
144
132
|
}
|
|
145
|
-
this
|
|
146
|
-
}
|
|
133
|
+
this.#dispose();
|
|
134
|
+
},
|
|
147
135
|
);
|
|
148
136
|
|
|
149
|
-
this
|
|
137
|
+
this.#writer = connection.writable.getWriter();
|
|
150
138
|
}
|
|
151
139
|
|
|
152
|
-
|
|
153
|
-
if (this
|
|
140
|
+
#handleOk(packet: AdbPacketData) {
|
|
141
|
+
if (this.#initializers.resolve(packet.arg1, packet.arg0)) {
|
|
154
142
|
// Device successfully created the socket
|
|
155
143
|
return;
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
const socket = this
|
|
146
|
+
const socket = this.#sockets.get(packet.arg1);
|
|
159
147
|
if (socket) {
|
|
160
148
|
// Device has received last `WRTE` to the socket
|
|
161
149
|
socket.ack();
|
|
@@ -167,13 +155,13 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
167
155
|
void this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0);
|
|
168
156
|
}
|
|
169
157
|
|
|
170
|
-
|
|
158
|
+
async #handleClose(packet: AdbPacketData) {
|
|
171
159
|
// If the socket is still pending
|
|
172
160
|
if (
|
|
173
161
|
packet.arg0 === 0 &&
|
|
174
|
-
this
|
|
162
|
+
this.#initializers.reject(
|
|
175
163
|
packet.arg1,
|
|
176
|
-
new Error("Socket open failed")
|
|
164
|
+
new Error("Socket open failed"),
|
|
177
165
|
)
|
|
178
166
|
) {
|
|
179
167
|
// Device failed to create the socket
|
|
@@ -192,18 +180,18 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
192
180
|
*/
|
|
193
181
|
|
|
194
182
|
// Ignore `arg0` and search for the socket
|
|
195
|
-
const socket = this
|
|
183
|
+
const socket = this.#sockets.get(packet.arg1);
|
|
196
184
|
if (socket) {
|
|
197
185
|
// The device want to close the socket
|
|
198
186
|
if (!socket.closed) {
|
|
199
187
|
await this.sendPacket(
|
|
200
188
|
AdbCommand.Close,
|
|
201
189
|
packet.arg1,
|
|
202
|
-
packet.arg0
|
|
190
|
+
packet.arg0,
|
|
203
191
|
);
|
|
204
192
|
}
|
|
205
193
|
await socket.dispose();
|
|
206
|
-
this
|
|
194
|
+
this.#sockets.delete(packet.arg1);
|
|
207
195
|
return;
|
|
208
196
|
}
|
|
209
197
|
|
|
@@ -212,77 +200,80 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
212
200
|
// the device may also respond with two `CLSE` packets.
|
|
213
201
|
}
|
|
214
202
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this._incomingSocketHandlers.delete(handler);
|
|
226
|
-
};
|
|
227
|
-
remove.dispose = remove;
|
|
228
|
-
return remove;
|
|
203
|
+
addReverseTunnel(service: string, handler: AdbIncomingSocketHandler) {
|
|
204
|
+
this.#incomingSocketHandlers.set(service, handler);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
removeReverseTunnel(address: string) {
|
|
208
|
+
this.#incomingSocketHandlers.delete(address);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
clearReverseTunnels() {
|
|
212
|
+
this.#incomingSocketHandlers.clear();
|
|
229
213
|
}
|
|
230
214
|
|
|
231
|
-
|
|
215
|
+
async #handleOpen(packet: AdbPacketData) {
|
|
232
216
|
// `AsyncOperationManager` doesn't support skipping IDs
|
|
233
217
|
// Use `add` + `resolve` to simulate this behavior
|
|
234
|
-
const [localId] = this
|
|
235
|
-
this
|
|
218
|
+
const [localId] = this.#initializers.add<number>();
|
|
219
|
+
this.#initializers.resolve(localId, undefined);
|
|
236
220
|
|
|
237
221
|
const remoteId = packet.arg0;
|
|
238
|
-
|
|
222
|
+
let service = decodeUtf8(packet.payload);
|
|
223
|
+
if (service.endsWith("\0")) {
|
|
224
|
+
service = service.substring(0, service.length - 1);
|
|
225
|
+
}
|
|
239
226
|
|
|
240
|
-
const
|
|
227
|
+
const handler = this.#incomingSocketHandlers.get(service);
|
|
228
|
+
if (!handler) {
|
|
229
|
+
await this.sendPacket(AdbCommand.Close, 0, remoteId);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const controller = new AdbDaemonSocketController({
|
|
241
234
|
dispatcher: this,
|
|
242
235
|
localId,
|
|
243
236
|
remoteId,
|
|
244
237
|
localCreated: false,
|
|
245
|
-
|
|
238
|
+
service,
|
|
246
239
|
});
|
|
247
240
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
241
|
+
try {
|
|
242
|
+
await handler(controller.socket);
|
|
243
|
+
this.#sockets.set(localId, controller);
|
|
244
|
+
await this.sendPacket(AdbCommand.OK, localId, remoteId);
|
|
245
|
+
} catch (e) {
|
|
246
|
+
await this.sendPacket(AdbCommand.Close, 0, remoteId);
|
|
254
247
|
}
|
|
255
|
-
|
|
256
|
-
await this.sendPacket(AdbCommand.Close, 0, remoteId);
|
|
257
248
|
}
|
|
258
249
|
|
|
259
|
-
|
|
250
|
+
async createSocket(service: string): Promise<AdbSocket> {
|
|
260
251
|
if (this.options.appendNullToServiceString) {
|
|
261
|
-
|
|
252
|
+
service += "\0";
|
|
262
253
|
}
|
|
263
254
|
|
|
264
|
-
const [localId, initializer] = this
|
|
265
|
-
await this.sendPacket(AdbCommand.Open, localId, 0,
|
|
255
|
+
const [localId, initializer] = this.#initializers.add<number>();
|
|
256
|
+
await this.sendPacket(AdbCommand.Open, localId, 0, service);
|
|
266
257
|
|
|
267
258
|
// Fulfilled by `handleOk`
|
|
268
259
|
const remoteId = await initializer;
|
|
269
|
-
const controller = new
|
|
260
|
+
const controller = new AdbDaemonSocketController({
|
|
270
261
|
dispatcher: this,
|
|
271
262
|
localId,
|
|
272
263
|
remoteId,
|
|
273
264
|
localCreated: true,
|
|
274
|
-
|
|
265
|
+
service,
|
|
275
266
|
});
|
|
276
|
-
this
|
|
267
|
+
this.#sockets.set(localId, controller);
|
|
277
268
|
|
|
278
269
|
return controller.socket;
|
|
279
270
|
}
|
|
280
271
|
|
|
281
|
-
|
|
272
|
+
async sendPacket(
|
|
282
273
|
command: AdbCommand,
|
|
283
274
|
arg0: number,
|
|
284
275
|
arg1: number,
|
|
285
|
-
payload: string | Uint8Array = EMPTY_UINT8_ARRAY
|
|
276
|
+
payload: string | Uint8Array = EMPTY_UINT8_ARRAY,
|
|
286
277
|
): Promise<void> {
|
|
287
278
|
if (typeof payload === "string") {
|
|
288
279
|
payload = encodeUtf8(payload);
|
|
@@ -292,7 +283,7 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
292
283
|
throw new Error("payload too large");
|
|
293
284
|
}
|
|
294
285
|
|
|
295
|
-
await ConsumableWritableStream.write(this
|
|
286
|
+
await ConsumableWritableStream.write(this.#writer, {
|
|
296
287
|
command,
|
|
297
288
|
arg0,
|
|
298
289
|
arg1,
|
|
@@ -304,29 +295,32 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
304
295
|
});
|
|
305
296
|
}
|
|
306
297
|
|
|
307
|
-
|
|
298
|
+
async close() {
|
|
308
299
|
// Send `CLSE` packets for all sockets
|
|
309
300
|
await Promise.all(
|
|
310
|
-
Array.from(this
|
|
301
|
+
Array.from(this.#sockets.values(), (socket) => socket.close()),
|
|
311
302
|
);
|
|
312
303
|
|
|
313
304
|
// Stop receiving
|
|
314
305
|
// It's possible that we haven't received all `CLSE` confirm packets,
|
|
315
306
|
// but it doesn't matter, the next connection can cope with them.
|
|
316
|
-
this
|
|
317
|
-
|
|
318
|
-
this.
|
|
307
|
+
this.#closed = true;
|
|
308
|
+
|
|
309
|
+
this.#readAbortController.abort();
|
|
310
|
+
if (this.options.preserveConnection ?? false) {
|
|
311
|
+
this.#writer.releaseLock();
|
|
312
|
+
} else {
|
|
313
|
+
await this.#writer.close();
|
|
314
|
+
}
|
|
319
315
|
|
|
320
316
|
// `pipe().then()` will call `dispose`
|
|
321
317
|
}
|
|
322
318
|
|
|
323
|
-
|
|
324
|
-
for (const socket of this
|
|
325
|
-
socket.dispose().catch(
|
|
326
|
-
void e;
|
|
327
|
-
});
|
|
319
|
+
#dispose() {
|
|
320
|
+
for (const socket of this.#sockets.values()) {
|
|
321
|
+
socket.dispose().catch(unreachable);
|
|
328
322
|
}
|
|
329
323
|
|
|
330
|
-
this.
|
|
324
|
+
this.#disconnected.resolve();
|
|
331
325
|
}
|
|
332
326
|
}
|
|
@@ -23,7 +23,7 @@ export type AdbPacketHeader = (typeof AdbPacketHeader)["TDeserializeResult"];
|
|
|
23
23
|
type AdbPacketHeaderInit = (typeof AdbPacketHeader)["TInit"];
|
|
24
24
|
|
|
25
25
|
export const AdbPacket = new Struct({ littleEndian: true })
|
|
26
|
-
.
|
|
26
|
+
.concat(AdbPacketHeader)
|
|
27
27
|
.uint8Array("payload", { lengthField: "payloadLength" });
|
|
28
28
|
|
|
29
29
|
export type AdbPacket = (typeof AdbPacket)["TDeserializeResult"];
|
|
@@ -31,11 +31,11 @@ export type AdbPacket = (typeof AdbPacket)["TDeserializeResult"];
|
|
|
31
31
|
/**
|
|
32
32
|
* `AdbPacketData` contains all the useful fields of `AdbPacket`.
|
|
33
33
|
*
|
|
34
|
-
* `
|
|
35
|
-
*
|
|
34
|
+
* `AdvDaemonConnection#connect` will return a `ReadableStream<AdbPacketData>`,
|
|
35
|
+
* allow each connection to encode `AdbPacket` in different methods.
|
|
36
36
|
*
|
|
37
|
-
* `
|
|
38
|
-
* however, `
|
|
37
|
+
* `AdbDaemonConnection#connect` will return a `WritableStream<AdbPacketInit>`,
|
|
38
|
+
* however, `AdbDaemonTransport` will transform `AdbPacketData` to `AdbPacketInit` for you,
|
|
39
39
|
* so `AdbSocket#writable#write` only needs `AdbPacketData`.
|
|
40
40
|
*/
|
|
41
41
|
export type AdbPacketData = Omit<
|
|
@@ -53,7 +53,7 @@ export class AdbPacketSerializeStream extends ConsumableTransformStream<
|
|
|
53
53
|
AdbPacketInit,
|
|
54
54
|
Uint8Array
|
|
55
55
|
> {
|
|
56
|
-
|
|
56
|
+
constructor() {
|
|
57
57
|
const headerBuffer = new Uint8Array(AdbPacketHeader.size);
|
|
58
58
|
super({
|
|
59
59
|
transform: async (chunk, controller) => {
|