Loading tools/fs_config/Android.mk +5 −2 Original line number Diff line number Diff line Loading @@ -81,15 +81,18 @@ LOCAL_SHARED_LIBRARIES := libcutils LOCAL_CFLAGS := -Werror -Wno-error=\#warnings ifneq ($(TARGET_FS_CONFIG_GEN),) system_android_filesystem_config := system/core/include/private/android_filesystem_config.h gen := $(local-generated-sources-dir)/$(ANDROID_FS_CONFIG_H) $(gen): PRIVATE_LOCAL_PATH := $(LOCAL_PATH) $(gen): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) $(gen): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/fs_config_generator.py fsconfig $(PRIVATE_TARGET_FS_CONFIG_GEN) > $@ $(gen): $(TARGET_FS_CONFIG_GEN) $(LOCAL_PATH)/fs_config_generator.py $(gen): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config) $(gen): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/fs_config_generator.py fsconfig --aid-header=$(PRIVATE_ANDROID_FS_HDR) $(PRIVATE_TARGET_FS_CONFIG_GEN) > $@ $(gen): $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(LOCAL_PATH)/fs_config_generator.py $(transform-generated-source) LOCAL_GENERATED_SOURCES := $(gen) my_fs_config_h := $(gen) system_android_filesystem_config := gen := endif Loading tools/fs_config/fs_config_generator.py +326 −13 Original line number Diff line number Diff line Loading @@ -66,6 +66,27 @@ class generator(object): # pylint: disable=invalid-name return generator._generators class Utils(object): """Various assorted static utilities.""" @staticmethod def in_any_range(value, ranges): """Tests if a value is in a list of given closed range tuples. A range tuple is a closed range. That means it's inclusive of its start and ending values. Args: value (int): The value to test. range [(int, int)]: The closed range list to test value within. Returns: True if value is within the closed range, false otherwise. """ return any(lower <= value <= upper for (lower, upper) in ranges) class AID(object): """This class represents an Android ID or an AID. Loading Loading @@ -124,6 +145,294 @@ class FSConfig(object): self.filename = filename class AIDHeaderParser(object): """Parses an android_filesystem_config.h file. Parses a C header file and extracts lines starting with #define AID_<name> It provides some basic sanity checks. The information extracted from this file can later be used to sanity check other things (like oem ranges) as well as generating a mapping of names to uids. It was primarily designed to parse the private/android_filesystem_config.h, but any C header should work. """ _SKIPWORDS = ['UNUSED'] _AID_KW = 'AID_' _AID_DEFINE = re.compile(r'\s*#define\s+%s.*' % _AID_KW) _OEM_START_KW = 'START' _OEM_END_KW = 'END' _OEM_RANGE = re.compile('AID_OEM_RESERVED_[0-9]*_{0,1}(%s|%s)' % (_OEM_START_KW, _OEM_END_KW)) # Some of the AIDS like AID_MEDIA_EX had names like mediaex # list a map of things to fixup until we can correct these # at a later date. _FIXUPS = { 'media_drm': 'mediadrm', 'media_ex': 'mediaex', 'media_codec': 'mediacodec' } def __init__(self, aid_header): """ Args: aid_header (str): file name for the header file containing AID entries. """ self._aid_header = aid_header self._aid_name_to_value = {} self._aid_value_to_name = {} self._oem_ranges = {} with open(aid_header) as open_file: self._parse(open_file) try: self._process_and_check() except ValueError as exception: sys.exit('Error processing parsed data: "%s"' % (str(exception))) def _parse(self, aid_file): """Parses an AID header file. Internal use only. Args: aid_file (file): The open AID header file to parse. """ for lineno, line in enumerate(aid_file): def error_message(msg): """Creates an error message with the current parsing state.""" return 'Error "{}" in file: "{}" on line: {}'.format( msg, self._aid_header, str(lineno)) if AIDHeaderParser._AID_DEFINE.match(line): chunks = line.split() if any(x in chunks[1] for x in AIDHeaderParser._SKIPWORDS): continue identifier = chunks[1] value = chunks[2] try: if AIDHeaderParser._is_oem_range(identifier): self._handle_oem_range(identifier, value) else: self._handle_aid(identifier, value) except ValueError as exception: sys.exit(error_message( '{} for "{}"'.format(exception, identifier))) def _handle_aid(self, identifier, value): """Handle an AID C #define. Handles an AID, sanity checking, generating the friendly name and adding it to the internal maps. Internal use only. Args: identifier (str): The name of the #define identifier. ie AID_FOO. value (str): The value associated with the identifier. Raises: ValueError: With message set to indicate the error. """ # friendly name name = AIDHeaderParser._convert_friendly(identifier) # duplicate name if name in self._aid_name_to_value: raise ValueError('Duplicate aid "%s"' % identifier) if value in self._aid_value_to_name: raise ValueError('Duplicate aid value "%u" for %s' % value, identifier) self._aid_name_to_value[name] = AID(identifier, value, self._aid_header) self._aid_value_to_name[value] = name def _handle_oem_range(self, identifier, value): """Handle an OEM range C #define. When encountering special AID defines, notably for the OEM ranges this method handles sanity checking and adding them to the internal maps. For internal use only. Args: identifier (str): The name of the #define identifier. ie AID_OEM_RESERVED_START/END. value (str): The value associated with the identifier. Raises: ValueError: With message set to indicate the error. """ try: int_value = int(value, 0) except ValueError: raise ValueError( 'Could not convert "%s" to integer value, got: "%s"' % (identifier, value)) # convert AID_OEM_RESERVED_START or AID_OEM_RESERVED_<num>_START # to AID_OEM_RESERVED or AID_OEM_RESERVED_<num> is_start = identifier.endswith(AIDHeaderParser._OEM_START_KW) if is_start: tostrip = len(AIDHeaderParser._OEM_START_KW) else: tostrip = len(AIDHeaderParser._OEM_END_KW) # ending _ tostrip = tostrip + 1 strip = identifier[:-tostrip] if strip not in self._oem_ranges: self._oem_ranges[strip] = [] if len(self._oem_ranges[strip]) > 2: raise ValueError('Too many same OEM Ranges "%s"' % identifier) if len(self._oem_ranges[strip]) == 1: tmp = self._oem_ranges[strip][0] if tmp == int_value: raise ValueError('START and END values equal %u' % int_value) elif is_start and tmp < int_value: raise ValueError('END value %u less than START value %u' % (tmp, int_value)) elif not is_start and tmp > int_value: raise ValueError('END value %u less than START value %u' % (int_value, tmp)) # Add START values to the head of the list and END values at the end. # Thus, the list is ordered with index 0 as START and index 1 as END. if is_start: self._oem_ranges[strip].insert(0, int_value) else: self._oem_ranges[strip].append(int_value) def _process_and_check(self): """Process, check and populate internal data structures. After parsing and generating the internal data structures, this method is responsible for sanity checking ALL of the acquired data. Raises: ValueError: With the message set to indicate the specific error. """ # tuplefy the lists since range() does not like them mutable. self._oem_ranges = [ AIDHeaderParser._convert_lst_to_tup(k, v) for k, v in self._oem_ranges.iteritems() ] # Check for overlapping ranges for i, range1 in enumerate(self._oem_ranges): for range2 in self._oem_ranges[i + 1:]: if AIDHeaderParser._is_overlap(range1, range2): raise ValueError("Overlapping OEM Ranges found %s and %s" % (str(range1), str(range2))) # No core AIDs should be within any oem range. for aid in self._aid_value_to_name: if Utils.in_any_range(aid, self._oem_ranges): name = self._aid_value_to_name[aid] raise ValueError( 'AID "%s" value: %u within reserved OEM Range: "%s"' % (name, aid, str(self._oem_ranges))) @property def oem_ranges(self): """Retrieves the OEM closed ranges as a list of tuples. Returns: A list of closed range tuples: [ (0, 42), (50, 105) ... ] """ return self._oem_ranges @property def aids(self): """Retrieves the list of found AIDs. Returns: A list of AID() objects. """ return self._aid_name_to_value.values() @staticmethod def _convert_lst_to_tup(name, lst): """Converts a mutable list to a non-mutable tuple. Used ONLY for ranges and thus enforces a length of 2. Args: lst (List): list that should be "tuplefied". Raises: ValueError if lst is not a list or len is not 2. Returns: Tuple(lst) """ if not lst or len(lst) != 2: raise ValueError('Mismatched range for "%s"' % name) return tuple(lst) @staticmethod def _convert_friendly(identifier): """ Translate AID_FOO_BAR to foo_bar (ie name) Args: identifier (str): The name of the #define. Returns: The friendly name as a str. """ name = identifier[len(AIDHeaderParser._AID_KW):].lower() if name in AIDHeaderParser._FIXUPS: return AIDHeaderParser._FIXUPS[name] return name @staticmethod def _is_oem_range(aid): """Detects if a given aid is within the reserved OEM range. Args: aid (int): The aid to test Returns: True if it is within the range, False otherwise. """ return AIDHeaderParser._OEM_RANGE.match(aid) @staticmethod def _is_overlap(range_a, range_b): """Calculates the overlap of two range tuples. A range tuple is a closed range. A closed range includes its endpoints. Note that python tuples use () notation which collides with the mathematical notation for open ranges. Args: range_a: The first tuple closed range eg (0, 5). range_b: The second tuple closed range eg (3, 7). Returns: True if they overlap, False otherwise. """ return max(range_a[0], range_b[0]) <= min(range_a[1], range_b[1]) class FSConfigFileParser(object): """Parses a config.fs ini format file. Loading @@ -131,19 +440,15 @@ class FSConfigFileParser(object): It collects and checks all the data in these files and makes it available for consumption post processed. """ # from system/core/include/private/android_filesystem_config.h _AID_OEM_RESERVED_RANGES = [ (2900, 2999), (5000, 5999), ] _AID_MATCH = re.compile('AID_[a-zA-Z]+') def __init__(self, config_files): def __init__(self, config_files, oem_ranges): """ Args: config_files ([str]): The list of config.fs files to parse. Note the filename is not important. oem_ranges ([(),()]): range tuples indicating reserved OEM ranges. """ self._files = [] Loading @@ -154,6 +459,8 @@ class FSConfigFileParser(object): # (name to file, value to aid) self._seen_aids = ({}, {}) self._oem_ranges = oem_ranges self._config_files = config_files for config_file in self._config_files: Loading Loading @@ -242,13 +549,10 @@ class FSConfigFileParser(object): error_message('Invalid "value", not aid number, got: \"%s\"' % value)) # Values must be within OEM range. if not any(lower <= int(aid.value, 0) <= upper for (lower, upper ) in FSConfigFileParser._AID_OEM_RESERVED_RANGES): # Values must be within OEM range if not Utils.in_any_range(int(aid.value, 0), self._oem_ranges): emsg = '"value" not in valid range %s, got: %s' emsg = emsg % (str(FSConfigFileParser._AID_OEM_RESERVED_RANGES), value) emsg = emsg % (str(self._oem_ranges), value) sys.exit(error_message(emsg)) # use the normalized int value in the dict and detect Loading Loading @@ -539,9 +843,18 @@ class FSConfigGen(BaseGenerator): opt_group.add_argument( 'fsconfig', nargs='+', help='The list of fsconfig files to parse') opt_group.add_argument( '--aid-header', required=True, help='An android_filesystem_config.h file' ' to parse AIDs and OEM Ranges from') def __call__(self, args): parser = FSConfigFileParser(args['fsconfig']) hdr = AIDHeaderParser(args['aid_header']) oem_ranges = hdr.oem_ranges parser = FSConfigFileParser(args['fsconfig'], oem_ranges) FSConfigGen._generate(parser.files, parser.dirs, parser.aids) @staticmethod Loading Loading
tools/fs_config/Android.mk +5 −2 Original line number Diff line number Diff line Loading @@ -81,15 +81,18 @@ LOCAL_SHARED_LIBRARIES := libcutils LOCAL_CFLAGS := -Werror -Wno-error=\#warnings ifneq ($(TARGET_FS_CONFIG_GEN),) system_android_filesystem_config := system/core/include/private/android_filesystem_config.h gen := $(local-generated-sources-dir)/$(ANDROID_FS_CONFIG_H) $(gen): PRIVATE_LOCAL_PATH := $(LOCAL_PATH) $(gen): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) $(gen): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/fs_config_generator.py fsconfig $(PRIVATE_TARGET_FS_CONFIG_GEN) > $@ $(gen): $(TARGET_FS_CONFIG_GEN) $(LOCAL_PATH)/fs_config_generator.py $(gen): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config) $(gen): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/fs_config_generator.py fsconfig --aid-header=$(PRIVATE_ANDROID_FS_HDR) $(PRIVATE_TARGET_FS_CONFIG_GEN) > $@ $(gen): $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(LOCAL_PATH)/fs_config_generator.py $(transform-generated-source) LOCAL_GENERATED_SOURCES := $(gen) my_fs_config_h := $(gen) system_android_filesystem_config := gen := endif Loading
tools/fs_config/fs_config_generator.py +326 −13 Original line number Diff line number Diff line Loading @@ -66,6 +66,27 @@ class generator(object): # pylint: disable=invalid-name return generator._generators class Utils(object): """Various assorted static utilities.""" @staticmethod def in_any_range(value, ranges): """Tests if a value is in a list of given closed range tuples. A range tuple is a closed range. That means it's inclusive of its start and ending values. Args: value (int): The value to test. range [(int, int)]: The closed range list to test value within. Returns: True if value is within the closed range, false otherwise. """ return any(lower <= value <= upper for (lower, upper) in ranges) class AID(object): """This class represents an Android ID or an AID. Loading Loading @@ -124,6 +145,294 @@ class FSConfig(object): self.filename = filename class AIDHeaderParser(object): """Parses an android_filesystem_config.h file. Parses a C header file and extracts lines starting with #define AID_<name> It provides some basic sanity checks. The information extracted from this file can later be used to sanity check other things (like oem ranges) as well as generating a mapping of names to uids. It was primarily designed to parse the private/android_filesystem_config.h, but any C header should work. """ _SKIPWORDS = ['UNUSED'] _AID_KW = 'AID_' _AID_DEFINE = re.compile(r'\s*#define\s+%s.*' % _AID_KW) _OEM_START_KW = 'START' _OEM_END_KW = 'END' _OEM_RANGE = re.compile('AID_OEM_RESERVED_[0-9]*_{0,1}(%s|%s)' % (_OEM_START_KW, _OEM_END_KW)) # Some of the AIDS like AID_MEDIA_EX had names like mediaex # list a map of things to fixup until we can correct these # at a later date. _FIXUPS = { 'media_drm': 'mediadrm', 'media_ex': 'mediaex', 'media_codec': 'mediacodec' } def __init__(self, aid_header): """ Args: aid_header (str): file name for the header file containing AID entries. """ self._aid_header = aid_header self._aid_name_to_value = {} self._aid_value_to_name = {} self._oem_ranges = {} with open(aid_header) as open_file: self._parse(open_file) try: self._process_and_check() except ValueError as exception: sys.exit('Error processing parsed data: "%s"' % (str(exception))) def _parse(self, aid_file): """Parses an AID header file. Internal use only. Args: aid_file (file): The open AID header file to parse. """ for lineno, line in enumerate(aid_file): def error_message(msg): """Creates an error message with the current parsing state.""" return 'Error "{}" in file: "{}" on line: {}'.format( msg, self._aid_header, str(lineno)) if AIDHeaderParser._AID_DEFINE.match(line): chunks = line.split() if any(x in chunks[1] for x in AIDHeaderParser._SKIPWORDS): continue identifier = chunks[1] value = chunks[2] try: if AIDHeaderParser._is_oem_range(identifier): self._handle_oem_range(identifier, value) else: self._handle_aid(identifier, value) except ValueError as exception: sys.exit(error_message( '{} for "{}"'.format(exception, identifier))) def _handle_aid(self, identifier, value): """Handle an AID C #define. Handles an AID, sanity checking, generating the friendly name and adding it to the internal maps. Internal use only. Args: identifier (str): The name of the #define identifier. ie AID_FOO. value (str): The value associated with the identifier. Raises: ValueError: With message set to indicate the error. """ # friendly name name = AIDHeaderParser._convert_friendly(identifier) # duplicate name if name in self._aid_name_to_value: raise ValueError('Duplicate aid "%s"' % identifier) if value in self._aid_value_to_name: raise ValueError('Duplicate aid value "%u" for %s' % value, identifier) self._aid_name_to_value[name] = AID(identifier, value, self._aid_header) self._aid_value_to_name[value] = name def _handle_oem_range(self, identifier, value): """Handle an OEM range C #define. When encountering special AID defines, notably for the OEM ranges this method handles sanity checking and adding them to the internal maps. For internal use only. Args: identifier (str): The name of the #define identifier. ie AID_OEM_RESERVED_START/END. value (str): The value associated with the identifier. Raises: ValueError: With message set to indicate the error. """ try: int_value = int(value, 0) except ValueError: raise ValueError( 'Could not convert "%s" to integer value, got: "%s"' % (identifier, value)) # convert AID_OEM_RESERVED_START or AID_OEM_RESERVED_<num>_START # to AID_OEM_RESERVED or AID_OEM_RESERVED_<num> is_start = identifier.endswith(AIDHeaderParser._OEM_START_KW) if is_start: tostrip = len(AIDHeaderParser._OEM_START_KW) else: tostrip = len(AIDHeaderParser._OEM_END_KW) # ending _ tostrip = tostrip + 1 strip = identifier[:-tostrip] if strip not in self._oem_ranges: self._oem_ranges[strip] = [] if len(self._oem_ranges[strip]) > 2: raise ValueError('Too many same OEM Ranges "%s"' % identifier) if len(self._oem_ranges[strip]) == 1: tmp = self._oem_ranges[strip][0] if tmp == int_value: raise ValueError('START and END values equal %u' % int_value) elif is_start and tmp < int_value: raise ValueError('END value %u less than START value %u' % (tmp, int_value)) elif not is_start and tmp > int_value: raise ValueError('END value %u less than START value %u' % (int_value, tmp)) # Add START values to the head of the list and END values at the end. # Thus, the list is ordered with index 0 as START and index 1 as END. if is_start: self._oem_ranges[strip].insert(0, int_value) else: self._oem_ranges[strip].append(int_value) def _process_and_check(self): """Process, check and populate internal data structures. After parsing and generating the internal data structures, this method is responsible for sanity checking ALL of the acquired data. Raises: ValueError: With the message set to indicate the specific error. """ # tuplefy the lists since range() does not like them mutable. self._oem_ranges = [ AIDHeaderParser._convert_lst_to_tup(k, v) for k, v in self._oem_ranges.iteritems() ] # Check for overlapping ranges for i, range1 in enumerate(self._oem_ranges): for range2 in self._oem_ranges[i + 1:]: if AIDHeaderParser._is_overlap(range1, range2): raise ValueError("Overlapping OEM Ranges found %s and %s" % (str(range1), str(range2))) # No core AIDs should be within any oem range. for aid in self._aid_value_to_name: if Utils.in_any_range(aid, self._oem_ranges): name = self._aid_value_to_name[aid] raise ValueError( 'AID "%s" value: %u within reserved OEM Range: "%s"' % (name, aid, str(self._oem_ranges))) @property def oem_ranges(self): """Retrieves the OEM closed ranges as a list of tuples. Returns: A list of closed range tuples: [ (0, 42), (50, 105) ... ] """ return self._oem_ranges @property def aids(self): """Retrieves the list of found AIDs. Returns: A list of AID() objects. """ return self._aid_name_to_value.values() @staticmethod def _convert_lst_to_tup(name, lst): """Converts a mutable list to a non-mutable tuple. Used ONLY for ranges and thus enforces a length of 2. Args: lst (List): list that should be "tuplefied". Raises: ValueError if lst is not a list or len is not 2. Returns: Tuple(lst) """ if not lst or len(lst) != 2: raise ValueError('Mismatched range for "%s"' % name) return tuple(lst) @staticmethod def _convert_friendly(identifier): """ Translate AID_FOO_BAR to foo_bar (ie name) Args: identifier (str): The name of the #define. Returns: The friendly name as a str. """ name = identifier[len(AIDHeaderParser._AID_KW):].lower() if name in AIDHeaderParser._FIXUPS: return AIDHeaderParser._FIXUPS[name] return name @staticmethod def _is_oem_range(aid): """Detects if a given aid is within the reserved OEM range. Args: aid (int): The aid to test Returns: True if it is within the range, False otherwise. """ return AIDHeaderParser._OEM_RANGE.match(aid) @staticmethod def _is_overlap(range_a, range_b): """Calculates the overlap of two range tuples. A range tuple is a closed range. A closed range includes its endpoints. Note that python tuples use () notation which collides with the mathematical notation for open ranges. Args: range_a: The first tuple closed range eg (0, 5). range_b: The second tuple closed range eg (3, 7). Returns: True if they overlap, False otherwise. """ return max(range_a[0], range_b[0]) <= min(range_a[1], range_b[1]) class FSConfigFileParser(object): """Parses a config.fs ini format file. Loading @@ -131,19 +440,15 @@ class FSConfigFileParser(object): It collects and checks all the data in these files and makes it available for consumption post processed. """ # from system/core/include/private/android_filesystem_config.h _AID_OEM_RESERVED_RANGES = [ (2900, 2999), (5000, 5999), ] _AID_MATCH = re.compile('AID_[a-zA-Z]+') def __init__(self, config_files): def __init__(self, config_files, oem_ranges): """ Args: config_files ([str]): The list of config.fs files to parse. Note the filename is not important. oem_ranges ([(),()]): range tuples indicating reserved OEM ranges. """ self._files = [] Loading @@ -154,6 +459,8 @@ class FSConfigFileParser(object): # (name to file, value to aid) self._seen_aids = ({}, {}) self._oem_ranges = oem_ranges self._config_files = config_files for config_file in self._config_files: Loading Loading @@ -242,13 +549,10 @@ class FSConfigFileParser(object): error_message('Invalid "value", not aid number, got: \"%s\"' % value)) # Values must be within OEM range. if not any(lower <= int(aid.value, 0) <= upper for (lower, upper ) in FSConfigFileParser._AID_OEM_RESERVED_RANGES): # Values must be within OEM range if not Utils.in_any_range(int(aid.value, 0), self._oem_ranges): emsg = '"value" not in valid range %s, got: %s' emsg = emsg % (str(FSConfigFileParser._AID_OEM_RESERVED_RANGES), value) emsg = emsg % (str(self._oem_ranges), value) sys.exit(error_message(emsg)) # use the normalized int value in the dict and detect Loading Loading @@ -539,9 +843,18 @@ class FSConfigGen(BaseGenerator): opt_group.add_argument( 'fsconfig', nargs='+', help='The list of fsconfig files to parse') opt_group.add_argument( '--aid-header', required=True, help='An android_filesystem_config.h file' ' to parse AIDs and OEM Ranges from') def __call__(self, args): parser = FSConfigFileParser(args['fsconfig']) hdr = AIDHeaderParser(args['aid_header']) oem_ranges = hdr.oem_ranges parser = FSConfigFileParser(args['fsconfig'], oem_ranges) FSConfigGen._generate(parser.files, parser.dirs, parser.aids) @staticmethod Loading