def process_func(filename, data): if filename == 'resources.pb': table = Resources_pb2.ResourceTable() table.ParseFromString(data) _HardcodeInTable(table, is_bundle_module, shared_resources_allowlist) data = table.SerializeToString() elif filename.endswith('.xml') and not filename.startswith('res/raw'): xml_node = Resources_pb2.XmlNode() xml_node.ParseFromString(data) _ProcessProtoXmlNode(xml_node) data = xml_node.SerializeToString() return data
def _HardcodeInTable(table, is_bundle_module, shared_resources_allowlist): translations_package = None if is_bundle_module: # A separate top level package will be added to the resources, which # contains only locale specific resources. The package ID of the locale # resources is hardcoded to SHARED_LIBRARY_HARDCODED_ID. This causes # resources in locale splits to all get assigned # SHARED_LIBRARY_HARDCODED_ID as their package ID, which prevents a bug # in shared library bundles where each split APK gets a separate dynamic # ID, and cannot be accessed by the main APK. translations_package = Resources_pb2.Package() translations_package.package_id.id = SHARED_LIBRARY_HARDCODED_ID translations_package.package_name = (table.package[0].package_name + '_translations') # These resources are allowed in the base resources, since they are needed # by WebView. allowed_resource_names = set() if shared_resources_allowlist: allowed_resource_names = set( resource_utils.GetRTxtStringResourceNames(shared_resources_allowlist)) for package in table.package: for _type in package.type: for entry in _type.entry: for config_value in entry.config_value: _ProcessProtoValue(config_value.value) if translations_package is not None: locale_type = _SplitLocaleResourceType(_type, allowed_resource_names) if locale_type: translations_package.type.add().CopyFrom(locale_type) if translations_package is not None: table.package.add().CopyFrom(translations_package)
def _SplitLocaleResourceType(_type, allowed_resource_names): """Splits locale specific resources out of |_type| and returns them. Any locale specific resources will be removed from |_type|, and a new Resources_pb2.Type value will be returned which contains those resources. Args: _type: A Resources_pb2.Type value allowed_resource_names: Names of locale resources that should be kept in the main type. """ locale_entries = [] for entry in _type.entry: if entry.name in allowed_resource_names: continue # First collect all resources values with a locale set. config_values_with_locale = [] for config_value in entry.config_value: if config_value.config.locale: config_values_with_locale.append(config_value) if config_values_with_locale: # Remove the locale resources from the original entry for value in config_values_with_locale: entry.config_value.remove(value) # Add locale resources to a new Entry, and save for later. locale_entry = Resources_pb2.Entry() locale_entry.CopyFrom(entry) del locale_entry.config_value[:] locale_entry.config_value.extend(config_values_with_locale) locale_entries.append(locale_entry) if not locale_entries: return None # Copy the original type and replace the entries with |locale_entries|. locale_type = Resources_pb2.Type() locale_type.CopyFrom(_type) del locale_type.entry[:] locale_type.entry.extend(locale_entries) return locale_type
def _TableFromFlatBytes(data): # https://cs.android.com/android/platform/superproject/+/master:frameworks/base/tools/aapt2/format/Container.cpp size_idx = len(_FLAT_ARSC_HEADER) proto_idx = size_idx + 8 if data[:size_idx] != _FLAT_ARSC_HEADER: raise Exception('Error parsing {} in {}'.format(info.filename, zip_path)) # Size is stored as uint64. size = struct.unpack('<Q', data[size_idx:proto_idx])[0] table = Resources_pb2.ResourceTable() proto_bytes = data[proto_idx:proto_idx + size] table.ParseFromString(proto_bytes) return table
def _HardcodeSharedLibraryDynamicAttributes(zip_path): """Hardcodes the package IDs of dynamic attributes to 0x02. This is a workaround for b/147674078, which affects Android versions pre-N. Args: zip_path: Path to proto APK file. """ with build_utils.TempDir() as tmp_dir: build_utils.ExtractAll(zip_path, path=tmp_dir) # First process the resources file. table = Resources_pb2.ResourceTable() with open(os.path.join(tmp_dir, 'resources.pb')) as f: table.ParseFromString(f.read()) for package in table.package: for _type in package.type: for entry in _type.entry: for config_value in entry.config_value: _ProcessProtoValue(config_value.value) with open(os.path.join(tmp_dir, 'resources.pb'), 'w') as f: f.write(table.SerializeToString()) # Next process all the XML files. xml_files = build_utils.FindInDirectory(tmp_dir, '*.xml') for xml_file in xml_files: xml_node = Resources_pb2.XmlNode() with open(xml_file) as f: xml_node.ParseFromString(f.read()) _ProcessProtoXmlNode(xml_node) with open(xml_file, 'w') as f: f.write(xml_node.SerializeToString()) # Overwrite the original zip file. build_utils.ZipDir(zip_path, tmp_dir)