diff --git a/libraries/adb-scrcpy/src/connection.ts b/libraries/adb-scrcpy/src/connection.ts index e0ce5dc6..e49cc3c9 100644 --- a/libraries/adb-scrcpy/src/connection.ts +++ b/libraries/adb-scrcpy/src/connection.ts @@ -114,21 +114,21 @@ export class AdbScrcpyForwardConnection extends AdbScrcpyConnection { const streams: AdbScrcpyConnectionStreams = {}; if (this.options.video) { - const video = await this.#connectAndRetry(sendDummyByte); - streams.video = video.readable; + const stream = await this.#connectAndRetry(sendDummyByte); + streams.video = stream.readable; sendDummyByte = false; } if (this.options.audio) { - const audio = await this.#connectAndRetry(sendDummyByte); - streams.audio = audio.readable; + const stream = await this.#connectAndRetry(sendDummyByte); + streams.audio = stream.readable; sendDummyByte = false; } if (this.options.control) { - const control = await this.#connectAndRetry(sendDummyByte); + const stream = await this.#connectAndRetry(sendDummyByte); + streams.control = stream; sendDummyByte = false; - streams.control = control; } return streams; @@ -180,18 +180,18 @@ export class AdbScrcpyReverseConnection extends AdbScrcpyConnection { const streams: AdbScrcpyConnectionStreams = {}; if (this.options.video) { - const video = await this.#accept(); - streams.video = video.readable; + const stream = await this.#accept(); + streams.video = stream.readable; } if (this.options.audio) { - const audio = await this.#accept(); - streams.audio = audio.readable; + const stream = await this.#accept(); + streams.audio = stream.readable; } if (this.options.control) { - const control = await this.#accept(); - streams.control = control; + const stream = await this.#accept(); + streams.control = stream; } return streams; diff --git a/libraries/adb/src/daemon/dispatcher.ts b/libraries/adb/src/daemon/dispatcher.ts index 15954d86..38848c28 100644 --- a/libraries/adb/src/daemon/dispatcher.ts +++ b/libraries/adb/src/daemon/dispatcher.ts @@ -28,6 +28,10 @@ export interface AdbPacketDispatcherOptions { */ appendNullToServiceString: boolean; maxPayloadSize: number; + /** + * Whether to preserve the connection open after the `AdbPacketDispatcher` is closed. + */ + preserveConnection?: boolean | undefined; } /** @@ -114,12 +118,7 @@ export class AdbPacketDispatcher implements Closeable { }, }), { - // There are multiple reasons for the pipe to stop, - // (device disconnection, protocol error, or user abortion) - // if the underlying streams are still open, - // it's still possible to create another ADB connection. - // So don't close `readable` here. - preventCancel: true, + preventCancel: options.preserveConnection ?? false, signal: this.#readAbortController.signal, }, ) diff --git a/libraries/adb/src/daemon/transport.ts b/libraries/adb/src/daemon/transport.ts index 14d41ab1..bf9869d3 100644 --- a/libraries/adb/src/daemon/transport.ts +++ b/libraries/adb/src/daemon/transport.ts @@ -32,6 +32,10 @@ interface AdbDaemonAuthenticationOptions { connection: ReadableWritablePair>; credentialStore: AdbCredentialStore; authenticators?: AdbAuthenticator[]; + /** + * Whether to preserve the connection open after the `AdbDaemonTransport` is closed. + */ + preserveConnection?: boolean | undefined; } interface AdbDaemonSocketConnectorConstructionOptions { @@ -40,6 +44,10 @@ interface AdbDaemonSocketConnectorConstructionOptions { version: number; maxPayloadSize: number; banner: string; + /** + * Whether to preserve the connection open after the `AdbDaemonTransport` is closed. + */ + preserveConnection?: boolean | undefined; } export class AdbDaemonTransport implements AdbTransport { @@ -56,6 +64,7 @@ export class AdbDaemonTransport implements AdbTransport { connection, credentialStore, authenticators = ADB_DEFAULT_AUTHENTICATORS, + preserveConnection, }: AdbDaemonAuthenticationOptions): Promise { // Initially, set to highest-supported version and payload size. let version = 0x01000001; @@ -180,6 +189,7 @@ export class AdbDaemonTransport implements AdbTransport { version, maxPayloadSize, banner, + preserveConnection, }); } @@ -215,6 +225,7 @@ export class AdbDaemonTransport implements AdbTransport { version, maxPayloadSize, banner, + preserveConnection, }: AdbDaemonSocketConnectorConstructionOptions) { this.#serial = serial; this.#banner = AdbBanner.parse(banner); @@ -233,6 +244,7 @@ export class AdbDaemonTransport implements AdbTransport { calculateChecksum, appendNullToServiceString, maxPayloadSize, + preserveConnection, }); this.#protocolVersion = version; diff --git a/libraries/android-bin/src/bu.ts b/libraries/android-bin/src/bu.ts index 0110b170..c6982b74 100644 --- a/libraries/android-bin/src/bu.ts +++ b/libraries/android-bin/src/bu.ts @@ -1,5 +1,6 @@ import { AdbCommandBase } from "@yume-chan/adb"; import type { Consumable, ReadableStream } from "@yume-chan/stream-extra"; +import { ConcatStringStream, DecodeUtf8Stream } from "@yume-chan/stream-extra"; export interface AdbBackupOptions { user: number; @@ -62,13 +63,18 @@ export class AdbBackup extends AdbCommandBase { * User must enter the password (if any) and * confirm restore on device within 60 seconds. */ - async restore(options: AdbRestoreOptions): Promise { + async restore(options: AdbRestoreOptions): Promise { const args = ["bu", "restore"]; if (options.user !== undefined) { args.push("--user", options.user.toString()); } const process = await this.adb.subprocess.spawn(args); - await options.file.pipeTo(process.stdin); - await process.exit; + const [output] = await Promise.all([ + process.stdout + .pipeThrough(new DecodeUtf8Stream()) + .pipeThrough(new ConcatStringStream()), + options.file.pipeTo(process.stdin), + ]); + return output; } }