Loading libraries/adb/src/commands/reverse.ts +1 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ const AdbReverseErrorResponse = new Struct() .concat(AdbReverseStringResponse) .postDeserialize((value) => { // https://issuetracker.google.com/issues/37066218 // ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over WiFi), // ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over Wi-Fi), // and returns this confusing "more than one device/emulator" error. if (value.content === "more than one device/emulator") { throw new AdbReverseNotSupportedError(); Loading libraries/adb/src/commands/subprocess/protocols/shell.ts +21 −91 Original line number Diff line number Diff line Loading @@ -6,12 +6,10 @@ import type { WritableStreamDefaultWriter, } from "@yume-chan/stream-extra"; import { ConsumableTransformStream, ConsumableWritableStream, PushReadableStream, StructDeserializeStream, WritableStream, pipeFrom, } from "@yume-chan/stream-extra"; import type { StructValueType } from "@yume-chan/struct"; import Struct, { placeholder } from "@yume-chan/struct"; Loading @@ -37,68 +35,8 @@ const AdbShellProtocolPacket = new Struct({ littleEndian: true }) .uint32("length") .uint8Array("data", { lengthField: "length" }); type AdbShellProtocolPacketInit = (typeof AdbShellProtocolPacket)["TInit"]; type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>; class StdinSerializeStream extends ConsumableTransformStream< Uint8Array, AdbShellProtocolPacketInit > { constructor() { super({ async transform(chunk, controller) { await controller.enqueue({ id: AdbShellProtocolId.Stdin, data: chunk, }); }, flush() { // TODO: AdbShellSubprocessProtocol: support closing stdin }, }); } } class MultiplexStream<T> { #readable: PushReadableStream<T>; #readableController!: PushReadableStreamController<T>; get readable() { return this.#readable; } #activeCount = 0; constructor() { this.#readable = new PushReadableStream((controller) => { this.#readableController = controller; }); } createWriteable() { return new WritableStream<T>({ start: () => { this.#activeCount += 1; }, write: async (chunk) => { await this.#readableController.enqueue(chunk); }, abort: () => { this.#activeCount -= 1; if (this.#activeCount === 0) { this.#readableController.close(); } }, close: () => { this.#activeCount -= 1; if (this.#activeCount === 0) { this.#readableController.close(); } }, }); } } /** * Shell v2 a.k.a Shell Protocol * Loading Loading @@ -126,9 +64,7 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol { } readonly #socket: AdbSocket; #socketWriter: WritableStreamDefaultWriter< Consumable<AdbShellProtocolPacketInit> >; #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>; #stdin: WritableStream<Consumable<Uint8Array>>; get stdin() { Loading Loading @@ -207,39 +143,33 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol { }, ); const multiplexer = new MultiplexStream< Consumable<AdbShellProtocolPacketInit> >(); void multiplexer.readable .pipeThrough( new ConsumableTransformStream({ async transform(chunk, controller) { await controller.enqueue( AdbShellProtocolPacket.serialize(chunk), ); }, }), ) .pipeTo(socket.writable); this.#writer = this.#socket.writable.getWriter(); this.#stdin = pipeFrom( multiplexer.createWriteable(), new StdinSerializeStream(), this.#stdin = new WritableStream<Consumable<Uint8Array>>({ write: async (chunk) => { await ConsumableWritableStream.write( this.#writer, AdbShellProtocolPacket.serialize({ id: AdbShellProtocolId.Stdin, data: chunk.value, }), ); this.#socketWriter = multiplexer.createWriteable().getWriter(); chunk.consume(); }, }); } async resize(rows: number, cols: number) { await ConsumableWritableStream.write(this.#socketWriter, { await ConsumableWritableStream.write( this.#writer, AdbShellProtocolPacket.serialize({ id: AdbShellProtocolId.WindowSizeChange, data: encodeUtf8( // The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}` // However, according to https://linux.die.net/man/4/tty_ioctl // `x_pixels` and `y_pixels` are unused, so always sending `0` should be fine. `${rows}x${cols},0x0\0`, ), }); data: encodeUtf8(`${rows}x${cols},0x0\0`), }), ); } kill() { Loading libraries/adb/src/commands/subprocess/utils.ts +1 −1 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ export function escapeArg(s: string) { break; } result += s.substring(base, found); // a'b becomes a'\'b // a'b becomes a'\'b (the backslash is not a escape character) result += String.raw`'\''`; base = found + 1; } Loading Loading
libraries/adb/src/commands/reverse.ts +1 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ const AdbReverseErrorResponse = new Struct() .concat(AdbReverseStringResponse) .postDeserialize((value) => { // https://issuetracker.google.com/issues/37066218 // ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over WiFi), // ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over Wi-Fi), // and returns this confusing "more than one device/emulator" error. if (value.content === "more than one device/emulator") { throw new AdbReverseNotSupportedError(); Loading
libraries/adb/src/commands/subprocess/protocols/shell.ts +21 −91 Original line number Diff line number Diff line Loading @@ -6,12 +6,10 @@ import type { WritableStreamDefaultWriter, } from "@yume-chan/stream-extra"; import { ConsumableTransformStream, ConsumableWritableStream, PushReadableStream, StructDeserializeStream, WritableStream, pipeFrom, } from "@yume-chan/stream-extra"; import type { StructValueType } from "@yume-chan/struct"; import Struct, { placeholder } from "@yume-chan/struct"; Loading @@ -37,68 +35,8 @@ const AdbShellProtocolPacket = new Struct({ littleEndian: true }) .uint32("length") .uint8Array("data", { lengthField: "length" }); type AdbShellProtocolPacketInit = (typeof AdbShellProtocolPacket)["TInit"]; type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>; class StdinSerializeStream extends ConsumableTransformStream< Uint8Array, AdbShellProtocolPacketInit > { constructor() { super({ async transform(chunk, controller) { await controller.enqueue({ id: AdbShellProtocolId.Stdin, data: chunk, }); }, flush() { // TODO: AdbShellSubprocessProtocol: support closing stdin }, }); } } class MultiplexStream<T> { #readable: PushReadableStream<T>; #readableController!: PushReadableStreamController<T>; get readable() { return this.#readable; } #activeCount = 0; constructor() { this.#readable = new PushReadableStream((controller) => { this.#readableController = controller; }); } createWriteable() { return new WritableStream<T>({ start: () => { this.#activeCount += 1; }, write: async (chunk) => { await this.#readableController.enqueue(chunk); }, abort: () => { this.#activeCount -= 1; if (this.#activeCount === 0) { this.#readableController.close(); } }, close: () => { this.#activeCount -= 1; if (this.#activeCount === 0) { this.#readableController.close(); } }, }); } } /** * Shell v2 a.k.a Shell Protocol * Loading Loading @@ -126,9 +64,7 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol { } readonly #socket: AdbSocket; #socketWriter: WritableStreamDefaultWriter< Consumable<AdbShellProtocolPacketInit> >; #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>; #stdin: WritableStream<Consumable<Uint8Array>>; get stdin() { Loading Loading @@ -207,39 +143,33 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol { }, ); const multiplexer = new MultiplexStream< Consumable<AdbShellProtocolPacketInit> >(); void multiplexer.readable .pipeThrough( new ConsumableTransformStream({ async transform(chunk, controller) { await controller.enqueue( AdbShellProtocolPacket.serialize(chunk), ); }, }), ) .pipeTo(socket.writable); this.#writer = this.#socket.writable.getWriter(); this.#stdin = pipeFrom( multiplexer.createWriteable(), new StdinSerializeStream(), this.#stdin = new WritableStream<Consumable<Uint8Array>>({ write: async (chunk) => { await ConsumableWritableStream.write( this.#writer, AdbShellProtocolPacket.serialize({ id: AdbShellProtocolId.Stdin, data: chunk.value, }), ); this.#socketWriter = multiplexer.createWriteable().getWriter(); chunk.consume(); }, }); } async resize(rows: number, cols: number) { await ConsumableWritableStream.write(this.#socketWriter, { await ConsumableWritableStream.write( this.#writer, AdbShellProtocolPacket.serialize({ id: AdbShellProtocolId.WindowSizeChange, data: encodeUtf8( // The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}` // However, according to https://linux.die.net/man/4/tty_ioctl // `x_pixels` and `y_pixels` are unused, so always sending `0` should be fine. `${rows}x${cols},0x0\0`, ), }); data: encodeUtf8(`${rows}x${cols},0x0\0`), }), ); } kill() { Loading
libraries/adb/src/commands/subprocess/utils.ts +1 −1 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ export function escapeArg(s: string) { break; } result += s.substring(base, found); // a'b becomes a'\'b // a'b becomes a'\'b (the backslash is not a escape character) result += String.raw`'\''`; base = found + 1; } Loading