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 m from 'mithril'; 16import { 17 RecordingPageController, 18} from '../../common/recordingV2/recording_page_controller'; 19import {EXTENSION_URL} from '../../common/recordingV2/recording_utils'; 20import { 21 CHROME_TARGET_FACTORY, 22 ChromeTargetFactory, 23} from '../../common/recordingV2/target_factories/chrome_target_factory'; 24import { 25 targetFactoryRegistry, 26} from '../../common/recordingV2/target_factory_registry'; 27import { 28 WebsocketMenuController, 29} from '../../common/recordingV2/websocket_menu_controller'; 30import {fullscreenModalContainer, ModalDefinition} from '../modal'; 31import {CodeSnippet} from '../record_widgets'; 32 33import {RecordingMultipleChoice} from './recording_multiple_choice'; 34 35const RUN_WEBSOCKET_CMD = '# Get tracebox\n' + 36 'curl -LO https://get.perfetto.dev/tracebox\n' + 37 'chmod +x ./tracebox\n' + 38 '# Option A - trace android devices\n' + 39 'adb start-server\n' + 40 '# Option B - trace the host OS\n' + 41 './tracebox traced --background\n' + 42 './tracebox traced_probes --background\n' + 43 '# Start the websocket server\n' + 44 './tracebox websocket_bridge\n'; 45 46export function addNewTarget(recordingPageController: RecordingPageController): 47 ModalDefinition { 48 const components = []; 49 components.push(m('text', 'Select platform:')); 50 51 components.push(assembleWebusbSection(recordingPageController)); 52 53 components.push(m('.line')); 54 components.push(assembleWebsocketSection(recordingPageController)); 55 56 components.push(m('.line')); 57 components.push(assembleChromeSection(recordingPageController)); 58 59 return { 60 title: 'Add new recording target', 61 content: m('.record-modal', components), 62 }; 63} 64 65function assembleWebusbSection( 66 recordingPageController: RecordingPageController): m.Vnode { 67 return m( 68 '.record-modal-section', 69 m('.logo-wrapping', m('i.material-icons', 'usb')), 70 m('.record-modal-description', 71 m('h3', 'Android device over WebUSB'), 72 m('text', 73 'Android developers: this option cannot co-operate ' + 74 'with the adb host on your machine. Only one entity between ' + 75 'the browser and adb can control the USB endpoint. If adb is ' + 76 'running, you will be prompted to re-assign the device to the ' + 77 'browser. Use the websocket option below to use both ' + 78 'simultaneously.'), 79 m('.record-modal-button', 80 { 81 onclick: () => { 82 fullscreenModalContainer.close(); 83 recordingPageController.addAndroidDevice(); 84 }, 85 }, 86 'Connect new WebUSB driver'))); 87} 88 89function assembleWebsocketSection( 90 recordingPageController: RecordingPageController): m.Vnode { 91 const websocketComponents = []; 92 websocketComponents.push( 93 m('h3', 'Android / Linux / MacOS device via Websocket')); 94 websocketComponents.push( 95 m('text', 96 'This option assumes that the adb server is already ' + 97 'running on your machine.'), 98 m('.record-modal-command', m(CodeSnippet, { 99 text: RUN_WEBSOCKET_CMD, 100 }))); 101 102 websocketComponents.push(m( 103 '.record-modal-command', 104 m('text', 'Websocket bridge address: '), 105 m('input[type=text]', { 106 value: websocketMenuController.getPath(), 107 oninput() { 108 websocketMenuController.setPath(this.value); 109 }, 110 }), 111 m('.record-modal-logo-button', 112 { 113 onclick: () => websocketMenuController.onPathChange(), 114 }, 115 m('i.material-icons', 'refresh')), 116 )); 117 118 websocketComponents.push(m(RecordingMultipleChoice, { 119 controller: recordingPageController, 120 targetFactories: websocketMenuController.getTargetFactories(), 121 })); 122 123 return m( 124 '.record-modal-section', 125 m('.logo-wrapping', m('i.material-icons', 'settings_ethernet')), 126 m('.record-modal-description', ...websocketComponents)); 127} 128 129function assembleChromeSection( 130 recordingPageController: RecordingPageController): m.Vnode|undefined { 131 if (!targetFactoryRegistry.has(CHROME_TARGET_FACTORY)) { 132 return undefined; 133 } 134 135 const chromeComponents = []; 136 chromeComponents.push(m('h3', 'Chrome Browser instance or ChromeOS device')); 137 138 const chromeFactory: ChromeTargetFactory = 139 targetFactoryRegistry.get(CHROME_TARGET_FACTORY) as ChromeTargetFactory; 140 141 if (!chromeFactory.isExtensionInstalled) { 142 chromeComponents.push( 143 m('text', 144 'Install the extension ', 145 m('a', {href: EXTENSION_URL, target: '_blank'}, 'from this link '), 146 'and refresh the page.')); 147 } else { 148 chromeComponents.push(m(RecordingMultipleChoice, { 149 controller: recordingPageController, 150 targetFactories: [chromeFactory], 151 })); 152 } 153 154 return m( 155 '.record-modal-section', 156 m('.logo-wrapping', m('i.material-icons', 'web')), 157 m('.record-modal-description', ...chromeComponents)); 158} 159 160const websocketMenuController = new WebsocketMenuController(); 161