• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/python3
2#
3# pylint: disable=line-too-long, missing-docstring, logging-format-interpolation, invalid-name
4
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18import argparse
19import re
20import sys
21import os
22import logging
23import xml.etree.ElementTree as ET
24from collections import OrderedDict
25import xml.dom.minidom as MINIDOM
26
27def parseArgs():
28    argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
29        structure file generator.\n\
30        Exit with the number of (recoverable or not) error that occured.")
31    argparser.add_argument('--androidaudiobaseheader',
32                           help="Android Audio Base C header file, Mandatory.",
33                           metavar="ANDROID_AUDIO_BASE_HEADER",
34                           type=argparse.FileType('r'),
35                           required=True)
36    argparser.add_argument('--commontypesstructure',
37                           help="Structure XML base file. Mandatory.",
38                           metavar="STRUCTURE_FILE_IN",
39                           type=argparse.FileType('r'),
40                           required=True)
41    argparser.add_argument('--outputfile',
42                           help="Structure XML file. Mandatory.",
43                           metavar="STRUCTURE_FILE_OUT",
44                           type=argparse.FileType('w'),
45                           required=True)
46    argparser.add_argument('--verbose',
47                           action='store_true')
48
49    return argparser.parse_args()
50
51
52def findBitPos(decimal):
53    pos = 0
54    i = 1
55    while i < decimal:
56        i = i << 1
57        pos = pos + 1
58        if pos == 64:
59            return -1
60
61    # TODO: b/168065706. This is just to fix the build. That the problem of devices with
62    # multiple bits set must be addressed more generally in the configurable audio policy
63    # and parameter framework.
64    if i > decimal:
65        logging.info("Device:{} which has multiple bits set is skipped. b/168065706".format(decimal))
66        return -2
67    return pos
68
69def generateXmlStructureFile(componentTypeDict, structureTypesFile, outputFile):
70
71    logging.info("Importing structureTypesFile {}".format(structureTypesFile))
72    component_types_in_tree = ET.parse(structureTypesFile)
73
74    component_types_root = component_types_in_tree.getroot()
75
76    for component_types_name, values_dict in componentTypeDict.items():
77        for component_type in component_types_root.findall('ComponentType'):
78            if component_type.get('Name') == component_types_name:
79                bitparameters_node = component_type.find("BitParameterBlock")
80                if bitparameters_node is not None:
81                    ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
82                    for key, value in ordered_values.items():
83                        pos = findBitPos(value)
84                        if pos >= 0:
85                            value_node = ET.SubElement(bitparameters_node, "BitParameter")
86                            value_node.set('Name', key)
87                            value_node.set('Size', "1")
88                            value_node.set('Pos', str(pos))
89
90                enum_parameter_node = component_type.find("EnumParameter")
91                if enum_parameter_node is not None:
92                    ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
93                    for key, value in ordered_values.items():
94                        value_node = ET.SubElement(enum_parameter_node, "ValuePair")
95                        value_node.set('Literal', key)
96                        value_node.set('Numerical', str(value))
97
98    xmlstr = ET.tostring(component_types_root, encoding='utf8', method='xml')
99    reparsed = MINIDOM.parseString(xmlstr)
100    prettyXmlStr = reparsed.toprettyxml(indent="    ", newl='\n')
101    prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()])
102    outputFile.write(prettyXmlStr)
103
104
105def capitalizeLine(line):
106    return ' '.join((w.capitalize() for w in line.split(' ')))
107
108def parseAndroidAudioFile(androidaudiobaseheaderFile):
109    #
110    # Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names
111    #
112    component_type_mapping_table = {
113        'AUDIO_STREAM' : "VolumeProfileType",
114        'AUDIO_DEVICE_OUT' : "OutputDevicesMask",
115        'AUDIO_DEVICE_IN' : "InputDevicesMask"}
116
117    all_component_types = {
118        'VolumeProfileType' : {},
119        'OutputDevicesMask' : {},
120        'InputDevicesMask' : {}
121    }
122
123    #
124    # _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users.
125    #
126    ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
127
128    criteria_pattern = re.compile(
129        r"\s*V\((?P<type>(?:"+'|'.join(component_type_mapping_table.keys()) + "))_" \
130        r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*,\s*" \
131        r"(?:AUDIO_DEVICE_BIT_IN \| )?(?P<values>(?:0[xX])[0-9a-fA-F]+|[0-9]+)")
132
133    logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
134
135    multi_bit_output_device_shift = 32
136    multi_bit_input_device_shift = 32
137
138    for line_number, line in enumerate(androidaudiobaseheaderFile):
139        match = criteria_pattern.match(line)
140        if match:
141            logging.debug("The following line is VALID: {}:{}\n{}".format(
142                androidaudiobaseheaderFile.name, line_number, line))
143
144            component_type_name = component_type_mapping_table[match.groupdict()['type']]
145            component_type_literal = match.groupdict()['literal'].lower()
146
147            component_type_numerical_value = match.groupdict()['values']
148
149            # for AUDIO_DEVICE_IN: rename default to stub
150            if component_type_name == "InputDevicesMask":
151                component_type_numerical_value = str(int(component_type_numerical_value, 0))
152                if component_type_literal == "default":
153                    component_type_literal = "stub"
154
155                string_int = int(component_type_numerical_value, 0)
156                num_bits = bin(string_int).count("1")
157                if num_bits > 1:
158                    logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
159                        .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
160                    string_int = 2**multi_bit_input_device_shift
161                    logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
162                    multi_bit_input_device_shift += 1
163                    component_type_numerical_value = str(string_int)
164
165            if component_type_name == "OutputDevicesMask":
166                if component_type_literal == "default":
167                    component_type_literal = "stub"
168
169                string_int = int(component_type_numerical_value, 0)
170                num_bits = bin(string_int).count("1")
171                if num_bits > 1:
172                    logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
173                        .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
174                    string_int = 2**multi_bit_output_device_shift
175                    logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
176                    multi_bit_output_device_shift += 1
177                    component_type_numerical_value = str(string_int)
178
179            # Remove duplicated numerical values
180            if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values():
181                logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name))
182                for key in list(all_component_types[component_type_name]):
183                    if all_component_types[component_type_name][key] == int(component_type_numerical_value, 0):
184                        del all_component_types[component_type_name][key]
185
186            all_component_types[component_type_name][component_type_literal] = int(component_type_numerical_value, 0)
187
188            logging.debug("type:{}, literal:{}, values:{}.".format(component_type_name, component_type_literal, component_type_numerical_value))
189
190    if "stub" not in all_component_types["OutputDevicesMask"]:
191        all_component_types["OutputDevicesMask"]["stub"] = 0x40000000
192        logging.info("added stub output device mask")
193    if "stub" not in all_component_types["InputDevicesMask"]:
194        all_component_types["InputDevicesMask"]["stub"] = 0x40000000
195        logging.info("added stub input device mask")
196
197    # Transform input source in inclusive criterion
198    for component_types in all_component_types:
199        values = ','.join('{}:{}'.format(value, key) for key, value in all_component_types[component_types].items())
200        logging.info("{}: <{}>".format(component_types, values))
201
202    return all_component_types
203
204
205def main():
206    logging.root.setLevel(logging.INFO)
207    args = parseArgs()
208    route_criteria = 0
209
210    all_component_types = parseAndroidAudioFile(args.androidaudiobaseheader)
211
212    generateXmlStructureFile(all_component_types, args.commontypesstructure, args.outputfile)
213
214# If this file is directly executed
215if __name__ == "__main__":
216    sys.exit(main())
217