@yume-chan/android-bin 2.1.0 → 3.0.0-beta.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 (96) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +4 -1
  3. package/esm/am.d.ts +20 -15
  4. package/esm/am.d.ts.map +1 -1
  5. package/esm/am.js +60 -13
  6. package/esm/am.js.map +1 -1
  7. package/esm/bu.d.ts +22 -18
  8. package/esm/bu.d.ts.map +1 -1
  9. package/esm/bu.js +10 -4
  10. package/esm/bu.js.map +1 -1
  11. package/esm/bug-report.d.ts +21 -20
  12. package/esm/bug-report.d.ts.map +1 -1
  13. package/esm/bug-report.js +15 -21
  14. package/esm/bug-report.js.map +1 -1
  15. package/esm/cmd/index.d.ts +2 -0
  16. package/esm/cmd/index.d.ts.map +1 -0
  17. package/esm/cmd/index.js +2 -0
  18. package/esm/cmd/index.js.map +1 -0
  19. package/esm/cmd/none.d.ts +5 -0
  20. package/esm/cmd/none.d.ts.map +1 -0
  21. package/esm/cmd/none.js +39 -0
  22. package/esm/cmd/none.js.map +1 -0
  23. package/esm/cmd/service.d.ts +29 -0
  24. package/esm/cmd/service.d.ts.map +1 -0
  25. package/esm/cmd/service.js +24 -0
  26. package/esm/cmd/service.js.map +1 -0
  27. package/esm/cmd/shell.d.ts +5 -0
  28. package/esm/cmd/shell.d.ts.map +1 -0
  29. package/esm/cmd/shell.js +43 -0
  30. package/esm/cmd/shell.js.map +1 -0
  31. package/esm/cmd/utils.d.ts +5 -0
  32. package/esm/cmd/utils.d.ts.map +1 -0
  33. package/esm/cmd/utils.js +24 -0
  34. package/esm/cmd/utils.js.map +1 -0
  35. package/esm/demo-mode.d.ts +3 -4
  36. package/esm/demo-mode.d.ts.map +1 -1
  37. package/esm/demo-mode.js +13 -21
  38. package/esm/demo-mode.js.map +1 -1
  39. package/esm/dumpsys.d.ts +4 -2
  40. package/esm/dumpsys.d.ts.map +1 -1
  41. package/esm/dumpsys.js +13 -10
  42. package/esm/dumpsys.js.map +1 -1
  43. package/esm/index.d.ts +1 -1
  44. package/esm/index.d.ts.map +1 -1
  45. package/esm/index.js +1 -1
  46. package/esm/index.js.map +1 -1
  47. package/esm/intent.d.ts +37 -9
  48. package/esm/intent.d.ts.map +1 -1
  49. package/esm/intent.js +116 -53
  50. package/esm/intent.js.map +1 -1
  51. package/esm/logcat.d.ts +4 -2
  52. package/esm/logcat.d.ts.map +1 -1
  53. package/esm/logcat.js +9 -6
  54. package/esm/logcat.js.map +1 -1
  55. package/esm/overlay-display.d.ts +17 -14
  56. package/esm/overlay-display.d.ts.map +1 -1
  57. package/esm/overlay-display.js +2 -4
  58. package/esm/overlay-display.js.map +1 -1
  59. package/esm/pm.d.ts +131 -168
  60. package/esm/pm.d.ts.map +1 -1
  61. package/esm/pm.js +229 -112
  62. package/esm/pm.js.map +1 -1
  63. package/esm/settings.d.ts +1 -2
  64. package/esm/settings.d.ts.map +1 -1
  65. package/esm/settings.js +9 -9
  66. package/esm/settings.js.map +1 -1
  67. package/esm/utils.d.ts +4 -1
  68. package/esm/utils.d.ts.map +1 -1
  69. package/esm/utils.js +24 -13
  70. package/esm/utils.js.map +1 -1
  71. package/package.json +9 -9
  72. package/src/am.ts +94 -34
  73. package/src/bu.ts +31 -20
  74. package/src/bug-report.ts +42 -42
  75. package/src/cmd/index.ts +1 -0
  76. package/src/cmd/none.ts +62 -0
  77. package/src/cmd/service.ts +54 -0
  78. package/src/cmd/shell.ts +67 -0
  79. package/src/cmd/utils.ts +35 -0
  80. package/src/demo-mode.ts +13 -24
  81. package/src/dumpsys.ts +16 -10
  82. package/src/index.ts +1 -1
  83. package/src/intent.ts +187 -49
  84. package/src/logcat.ts +12 -6
  85. package/src/overlay-display.ts +21 -19
  86. package/src/pm.ts +374 -301
  87. package/src/settings.ts +11 -10
  88. package/src/utils.ts +30 -13
  89. package/tsconfig.build.json +0 -11
  90. package/CHANGELOG.md +0 -181
  91. package/esm/cmd.d.ts +0 -23
  92. package/esm/cmd.d.ts.map +0 -1
  93. package/esm/cmd.js +0 -104
  94. package/esm/cmd.js.map +0 -1
  95. package/src/cmd.ts +0 -166
  96. package/tsconfig.build.tsbuildinfo +0 -1
@@ -0,0 +1,67 @@
1
+ import type { Adb } from "@yume-chan/adb";
2
+ import {
3
+ AdbFeature,
4
+ AdbShellProtocolProcessImpl,
5
+ adbShellProtocolSpawner,
6
+ escapeArg,
7
+ } from "@yume-chan/adb";
8
+
9
+ import { Cmd } from "./service.js";
10
+ import { checkCommand, resolveFallback, serializeAbbService } from "./utils.js";
11
+
12
+ export function createShellProtocol(
13
+ adb: Adb,
14
+ fallback: NonNullable<Cmd.Fallback>,
15
+ ): Cmd.ShellProtocolService;
16
+ export function createShellProtocol(
17
+ adb: Adb,
18
+ fallback?: Cmd.Fallback,
19
+ ): Cmd.ShellProtocolService | undefined;
20
+ export function createShellProtocol(
21
+ adb: Adb,
22
+ fallback?: Cmd.Fallback,
23
+ ): Cmd.ShellProtocolService | undefined {
24
+ if (adb.canUseFeature(AdbFeature.Abb)) {
25
+ return {
26
+ mode: Cmd.Mode.Abb,
27
+ spawn: adbShellProtocolSpawner(async (command, signal) => {
28
+ const service = serializeAbbService("abb", command);
29
+ const socket = await adb.createSocket(service);
30
+ return new AdbShellProtocolProcessImpl(socket, signal);
31
+ }),
32
+ };
33
+ }
34
+
35
+ const shellProtocolService = adb.subprocess.shellProtocol;
36
+ if (!shellProtocolService) {
37
+ return undefined;
38
+ }
39
+
40
+ if (adb.canUseFeature(AdbFeature.Cmd)) {
41
+ return {
42
+ mode: Cmd.Mode.Cmd,
43
+ spawn: adbShellProtocolSpawner(async (command, signal) => {
44
+ checkCommand(command);
45
+
46
+ const newCommand = command.map(escapeArg);
47
+ newCommand.unshift("cmd");
48
+ return shellProtocolService.spawn(newCommand, signal);
49
+ }),
50
+ };
51
+ }
52
+
53
+ if (fallback) {
54
+ return {
55
+ mode: Cmd.Mode.Fallback,
56
+ spawn: adbShellProtocolSpawner(async (command, signal) => {
57
+ checkCommand(command);
58
+
59
+ const newCommand = command.map(escapeArg);
60
+ newCommand[0] = resolveFallback(fallback, command[0]!);
61
+ return shellProtocolService.spawn(newCommand, signal);
62
+ }),
63
+ };
64
+ }
65
+
66
+ return undefined;
67
+ }
@@ -0,0 +1,35 @@
1
+ import type { Cmd } from "./service.js";
2
+
3
+ export function resolveFallback(
4
+ fallback: Cmd.Fallback,
5
+ command: string,
6
+ ): string {
7
+ if (typeof fallback === "function") {
8
+ fallback = fallback(command);
9
+ } else if (typeof fallback === "object" && fallback !== null) {
10
+ fallback = fallback[command];
11
+ }
12
+
13
+ if (!fallback) {
14
+ throw new Error(`No fallback configured for command "${command}"`);
15
+ }
16
+
17
+ return fallback;
18
+ }
19
+
20
+ export function checkCommand(command: readonly string[]) {
21
+ if (!command.length) {
22
+ throw new TypeError("Command is empty");
23
+ }
24
+ }
25
+
26
+ export function serializeAbbService(
27
+ prefix: string,
28
+ command: readonly string[],
29
+ ): string {
30
+ checkCommand(command);
31
+
32
+ // `abb` mode uses `\0` as the separator, allowing space in arguments.
33
+ // The last `\0` is required for older versions of `adb`.
34
+ return `${prefix}:${command.join("\0")}\0`;
35
+ }
package/src/demo-mode.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  // cspell: ignore sysui
6
6
 
7
7
  import type { Adb } from "@yume-chan/adb";
8
- import { AdbServiceBase } from "@yume-chan/adb";
9
8
 
9
+ import { ActivityManager } from "./am.js";
10
10
  import { Settings } from "./settings.js";
11
11
 
12
12
  export enum DemoModeSignalStrength {
@@ -51,11 +51,12 @@ export const DemoModeStatusBarModes = [
51
51
 
52
52
  export type DemoModeStatusBarMode = (typeof DemoModeStatusBarModes)[number];
53
53
 
54
- export class DemoMode extends AdbServiceBase {
54
+ export class DemoMode {
55
+ #am: ActivityManager;
55
56
  #settings: Settings;
56
57
 
57
- constructor(adb: Adb) {
58
- super(adb);
58
+ constructor(adb: Adb, apiLevel?: number) {
59
+ this.#am = new ActivityManager(adb, apiLevel);
59
60
  this.#settings = new Settings(adb);
60
61
  }
61
62
 
@@ -108,26 +109,14 @@ export class DemoMode extends AdbServiceBase {
108
109
  }
109
110
  }
110
111
 
111
- async broadcast(
112
- command: string,
113
- extra?: Record<string, string>,
114
- ): Promise<void> {
115
- await this.adb.subprocess.noneProtocol.spawnWaitText([
116
- "am",
117
- "broadcast",
118
- "-a",
119
- "com.android.systemui.demo",
120
- "-e",
121
- "command",
122
- command,
123
- ...(extra
124
- ? Object.entries(extra).flatMap(([key, value]) => [
125
- "-e",
126
- key,
127
- value,
128
- ])
129
- : []),
130
- ]);
112
+ broadcast(command: string, extras?: Record<string, string>): Promise<void> {
113
+ return this.#am.broadcast({
114
+ action: "com.android.systemui.demo",
115
+ extras: {
116
+ command,
117
+ ...extras,
118
+ },
119
+ });
131
120
  }
132
121
 
133
122
  async setBatteryLevel(level: number): Promise<void> {
package/src/dumpsys.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AdbServiceBase } from "@yume-chan/adb";
1
+ import type { Adb } from "@yume-chan/adb";
2
2
 
3
3
  const BatteryDumpFields: Record<
4
4
  string,
@@ -54,14 +54,20 @@ const Battery = {
54
54
  Health,
55
55
  };
56
56
 
57
- export class DumpSys extends AdbServiceBase {
57
+ export class DumpSys {
58
58
  static readonly Battery = Battery;
59
59
 
60
+ readonly #adb: Adb;
61
+
62
+ constructor(adb: Adb) {
63
+ this.#adb = adb;
64
+ }
65
+
60
66
  async diskStats() {
61
- const result = await this.adb.subprocess.noneProtocol.spawnWaitText([
62
- "dumpsys",
63
- "diskstats",
64
- ]);
67
+ const result = await this.#adb.subprocess.noneProtocol
68
+ .spawn(["dumpsys", "diskstats"])
69
+ .wait()
70
+ .toString();
65
71
 
66
72
  function getSize(name: string) {
67
73
  const match = result.match(
@@ -91,10 +97,10 @@ export class DumpSys extends AdbServiceBase {
91
97
  }
92
98
 
93
99
  async battery(): Promise<DumpSys.Battery.Info> {
94
- const result = await this.adb.subprocess.noneProtocol.spawnWaitText([
95
- "dumpsys",
96
- "battery",
97
- ]);
100
+ const result = await this.#adb.subprocess.noneProtocol
101
+ .spawn(["dumpsys", "battery"])
102
+ .wait()
103
+ .toString();
98
104
 
99
105
  const info: DumpSys.Battery.Info = {
100
106
  acPowered: false,
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  export * from "./am.js";
4
4
  export * from "./bu.js";
5
5
  export * from "./bug-report.js";
6
- export * from "./cmd.js";
6
+ export * from "./cmd/index.js";
7
7
  export * from "./demo-mode.js";
8
8
  export * from "./dumpsys.js";
9
9
  export * from "./intent.js";
package/src/intent.ts CHANGED
@@ -1,73 +1,211 @@
1
- export class IntentBuilder {
2
- #action: string | undefined;
3
- #categories: string[] = [];
4
- #packageName: string | undefined;
5
- #component: string | undefined;
6
- #data: string | undefined;
7
- #type: string | undefined;
8
- #stringExtras = new Map<string, string>();
9
-
10
- setAction(action: string): this {
11
- this.#action = action;
12
- return this;
13
- }
1
+ // cspell: ignore eial
2
+ // cspell: ignore elal
3
+ // cspell: ignore efal
4
+ // cspell: ignore esal
5
+ // cspell: ignore edal
6
+
7
+ export interface ComponentName {
8
+ packageName: string;
9
+ className: string;
10
+ }
11
+
12
+ export interface Intent {
13
+ action?: string | undefined;
14
+ data?: string | undefined;
15
+ type?: string | undefined;
16
+ identifier?: string | undefined;
17
+ categories?: string[] | undefined;
18
+ extras?:
19
+ | Record<
20
+ string,
21
+ | string
22
+ | null
23
+ | number
24
+ | bigint
25
+ | Intent.Extra.String
26
+ | ComponentName
27
+ | Intent.Extra.NumberArray
28
+ | Intent.Extra.StringArray
29
+ | Intent.Extra.Number
30
+ | boolean
31
+ >
32
+ | undefined;
33
+ flags?: number | undefined;
34
+ package?: string | undefined;
35
+ component?: ComponentName | undefined;
36
+ }
37
+
38
+ export namespace Intent {
39
+ export namespace Extra {
40
+ export interface Number {
41
+ type: "long" | "float" | "double";
42
+ value: number;
43
+ }
14
44
 
15
- addCategory(category: string): this {
16
- this.#categories.push(category);
17
- return this;
45
+ export interface String {
46
+ type: "uri";
47
+ value: string;
48
+ }
49
+
50
+ export interface NumberArray {
51
+ type: "array" | "arrayList";
52
+ itemType: "int" | Number["type"];
53
+ value: number[];
54
+ }
55
+
56
+ export interface StringArray {
57
+ type: "array" | "arrayList";
58
+ itemType: "string";
59
+ value: string[];
60
+ }
18
61
  }
62
+ }
19
63
 
20
- setPackage(packageName: string): this {
21
- this.#packageName = packageName;
22
- return this;
64
+ function getNumberType(type: "int" | Intent.Extra.Number["type"]) {
65
+ switch (type) {
66
+ case "int":
67
+ return "--ei";
68
+ case "long":
69
+ return "--el";
70
+ case "float":
71
+ return "--ef";
72
+ case "double":
73
+ return "--ed";
74
+ default:
75
+ throw new Error(`Unknown number type: ${type as string}`);
23
76
  }
77
+ }
24
78
 
25
- setComponent(component: string): this {
26
- this.#component = component;
27
- return this;
79
+ function serializeExtraArray(
80
+ array: Intent.Extra.NumberArray | Intent.Extra.StringArray,
81
+ ): [type: string, value: string] {
82
+ let type: string;
83
+ let value: string;
84
+
85
+ if (array.itemType === "string") {
86
+ type = "--es";
87
+ value = array.value
88
+ .map((item) => item.replaceAll(",", "\\,"))
89
+ .join(",");
90
+ } else {
91
+ type = getNumberType(array.itemType);
92
+ value = array.value.join(",");
28
93
  }
29
94
 
30
- setData(data: string): this {
31
- this.#data = data;
32
- return this;
95
+ if (array.type === "array") {
96
+ type += "a";
97
+ } else {
98
+ type += "al";
33
99
  }
34
100
 
35
- addStringExtra(key: string, value: string): this {
36
- this.#stringExtras.set(key, value);
37
- return this;
101
+ return [type, value];
102
+ }
103
+
104
+ function serializeString(
105
+ result: string[],
106
+ key: string,
107
+ value: string | undefined,
108
+ ) {
109
+ // Treat empty string as not set
110
+ if (value) {
111
+ result.push(key, value);
38
112
  }
113
+ }
39
114
 
40
- build(): string[] {
41
- const result: string[] = [];
115
+ export function serializeIntent(intent: Intent) {
116
+ const result: string[] = [];
42
117
 
43
- if (this.#action) {
44
- result.push("-a", this.#action);
45
- }
118
+ serializeString(result, "-a", intent.action);
119
+ serializeString(result, "-d", intent.data);
120
+ serializeString(result, "-t", intent.type);
121
+ serializeString(result, "-i", intent.identifier);
46
122
 
47
- for (const category of this.#categories) {
123
+ if (intent.categories) {
124
+ for (const category of intent.categories) {
48
125
  result.push("-c", category);
49
126
  }
127
+ }
50
128
 
51
- if (this.#packageName) {
52
- result.push("-p", this.#packageName);
53
- }
129
+ if (intent.extras) {
130
+ for (const [key, value] of Object.entries(intent.extras)) {
131
+ switch (typeof value) {
132
+ case "string":
133
+ result.push("--es", key, value);
134
+ break;
135
+ case "object":
136
+ if (value === null) {
137
+ result.push("--esn", key);
138
+ break;
139
+ }
54
140
 
55
- if (this.#component) {
56
- result.push("-n", this.#component);
57
- }
141
+ // `ComponentName`
142
+ if ("packageName" in value) {
143
+ result.push(
144
+ "--ecn",
145
+ key,
146
+ value.packageName + "/" + value.className,
147
+ );
148
+ break;
149
+ }
58
150
 
59
- if (this.#data) {
60
- result.push("-d", this.#data);
151
+ switch (value.type) {
152
+ case "uri":
153
+ result.push("--eu", key, value.value);
154
+ break;
155
+ case "array":
156
+ case "arrayList":
157
+ {
158
+ const [type, valueString] =
159
+ serializeExtraArray(value);
160
+ result.push(type, key, valueString);
161
+ }
162
+ break;
163
+ default:
164
+ result.push(
165
+ getNumberType(value.type),
166
+ key,
167
+ value.value.toString(),
168
+ );
169
+ break;
170
+ }
171
+ break;
172
+ case "number":
173
+ if (value % 1 !== 0) {
174
+ throw new Error(
175
+ `Extra \`${key}\` is not an integer, must use \`Intent.Extra.Number\` type instead`,
176
+ );
177
+ }
178
+ // Infer type from value
179
+ if (value < -2147483648 || value > 2147483647) {
180
+ result.push("--el", key, value.toString());
181
+ } else {
182
+ result.push("--ei", key, value.toString());
183
+ }
184
+ break;
185
+ case "bigint":
186
+ result.push("--el", key, value.toString());
187
+ break;
188
+ case "boolean":
189
+ result.push("--ez", key, value ? "true" : "false");
190
+ break;
191
+ }
61
192
  }
193
+ }
62
194
 
63
- if (this.#type) {
64
- result.push("-t", this.#type);
65
- }
195
+ if (intent.component) {
196
+ result.push(
197
+ "-n",
198
+ intent.component.packageName + "/" + intent.component.className,
199
+ );
200
+ }
66
201
 
67
- for (const [key, value] of this.#stringExtras) {
68
- result.push("--es", key, value);
69
- }
202
+ serializeString(result, "-p", intent.package);
70
203
 
71
- return result;
204
+ // `0` is the default value for `flags` when deserializing
205
+ // so it can be omitted if it's either `undefined` or `0`
206
+ if (intent.flags) {
207
+ result.push("-f", intent.flags.toString());
72
208
  }
209
+
210
+ return result;
73
211
  }
package/src/logcat.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // cspell: ignore logcat
2
2
  // cspell: ignore usec
3
3
 
4
- import { AdbServiceBase } from "@yume-chan/adb";
4
+ import type { Adb } from "@yume-chan/adb";
5
5
  import type { ReadableStream } from "@yume-chan/stream-extra";
6
6
  import {
7
7
  BufferedTransformStream,
@@ -405,7 +405,7 @@ export interface LogSize {
405
405
  maxPayloadSize: number;
406
406
  }
407
407
 
408
- export class Logcat extends AdbServiceBase {
408
+ export class Logcat {
409
409
  static logIdToName(id: LogId): string {
410
410
  return LogIdName[id]!;
411
411
  }
@@ -434,8 +434,14 @@ export class Logcat extends AdbServiceBase {
434
434
  static readonly LOG_SIZE_REGEX_11: RegExp =
435
435
  /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
436
436
 
437
+ readonly #adb: Adb;
438
+
439
+ constructor(adb: Adb) {
440
+ this.#adb = adb;
441
+ }
442
+
437
443
  async getLogSize(ids?: readonly LogId[]): Promise<LogSize[]> {
438
- const process = await this.adb.subprocess.noneProtocol.spawn([
444
+ const process = await this.#adb.subprocess.noneProtocol.spawn([
439
445
  "logcat",
440
446
  "-g",
441
447
  ...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
@@ -444,7 +450,7 @@ export class Logcat extends AdbServiceBase {
444
450
  const result: LogSize[] = [];
445
451
  for await (const line of process.output
446
452
  .pipeThrough(new TextDecoderStream())
447
- .pipeThrough(new SplitStringStream("\n"))) {
453
+ .pipeThrough(new SplitStringStream("\n", { trim: true }))) {
448
454
  let match = line.match(Logcat.LOG_SIZE_REGEX_11);
449
455
  if (match) {
450
456
  result.push({
@@ -494,7 +500,7 @@ export class Logcat extends AdbServiceBase {
494
500
  args.push("-b", Logcat.joinLogId(ids));
495
501
  }
496
502
 
497
- await this.adb.subprocess.noneProtocol.spawnWaitText(args);
503
+ await this.#adb.subprocess.noneProtocol.spawn(args).wait();
498
504
  }
499
505
 
500
506
  binary(options?: LogcatOptions): ReadableStream<AndroidLogEntry> {
@@ -520,7 +526,7 @@ export class Logcat extends AdbServiceBase {
520
526
 
521
527
  // TODO: make `spawn` return synchronously with streams pending
522
528
  // so it's easier to chain them.
523
- const process = await this.adb.subprocess.noneProtocol.spawn(args);
529
+ const process = await this.#adb.subprocess.noneProtocol.spawn(args);
524
530
  return process.output;
525
531
  }).pipeThrough(
526
532
  new BufferedTransformStream((stream) => {
@@ -1,25 +1,9 @@
1
1
  import type { Adb } from "@yume-chan/adb";
2
- import { AdbServiceBase } from "@yume-chan/adb";
3
2
 
4
3
  import { Settings } from "./settings.js";
5
4
  import { p } from "./string-format.js";
6
5
 
7
- export interface OverlayDisplayDeviceMode {
8
- width: number;
9
- height: number;
10
- density: number;
11
- }
12
-
13
- export interface OverlayDisplayDevice {
14
- modes: OverlayDisplayDeviceMode[];
15
- secure: boolean;
16
- ownContentOnly: boolean;
17
- showSystemDecorations: boolean;
18
- }
19
-
20
- export class OverlayDisplay extends AdbServiceBase {
21
- #settings: Settings;
22
-
6
+ export class OverlayDisplay {
23
7
  static readonly SETTING_KEY = "overlay_display_devices";
24
8
 
25
9
  static readonly SETTING_FORMAT = p.separated(
@@ -59,8 +43,9 @@ export class OverlayDisplay extends AdbServiceBase {
59
43
  ),
60
44
  );
61
45
 
46
+ readonly #settings: Settings;
47
+
62
48
  constructor(adb: Adb) {
63
- super(adb);
64
49
  this.#settings = new Settings(adb);
65
50
  }
66
51
 
@@ -81,7 +66,7 @@ export class OverlayDisplay extends AdbServiceBase {
81
66
  }));
82
67
  }
83
68
 
84
- async set(devices: readonly OverlayDisplayDevice[]): Promise<void> {
69
+ async set(devices: readonly OverlayDisplay.Device[]): Promise<void> {
85
70
  await this.#settings.put(
86
71
  "global",
87
72
  OverlayDisplay.SETTING_KEY,
@@ -110,3 +95,20 @@ export class OverlayDisplay extends AdbServiceBase {
110
95
  );
111
96
  }
112
97
  }
98
+
99
+ export namespace OverlayDisplay {
100
+ export interface Device {
101
+ modes: Device.Mode[];
102
+ secure: boolean;
103
+ ownContentOnly: boolean;
104
+ showSystemDecorations: boolean;
105
+ }
106
+
107
+ export namespace Device {
108
+ export interface Mode {
109
+ width: number;
110
+ height: number;
111
+ density: number;
112
+ }
113
+ }
114
+ }