Shell protocol
export declare class AdbShellProtocolSubprocessService {
spawn(
command: string | string[],
signal?: AbortSignal,
): Promise<AdbShellProtocolProcess>;
spawnWait(
command: string | string[],
): Promise<AdbShellProtocolSpawner.WaitResult<Uint8Array>>;
spawnWaitText(
command: string | string[],
): Promise<AdbShellProtocolSpawner.WaitResult<string>>;
pty(options?: {
command?: string | string[] | undefined;
terminalType?: string;
}): Promise<AdbShellProtocolPtyProcess>;
}
An instance of AdbShellProtocolSubprocessService
is available at adb.subprocess.shellProtocol
.
If the device doesn't support shell protocol, adb.subprocess.shellProtocol
will be undefined
.
spawn
Start a process in raw mode.
import type { MaybePromiseLike } from "@yume-chan/async";
import type {
MaybeConsumable,
ReadableStream,
WritableStream,
} from "@yume-chan/stream-extra";
export interface AdbShellProtocolProcess {
get stdin(): WritableStream<MaybeConsumable<Uint8Array>>;
get output(): ReadableStream<Uint8Array>;
get exited(): Promise<void>;
kill(): MaybePromiseLike<void>;
}
export declare class AdbShellProtocolSubprocessService {
spawn(
command: string | string[],
signal?: AbortSignal,
): Promise<AdbShellProtocolProcess>;
}
command
The command
parameter can either be a single string, or an array of strings
- JavaScript
- TypeScript
await adb.subprocess.shellProtocol.spawn("ls -al /");
// same as
await adb.subprocess.shellProtocol.spawn(["ls", "-al", "/"]);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
await adb.subprocess.shellProtocol!.spawn("ls -al /");
// same as
await adb.subprocess.shellProtocol!.spawn(["ls", "-al", "/"]);
signal
A optional AbortSignal
to kill the process.
When the process is killed by signal
:
stdin
will be closed, writing to it will throw an error.stdout
andstderr
will be closed after all buffered data is consumed.exited
will be rejected withsignal.reason
- JavaScript
- TypeScript
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 1000);
const process = await adb.subprocess.shellProtocol.spawn("logcat", abortController.signal);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 1000);
const process = await adb.subprocess.shellProtocol!.spawn("logcat", abortController.signal);
stdin
Writes to the process's stdin.
- JavaScript
- TypeScript
import { encodeUtf8 } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
// `cat` will output whatever is written to its `stdin`
const process = await adb.subprocess.shellProtocol.spawn("cat");
const writer = process.stdin.getWriter();
await writer.write(encodeUtf8("Hello World!"));
for await (const chunk of process.stdout.pipeThrough(new TextDecoderStream())) {
console.log(chunk); // "Hello World!"
await process.exit();
}
import type { Adb } from "@yume-chan/adb";
import { encodeUtf8 } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
// `cat` will output whatever is written to its `stdin`
const process = await adb.subprocess.shellProtocol!.spawn("cat");
const writer = process.stdin.getWriter();
await writer.write(encodeUtf8("Hello World!"));
for await (const chunk of process.stdout.pipeThrough(new TextDecoderStream())) {
console.log(chunk); // "Hello World!"
await process.exit();
}
stdout
and stderr
Read from the process's stdout
and stderr
. The streams will end when the process exits.
- JavaScript
- TypeScript
import { TextDecoderStream } from "@yume-chan/stream-extra";
const process = await adb.subprocess.shellProtocol.spawn("ls -al /");
await Promise.all([
process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stdout", chunk);
},
}),
),
process.stderr.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stderr", chunk);
},
}),
),
]);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.shellProtocol!.spawn("ls -al /");
await Promise.all([
process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stdout", chunk);
},
}),
),
process.stderr.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stderr", chunk);
},
}),
),
]);
ADB is a multiplexing protocol (multiple logic streams are transmitted over one connection), so blocking one stream will block all other streams.
You must continuously read from all incoming streams (either by piping them to WritableStream
s, using for await
loop, or calling reader.read()
in a loop) to prevent this from happening.
If the remaining data is not needed, stream.cancel()
(or reader.cancel()
if using a reader) can be called to discard them.
exited
A Promise
that resolves with the process's exit code.
The exit code is an unsigned byte, in the range of 0 to 255.
If the process is killed by signal
, its exited
promise will be rejected with signal.reason
.
- JavaScript
- TypeScript
const process = await adb.subprocess.shellProtocol.spawn("sleep 1");
const exitCode = await process.exited; // Resolves after 1 second
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.shellProtocol!.spawn("sleep 1");
const exitCode: number = await process.exited; // Resolves after 1 second
kill
Forcefully kill the process.
If there are any remaining data in stdout
and stderr
, they can still be read after the process has exited.
Calling kill
closes the socket before the exit code can be received, so the exited
Promise
will be rejected with an error.
- JavaScript
- TypeScript
import { TextDecoderStream } from "@yume-chan/stream-extra";
const process = await adb.subprocess.shellProtocol.spawn("logcat");
setTimeout(() => process.kill(), 1000);
void process.exited.catch((e) => {
// process has been killed
});
await Promise.all([
process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stdout", chunk);
},
}),
),
process.stderr.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stderr", chunk);
},
}),
),
]);
import type { Adb } from "@yume-chan/adb";
import { TextDecoderStream } from "@yume-chan/stream-extra";
declare const adb: Adb;
const process = await adb.subprocess.shellProtocol!.spawn("logcat");
setTimeout(() => process.kill(), 1000);
void process.exited.catch((e) => {
// process has been killed
});
await Promise.all([
process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stdout", chunk);
},
}),
),
process.stderr.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(chunk) {
console.log("stderr", chunk);
},
}),
),
]);
spawnWait
and spawnWaitText
export declare namespace AdbShellProtocolSpawner {
interface WaitResult<T> {
stdout: T;
stderr: T;
exitCode: number;
}
}
export declare class AdbShellProtocolSubprocessService {
spawnWait(
command: string | string[],
): Promise<AdbShellProtocolSpawner.WaitResult<Uint8Array>>;
spawnWaitText(
command: string | string[],
): Promise<AdbShellProtocolSpawner.WaitResult<string>>;
}
Start a process in raw mode, buffer its output, wait for it to exit, then return the buffered output all at once.
spawnWait
returns binary output, while spawnWaitText
returns text output.
Unlike none protocol, shell protocol's spawnWait
and spawnWaitText
will return an object with stdout
, stderr
and exitCode
properties.
- JavaScript
- TypeScript
const { exitCode /* : number */, stdout /* : Uint8Array */, stderr /* : Uint8Array */ } =
await adb.subprocess.shellProtocol.spawnWait("ls -al /");
import type { Adb } from "@yume-chan/adb";
declare const adb: Adb;
const { exitCode /* : number */, stdout /* : Uint8Array */, stderr /* : Uint8Array */ } =
await adb.subprocess.shellProtocol!.spawnWait("ls -al /");
- JavaScript
- TypeScript
const { exitCode /* : number */, stdout /* : string */, stderr /* : string */ } =
await adb.subprocess.shellProtocol.spawnWaitText("ls -al /");
import type { Adb } from "@yume-chan/adb";
declare const adb: Adb;
const { exitCode /* : number */, stdout /* : string */, stderr /* : string */ } =
await adb.subprocess.shellProtocol!.spawnWaitText("ls -al /");
pty
Start a process in PTY mode.
export declare class AdbShellProtocolPtyProcess {
get input(): WritableStream<MaybeConsumable<Uint8Array<ArrayBufferLike>>>;
get output(): ReadableStream<Uint8Array<ArrayBufferLike>>;
get exited(): Promise<number>;
resize(rows: number, cols: number): Promise<void>;
sigint(): Promise<void>;
kill(): MaybePromiseLike<void>;
}
export declare class AdbNoneProtocolSubprocessService {
pty(options?: {
command?: string | string[] | undefined;
terminalType?: string;
}): Promise<AdbShellProtocolPtyProcess>;
}
input
, stdout
, stderr
, exited
and kill
is basically same as raw mode's stdin
, stdout
, stderr
, exited
and kill
.
command
If command
parameter is undefined
, the default shell will be started.
terminalType
Sets the TERM
environment variable in the PTY process.
resize
Sets the size of the PTY.
When using xterm.js, the terminal.onResize
event can be used to update the size of the PTY.
- JavaScript
- TypeScript
terminal.onResize((size) => {
void pty.resize(size.rows, size.cols);
});
// Sets initial size
await pty.resize(terminal.rows, terminal.cols);
import type { Terminal } from "xterm.js";
declare const pty: AdbShellProtocolPtyProcess;
declare const terminal: Terminal;
terminal.onResize((size) => {
void pty.resize(size.rows, size.cols);
});
// Sets initial size
await pty.resize(terminal.rows, terminal.cols);
sigint
A shortcut method to write 0x03
to the PTY input, to send a SIGINT
to the foreground process.