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

Unverified Commit a6e3f0e5 authored by Simon Chan's avatar Simon Chan
Browse files

feat(adb): add pair device and connect device commands to server client mode

parent f5e1a2b7
Loading
Loading
Loading
Loading
+74 −10
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ export class AdbServerClient {
        throw new Error(`Unexpected response: ${decodeUtf8(response)}`);
    }

    async connect(
    async createConnection(
        request: string,
        options?: AdbServerConnectionOptions,
    ): Promise<AdbServerConnection> {
@@ -219,7 +219,7 @@ export class AdbServerClient {
    }

    async getVersion(): Promise<number> {
        const connection = await this.connect("host:version");
        const connection = await this.createConnection("host:version");
        const readable = new BufferedReadableStream(connection.readable);
        try {
            const length = hexToNumber(await readable.readExactly(4));
@@ -241,13 +241,13 @@ export class AdbServerClient {
    }

    async killServer(): Promise<void> {
        const connection = await this.connect("host:kill");
        const connection = await this.createConnection("host:kill");
        connection.writable.close().catch(NOOP);
        connection.readable.cancel().catch(NOOP);
    }

    async getServerFeatures(): Promise<AdbFeature[]> {
        const connection = await this.connect("host:host-features");
        const connection = await this.createConnection("host:host-features");
        const readable = new BufferedReadableStream(connection.readable);
        try {
            const response = await AdbServerClient.readString(readable);
@@ -258,6 +258,44 @@ export class AdbServerClient {
        }
    }

    async pairDevice(address: string, code: string): Promise<void> {
        const connection = await this.createConnection(
            `host:pair:${code}:${address}`,
        );
        const readable = new BufferedReadableStream(connection.readable);
        try {
            const response = await AdbServerClient.readString(readable);
            if (!response.startsWith("Successfully paired to")) {
                throw new AdbServerClient.UnauthorizedError(response);
            }
        } finally {
            connection.writable.close().catch(NOOP);
            readable.cancel().catch(NOOP);
        }
    }

    async connectDevice(address: string): Promise<void> {
        const connection = await this.createConnection(
            `host:connect:${address}`,
        );
        const readable = new BufferedReadableStream(connection.readable);
        try {
            const response = await AdbServerClient.readString(readable);
            if (response === `already connected to ${address}`) {
                throw new AdbServerClient.AlreadyConnectedError(response);
            }
            if (response === `failed to connect to ${address}`) {
                throw new AdbServerClient.UnauthorizedError(response);
            }
            if (response !== `connected to ${address}`) {
                throw new AdbServerClient.NetworkError(response);
            }
        } finally {
            connection.writable.close().catch(NOOP);
            readable.cancel().catch(NOOP);
        }
    }

    parseDeviceList(value: string): AdbServerDevice[] {
        const devices: AdbServerDevice[] = [];
        for (const line of value.split("\n")) {
@@ -309,7 +347,7 @@ export class AdbServerClient {
    }

    async getDevices(): Promise<AdbServerDevice[]> {
        const connection = await this.connect("host:devices-l");
        const connection = await this.createConnection("host:devices-l");
        const readable = new BufferedReadableStream(connection.readable);
        try {
            const response = await AdbServerClient.readString(readable);
@@ -323,7 +361,7 @@ export class AdbServerClient {
    async trackDevices(
        callback: (devices: AdbServerDevice[]) => void,
    ): Promise<() => void> {
        const connection = await this.connect("host:track-devices-l");
        const connection = await this.createConnection("host:track-devices-l");
        const readable = new BufferedReadableStream(connection.readable);
        let running = true;
        (async () => {
@@ -380,7 +418,10 @@ export class AdbServerClient {
        // Also, if the command is about a device, but didn't specify a selector,
        // it will be executed against the device selected previously by `connectDevice`.
        // Using this method, we can get the transport ID and device features in one connection.
        const socket = await this.connectDevice(device, "host:features");
        const socket = await this.createDeviceConnection(
            device,
            "host:features",
        );
        try {
            const readable = new BufferedReadableStream(socket.readable);
            const featuresString = await AdbServerClient.readString(readable);
@@ -397,7 +438,7 @@ export class AdbServerClient {
     * @param service The service to forward
     * @returns An `AdbServerSocket` that can be used to communicate with the service
     */
    async connectDevice(
    async createDeviceConnection(
        device: AdbServerDeviceSelector,
        service: string,
    ): Promise<AdbServerSocket> {
@@ -420,7 +461,7 @@ export class AdbServerClient {
            throw new Error("Invalid device selector");
        }

        const connection = await this.connect(switchService);
        const connection = await this.createConnection(switchService);

        try {
            const writer = connection.writable.getWriter();
@@ -496,7 +537,7 @@ export class AdbServerClient {
            `wait-for-${type}-${state}`,
        );

        const socket = await this.connect(service, options);
        const socket = await this.createConnection(service, options);
        const readable = new BufferedReadableStream(socket.readable);
        await AdbServerClient.readOkay(readable);

@@ -560,3 +601,26 @@ export async function raceSignal<T>(
        }
    }
}

export namespace AdbServerClient {
    export class NetworkError extends Error {
        constructor(message: string) {
            super(message);
            this.name = "ConnectionFailedError";
        }
    }

    export class UnauthorizedError extends Error {
        constructor(message: string) {
            super(message);
            this.name = "UnauthorizedError";
        }
    }

    export class AlreadyConnectedError extends Error {
        constructor(message: string) {
            super(message);
            this.name = "AlreadyConnectedError";
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ export class AdbServerTransport implements AdbTransport {
    }

    async connect(service: string): Promise<AdbSocket> {
        return await this.#client.connectDevice(
        return await this.#client.createDeviceConnection(
            {
                transportId: this.transportId,
            },