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