Loading libraries/android-bin/src/am.ts +21 −8 Original line number Original line Diff line number Diff line import type { Adb } from "@yume-chan/adb"; import type { Adb, AdbNoneProtocolProcess } from "@yume-chan/adb"; import { AdbServiceBase } from "@yume-chan/adb"; import { AdbServiceBase, escapeArg } from "@yume-chan/adb"; import { SplitStringStream, TextDecoderStream } from "@yume-chan/stream-extra"; import { SplitStringStream, TextDecoderStream } from "@yume-chan/stream-extra"; import { Cmd } from "./cmd/index.js"; import { Cmd } from "./cmd/index.js"; Loading @@ -25,28 +25,41 @@ const START_ACTIVITY_OPTIONS_MAP: Partial< }; }; export class ActivityManager extends AdbServiceBase { export class ActivityManager extends AdbServiceBase { static ServiceName = "activity"; static readonly ServiceName = "activity"; static CommandName = "am"; static readonly CommandName = "am"; #apiLevel: number; #cmd: Cmd.NoneProtocolService; #cmd: Cmd.NoneProtocolService; constructor(adb: Adb) { constructor(adb: Adb, apiLevel = 0) { super(adb); super(adb); this.#apiLevel = apiLevel; this.#cmd = Cmd.createNoneProtocol(adb, ActivityManager.CommandName); this.#cmd = Cmd.createNoneProtocol(adb, ActivityManager.CommandName); } } async startActivity( async startActivity( options: ActivityManagerStartActivityOptions, options: ActivityManagerStartActivityOptions, ): Promise<void> { ): Promise<void> { // `am start` and `am start-activity` are the same, // but `am start-activity` was added in Android 8. let args = buildArguments( let args = buildArguments( [ActivityManager.ServiceName, "start-activity", "-W"], [ActivityManager.ServiceName, "start", "-W"], options, options, START_ACTIVITY_OPTIONS_MAP, START_ACTIVITY_OPTIONS_MAP, ); ); args = args.concat(options.intent.build()); args = args.concat(options.intent.build().map(escapeArg)); // `cmd activity` doesn't support `start` command on Android 7. let process: AdbNoneProtocolProcess; if (this.#apiLevel <= 25) { args[0] = ActivityManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); } else { process = await this.#cmd.spawn(args); } const process = await this.#cmd.spawn(args); const lines = process.output const lines = process.output .pipeThrough(new TextDecoderStream()) .pipeThrough(new TextDecoderStream()) .pipeThrough(new SplitStringStream("\n", { trim: true })); .pipeThrough(new SplitStringStream("\n", { trim: true })); Loading libraries/android-bin/src/pm.ts +17 −83 Original line number Original line Diff line number Diff line Loading @@ -281,32 +281,17 @@ function buildInstallArguments( return args; return args; } } function shouldUsePm( options: PackageManager.UsePmOptions | undefined, maxApiLevel: number, ) { if (options) { if (options.usePm) { return true; } else if ( options.apiLevel !== undefined && options.apiLevel <= maxApiLevel ) { return true; } } return false; } export class PackageManager extends AdbServiceBase { export class PackageManager extends AdbServiceBase { static readonly ServiceName = "package"; static readonly ServiceName = "package"; static readonly CommandName = "pm"; static readonly CommandName = "pm"; #apiLevel: number; #cmd: Cmd.NoneProtocolService; #cmd: Cmd.NoneProtocolService; constructor(adb: Adb) { constructor(adb: Adb, apiLevel = 0) { super(adb); super(adb); this.#apiLevel = apiLevel; this.#cmd = Cmd.createNoneProtocol(adb, PackageManager.CommandName); this.#cmd = Cmd.createNoneProtocol(adb, PackageManager.CommandName); } } Loading Loading @@ -491,13 +476,7 @@ export class PackageManager extends AdbServiceBase { * * * On supported Android versions, all split APKs are included. * On supported Android versions, all split APKs are included. * @param packageName The package name to query * @param packageName The package name to query * @param options * @param options The user ID to query * Whether to use `pm path` instead of `cmd package path`. * * `cmd package` is supported on Android 7, * but `cmd package path` is not supported until Android 9. * * See {@link PackageManager.UsePmOptions} for details * @returns An array of APK file paths * @returns An array of APK file paths */ */ async getPackageSources( async getPackageSources( Loading @@ -507,16 +486,19 @@ export class PackageManager extends AdbServiceBase { * The user ID to query * The user ID to query */ */ user?: number | undefined; user?: number | undefined; } & PackageManager.UsePmOptions, }, ): Promise<string[]> { ): Promise<string[]> { // `pm path` and `pm -p` are the same, // but `pm path` allows an optional `--user` option. const args = [PackageManager.ServiceName, "path"]; const args = [PackageManager.ServiceName, "path"]; if (options?.user !== undefined) { if (options?.user !== undefined) { args.push("--user", options.user.toString()); args.push("--user", options.user.toString()); } } args.push(escapeArg(packageName)); args.push(escapeArg(packageName)); // `cmd package` doesn't support `path` command on Android 7 and 8. let process: AdbNoneProtocolProcess; let process: AdbNoneProtocolProcess; if (shouldUsePm(options, 27)) { if (this.#apiLevel <= 27) { args[0] = PackageManager.CommandName; args[0] = PackageManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); process = await this.adb.subprocess.noneProtocol.spawn(args); } else { } else { Loading Loading @@ -675,28 +657,20 @@ export class PackageManager extends AdbServiceBase { /** /** * Commit an install session. * Commit an install session. * @param sessionId ID of install session returned by `createSession` * @param sessionId ID of install session returned by `createSession` * @param options * Whether to use `pm install-commit` instead of `cmd package install-commit`. * * On Android 7, `cmd package install-commit` is supported, * but the "Success" message is not forwarded back to the client, * causing this function to fail with an empty message. * * https://cs.android.com/android/_/android/platform/frameworks/base/+/b6e96e52e379927859e82606c5b041d99f36a29e * @returns A `Promise` that resolves when the session is committed * @returns A `Promise` that resolves when the session is committed */ */ async sessionCommit( async sessionCommit(sessionId: number): Promise<void> { sessionId: number, options?: PackageManager.UsePmOptions, ): Promise<void> { const args: string[] = [ const args: string[] = [ PackageManager.ServiceName, PackageManager.ServiceName, "install-commit", "install-commit", sessionId.toString(), sessionId.toString(), ]; ]; // `cmd package` does support `install-commit` command on Android 7, // but the "Success" message is not forwarded back to the client, // causing this function to fail with an empty message. let process: AdbNoneProtocolProcess; let process: AdbNoneProtocolProcess; if (shouldUsePm(options, 25)) { if (this.#apiLevel <= 25) { args[0] = PackageManager.CommandName; args[0] = PackageManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); process = await this.adb.subprocess.noneProtocol.spawn(args); } else { } else { Loading @@ -717,42 +691,6 @@ export class PackageManager extends AdbServiceBase { } } } } export namespace PackageManager { /** * `PackageManager` wrapper supports multiple methods to run commands: * * * `pm` executable: Run traditional `pm` executable, * which needs to initialize the whole Android framework each time it starts. * * `cmd` executable: Run `cmd` executable, * which sends arguments directly to the running `system` process. * * `abb`: Use ADB abb command, which is similar to `cmd` executable, * but uses a daemon process instead of starting a new process every time. * * `cmd` and `abb` modes are faster, so they are preferred when supported. * However, in older versions of Android, they don't support all commands, * or have bugs that make them unsafe to use. * * These options allows you to force using `pm` even if `cmd` or `abb` modes are supported. * * See each command's documentation for details. */ export interface UsePmOptions { /** * When `true`, always use `pm`, even if `cmd` or `abb` modes are supported. * * Overrides {@link apiLevel}. */ usePm?: boolean | undefined; /** * API Level of the device. * * When provided, each command will determine whether `pm` should be used * based on hardcoded API level checks. */ apiLevel?: number | undefined; } } export class PackageManagerInstallSession { export class PackageManagerInstallSession { static async create( static async create( packageManager: PackageManager, packageManager: PackageManager, Loading Loading @@ -793,14 +731,10 @@ export class PackageManagerInstallSession { /** /** * Commit this install session. * Commit this install session. * @param options * Whether to use `pm install-commit` instead of `cmd package install-commit`. * * See {@link PackageManager.sessionCommit} for details * @returns A `Promise` that resolves when the session is committed * @returns A `Promise` that resolves when the session is committed */ */ commit(options?: PackageManager.UsePmOptions): Promise<void> { commit(): Promise<void> { return this.#packageManager.sessionCommit(this.#id, options); return this.#packageManager.sessionCommit(this.#id); } } abandon(): Promise<void> { abandon(): Promise<void> { Loading Loading
libraries/android-bin/src/am.ts +21 −8 Original line number Original line Diff line number Diff line import type { Adb } from "@yume-chan/adb"; import type { Adb, AdbNoneProtocolProcess } from "@yume-chan/adb"; import { AdbServiceBase } from "@yume-chan/adb"; import { AdbServiceBase, escapeArg } from "@yume-chan/adb"; import { SplitStringStream, TextDecoderStream } from "@yume-chan/stream-extra"; import { SplitStringStream, TextDecoderStream } from "@yume-chan/stream-extra"; import { Cmd } from "./cmd/index.js"; import { Cmd } from "./cmd/index.js"; Loading @@ -25,28 +25,41 @@ const START_ACTIVITY_OPTIONS_MAP: Partial< }; }; export class ActivityManager extends AdbServiceBase { export class ActivityManager extends AdbServiceBase { static ServiceName = "activity"; static readonly ServiceName = "activity"; static CommandName = "am"; static readonly CommandName = "am"; #apiLevel: number; #cmd: Cmd.NoneProtocolService; #cmd: Cmd.NoneProtocolService; constructor(adb: Adb) { constructor(adb: Adb, apiLevel = 0) { super(adb); super(adb); this.#apiLevel = apiLevel; this.#cmd = Cmd.createNoneProtocol(adb, ActivityManager.CommandName); this.#cmd = Cmd.createNoneProtocol(adb, ActivityManager.CommandName); } } async startActivity( async startActivity( options: ActivityManagerStartActivityOptions, options: ActivityManagerStartActivityOptions, ): Promise<void> { ): Promise<void> { // `am start` and `am start-activity` are the same, // but `am start-activity` was added in Android 8. let args = buildArguments( let args = buildArguments( [ActivityManager.ServiceName, "start-activity", "-W"], [ActivityManager.ServiceName, "start", "-W"], options, options, START_ACTIVITY_OPTIONS_MAP, START_ACTIVITY_OPTIONS_MAP, ); ); args = args.concat(options.intent.build()); args = args.concat(options.intent.build().map(escapeArg)); // `cmd activity` doesn't support `start` command on Android 7. let process: AdbNoneProtocolProcess; if (this.#apiLevel <= 25) { args[0] = ActivityManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); } else { process = await this.#cmd.spawn(args); } const process = await this.#cmd.spawn(args); const lines = process.output const lines = process.output .pipeThrough(new TextDecoderStream()) .pipeThrough(new TextDecoderStream()) .pipeThrough(new SplitStringStream("\n", { trim: true })); .pipeThrough(new SplitStringStream("\n", { trim: true })); Loading
libraries/android-bin/src/pm.ts +17 −83 Original line number Original line Diff line number Diff line Loading @@ -281,32 +281,17 @@ function buildInstallArguments( return args; return args; } } function shouldUsePm( options: PackageManager.UsePmOptions | undefined, maxApiLevel: number, ) { if (options) { if (options.usePm) { return true; } else if ( options.apiLevel !== undefined && options.apiLevel <= maxApiLevel ) { return true; } } return false; } export class PackageManager extends AdbServiceBase { export class PackageManager extends AdbServiceBase { static readonly ServiceName = "package"; static readonly ServiceName = "package"; static readonly CommandName = "pm"; static readonly CommandName = "pm"; #apiLevel: number; #cmd: Cmd.NoneProtocolService; #cmd: Cmd.NoneProtocolService; constructor(adb: Adb) { constructor(adb: Adb, apiLevel = 0) { super(adb); super(adb); this.#apiLevel = apiLevel; this.#cmd = Cmd.createNoneProtocol(adb, PackageManager.CommandName); this.#cmd = Cmd.createNoneProtocol(adb, PackageManager.CommandName); } } Loading Loading @@ -491,13 +476,7 @@ export class PackageManager extends AdbServiceBase { * * * On supported Android versions, all split APKs are included. * On supported Android versions, all split APKs are included. * @param packageName The package name to query * @param packageName The package name to query * @param options * @param options The user ID to query * Whether to use `pm path` instead of `cmd package path`. * * `cmd package` is supported on Android 7, * but `cmd package path` is not supported until Android 9. * * See {@link PackageManager.UsePmOptions} for details * @returns An array of APK file paths * @returns An array of APK file paths */ */ async getPackageSources( async getPackageSources( Loading @@ -507,16 +486,19 @@ export class PackageManager extends AdbServiceBase { * The user ID to query * The user ID to query */ */ user?: number | undefined; user?: number | undefined; } & PackageManager.UsePmOptions, }, ): Promise<string[]> { ): Promise<string[]> { // `pm path` and `pm -p` are the same, // but `pm path` allows an optional `--user` option. const args = [PackageManager.ServiceName, "path"]; const args = [PackageManager.ServiceName, "path"]; if (options?.user !== undefined) { if (options?.user !== undefined) { args.push("--user", options.user.toString()); args.push("--user", options.user.toString()); } } args.push(escapeArg(packageName)); args.push(escapeArg(packageName)); // `cmd package` doesn't support `path` command on Android 7 and 8. let process: AdbNoneProtocolProcess; let process: AdbNoneProtocolProcess; if (shouldUsePm(options, 27)) { if (this.#apiLevel <= 27) { args[0] = PackageManager.CommandName; args[0] = PackageManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); process = await this.adb.subprocess.noneProtocol.spawn(args); } else { } else { Loading Loading @@ -675,28 +657,20 @@ export class PackageManager extends AdbServiceBase { /** /** * Commit an install session. * Commit an install session. * @param sessionId ID of install session returned by `createSession` * @param sessionId ID of install session returned by `createSession` * @param options * Whether to use `pm install-commit` instead of `cmd package install-commit`. * * On Android 7, `cmd package install-commit` is supported, * but the "Success" message is not forwarded back to the client, * causing this function to fail with an empty message. * * https://cs.android.com/android/_/android/platform/frameworks/base/+/b6e96e52e379927859e82606c5b041d99f36a29e * @returns A `Promise` that resolves when the session is committed * @returns A `Promise` that resolves when the session is committed */ */ async sessionCommit( async sessionCommit(sessionId: number): Promise<void> { sessionId: number, options?: PackageManager.UsePmOptions, ): Promise<void> { const args: string[] = [ const args: string[] = [ PackageManager.ServiceName, PackageManager.ServiceName, "install-commit", "install-commit", sessionId.toString(), sessionId.toString(), ]; ]; // `cmd package` does support `install-commit` command on Android 7, // but the "Success" message is not forwarded back to the client, // causing this function to fail with an empty message. let process: AdbNoneProtocolProcess; let process: AdbNoneProtocolProcess; if (shouldUsePm(options, 25)) { if (this.#apiLevel <= 25) { args[0] = PackageManager.CommandName; args[0] = PackageManager.CommandName; process = await this.adb.subprocess.noneProtocol.spawn(args); process = await this.adb.subprocess.noneProtocol.spawn(args); } else { } else { Loading @@ -717,42 +691,6 @@ export class PackageManager extends AdbServiceBase { } } } } export namespace PackageManager { /** * `PackageManager` wrapper supports multiple methods to run commands: * * * `pm` executable: Run traditional `pm` executable, * which needs to initialize the whole Android framework each time it starts. * * `cmd` executable: Run `cmd` executable, * which sends arguments directly to the running `system` process. * * `abb`: Use ADB abb command, which is similar to `cmd` executable, * but uses a daemon process instead of starting a new process every time. * * `cmd` and `abb` modes are faster, so they are preferred when supported. * However, in older versions of Android, they don't support all commands, * or have bugs that make them unsafe to use. * * These options allows you to force using `pm` even if `cmd` or `abb` modes are supported. * * See each command's documentation for details. */ export interface UsePmOptions { /** * When `true`, always use `pm`, even if `cmd` or `abb` modes are supported. * * Overrides {@link apiLevel}. */ usePm?: boolean | undefined; /** * API Level of the device. * * When provided, each command will determine whether `pm` should be used * based on hardcoded API level checks. */ apiLevel?: number | undefined; } } export class PackageManagerInstallSession { export class PackageManagerInstallSession { static async create( static async create( packageManager: PackageManager, packageManager: PackageManager, Loading Loading @@ -793,14 +731,10 @@ export class PackageManagerInstallSession { /** /** * Commit this install session. * Commit this install session. * @param options * Whether to use `pm install-commit` instead of `cmd package install-commit`. * * See {@link PackageManager.sessionCommit} for details * @returns A `Promise` that resolves when the session is committed * @returns A `Promise` that resolves when the session is committed */ */ commit(options?: PackageManager.UsePmOptions): Promise<void> { commit(): Promise<void> { return this.#packageManager.sessionCommit(this.#id, options); return this.#packageManager.sessionCommit(this.#id); } } abandon(): Promise<void> { abandon(): Promise<void> { Loading