Refactor code to use common connection.

None of the components should directly interact with the proxy because
non-android development workflow (using web adb) will also be possible
in the future. Instead the ui components interact with a common
connection interface which handles the proxy or web adb respectively.

Test: check that proxy workflow still works as expected.

Bug: b/234103636
Change-Id: I0e7bbaf4b5f342ca99dd67f890c135dbf3ef61ec
This commit is contained in:
Priyanka Patel
2022-07-19 10:32:09 +00:00
parent 88ea98f6ba
commit b6ee5d4794
14 changed files with 531 additions and 347 deletions

View File

@@ -22,11 +22,14 @@
"@ngrx/effects": "^14.0.2",
"@ngrx/store": "^14.0.2",
"@ngxs/store": "^3.7.4",
"@types/jsbn": "^1.2.30",
"angular2-template-loader": "^0.6.2",
"auth0": "^2.42.0",
"html-loader": "^3.1.0",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
"html-webpack-plugin": "^5.5.0",
"jsbn": "^1.1.0",
"jsbn-rsa": "^1.0.4",
"kotlin": "^1.7.0",
"kotlin-compiler": "^1.7.0",
"loader-utils": "^2.0.0",
@@ -46,7 +49,9 @@
"@angular/compiler-cli": "^14.0.0",
"@ngxs/devtools-plugin": "^3.7.4",
"@types/jasmine": "~4.0.0",
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.4",
"@types/w3c-web-usb": "^1.0.6",
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"eslint": "^8.19.0",
@@ -3385,6 +3390,20 @@
"integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==",
"dev": true
},
"node_modules/@types/jquery": {
"version": "3.5.14",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
"integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
"dev": true,
"dependencies": {
"@types/sizzle": "*"
}
},
"node_modules/@types/jsbn": {
"version": "1.2.30",
"resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz",
"integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q=="
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -3457,6 +3476,12 @@
"@types/node": "*"
}
},
"node_modules/@types/sizzle": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true
},
"node_modules/@types/sockjs": {
"version": "0.3.33",
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
@@ -3466,6 +3491,12 @@
"@types/node": "*"
}
},
"node_modules/@types/w3c-web-usb": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
"integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
"dev": true
},
"node_modules/@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@@ -6086,6 +6117,12 @@
"safer-buffer": "^2.1.0"
}
},
"node_modules/ecc-jsbn/node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -8923,10 +8960,14 @@
}
},
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/jsbn-rsa": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz",
"integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew=="
},
"node_modules/jsesc": {
"version": "2.5.2",
@@ -13872,6 +13913,12 @@
"node": ">=0.10.0"
}
},
"node_modules/sshpk/node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"node_modules/ssri": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
@@ -17993,6 +18040,20 @@
"integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==",
"dev": true
},
"@types/jquery": {
"version": "3.5.14",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
"integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
"dev": true,
"requires": {
"@types/sizzle": "*"
}
},
"@types/jsbn": {
"version": "1.2.30",
"resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz",
"integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q=="
},
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -18065,6 +18126,12 @@
"@types/node": "*"
}
},
"@types/sizzle": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true
},
"@types/sockjs": {
"version": "0.3.33",
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
@@ -18074,6 +18141,12 @@
"@types/node": "*"
}
},
"@types/w3c-web-usb": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz",
"integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==",
"dev": true
},
"@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@@ -20040,6 +20113,14 @@
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
},
"dependencies": {
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
}
}
},
"ecdsa-sig-formatter": {
@@ -22089,10 +22170,14 @@
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"jsbn-rsa": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz",
"integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew=="
},
"jsesc": {
"version": "2.5.2",
@@ -25850,6 +25935,14 @@
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"dependencies": {
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
}
}
},
"ssri": {

View File

@@ -30,11 +30,14 @@
"@ngrx/effects": "^14.0.2",
"@ngrx/store": "^14.0.2",
"@ngxs/store": "^3.7.4",
"@types/jsbn": "^1.2.30",
"angular2-template-loader": "^0.6.2",
"auth0": "^2.42.0",
"html-loader": "^3.1.0",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
"html-webpack-plugin": "^5.5.0",
"jsbn": "^1.1.0",
"jsbn-rsa": "^1.0.4",
"kotlin": "^1.7.0",
"kotlin-compiler": "^1.7.0",
"loader-utils": "^2.0.0",
@@ -54,7 +57,9 @@
"@angular/compiler-cli": "^14.0.0",
"@ngxs/devtools-plugin": "^3.7.4",
"@types/jasmine": "~4.0.0",
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.4",
"@types/w3c-web-usb": "^1.0.6",
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"eslint": "^8.19.0",

View File

@@ -39,6 +39,8 @@ import { PersistentStore } from "../common/persistent_store";
</div>
<div id="inputfile">
<button type="button" mat-raised-button (click)="fileInput.click()">Choose File</button>
<input hidden (change)="onInputFile($event)" #fileInput type="file" id="file">
<input type="file" (change)="onInputFile($event)" #fileUpload>
</div>

View File

@@ -20,7 +20,7 @@ import { AppComponent } from "./app.component";
import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component";
import { CollectTracesComponent } from "trace_collection/collect_traces.component";
import { AdbProxyComponent } from "trace_collection/adb_proxy.component";
import { WebAdbComponent } from "trace_collection/web_adb.component";
import { WebAdbComponent } from "trace_collection/web_adb/web_adb.component";
import { TraceConfigComponent } from "trace_collection/trace_config.component";
@NgModule({

View File

@@ -45,6 +45,10 @@ mat-form-field {
height: 5px;
}
mat-icon {
margin: 5px;
}
.card-block {
margin: 15px;
}

View File

@@ -16,13 +16,12 @@
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { ProxyClient, ProxyState } from "./proxy_client";
@Component({
selector: "adb-proxy",
template: `
<div *ngIf="proxy.state===states.NO_PROXY">
<div>
<mat-icon>error</mat-icon>
<mat-icon class="icon-message">error</mat-icon>
<span class="icon-message">Unable to connect to Winscope ADB proxy</span>
</div>
<div class="md-body-2" layout="layout-md">
@@ -30,7 +29,7 @@ import { ProxyClient, ProxyState } from "./proxy_client";
<p>Python 3.5+ and ADB are required.</p>
<p>Run:</p>
<pre>python3</pre>
<pre>$ANDROID_BUILD_TOP/development/tools/winscope/adb_proxy/winscope_proxy.py</pre>
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py</pre>
<p>Or get it from the AOSP repository.</p>
</div>
<div class="md-layout">
@@ -50,7 +49,7 @@ import { ProxyClient, ProxyState } from "./proxy_client";
<p>Please update the proxy to version {{ proxyVersion }}.</p>
<p>Run:</p>
<pre>python3</pre>
<pre>$ANDROID_BUILD_TOP/development/tools/winscope/adb_proxy/winscope_proxy.py</pre>
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py</pre>
<p>Or get it from the AOSP repository.</p>
</div>
<div class="md-layout">
@@ -77,32 +76,30 @@ import { ProxyClient, ProxyState } from "./proxy_client";
<button mat-raised-button class="md-accent" (click)="restart()">Connect</button>
</div>
</div>
`,
styles: [".proxy-key-field {width: 30rem}", ".icon-message {vertical-align: middle;}"]
})
export class AdbProxyComponent {
@Input()
proxy: any = {VERSION: 0.8};
@Output()
proxyChange = new EventEmitter<ProxyClient>();
@Output()
addKey = new EventEmitter<string>();
states = ProxyState;
proxyKeyItem = "";
@Input()
proxy: any = {};
readonly proxyVersion = this.proxy.VERSION;
readonly downloadProxyUrl: string = "https://android.googlesource.com/platform/development/+/master/tools/winscope/adb_proxy/winscope_proxy.py";
@Output()
proxyChange = new EventEmitter<ProxyClient>();
@Output() addKey = new EventEmitter<string>();
readonly downloadProxyUrl: string = "https://android.googlesource.com/platform/development/+/master/tools/winscope-ng/adb/winscope_proxy.py";
public restart() {
this.addKey.emit(this.proxyKeyItem);
this.proxy.setState(this.states.CONNECTING);
this.proxyChange.emit(this.proxy);
}
public triggerUnauthComponent() {
this.proxy.setState(this.states.UNAUTH);
this.proxyChange.emit(this.proxy);
}
}
}

View File

@@ -1,124 +1,106 @@
import {Component, Input, OnInit } from "@angular/core";
import { ProxyState, proxyClient, ProxyEndpoint } from "./proxy_client";
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, Input, OnInit} from "@angular/core";
import { Connection, ProxyConnection, Device } from "./connection";
import { ProxyState } from "./proxy_client";
import { traceConfigurations, configMap } from "./trace_collection_utils";
import { PersistentStore } from "../common/persistent_store";
interface TraceConfiguration {
name: string,
defaultCheck?: boolean,
config?: ConfigurationOptions
}
export interface ConfigurationOptions {
enableConfigs: Array<string>,
selectionConfigs: Array<SelectionConfiguration>
}
export interface SelectionConfiguration {
name: string,
options: Array<string>,
value: string
}
export type configMap = {
[key: string]: Array<string> | string;
}
interface Device {
authorised: boolean;
model: string;
}
@Component({
selector: "collect-traces",
template: `
<mat-card-title>Collect Traces</mat-card-title>
<mat-card-content>
<div *ngIf="proxy.state===states.CONNECTING">Connecting...</div>
<mat-card-title>Collect Traces</mat-card-title>
<mat-card-content>
<div id="set-up-adb" *ngIf="!adbSuccess()">
<button mat-raised-button [ngClass]="tabClass(true)" (click)="displayAdbProxyTab()">ADB Proxy</button>
<button mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button>
<adb-proxy *ngIf="isAdbProxy" [(proxy)]="proxy" (addKey)="onAddKey($event)"></adb-proxy>
<web-adb *ngIf="!isAdbProxy"></web-adb>
</div>
<div *ngIf="connect.state()===states.CONNECTING">Connecting...</div>
<div id="devices-connecting" *ngIf="proxy.state===states.DEVICES">
<div> {{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}</div>
<mat-list class="device-choice">
<mat-list-item *ngFor="let deviceId of devices()" (click)="selectDevice(deviceId)">
<mat-icon>
{{ proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }}
</mat-icon>
<span class="md-list-item-text">
{{ proxy.devices[deviceId].authorised ? proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }})
</span>
</mat-list-item>
</mat-list>
</div>
<div id="set-up-adb" *ngIf="!adbSuccess()">
<button mat-raised-button [ngClass]="tabClass(true)" (click)="displayAdbProxyTab()">ADB Proxy</button>
<button mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button>
<adb-proxy *ngIf="isAdbProxy" [(proxy)]="connect.proxy" (addKey)="onAddKey($event)"></adb-proxy>
<web-adb *ngIf="!isAdbProxy"></web-adb>
</div>
<div id="trace-collection-config" *ngIf="proxy.state===states.START_TRACE">
<div class="device-choice">
<div id="devices-connecting" *ngIf="connect.state()===states.DEVICES">
<div> {{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}</div>
<mat-list class="device-choice">
<mat-list-item>
<mat-icon>smartphone</mat-icon>
<span class="md-list-item-text">{{ selectedDevice().model }} ({{ proxy.selectedDevice }})</span>
</mat-list-item>
<mat-list-item *ngFor="let deviceId of devices()" (click)="selectDevice(deviceId)">
<mat-icon>{{ connect.proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }}</mat-icon>
<span class="md-list-item-text">{{ connect.proxy.devices[deviceId].authorised ? connect.proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }})</span>
</mat-list-item>
</mat-list>
</div>
<div class="trace-section">
<div class="md-layout">
<button mat-raised-button class="md-accent" (click)="startTracing()">Start Trace</button>
<button mat-raised-button (click)="dumpState()">Dump State</button>
<button mat-raised-button class="md-primary" (click)="resetLastDevice()">Change Device</button>
<div id="trace-collection-config" *ngIf="connect.state()===states.START_TRACE">
<div class="device-choice">
<mat-list class="device-choice">
<mat-list-item>
<mat-icon>smartphone</mat-icon>
<span class="md-list-item-text">{{ selectedDevice().model }} ({{ connect.proxy.selectedDevice }})</span>
</mat-list-item>
</mat-list>
</div>
<h3>Trace targets:</h3>
<trace-config
*ngFor="let trace of traceConfigurations"
[name]="trace.name"
[defaultCheck]="trace.defaultCheck"
[configs]="trace.config ? trace.config : null"
[(proxy)]="proxy"
></trace-config>
</div>
<div class="dump-section">
<h3>Dump targets:</h3>
<div class="selection">
<mat-checkbox
class="md-primary"
*ngFor="let dumpKey of objectKeys(DUMPS)"
[checked]="true">{{DUMPS[dumpKey]}}
</mat-checkbox>
<div class="trace-section">
<div class="md-layout">
<button mat-raised-button class="md-accent" (click)="startTracing()">Start Trace</button>
<button mat-raised-button (click)="dumpState()">Dump State</button>
<button mat-raised-button class="md-primary" (click)="resetLastDevice()">Change Device</button>
</div>
<h3>Trace targets:</h3>
<trace-config
*ngFor="let trace of traceConfigurations"
[name]="trace.name"
[defaultCheck]="trace.defaultCheck"
[configs]="trace.config ? trace.config : null"
></trace-config>
</div>
<div class="dump-section">
<h3>Dump targets:</h3>
<div class="selection">
<mat-checkbox class="md-primary" *ngFor="let dumpKey of objectKeys(DUMPS)" [checked]="true">{{DUMPS[dumpKey]}}</mat-checkbox>
</div>
</div>
</div>
</div>
<div id="unknown-error" *ngIf="proxy.state===states.ERROR">
<mat-icon>error</mat-icon>
<span class="md-subheading">Error:</span>
<pre>
{{ errorText }}
</pre>
<button mat-raised-button (click)="restart()">Retry</button>
</div>
<div id="unknown-error" *ngIf="connect.state()===states.ERROR">
<mat-icon>error</mat-icon>
<span class="md-subheading">Error:</span>
<pre>
{{ errorText }}
</pre>
<button mat-raised-button (click)="restart()">Retry</button>
</div>
<div id="end-tracing" *ngIf="proxy.state===states.END_TRACE">
<span class="md-subheading">Tracing...</span>
<mat-progress-bar md-indeterminate></mat-progress-bar>
<pre>
{{ errorText }}
</pre>
<button mat-raised-button (click)="endTrace()">End trace</button>
</div>
<div id="end-tracing" *ngIf="connect.state()===states.END_TRACE">
<span class="md-subheading">Tracing...</span>
<mat-progress-bar md-indeterminate></mat-progress-bar>
<pre>
{{ errorText }}
</pre>
<button mat-raised-button (click)="endTrace()">End trace</button>
</div>
<div id="load-data" *ngIf="proxy.state===states.LOAD_DATA">
<span class="md-subheading">Loading data...</span>
<mat-progress-bar md-indeterminate></mat-progress-bar>
</div>
<div id="load-data" *ngIf="connect.state()===states.LOAD_DATA">
<span class="md-subheading">Loading data...</span>
<mat-progress-bar md-indeterminate></mat-progress-bar>
</div>
</mat-card-content>
</mat-card-content>
`,
styles: [".device-choice {cursor: pointer}"]
})
@@ -128,92 +110,78 @@ export class CollectTracesComponent implements OnInit {
startTrace = false;
startDump = false;
errorText = "";
proxy: any = null;
downloadProxyUrl = "https://android.googlesource.com/platform/development/+/master/tools/winscope/adb_proxy/winscope_proxy.py";
states = ProxyState;
notConnected = [
this.states.NO_PROXY,
this.states.UNAUTH,
this.states.INVALID_VERSION,
];
traceConfigurations = traceConfigurations;
constructor() {
this.proxy = proxyClient;
}
connect: any = new ProxyConnection();
downloadProxyUrl = "https://android.googlesource.com/platform/development/+/master/tools/winscope-ng/adb/winscope_proxy.py";
states = ProxyState;
@Input()
store: PersistentStore = new PersistentStore();
ngOnInit(): void {
this.proxy.setState(ProxyState.CONNECTING);
this.proxy.onProxyChange(this.onProxyChange);
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("token")) {
this.proxy.proxyKey = urlParams.get("token")!;
if (this.isAdbProxy) {
this.connect = new ProxyConnection();
} else {
//TODO: change to WebAdbConnection
this.connect = new ProxyConnection();
}
this.proxy.getDevices();
}
ngOnDestroy(): void {
this.proxy.removeOnProxyChange(this.onProxyChange);
this.connect.proxy.removeOnProxyChange(this.onProxyChange);
}
public onAddKey(key: string) {
this.store.addToStore("adb.proxyKey", key);
proxyClient.proxyKey = key;
this.connect.setProxyKey(key);
this.restart();
}
public adbSuccess() {
return !this.notConnected.includes(this.proxy.state);
public onConnectChange(newState: Connection) {
this.connect.onConnectChange(newState);
}
public onProxyChange(newState: ProxyState, errorText: string) {
if (newState === ProxyState.CONNECTING) {
proxyClient.getDevices();
}
if (newState == ProxyState.START_TRACE) {
proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request: any,view:any) {
try {
if(request.responseText == "true") {
//TODO: add this function
//view.appendOptionalTraces('arc', view.TRACES);
}
} catch(err) {
console.error(err);
proxyClient.setState(ProxyState.ERROR, request.responseText);
}
});
}
this.connect.onConnectChange(newState);
}
public adbSuccess() {
return this.connect.adbSuccess();
}
public devices(): Array<string> {
return Object.keys(this.proxy.devices);
return this.connect.devices();
}
public selectedDevice(): Device {
return this.proxy.devices[this.proxy.selectedDevice];
return this.connect.selectedDevice();
}
public restart() {
this.proxy.setState(this.states.CONNECTING);
this.connect.restart();
}
public resetLastDevice() {
this.proxy.resetLastDevice();
this.restart();
this.connect.resetLastDevice();
}
public selectDevice(id: string) {
this.proxy.selectDevice(id);
this.connect.selectDevice(id);
}
public displayAdbProxyTab() {
this.isAdbProxy = true;
this.connect = new ProxyConnection();
}
public displayWebAdbTab() {
this.isAdbProxy = false;
//TODO: change to WebAdbConnection
this.connect = new ProxyConnection();
}
public startTracing() {
@@ -231,18 +199,7 @@ export class CollectTracesComponent implements OnInit {
}
public setAvailableTraces() {
this.DYNAMIC_TRACES = this.TRACES["default"];
proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request:any, view:any) {
try {
if(request.responseText == "true") {
//TODO: add this function
//view.appendOptionalTraces('arc');
}
} catch(err) {
console.error(err);
proxyClient.setState(ProxyState.ERROR, request.responseText);
}
});
this.connect.setAvailableTraces();
}
public tabClass(adbTab: boolean) {
@@ -255,43 +212,6 @@ export class CollectTracesComponent implements OnInit {
return ["tab", isActive];
}
TRACES = {
"default": {
"window_trace": {
name: "Window Manager",
},
"accessibility_trace": {
name: "Accessibility",
},
"layers_trace": {
name: "Surface Flinger",
},
"transactions": {
name: "Transaction",
},
"proto_log": {
name: "ProtoLog",
},
"screen_recording": {
name: "Screen Recording",
},
"ime_trace_clients": {
name: "Input Method Clients",
},
"ime_trace_service": {
name: "Input Method Service",
},
"ime_trace_managerservice": {
name: "Input Method Manager Service",
},
},
"arc": {
"wayland_trace": {
name: "Wayland",
},
},
};
DYNAMIC_TRACES: any = null;
DUMPS: configMap = {
@@ -299,79 +219,4 @@ export class CollectTracesComponent implements OnInit {
"layers_dump": "Surface Flinger"
};
wmTraceSelectionConfigs = [
{
name: "wmbuffersize (KB)",
options: [
"4000",
"8000",
"16000",
"32000",
],
value: "4000"
},
{
name: "tracingtype",
options: [
"frame",
"transaction",
],
value: "frame"
},
{
name: "tracinglevel",
options: [
"verbose",
"debug",
"critical",
],
value: "verbose"
},
];
traceConfigurations: Array<TraceConfiguration> = [
{
name: "Surface Flinger",
defaultCheck: true,
config: {
enableConfigs: ["composition","metadata","hwc","tracebuffers"],
selectionConfigs: [
{
name: "sfbuffersize (KB)",
options: ["4000","8000","16000","32000",],
value: "4000"
}
]
}
},
{
name: "Window Manager",
defaultCheck: true,
config: {
enableConfigs: [],
selectionConfigs: this.wmTraceSelectionConfigs,
}
},
{
name: "Screen Recording",
},
{
name: "Accessibility",
},
{
name: "Transaction",
},
{
name: "Input Method Clients",
defaultCheck: true,
},
{
name: "Input Method Service",
defaultCheck: true,
},
{
name: "Input Method Manager Service",
defaultCheck: true,
},
];
}

View File

@@ -0,0 +1,107 @@
import { proxyClient, ProxyState, ProxyEndpoint, ProxyClient } from "trace_collection/proxy_client";
import {TRACES, traceConfigurations} from "./trace_collection_utils";
export interface Device {
authorised: boolean;
model: string;
}
export interface Connection {
adbSuccess: () => boolean;
setProxyKey(key:string): any;
devices(): Array<string>;
selectedDevice(): Device;
restart(): any;
selectDevice(id:string): any;
DYNAMIC_TRACES: any;
state(): ProxyState;
onConnectChange(newState: any): any;
setAvailableTraces(): any;
resetLastDevice(): any;
}
export class ProxyConnection implements Connection {
DYNAMIC_TRACES: any;
proxy = proxyClient;
public state() {
return this.proxy.state;
}
public devices() {
return Object.keys(this.proxy.devices);
}
notConnected = [
ProxyState.NO_PROXY,
ProxyState.UNAUTH,
ProxyState.INVALID_VERSION,
];
constructor() {
this.proxy.setState(ProxyState.CONNECTING);
this.proxy.onProxyChange(this.onConnectChange);
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("token")) {
this.proxy.proxyKey = urlParams.get("token")!;
}
this.proxy.getDevices();
}
public onConnectChange(newState: ProxyState) {
if (newState === ProxyState.CONNECTING) {
proxyClient.getDevices();
}
if (newState == ProxyState.START_TRACE) {
proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request: any,view:any) {
try {
if(request.responseText == "true") {
//view.appendOptionalTraces('arc', view.TRACES);
}
} catch(err) {
console.error(err);
proxyClient.setState(ProxyState.ERROR, request.responseText);
}
});
}
}
public setProxyKey(key: string) {
proxyClient.proxyKey = key;
this.restart();
}
public adbSuccess() {
return !this.notConnected.includes(this.proxy.state);
}
public selectedDevice(): Device {
return this.proxy.devices[this.proxy.selectedDevice];
}
public restart() {
this.proxy.setState(ProxyState.CONNECTING);
}
public resetLastDevice() {
this.proxy.resetLastDevice();
this.restart();
}
public selectDevice(id: string) {
this.proxy.selectDevice(id);
}
public setAvailableTraces() {
this.DYNAMIC_TRACES = TRACES["default"];
proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request:any, view:any) {
try {
if(request.responseText == "true") {
//view.appendOptionalTraces('arc');
}
} catch(err) {
console.error(err);
proxyClient.setState(ProxyState.ERROR, request.responseText);
}
});
}
}

View File

@@ -58,7 +58,7 @@ export class ProxyClient {
call(method: string, path: string, view: any, onSuccess: any, type = null, jsonRequest = null) {
const request = new XMLHttpRequest();
const client: ProxyClient = this;
const client = this;
request.onreadystatechange = function() {
if (this.readyState !== 4) {
return;
@@ -88,7 +88,6 @@ export class ProxyClient {
if (lastKey !== null) {
client.proxyKey = lastKey;
}
request.setRequestHeader("Winscope-Token", client.proxyKey);
if (jsonRequest) {
const json = JSON.stringify(jsonRequest);
@@ -147,6 +146,7 @@ export class ProxyClient {
selectDevice(device_id: string) {
this.selectedDevice = device_id;
this.store.addToStore("adb.lastDevice", device_id);
this.lastDevice = device_id;
this.setState(ProxyState.START_TRACE);
}

View File

@@ -0,0 +1,145 @@
interface TraceConfiguration {
name: string,
defaultCheck?: boolean,
config?: ConfigurationOptions
}
export interface ConfigurationOptions {
enableConfigs: Array<EnableConfiguration>,
selectionConfigs: Array<SelectionConfiguration>
}
export interface EnableConfiguration {
name: string,
defaultCheck: boolean,
}
export interface SelectionConfiguration {
name: string,
options: Array<string>,
value: string
}
export type configMap = {
[key: string]: Array<string> | string;
}
export const TRACES = {
"default": {
"window_trace": {
name: "Window Manager",
},
"accessibility_trace": {
name: "Accessibility",
},
"layers_trace": {
name: "Surface Flinger",
},
"transactions": {
name: "Transaction",
},
"proto_log": {
name: "ProtoLog",
},
"screen_recording": {
name: "Screen Recording",
},
"ime_trace_clients": {
name: "Input Method Clients",
},
"ime_trace_service": {
name: "Input Method Service",
},
"ime_trace_managerservice": {
name: "Input Method Manager Service",
},
},
"arc": {
"wayland_trace": {
name: "Wayland",
},
},
};
const wmTraceSelectionConfigs = [
{
name: "wmbuffersize (KB)",
options: [
"4000",
"8000",
"16000",
"32000",
],
value: "4000"
},
{
name: "tracingtype",
options: [
"frame",
"transaction",
],
value: "frame"
},
{
name: "tracinglevel",
options: [
"verbose",
"debug",
"critical",
],
value: "verbose"
},
];
export const traceConfigurations: Array<TraceConfiguration> = [
{
name: "Surface Flinger",
defaultCheck: true,
config: {
enableConfigs: [
{name: "composition", defaultCheck: false},
{name: "metadata", defaultCheck: false},
{name: "hwc", defaultCheck: false},
{name: "tracebuffers", defaultCheck: false}
],
selectionConfigs: [
{
name: "sfbuffersize (KB)",
options: ["4000","8000","16000","32000",],
value: "4000"
}
]
}
},
{
name: "Window Manager",
defaultCheck: true,
config: {
enableConfigs: [],
selectionConfigs: wmTraceSelectionConfigs,
}
},
{
name: "Screen Recording",
},
{
name: "Accessibility",
},
{
name: "Transaction",
},
{
name: "IME Tracing",
defaultCheck: true,
config: {
enableConfigs: [
{name: "Input Method Clients", defaultCheck: true},
{name: "Input Method Service", defaultCheck: true},
{name: "Input Method Manager Service", defaultCheck: true},
],
selectionConfigs: []
}
},
];

View File

@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigurationOptions, SelectionConfiguration } from "./collect_traces.component";
import { ConfigurationOptions, SelectionConfiguration } from "./trace_collection_utils";
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { ProxyState, ProxyClient } from "./proxy_client";
@@ -22,22 +21,22 @@ import { ProxyState, ProxyClient } from "./proxy_client";
@Component({
selector: "trace-config",
template: `
<div class="card-block" *ngIf="proxy.state===states.START_TRACE">
<mat-checkbox class="md-primary" [checked]="defaultCheck">{{name}}</mat-checkbox>
<div class="adv-config" *ngIf="configs">
<mat-checkbox class="md-primary" *ngFor="let enableConfig of traceEnableConfigs()">{{enableConfig}}</mat-checkbox>
<div class="selection">
<mat-form-field appearance="fill" class="config-selection" *ngFor="let con of traceSelectionConfigs()">
<mat-label>{{con.name}}</mat-label>
<mat-select>
<mat-option
*ngFor="let option of con.options"
[value]="con.value"
>{{ option }}</mat-option>
</mat-select>
</mat-form-field>
<div class="card-block">
<mat-checkbox class="md-primary" [checked]="defaultCheck">{{name}}</mat-checkbox>
<div class="adv-config" *ngIf="configs">
<mat-checkbox class="md-primary" *ngFor="let enableConfig of traceEnableConfigs()" [checked]="enableConfig.defaultCheck">{{enableConfig.name}}</mat-checkbox>
<div class="selection">
<mat-form-field appearance="fill" class="config-selection" *ngFor="let con of traceSelectionConfigs()">
<mat-label>{{con.name}}</mat-label>
<mat-select>
<mat-option
*ngFor="let option of con.options"
[value]="con.value"
>{{ option }}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</div>
`,
styles: [".adv-config {margin-left: 5rem;}"],
@@ -48,21 +47,15 @@ export class TraceConfigComponent {
objectKeys = Object.keys;
@Input()
name = "";
name = "";
@Input()
configs: ConfigurationOptions | null = null;
configs: ConfigurationOptions | null = null;
@Input()
defaultCheck: boolean | undefined = false;
defaultCheck: boolean | undefined = false;
@Input()
proxy: any = null;
@Output()
proxyChange = new EventEmitter<ProxyClient>();
public traceEnableConfigs(): Array<string> {
public traceEnableConfigs(): Array<any> {
if (this.configs && this.configs.enableConfigs) {
return this.configs.enableConfigs;
} else {
@@ -77,13 +70,4 @@ export class TraceConfigComponent {
return [];
}
}
public restart() {
this.proxy.setState(this.states.CONNECTING);
this.proxyChange.emit(this.proxy);
}
public resetLastDevice() {
this.restart();
}
}
}

View File

@@ -15,21 +15,23 @@
*/
import {Component} from "@angular/core";
@Component({
selector: "web-adb",
template: `
<div class="title">Connect a new device</div>
<div class="md-body-2">
<p>Follow instructions in the Chrome pop-up.</p>
</div>
<div class="md-layout">
<button mat-raised-button class="md-accent" (click)="restart()">Retry</button>
</div>
<div>
<mat-icon class="icon-message">info</mat-icon>
<span class="icon-message">Add new device</span>
</div>
<div class="md-body-2">
<p>Click the button below to follow instructions in the Chrome pop-up.</p>
<p>Selecting a device will kill all existing ADB connections.</p>
</div>
<div class="md-layout">
<button mat-raised-button class="md-accent">Select a device</button>
</div>
`,
styles: [".icon-message {vertical-align: middle;}"]
})
export class WebAdbComponent {
public restart() {
console.log("Try connecting again");
}
adbDevice: any = null;
}