@yume-chan/android-bin 1.0.0 → 2.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +1 -1
  3. package/esm/am.d.ts +4 -2
  4. package/esm/am.d.ts.map +1 -1
  5. package/esm/am.js +9 -17
  6. package/esm/am.js.map +1 -1
  7. package/esm/bu.d.ts +2 -2
  8. package/esm/bu.d.ts.map +1 -1
  9. package/esm/bu.js +6 -14
  10. package/esm/bu.js.map +1 -1
  11. package/esm/bug-report.d.ts +2 -2
  12. package/esm/bug-report.d.ts.map +1 -1
  13. package/esm/bug-report.js +15 -13
  14. package/esm/bug-report.js.map +1 -1
  15. package/esm/cmd.d.ts +18 -20
  16. package/esm/cmd.d.ts.map +1 -1
  17. package/esm/cmd.js +84 -63
  18. package/esm/cmd.js.map +1 -1
  19. package/esm/demo-mode.d.ts +2 -2
  20. package/esm/demo-mode.js +3 -3
  21. package/esm/demo-mode.js.map +1 -1
  22. package/esm/dumpsys.d.ts +2 -2
  23. package/esm/dumpsys.js +6 -6
  24. package/esm/dumpsys.js.map +1 -1
  25. package/esm/logcat.d.ts +7 -8
  26. package/esm/logcat.d.ts.map +1 -1
  27. package/esm/logcat.js +18 -10
  28. package/esm/logcat.js.map +1 -1
  29. package/esm/overlay-display.d.ts +2 -2
  30. package/esm/overlay-display.js +2 -2
  31. package/esm/pm.d.ts +7 -3
  32. package/esm/pm.d.ts.map +1 -1
  33. package/esm/pm.js +81 -88
  34. package/esm/pm.js.map +1 -1
  35. package/esm/settings.d.ts +10 -7
  36. package/esm/settings.d.ts.map +1 -1
  37. package/esm/settings.js +14 -23
  38. package/esm/settings.js.map +1 -1
  39. package/package.json +9 -9
  40. package/src/am.ts +14 -21
  41. package/src/bu.ts +6 -14
  42. package/src/bug-report.ts +16 -16
  43. package/src/cmd.ts +136 -85
  44. package/src/demo-mode.ts +3 -3
  45. package/src/dumpsys.ts +6 -6
  46. package/src/logcat.ts +26 -15
  47. package/src/overlay-display.ts +2 -2
  48. package/src/pm.ts +92 -96
  49. package/src/settings.ts +20 -28
  50. package/tsconfig.build.tsbuildinfo +1 -1
package/src/pm.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  // cspell:ignore versioncode
5
5
 
6
6
  import type { Adb } from "@yume-chan/adb";
7
- import { AdbCommandBase, escapeArg } from "@yume-chan/adb";
7
+ import { AdbServiceBase } from "@yume-chan/adb";
8
8
  import type { MaybeConsumable, ReadableStream } from "@yume-chan/stream-extra";
9
9
  import {
10
10
  ConcatStringStream,
@@ -12,7 +12,7 @@ import {
12
12
  TextDecoderStream,
13
13
  } from "@yume-chan/stream-extra";
14
14
 
15
- import { Cmd } from "./cmd.js";
15
+ import { CmdNoneProtocolService } from "./cmd.js";
16
16
  import type { IntentBuilder } from "./intent.js";
17
17
  import type { SingleUserOrAll } from "./utils.js";
18
18
  import { buildArguments } from "./utils.js";
@@ -260,7 +260,7 @@ function buildInstallArguments(
260
260
  options: Partial<PackageManagerInstallOptions> | undefined,
261
261
  ): string[] {
262
262
  const args = buildArguments(
263
- ["pm", command],
263
+ [PackageManager.ServiceName, command],
264
264
  options,
265
265
  PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
266
266
  );
@@ -281,12 +281,15 @@ function buildInstallArguments(
281
281
  return args;
282
282
  }
283
283
 
284
- export class PackageManager extends AdbCommandBase {
285
- #cmd: Cmd;
284
+ export class PackageManager extends AdbServiceBase {
285
+ static ServiceName = "package";
286
+ static CommandName = "pm";
287
+
288
+ #cmd: CmdNoneProtocolService;
286
289
 
287
290
  constructor(adb: Adb) {
288
291
  super(adb);
289
- this.#cmd = new Cmd(adb);
292
+ this.#cmd = new CmdNoneProtocolService(adb, PackageManager.CommandName);
290
293
  }
291
294
 
292
295
  /**
@@ -299,20 +302,39 @@ export class PackageManager extends AdbCommandBase {
299
302
  options?: Partial<PackageManagerInstallOptions>,
300
303
  ): Promise<string> {
301
304
  const args = buildInstallArguments("install", options);
305
+ args[0] = PackageManager.CommandName;
302
306
  // WIP: old version of pm doesn't support multiple apks
303
307
  args.push(...apks);
304
- return await this.adb.subprocess.spawnAndWaitLegacy(args);
308
+
309
+ // Starting from Android 7, `pm` becomes a wrapper to `cmd package`.
310
+ // The benefit of `cmd package` is it starts faster than the old `pm`,
311
+ // because it connects to the already running `system` process,
312
+ // instead of initializing all system components from scratch.
313
+ //
314
+ // But launching `cmd package` directly causes it to not be able to
315
+ // read files in `/data/local/tmp` (and many other places) due to SELinux policies,
316
+ // so installing files must still use `pm`.
317
+ // (the starting executable file decides which SELinux policies to apply)
318
+ const output = await this.adb.subprocess.noneProtocol
319
+ .spawnWaitText(args)
320
+ .then((output) => output.trim());
321
+
322
+ if (output !== "Success") {
323
+ throw new Error(output);
324
+ }
325
+
326
+ return output;
305
327
  }
306
328
 
307
329
  async pushAndInstallStream(
308
330
  stream: ReadableStream<MaybeConsumable<Uint8Array>>,
309
331
  options?: Partial<PackageManagerInstallOptions>,
310
- ): Promise<void> {
311
- const sync = await this.adb.sync();
312
-
332
+ ): Promise<string> {
313
333
  const fileName = Math.random().toString().substring(2);
314
334
  const filePath = `/data/local/tmp/${fileName}.apk`;
315
335
 
336
+ const sync = await this.adb.sync();
337
+
316
338
  try {
317
339
  await sync.write({
318
340
  filename: filePath,
@@ -322,26 +344,8 @@ export class PackageManager extends AdbCommandBase {
322
344
  await sync.dispose();
323
345
  }
324
346
 
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);
335
- args.push(filePath);
336
-
337
347
  try {
338
- const output = await this.adb.subprocess
339
- .spawnAndWaitLegacy(args.map(escapeArg))
340
- .then((output) => output.trim());
341
-
342
- if (output !== "Success") {
343
- throw new Error(output);
344
- }
348
+ return await this.install([filePath], options);
345
349
  } finally {
346
350
  await this.adb.rm(filePath);
347
351
  }
@@ -356,20 +360,17 @@ export class PackageManager extends AdbCommandBase {
356
360
  // It's hard to detect whether `pm` supports streaming install (unless actually trying),
357
361
  // so check for whether `cmd` is supported,
358
362
  // and assume `pm` streaming install support status is same as that.
359
- if (!this.#cmd.supportsCmd) {
363
+ if (!this.#cmd.isSupported) {
360
364
  // Fall back to push file then install
361
365
  await this.pushAndInstallStream(stream, options);
362
366
  return;
363
367
  }
364
368
 
365
369
  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>`
368
- args.shift();
369
370
  args.push("-S", size.toString());
370
- const process = await this.#cmd.spawn(false, "package", ...args);
371
+ const process = await this.#cmd.spawn(args);
371
372
 
372
- const output = process.stdout
373
+ const output = process.output
373
374
  .pipeThrough(new TextDecoderStream())
374
375
  .pipeThrough(new ConcatStringStream())
375
376
  .then((output) => output.trim());
@@ -437,20 +438,11 @@ export class PackageManager extends AdbCommandBase {
437
438
  };
438
439
  }
439
440
 
440
- async #cmdOrSubprocess(args: string[]) {
441
- if (this.#cmd.supportsCmd) {
442
- args.shift();
443
- return await this.#cmd.spawn(false, "package", ...args);
444
- }
445
-
446
- return this.adb.subprocess.spawn(args);
447
- }
448
-
449
441
  async *listPackages(
450
442
  options?: Partial<PackageManagerListPackagesOptions>,
451
443
  ): AsyncGenerator<PackageManagerListPackagesResult, void, void> {
452
444
  const args = buildArguments(
453
- ["pm", "list", "packages"],
445
+ ["package", "list", "packages"],
454
446
  options,
455
447
  PACKAGE_MANAGER_LIST_PACKAGES_OPTIONS_MAP,
456
448
  );
@@ -458,12 +450,9 @@ export class PackageManager extends AdbCommandBase {
458
450
  args.push(options.filter);
459
451
  }
460
452
 
461
- const process = await this.#cmdOrSubprocess(args);
462
- const reader = process.stdout
453
+ const process = await this.#cmd.spawn(args);
454
+ const reader = process.output
463
455
  .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.
467
456
  .pipeThrough(new SplitStringStream("\n"))
468
457
  .getReader();
469
458
  while (true) {
@@ -475,12 +464,27 @@ export class PackageManager extends AdbCommandBase {
475
464
  }
476
465
  }
477
466
 
467
+ async getPackageSources(packageName: string): Promise<string[]> {
468
+ const args = [PackageManager.ServiceName, "-p", packageName];
469
+ const process = await this.#cmd.spawn(args);
470
+ const result: string[] = [];
471
+ for await (const line of process.output
472
+ .pipeThrough(new TextDecoderStream())
473
+ .pipeThrough(new SplitStringStream("\n"))) {
474
+ if (line.startsWith("package:")) {
475
+ result.push(line.substring("package:".length));
476
+ }
477
+ }
478
+
479
+ return result;
480
+ }
481
+
478
482
  async uninstall(
479
483
  packageName: string,
480
484
  options?: Partial<PackageManagerUninstallOptions>,
481
485
  ): Promise<void> {
482
486
  const args = buildArguments(
483
- ["pm", "uninstall"],
487
+ [PackageManager.ServiceName, "uninstall"],
484
488
  options,
485
489
  PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP,
486
490
  );
@@ -489,10 +493,8 @@ export class PackageManager extends AdbCommandBase {
489
493
  args.push(...options.splitNames);
490
494
  }
491
495
 
492
- const process = await this.#cmdOrSubprocess(args);
493
- const output = await process.stdout
494
- .pipeThrough(new TextDecoderStream())
495
- .pipeThrough(new ConcatStringStream())
496
+ const output = await this.#cmd
497
+ .spawnWaitText(args)
496
498
  .then((output) => output.trim());
497
499
  if (output !== "Success") {
498
500
  throw new Error(output);
@@ -503,17 +505,15 @@ export class PackageManager extends AdbCommandBase {
503
505
  options: PackageManagerResolveActivityOptions,
504
506
  ): Promise<string | undefined> {
505
507
  let args = buildArguments(
506
- ["pm", "resolve-activity", "--components"],
508
+ [PackageManager.ServiceName, "resolve-activity", "--components"],
507
509
  options,
508
510
  PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP,
509
511
  );
510
512
 
511
513
  args = args.concat(options.intent.build());
512
514
 
513
- const process = await this.#cmdOrSubprocess(args);
514
- const output = await process.stdout
515
- .pipeThrough(new TextDecoderStream())
516
- .pipeThrough(new ConcatStringStream())
515
+ const output = await this.#cmd
516
+ .spawnWaitText(args)
517
517
  .then((output) => output.trim());
518
518
 
519
519
  if (output === "No activity found") {
@@ -538,10 +538,8 @@ export class PackageManager extends AdbCommandBase {
538
538
  ): Promise<number> {
539
539
  const args = buildInstallArguments("install-create", options);
540
540
 
541
- const process = await this.#cmdOrSubprocess(args);
542
- const output = await process.stdout
543
- .pipeThrough(new TextDecoderStream())
544
- .pipeThrough(new ConcatStringStream())
541
+ const output = await this.#cmd
542
+ .spawnWaitText(args)
545
543
  .then((output) => output.trim());
546
544
 
547
545
  const sessionIdString = output.match(/.*\[(\d+)\].*/);
@@ -552,6 +550,17 @@ export class PackageManager extends AdbCommandBase {
552
550
  return Number.parseInt(sessionIdString[1]!, 10);
553
551
  }
554
552
 
553
+ async checkResult(stream: ReadableStream<Uint8Array>) {
554
+ const output = await stream
555
+ .pipeThrough(new TextDecoderStream())
556
+ .pipeThrough(new ConcatStringStream())
557
+ .then((output) => output.trim());
558
+
559
+ if (!output.startsWith("Success")) {
560
+ throw new Error(output);
561
+ }
562
+ }
563
+
555
564
  async sessionAddSplit(
556
565
  sessionId: number,
557
566
  splitName: string,
@@ -565,12 +574,8 @@ export class PackageManager extends AdbCommandBase {
565
574
  path,
566
575
  ];
567
576
 
568
- const output = await this.adb.subprocess
569
- .spawnAndWaitLegacy(args)
570
- .then((output) => output.trim());
571
- if (!output.startsWith("Success")) {
572
- throw new Error(output);
573
- }
577
+ const process = await this.adb.subprocess.noneProtocol.spawn(args);
578
+ await this.checkResult(process.output);
574
579
  }
575
580
 
576
581
  async sessionAddSplitStream(
@@ -580,7 +585,7 @@ export class PackageManager extends AdbCommandBase {
580
585
  stream: ReadableStream<MaybeConsumable<Uint8Array>>,
581
586
  ): Promise<void> {
582
587
  const args: string[] = [
583
- "pm",
588
+ PackageManager.ServiceName,
584
589
  "install-write",
585
590
  "-S",
586
591
  size.toString(),
@@ -589,40 +594,31 @@ export class PackageManager extends AdbCommandBase {
589
594
  "-",
590
595
  ];
591
596
 
592
- const process = await this.#cmdOrSubprocess(args);
593
- const output = process.stdout
594
- .pipeThrough(new TextDecoderStream())
595
- .pipeThrough(new ConcatStringStream())
596
- .then((output) => output.trim());
597
-
597
+ const process = await this.#cmd.spawn(args);
598
598
  await Promise.all([
599
599
  stream.pipeTo(process.stdin),
600
- output.then((output) => {
601
- if (!output.startsWith("Success")) {
602
- throw new Error(output);
603
- }
604
- }),
600
+ this.checkResult(process.output),
605
601
  ]);
606
602
  }
607
603
 
608
604
  async sessionCommit(sessionId: number): Promise<void> {
609
- const args: string[] = ["pm", "install-commit", sessionId.toString()];
610
- const output = await this.adb.subprocess
611
- .spawnAndWaitLegacy(args)
612
- .then((output) => output.trim());
613
- if (output !== "Success") {
614
- throw new Error(output);
615
- }
605
+ const args: string[] = [
606
+ PackageManager.ServiceName,
607
+ "install-commit",
608
+ sessionId.toString(),
609
+ ];
610
+ const process = await this.#cmd.spawn(args);
611
+ await this.checkResult(process.output);
616
612
  }
617
613
 
618
614
  async sessionAbandon(sessionId: number): Promise<void> {
619
- const args: string[] = ["pm", "install-abandon", sessionId.toString()];
620
- const output = await this.adb.subprocess
621
- .spawnAndWaitLegacy(args)
622
- .then((output) => output.trim());
623
- if (output !== "Success") {
624
- throw new Error(output);
625
- }
615
+ const args: string[] = [
616
+ PackageManager.ServiceName,
617
+ "install-abandon",
618
+ sessionId.toString(),
619
+ ];
620
+ const process = await this.#cmd.spawn(args);
621
+ await this.checkResult(process.output);
626
622
  }
627
623
  }
628
624
 
package/src/settings.ts CHANGED
@@ -1,16 +1,19 @@
1
- import type { Adb, AdbSubprocessWaitResult } from "@yume-chan/adb";
2
- import { AdbCommandBase } from "@yume-chan/adb";
1
+ import type { Adb } from "@yume-chan/adb";
2
+ import { AdbServiceBase } from "@yume-chan/adb";
3
3
 
4
- import { Cmd } from "./cmd.js";
4
+ import { CmdNoneProtocolService } from "./cmd.js";
5
5
  import type { SingleUser } from "./utils.js";
6
6
 
7
7
  export type SettingsNamespace = "system" | "secure" | "global";
8
8
 
9
- export enum SettingsResetMode {
10
- UntrustedDefaults = "untrusted_defaults",
11
- UntrustedClear = "untrusted_clear",
12
- TrustedDefaults = "trusted_defaults",
13
- }
9
+ export const SettingsResetMode = {
10
+ UntrustedDefaults: "untrusted_defaults",
11
+ UntrustedClear: "untrusted_clear",
12
+ TrustedDefaults: "trusted_defaults",
13
+ } as const;
14
+
15
+ export type SettingsResetMode =
16
+ (typeof SettingsResetMode)[keyof typeof SettingsResetMode];
14
17
 
15
18
  export interface SettingsOptions {
16
19
  user?: SingleUser;
@@ -22,21 +25,24 @@ export interface SettingsPutOptions extends SettingsOptions {
22
25
  }
23
26
 
24
27
  // frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
25
- export class Settings extends AdbCommandBase {
26
- #cmd: Cmd;
28
+ export class Settings extends AdbServiceBase {
29
+ static ServiceName = "settings";
30
+ static CommandName = "settings";
31
+
32
+ #cmd: CmdNoneProtocolService;
27
33
 
28
34
  constructor(adb: Adb) {
29
35
  super(adb);
30
- this.#cmd = new Cmd(adb);
36
+ this.#cmd = new CmdNoneProtocolService(adb, Settings.CommandName);
31
37
  }
32
38
 
33
- async base(
39
+ base(
34
40
  verb: string,
35
41
  namespace: SettingsNamespace,
36
42
  options: SettingsOptions | undefined,
37
43
  ...args: string[]
38
44
  ): Promise<string> {
39
- let command = ["settings"];
45
+ let command = [Settings.ServiceName];
40
46
 
41
47
  if (options?.user !== undefined) {
42
48
  command.push("--user", options.user.toString());
@@ -45,21 +51,7 @@ export class Settings extends AdbCommandBase {
45
51
  command.push(verb, namespace);
46
52
  command = command.concat(args);
47
53
 
48
- let output: AdbSubprocessWaitResult;
49
- if (this.#cmd.supportsCmd) {
50
- output = await this.#cmd.spawnAndWait(
51
- command[0]!,
52
- ...command.slice(1),
53
- );
54
- } else {
55
- output = await this.adb.subprocess.spawnAndWait(command);
56
- }
57
-
58
- if (output.stderr) {
59
- throw new Error(output.stderr);
60
- }
61
-
62
- return output.stdout;
54
+ return this.#cmd.spawnWaitText(command);
63
55
  }
64
56
 
65
57
  async get(