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