xref: /aosp_15_r20/development/tools/winscope/src/parsers/operations/translate_intdef.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import intDefMapping from 'common/intDefMapping.json';
18import {TamperedProtoField} from 'parsers/tampered_message_type';
19import {FixedStringFormatter} from 'trace/tree_node/formatters';
20import {Operation} from 'trace/tree_node/operations/operation';
21import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
22
23export class TranslateIntDef implements Operation<PropertyTreeNode> {
24  constructor(private readonly rootField: TamperedProtoField) {}
25
26  apply(value: PropertyTreeNode, parentField = this.rootField): void {
27    const protoType = parentField.tamperedMessageType;
28
29    if (protoType === undefined) {
30      return;
31    }
32
33    let field = parentField;
34    if (field.name !== value.name) {
35      field = protoType.fields[value.name] ?? parentField;
36    }
37
38    if (value.getAllChildren().length > 0) {
39      value.getAllChildren().forEach((value) => {
40        this.apply(value, field);
41      });
42    } else {
43      const propertyValue = Number(value.getValue());
44      if (!Number.isNaN(propertyValue) && propertyValue !== -1) {
45        const translation = this.translateIntDefToStringIfNeeded(
46          propertyValue,
47          field,
48        );
49        if (typeof translation === 'string') {
50          value.setFormatter(new FixedStringFormatter(translation));
51        }
52      }
53    }
54  }
55
56  private translateIntDefToStringIfNeeded(
57    value: number,
58    field: TamperedProtoField,
59  ): string | number {
60    const typeDefSpec = this.getTypeDefSpecFromField(field);
61
62    if (typeDefSpec) {
63      return this.getIntFlagsAsStrings(value, typeDefSpec);
64    } else {
65      const propertyPath = `${field.parent?.name}.${field.name}`;
66      if (this.intDefColumn[propertyPath]) {
67        return this.getIntFlagsAsStrings(
68          value,
69          this.intDefColumn[propertyPath] as string,
70        );
71      }
72    }
73
74    return value;
75  }
76
77  private getTypeDefSpecFromField(
78    field: TamperedProtoField,
79  ): string | undefined {
80    if (field.options === undefined) {
81      return undefined;
82    } else if (field.options['(.android.typedef)'] !== undefined) {
83      return field.options['(.android.typedef)'];
84    } else if (field.options['(.perfetto.protos.typedef)'] !== undefined) {
85      return field.options['(.perfetto.protos.typedef)'];
86    }
87    return undefined;
88  }
89
90  private getIntFlagsAsStrings(
91    intFlags: number,
92    annotationType: string,
93  ): string {
94    let flags = '';
95    const mapping =
96      intDefMapping[annotationType as keyof typeof intDefMapping]?.values ?? {};
97
98    const knownFlagValues = Object.keys(mapping)
99      .reverse()
100      .map((x) => Math.floor(Number(x)));
101
102    if (knownFlagValues.length === 0) {
103      console.warn('No mapping for type', annotationType);
104      return intFlags + '';
105    }
106
107    // Will only contain bits that have not been associated with a flag.
108    const parsedIntFlags = Math.floor(Number(intFlags));
109    let leftOver = parsedIntFlags;
110
111    for (const flagValue of knownFlagValues) {
112      if (
113        (leftOver & flagValue && (intFlags & flagValue) === flagValue) ||
114        (parsedIntFlags === 0 && flagValue === 0)
115      ) {
116        if (flags.length > 0) flags += ' | ';
117        flags += mapping[flagValue as keyof typeof mapping];
118
119        leftOver = leftOver & ~flagValue;
120      }
121    }
122
123    if (flags.length === 0) {
124      return `${intFlags}`;
125    }
126
127    if (leftOver) {
128      // If 0 is a valid flag value that isn't in the intDefMapping it will be ignored
129      flags += ' | ' + leftOver;
130    }
131
132    return flags;
133  }
134
135  private readonly intDefColumn: {[key: string]: string} = {
136    'WindowLayoutParams.type':
137      'android.view.WindowManager.LayoutParams.WindowType',
138    'WindowLayoutParams.flags': 'android.view.WindowManager.LayoutParams.Flags',
139    'WindowLayoutParams.privateFlags':
140      'android.view.WindowManager.LayoutParams.PrivateFlags',
141    'WindowLayoutParams.gravity': 'android.view.Gravity.GravityFlags',
142    'WindowLayoutParams.softInputMode':
143      'android.view.WindowManager.LayoutParams.WindowType',
144    'WindowLayoutParams.systemUiVisibilityFlags':
145      'android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags',
146    'WindowLayoutParams.subtreeSystemUiVisibilityFlags':
147      'android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags',
148    'WindowLayoutParams.behavior':
149      'android.view.WindowInsetsController.Behavior',
150    'WindowLayoutParams.fitInsetsSides':
151      'android.view.WindowInsets.Side.InsetsSide',
152    'InputWindowInfoProto.layoutParamsFlags':
153      'android.view.WindowManager.LayoutParams.Flags',
154    'InputWindowInfoProto.inputConfig':
155      'android.view.InputWindowHandle.InputConfigFlags',
156    'Configuration.windowingMode':
157      'android.app.WindowConfiguration.WindowingMode',
158    'WindowConfiguration.windowingMode':
159      'android.app.WindowConfiguration.WindowingMode',
160    'Configuration.orientation':
161      'android.content.pm.ActivityInfo.ScreenOrientation',
162    'WindowConfiguration.orientation':
163      'android.content.pm.ActivityInfo.ScreenOrientation',
164    'WindowState.orientation':
165      'android.content.pm.ActivityInfo.ScreenOrientation',
166    'InsetsSourceControlProto.typeNumber':
167      'android.view.WindowInsets.Type.InsetsType',
168    'InsetsSourceConsumerProto.typeNumber':
169      'android.view.WindowInsets.Type.InsetsType',
170    'WindowStateProto.requestedVisibleTypes':
171      'android.view.WindowInsets.Type.InsetsType',
172    'Target.flags': 'android.window.TransitionInfo.ChangeFlags',
173    'Transition.flags': 'android.view.WindowManager.TransitionFlags',
174  };
175}
176