• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2022 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {_TextDecoder} from 'custom_utils';
16
17import {defer} from '../../base/deferred';
18import {ArrayBufferBuilder} from '../array_buffer_builder';
19
20import {AdbFileHandler} from './adb_file_handler';
21import {
22  AdbConnection,
23  ByteStream,
24  OnDisconnectCallback,
25  OnMessageCallback,
26} from './recording_interfaces_v2';
27
28const textDecoder = new _TextDecoder();
29
30export abstract class AdbConnectionImpl implements AdbConnection {
31  // onStatus and onDisconnect are set to callbacks passed from the caller.
32  // This happens for instance in the AndroidWebusbTarget, which instantiates
33  // them with callbacks passed from the UI.
34  onStatus: OnMessageCallback = () => {};
35  onDisconnect: OnDisconnectCallback = (_) => {};
36
37  // Starts a shell command, and returns a promise resolved when the command
38  // completes.
39  async shellAndWaitCompletion(cmd: string): Promise<void> {
40    const adbStream = await this.shell(cmd);
41    const onStreamingEnded = defer<void>();
42
43    // We wait for the stream to be closed by the device, which happens
44    // after the shell command is successfully received.
45    adbStream.addOnStreamCloseCallback(() => {
46      onStreamingEnded.resolve();
47    });
48    return onStreamingEnded;
49  }
50
51  // Starts a shell command, then gathers all its output and returns it as
52  // a string.
53  async shellAndGetOutput(cmd: string): Promise<string> {
54    const adbStream = await this.shell(cmd);
55    const commandOutput = new ArrayBufferBuilder();
56    const onStreamingEnded = defer<string>();
57
58    adbStream.addOnStreamDataCallback((data: Uint8Array) => {
59      commandOutput.append(data);
60    });
61    adbStream.addOnStreamCloseCallback(() => {
62      onStreamingEnded.resolve(
63          textDecoder.decode(commandOutput.toArrayBuffer()));
64    });
65    return onStreamingEnded;
66  }
67
68  async push(binary: Uint8Array, path: string): Promise<void> {
69    const byteStream = await this.openStream('sync:');
70    await (new AdbFileHandler(byteStream)).pushBinary(binary, path);
71    // We need to wait until the bytestream is closed. Otherwise, we can have a
72    // race condition:
73    // If this is the last stream, it will try to disconnect the device. In the
74    // meantime, the caller might create another stream which will try to open
75    // the device.
76    await byteStream.closeAndWaitForTeardown();
77  }
78
79  abstract shell(cmd: string): Promise<ByteStream>;
80
81  abstract canConnectWithoutContention(): Promise<boolean>;
82
83  abstract connectSocket(path: string): Promise<ByteStream>;
84
85  abstract disconnect(disconnectMessage?: string): Promise<void>;
86
87  protected abstract openStream(destination: string): Promise<ByteStream>;
88}
89