blob: 9fae9268c0de7073f202e4e9d7eb115f5176cc08 [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {_TextDecoder} from 'custom_utils';
import {defer} from '../../base/deferred';
import {ArrayBufferBuilder} from '../array_buffer_builder';
import {AdbFileHandler} from './adb_file_handler';
import {
AdbConnection,
ByteStream,
OnDisconnectCallback,
OnMessageCallback,
} from './recording_interfaces_v2';
const textDecoder = new _TextDecoder();
export abstract class AdbConnectionImpl implements AdbConnection {
// onStatus and onDisconnect are set to callbacks passed from the caller.
// This happens for instance in the AndroidWebusbTarget, which instantiates
// them with callbacks passed from the UI.
onStatus: OnMessageCallback = () => {};
onDisconnect: OnDisconnectCallback = (_) => {};
// Starts a shell command, and returns a promise resolved when the command
// completes.
async shellAndWaitCompletion(cmd: string): Promise<void> {
const adbStream = await this.shell(cmd);
const onStreamingEnded = defer<void>();
// We wait for the stream to be closed by the device, which happens
// after the shell command is successfully received.
adbStream.addOnStreamCloseCallback(() => {
onStreamingEnded.resolve();
});
return onStreamingEnded;
}
// Starts a shell command, then gathers all its output and returns it as
// a string.
async shellAndGetOutput(cmd: string): Promise<string> {
const adbStream = await this.shell(cmd);
const commandOutput = new ArrayBufferBuilder();
const onStreamingEnded = defer<string>();
adbStream.addOnStreamDataCallback((data: Uint8Array) => {
commandOutput.append(data);
});
adbStream.addOnStreamCloseCallback(() => {
onStreamingEnded.resolve(
textDecoder.decode(commandOutput.toArrayBuffer()));
});
return onStreamingEnded;
}
async push(binary: Uint8Array, path: string): Promise<void> {
const byteStream = await this.openStream('sync:');
await (new AdbFileHandler(byteStream)).pushBinary(binary, path);
// We need to wait until the bytestream is closed. Otherwise, we can have a
// race condition:
// If this is the last stream, it will try to disconnect the device. In the
// meantime, the caller might create another stream which will try to open
// the device.
await byteStream.closeAndWaitForTeardown();
}
abstract shell(cmd: string): Promise<ByteStream>;
abstract canConnectWithoutContention(): Promise<boolean>;
abstract connectSocket(path: string): Promise<ByteStream>;
abstract disconnect(disconnectMessage?: string): Promise<void>;
protected abstract openStream(destination: string): Promise<ByteStream>;
}