Configurable Audio Policy: migrate settings to AudioAIDL
Add the missing support of Cap Settings through Audio AIDL.
Bug: 307310023
Test: manual
Change-Id: I06920b3318bffd210829f45f28175df12dc0267f
Signed-off-by: François Gaffie <francois.gaffie@renault.com>
diff --git a/services/audiopolicy/engineconfigurable/tools/Android.bp b/services/audiopolicy/engineconfigurable/tools/Android.bp
index d1fb2fb..7ae124c 100644
--- a/services/audiopolicy/engineconfigurable/tools/Android.bp
+++ b/services/audiopolicy/engineconfigurable/tools/Android.bp
@@ -23,7 +23,7 @@
}
//##################################################################################################
-// Tools for audio policy engine criterion type configuration file
+// Legacy tools for audio policy engine criterion type configuration file
//
python_binary_host {
name: "buildPolicyCriterionTypes",
@@ -57,6 +57,40 @@
}
//##################################################################################################
+// Tools for audio policy engine criterion type configuration file
+//
+python_binary_host {
+ name: "capBuildPolicyCriterionTypes",
+ main: "capBuildPolicyCriterionTypes.py",
+ srcs: [
+ "capBuildPolicyCriterionTypes.py",
+ ],
+}
+
+genrule_defaults {
+ name: "capbuildpolicycriteriontypesrule",
+ tools: ["capBuildPolicyCriterionTypes"],
+ cmd: "cp $(locations :audio_policy_configuration_files) $(genDir)/. && " +
+ "cp $(location :audio_policy_configuration_top_file) $(genDir)/audio_policy_configuration.xml && " +
+ "$(location capBuildPolicyCriterionTypes) " +
+ " --androidaudiobaseheader $(location :libaudio_system_audio_base) " +
+ " --androidaudiocommonbaseheader $(location :libaudio_system_audio_common_base) " +
+ "--audiopolicyconfigurationfile $(genDir)/audio_policy_configuration.xml " +
+ "--criteriontypes $(location :audio_policy_engine_aidl_criterion_types_template) " +
+ "--outputfile $(out)",
+ srcs: [
+ // The commented inputs must be provided to use this genrule_defaults
+ // @todo uncomment if 1428659 is merged":android_audio_base_header_file",
+ ":audio_policy_engine_aidl_criterion_types_template",
+ ":libaudio_system_audio_base",
+ ":libaudio_system_audio_common_base",
+ // ":audio_policy_configuration_top_file",
+ // ":audio_policy_configuration_files",
+ ],
+ out: ["audio_policy_engine_criterion_types.xml"],
+}
+
+//##################################################################################################
// Tools for audio policy engine parameter framework configurable domains
//
python_binary_host {
@@ -84,6 +118,7 @@
cmd: "mkdir -p $(genDir)/Structure/Policy && " +
"cp $(locations :audio_policy_pfw_structure_files) $(genDir)/Structure/Policy && " +
"cp $(location :audio_policy_pfw_toplevel) $(genDir)/top_level && " +
+ "sed -i -e 's|TuningAllowed=\"false\"|TuningAllowed=\"true\" ServerPort=\"unix:///dev/socket/audioserver/policy_debug\"|g' $(genDir)/top_level &&" +
"$(location domainGeneratorPolicy) " +
"--validate " +
"--domain-generator-tool $(location domainGeneratorConnector) " +
@@ -106,7 +141,7 @@
}
//##################################################################################################
-// Tools for policy parameter-framework product strategies structure file generation
+// Legacy tools for policy parameter-framework product strategies structure file generation
//
python_binary_host {
name: "buildStrategiesStructureFile",
@@ -154,5 +189,4 @@
":common_types_structure_template",
":libaudio_system_audio_base",
],
- out: ["PolicySubsystem-CommonTypes.xml"],
}
diff --git a/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py
index f69d346..6eabc57 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py
@@ -75,7 +75,7 @@
strategy_components = strategies_root.find('ComponentType')
for strategy_name in strategies:
- context_mapping = "".join(map(str, ["Name:", strategy_name]))
+ context_mapping = "".join(map(str, ["Identifier:-1,Name:", strategy_name]))
strategy_pfw_name = strategy_name.replace('STRATEGY_', '').lower()
ET.SubElement(strategy_components, "Component",
Name=strategy_pfw_name, Type="ProductStrategy",
diff --git a/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py
new file mode 100755
index 0000000..b873830
--- /dev/null
+++ b/services/audiopolicy/engineconfigurable/tools/capBuildPolicyCriterionTypes.py
@@ -0,0 +1,368 @@
+#!/usr/bin/python3
+
+#
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import argparse
+import re
+import sys
+import os
+import logging
+import xml.etree.ElementTree as ET
+import xml.etree.ElementInclude as EI
+import xml.dom.minidom as MINIDOM
+from collections import OrderedDict
+
+#
+# Helper script that helps to feed at build time the XML criterion types file used by
+# the engineconfigurable to start the parameter-framework.
+# It prevents to fill them manually and avoid divergences with android.
+#
+# The Device Types criterion types are fed from audio-base.h file with the option
+# --androidaudiobaseheader <path/to/android/audio/base/file/audio-base.h>
+#
+# The Device Addresses criterion types are fed from the audio policy configuration file
+# in order to discover all the devices for which the address matter.
+# --audiopolicyconfigurationfile <path/to/audio_policy_configuration.xml>
+#
+# The reference file of criterion types must also be set as an input of the script:
+# --criteriontypes <path/to/criterion/file/audio_criterion_types.xml.in>
+#
+# At last, the output of the script shall be set also:
+# --outputfile <path/to/out/vendor/etc/audio_criterion_types.xml>
+#
+
+def parseArgs():
+ argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
+ audio criterion type file generator.\n\
+ Exit with the number of (recoverable or not) \
+ error that occurred.")
+ argparser.add_argument('--androidaudiobaseheader',
+ help="Android Audio Base C header file, Mandatory.",
+ metavar="ANDROID_AUDIO_BASE_HEADER",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--androidaudiocommonbaseheader',
+ help="Android Audio CommonBase C header file, Mandatory.",
+ metavar="ANDROID_AUDIO_COMMON_BASE_HEADER",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--audiopolicyconfigurationfile',
+ help="Android Audio Policy Configuration file, Mandatory.",
+ metavar="(AUDIO_POLICY_CONFIGURATION_FILE)",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--criteriontypes',
+ help="Criterion types XML base file, in \
+ '<criterion_types> \
+ <criterion_type name="" type=<inclusive|exclusive> \
+ values=<value1,value2,...>/>' \
+ format. Mandatory.",
+ metavar="CRITERION_TYPE_FILE",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--outputfile',
+ help="Criterion types outputfile file. Mandatory.",
+ metavar="CRITERION_TYPE_OUTPUT_FILE",
+ type=argparse.FileType('w'),
+ required=True)
+ argparser.add_argument('--verbose',
+ action='store_true')
+
+ return argparser.parse_args()
+
+
+output_devices_type_value = {}
+input_devices_type_value = {}
+
+def generateXmlCriterionTypesFile(criterionTypes, addressCriteria, criterionTypesFile, outputFile):
+
+ logging.info("Importing criterionTypesFile {}".format(criterionTypesFile))
+ criterion_types_in_tree = ET.parse(criterionTypesFile)
+
+ criterion_types_root = criterion_types_in_tree.getroot()
+
+ for criterion_name, values_dict in criterionTypes.items():
+ for criterion_type in criterion_types_root.findall('criterion_type'):
+ if criterion_type.get('name') == criterion_name:
+ values_node = ET.SubElement(criterion_type, "values")
+ ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1]))
+ for key, value in ordered_values.items():
+ value_node = ET.SubElement(values_node, "value")
+ value_node.set('numerical', str(value))
+ value_node.set('literal', key)
+
+ if criterion_type.get('name') == "OutputDevicesMaskType":
+ value_node.set('android_type', output_devices_type_value[key])
+ if criterion_type.get('name') == "InputDevicesMaskType":
+ value_node.set('android_type', input_devices_type_value[key])
+
+ if addressCriteria:
+ for criterion_name, values_list in addressCriteria.items():
+ for criterion_type in criterion_types_root.findall('criterion_type'):
+ if criterion_type.get('name') == criterion_name:
+ index = 0
+ existing_values_node = criterion_type.find("values")
+ if existing_values_node is not None:
+ for existing_value in existing_values_node.findall('value'):
+ if existing_value.get('numerical') == str(1 << index):
+ index += 1
+ values_node = existing_values_node
+ else:
+ values_node = ET.SubElement(criterion_type, "values")
+
+ for value in values_list:
+ value_node = ET.SubElement(values_node, "value", literal=value)
+ value_node.set('numerical', str(1 << index))
+ index += 1
+
+ xmlstr = ET.tostring(criterion_types_root, encoding='utf8', method='xml')
+ reparsed = MINIDOM.parseString(xmlstr)
+ prettyXmlStr = reparsed.toprettyxml(newl='\r\n')
+ prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()])
+ outputFile.write(prettyXmlStr)
+
+def capitalizeLine(line):
+ return ' '.join((w.capitalize() for w in line.split(' ')))
+
+
+#
+# Parse the audio policy configuration file and output a dictionary of device criteria addresses
+#
+def parseAndroidAudioPolicyConfigurationFile(audiopolicyconfigurationfile):
+
+ logging.info("Checking Audio Policy Configuration file {}".format(audiopolicyconfigurationfile))
+ #
+ # extract all devices addresses from audio policy configuration file
+ #
+ address_criteria_mapping_table = {
+ 'sink' : "OutputDevicesAddressesType",
+ 'source' : "InputDevicesAddressesType"}
+
+ address_criteria = {
+ 'OutputDevicesAddressesType' : [],
+ 'InputDevicesAddressesType' : []}
+
+ old_working_dir = os.getcwd()
+ print("Current working directory %s" % old_working_dir)
+
+ new_dir = os.path.join(old_working_dir, audiopolicyconfigurationfile.name)
+
+ policy_in_tree = ET.parse(audiopolicyconfigurationfile)
+ os.chdir(os.path.dirname(os.path.normpath(new_dir)))
+
+ print("new working directory %s" % os.getcwd())
+
+ policy_root = policy_in_tree.getroot()
+ EI.include(policy_root)
+
+ os.chdir(old_working_dir)
+
+ for device in policy_root.iter('devicePort'):
+ for key in address_criteria_mapping_table.keys():
+ if device.get('role') == key and device.get('address'):
+ logging.info("{}: <{}>".format(key, device.get('address')))
+ address_criteria[address_criteria_mapping_table[key]].append(device.get('address'))
+
+ for criteria in address_criteria:
+ values = ','.join(address_criteria[criteria])
+ logging.info("{}: <{}>".format(criteria, values))
+
+ return address_criteria
+
+#
+# Parse the audio-base.h file and output a dictionary of android dependent criterion types:
+# -Android Mode
+# -Output devices type
+# -Input devices type
+#
+def parseAndroidAudioFile(androidaudiobaseheaderFile, androidaudiocommonbaseheaderFile):
+ #
+ # Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names
+ #
+ criterion_mapping_table = {
+ 'HAL_AUDIO_MODE' : "AndroidModeType",
+ 'AUDIO_DEVICE_OUT' : "OutputDevicesMaskType",
+ 'AUDIO_DEVICE_IN' : "InputDevicesMaskType"}
+
+ all_criteria = {
+ 'AndroidModeType' : {},
+ 'OutputDevicesMaskType' : {},
+ 'InputDevicesMaskType' : {}}
+
+ #
+ # _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users.
+ #
+ ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
+
+ multi_bit_outputdevice_shift = 32
+ multi_bit_inputdevice_shift = 32
+
+ criteria_pattern = re.compile(
+ r"\s*V\((?P<type>(?:"+'|'.join(criterion_mapping_table.keys()) + "))_" \
+ r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*,\s*" \
+ r"(?:AUDIO_DEVICE_BIT_IN \| )?(?P<values>(?:0[xX])?[0-9a-fA-F]+|[0-9]+)")
+
+ logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
+
+ for line_number, line in enumerate(androidaudiobaseheaderFile):
+ match = criteria_pattern.match(line)
+ if match:
+ logging.debug("The following line is VALID: {}:{}\n{}".format(
+ androidaudiobaseheaderFile.name, line_number, line))
+
+ criterion_name = criterion_mapping_table[match.groupdict()['type']]
+ criterion_literal = ''.join(match.groupdict()['literal'])
+ criterion_numerical_value = match.groupdict()['values']
+
+ if criterion_name == "InputDevicesMaskType":
+ # Remove ambient and in_communication since they were deprecated
+ logging.info("Remove deprecated device {}".format(criterion_literal))
+ if criterion_literal == "AMBIENT" or criterion_literal == "COMMUNICATION":
+ logging.info("Remove deprecated device {}".format(criterion_literal))
+ continue
+ # for AUDIO_DEVICE_IN: rename default to stub
+ elif criterion_literal == "DEFAULT":
+ criterion_numerical_value = str(int("0x40000000", 0))
+ input_devices_type_value[criterion_literal] = "0xC0000000"
+ else:
+ try:
+ string_int = int(criterion_numerical_value, 0)
+ # Append AUDIO_DEVICE_IN for android type tag
+ input_devices_type_value[criterion_literal] = hex(string_int | 2147483648)
+
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+ .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_inputdevice_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_inputdevice_shift += 1
+ criterion_numerical_value = str(string_int)
+
+ except ValueError:
+ # Handle the exception
+ logging.info("value {}:{} for criterion {} is not a number, ignoring"
+ .format(criterion_numerical_value, criterion_literal, criterion_name))
+ continue
+
+ if criterion_name == "OutputDevicesMaskType":
+ if criterion_literal == "DEFAULT":
+ criterion_numerical_value = str(int("0x40000000", 0))
+ output_devices_type_value[criterion_literal] = "0x40000000"
+ else:
+ try:
+ string_int = int(criterion_numerical_value, 0)
+ output_devices_type_value[criterion_literal] = criterion_numerical_value
+
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+ .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_outputdevice_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_outputdevice_shift += 1
+ criterion_numerical_value = str(string_int)
+
+ except ValueError:
+ # Handle the exception
+ logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
+ .format(criterion_numerical_value, criterion_literal, criterion_name))
+ continue
+
+ try:
+ string_int = int(criterion_numerical_value, 0)
+
+ except ValueError:
+ # Handle the exception
+ logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
+ .format(criterion_numerical_value, criterion_literal, criterion_name))
+ continue
+
+ # Remove duplicated numerical values
+ if int(criterion_numerical_value, 0) in all_criteria[criterion_name].values():
+ logging.info("criterion {} duplicated values:".format(criterion_name))
+ logging.info("{}:{}".format(criterion_numerical_value, criterion_literal))
+ logging.info("KEEPING LATEST")
+ for key in list(all_criteria[criterion_name]):
+ if all_criteria[criterion_name][key] == int(criterion_numerical_value, 0):
+ del all_criteria[criterion_name][key]
+
+ all_criteria[criterion_name][criterion_literal] = int(criterion_numerical_value, 0)
+
+ logging.debug("type:{},".format(criterion_name))
+ logging.debug("iteral:{},".format(criterion_literal))
+ logging.debug("values:{}.".format(criterion_numerical_value))
+
+ logging.info("Checking Android Common Header file {}".format(androidaudiocommonbaseheaderFile))
+
+ criteria_pattern = re.compile(
+ r"\s*(?P<type>(?:"+'|'.join(criterion_mapping_table.keys()) + "))_" \
+ r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*=\s*" \
+ r"(?:AUDIO_DEVICE_BIT_IN \| )?(?P<values>(?:0[xX])?[0-9a-fA-F]+|[0-9]+)")
+
+ for line_number, line in enumerate(androidaudiocommonbaseheaderFile):
+ match = criteria_pattern.match(line)
+ if match:
+ logging.debug("The following line is VALID: {}:{}\n{}".format(
+ androidaudiocommonbaseheaderFile.name, line_number, line))
+
+ criterion_name = criterion_mapping_table[match.groupdict()['type']]
+ criterion_literal = ''.join(match.groupdict()['literal'])
+ criterion_numerical_value = match.groupdict()['values']
+
+ try:
+ string_int = int(criterion_numerical_value, 0)
+ except ValueError:
+ # Handle the exception
+ logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
+ .format(criterion_numerical_value, criterion_literal, criterion_name))
+ continue
+
+ # Remove duplicated numerical values
+ if int(criterion_numerical_value, 0) in all_criteria[criterion_name].values():
+ logging.info("criterion {} duplicated values:".format(criterion_name))
+ logging.info("{}:{}".format(criterion_numerical_value, criterion_literal))
+ logging.info("KEEPING LATEST")
+ for key in list(all_criteria[criterion_name]):
+ if all_criteria[criterion_name][key] == int(criterion_numerical_value, 0):
+ del all_criteria[criterion_name][key]
+
+ all_criteria[criterion_name][criterion_literal] = int(criterion_numerical_value, 0)
+
+ logging.debug("type:{},".format(criterion_name))
+ logging.debug("iteral:{},".format(criterion_literal))
+ logging.debug("values:{}.".format(criterion_numerical_value))
+
+ return all_criteria
+
+
+def main():
+ logging.root.setLevel(logging.INFO)
+ args = parseArgs()
+
+ all_criteria = parseAndroidAudioFile(args.androidaudiobaseheader,
+ args.androidaudiocommonbaseheader)
+
+ address_criteria = parseAndroidAudioPolicyConfigurationFile(args.audiopolicyconfigurationfile)
+
+ criterion_types = args.criteriontypes
+
+ generateXmlCriterionTypesFile(all_criteria, address_criteria, criterion_types, args.outputfile)
+
+# If this file is directly executed
+if __name__ == "__main__":
+ sys.exit(main())