@yume-chan/android-bin 0.0.23 → 1.0.0
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.md +40 -2
- package/LICENSE +1 -1
- package/README.md +5 -2
- package/esm/am.d.ts.map +1 -1
- package/esm/am.js +2 -2
- package/esm/am.js.map +1 -1
- package/esm/bu.d.ts +2 -2
- package/esm/bu.d.ts.map +1 -1
- package/esm/bu.js +2 -2
- package/esm/bu.js.map +1 -1
- package/esm/bug-report.d.ts.map +1 -1
- package/esm/bug-report.js +27 -29
- package/esm/bug-report.js.map +1 -1
- package/esm/cmd.d.ts +12 -0
- package/esm/cmd.d.ts.map +1 -1
- package/esm/cmd.js +15 -3
- package/esm/cmd.js.map +1 -1
- package/esm/dumpsys.d.ts +55 -26
- package/esm/dumpsys.d.ts.map +1 -1
- package/esm/dumpsys.js +66 -69
- package/esm/dumpsys.js.map +1 -1
- package/esm/intent.d.ts +1 -0
- package/esm/intent.d.ts.map +1 -1
- package/esm/intent.js +8 -0
- package/esm/intent.js.map +1 -1
- package/esm/logcat.d.ts +59 -46
- package/esm/logcat.d.ts.map +1 -1
- package/esm/logcat.js +82 -81
- package/esm/logcat.js.map +1 -1
- package/esm/overlay-display.d.ts.map +1 -1
- package/esm/overlay-display.js +1 -1
- package/esm/overlay-display.js.map +1 -1
- package/esm/pm.d.ts +5 -5
- package/esm/pm.d.ts.map +1 -1
- package/esm/pm.js +51 -28
- package/esm/pm.js.map +1 -1
- package/esm/settings.d.ts.map +1 -1
- package/esm/string-format.d.ts +9 -6
- package/esm/string-format.d.ts.map +1 -1
- package/esm/string-format.js +3 -4
- package/esm/string-format.js.map +1 -1
- package/package.json +12 -16
- package/src/am.ts +5 -3
- package/src/bu.ts +4 -4
- package/src/bug-report.ts +40 -44
- package/src/cmd.ts +19 -7
- package/src/dumpsys.ts +100 -66
- package/src/intent.ts +10 -0
- package/src/logcat.ts +117 -105
- package/src/overlay-display.ts +2 -2
- package/src/pm.ts +81 -52
- package/src/settings.ts +1 -1
- package/src/string-format.ts +3 -5
- package/tsconfig.build.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/CHANGELOG.json +0 -175
package/src/logcat.ts
CHANGED
|
@@ -5,42 +5,50 @@ import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
|
|
|
5
5
|
import type { ReadableStream } from "@yume-chan/stream-extra";
|
|
6
6
|
import {
|
|
7
7
|
BufferedTransformStream,
|
|
8
|
-
DecodeUtf8Stream,
|
|
9
8
|
SplitStringStream,
|
|
9
|
+
TextDecoderStream,
|
|
10
10
|
WrapReadableStream,
|
|
11
|
-
WritableStream,
|
|
12
11
|
} from "@yume-chan/stream-extra";
|
|
13
|
-
import type { AsyncExactReadable } from "@yume-chan/struct";
|
|
14
|
-
import
|
|
12
|
+
import type { AsyncExactReadable, StructValue } from "@yume-chan/struct";
|
|
13
|
+
import { decodeUtf8, struct, u16, u32 } from "@yume-chan/struct";
|
|
15
14
|
|
|
16
15
|
// `adb logcat` is an alias to `adb shell logcat`
|
|
17
16
|
// so instead of adding to core library, it's implemented here
|
|
18
17
|
|
|
19
18
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=141;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
|
20
|
-
export
|
|
21
|
-
All
|
|
22
|
-
Main,
|
|
23
|
-
Radio,
|
|
24
|
-
Events,
|
|
25
|
-
System,
|
|
26
|
-
Crash,
|
|
27
|
-
Stats,
|
|
28
|
-
Security,
|
|
29
|
-
Kernel,
|
|
30
|
-
}
|
|
19
|
+
export const LogId = {
|
|
20
|
+
All: -1,
|
|
21
|
+
Main: 0,
|
|
22
|
+
Radio: 1,
|
|
23
|
+
Events: 2,
|
|
24
|
+
System: 3,
|
|
25
|
+
Crash: 4,
|
|
26
|
+
Stats: 5,
|
|
27
|
+
Security: 6,
|
|
28
|
+
Kernel: 7,
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export type LogId = (typeof LogId)[keyof typeof LogId];
|
|
32
|
+
|
|
33
|
+
const LogIdName =
|
|
34
|
+
/* #__PURE__ */
|
|
35
|
+
(() => Object.fromEntries(Object.entries(LogId).map(([k, v]) => [v, k])))();
|
|
31
36
|
|
|
32
37
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=73;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
|
33
|
-
export
|
|
34
|
-
Unknown,
|
|
35
|
-
Default,
|
|
36
|
-
Verbose,
|
|
37
|
-
Debug,
|
|
38
|
-
Info,
|
|
39
|
-
Warn,
|
|
40
|
-
Error,
|
|
41
|
-
Fatal,
|
|
42
|
-
Silent,
|
|
43
|
-
}
|
|
38
|
+
export const AndroidLogPriority = {
|
|
39
|
+
Unknown: 0,
|
|
40
|
+
Default: 1,
|
|
41
|
+
Verbose: 2,
|
|
42
|
+
Debug: 3,
|
|
43
|
+
Info: 4,
|
|
44
|
+
Warn: 5,
|
|
45
|
+
Error: 6,
|
|
46
|
+
Fatal: 7,
|
|
47
|
+
Silent: 8,
|
|
48
|
+
} as const;
|
|
49
|
+
|
|
50
|
+
export type AndroidLogPriority =
|
|
51
|
+
(typeof AndroidLogPriority)[keyof typeof AndroidLogPriority];
|
|
44
52
|
|
|
45
53
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=140;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
|
|
46
54
|
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
|
@@ -56,16 +64,18 @@ export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
|
|
56
64
|
[AndroidLogPriority.Silent]: "S",
|
|
57
65
|
};
|
|
58
66
|
|
|
59
|
-
export
|
|
60
|
-
Brief,
|
|
61
|
-
Process,
|
|
62
|
-
Tag,
|
|
63
|
-
Thread,
|
|
64
|
-
Raw,
|
|
65
|
-
Time,
|
|
66
|
-
ThreadTime,
|
|
67
|
-
Long,
|
|
68
|
-
}
|
|
67
|
+
export const LogcatFormat = {
|
|
68
|
+
Brief: 0,
|
|
69
|
+
Process: 1,
|
|
70
|
+
Tag: 2,
|
|
71
|
+
Thread: 3,
|
|
72
|
+
Raw: 4,
|
|
73
|
+
Time: 5,
|
|
74
|
+
ThreadTime: 6,
|
|
75
|
+
Long: 7,
|
|
76
|
+
} as const;
|
|
77
|
+
|
|
78
|
+
export type LogcatFormat = (typeof LogcatFormat)[keyof typeof LogcatFormat];
|
|
69
79
|
|
|
70
80
|
export interface LogcatFormatModifiers {
|
|
71
81
|
microseconds?: boolean;
|
|
@@ -85,28 +95,34 @@ export interface LogcatOptions {
|
|
|
85
95
|
ids?: LogId[];
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
const NANOSECONDS_PER_SECOND = BigInt(1e9);
|
|
98
|
+
const NANOSECONDS_PER_SECOND = /* #__PURE__ */ BigInt(1e9);
|
|
89
99
|
|
|
90
100
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
|
91
|
-
export const LoggerEntry =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
101
|
+
export const LoggerEntry = struct(
|
|
102
|
+
{
|
|
103
|
+
payloadSize: u16,
|
|
104
|
+
headerSize: u16,
|
|
105
|
+
pid: u32,
|
|
106
|
+
tid: u32,
|
|
107
|
+
seconds: u32,
|
|
108
|
+
nanoseconds: u32,
|
|
109
|
+
logId: u32,
|
|
110
|
+
uid: u32,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
littleEndian: true,
|
|
114
|
+
extra: {
|
|
115
|
+
get timestamp(): bigint {
|
|
116
|
+
return (
|
|
117
|
+
BigInt(this.seconds) * NANOSECONDS_PER_SECOND +
|
|
118
|
+
BigInt(this.nanoseconds)
|
|
119
|
+
);
|
|
120
|
+
},
|
|
106
121
|
},
|
|
107
|
-
}
|
|
122
|
+
},
|
|
123
|
+
);
|
|
108
124
|
|
|
109
|
-
export type LoggerEntry =
|
|
125
|
+
export type LoggerEntry = StructValue<typeof LoggerEntry>;
|
|
110
126
|
|
|
111
127
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
|
|
112
128
|
export interface AndroidLogEntry extends LoggerEntry {
|
|
@@ -353,6 +369,7 @@ export async function deserializeAndroidLogEntry(
|
|
|
353
369
|
): Promise<AndroidLogEntry> {
|
|
354
370
|
const entry = (await LoggerEntry.deserialize(stream)) as AndroidLogEntry;
|
|
355
371
|
if (entry.headerSize !== LoggerEntry.size) {
|
|
372
|
+
// Skip unknown fields
|
|
356
373
|
await stream.readExactly(entry.headerSize - LoggerEntry.size);
|
|
357
374
|
}
|
|
358
375
|
|
|
@@ -384,7 +401,7 @@ export interface LogSize {
|
|
|
384
401
|
|
|
385
402
|
export class Logcat extends AdbCommandBase {
|
|
386
403
|
static logIdToName(id: LogId): string {
|
|
387
|
-
return
|
|
404
|
+
return LogIdName[id]!;
|
|
388
405
|
}
|
|
389
406
|
|
|
390
407
|
static logNameToId(name: string): LogId {
|
|
@@ -403,12 +420,12 @@ export class Logcat extends AdbCommandBase {
|
|
|
403
420
|
|
|
404
421
|
// TODO: logcat: Support output format before Android 10
|
|
405
422
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/748128
|
|
406
|
-
static readonly LOG_SIZE_REGEX_10 =
|
|
423
|
+
static readonly LOG_SIZE_REGEX_10: RegExp =
|
|
407
424
|
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
|
|
408
425
|
|
|
409
426
|
// Android 11 added `readable` part
|
|
410
427
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/1390940
|
|
411
|
-
static readonly LOG_SIZE_REGEX_11 =
|
|
428
|
+
static readonly LOG_SIZE_REGEX_11: RegExp =
|
|
412
429
|
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
|
|
413
430
|
|
|
414
431
|
async getLogSize(ids?: LogId[]): Promise<LogSize[]> {
|
|
@@ -419,58 +436,53 @@ export class Logcat extends AdbCommandBase {
|
|
|
419
436
|
]);
|
|
420
437
|
|
|
421
438
|
const result: LogSize[] = [];
|
|
422
|
-
await stdout
|
|
423
|
-
.pipeThrough(new
|
|
424
|
-
.pipeThrough(new SplitStringStream("\n"))
|
|
425
|
-
.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
}),
|
|
468
|
-
);
|
|
439
|
+
for await (const line of stdout
|
|
440
|
+
.pipeThrough(new TextDecoderStream())
|
|
441
|
+
.pipeThrough(new SplitStringStream("\n"))) {
|
|
442
|
+
let match = line.match(Logcat.LOG_SIZE_REGEX_11);
|
|
443
|
+
if (match) {
|
|
444
|
+
result.push({
|
|
445
|
+
id: Logcat.logNameToId(match[1]!),
|
|
446
|
+
size: Logcat.parseSize(
|
|
447
|
+
Number.parseInt(match[2]!, 10),
|
|
448
|
+
match[3]!,
|
|
449
|
+
),
|
|
450
|
+
readable: Logcat.parseSize(
|
|
451
|
+
Number.parseInt(match[6]!, 10),
|
|
452
|
+
match[7]!,
|
|
453
|
+
),
|
|
454
|
+
consumed: Logcat.parseSize(
|
|
455
|
+
Number.parseInt(match[4]!, 10),
|
|
456
|
+
match[5]!,
|
|
457
|
+
),
|
|
458
|
+
maxEntrySize: parseInt(match[8]!, 10),
|
|
459
|
+
maxPayloadSize: parseInt(match[9]!, 10),
|
|
460
|
+
});
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
match = line.match(Logcat.LOG_SIZE_REGEX_10);
|
|
465
|
+
if (match) {
|
|
466
|
+
result.push({
|
|
467
|
+
id: Logcat.logNameToId(match[1]!),
|
|
468
|
+
size: Logcat.parseSize(
|
|
469
|
+
Number.parseInt(match[2]!, 10),
|
|
470
|
+
match[3]!,
|
|
471
|
+
),
|
|
472
|
+
consumed: Logcat.parseSize(
|
|
473
|
+
Number.parseInt(match[4]!, 10),
|
|
474
|
+
match[5]!,
|
|
475
|
+
),
|
|
476
|
+
maxEntrySize: parseInt(match[6]!, 10),
|
|
477
|
+
maxPayloadSize: parseInt(match[7]!, 10),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
469
481
|
|
|
470
482
|
return result;
|
|
471
483
|
}
|
|
472
484
|
|
|
473
|
-
async clear(ids?: LogId[]) {
|
|
485
|
+
async clear(ids?: LogId[]): Promise<void> {
|
|
474
486
|
const args = ["logcat", "-c"];
|
|
475
487
|
if (ids && ids.length > 0) {
|
|
476
488
|
args.push("-b", Logcat.joinLogId(ids));
|
package/src/overlay-display.ts
CHANGED
|
@@ -36,7 +36,7 @@ export class OverlayDisplay extends AdbCommandBase {
|
|
|
36
36
|
p.literal("/"),
|
|
37
37
|
{ name: "density", format: p.digits() },
|
|
38
38
|
),
|
|
39
|
-
1,
|
|
39
|
+
{ min: 1 },
|
|
40
40
|
),
|
|
41
41
|
},
|
|
42
42
|
{
|
|
@@ -81,7 +81,7 @@ export class OverlayDisplay extends AdbCommandBase {
|
|
|
81
81
|
}));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
async set(devices: OverlayDisplayDevice[]) {
|
|
84
|
+
async set(devices: OverlayDisplayDevice[]): Promise<void> {
|
|
85
85
|
await this.#settings.put(
|
|
86
86
|
"global",
|
|
87
87
|
OverlayDisplay.SETTING_KEY,
|
package/src/pm.ts
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Adb } from "@yume-chan/adb";
|
|
7
7
|
import { AdbCommandBase, escapeArg } from "@yume-chan/adb";
|
|
8
|
-
import type {
|
|
8
|
+
import type { MaybeConsumable, ReadableStream } from "@yume-chan/stream-extra";
|
|
9
9
|
import {
|
|
10
10
|
ConcatStringStream,
|
|
11
|
-
DecodeUtf8Stream,
|
|
12
11
|
SplitStringStream,
|
|
12
|
+
TextDecoderStream,
|
|
13
13
|
} from "@yume-chan/stream-extra";
|
|
14
14
|
|
|
15
15
|
import { Cmd } from "./cmd.js";
|
|
@@ -255,6 +255,32 @@ const PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP: Partial<
|
|
|
255
255
|
user: "--user",
|
|
256
256
|
};
|
|
257
257
|
|
|
258
|
+
function buildInstallArguments(
|
|
259
|
+
command: string,
|
|
260
|
+
options: Partial<PackageManagerInstallOptions> | undefined,
|
|
261
|
+
): string[] {
|
|
262
|
+
const args = buildArguments(
|
|
263
|
+
["pm", command],
|
|
264
|
+
options,
|
|
265
|
+
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
|
|
266
|
+
);
|
|
267
|
+
if (!options?.skipExisting) {
|
|
268
|
+
/*
|
|
269
|
+
* | behavior | previous version | modern version |
|
|
270
|
+
* | -------------------- | -------------------- | -------------------- |
|
|
271
|
+
* | replace existing app | requires `-r` | default behavior [1] |
|
|
272
|
+
* | skip existing app | default behavior [2] | requires `-R` |
|
|
273
|
+
*
|
|
274
|
+
* [1]: `-r` recognized but ignored
|
|
275
|
+
* [2]: `-R` not recognized but ignored
|
|
276
|
+
*
|
|
277
|
+
* So add `-r` when `skipExisting` is `false` for compatibility.
|
|
278
|
+
*/
|
|
279
|
+
args.push("-r");
|
|
280
|
+
}
|
|
281
|
+
return args;
|
|
282
|
+
}
|
|
283
|
+
|
|
258
284
|
export class PackageManager extends AdbCommandBase {
|
|
259
285
|
#cmd: Cmd;
|
|
260
286
|
|
|
@@ -263,22 +289,6 @@ export class PackageManager extends AdbCommandBase {
|
|
|
263
289
|
this.#cmd = new Cmd(adb);
|
|
264
290
|
}
|
|
265
291
|
|
|
266
|
-
#buildInstallArguments(
|
|
267
|
-
command: string,
|
|
268
|
-
options: Partial<PackageManagerInstallOptions> | undefined,
|
|
269
|
-
): string[] {
|
|
270
|
-
const args = buildArguments(
|
|
271
|
-
["pm", command],
|
|
272
|
-
options,
|
|
273
|
-
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
|
|
274
|
-
);
|
|
275
|
-
if (!options?.skipExisting) {
|
|
276
|
-
// Compatibility with old versions of pm
|
|
277
|
-
args.push("-r");
|
|
278
|
-
}
|
|
279
|
-
return args;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
292
|
/**
|
|
283
293
|
* Install the apk file.
|
|
284
294
|
*
|
|
@@ -288,14 +298,14 @@ export class PackageManager extends AdbCommandBase {
|
|
|
288
298
|
apks: string[],
|
|
289
299
|
options?: Partial<PackageManagerInstallOptions>,
|
|
290
300
|
): Promise<string> {
|
|
291
|
-
const args =
|
|
301
|
+
const args = buildInstallArguments("install", options);
|
|
292
302
|
// WIP: old version of pm doesn't support multiple apks
|
|
293
303
|
args.push(...apks);
|
|
294
304
|
return await this.adb.subprocess.spawnAndWaitLegacy(args);
|
|
295
305
|
}
|
|
296
306
|
|
|
297
307
|
async pushAndInstallStream(
|
|
298
|
-
stream: ReadableStream<
|
|
308
|
+
stream: ReadableStream<MaybeConsumable<Uint8Array>>,
|
|
299
309
|
options?: Partial<PackageManagerInstallOptions>,
|
|
300
310
|
): Promise<void> {
|
|
301
311
|
const sync = await this.adb.sync();
|
|
@@ -312,11 +322,16 @@ export class PackageManager extends AdbCommandBase {
|
|
|
312
322
|
await sync.dispose();
|
|
313
323
|
}
|
|
314
324
|
|
|
315
|
-
// Starting from Android 7, `pm`
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
|
|
325
|
+
// Starting from Android 7, `pm` becomes a wrapper to `cmd package`.
|
|
326
|
+
// The benefit of `cmd package` is it starts faster than the old `pm`,
|
|
327
|
+
// because it connects to the already running `system` process,
|
|
328
|
+
// instead of initializing all system components from scratch.
|
|
329
|
+
//
|
|
330
|
+
// But launching `cmd package` directly causes it to not be able to
|
|
331
|
+
// read files in `/data/local/tmp` (and many other places) due to SELinux policies,
|
|
332
|
+
// so installing files must still use `pm`.
|
|
333
|
+
// (the starting executable file decides which SELinux policies to apply)
|
|
334
|
+
const args = buildInstallArguments("install", options);
|
|
320
335
|
args.push(filePath);
|
|
321
336
|
|
|
322
337
|
try {
|
|
@@ -334,25 +349,28 @@ export class PackageManager extends AdbCommandBase {
|
|
|
334
349
|
|
|
335
350
|
async installStream(
|
|
336
351
|
size: number,
|
|
337
|
-
stream: ReadableStream<
|
|
352
|
+
stream: ReadableStream<MaybeConsumable<Uint8Array>>,
|
|
338
353
|
options?: Partial<PackageManagerInstallOptions>,
|
|
339
354
|
): Promise<void> {
|
|
340
355
|
// Android 7 added both `cmd` command and streaming install support,
|
|
341
|
-
//
|
|
342
|
-
// so
|
|
356
|
+
// It's hard to detect whether `pm` supports streaming install (unless actually trying),
|
|
357
|
+
// so check for whether `cmd` is supported,
|
|
358
|
+
// and assume `pm` streaming install support status is same as that.
|
|
343
359
|
if (!this.#cmd.supportsCmd) {
|
|
360
|
+
// Fall back to push file then install
|
|
344
361
|
await this.pushAndInstallStream(stream, options);
|
|
345
362
|
return;
|
|
346
363
|
}
|
|
347
364
|
|
|
348
|
-
const args =
|
|
349
|
-
// Remove `pm` from args,
|
|
365
|
+
const args = buildInstallArguments("install", options);
|
|
366
|
+
// Remove `pm` from args, `Cmd#spawn` will prepend `cmd <command>` so the final args
|
|
367
|
+
// will be `cmd package install <args>`
|
|
350
368
|
args.shift();
|
|
351
369
|
args.push("-S", size.toString());
|
|
352
370
|
const process = await this.#cmd.spawn(false, "package", ...args);
|
|
353
371
|
|
|
354
372
|
const output = process.stdout
|
|
355
|
-
.pipeThrough(new
|
|
373
|
+
.pipeThrough(new TextDecoderStream())
|
|
356
374
|
.pipeThrough(new ConcatStringStream())
|
|
357
375
|
.then((output) => output.trim());
|
|
358
376
|
|
|
@@ -377,7 +395,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
377
395
|
let installer: string | undefined;
|
|
378
396
|
let uid: number | undefined;
|
|
379
397
|
|
|
380
|
-
//
|
|
398
|
+
// The output format is easier to parse in backwards
|
|
381
399
|
let index = line.indexOf(" uid:");
|
|
382
400
|
if (index !== -1) {
|
|
383
401
|
uid = Number.parseInt(line.substring(index + " uid:".length), 10);
|
|
@@ -399,7 +417,9 @@ export class PackageManager extends AdbCommandBase {
|
|
|
399
417
|
line = line.substring(0, index);
|
|
400
418
|
}
|
|
401
419
|
|
|
402
|
-
// `sourceDir` may contain `=`
|
|
420
|
+
// `sourceDir` may contain `=` characters
|
|
421
|
+
// (because in newer Android versions it's a base64 string of encrypted package name),
|
|
422
|
+
// so use `lastIndexOf`
|
|
403
423
|
index = line.lastIndexOf("=");
|
|
404
424
|
if (index !== -1) {
|
|
405
425
|
sourceDir = line.substring(0, index);
|
|
@@ -440,7 +460,10 @@ export class PackageManager extends AdbCommandBase {
|
|
|
440
460
|
|
|
441
461
|
const process = await this.#cmdOrSubprocess(args);
|
|
442
462
|
const reader = process.stdout
|
|
443
|
-
.pipeThrough(new
|
|
463
|
+
.pipeThrough(new TextDecoderStream())
|
|
464
|
+
// FIXME: `SplitStringStream` will throw away some data
|
|
465
|
+
// if it doesn't end with a separator. So each chunk of data
|
|
466
|
+
// must contain several complete lines.
|
|
444
467
|
.pipeThrough(new SplitStringStream("\n"))
|
|
445
468
|
.getReader();
|
|
446
469
|
while (true) {
|
|
@@ -468,7 +491,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
468
491
|
|
|
469
492
|
const process = await this.#cmdOrSubprocess(args);
|
|
470
493
|
const output = await process.stdout
|
|
471
|
-
.pipeThrough(new
|
|
494
|
+
.pipeThrough(new TextDecoderStream())
|
|
472
495
|
.pipeThrough(new ConcatStringStream())
|
|
473
496
|
.then((output) => output.trim());
|
|
474
497
|
if (output !== "Success") {
|
|
@@ -489,7 +512,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
489
512
|
|
|
490
513
|
const process = await this.#cmdOrSubprocess(args);
|
|
491
514
|
const output = await process.stdout
|
|
492
|
-
.pipeThrough(new
|
|
515
|
+
.pipeThrough(new TextDecoderStream())
|
|
493
516
|
.pipeThrough(new ConcatStringStream())
|
|
494
517
|
.then((output) => output.trim());
|
|
495
518
|
|
|
@@ -510,12 +533,14 @@ export class PackageManager extends AdbCommandBase {
|
|
|
510
533
|
* @param options Options for the install session
|
|
511
534
|
* @returns ID of the new install session
|
|
512
535
|
*/
|
|
513
|
-
async sessionCreate(
|
|
514
|
-
|
|
536
|
+
async sessionCreate(
|
|
537
|
+
options?: Partial<PackageManagerInstallOptions>,
|
|
538
|
+
): Promise<number> {
|
|
539
|
+
const args = buildInstallArguments("install-create", options);
|
|
515
540
|
|
|
516
541
|
const process = await this.#cmdOrSubprocess(args);
|
|
517
542
|
const output = await process.stdout
|
|
518
|
-
.pipeThrough(new
|
|
543
|
+
.pipeThrough(new TextDecoderStream())
|
|
519
544
|
.pipeThrough(new ConcatStringStream())
|
|
520
545
|
.then((output) => output.trim());
|
|
521
546
|
|
|
@@ -527,7 +552,11 @@ export class PackageManager extends AdbCommandBase {
|
|
|
527
552
|
return Number.parseInt(sessionIdString[1]!, 10);
|
|
528
553
|
}
|
|
529
554
|
|
|
530
|
-
async sessionAddSplit(
|
|
555
|
+
async sessionAddSplit(
|
|
556
|
+
sessionId: number,
|
|
557
|
+
splitName: string,
|
|
558
|
+
path: string,
|
|
559
|
+
): Promise<void> {
|
|
531
560
|
const args: string[] = [
|
|
532
561
|
"pm",
|
|
533
562
|
"install-write",
|
|
@@ -548,8 +577,8 @@ export class PackageManager extends AdbCommandBase {
|
|
|
548
577
|
sessionId: number,
|
|
549
578
|
splitName: string,
|
|
550
579
|
size: number,
|
|
551
|
-
stream: ReadableStream<
|
|
552
|
-
) {
|
|
580
|
+
stream: ReadableStream<MaybeConsumable<Uint8Array>>,
|
|
581
|
+
): Promise<void> {
|
|
553
582
|
const args: string[] = [
|
|
554
583
|
"pm",
|
|
555
584
|
"install-write",
|
|
@@ -562,7 +591,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
562
591
|
|
|
563
592
|
const process = await this.#cmdOrSubprocess(args);
|
|
564
593
|
const output = process.stdout
|
|
565
|
-
.pipeThrough(new
|
|
594
|
+
.pipeThrough(new TextDecoderStream())
|
|
566
595
|
.pipeThrough(new ConcatStringStream())
|
|
567
596
|
.then((output) => output.trim());
|
|
568
597
|
|
|
@@ -576,7 +605,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
576
605
|
]);
|
|
577
606
|
}
|
|
578
607
|
|
|
579
|
-
async sessionCommit(sessionId: number) {
|
|
608
|
+
async sessionCommit(sessionId: number): Promise<void> {
|
|
580
609
|
const args: string[] = ["pm", "install-commit", sessionId.toString()];
|
|
581
610
|
const output = await this.adb.subprocess
|
|
582
611
|
.spawnAndWaitLegacy(args)
|
|
@@ -586,7 +615,7 @@ export class PackageManager extends AdbCommandBase {
|
|
|
586
615
|
}
|
|
587
616
|
}
|
|
588
617
|
|
|
589
|
-
async sessionAbandon(sessionId: number) {
|
|
618
|
+
async sessionAbandon(sessionId: number): Promise<void> {
|
|
590
619
|
const args: string[] = ["pm", "install-abandon", sessionId.toString()];
|
|
591
620
|
const output = await this.adb.subprocess
|
|
592
621
|
.spawnAndWaitLegacy(args)
|
|
@@ -601,7 +630,7 @@ export class PackageManagerInstallSession {
|
|
|
601
630
|
static async create(
|
|
602
631
|
packageManager: PackageManager,
|
|
603
632
|
options?: Partial<PackageManagerInstallOptions>,
|
|
604
|
-
) {
|
|
633
|
+
): Promise<PackageManagerInstallSession> {
|
|
605
634
|
const id = await packageManager.sessionCreate(options);
|
|
606
635
|
return new PackageManagerInstallSession(packageManager, id);
|
|
607
636
|
}
|
|
@@ -609,7 +638,7 @@ export class PackageManagerInstallSession {
|
|
|
609
638
|
#packageManager: PackageManager;
|
|
610
639
|
|
|
611
640
|
#id: number;
|
|
612
|
-
get id() {
|
|
641
|
+
get id(): number {
|
|
613
642
|
return this.#id;
|
|
614
643
|
}
|
|
615
644
|
|
|
@@ -618,15 +647,15 @@ export class PackageManagerInstallSession {
|
|
|
618
647
|
this.#id = id;
|
|
619
648
|
}
|
|
620
649
|
|
|
621
|
-
addSplit(splitName: string, path: string) {
|
|
650
|
+
addSplit(splitName: string, path: string): Promise<void> {
|
|
622
651
|
return this.#packageManager.sessionAddSplit(this.#id, splitName, path);
|
|
623
652
|
}
|
|
624
653
|
|
|
625
654
|
addSplitStream(
|
|
626
655
|
splitName: string,
|
|
627
656
|
size: number,
|
|
628
|
-
stream: ReadableStream<
|
|
629
|
-
) {
|
|
657
|
+
stream: ReadableStream<MaybeConsumable<Uint8Array>>,
|
|
658
|
+
): Promise<void> {
|
|
630
659
|
return this.#packageManager.sessionAddSplitStream(
|
|
631
660
|
this.#id,
|
|
632
661
|
splitName,
|
|
@@ -635,11 +664,11 @@ export class PackageManagerInstallSession {
|
|
|
635
664
|
);
|
|
636
665
|
}
|
|
637
666
|
|
|
638
|
-
commit() {
|
|
667
|
+
commit(): Promise<void> {
|
|
639
668
|
return this.#packageManager.sessionCommit(this.#id);
|
|
640
669
|
}
|
|
641
670
|
|
|
642
|
-
abandon() {
|
|
671
|
+
abandon(): Promise<void> {
|
|
643
672
|
return this.#packageManager.sessionAbandon(this.#id);
|
|
644
673
|
}
|
|
645
674
|
}
|
package/src/settings.ts
CHANGED
|
@@ -66,7 +66,7 @@ export class Settings extends AdbCommandBase {
|
|
|
66
66
|
namespace: SettingsNamespace,
|
|
67
67
|
key: string,
|
|
68
68
|
options?: SettingsOptions,
|
|
69
|
-
) {
|
|
69
|
+
): Promise<string> {
|
|
70
70
|
const output = await this.base("get", namespace, options, key);
|
|
71
71
|
// Remove last \n
|
|
72
72
|
return output.substring(0, output.length - 1);
|
package/src/string-format.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
2
1
|
export class ParseError extends Error {
|
|
3
2
|
#expected: string[];
|
|
4
3
|
|
|
@@ -94,7 +93,7 @@ export const p = {
|
|
|
94
93
|
const result = format.stringify(value);
|
|
95
94
|
// Parse the result to make sure it is valid
|
|
96
95
|
format.parse({ value: result, position: 0 });
|
|
97
|
-
} catch
|
|
96
|
+
} catch {
|
|
98
97
|
// ignore
|
|
99
98
|
}
|
|
100
99
|
}
|
|
@@ -104,8 +103,7 @@ export const p = {
|
|
|
104
103
|
separated: <T>(
|
|
105
104
|
separator: string,
|
|
106
105
|
format: Format<T>,
|
|
107
|
-
min = 0,
|
|
108
|
-
max = Infinity,
|
|
106
|
+
{ min = 0, max = Infinity }: { min?: number; max?: number } = {},
|
|
109
107
|
): Format<T[]> => ({
|
|
110
108
|
parse(reader: Reader) {
|
|
111
109
|
const result: T[] = [];
|
|
@@ -143,7 +141,7 @@ export const p = {
|
|
|
143
141
|
while (true) {
|
|
144
142
|
try {
|
|
145
143
|
result.push(format.parse(reader));
|
|
146
|
-
} catch
|
|
144
|
+
} catch {
|
|
147
145
|
break;
|
|
148
146
|
}
|
|
149
147
|
}
|