Edit SF and WM config from winscope.

Can now set the buffer sizes for these two traces, as well as set
tracing level and type for WM. Tested for multiple combinations of
config settings, as well as if settings are left as
placeholders/selected as the name of the config.

Bug: b/200159899

Test: Run a SF/WM trace with arbitrary config settings and check that all loads as normal.

Merged-In: I50ccc271bd2f90dba1d22c3e8c568490c4bf831f
Change-Id: I50ccc271bd2f90dba1d22c3e8c568490c4bf831f
(cherry picked from commit 9d3f674027e4380339b1c879f61380117e17fe5e)
diff --git a/tools/winscope/adb_proxy/winscope_proxy.py b/tools/winscope/adb_proxy/winscope_proxy.py
index 8c16e96..344c3cc 100755
--- a/tools/winscope/adb_proxy/winscope_proxy.py
+++ b/tools/winscope/adb_proxy/winscope_proxy.py
@@ -203,6 +203,52 @@
     def command(self) -> str:
         return f'su root service call SurfaceFlinger 1033 i32 {self.flags}'
 
+class SurfaceFlingerTraceSelectedConfig:
+    """Handles optional selected configuration for surfaceflinger traces.
+    """
+
+    def __init__(self) -> None:
+        # defaults set for all configs
+        self.selectedConfigs = {
+            "sfbuffersize": "16000"
+        }
+
+    def add(self, configType, configValue) -> None:
+        self.selectedConfigs[configType] = configValue
+
+    def is_valid(self, configType) -> bool:
+        return configType in CONFIG_SF_SELECTION
+
+    def setBufferSize(self) -> str:
+        return f'su root service call SurfaceFlinger 1029 i32 {self.selectedConfigs["sfbuffersize"]}'
+
+class WindowManagerTraceSelectedConfig:
+    """Handles optional selected configuration for windowmanager traces.
+    """
+
+    def __init__(self) -> None:
+        # defaults set for all configs
+        self.selectedConfigs = {
+            "wmbuffersize": "16000",
+            "tracinglevel": "all",
+            "tracingtype": "frame",
+        }
+
+    def add(self, configType, configValue) -> None:
+        self.selectedConfigs[configType] = configValue
+
+    def is_valid(self, configType) -> bool:
+        return configType in CONFIG_WM_SELECTION
+
+    def setBufferSize(self) -> str:
+        return f'su root cmd window tracing size {self.selectedConfigs["wmbuffersize"]}'
+
+    def setTracingLevel(self) -> str:
+        return f'su root cmd window tracing level {self.selectedConfigs["tracinglevel"]}'
+
+    def setTracingType(self) -> str:
+        return f'su root cmd window tracing {self.selectedConfigs["tracingtype"]}'
+
 
 CONFIG_FLAG = {
     "composition": 1 << 2,
@@ -210,6 +256,17 @@
     "hwc": 1 << 4
 }
 
+#Keep up to date with options in DataAdb.vue
+CONFIG_SF_SELECTION = [
+    "sfbuffersize",
+]
+
+#Keep up to date with options in DataAdb.vue
+CONFIG_WM_SELECTION = [
+    "wmbuffersize",
+    "tracingtype",
+    "tracinglevel",
+]
 
 class DumpTarget:
     """Defines a single parameter to trace.
@@ -610,6 +667,18 @@
                     "utf-8"))
 
 
+def execute_command(server, device_id, shell, configType, configValue):
+    process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                   stdin=subprocess.PIPE, start_new_session=True)
+    log.debug(f"Changing trace config on device {device_id} {configType}:{configValue}")
+    out, err = process.communicate(configValue.encode('utf-8'))
+    if process.returncode != 0:
+        raise AdbError(
+            f"Error executing command:\n {configValue}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}")
+    log.debug(f"Changing trace config finished on device {device_id}")
+    server.respond(HTTPStatus.OK, b'', "text/plain")
+
+
 class ConfigTrace(DeviceRequestEndpoint):
     def process_with_device(self, server, path, device_id):
         try:
@@ -630,15 +699,50 @@
         command = config.command()
         shell = ['adb', '-s', device_id, 'shell']
         log.debug(f"Starting shell {' '.join(shell)}")
-        process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                                   stdin=subprocess.PIPE, start_new_session=True)
-        log.debug(f"Changing trace config on device {device_id} cmd:{command}")
-        out, err = process.communicate(command.encode('utf-8'))
-        if process.returncode != 0:
-            raise AdbError(
-                f"Error executing command:\n {command}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}")
-        log.debug(f"Changing trace config finished on device {device_id}")
-        server.respond(HTTPStatus.OK, b'', "text/plain")
+        execute_command(server, device_id, shell, "sf buffer size", command)
+
+
+def add_selected_request_to_config(self, server, device_id, config):
+    try:
+        requested_configs = self.get_request(server)
+        for requested_config in requested_configs:
+            if config.is_valid(requested_config):
+                config.add(requested_config, requested_configs[requested_config])
+            else:
+                raise BadRequest(
+                        f"Unsupported config {requested_config}\n")
+    except KeyError as err:
+        raise BadRequest("Unsupported trace target\n" + str(err))
+    if device_id in TRACE_THREADS:
+        BadRequest(f"Trace in progress for {device_id}")
+    if not check_root(device_id):
+        raise AdbError(
+            f"Unable to acquire root privileges on the device - check the output of 'adb -s {device_id} shell su root id'")
+    return config
+
+
+class SurfaceFlingerSelectedConfigTrace(DeviceRequestEndpoint):
+    def process_with_device(self, server, path, device_id):
+        config = SurfaceFlingerTraceSelectedConfig()
+        config = add_selected_request_to_config(self, server, device_id, config)
+        setBufferSize = config.setBufferSize()
+        shell = ['adb', '-s', device_id, 'shell']
+        log.debug(f"Starting shell {' '.join(shell)}")
+        execute_command(server, device_id, shell, "sf buffer size", setBufferSize)
+
+
+class WindowManagerSelectedConfigTrace(DeviceRequestEndpoint):
+    def process_with_device(self, server, path, device_id):
+        config = WindowManagerTraceSelectedConfig()
+        config = add_selected_request_to_config(self, server, device_id, config)
+        setBufferSize = config.setBufferSize()
+        setTracingType = config.setTracingType()
+        setTracingLevel = config.setTracingLevel()
+        shell = ['adb', '-s', device_id, 'shell']
+        log.debug(f"Starting shell {' '.join(shell)}")
+        execute_command(server, device_id, shell, "wm buffer size", setBufferSize)
+        execute_command(server, device_id, shell, "tracing type", setTracingType)
+        execute_command(server, device_id, shell, "tracing level", setTracingLevel)
 
 
 class StatusEndpoint(DeviceRequestEndpoint):
@@ -691,6 +795,10 @@
         self.router.register_endpoint(RequestType.POST, "dump", DumpEndpoint())
         self.router.register_endpoint(
             RequestType.POST, "configtrace", ConfigTrace())
+        self.router.register_endpoint(
+            RequestType.POST, "selectedsfconfigtrace", SurfaceFlingerSelectedConfigTrace())
+        self.router.register_endpoint(
+            RequestType.POST, "selectedwmconfigtrace", WindowManagerSelectedConfigTrace())
         super().__init__(request, client_address, server)
 
     def respond(self, code: int, data: bytes, mime: str) -> None:
diff --git a/tools/winscope/src/DataAdb.vue b/tools/winscope/src/DataAdb.vue
index 1cffa61..6490f39 100644
--- a/tools/winscope/src/DataAdb.vue
+++ b/tools/winscope/src/DataAdb.vue
@@ -86,10 +86,29 @@
         <div class="selection">
           <md-checkbox class="md-primary" v-for="traceKey in Object.keys(TRACES)" :key="traceKey" v-model="adbStore[traceKey]">{{TRACES[traceKey].name}}</md-checkbox>
         </div>
-        <div class="trace-config" v-for="traceKey in Object.keys(TRACE_CONFIG)" :key="traceKey">
-            <h4>{{TRACES[traceKey].name}} config</h4>
+        <div class="trace-config">
+            <h4>Surface Flinger config</h4>
             <div class="selection">
-              <md-checkbox class="md-primary" v-for="config in TRACE_CONFIG[traceKey]" :key="config" v-model="adbStore[config]">{{config}}</md-checkbox>
+              <md-checkbox class="md-primary" v-for="config in TRACE_CONFIG['layers_trace']" :key="config" v-model="adbStore[config]">{{config}}</md-checkbox>
+              <div class="selection">
+                <md-field class="config-selection" v-for="selectConfig in Object.keys(SF_SELECTED_CONFIG)" :key="selectConfig">
+                  <md-select v-model="SF_SELECTED_CONFIG_VALUES[selectConfig]" :placeholder="selectConfig">
+                    <md-option value="">{{selectConfig}}</md-option>
+                    <md-option v-for="option in SF_SELECTED_CONFIG[selectConfig]" :key="option" :value="option">{{ option }}</md-option>
+                  </md-select>
+                </md-field>
+              </div>
+            </div>
+        </div>
+        <div class="trace-config">
+            <h4>Window Manager config</h4>
+            <div class="selection">
+              <md-field class="config-selection" v-for="selectConfig in Object.keys(WM_SELECTED_CONFIG)" :key="selectConfig">
+                <md-select v-model="WM_SELECTED_CONFIG_VALUES[selectConfig]" :placeholder="selectConfig">
+                  <md-option value="">{{selectConfig}}</md-option>
+                  <md-option v-for="option in WM_SELECTED_CONFIG[selectConfig]" :key="option" :value="option">{{ option }}</md-option>
+                </md-select>
+              </md-field>
             </div>
         </div>
         <md-button class="md-primary trace-btn" @click="startTrace">Start trace</md-button>
@@ -149,6 +168,8 @@
   START_TRACE: '/start/',
   END_TRACE: '/end/',
   CONFIG_TRACE: '/configtrace/',
+  SELECTED_WM_CONFIG_TRACE: '/selectedwmconfigtrace/',
+  SELECTED_SF_CONFIG_TRACE: '/selectedsfconfigtrace/',
   DUMP: '/dump/',
   FETCH: '/fetch/',
   STATUS: '/status/',
@@ -192,6 +213,33 @@
   ],
 };
 
+const SF_SELECTED_CONFIG = {
+  'sfbuffersize': [
+    '4000',
+    '8000',
+    '16000',
+    '32000',
+  ],
+};
+
+const WM_SELECTED_CONFIG = {
+  'wmbuffersize': [
+    '4000',
+    '8000',
+    '16000',
+    '32000',
+  ],
+  'tracingtype': [
+    'frame',
+    'transaction',
+  ],
+  'tracinglevel': [
+    'all',
+    'trim',
+    'critical',
+  ],
+};
+
 const DUMPS = {
   'window_dump': {
     name: 'Window Manager',
@@ -228,6 +276,10 @@
       STATES,
       TRACES,
       TRACE_CONFIG,
+      SF_SELECTED_CONFIG,
+      WM_SELECTED_CONFIG,
+      SF_SELECTED_CONFIG_VALUES: {},
+      WM_SELECTED_CONFIG_VALUES: {},
       DUMPS,
       FILE_DECODERS,
       WINSCOPE_PROXY_VERSION,
@@ -292,7 +344,7 @@
         this.keep_alive_worker = null;
         return;
       }
-      this.callProxy('GET', PROXY_ENDPOINTS.STATUS + this.deviceId() + '/', this, function(request, view) {
+      this.callProxy('GET', `${PROXY_ENDPOINTS.STATUS}${this.deviceId()}/`, this, function(request, view) {
         if (request.responseText !== 'True') {
           view.endTrace();
         } else if (view.keep_alive_worker === null) {
@@ -303,16 +355,21 @@
     startTrace() {
       const requested = this.toTrace();
       const requestedConfig = this.toTraceConfig();
+      const requestedSelectedSfConfig = this.toSelectedSfTraceConfig();
+      const requestedSelectedWmConfig = this.toSelectedWmTraceConfig();
       if (requested.length < 1) {
         this.errorText = 'No targets selected';
         this.status = STATES.ERROR;
         this.newEventOccurred("No targets selected");
         return;
       }
+
       this.newEventOccurred("Start Trace");
-      this.callProxy('POST', PROXY_ENDPOINTS.CONFIG_TRACE + this.deviceId() + '/', this, null, null, requestedConfig);
+      this.callProxy('POST', `${PROXY_ENDPOINTS.CONFIG_TRACE}${this.deviceId()}/`, this, null, null, requestedConfig);
+      this.callProxy('POST', `${PROXY_ENDPOINTS.SELECTED_SF_CONFIG_TRACE}${this.deviceId()}/`, this, null, null, requestedSelectedSfConfig);
+      this.callProxy('POST',  `${PROXY_ENDPOINTS.SELECTED_WM_CONFIG_TRACE}${this.deviceId()}/`, this, null, null, requestedSelectedWmConfig);
       this.status = STATES.END_TRACE;
-      this.callProxy('POST', PROXY_ENDPOINTS.START_TRACE + this.deviceId() + '/', this, function(request, view) {
+      this.callProxy('POST', `${PROXY_ENDPOINTS.START_TRACE}${this.deviceId()}/`, this, function(request, view) {
         view.keepAliveTrace();
       }, null, requested);
     },
@@ -326,19 +383,19 @@
         return;
       }
       this.status = STATES.LOAD_DATA;
-      this.callProxy('POST', PROXY_ENDPOINTS.DUMP + this.deviceId() + '/', this, function(request, view) {
+      this.callProxy('POST', `${PROXY_ENDPOINTS.DUMP}${this.deviceId()}/`, this, function(request, view) {
         view.loadFile(requested, 0);
       }, null, requested);
     },
     endTrace() {
       this.status = STATES.LOAD_DATA;
-      this.callProxy('POST', PROXY_ENDPOINTS.END_TRACE + this.deviceId() + '/', this, function(request, view) {
+      this.callProxy('POST', `${PROXY_ENDPOINTS.END_TRACE}${this.deviceId()}/`, this, function(request, view) {
         view.loadFile(view.toTrace(), 0);
       });
       this.newEventOccurred("Ended Trace");
     },
     loadFile(files, idx) {
-      this.callProxy('GET', PROXY_ENDPOINTS.FETCH + this.deviceId() + '/' + files[idx] + '/', this, function(request, view) {
+      this.callProxy('GET', `${PROXY_ENDPOINTS.FETCH}${this.deviceId()}/${files[idx]}/`, this, function(request, view) {
         try {
           const enc = new TextDecoder('utf-8');
           const resp = enc.decode(request.response);
@@ -380,6 +437,24 @@
           .flatMap((file) => TRACE_CONFIG[file])
           .filter((config) => this.adbStore[config]);
     },
+    toSelectedSfTraceConfig() {
+      const requestedSelectedConfig = {};
+      for (const config in this.SF_SELECTED_CONFIG_VALUES) {
+        if (this.SF_SELECTED_CONFIG_VALUES[config] !== "") {
+          requestedSelectedConfig[config] = this.SF_SELECTED_CONFIG_VALUES[config];
+        }
+      }
+      return requestedSelectedConfig;
+    },
+    toSelectedWmTraceConfig() {
+      const requestedSelectedConfig = {};
+      for (const config in this.WM_SELECTED_CONFIG_VALUES) {
+        if (this.WM_SELECTED_CONFIG_VALUES[config] !== "") {
+          requestedSelectedConfig[config] = this.WM_SELECTED_CONFIG_VALUES[config];
+        }
+      }
+      return requestedSelectedConfig;
+    },
     toDump() {
       return Object.keys(DUMPS)
           .filter((dumpKey) => this.adbStore[dumpKey]);
@@ -459,6 +534,12 @@
 
 </script>
 <style scoped>
+.config-selection {
+  width: 150px;
+  display: inline-flex;
+  margin-left: 5px;
+  margin-right: 5px;
+}
 .device-choice {
   display: inline-flex;
 }