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 {defer} from '../../../base/deferred'; 16import { 17 ByteStream, 18 OnStreamCloseCallback, 19 OnStreamDataCallback, 20} from './recording_interfaces_v2'; 21 22// A HostOsByteStream instantiates a websocket connection to the host OS. 23// It exposes an API to write commands to this websocket and read its output. 24export class HostOsByteStream implements ByteStream { 25 // handshakeSignal will be resolved with the stream when the websocket 26 // connection becomes open. 27 private handshakeSignal = defer<HostOsByteStream>(); 28 private _isConnected: boolean = false; 29 private websocket: WebSocket; 30 private onStreamDataCallbacks: OnStreamDataCallback[] = []; 31 private onStreamCloseCallbacks: OnStreamCloseCallback[] = []; 32 33 private constructor(websocketUrl: string) { 34 this.websocket = new WebSocket(websocketUrl); 35 this.websocket.onmessage = this.onMessage.bind(this); 36 this.websocket.onopen = this.onOpen.bind(this); 37 } 38 39 addOnStreamDataCallback(onStreamData: OnStreamDataCallback): void { 40 this.onStreamDataCallbacks.push(onStreamData); 41 } 42 43 addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback): void { 44 this.onStreamCloseCallbacks.push(onStreamClose); 45 } 46 47 close(): void { 48 this.websocket.close(); 49 for (const onStreamClose of this.onStreamCloseCallbacks) { 50 onStreamClose(); 51 } 52 this.onStreamDataCallbacks = []; 53 this.onStreamCloseCallbacks = []; 54 } 55 56 async closeAndWaitForTeardown(): Promise<void> { 57 this.close(); 58 } 59 60 isConnected(): boolean { 61 return this._isConnected; 62 } 63 64 write(msg: string | Uint8Array): void { 65 this.websocket.send(msg); 66 } 67 68 private async onMessage(evt: MessageEvent) { 69 for (const onStreamData of this.onStreamDataCallbacks) { 70 const arrayBufferResponse = await evt.data.arrayBuffer(); 71 onStreamData(new Uint8Array(arrayBufferResponse)); 72 } 73 } 74 75 private onOpen() { 76 this._isConnected = true; 77 this.handshakeSignal.resolve(this); 78 } 79 80 static create(websocketUrl: string): Promise<HostOsByteStream> { 81 return new HostOsByteStream(websocketUrl).handshakeSignal; 82 } 83} 84