@yume-chan/adb 0.0.16 → 0.0.18
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 +18 -0
- package/CHANGELOG.md +13 -1
- package/LICENSE +1 -1
- package/esm/adb.d.ts +13 -8
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +63 -38
- package/esm/adb.js.map +1 -1
- package/esm/auth.d.ts +4 -4
- package/esm/auth.d.ts.map +1 -1
- package/esm/auth.js +6 -6
- package/esm/auth.js.map +1 -1
- package/esm/backend.d.ts +3 -3
- package/esm/backend.d.ts.map +1 -1
- package/esm/commands/base.d.ts +2 -2
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js +1 -1
- package/esm/commands/base.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +22 -6
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +33 -45
- package/esm/commands/framebuffer.js.map +1 -1
- package/esm/commands/install.d.ts +2 -2
- package/esm/commands/install.d.ts.map +1 -1
- package/esm/commands/install.js +12 -5
- package/esm/commands/install.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +13 -9
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts +5 -5
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +20 -18
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.d.ts +52 -0
- package/esm/commands/subprocess/command.d.ts.map +1 -0
- package/esm/commands/subprocess/command.js +81 -0
- package/esm/commands/subprocess/command.js.map +1 -0
- package/esm/commands/subprocess/index.d.ts +1 -53
- package/esm/commands/subprocess/index.d.ts.map +1 -1
- package/esm/commands/subprocess/index.js +1 -85
- package/esm/commands/subprocess/index.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +5 -5
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +5 -3
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +4 -4
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +28 -20
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +7 -7
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/sync/index.d.ts +1 -1
- package/esm/commands/sync/index.d.ts.map +1 -1
- package/esm/commands/sync/index.js +1 -1
- package/esm/commands/sync/index.js.map +1 -1
- package/esm/commands/sync/list.d.ts +7 -7
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +34 -50
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts +5 -4
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +21 -22
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.d.ts +4 -4
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +7 -11
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.d.ts +4 -4
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +6 -6
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +5 -10
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +35 -26
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/stat.d.ts +7 -7
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +48 -55
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +7 -7
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +25 -22
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/crypto.d.ts.map +1 -1
- package/esm/crypto.js +26 -16
- package/esm/crypto.js.map +1 -1
- package/esm/index.d.ts +9 -10
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +9 -10
- package/esm/index.js.map +1 -1
- package/esm/packet.d.ts +8 -8
- package/esm/packet.d.ts.map +1 -1
- package/esm/packet.js +14 -12
- package/esm/packet.js.map +1 -1
- package/esm/socket/dispatcher.d.ts +11 -6
- package/esm/socket/dispatcher.d.ts.map +1 -1
- package/esm/socket/dispatcher.js +28 -20
- package/esm/socket/dispatcher.js.map +1 -1
- package/esm/socket/index.d.ts +1 -1
- package/esm/socket/index.d.ts.map +1 -1
- package/esm/socket/index.js +1 -1
- package/esm/socket/index.js.map +1 -1
- package/esm/socket/socket.d.ts +5 -5
- package/esm/socket/socket.d.ts.map +1 -1
- package/esm/socket/socket.js +37 -17
- package/esm/socket/socket.js.map +1 -1
- package/esm/utils/auto-reset-event.d.ts +1 -1
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js +2 -2
- package/esm/utils/base64.d.ts +23 -0
- package/esm/utils/base64.d.ts.map +1 -1
- package/esm/utils/base64.js +36 -29
- package/esm/utils/base64.js.map +1 -1
- package/package.json +19 -17
- package/src/adb.ts +140 -86
- package/src/auth.ts +27 -18
- package/src/backend.ts +7 -4
- package/src/commands/base.ts +3 -2
- package/src/commands/framebuffer.ts +48 -47
- package/src/commands/install.ts +20 -10
- package/src/commands/power.ts +14 -10
- package/src/commands/reverse.ts +53 -33
- package/src/commands/subprocess/command.ts +134 -0
- package/src/commands/subprocess/index.ts +1 -137
- package/src/commands/subprocess/protocols/none.ts +10 -6
- package/src/commands/subprocess/protocols/shell.ts +72 -39
- package/src/commands/subprocess/protocols/types.ts +12 -8
- package/src/commands/sync/index.ts +1 -1
- package/src/commands/sync/list.ts +63 -68
- package/src/commands/sync/pull.ts +53 -37
- package/src/commands/sync/push.ts +26 -15
- package/src/commands/sync/request.ts +20 -21
- package/src/commands/sync/response.ts +63 -53
- package/src/commands/sync/stat.ts +91 -90
- package/src/commands/sync/sync.ts +56 -34
- package/src/crypto.ts +61 -33
- package/src/features.ts +1 -1
- package/src/index.ts +9 -11
- package/src/packet.ts +40 -30
- package/src/socket/dispatcher.ts +119 -77
- package/src/socket/index.ts +1 -1
- package/src/socket/socket.ts +68 -26
- package/src/utils/auto-reset-event.ts +3 -3
- package/src/utils/base64.ts +75 -42
- package/tsconfig.build.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/stream/buffered.d.ts +0 -39
- package/esm/stream/buffered.d.ts.map +0 -1
- package/esm/stream/buffered.js +0 -143
- package/esm/stream/buffered.js.map +0 -1
- package/esm/stream/detect.d.ts +0 -2
- package/esm/stream/detect.d.ts.map +0 -1
- package/esm/stream/detect.js +0 -6
- package/esm/stream/detect.js.map +0 -1
- package/esm/stream/detect.native.d.ts +0 -240
- package/esm/stream/detect.native.d.ts.map +0 -1
- package/esm/stream/detect.native.js +0 -60
- package/esm/stream/detect.native.js.map +0 -1
- package/esm/stream/detect.polyfill.d.ts +0 -18
- package/esm/stream/detect.polyfill.d.ts.map +0 -1
- package/esm/stream/detect.polyfill.js +0 -4
- package/esm/stream/detect.polyfill.js.map +0 -1
- package/esm/stream/index.d.ts +0 -4
- package/esm/stream/index.d.ts.map +0 -1
- package/esm/stream/index.js +0 -4
- package/esm/stream/index.js.map +0 -1
- package/esm/stream/transform.d.ts +0 -124
- package/esm/stream/transform.d.ts.map +0 -1
- package/esm/stream/transform.js +0 -379
- package/esm/stream/transform.js.map +0 -1
- package/src/stream/buffered.ts +0 -168
- package/src/stream/detect.native.ts +0 -362
- package/src/stream/detect.polyfill.ts +0 -22
- package/src/stream/detect.ts +0 -7
- package/src/stream/index.ts +0 -3
- package/src/stream/transform.ts +0 -474
- package/tsconfig.test.json +0 -9
- package/web-streams-polyfill-4.0.0-beta.2.tgz +0 -0
package/src/crypto.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getBigUint64,
|
|
3
|
+
setBigUint64,
|
|
4
|
+
} from "@yume-chan/dataview-bigint-polyfill/esm/fallback.js";
|
|
2
5
|
|
|
3
6
|
const BigInt0 = BigInt(0);
|
|
4
7
|
const BigInt1 = BigInt(1);
|
|
@@ -12,7 +15,11 @@ const BigInt64 = BigInt(64);
|
|
|
12
15
|
* Only supports Big-Endian, because that's what ADB uses.
|
|
13
16
|
* @param byteOffset The place in the buffer at which the value should be retrieved.
|
|
14
17
|
*/
|
|
15
|
-
export function getBigUint(
|
|
18
|
+
export function getBigUint(
|
|
19
|
+
dataView: DataView,
|
|
20
|
+
byteOffset: number,
|
|
21
|
+
length: number
|
|
22
|
+
): bigint {
|
|
16
23
|
let result = BigInt0;
|
|
17
24
|
|
|
18
25
|
// Currently `length` must be a multiplication of 8
|
|
@@ -34,7 +41,12 @@ export function getBigUint(dataView: DataView, byteOffset: number, length: numbe
|
|
|
34
41
|
* @param littleEndian If `false` or `undefined`, a big-endian value should be written,
|
|
35
42
|
* otherwise a little-endian value should be written.
|
|
36
43
|
*/
|
|
37
|
-
export function setBigUint(
|
|
44
|
+
export function setBigUint(
|
|
45
|
+
dataView: DataView,
|
|
46
|
+
byteOffset: number,
|
|
47
|
+
value: bigint,
|
|
48
|
+
littleEndian?: boolean
|
|
49
|
+
) {
|
|
38
50
|
const start = byteOffset;
|
|
39
51
|
|
|
40
52
|
if (littleEndian) {
|
|
@@ -96,9 +108,9 @@ export function parsePrivateKey(key: Uint8Array): [n: bigint, d: bigint] {
|
|
|
96
108
|
|
|
97
109
|
// Taken from https://stackoverflow.com/a/51562038
|
|
98
110
|
// I can't understand, but it does work
|
|
99
|
-
// Only used with numbers
|
|
111
|
+
// Only used with numbers smaller than 2^32 so doesn't need BigInt
|
|
100
112
|
export function modInverse(a: number, m: number) {
|
|
101
|
-
a = (a % m + m) % m;
|
|
113
|
+
a = ((a % m) + m) % m;
|
|
102
114
|
if (!a || m < 2) {
|
|
103
115
|
return NaN; // invalid input
|
|
104
116
|
}
|
|
@@ -115,26 +127,24 @@ export function modInverse(a: number, m: number) {
|
|
|
115
127
|
// find the inverse
|
|
116
128
|
let x = 1;
|
|
117
129
|
let y = 0;
|
|
118
|
-
for (let i = s.length - 2; i >= 0;
|
|
130
|
+
for (let i = s.length - 2; i >= 0; i -= 1) {
|
|
119
131
|
[x, y] = [y, x - y * Math.floor(s[i]!.a / s[i]!.b)];
|
|
120
132
|
}
|
|
121
|
-
return (y % m + m) % m;
|
|
133
|
+
return ((y % m) + m) % m;
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
export function calculatePublicKeyLength() {
|
|
125
137
|
return 4 + 4 + 2048 / 8 + 2048 / 8 + 4;
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
export function calculatePublicKey(
|
|
129
|
-
privateKey: Uint8Array
|
|
130
|
-
): Uint8Array;
|
|
140
|
+
export function calculatePublicKey(privateKey: Uint8Array): Uint8Array;
|
|
131
141
|
export function calculatePublicKey(
|
|
132
142
|
privateKey: Uint8Array,
|
|
133
|
-
output: Uint8Array
|
|
143
|
+
output: Uint8Array
|
|
134
144
|
): number;
|
|
135
145
|
export function calculatePublicKey(
|
|
136
146
|
privateKey: Uint8Array,
|
|
137
|
-
output?: Uint8Array
|
|
147
|
+
output?: Uint8Array
|
|
138
148
|
): Uint8Array | number {
|
|
139
149
|
// Android has its own public key generation algorithm
|
|
140
150
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
|
|
@@ -156,20 +166,24 @@ export function calculatePublicKey(
|
|
|
156
166
|
// extract `n` from private key
|
|
157
167
|
const [n] = parsePrivateKey(privateKey);
|
|
158
168
|
|
|
159
|
-
let outputType:
|
|
169
|
+
let outputType: "Uint8Array" | "number";
|
|
160
170
|
const outputLength = calculatePublicKeyLength();
|
|
161
171
|
if (!output) {
|
|
162
172
|
output = new Uint8Array(outputLength);
|
|
163
|
-
outputType =
|
|
173
|
+
outputType = "Uint8Array";
|
|
164
174
|
} else {
|
|
165
175
|
if (output.byteLength < outputLength) {
|
|
166
|
-
throw new Error(
|
|
176
|
+
throw new Error("output buffer is too small");
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
outputType =
|
|
179
|
+
outputType = "number";
|
|
170
180
|
}
|
|
171
181
|
|
|
172
|
-
const outputView = new DataView(
|
|
182
|
+
const outputView = new DataView(
|
|
183
|
+
output.buffer,
|
|
184
|
+
output.byteOffset,
|
|
185
|
+
output.byteLength
|
|
186
|
+
);
|
|
173
187
|
let outputOffset = 0;
|
|
174
188
|
|
|
175
189
|
// modulusLengthInWords
|
|
@@ -179,7 +193,10 @@ export function calculatePublicKey(
|
|
|
179
193
|
// Calculate `n0inv`
|
|
180
194
|
// Don't know why need to multiple -1
|
|
181
195
|
// Didn't exist in Android codebase
|
|
182
|
-
const n0inv = modInverse(
|
|
196
|
+
const n0inv = modInverse(
|
|
197
|
+
Number(BigInt.asUintN(32, n) * BigInt(-1)),
|
|
198
|
+
2 ** 32
|
|
199
|
+
);
|
|
183
200
|
outputView.setUint32(outputOffset, n0inv, true);
|
|
184
201
|
outputOffset += 4;
|
|
185
202
|
|
|
@@ -188,14 +205,14 @@ export function calculatePublicKey(
|
|
|
188
205
|
outputOffset += 256;
|
|
189
206
|
|
|
190
207
|
// Calculate rr = (2^(rsa_size)) ^ 2 mod n
|
|
191
|
-
|
|
208
|
+
const rr = BigInt(2) ** BigInt(4096) % n;
|
|
192
209
|
outputOffset += setBigUint(outputView, outputOffset, rr, true);
|
|
193
210
|
|
|
194
211
|
// exponent
|
|
195
212
|
outputView.setUint32(outputOffset, 65537, true);
|
|
196
213
|
outputOffset += 4;
|
|
197
214
|
|
|
198
|
-
if (outputType ===
|
|
215
|
+
if (outputType === "Uint8Array") {
|
|
199
216
|
return output;
|
|
200
217
|
} else {
|
|
201
218
|
return outputLength;
|
|
@@ -209,7 +226,11 @@ export function calculatePublicKey(
|
|
|
209
226
|
*
|
|
210
227
|
* See https://en.wikipedia.org/wiki/Modular_exponentiation#Implementation_in_Lua
|
|
211
228
|
*/
|
|
212
|
-
export function powMod(
|
|
229
|
+
export function powMod(
|
|
230
|
+
base: bigint,
|
|
231
|
+
exponent: bigint,
|
|
232
|
+
modulus: bigint
|
|
233
|
+
): bigint {
|
|
213
234
|
if (modulus === BigInt1) {
|
|
214
235
|
return BigInt0;
|
|
215
236
|
}
|
|
@@ -219,7 +240,7 @@ export function powMod(base: bigint, exponent: bigint, modulus: bigint): bigint
|
|
|
219
240
|
|
|
220
241
|
while (exponent > BigInt0) {
|
|
221
242
|
if (BigInt.asUintN(1, exponent) === BigInt1) {
|
|
222
|
-
r = r * base % modulus;
|
|
243
|
+
r = (r * base) % modulus;
|
|
223
244
|
}
|
|
224
245
|
|
|
225
246
|
exponent >>= BigInt1;
|
|
@@ -238,12 +259,22 @@ export const ASN1_OID = 0x06;
|
|
|
238
259
|
|
|
239
260
|
// PKCS#1 SHA-1 hash digest info
|
|
240
261
|
export const SHA1_DIGEST_INFO = new Uint8Array([
|
|
241
|
-
ASN1_SEQUENCE,
|
|
242
|
-
|
|
262
|
+
ASN1_SEQUENCE,
|
|
263
|
+
0x0d + SHA1_DIGEST_LENGTH,
|
|
264
|
+
ASN1_SEQUENCE,
|
|
265
|
+
0x09,
|
|
243
266
|
// SHA-1 (1 3 14 3 2 26)
|
|
244
|
-
ASN1_OID,
|
|
245
|
-
|
|
246
|
-
|
|
267
|
+
ASN1_OID,
|
|
268
|
+
0x05,
|
|
269
|
+
1 * 40 + 3,
|
|
270
|
+
14,
|
|
271
|
+
3,
|
|
272
|
+
2,
|
|
273
|
+
26,
|
|
274
|
+
ASN1_NULL,
|
|
275
|
+
0x00,
|
|
276
|
+
ASN1_OCTET_STRING,
|
|
277
|
+
SHA1_DIGEST_LENGTH,
|
|
247
278
|
]);
|
|
248
279
|
|
|
249
280
|
// SubtleCrypto.sign() will hash the given data and sign the hash
|
|
@@ -265,7 +296,8 @@ export function sign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
|
|
|
265
296
|
padded[index] = 1;
|
|
266
297
|
index += 1;
|
|
267
298
|
|
|
268
|
-
const fillLength =
|
|
299
|
+
const fillLength =
|
|
300
|
+
padded.length - SHA1_DIGEST_INFO.length - data.length - 1;
|
|
269
301
|
while (index < fillLength) {
|
|
270
302
|
padded[index] = 0xff;
|
|
271
303
|
index += 1;
|
|
@@ -282,11 +314,7 @@ export function sign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
|
|
|
282
314
|
// Encryption
|
|
283
315
|
// signature = padded ** d % n
|
|
284
316
|
const view = new DataView(padded.buffer);
|
|
285
|
-
const signature = powMod(
|
|
286
|
-
getBigUint(view, 0, view.byteLength),
|
|
287
|
-
d,
|
|
288
|
-
n
|
|
289
|
-
);
|
|
317
|
+
const signature = powMod(getBigUint(view, 0, view.byteLength), d, n);
|
|
290
318
|
|
|
291
319
|
// `padded` is not used anymore,
|
|
292
320
|
// re-use the buffer to store the result
|
package/src/features.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The order follows
|
|
2
2
|
// https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
|
|
3
3
|
export enum AdbFeatures {
|
|
4
|
-
ShellV2 =
|
|
4
|
+
ShellV2 = 'shell_v2',
|
|
5
5
|
Cmd = 'cmd',
|
|
6
6
|
StatV2 = 'stat_v2',
|
|
7
7
|
ListV2 = 'ls_v2',
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from './stream/index.js';
|
|
11
|
-
export * from './utils/index.js';
|
|
1
|
+
export * from "./adb.js";
|
|
2
|
+
export * from "./auth.js";
|
|
3
|
+
export * from "./backend.js";
|
|
4
|
+
export * from "./commands/index.js";
|
|
5
|
+
export * from "./crypto.js";
|
|
6
|
+
export * from "./features.js";
|
|
7
|
+
export * from "./packet.js";
|
|
8
|
+
export * from "./socket/index.js";
|
|
9
|
+
export * from "./utils/index.js";
|
package/src/packet.ts
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { TransformStream } from "@yume-chan/stream-extra";
|
|
2
|
+
import Struct from "@yume-chan/struct";
|
|
3
3
|
|
|
4
4
|
export enum AdbCommand {
|
|
5
|
-
Auth = 0x48545541,
|
|
6
|
-
Close = 0x45534c43,
|
|
5
|
+
Auth = 0x48545541, // 'AUTH'
|
|
6
|
+
Close = 0x45534c43, // 'CLSE'
|
|
7
7
|
Connect = 0x4e584e43, // 'CNXN'
|
|
8
|
-
OK = 0x59414b4f,
|
|
9
|
-
Open = 0x4e45504f,
|
|
10
|
-
Write = 0x45545257,
|
|
8
|
+
OK = 0x59414b4f, // 'OKAY'
|
|
9
|
+
Open = 0x4e45504f, // 'OPEN'
|
|
10
|
+
Write = 0x45545257, // 'WRTE'
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export const AdbPacketHeader =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.int32('magic');
|
|
13
|
+
export const AdbPacketHeader = new Struct({ littleEndian: true })
|
|
14
|
+
.uint32("command")
|
|
15
|
+
.uint32("arg0")
|
|
16
|
+
.uint32("arg1")
|
|
17
|
+
.uint32("payloadLength")
|
|
18
|
+
.uint32("checksum")
|
|
19
|
+
.int32("magic");
|
|
21
20
|
|
|
22
|
-
export type AdbPacketHeader = typeof AdbPacketHeader[
|
|
21
|
+
export type AdbPacketHeader = typeof AdbPacketHeader["TDeserializeResult"];
|
|
23
22
|
|
|
24
|
-
type AdbPacketHeaderInit = typeof AdbPacketHeader[
|
|
23
|
+
type AdbPacketHeaderInit = typeof AdbPacketHeader["TInit"];
|
|
25
24
|
|
|
26
|
-
export const AdbPacket =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.uint8Array('payload', { lengthField: 'payloadLength' });
|
|
25
|
+
export const AdbPacket = new Struct({ littleEndian: true })
|
|
26
|
+
.fields(AdbPacketHeader)
|
|
27
|
+
.uint8Array("payload", { lengthField: "payloadLength" });
|
|
30
28
|
|
|
31
|
-
export type AdbPacket = typeof AdbPacket[
|
|
29
|
+
export type AdbPacket = typeof AdbPacket["TDeserializeResult"];
|
|
32
30
|
|
|
33
31
|
/**
|
|
34
32
|
* `AdbPacketData` contains all the useful fields of `AdbPacket`.
|
|
@@ -40,29 +38,41 @@ export type AdbPacket = typeof AdbPacket['TDeserializeResult'];
|
|
|
40
38
|
* however, `AdbPacketDispatcher` will transform `AdbPacketData` to `AdbPacketInit` for you,
|
|
41
39
|
* so `AdbSocket#writable#write` only needs `AdbPacketData`.
|
|
42
40
|
*/
|
|
43
|
-
export type AdbPacketData = Omit<
|
|
41
|
+
export type AdbPacketData = Omit<
|
|
42
|
+
typeof AdbPacket["TInit"],
|
|
43
|
+
"checksum" | "magic"
|
|
44
|
+
>;
|
|
44
45
|
|
|
45
46
|
// All fields except `magic`, which can be calculated in `AdbPacketSerializeStream`
|
|
46
|
-
export type AdbPacketInit = Omit<typeof AdbPacket[
|
|
47
|
+
export type AdbPacketInit = Omit<typeof AdbPacket["TInit"], "magic">;
|
|
47
48
|
|
|
48
49
|
export function calculateChecksum(payload: Uint8Array): number;
|
|
49
50
|
export function calculateChecksum(init: AdbPacketData): AdbPacketInit;
|
|
50
|
-
export function calculateChecksum(
|
|
51
|
+
export function calculateChecksum(
|
|
52
|
+
payload: Uint8Array | AdbPacketData
|
|
53
|
+
): number | AdbPacketInit {
|
|
51
54
|
if (payload instanceof Uint8Array) {
|
|
52
55
|
return payload.reduce((result, item) => result + item, 0);
|
|
53
56
|
} else {
|
|
54
|
-
(payload as AdbPacketInit).checksum = calculateChecksum(
|
|
57
|
+
(payload as AdbPacketInit).checksum = calculateChecksum(
|
|
58
|
+
payload.payload
|
|
59
|
+
);
|
|
55
60
|
return payload as AdbPacketInit;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
export class AdbPacketSerializeStream extends TransformStream<
|
|
64
|
+
export class AdbPacketSerializeStream extends TransformStream<
|
|
65
|
+
AdbPacketInit,
|
|
66
|
+
Uint8Array
|
|
67
|
+
> {
|
|
60
68
|
public constructor() {
|
|
61
69
|
super({
|
|
62
|
-
transform:
|
|
70
|
+
transform: (init, controller) => {
|
|
63
71
|
// This syntax is ugly, but I don't want to create a new object.
|
|
64
|
-
(init as unknown as AdbPacketHeaderInit).magic =
|
|
65
|
-
|
|
72
|
+
(init as unknown as AdbPacketHeaderInit).magic =
|
|
73
|
+
init.command ^ 0xffffffff;
|
|
74
|
+
(init as unknown as AdbPacketHeaderInit).payloadLength =
|
|
75
|
+
init.payload.byteLength;
|
|
66
76
|
|
|
67
77
|
controller.enqueue(
|
|
68
78
|
AdbPacketHeader.serialize(
|
package/src/socket/dispatcher.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import { AsyncOperationManager, PromiseResolver } from
|
|
2
|
-
import type
|
|
1
|
+
import { AsyncOperationManager, PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import { type RemoveEventListener } from "@yume-chan/event";
|
|
3
|
+
import {
|
|
4
|
+
AbortController,
|
|
5
|
+
WritableStream,
|
|
6
|
+
type ReadableWritablePair,
|
|
7
|
+
type WritableStreamDefaultWriter,
|
|
8
|
+
} from "@yume-chan/stream-extra";
|
|
3
9
|
import { EMPTY_UINT8_ARRAY, type ValueOrPromise } from "@yume-chan/struct";
|
|
4
10
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
import {
|
|
12
|
+
AdbCommand,
|
|
13
|
+
calculateChecksum,
|
|
14
|
+
type AdbPacketData,
|
|
15
|
+
type AdbPacketInit,
|
|
16
|
+
} from "../packet.js";
|
|
17
|
+
import { decodeUtf8, encodeUtf8 } from "../utils/index.js";
|
|
18
|
+
|
|
19
|
+
import { AdbSocketController, type AdbSocket } from "./socket.js";
|
|
9
20
|
|
|
10
21
|
export interface AdbPacketDispatcherOptions {
|
|
11
22
|
calculateChecksum: boolean;
|
|
@@ -19,7 +30,9 @@ export interface AdbPacketDispatcherOptions {
|
|
|
19
30
|
maxPayloadSize: number;
|
|
20
31
|
}
|
|
21
32
|
|
|
22
|
-
export type AdbIncomingSocketHandler = (
|
|
33
|
+
export type AdbIncomingSocketHandler = (
|
|
34
|
+
socket: AdbSocket
|
|
35
|
+
) => ValueOrPromise<boolean>;
|
|
23
36
|
|
|
24
37
|
export interface Closeable {
|
|
25
38
|
close(): ValueOrPromise<void>;
|
|
@@ -50,7 +63,9 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
50
63
|
|
|
51
64
|
private _closed = false;
|
|
52
65
|
private _disconnected = new PromiseResolver<void>();
|
|
53
|
-
public get disconnected() {
|
|
66
|
+
public get disconnected() {
|
|
67
|
+
return this._disconnected.promise;
|
|
68
|
+
}
|
|
54
69
|
|
|
55
70
|
private _incomingSocketHandlers: Set<AdbIncomingSocketHandler> = new Set();
|
|
56
71
|
|
|
@@ -63,53 +78,68 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
63
78
|
this.options = options;
|
|
64
79
|
|
|
65
80
|
connection.readable
|
|
66
|
-
.pipeTo(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
case AdbCommand.Close:
|
|
73
|
-
await this.handleClose(packet);
|
|
74
|
-
break;
|
|
75
|
-
case AdbCommand.Write:
|
|
76
|
-
if (this.sockets.has(packet.arg1)) {
|
|
77
|
-
await this.sockets.get(packet.arg1)!.enqueue(packet.payload);
|
|
78
|
-
await this.sendPacket(AdbCommand.OK, packet.arg1, packet.arg0);
|
|
81
|
+
.pipeTo(
|
|
82
|
+
new WritableStream({
|
|
83
|
+
write: async (packet) => {
|
|
84
|
+
switch (packet.command) {
|
|
85
|
+
case AdbCommand.OK:
|
|
86
|
+
this.handleOk(packet);
|
|
79
87
|
break;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
case AdbCommand.Close:
|
|
89
|
+
await this.handleClose(packet);
|
|
90
|
+
break;
|
|
91
|
+
case AdbCommand.Write:
|
|
92
|
+
if (this.sockets.has(packet.arg1)) {
|
|
93
|
+
await this.sockets
|
|
94
|
+
.get(packet.arg1)!
|
|
95
|
+
.enqueue(packet.payload);
|
|
96
|
+
await this.sendPacket(
|
|
97
|
+
AdbCommand.OK,
|
|
98
|
+
packet.arg1,
|
|
99
|
+
packet.arg0
|
|
100
|
+
);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Unknown local socket id: ${packet.arg1}`
|
|
105
|
+
);
|
|
106
|
+
case AdbCommand.Open:
|
|
107
|
+
await this.handleOpen(packet);
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
// Junk data may only appear in the authentication phase,
|
|
111
|
+
// since the dispatcher only works after authentication,
|
|
112
|
+
// all packets should have a valid command.
|
|
113
|
+
// (although it's possible that Adb added new commands in the future)
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Unknown command: ${packet.command.toString(
|
|
116
|
+
16
|
|
117
|
+
)}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
{
|
|
123
|
+
// There are multiple reasons for the pipe to stop,
|
|
124
|
+
// (device disconnection, protocol error, or user abortion)
|
|
125
|
+
// if the underlying streams are still open,
|
|
126
|
+
// it's still possible to create another ADB connection.
|
|
127
|
+
// So don't close `readable` here.
|
|
128
|
+
preventCancel: true,
|
|
129
|
+
signal: this._abortController.signal,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
.then(
|
|
133
|
+
() => {
|
|
134
|
+
this.dispose();
|
|
92
135
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// So don't close `readable` here.
|
|
99
|
-
preventCancel: true,
|
|
100
|
-
signal: this._abortController.signal,
|
|
101
|
-
})
|
|
102
|
-
.then(() => {
|
|
103
|
-
this.dispose();
|
|
104
|
-
}, (e) => {
|
|
105
|
-
// https://github.com/MattiasBuelens/web-streams-polyfill/issues/115
|
|
106
|
-
// `e` is always `AbortError` (instead of what I give in `abortController.abort()`)
|
|
107
|
-
// so we can't check if `e` is a real error.
|
|
108
|
-
if (!this._closed) {
|
|
109
|
-
this._disconnected.reject(e);
|
|
136
|
+
(e) => {
|
|
137
|
+
if (!this._closed) {
|
|
138
|
+
this._disconnected.reject(e);
|
|
139
|
+
}
|
|
140
|
+
this.dispose();
|
|
110
141
|
}
|
|
111
|
-
|
|
112
|
-
});
|
|
142
|
+
);
|
|
113
143
|
|
|
114
144
|
this._writer = connection.writable.getWriter();
|
|
115
145
|
}
|
|
@@ -129,13 +159,18 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
129
159
|
|
|
130
160
|
// Maybe the device is responding to a packet of last connection
|
|
131
161
|
// Tell the device to close the socket
|
|
132
|
-
this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0);
|
|
162
|
+
void this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0);
|
|
133
163
|
}
|
|
134
164
|
|
|
135
165
|
private async handleClose(packet: AdbPacketData) {
|
|
136
166
|
// If the socket is still pending
|
|
137
|
-
if (
|
|
138
|
-
|
|
167
|
+
if (
|
|
168
|
+
packet.arg0 === 0 &&
|
|
169
|
+
this.initializers.reject(
|
|
170
|
+
packet.arg1,
|
|
171
|
+
new Error("Socket open failed")
|
|
172
|
+
)
|
|
173
|
+
) {
|
|
139
174
|
// Device failed to create the socket
|
|
140
175
|
// (unknown service string, failed to execute command, etc.)
|
|
141
176
|
// it doesn't break the connection,
|
|
@@ -156,9 +191,13 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
156
191
|
if (socket) {
|
|
157
192
|
// The device want to close the socket
|
|
158
193
|
if (!socket.closed) {
|
|
159
|
-
await this.sendPacket(
|
|
194
|
+
await this.sendPacket(
|
|
195
|
+
AdbCommand.Close,
|
|
196
|
+
packet.arg1,
|
|
197
|
+
packet.arg0
|
|
198
|
+
);
|
|
160
199
|
}
|
|
161
|
-
socket.dispose();
|
|
200
|
+
await socket.dispose();
|
|
162
201
|
this.sockets.delete(packet.arg1);
|
|
163
202
|
return;
|
|
164
203
|
}
|
|
@@ -168,7 +207,14 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
168
207
|
// the device may also respond with two `CLSE` packets.
|
|
169
208
|
}
|
|
170
209
|
|
|
171
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Add a handler for incoming socket.
|
|
212
|
+
* @param handler A function to call with new incoming sockets. It must return `true` if it accepts the socket.
|
|
213
|
+
* @returns A function to remove the handler.
|
|
214
|
+
*/
|
|
215
|
+
public onIncomingSocket(
|
|
216
|
+
handler: AdbIncomingSocketHandler
|
|
217
|
+
): RemoveEventListener {
|
|
172
218
|
this._incomingSocketHandlers.add(handler);
|
|
173
219
|
const remove = () => {
|
|
174
220
|
this._incomingSocketHandlers.delete(handler);
|
|
@@ -178,7 +224,7 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
178
224
|
}
|
|
179
225
|
|
|
180
226
|
private async handleOpen(packet: AdbPacketData) {
|
|
181
|
-
// AsyncOperationManager doesn't support
|
|
227
|
+
// `AsyncOperationManager` doesn't support skipping IDs
|
|
182
228
|
// Use `add` + `resolve` to simulate this behavior
|
|
183
229
|
const [localId] = this.initializers.add<number>();
|
|
184
230
|
this.initializers.resolve(localId, undefined);
|
|
@@ -207,16 +253,11 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
207
253
|
|
|
208
254
|
public async createSocket(serviceString: string): Promise<AdbSocket> {
|
|
209
255
|
if (this.options.appendNullToServiceString) {
|
|
210
|
-
serviceString +=
|
|
256
|
+
serviceString += "\0";
|
|
211
257
|
}
|
|
212
258
|
|
|
213
259
|
const [localId, initializer] = this.initializers.add<number>();
|
|
214
|
-
await this.sendPacket(
|
|
215
|
-
AdbCommand.Open,
|
|
216
|
-
localId,
|
|
217
|
-
0,
|
|
218
|
-
serviceString
|
|
219
|
-
);
|
|
260
|
+
await this.sendPacket(AdbCommand.Open, localId, 0, serviceString);
|
|
220
261
|
|
|
221
262
|
// Fulfilled by `handleOk`
|
|
222
263
|
const remoteId = await initializer;
|
|
@@ -243,27 +284,29 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
243
284
|
packetOrCommand: AdbPacketInit | AdbCommand,
|
|
244
285
|
arg0?: number,
|
|
245
286
|
arg1?: number,
|
|
246
|
-
payload: string | Uint8Array = EMPTY_UINT8_ARRAY
|
|
287
|
+
payload: string | Uint8Array = EMPTY_UINT8_ARRAY
|
|
247
288
|
): Promise<void> {
|
|
248
289
|
let init: AdbPacketData;
|
|
249
290
|
if (arg0 === undefined) {
|
|
250
291
|
init = packetOrCommand as AdbPacketInit;
|
|
251
292
|
} else {
|
|
252
|
-
if (typeof payload ===
|
|
293
|
+
if (typeof payload === "string") {
|
|
253
294
|
payload = encodeUtf8(payload);
|
|
254
295
|
}
|
|
255
296
|
|
|
256
297
|
init = {
|
|
257
298
|
command: packetOrCommand as AdbCommand,
|
|
258
|
-
arg0: arg0
|
|
299
|
+
arg0: arg0,
|
|
259
300
|
arg1: arg1 as number,
|
|
260
301
|
payload,
|
|
261
302
|
};
|
|
262
303
|
}
|
|
263
304
|
|
|
264
|
-
if (
|
|
265
|
-
init.payload
|
|
266
|
-
|
|
305
|
+
if (
|
|
306
|
+
init.payload &&
|
|
307
|
+
init.payload.byteLength > this.options.maxPayloadSize
|
|
308
|
+
) {
|
|
309
|
+
throw new Error("payload too large");
|
|
267
310
|
}
|
|
268
311
|
|
|
269
312
|
if (this.options.calculateChecksum) {
|
|
@@ -279,10 +322,7 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
279
322
|
public async close() {
|
|
280
323
|
// Send `CLSE` packets for all sockets
|
|
281
324
|
await Promise.all(
|
|
282
|
-
Array.from(
|
|
283
|
-
this.sockets.values(),
|
|
284
|
-
socket => socket.close(),
|
|
285
|
-
)
|
|
325
|
+
Array.from(this.sockets.values(), (socket) => socket.close())
|
|
286
326
|
);
|
|
287
327
|
|
|
288
328
|
// Stop receiving
|
|
@@ -297,7 +337,9 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
297
337
|
|
|
298
338
|
private dispose() {
|
|
299
339
|
for (const socket of this.sockets.values()) {
|
|
300
|
-
socket.dispose()
|
|
340
|
+
socket.dispose().catch((e) => {
|
|
341
|
+
void e;
|
|
342
|
+
});
|
|
301
343
|
}
|
|
302
344
|
|
|
303
345
|
this._disconnected.resolve();
|
package/src/socket/index.ts
CHANGED