Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 8a521c8d authored by Simon Chan's avatar Simon Chan
Browse files

refactor(adb): let backends deserialize packets by themselves for better optimization

parent 38a76a2e
Loading
Loading
Loading
Loading
+3 −11
Original line number Diff line number Diff line
@@ -158,25 +158,17 @@ function _Connect(): JSX.Element | null {
                try {
                    setConnecting(true);

                    const dataStreamPair = await selectedBackend.connect();

                    const packetStreamPair = Adb.createConnection({
                        readable: dataStreamPair.readable
                            .pipeThrough(new InspectStream(chunk => {
                                byteInAcc.current += chunk.byteLength;
                            })),
                        writable: dataStreamPair.writable,
                    });
                    const streams = await selectedBackend.connect();

                    // Use `TransformStream` to intercept packets and log them
                    const readable = packetStreamPair.readable
                    const readable = streams.readable
                        .pipeThrough(
                            new InspectStream(packet => {
                                globalState.appendLog('Incoming', packet);
                            })
                        );
                    const writable = pipeFrom(
                        packetStreamPair.writable,
                        streams.writable,
                        new InspectStream(packet => {
                            globalState.appendLog('Outgoing', packet);
                        })
+5 −4
Original line number Diff line number Diff line
import { AdbBackend, ReadableStream, WrapReadableStream, WrapWritableStream, WritableStream } from '@yume-chan/adb';
import { AdbBackend, AdbPacket, AdbPacketSerializeStream, pipeFrom, ReadableStream, StructDeserializeStream, WrapReadableStream, WrapWritableStream, WritableStream } from '@yume-chan/adb';

declare global {
    interface TCPSocket {
@@ -55,6 +55,7 @@ export default class AdbDirectSocketsBackend implements AdbBackend {
            remotePort: this.port,
            noDelay: true,
        });

        // Native streams can't `pipeTo()` or `pipeThrough()` polyfilled streams, so we need to wrap them
        return {
            readable: new WrapReadableStream<Uint8Array, ReadableStream<Uint8Array>, void>({
@@ -64,15 +65,15 @@ export default class AdbDirectSocketsBackend implements AdbBackend {
                        state: undefined,
                    };
                }
            }),
            writable: new WrapWritableStream({
            }).pipeThrough(new StructDeserializeStream(AdbPacket)),
            writable: pipeFrom(new WrapWritableStream({
                async start() {
                    return {
                        writable,
                        state: undefined,
                    };
                }
            }),
            }), new AdbPacketSerializeStream()),
        };
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
    "dependencies": {
        "@types/w3c-web-usb": "^1.0.4",
        "@yume-chan/adb": "^0.0.10",
        "@yume-chan/struct": "^0.0.10",
        "tslib": "^2.3.1"
    },
    "devDependencies": {
+24 −16
Original line number Diff line number Diff line
import { DuplexStreamFactory, type AdbBackend, type ReadableStream, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb';
import { AdbPacket, AdbPacketSerializeStream, DuplexStreamFactory, pipeFrom, ReadableStream, type AdbBackend, type AdbPacketCore, type AdbPacketInit, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb';
import type { StructAsyncDeserializeStream } from "@yume-chan/struct";

export const WebUsbDeviceFilter: USBDeviceFilter = {
    classCode: 0xFF,
@@ -6,15 +7,15 @@ export const WebUsbDeviceFilter: USBDeviceFilter = {
    protocolCode: 1,
};

export class AdbWebUsbBackendStream implements ReadableWritablePair<Uint8Array, Uint8Array>{
    private _readable: ReadableStream<Uint8Array>;
export class AdbWebUsbBackendStream implements ReadableWritablePair<AdbPacketCore, AdbPacketInit>{
    private _readable: ReadableStream<AdbPacketCore>;
    public get readable() { return this._readable; }

    private _writable: WritableStream<Uint8Array>;
    private _writable: WritableStream<AdbPacketInit>;
    public get writable() { return this._writable; }

    public constructor(device: USBDevice, inEndpoint: USBEndpoint, outEndpoint: USBEndpoint) {
        const factory = new DuplexStreamFactory<Uint8Array, Uint8Array>({
        const factory = new DuplexStreamFactory<AdbPacketCore, Uint8Array>({
            close: async () => {
                navigator.usb.removeEventListener('disconnect', handleUsbDisconnect);
                try {
@@ -33,9 +34,13 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair<Uint8Array,

        navigator.usb.addEventListener('disconnect', handleUsbDisconnect);

        this._readable = factory.createReadable({
            pull: async (controller) => {
                const result = await device.transferIn(inEndpoint.endpointNumber, inEndpoint.packetSize);
        const incomingStream: StructAsyncDeserializeStream = {
            async read(length) {
                // `ReadableStream<Uin8Array>` don't know how many bytes the consumer need in each `pull`,
                // But `transferIn(endpointNumber, packetSize)` is much slower than `transferIn(endpointNumber, length)`
                // So `AdbBackend` is refactored to use `ReadableStream<AdbPacketCore>` directly,
                // (let each backend deserialize the packets in their own way)
                const result = await device.transferIn(inEndpoint.endpointNumber, length);

                // `USBTransferResult` has three states: "ok", "stall" and "babble",
                // but ADBd on Android won't enter "stall" (halt) state,
@@ -44,22 +49,25 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair<Uint8Array,
                // "babble" just means there is more data to be read.

                // From spec, the `result.data` always covers the whole `buffer`.
                const chunk = new Uint8Array(result.data!.buffer);
                controller.enqueue(chunk);
                return new Uint8Array(result.data!.buffer);
            }
        };

        this._readable = factory.createWrapReadable(new ReadableStream<AdbPacketCore>({
            async pull(controller) {
                const value = await AdbPacket.deserialize(incomingStream);
                controller.enqueue(value);
            },
        }, {
            highWaterMark: 16 * 1024,
            size(chunk) { return chunk.byteLength; },
        });
        }));

        this._writable = factory.createWritable({
        this._writable = pipeFrom(factory.createWritable({
            write: async (chunk) => {
                await device.transferOut(outEndpoint.endpointNumber, chunk);
            },
        }, {
            highWaterMark: 16 * 1024,
            size(chunk) { return chunk.byteLength; },
        });
        }), new AdbPacketSerializeStream());
    }
}

+3 −0
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@
    "references": [
        {
            "path": "../adb/tsconfig.build.json"
        },
        {
            "path": "../struct/tsconfig.json"
        }
    ]
}
Loading