@yume-chan/android-bin 0.0.20 → 0.0.22
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 +54 -0
- package/CHANGELOG.md +25 -1
- package/LICENSE +21 -0
- package/README.md +40 -14
- package/esm/am.d.ts +17 -0
- package/esm/am.d.ts.map +1 -0
- package/esm/am.js +42 -0
- package/esm/am.js.map +1 -0
- package/esm/bu.d.ts +20 -7
- package/esm/bu.d.ts.map +1 -1
- package/esm/bu.js +47 -8
- package/esm/bu.js.map +1 -1
- package/esm/bug-report.d.ts +82 -21
- package/esm/bug-report.d.ts.map +1 -1
- package/esm/bug-report.js +187 -53
- package/esm/bug-report.js.map +1 -1
- package/esm/cmd.d.ts +4 -6
- package/esm/cmd.d.ts.map +1 -1
- package/esm/cmd.js +36 -21
- package/esm/cmd.js.map +1 -1
- package/esm/demo-mode.d.ts +3 -3
- package/esm/demo-mode.d.ts.map +1 -1
- package/esm/demo-mode.js +14 -15
- package/esm/demo-mode.js.map +1 -1
- package/esm/dumpsys.d.ts +39 -0
- package/esm/dumpsys.d.ts.map +1 -1
- package/esm/dumpsys.js +111 -0
- package/esm/dumpsys.js.map +1 -1
- package/esm/index.d.ts +6 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +6 -0
- package/esm/index.js.map +1 -1
- package/esm/intent.d.ts +10 -0
- package/esm/intent.d.ts.map +1 -0
- package/esm/intent.js +51 -0
- package/esm/intent.js.map +1 -0
- package/esm/logcat.d.ts +1 -0
- package/esm/logcat.d.ts.map +1 -1
- package/esm/logcat.js +20 -18
- package/esm/logcat.js.map +1 -1
- package/esm/overlay-display.d.ts +20 -3
- package/esm/overlay-display.d.ts.map +1 -1
- package/esm/overlay-display.js +34 -61
- package/esm/overlay-display.js.map +1 -1
- package/esm/pm.d.ts +42 -5
- package/esm/pm.d.ts.map +1 -1
- package/esm/pm.js +164 -46
- package/esm/pm.js.map +1 -1
- package/esm/settings.d.ts +22 -8
- package/esm/settings.d.ts.map +1 -1
- package/esm/settings.js +50 -25
- package/esm/settings.js.map +1 -1
- package/esm/string-format.d.ts +40 -0
- package/esm/string-format.d.ts.map +1 -0
- package/esm/string-format.js +153 -0
- package/esm/string-format.js.map +1 -0
- package/esm/utils.d.ts +4 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +23 -0
- package/esm/utils.js.map +1 -0
- package/package.json +13 -8
- package/src/am.ts +69 -0
- package/src/bu.ts +68 -14
- package/src/bug-report.ts +234 -68
- package/src/cmd.ts +64 -30
- package/src/demo-mode.ts +47 -43
- package/src/dumpsys.ts +129 -0
- package/src/index.ts +6 -0
- package/src/intent.ts +63 -0
- package/src/logcat.ts +45 -42
- package/src/overlay-display.ts +82 -85
- package/src/pm.ts +274 -61
- package/src/settings.ts +94 -43
- package/src/string-format.ts +199 -0
- package/src/utils.ts +29 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/overlay-display.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Adb } from "@yume-chan/adb";
|
|
|
2
2
|
import { AdbCommandBase } from "@yume-chan/adb";
|
|
3
3
|
|
|
4
4
|
import { Settings } from "./settings.js";
|
|
5
|
+
import { p } from "./string-format.js";
|
|
5
6
|
|
|
6
7
|
export interface OverlayDisplayDeviceMode {
|
|
7
8
|
width: number;
|
|
@@ -17,99 +18,95 @@ export interface OverlayDisplayDevice {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export class OverlayDisplay extends AdbCommandBase {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
#settings: Settings;
|
|
22
|
+
|
|
23
|
+
static readonly SETTING_KEY = "overlay_display_devices";
|
|
24
|
+
|
|
25
|
+
static readonly SETTING_FORMAT = p.separated(
|
|
26
|
+
";",
|
|
27
|
+
p.sequence(
|
|
28
|
+
{
|
|
29
|
+
name: "modes",
|
|
30
|
+
format: p.separated(
|
|
31
|
+
"|",
|
|
32
|
+
p.sequence(
|
|
33
|
+
{ name: "width", format: p.digits() },
|
|
34
|
+
p.literal("x"),
|
|
35
|
+
{ name: "height", format: p.digits() },
|
|
36
|
+
p.literal("/"),
|
|
37
|
+
{ name: "density", format: p.digits() },
|
|
38
|
+
),
|
|
39
|
+
1,
|
|
40
|
+
),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "flags",
|
|
44
|
+
format: p.map(
|
|
45
|
+
p.repeated(
|
|
46
|
+
p.sequence(p.literal(","), {
|
|
47
|
+
name: "flag",
|
|
48
|
+
format: p.union(
|
|
49
|
+
p.literal("secure"),
|
|
50
|
+
p.literal("own_content_only"),
|
|
51
|
+
p.literal("show_system_decorations"),
|
|
52
|
+
),
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
(value) => value.map((item) => item.flag),
|
|
56
|
+
(value) => value.map((item) => ({ flag: item })),
|
|
57
|
+
),
|
|
58
|
+
},
|
|
59
|
+
),
|
|
60
|
+
);
|
|
24
61
|
|
|
25
62
|
constructor(adb: Adb) {
|
|
26
63
|
super(adb);
|
|
27
|
-
this
|
|
64
|
+
this.#settings = new Settings(adb);
|
|
28
65
|
}
|
|
29
66
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const device: OverlayDisplayDevice = {
|
|
46
|
-
modes: [],
|
|
47
|
-
secure: false,
|
|
48
|
-
ownContentOnly: false,
|
|
49
|
-
showSystemDecorations: false,
|
|
50
|
-
};
|
|
51
|
-
for (const modeString of modesString.split("|")) {
|
|
52
|
-
const match = modeString.match(/(\d+)x(\d+)\/(\d+)/);
|
|
53
|
-
if (!match) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
device.modes.push({
|
|
57
|
-
width: parseInt(match[1]!, 10),
|
|
58
|
-
height: parseInt(match[2]!, 10),
|
|
59
|
-
density: parseInt(match[3]!, 10),
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
if (device.modes.length === 0) {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
for (const flagString of flagStrings) {
|
|
67
|
-
switch (flagString) {
|
|
68
|
-
case "secure":
|
|
69
|
-
device.secure = true;
|
|
70
|
-
break;
|
|
71
|
-
case "own_content_only":
|
|
72
|
-
device.ownContentOnly = true;
|
|
73
|
-
break;
|
|
74
|
-
case "show_system_decorations":
|
|
75
|
-
device.showSystemDecorations = true;
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
devices.push(device);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return devices;
|
|
67
|
+
async get() {
|
|
68
|
+
return OverlayDisplay.SETTING_FORMAT.parse({
|
|
69
|
+
value: await this.#settings.get(
|
|
70
|
+
"global",
|
|
71
|
+
OverlayDisplay.SETTING_KEY,
|
|
72
|
+
),
|
|
73
|
+
position: 0,
|
|
74
|
+
}).map((device) => ({
|
|
75
|
+
modes: device.modes,
|
|
76
|
+
secure: device.flags.includes("secure"),
|
|
77
|
+
ownContentOnly: device.flags.includes("own_content_only"),
|
|
78
|
+
showSystemDecorations: device.flags.includes(
|
|
79
|
+
"show_system_decorations",
|
|
80
|
+
),
|
|
81
|
+
}));
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (const device of devices) {
|
|
88
|
-
if (settingString) {
|
|
89
|
-
settingString += ";";
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
settingString += device.modes
|
|
93
|
-
.map((mode) => `${mode.width}x${mode.height}/${mode.density}`)
|
|
94
|
-
.join("|");
|
|
95
|
-
|
|
96
|
-
if (device.secure) {
|
|
97
|
-
settingString += ",secure";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (device.ownContentOnly) {
|
|
101
|
-
settingString += ",own_content_only";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (device.showSystemDecorations) {
|
|
105
|
-
settingString += ",show_system_decorations";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
await this.settings.put(
|
|
84
|
+
async set(devices: OverlayDisplayDevice[]) {
|
|
85
|
+
await this.#settings.put(
|
|
110
86
|
"global",
|
|
111
|
-
OverlayDisplay.
|
|
112
|
-
|
|
87
|
+
OverlayDisplay.SETTING_KEY,
|
|
88
|
+
OverlayDisplay.SETTING_FORMAT.stringify(
|
|
89
|
+
devices.map((device) => {
|
|
90
|
+
const flags: (
|
|
91
|
+
| "secure"
|
|
92
|
+
| "own_content_only"
|
|
93
|
+
| "show_system_decorations"
|
|
94
|
+
)[] = [];
|
|
95
|
+
if (device.secure) {
|
|
96
|
+
flags.push("secure");
|
|
97
|
+
}
|
|
98
|
+
if (device.ownContentOnly) {
|
|
99
|
+
flags.push("own_content_only");
|
|
100
|
+
}
|
|
101
|
+
if (device.showSystemDecorations) {
|
|
102
|
+
flags.push("show_system_decorations");
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
modes: device.modes,
|
|
106
|
+
flags,
|
|
107
|
+
};
|
|
108
|
+
}),
|
|
109
|
+
),
|
|
113
110
|
);
|
|
114
111
|
}
|
|
115
112
|
}
|
package/src/pm.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// cspell:ignore dont
|
|
2
2
|
// cspell:ignore instantapp
|
|
3
3
|
// cspell:ignore apks
|
|
4
|
+
// cspell:ignore versioncode
|
|
4
5
|
|
|
5
6
|
import type { Adb } from "@yume-chan/adb";
|
|
6
7
|
import {
|
|
@@ -9,9 +10,16 @@ import {
|
|
|
9
10
|
escapeArg,
|
|
10
11
|
} from "@yume-chan/adb";
|
|
11
12
|
import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ConcatStringStream,
|
|
15
|
+
DecodeUtf8Stream,
|
|
16
|
+
SplitStringStream,
|
|
17
|
+
} from "@yume-chan/stream-extra";
|
|
13
18
|
|
|
14
19
|
import { Cmd } from "./cmd.js";
|
|
20
|
+
import type { IntentBuilder } from "./intent.js";
|
|
21
|
+
import type { SingleUserOrAll } from "./utils.js";
|
|
22
|
+
import { buildArguments } from "./utils.js";
|
|
15
23
|
|
|
16
24
|
export enum PackageManagerInstallLocation {
|
|
17
25
|
Auto,
|
|
@@ -96,7 +104,7 @@ export interface PackageManagerInstallOptions {
|
|
|
96
104
|
/**
|
|
97
105
|
* `--user`
|
|
98
106
|
*/
|
|
99
|
-
|
|
107
|
+
user: SingleUserOrAll;
|
|
100
108
|
/**
|
|
101
109
|
* `--install-location`
|
|
102
110
|
*/
|
|
@@ -163,7 +171,7 @@ export const PACKAGE_MANAGER_INSTALL_OPTIONS_MAP: Record<
|
|
|
163
171
|
instantApp: "--instant",
|
|
164
172
|
full: "--full",
|
|
165
173
|
preload: "--preload",
|
|
166
|
-
|
|
174
|
+
user: "--user",
|
|
167
175
|
installLocation: "--install-location",
|
|
168
176
|
installReason: "--install-reason",
|
|
169
177
|
forceUuid: "--force-uuid",
|
|
@@ -177,56 +185,107 @@ export const PACKAGE_MANAGER_INSTALL_OPTIONS_MAP: Record<
|
|
|
177
185
|
bypassLowTargetSdkBlock: "--bypass-low-target-sdk-block",
|
|
178
186
|
};
|
|
179
187
|
|
|
188
|
+
export interface PackageManagerListPackagesOptions {
|
|
189
|
+
listDisabled: boolean;
|
|
190
|
+
listEnabled: boolean;
|
|
191
|
+
showSourceDir: boolean;
|
|
192
|
+
showInstaller: boolean;
|
|
193
|
+
listSystem: boolean;
|
|
194
|
+
showUid: boolean;
|
|
195
|
+
listThirdParty: boolean;
|
|
196
|
+
showVersionCode: boolean;
|
|
197
|
+
listApexOnly: boolean;
|
|
198
|
+
user: SingleUserOrAll;
|
|
199
|
+
uid: number;
|
|
200
|
+
filter: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const PACKAGE_MANAGER_LIST_PACKAGES_OPTIONS_MAP: Record<
|
|
204
|
+
keyof PackageManagerListPackagesOptions,
|
|
205
|
+
string
|
|
206
|
+
> = {
|
|
207
|
+
listDisabled: "-d",
|
|
208
|
+
listEnabled: "-e",
|
|
209
|
+
showSourceDir: "-f",
|
|
210
|
+
showInstaller: "-i",
|
|
211
|
+
listSystem: "-s",
|
|
212
|
+
showUid: "-U",
|
|
213
|
+
listThirdParty: "-3",
|
|
214
|
+
showVersionCode: "--show-versioncode",
|
|
215
|
+
listApexOnly: "--apex-only",
|
|
216
|
+
user: "--user",
|
|
217
|
+
uid: "--uid",
|
|
218
|
+
filter: "",
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export interface PackageManagerListPackagesResult {
|
|
222
|
+
packageName: string;
|
|
223
|
+
sourceDir?: string | undefined;
|
|
224
|
+
versionCode?: number | undefined;
|
|
225
|
+
installer?: string | undefined;
|
|
226
|
+
uid?: number | undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface PackageManagerUninstallOptions {
|
|
230
|
+
keepData: boolean;
|
|
231
|
+
user: SingleUserOrAll;
|
|
232
|
+
versionCode: number;
|
|
233
|
+
splitNames: string[];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP: Record<
|
|
237
|
+
keyof PackageManagerUninstallOptions,
|
|
238
|
+
string
|
|
239
|
+
> = {
|
|
240
|
+
keepData: "-k",
|
|
241
|
+
user: "--user",
|
|
242
|
+
versionCode: "--versionCode",
|
|
243
|
+
splitNames: "",
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export interface PackageManagerResolveActivityOptions {
|
|
247
|
+
user?: SingleUserOrAll;
|
|
248
|
+
intent: IntentBuilder;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP: Partial<
|
|
252
|
+
Record<keyof PackageManagerResolveActivityOptions, string>
|
|
253
|
+
> = {
|
|
254
|
+
user: "--user",
|
|
255
|
+
};
|
|
256
|
+
|
|
180
257
|
export class PackageManager extends AdbCommandBase {
|
|
181
|
-
|
|
258
|
+
#cmd: Cmd;
|
|
182
259
|
|
|
183
|
-
|
|
260
|
+
constructor(adb: Adb) {
|
|
184
261
|
super(adb);
|
|
185
|
-
this
|
|
262
|
+
this.#cmd = new Cmd(adb);
|
|
186
263
|
}
|
|
187
264
|
|
|
188
|
-
|
|
189
|
-
options
|
|
265
|
+
#buildInstallArguments(
|
|
266
|
+
options: Partial<PackageManagerInstallOptions> | undefined,
|
|
190
267
|
): string[] {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP[
|
|
197
|
-
key as keyof PackageManagerInstallOptions
|
|
198
|
-
];
|
|
199
|
-
if (option) {
|
|
200
|
-
args.push(option);
|
|
201
|
-
switch (typeof value) {
|
|
202
|
-
case "number":
|
|
203
|
-
args.push(value.toString());
|
|
204
|
-
break;
|
|
205
|
-
case "string":
|
|
206
|
-
args.push(value);
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return args;
|
|
268
|
+
return buildArguments(
|
|
269
|
+
["pm", "install"],
|
|
270
|
+
options,
|
|
271
|
+
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
|
|
272
|
+
);
|
|
214
273
|
}
|
|
215
274
|
|
|
216
|
-
|
|
275
|
+
async install(
|
|
217
276
|
apks: string[],
|
|
218
|
-
options?: Partial<PackageManagerInstallOptions
|
|
277
|
+
options?: Partial<PackageManagerInstallOptions>,
|
|
219
278
|
): Promise<string> {
|
|
220
|
-
const args = this
|
|
279
|
+
const args = this.#buildInstallArguments(options);
|
|
221
280
|
// WIP: old version of pm doesn't support multiple apks
|
|
222
281
|
args.push(...apks);
|
|
223
282
|
return await this.adb.subprocess.spawnAndWaitLegacy(args);
|
|
224
283
|
}
|
|
225
284
|
|
|
226
|
-
|
|
285
|
+
async pushAndInstallStream(
|
|
227
286
|
stream: ReadableStream<Consumable<Uint8Array>>,
|
|
228
|
-
options?: Partial<PackageManagerInstallOptions
|
|
229
|
-
): Promise<
|
|
287
|
+
options?: Partial<PackageManagerInstallOptions>,
|
|
288
|
+
): Promise<void> {
|
|
230
289
|
const sync = await this.adb.sync();
|
|
231
290
|
|
|
232
291
|
const fileName = Math.random().toString().substring(2);
|
|
@@ -241,41 +300,195 @@ export class PackageManager extends AdbCommandBase {
|
|
|
241
300
|
await sync.dispose();
|
|
242
301
|
}
|
|
243
302
|
|
|
244
|
-
|
|
303
|
+
// Starting from Android 7, `pm` is only a wrapper for `cmd package`,
|
|
304
|
+
// and `cmd package` launches faster than `pm`.
|
|
305
|
+
// But `cmd package` can't read `/data/local/tmp` folder due to SELinux policy,
|
|
306
|
+
// so installing a file must use `pm`.
|
|
307
|
+
const args = this.#buildInstallArguments(options);
|
|
245
308
|
args.push(filePath);
|
|
246
|
-
const process = await
|
|
247
|
-
|
|
248
|
-
args.map(escapeArg).join(" ")
|
|
249
|
-
);
|
|
250
|
-
return new WrapReadableStream({
|
|
251
|
-
start: () => process.stdout.pipeThrough(new DecodeUtf8Stream()),
|
|
252
|
-
close: async () => {
|
|
253
|
-
await this.adb.rm(filePath);
|
|
254
|
-
},
|
|
309
|
+
const process = await this.adb.subprocess.spawn(args.map(escapeArg), {
|
|
310
|
+
protocols: [AdbSubprocessNoneProtocol],
|
|
255
311
|
});
|
|
312
|
+
|
|
313
|
+
const output = await process.stdout
|
|
314
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
315
|
+
.pipeThrough(new ConcatStringStream())
|
|
316
|
+
.then((output) => output.trim());
|
|
317
|
+
|
|
318
|
+
await this.adb.rm(filePath);
|
|
319
|
+
|
|
320
|
+
if (output !== "Success") {
|
|
321
|
+
throw new Error(output);
|
|
322
|
+
}
|
|
256
323
|
}
|
|
257
324
|
|
|
258
|
-
|
|
325
|
+
async installStream(
|
|
259
326
|
size: number,
|
|
260
327
|
stream: ReadableStream<Consumable<Uint8Array>>,
|
|
261
|
-
options?: Partial<PackageManagerInstallOptions
|
|
262
|
-
): Promise<
|
|
263
|
-
|
|
264
|
-
|
|
328
|
+
options?: Partial<PackageManagerInstallOptions>,
|
|
329
|
+
): Promise<void> {
|
|
330
|
+
// Android 7 added both `cmd` command and streaming install support,
|
|
331
|
+
// we can't detect whether `pm` supports streaming install,
|
|
332
|
+
// so we detect `cmd` command support instead.
|
|
333
|
+
if (!this.#cmd.supportsCmd) {
|
|
334
|
+
await this.pushAndInstallStream(stream, options);
|
|
335
|
+
return;
|
|
265
336
|
}
|
|
266
337
|
|
|
267
|
-
|
|
268
|
-
//
|
|
269
|
-
// However, `cmd package install` can't read `/data/local/tmp` folder due to SELinux policy,
|
|
270
|
-
// so even ADB today is still using `pm install` for non-streaming installs.
|
|
271
|
-
const args = this.buildInstallArgs(options);
|
|
272
|
-
// Remove `pm` from args, final command will be `cmd package install`
|
|
338
|
+
const args = this.#buildInstallArguments(options);
|
|
339
|
+
// Remove `pm` from args, final command will starts with `cmd package install`
|
|
273
340
|
args.shift();
|
|
274
341
|
args.push("-S", size.toString());
|
|
275
|
-
const process = await this.
|
|
276
|
-
|
|
277
|
-
|
|
342
|
+
const process = await this.#cmd.spawn(false, "package", ...args);
|
|
343
|
+
|
|
344
|
+
const output = process.stdout
|
|
345
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
346
|
+
.pipeThrough(new ConcatStringStream())
|
|
347
|
+
.then((output) => output.trim());
|
|
348
|
+
|
|
349
|
+
await Promise.all([
|
|
350
|
+
stream.pipeTo(process.stdin),
|
|
351
|
+
output.then((output) => {
|
|
352
|
+
if (output !== "Success") {
|
|
353
|
+
throw new Error(output);
|
|
354
|
+
}
|
|
355
|
+
}),
|
|
356
|
+
]);
|
|
278
357
|
}
|
|
279
358
|
|
|
280
359
|
// TODO: install: support split apk formats (`adb install-multiple`)
|
|
360
|
+
|
|
361
|
+
static parsePackageListItem(
|
|
362
|
+
line: string,
|
|
363
|
+
): PackageManagerListPackagesResult {
|
|
364
|
+
line = line.substring("package:".length);
|
|
365
|
+
|
|
366
|
+
let packageName: string;
|
|
367
|
+
let sourceDir: string | undefined;
|
|
368
|
+
let versionCode: number | undefined;
|
|
369
|
+
let installer: string | undefined;
|
|
370
|
+
let uid: number | undefined;
|
|
371
|
+
|
|
372
|
+
// Parse backwards
|
|
373
|
+
let index = line.indexOf(" uid:");
|
|
374
|
+
if (index !== -1) {
|
|
375
|
+
uid = Number.parseInt(line.substring(index + " uid:".length), 10);
|
|
376
|
+
line = line.substring(0, index);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
index = line.indexOf(" installer=");
|
|
380
|
+
if (index !== -1) {
|
|
381
|
+
installer = line.substring(index + " installer=".length);
|
|
382
|
+
line = line.substring(0, index);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
index = line.indexOf(" versionCode:");
|
|
386
|
+
if (index !== -1) {
|
|
387
|
+
versionCode = Number.parseInt(
|
|
388
|
+
line.substring(index + " versionCode:".length),
|
|
389
|
+
10,
|
|
390
|
+
);
|
|
391
|
+
line = line.substring(0, index);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// `sourceDir` may contain `=` so use `lastIndexOf`
|
|
395
|
+
index = line.lastIndexOf("=");
|
|
396
|
+
if (index !== -1) {
|
|
397
|
+
sourceDir = line.substring(0, index);
|
|
398
|
+
packageName = line.substring(index + "=".length);
|
|
399
|
+
} else {
|
|
400
|
+
packageName = line;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
packageName,
|
|
405
|
+
sourceDir,
|
|
406
|
+
versionCode,
|
|
407
|
+
installer,
|
|
408
|
+
uid,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async #cmdOrSubprocess(args: string[]) {
|
|
413
|
+
if (this.#cmd.supportsCmd) {
|
|
414
|
+
args.shift();
|
|
415
|
+
return await this.#cmd.spawn(false, "package", ...args);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return this.adb.subprocess.spawn(args);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async *listPackages(
|
|
422
|
+
options?: Partial<PackageManagerListPackagesOptions>,
|
|
423
|
+
): AsyncGenerator<PackageManagerListPackagesResult, void, void> {
|
|
424
|
+
const args = buildArguments(
|
|
425
|
+
["pm", "list", "packages"],
|
|
426
|
+
options,
|
|
427
|
+
PACKAGE_MANAGER_LIST_PACKAGES_OPTIONS_MAP,
|
|
428
|
+
);
|
|
429
|
+
if (options?.filter) {
|
|
430
|
+
args.push(options.filter);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const process = await this.#cmdOrSubprocess(args);
|
|
434
|
+
const reader = process.stdout
|
|
435
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
436
|
+
.pipeThrough(new SplitStringStream("\n"))
|
|
437
|
+
.getReader();
|
|
438
|
+
while (true) {
|
|
439
|
+
const { done, value } = await reader.read();
|
|
440
|
+
if (done) {
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
yield PackageManager.parsePackageListItem(value);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async uninstall(
|
|
448
|
+
packageName: string,
|
|
449
|
+
options?: Partial<PackageManagerUninstallOptions>,
|
|
450
|
+
): Promise<void> {
|
|
451
|
+
const args = buildArguments(
|
|
452
|
+
["pm", "uninstall"],
|
|
453
|
+
options,
|
|
454
|
+
PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP,
|
|
455
|
+
);
|
|
456
|
+
args.push(packageName);
|
|
457
|
+
if (options?.splitNames) {
|
|
458
|
+
args.push(...options.splitNames);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const process = await this.#cmdOrSubprocess(args);
|
|
462
|
+
const output = await process.stdout
|
|
463
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
464
|
+
.pipeThrough(new ConcatStringStream())
|
|
465
|
+
.then((output) => output.trim());
|
|
466
|
+
if (output !== "Success") {
|
|
467
|
+
throw new Error(output);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async resolveActivity(
|
|
472
|
+
options: PackageManagerResolveActivityOptions,
|
|
473
|
+
): Promise<string | undefined> {
|
|
474
|
+
let args = buildArguments(
|
|
475
|
+
["pm", "resolve-activity", "--components"],
|
|
476
|
+
options,
|
|
477
|
+
PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
args = args.concat(options.intent.build());
|
|
481
|
+
|
|
482
|
+
const process = await this.#cmdOrSubprocess(args);
|
|
483
|
+
const output = await process.stdout
|
|
484
|
+
.pipeThrough(new DecodeUtf8Stream())
|
|
485
|
+
.pipeThrough(new ConcatStringStream())
|
|
486
|
+
.then((output) => output.trim());
|
|
487
|
+
|
|
488
|
+
if (output === "No activity found") {
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return output;
|
|
493
|
+
}
|
|
281
494
|
}
|