• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2
3#
4# Script for generation of VHAL properties metadata .json from AIDL interface
5#
6# This metadata is used to display human property names, names of enum
7# data types for their values, change and access modes and other information,
8# available from AIDL block comments, but not at runtime.
9#
10# Usage example:
11#  ./emu_metadata/generate_emulator_metadata.py android/hardware/automotive/vehicle $OUT/android.hardware.automotive.vehicle-types-meta.json
12#  (Note, that the resulting file has to match a '*types-meta.json' pattern to be parsed by the emulator).
13#
14
15import json
16import os
17import re
18import sys
19
20from pathlib import Path
21
22RE_ENUM = re.compile(r"\s*enum\s+(\w*) {\n(.*)}", re.MULTILINE | re.DOTALL)
23RE_COMMENT = re.compile(r"(?:(?:\/\*\*)((?:.|\n)*?)(?:\*\/))?(?:\n|^)\s*(\w*)(?:\s+=\s*)?((?:[a-zA-Z0-9]|\s|\+|)*),", re.DOTALL)
24RE_BLOCK_COMMENT_TITLE = re.compile("^(?:\s|\*)*((?:\w|\s|\.)*)\n(?:\s|\*)*(?:\n|$)")
25RE_BLOCK_COMMENT_ANNOTATION = re.compile("^(?:\s|\*)*@(\w*)\s+((?:\w|:)*)", re.MULTILINE)
26RE_HEX_NUMBER = re.compile("([0-9A-Fa-fxX]+)")
27
28
29class JEnum:
30    def __init__(self, name):
31        self.name = name
32        self.values = []
33
34
35class Converter:
36    # Only addition is supported for now, but that covers all existing properties except
37    # OBD diagnostics, which use bitwise shifts
38    def calculateValue(self, expression, default_value):
39        numbers = RE_HEX_NUMBER.findall(expression)
40        if len(numbers) == 0:
41            return default_value
42        result = 0
43        base = 10
44        if numbers[0].lower().startswith("0x"):
45            base = 16
46        for number in numbers:
47            result += int(number, base)
48        return result
49
50    def parseBlockComment(self, value, blockComment):
51        titles = RE_BLOCK_COMMENT_TITLE.findall(blockComment)
52        for title in titles:
53            value['name'] = title
54            break
55        annots_res = RE_BLOCK_COMMENT_ANNOTATION.findall(blockComment)
56        for annot in annots_res:
57            value[annot[0]] = annot[1]
58
59    def parseEnumContents(self, enum: JEnum, enumValue):
60        matches = RE_COMMENT.findall(enumValue)
61        defaultValue = 0
62        for match in matches:
63            value = dict()
64            value['name'] = match[1]
65            value['value'] = self.calculateValue(match[2], defaultValue)
66            defaultValue = value['value'] + 1
67            if enum.name == "VehicleProperty":
68                block_comment = match[0]
69                self.parseBlockComment(value, block_comment)
70            enum.values.append(value)
71
72    def convert(self, input):
73        text = Path(input).read_text()
74        matches = RE_ENUM.findall(text)
75        jenums = []
76        for match in matches:
77            enum = JEnum(match[0])
78            self.parseEnumContents(enum, match[1])
79            jenums.append(enum)
80        return jenums
81
82def main():
83    if (len(sys.argv) != 3):
84        print("Usage: ", sys.argv[0], " INPUT_PATH OUTPUT")
85        sys.exit(1)
86    aidl_path = sys.argv[1]
87    out_path = sys.argv[2]
88    result = []
89    for file in os.listdir(aidl_path):
90        result.extend(Converter().convert(os.path.join(aidl_path, file)))
91    json_result = json.dumps(result, default=vars, indent=2)
92    with open(out_path, 'w') as f:
93        f.write(json_result)
94
95
96if __name__ == "__main__":
97    main()
98