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 {TraceConfig} from '../protos';
16
17// TargetFactory connects, disconnects and keeps track of targets.
18// There is one factory for AndroidWebusb, AndroidWebsocket, Chrome etc.
19// For instance, the AndroidWebusb factory returns a RecordingTargetV2 for each
20// device.
21export interface TargetFactory {
22  // Store the kind explicitly as a string as opposed to using class.kind in
23  // case we ever minify our code.
24  readonly kind: string;
25
26  // Setter for OnTargetChange, which is executed when a target is
27  // added/removed or when its information is updated.
28  setOnTargetChange(onTargetChange: OnTargetChangeCallback): void;
29
30  getName(): string;
31
32  listTargets(): RecordingTargetV2[];
33  // Returns recording problems that we encounter when not directly using the
34  // target. For instance we connect webusb devices when Perfetto is loaded. If
35  // there is an issue with connecting a webusb device, we do not want to crash
36  // all of Perfetto, as the user may not want to use the recording
37  // functionality at all.
38  listRecordingProblems(): string[];
39
40  connectNewTarget(): Promise<RecordingTargetV2>;
41}
42
43export interface DataSource {
44  name: string;
45
46  // Contains information that is opaque to the recording code. The caller can
47  // use the DataSource name to type cast the DataSource descriptor.
48  // For targets calling QueryServiceState, 'descriptor' will hold the
49  // datasource descriptor:
50  // https://source.corp.google.com/android/external/perfetto/protos/perfetto/
51  // common/data_source_descriptor.proto;l=28-60
52  // For Chrome, 'descriptor' will contain the answer received from
53  // 'GetCategories':
54  // https://source.corp.google.com/android/external/perfetto/ui/src/
55  // chrome_extension/chrome_tracing_controller.ts;l=220
56  descriptor: unknown;
57}
58
59// Common fields for all types of targetInfo: Chrome, Android, Linux etc.
60interface TargetInfoBase {
61  name: string;
62
63  // The dataSources exposed by a target. They are fetched from the target
64  // (ex: using QSS for Android or GetCategories for Chrome).
65  dataSources: DataSource[];
66}
67
68export interface AndroidTargetInfo extends TargetInfoBase {
69  targetType: 'ANDROID';
70
71  // This is the Android API level. For instance, it can be 32, 31, 30 etc.
72  // It is the "API level" column here:
73  // https://source.android.com/setup/start/build-numbers
74  androidApiLevel?: number;
75}
76
77export interface ChromeTargetInfo extends TargetInfoBase {
78  targetType: 'CHROME' | 'CHROME_OS' | 'WINDOWS';
79}
80
81export interface HostOsTargetInfo extends TargetInfoBase {
82  targetType: 'LINUX' | 'MACOS';
83}
84
85// Holds information about a target. It's used by the UI and the logic which
86// generates a config.
87export type TargetInfo =
88  | AndroidTargetInfo
89  | ChromeTargetInfo
90  | HostOsTargetInfo;
91
92// RecordingTargetV2 is subclassed by Android devices and the Chrome browser/OS.
93// It creates tracing sessions which are used by the UI. For Android, it manages
94// the connection with the device.
95export interface RecordingTargetV2 {
96  // Allows targets to surface target specific information such as
97  // well known key/value pairs: OS, targetType('ANDROID', 'CHROME', etc.)
98  getInfo(): TargetInfo;
99
100  // Disconnects the target.
101  disconnect(disconnectMessage?: string): Promise<void>;
102
103  // Returns true if we are able to connect to the target without interfering
104  // with other processes. For example, for adb devices connected over WebUSB,
105  // this will be false when we can not claim the interface (Which most likely
106  // means that 'adb server' is running locally.). After querrying this method,
107  // the caller can decide if they want to connect to the target and as a side
108  // effect take the connection away from other processes.
109  canConnectWithoutContention(): Promise<boolean>;
110
111  // Whether the recording target can be used in a tracing session. For example,
112  // virtual targets do not support a tracing session.
113  canCreateTracingSession(recordingMode?: string): boolean;
114
115  // Some target information can only be obtained after connecting to the
116  // target. This will establish a connection and retrieve data such as
117  // dataSources and apiLevel for Android.
118  fetchTargetInfo(
119    tracingSessionListener: TracingSessionListener,
120  ): Promise<void>;
121
122  createTracingSession(
123    tracingSessionListener: TracingSessionListener,
124  ): Promise<TracingSession>;
125}
126
127// TracingSession is used by the UI to record a trace. Depending on user
128// actions, the UI can start/stop/cancel a session. During the recording, it
129// provides updates about buffer usage. It is subclassed by
130// TracedTracingSession, which manages the communication with traced and has
131// logic for encoding/decoding Perfetto client requests/replies.
132export interface TracingSession {
133  // Starts the tracing session.
134  start(config: TraceConfig): void;
135
136  // Will stop the tracing session and NOT return any trace.
137  cancel(): void;
138
139  // Will stop the tracing session. The implementing class may also return
140  // the trace using a callback.
141  stop(): void;
142
143  // Returns the percentage of the trace buffer that is currently being
144  // occupied.
145  getTraceBufferUsage(): Promise<number>;
146}
147
148// Connection with an Adb device. Implementations will have logic specific to
149// the connection protocol used(Ex: WebSocket, WebUsb).
150export interface AdbConnection {
151  // Will push a binary to a given path.
152  push(binary: ArrayBuffer, path: string): Promise<void>;
153
154  // Will issue a shell command to the device.
155  shell(cmd: string): Promise<ByteStream>;
156
157  // Will establish a connection(a ByteStream) with the device.
158  connectSocket(path: string): Promise<ByteStream>;
159
160  // Returns true if we are able to connect without interfering
161  // with other processes. For example, for adb devices connected over WebUSB,
162  // this will be false when we can not claim the interface (Which most likely
163  // means that 'adb server' is running locally.).
164  canConnectWithoutContention(): Promise<boolean>;
165
166  // Ends the connection.
167  disconnect(disconnectMessage?: string): Promise<void>;
168}
169
170// A stream for a connection between a target and a tracing session.
171export interface ByteStream {
172  // The caller can add callbacks, to be executed when the stream receives new
173  // data or when it finished closing itself.
174  addOnStreamDataCallback(onStreamData: OnStreamDataCallback): void;
175  addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback): void;
176
177  isConnected(): boolean;
178  write(data: string | Uint8Array): void;
179
180  close(): void;
181  closeAndWaitForTeardown(): Promise<void>;
182}
183
184// Handles binary messages received over the ByteStream.
185export interface OnStreamDataCallback {
186  (data: Uint8Array): void;
187}
188
189// Called when the ByteStream is closed.
190export interface OnStreamCloseCallback {
191  (): void;
192}
193
194// OnTraceDataCallback will return the entire trace when it has been fully
195// assembled. This will be changed in the following CL aosp/2057640.
196export interface OnTraceDataCallback {
197  (trace: Uint8Array): void;
198}
199
200// Handles messages that are useful in the UI and that occur at any layer of the
201// recording (trace, connection). The messages includes both status messages and
202// error messages.
203export interface OnMessageCallback {
204  (message: string): void;
205}
206
207// Handles the loss of the connection at the connection layer (used by the
208// AdbConnection).
209export interface OnDisconnectCallback {
210  (errorMessage?: string): void;
211}
212
213// Called when there is a change of targets or within a target.
214// For instance, it's used when an Adb device becomes connected/disconnected.
215// It's also executed by a target when the information it stores gets updated.
216export interface OnTargetChangeCallback {
217  (): void;
218}
219
220// A collection of callbacks that is passed to RecordingTargetV2 and
221// subsequently to TracingSession. The callbacks are decided by the UI, so the
222// recording code is not coupled with the rendering logic.
223export interface TracingSessionListener {
224  onTraceData: OnTraceDataCallback;
225  onStatus: OnMessageCallback;
226  onDisconnect: OnDisconnectCallback;
227  onError: OnMessageCallback;
228}
229