Translate property int flags to their respective string identifier

Test: Manual
Change-Id: I1e596652a5b690e115b8a9aeb8fa4c25af1d1007
diff --git a/tools/winscope/README.md b/tools/winscope/README.md
index 8821601..d09563d 100644
--- a/tools/winscope/README.md
+++ b/tools/winscope/README.md
@@ -17,6 +17,29 @@
 * Navigate to `development/tools/winscope`
 * Run `yarn run dev`
 
+### Update IntDefMapping
+* Build `framework-all` module and a preprocessor will generate the latest
+IntDefMapping. From the `ANDROID_ROOT` run:
+```
+. build/envsetup.sh
+m framework-all
+```
+
+* Copy the generated `intDefMapping.json` file to the `prebuilts` repo.
+```
+cp
+./out/soong/.intermediates/frameworks/base/framework-all/android_common/javac/classes/com/android/winscope/intDefMapping.json
+./prebuilts/misc/common/winscope/intDefMapping.json
+```
+
+* Upload the changes.
+```
+cd ./prebuilts/misc/common/winscope
+repo start intdef-update
+git commit -am "Update intdef mapping" "Test: N/A"
+repo upload --cbr .
+```
+
 ### Building with internal extensions
 Internal paths in vendor/ which are not available in AOSP must be replaced by
 stub files. See getWaylandSafePath for an example
diff --git a/tools/winscope/src/TraceView.vue b/tools/winscope/src/TraceView.vue
index 43d19ff..7e8fdc9 100644
--- a/tools/winscope/src/TraceView.vue
+++ b/tools/winscope/src/TraceView.vue
@@ -182,13 +182,13 @@
       const transformer = new ObjectTransformer(
         item.obj,
         item.name,
-        stableIdCompatibilityFixup(item)
+        stableIdCompatibilityFixup(item),
       ).setOptions({
-          skip: item.skip,
-          formatter: formatProto,
-        });
+        skip: item.skip,
+        formatter: formatProto,
+      });
 
-      if (this.showPropertiesDiff) {
+      if (this.showPropertiesDiff && this.diffVisualizationAvailable) {
         const prevItem = this.getItemFromPrevTree(item);
         transformer.withDiff(prevItem?.obj);
       }
diff --git a/tools/winscope/src/transform.js b/tools/winscope/src/transform.js
index 5199fd2..941bc64 100644
--- a/tools/winscope/src/transform.js
+++ b/tools/winscope/src/transform.js
@@ -15,6 +15,7 @@
  */
 
 import { DiffType } from './utils/diff.js';
+import intDefMapping from '../../../../prebuilts/misc/common/winscope/intDefMapping.json';
 
 // kind - a type used for categorization of different levels
 // name - name of the node
@@ -127,9 +128,10 @@
 		return this;
 	}
 
-	withDiff(obj) {
+	withDiff(obj, fieldOptions) {
 		this.diff = true;
 		this.compareWithObj = obj ?? new Terminal();
+		this.compareWithFieldOptions = fieldOptions;
 		return this;
 	}
 
@@ -139,68 +141,90 @@
 			throw new Error("Missing formatter, please set with setOptions()");
 		}
 
-		return this._transform(this.obj, this.rootName, this.compareWithObj, this.rootName, this.stableId);
+		return this._transform(this.obj, this.rootName, null,
+			this.compareWithObj, this.rootName, null,
+			this.stableId);
 	}
 
-	_transformKeys(obj) {
+	_transformObject(obj, fieldOptions) {
 		const { skip, formatter } = this.options;
-		const transformedObj = {};
+		const transformedObj = {
+			obj: {},
+			fieldOptions: {},
+		};
 		let formatted = undefined;
 
 		if (skip && skip.includes(obj)) {
 			// skip
 		} else if ((formatted = formatter(obj))) {
 			// Obj has been formatted into a terminal node — has no children.
-			transformedObj[formatted] = new Terminal();
+			transformedObj.obj[formatted] = new Terminal();
+			transformedObj.fieldOptions[formatted] = fieldOptions;
 		} else if (Array.isArray(obj)) {
 			obj.forEach((e, i) => {
-				transformedObj["" + i] = e;
+				transformedObj.obj["" + i] = e;
+				transformedObj.fieldOptions["" + i] = fieldOptions;
 			});
 		} else if (typeof obj == 'string') {
 			// Object is a primitive type — has no children. Set to terminal
 			// to differentiate between null object and Terminal element.
-			transformedObj[obj] = new Terminal();
+			transformedObj.obj[obj] = new Terminal();
+			transformedObj.fieldOptions[obj] = fieldOptions;
 		} else if (typeof obj == 'number' || typeof obj == 'boolean') {
 			// Similar to above — primitive type node has no children.
-			transformedObj["" + obj] = new Terminal();
+			transformedObj.obj["" + obj] = new Terminal();
+			transformedObj.fieldOptions["" + obj] = fieldOptions;
 		} else if (obj && typeof obj == 'object') {
 			Object.keys(obj).forEach((key) => {
-				transformedObj[key] = obj[key];
+				transformedObj.obj[key] = obj[key];
+				transformedObj.fieldOptions[key] = obj.$type?.fields[key]?.options;
 			});
 		} else if (obj === null) {
 			// Null object is a has no children — set to be terminal node.
-			transformedObj.null = new Terminal();
+			transformedObj.obj.null = new Terminal();
+			transformedObj.fieldOptions.null = undefined;
 		}
 
 		return transformedObj;
 	}
 
-	_transform(obj, name, compareWithObj, compareWithName, stableId) {
+	_transform(obj, name, fieldOptions,
+		compareWithObj, compareWithName, compareWithFieldOptions,
+		stableId) {
 		const children = [];
 
 		if (!isTerminal(obj)) {
-			obj = this._transformKeys(obj);
+			const transformedObj = this._transformObject(obj, fieldOptions);
+			obj = transformedObj.obj;
+			fieldOptions = transformedObj.fieldOptions;
 		}
 		if (!isTerminal(compareWithObj)) {
-			compareWithObj = this._transformKeys(compareWithObj);
+			const transformedObj = this._transformObject(compareWithObj, compareWithFieldOptions);
+			compareWithObj = transformedObj.obj;
+			compareWithFieldOptions = transformedObj.fieldOptions;
 		}
 
 		for (const key in obj) {
 			if (obj.hasOwnProperty(key)) {
 				let compareWithChild = new Terminal();
-				let compareWithName = new Terminal();
+				let compareWithChildName = new Terminal();
+				let compareWithChildFieldOptions = undefined;
 				if (compareWithObj.hasOwnProperty(key)) {
 					compareWithChild = compareWithObj[key];
-					compareWithName = key;
+					compareWithChildName = key;
+					compareWithChildFieldOptions = compareWithFieldOptions[key];
 				}
-				children.push(this._transform(obj[key], key, compareWithChild, compareWithName, `${stableId}.${key}`));
+				children.push(this._transform(obj[key], key, fieldOptions[key],
+					compareWithChild, compareWithChildName, compareWithChildFieldOptions,
+					`${stableId}.${key}`));
 			}
 		}
 
 		// Takes care of adding deleted items to final tree
 		for (const key in compareWithObj) {
 			if (!obj.hasOwnProperty(key) && compareWithObj.hasOwnProperty(key)) {
-				children.push(this._transform(new Terminal(), new Terminal(), compareWithObj[key], key));
+				children.push(this._transform(new Terminal(), new Terminal(), undefined,
+					compareWithObj[key], key, compareWithFieldOptions[key], `${stableId}.${key}`));
 			}
 		}
 
@@ -232,12 +256,26 @@
 				children,
 			};
 
+			let fieldOptionsToUse = fieldOptions;
+
 			if (this.diff) {
 				const diff = getDiff(name, compareWithName);
 				transformedObj.diff = diff;
 
 				if (diff.type == DiffType.DELETED) {
 					transformedObj.name = compareWithName;
+					fieldOptionsToUse = compareWithFieldOptions;
+				}
+			}
+
+			const annotationType = fieldOptionsToUse?.["(.android.typedef)"];
+			if (annotationType) {
+				if (intDefMapping[annotationType] === undefined) {
+					console.error(`Missing intDef mapping for translation for ${annotationType}`);
+				} else if (intDefMapping[annotationType].flag) {
+					transformedObj.name = `${getIntFlagsAsStrings(transformedObj.name, annotationType)} (${transformedObj.name})`;
+				} else {
+					transformedObj.name = `${intDefMapping[annotationType].values[transformedObj.name]} (${transformedObj.name})`;
 				}
 			}
 		}
@@ -246,6 +284,35 @@
 	}
 }
 
+function getIntFlagsAsStrings(intFlags, annotationType) {
+	const flags = [];
+
+	const mapping = intDefMapping[annotationType].values;
+
+	// Will only contain bits that have not been associated with a flag.
+	let leftOver = intFlags;
+
+	for (const intValue in mapping) {
+		if ((intFlags & parseInt(intValue)) === parseInt(intValue)) {
+			flags.push(mapping[intValue]);
+
+			leftOver = leftOver & ~intValue;
+		}
+	}
+
+	if (flags.length === 0) {
+		console.error("No valid flag mappings found for ", intFlags, "of type", annotationType);
+	}
+
+	if (leftOver) {
+		// If 0 is a valid flag value that isn't in the intDefMapping
+		// it will be ignored
+		flags.push(leftOver);
+	}
+
+	return flags.join(' | ');
+}
+
 function nanos_to_string(elapsedRealtimeNanos) {
 	var units = [
 		[1000000, '(ns)'],