diff options
72 files changed, 1316 insertions, 872 deletions
diff --git a/ci/build_metadata b/ci/build_metadata index 8136702d94..a8eb65dd36 100755 --- a/ci/build_metadata +++ b/ci/build_metadata @@ -21,5 +21,5 @@ export TARGET_RELEASE=trunk_staging export TARGET_BUILD_VARIANT=eng build/soong/bin/m dist \ - code_metadata + all_teams diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py index 402880c6ac..b8c4a385e0 100644 --- a/ci/build_test_suites.py +++ b/ci/build_test_suites.py @@ -20,7 +20,6 @@ import json import logging import os import pathlib -import re import subprocess import sys from typing import Callable @@ -70,7 +69,7 @@ class BuildPlanner: return BuildPlan(set(self.args.extra_targets), set()) build_targets = set() - packaging_functions = set() + packaging_commands_getters = [] for target in self.args.extra_targets: if self._unused_target_exclusion_enabled( target @@ -86,9 +85,11 @@ class BuildPlanner: target, self.build_context, self.args ) build_targets.update(target_optimizer.get_build_targets()) - packaging_functions.add(target_optimizer.package_outputs) + packaging_commands_getters.append( + target_optimizer.get_package_outputs_commands + ) - return BuildPlan(build_targets, packaging_functions) + return BuildPlan(build_targets, packaging_commands_getters) def _unused_target_exclusion_enabled(self, target: str) -> bool: return ( @@ -100,7 +101,7 @@ class BuildPlanner: @dataclass(frozen=True) class BuildPlan: build_targets: set[str] - packaging_functions: set[Callable[..., None]] + packaging_commands_getters: list[Callable[[], list[list[str]]]] def build_test_suites(argv: list[str]) -> int: @@ -182,8 +183,12 @@ def execute_build_plan(build_plan: BuildPlan): except subprocess.CalledProcessError as e: raise BuildFailureError(e.returncode) from e - for packaging_function in build_plan.packaging_functions: - packaging_function() + for packaging_commands_getter in build_plan.packaging_commands_getters: + try: + for packaging_command in packaging_commands_getter(): + run_command(packaging_command) + except subprocess.CalledProcessError as e: + raise BuildFailureError(e.returncode) from e def get_top() -> pathlib.Path: diff --git a/ci/build_test_suites_test.py b/ci/build_test_suites_test.py index f3ff6f4926..2afaab7711 100644 --- a/ci/build_test_suites_test.py +++ b/ci/build_test_suites_test.py @@ -241,17 +241,17 @@ class BuildPlannerTest(unittest.TestCase): class TestOptimizedBuildTarget(optimized_targets.OptimizedBuildTarget): def __init__( - self, target, build_context, args, output_targets, packaging_outputs + self, target, build_context, args, output_targets, packaging_commands ): super().__init__(target, build_context, args) self.output_targets = output_targets - self.packaging_outputs = packaging_outputs + self.packaging_commands = packaging_commands def get_build_targets_impl(self): return self.output_targets - def package_outputs_impl(self): - self.packaging_outputs.add(f'packaging {" ".join(self.output_targets)}') + def get_package_outputs_commands_impl(self): + return self.packaging_commands def get_enabled_flag(self): return f'{self.target}_enabled' @@ -276,7 +276,8 @@ class BuildPlannerTest(unittest.TestCase): build_plan = build_planner.create_build_plan() - self.assertEqual(len(build_plan.packaging_functions), 0) + for packaging_command in self.run_packaging_commands(build_plan): + self.assertEqual(len(packaging_command), 0) def test_build_optimization_on_optimizes_target(self): build_targets = {'target_1', 'target_2'} @@ -294,20 +295,19 @@ class BuildPlannerTest(unittest.TestCase): def test_build_optimization_on_packages_target(self): build_targets = {'target_1', 'target_2'} - packaging_outputs = set() + optimized_target_name = self.get_optimized_target_name('target_1') + packaging_commands = [[f'packaging {optimized_target_name}']] build_planner = self.create_build_planner( build_targets=build_targets, build_context=self.create_build_context( enabled_build_features=[{'name': self.get_target_flag('target_1')}] ), - packaging_outputs=packaging_outputs, + packaging_commands=packaging_commands, ) build_plan = build_planner.create_build_plan() - self.run_packaging_functions(build_plan) - optimized_target_name = self.get_optimized_target_name('target_1') - self.assertIn(f'packaging {optimized_target_name}', packaging_outputs) + self.assertIn(packaging_commands, self.run_packaging_commands(build_plan)) def test_individual_build_optimization_off_doesnt_optimize(self): build_targets = {'target_1', 'target_2'} @@ -321,16 +321,16 @@ class BuildPlannerTest(unittest.TestCase): def test_individual_build_optimization_off_doesnt_package(self): build_targets = {'target_1', 'target_2'} - packaging_outputs = set() + packaging_commands = [['packaging command']] build_planner = self.create_build_planner( build_targets=build_targets, - packaging_outputs=packaging_outputs, + packaging_commands=packaging_commands, ) build_plan = build_planner.create_build_plan() - self.run_packaging_functions(build_plan) - self.assertFalse(packaging_outputs) + for packaging_command in self.run_packaging_commands(build_plan): + self.assertEqual(len(packaging_command), 0) def test_target_output_used_target_built(self): build_target = 'test_target' @@ -408,7 +408,7 @@ class BuildPlannerTest(unittest.TestCase): target_optimizations: dict[ str, optimized_targets.OptimizedBuildTarget ] = None, - packaging_outputs: set[str] = set(), + packaging_commands: list[list[str]] = [], ) -> build_test_suites.BuildPlanner: if not build_context: build_context = self.create_build_context() @@ -418,7 +418,7 @@ class BuildPlannerTest(unittest.TestCase): target_optimizations = self.create_target_optimizations( build_context, build_targets, - packaging_outputs, + packaging_commands, ) return build_test_suites.BuildPlanner( build_context, args, target_optimizations @@ -450,14 +450,14 @@ class BuildPlannerTest(unittest.TestCase): self, build_context: BuildContext, build_targets: set[str], - packaging_outputs: set[str] = set(), + packaging_commands: list[list[str]] = [], ): target_optimizations = dict() for target in build_targets: target_optimizations[target] = functools.partial( self.TestOptimizedBuildTarget, output_targets={self.get_optimized_target_name(target)}, - packaging_outputs=packaging_outputs, + packaging_commands=packaging_commands, ) return target_optimizations @@ -468,10 +468,6 @@ class BuildPlannerTest(unittest.TestCase): def get_optimized_target_name(self, target: str): return f'{target}_optimized' - def run_packaging_functions(self, build_plan: build_test_suites.BuildPlan): - for packaging_function in build_plan.packaging_functions: - packaging_function() - def get_test_context(self, target: str): return { 'testInfos': [ @@ -491,6 +487,12 @@ class BuildPlannerTest(unittest.TestCase): ], } + def run_packaging_commands(self, build_plan: build_test_suites.BuildPlan): + return [ + packaging_command_getter() + for packaging_command_getter in build_plan.packaging_commands_getters + ] + def wait_until( condition_function: Callable[[], bool], diff --git a/ci/optimized_targets.py b/ci/optimized_targets.py index fddde176ec..688bdd8370 100644 --- a/ci/optimized_targets.py +++ b/ci/optimized_targets.py @@ -16,12 +16,13 @@ from abc import ABC import argparse import functools -from build_context import BuildContext import json import logging import os -from typing import Self +import pathlib +import subprocess +from build_context import BuildContext import test_mapping_module_retriever @@ -33,6 +34,9 @@ class OptimizedBuildTarget(ABC): build. """ + _SOONG_UI_BASH_PATH = 'build/soong/soong_ui.bash' + _PREBUILT_SOONG_ZIP_PATH = 'prebuilts/build-tools/linux-x86/bin/soong_zip' + def __init__( self, target: str, @@ -52,14 +56,17 @@ class OptimizedBuildTarget(ABC): self.modules_to_build = {self.target} return {self.target} - def package_outputs(self): + def get_package_outputs_commands(self) -> list[list[str]]: features = self.build_context.enabled_build_features if self.get_enabled_flag() in features: - return self.package_outputs_impl() + return self.get_package_outputs_commands_impl() - def package_outputs_impl(self): + return [] + + def get_package_outputs_commands_impl(self) -> list[list[str]]: raise NotImplementedError( - f'package_outputs_impl not implemented in {type(self).__name__}' + 'get_package_outputs_commands_impl not implemented in' + f' {type(self).__name__}' ) def get_enabled_flag(self): @@ -72,6 +79,88 @@ class OptimizedBuildTarget(ABC): f'get_build_targets_impl not implemented in {type(self).__name__}' ) + def _generate_zip_options_for_items( + self, + prefix: str = '', + relative_root: str = '', + list_files: list[str] | None = None, + files: list[str] | None = None, + directories: list[str] | None = None, + ) -> list[str]: + if not list_files and not files and not directories: + raise RuntimeError( + f'No items specified to be added to zip! Prefix: {prefix}, Relative' + f' root: {relative_root}' + ) + command_segment = [] + # These are all soong_zip options so consult soong_zip --help for specifics. + if prefix: + command_segment.append('-P') + command_segment.append(prefix) + if relative_root: + command_segment.append('-C') + command_segment.append(relative_root) + if list_files: + for list_file in list_files: + command_segment.append('-l') + command_segment.append(list_file) + if files: + for file in files: + command_segment.append('-f') + command_segment.append(file) + if directories: + for directory in directories: + command_segment.append('-D') + command_segment.append(directory) + + return command_segment + + def _query_soong_vars( + self, src_top: pathlib.Path, soong_vars: list[str] + ) -> dict[str, str]: + process_result = subprocess.run( + args=[ + f'{src_top / self._SOONG_UI_BASH_PATH}', + '--dumpvars-mode', + f'--abs-vars={" ".join(soong_vars)}', + ], + env=os.environ, + check=False, + capture_output=True, + text=True, + ) + if not process_result.returncode == 0: + logging.error('soong dumpvars command failed! stderr:') + logging.error(process_result.stderr) + raise RuntimeError('Soong dumpvars failed! See log for stderr.') + + if not process_result.stdout: + raise RuntimeError( + 'Necessary soong variables ' + soong_vars + ' not found.' + ) + + try: + return { + line.split('=')[0]: line.split('=')[1].strip("'") + for line in process_result.stdout.strip().split('\n') + } + except IndexError as e: + raise RuntimeError( + 'Error parsing soong dumpvars output! See output here:' + f' {process_result.stdout}', + e, + ) + + def _base_zip_command( + self, src_top: pathlib.Path, dist_dir: pathlib.Path, name: str + ) -> list[str]: + return [ + f'{src_top / self._PREBUILT_SOONG_ZIP_PATH }', + '-d', + '-o', + f'{dist_dir / name}', + ] + class NullOptimizer(OptimizedBuildTarget): """No-op target optimizer. @@ -86,8 +175,8 @@ class NullOptimizer(OptimizedBuildTarget): def get_build_targets(self): return {self.target} - def package_outputs(self): - pass + def get_package_outputs_commands(self): + return [] class ChangeInfo: @@ -114,11 +203,10 @@ class ChangeInfo: return changed_files + class GeneralTestsOptimizer(OptimizedBuildTarget): """general-tests optimizer - TODO(b/358215235): Implement - This optimizer reads in the list of changed files from the file located in env[CHANGE_INFO] and uses this list alongside the normal TEST MAPPING logic to determine what test mapping modules will run for the given changes. It then @@ -126,10 +214,13 @@ class GeneralTestsOptimizer(OptimizedBuildTarget): normally built. """ - # List of modules that are always required to be in general-tests.zip. - _REQUIRED_MODULES = frozenset( - ['cts-tradefed', 'vts-tradefed', 'compatibility-host-util'] - ) + # List of modules that are built alongside general-tests as dependencies. + _REQUIRED_MODULES = frozenset([ + 'cts-tradefed', + 'vts-tradefed', + 'compatibility-host-util', + 'general-tests-shared-libs', + ]) def get_build_targets_impl(self) -> set[str]: change_info_file_path = os.environ.get('CHANGE_INFO') @@ -173,6 +264,212 @@ class GeneralTestsOptimizer(OptimizedBuildTarget): return modules_to_build + def get_package_outputs_commands_impl(self): + src_top = pathlib.Path(os.environ.get('TOP', os.getcwd())) + dist_dir = pathlib.Path(os.environ.get('DIST_DIR')) + + soong_vars = self._query_soong_vars( + src_top, + [ + 'HOST_OUT_TESTCASES', + 'TARGET_OUT_TESTCASES', + 'PRODUCT_OUT', + 'SOONG_HOST_OUT', + 'HOST_OUT', + ], + ) + host_out_testcases = pathlib.Path(soong_vars.get('HOST_OUT_TESTCASES')) + target_out_testcases = pathlib.Path(soong_vars.get('TARGET_OUT_TESTCASES')) + product_out = pathlib.Path(soong_vars.get('PRODUCT_OUT')) + soong_host_out = pathlib.Path(soong_vars.get('SOONG_HOST_OUT')) + host_out = pathlib.Path(soong_vars.get('HOST_OUT')) + + host_paths = [] + target_paths = [] + host_config_files = [] + target_config_files = [] + for module in self.modules_to_build: + # The required modules are handled separately, no need to package. + if module in self._REQUIRED_MODULES: + continue + + host_path = host_out_testcases / module + if os.path.exists(host_path): + host_paths.append(host_path) + self._collect_config_files(src_top, host_path, host_config_files) + + target_path = target_out_testcases / module + if os.path.exists(target_path): + target_paths.append(target_path) + self._collect_config_files(src_top, target_path, target_config_files) + + if not os.path.exists(host_path) and not os.path.exists(target_path): + logging.info(f'No host or target build outputs found for {module}.') + + zip_commands = [] + + zip_commands.extend( + self._get_zip_test_configs_zips_commands( + src_top, + dist_dir, + host_out, + product_out, + host_config_files, + target_config_files, + ) + ) + + zip_command = self._base_zip_command(src_top, dist_dir, 'general-tests.zip') + + # Add host testcases. + if host_paths: + zip_command.extend( + self._generate_zip_options_for_items( + prefix='host', + relative_root=f'{src_top / soong_host_out}', + directories=host_paths, + ) + ) + + # Add target testcases. + if target_paths: + zip_command.extend( + self._generate_zip_options_for_items( + prefix='target', + relative_root=f'{src_top / product_out}', + directories=target_paths, + ) + ) + + # TODO(lucafarsi): Push this logic into a general-tests-minimal build command + # Add necessary tools. These are also hardcoded in general-tests.mk. + framework_path = soong_host_out / 'framework' + + zip_command.extend( + self._generate_zip_options_for_items( + prefix='host/tools', + relative_root=str(framework_path), + files=[ + f"{framework_path / 'cts-tradefed.jar'}", + f"{framework_path / 'compatibility-host-util.jar'}", + f"{framework_path / 'vts-tradefed.jar'}", + ], + ) + ) + + zip_commands.append(zip_command) + return zip_commands + + def _collect_config_files( + self, + src_top: pathlib.Path, + root_dir: pathlib.Path, + config_files: list[str], + ): + for root, dirs, files in os.walk(src_top / root_dir): + for file in files: + if file.endswith('.config'): + config_files.append(root_dir / file) + + def _get_zip_test_configs_zips_commands( + self, + src_top: pathlib.Path, + dist_dir: pathlib.Path, + host_out: pathlib.Path, + product_out: pathlib.Path, + host_config_files: list[str], + target_config_files: list[str], + ) -> tuple[list[str], list[str]]: + """Generate general-tests_configs.zip and general-tests_list.zip. + + general-tests_configs.zip contains all of the .config files that were + built and general-tests_list.zip contains a text file which lists + all of the .config files that are in general-tests_configs.zip. + + general-tests_configs.zip is organized as follows: + / + host/ + testcases/ + test_1.config + test_2.config + ... + target/ + testcases/ + test_1.config + test_2.config + ... + + So the process is we write out the paths to all the host config files into + one + file and all the paths to the target config files in another. We also write + the paths to all the config files into a third file to use for + general-tests_list.zip. + + Args: + dist_dir: dist directory. + host_out: host out directory. + product_out: product out directory. + host_config_files: list of all host config files. + target_config_files: list of all target config files. + + Returns: + The commands to generate general-tests_configs.zip and + general-tests_list.zip + """ + with open( + f"{host_out / 'host_general-tests_list'}", 'w' + ) as host_list_file, open( + f"{product_out / 'target_general-tests_list'}", 'w' + ) as target_list_file, open( + f"{host_out / 'general-tests_list'}", 'w' + ) as list_file: + + for config_file in host_config_files: + host_list_file.write(f'{config_file}' + '\n') + list_file.write('host/' + os.path.relpath(config_file, host_out) + '\n') + + for config_file in target_config_files: + target_list_file.write(f'{config_file}' + '\n') + list_file.write( + 'target/' + os.path.relpath(config_file, product_out) + '\n' + ) + + zip_commands = [] + + tests_config_zip_command = self._base_zip_command( + src_top, dist_dir, 'general-tests_configs.zip' + ) + tests_config_zip_command.extend( + self._generate_zip_options_for_items( + prefix='host', + relative_root=str(host_out), + list_files=[f"{host_out / 'host_general-tests_list'}"], + ) + ) + + tests_config_zip_command.extend( + self._generate_zip_options_for_items( + prefix='target', + relative_root=str(product_out), + list_files=[f"{product_out / 'target_general-tests_list'}"], + ), + ) + + zip_commands.append(tests_config_zip_command) + + tests_list_zip_command = self._base_zip_command( + src_top, dist_dir, 'general-tests_list.zip' + ) + tests_list_zip_command.extend( + self._generate_zip_options_for_items( + relative_root=str(host_out), + files=[f"{host_out / 'general-tests_list'}"], + ) + ) + zip_commands.append(tests_list_zip_command) + + return zip_commands + def get_enabled_flag(self): return 'general_tests_optimized' diff --git a/ci/optimized_targets_test.py b/ci/optimized_targets_test.py index 919c193955..0b0c0ec087 100644 --- a/ci/optimized_targets_test.py +++ b/ci/optimized_targets_test.py @@ -19,10 +19,12 @@ import logging import os import pathlib import re +import subprocess +import textwrap import unittest from unittest import mock -import optimized_targets from build_context import BuildContext +import optimized_targets from pyfakefs import fake_filesystem_unittest @@ -43,11 +45,68 @@ class GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase): def _setup_working_build_env(self): self.change_info_file = pathlib.Path('/tmp/change_info') + self._write_soong_ui_file() + self._host_out_testcases = pathlib.Path('/tmp/top/host_out_testcases') + self._host_out_testcases.mkdir(parents=True) + self._target_out_testcases = pathlib.Path('/tmp/top/target_out_testcases') + self._target_out_testcases.mkdir(parents=True) + self._product_out = pathlib.Path('/tmp/top/product_out') + self._product_out.mkdir(parents=True) + self._soong_host_out = pathlib.Path('/tmp/top/soong_host_out') + self._soong_host_out.mkdir(parents=True) + self._host_out = pathlib.Path('/tmp/top/host_out') + self._host_out.mkdir(parents=True) + + self._dist_dir = pathlib.Path('/tmp/top/out/dist') + self._dist_dir.mkdir(parents=True) self.mock_os_environ.update({ 'CHANGE_INFO': str(self.change_info_file), + 'TOP': '/tmp/top', + 'DIST_DIR': '/tmp/top/out/dist', }) + def _write_soong_ui_file(self): + soong_path = pathlib.Path('/tmp/top/build/soong') + soong_path.mkdir(parents=True) + with open(os.path.join(soong_path, 'soong_ui.bash'), 'w') as f: + f.write(""" + #/bin/bash + echo HOST_OUT_TESTCASES='/tmp/top/host_out_testcases' + echo TARGET_OUT_TESTCASES='/tmp/top/target_out_testcases' + echo PRODUCT_OUT='/tmp/top/product_out' + echo SOONG_HOST_OUT='/tmp/top/soong_host_out' + echo HOST_OUT='/tmp/top/host_out' + """) + os.chmod(os.path.join(soong_path, 'soong_ui.bash'), 0o666) + + def _write_change_info_file(self): + change_info_contents = { + 'changes': [{ + 'projectPath': '/project/path', + 'revisions': [{ + 'fileInfos': [{ + 'path': 'file/path/file_name', + }], + }], + }] + } + + with open(self.change_info_file, 'w') as f: + json.dump(change_info_contents, f) + + def _write_test_mapping_file(self): + test_mapping_contents = { + 'test-mapping-group': [ + { + 'name': 'test_mapping_module', + }, + ], + } + + with open('/project/path/file/path/TEST_MAPPING', 'w') as f: + json.dump(test_mapping_contents, f) + def test_general_tests_optimized(self): optimizer = self._create_general_tests_optimizer() @@ -124,36 +183,44 @@ class GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase): with self.assertRaises(json.decoder.JSONDecodeError): build_targets = optimizer.get_build_targets() - def _write_change_info_file(self): - change_info_contents = { - 'changes': [{ - 'projectPath': '/project/path', - 'revisions': [{ - 'fileInfos': [{ - 'path': 'file/path/file_name', - }], - }], - }] - } + @mock.patch('subprocess.run') + def test_packaging_outputs_success(self, subprocess_run): + subprocess_run.return_value = self._get_soong_vars_output() + optimizer = self._create_general_tests_optimizer() + self._set_up_build_outputs(['test_mapping_module']) - with open(self.change_info_file, 'w') as f: - json.dump(change_info_contents, f) + targets = optimizer.get_build_targets() + package_commands = optimizer.get_package_outputs_commands() - def _write_test_mapping_file(self): - test_mapping_contents = { - 'test-mapping-group': [ - { - 'name': 'test_mapping_module', - }, - ], - } + self._verify_soong_zip_commands(package_commands, ['test_mapping_module']) - with open('/project/path/file/path/TEST_MAPPING', 'w') as f: - json.dump(test_mapping_contents, f) + @mock.patch('subprocess.run') + def test_get_soong_dumpvars_fails_raises(self, subprocess_run): + subprocess_run.return_value = self._get_soong_vars_output(return_code=-1) + optimizer = self._create_general_tests_optimizer() + self._set_up_build_outputs(['test_mapping_module']) + + targets = optimizer.get_build_targets() - def _create_general_tests_optimizer( - self, build_context: BuildContext = None - ): + with self.assertRaisesRegex(RuntimeError, 'Soong dumpvars failed!'): + package_commands = optimizer.get_package_outputs_commands() + + @mock.patch('subprocess.run') + def test_get_soong_dumpvars_bad_output_raises(self, subprocess_run): + subprocess_run.return_value = self._get_soong_vars_output( + stdout='This output is bad' + ) + optimizer = self._create_general_tests_optimizer() + self._set_up_build_outputs(['test_mapping_module']) + + targets = optimizer.get_build_targets() + + with self.assertRaisesRegex( + RuntimeError, 'Error parsing soong dumpvars output' + ): + package_commands = optimizer.get_package_outputs_commands() + + def _create_general_tests_optimizer(self, build_context: BuildContext = None): if not build_context: build_context = self._create_build_context() return optimized_targets.GeneralTestsOptimizer( @@ -170,7 +237,9 @@ class GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase): build_context_dict = {} build_context_dict['enabledBuildFeatures'] = [{'name': 'optimized_build'}] if general_tests_optimized: - build_context_dict['enabledBuildFeatures'].append({'name': 'general_tests_optimized'}) + build_context_dict['enabledBuildFeatures'].append( + {'name': 'general_tests_optimized'} + ) build_context_dict['testContext'] = test_context return BuildContext(build_context_dict) @@ -199,6 +268,81 @@ class GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase): ], } + def _get_soong_vars_output( + self, return_code: int = 0, stdout: str = '' + ) -> subprocess.CompletedProcess: + return_value = subprocess.CompletedProcess(args=[], returncode=return_code) + if not stdout: + stdout = textwrap.dedent(f"""\ + HOST_OUT_TESTCASES='{self._host_out_testcases}' + TARGET_OUT_TESTCASES='{self._target_out_testcases}' + PRODUCT_OUT='{self._product_out}' + SOONG_HOST_OUT='{self._soong_host_out}' + HOST_OUT='{self._host_out}'""") + + return_value.stdout = stdout + return return_value + + def _set_up_build_outputs(self, targets: list[str]): + for target in targets: + host_dir = self._host_out_testcases / target + host_dir.mkdir() + (host_dir / f'{target}.config').touch() + (host_dir / f'test_file').touch() + + target_dir = self._target_out_testcases / target + target_dir.mkdir() + (target_dir / f'{target}.config').touch() + (target_dir / f'test_file').touch() + + def _verify_soong_zip_commands(self, commands: list[str], targets: list[str]): + """Verify the structure of the zip commands. + + Zip commands have to start with the soong_zip binary path, then are followed + by a couple of options and the name of the file being zipped. Depending on + which zip we are creating look for a few essential items being added in + those zips. + + Args: + commands: list of command lists + targets: list of targets expected to be in general-tests.zip + """ + for command in commands: + self.assertEqual( + '/tmp/top/prebuilts/build-tools/linux-x86/bin/soong_zip', + command[0], + ) + self.assertEqual('-d', command[1]) + self.assertEqual('-o', command[2]) + match (command[3]): + case '/tmp/top/out/dist/general-tests_configs.zip': + self.assertIn(f'{self._host_out}/host_general-tests_list', command) + self.assertIn( + f'{self._product_out}/target_general-tests_list', command + ) + return + case '/tmp/top/out/dist/general-tests_list.zip': + self.assertIn('-f', command) + self.assertIn(f'{self._host_out}/general-tests_list', command) + return + case '/tmp/top/out/dist/general-tests.zip': + for target in targets: + self.assertIn(f'{self._host_out_testcases}/{target}', command) + self.assertIn(f'{self._target_out_testcases}/{target}', command) + self.assertIn( + f'{self._soong_host_out}/framework/cts-tradefed.jar', command + ) + self.assertIn( + f'{self._soong_host_out}/framework/compatibility-host-util.jar', + command, + ) + self.assertIn( + f'{self._soong_host_out}/framework/vts-tradefed.jar', command + ) + return + case _: + self.fail(f'malformed command: {command}') + if __name__ == '__main__': # Setup logging to be silent so unit tests can pass through TF. diff --git a/core/Makefile b/core/Makefile index bf2d48a159..49b1432266 100644 --- a/core/Makefile +++ b/core/Makefile @@ -3511,6 +3511,8 @@ $(SYSTEM_LINKER_CONFIG): $(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SO --output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)" $(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key requireLibs \ --value "$(foreach lib,$(LLNDK_MOVED_TO_APEX_LIBRARIES), $(lib).so)" + $(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key provideLibs \ + --value "$(foreach lib,$(PRODUCT_EXTRA_STUB_LIBRARIES), $(lib).so)" $(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),) $(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE)) @@ -3588,10 +3590,10 @@ ifeq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true) ifeq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),) $(error PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true) endif -soong_defined_system_image := $(call intermediates-dir-for,ETC,$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE))/$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE) -$(BUILT_SYSTEMIMAGE): $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(soong_defined_system_image) -$(eval $(call copy-one-file, $(soong_defined_system_image), $(BUILT_SYSTEMIMAGE))) -soong_defined_system_image := +SOONG_DEFINED_SYSTEM_IMAGE_PATH := $(call intermediates-dir-for,ETC,$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE))/$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE) +SOONG_DEFINED_SYSTEM_IMAGE_BASE := $(dir $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST)) +$(BUILT_SYSTEMIMAGE): $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(SOONG_DEFINED_SYSTEM_IMAGE_PATH) +$(eval $(call copy-one-file, $(SOONG_DEFINED_SYSTEM_IMAGE_PATH), $(BUILT_SYSTEMIMAGE))) else $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(call build-systemimage-target,$@) @@ -6134,6 +6136,9 @@ $(BUILT_TARGET_FILES_PACKAGE): zip_root := $(intermediates)/$(name) $(BUILT_TARGET_FILES_DIR): zip_root := $(intermediates)/$(name) $(BUILT_TARGET_FILES_DIR): intermediates := $(intermediates) +ifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),) + $(BUILT_TARGET_FILES_DIR): $(SOONG_DEFINED_SYSTEM_IMAGE_PATH) +endif # $(1): Directory to copy # $(2): Location to copy it to @@ -6462,8 +6467,11 @@ $(BUILT_TARGET_FILES_DIR): \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_DTBIMAGE_TARGET) \ $(INSTALLED_2NDBOOTLOADER_TARGET) \ + $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ $(BUILT_RAMDISK_16K_TARGET) \ $(BUILT_KERNEL_16K_TARGET) \ + $(BUILT_BOOTIMAGE_16K_TARGET) \ + $(INSTALLED_DTBOIMAGE_16KB_TARGET) \ $(BOARD_PREBUILT_DTBOIMAGE) \ $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \ $(BOARD_RECOVERY_ACPIO) \ @@ -6617,8 +6625,13 @@ endif endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET ifdef BUILDING_SYSTEM_IMAGE @# Contents of the system image +ifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),) + $(hide) $(call package_files-copy-root, \ + $(SOONG_DEFINED_SYSTEM_IMAGE_BASE)/root/system,$(zip_root)/SYSTEM) +else $(hide) $(call package_files-copy-root, \ $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM) +endif else ifdef INSTALLED_BUILD_PROP_TARGET @# Copy the system build.prop even if not building a system image @# because add_img_to_target_files may need it to build other partition diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk index 5fc8fd4027..75e9f77ffa 100644 --- a/core/android_soong_config_vars.mk +++ b/core/android_soong_config_vars.mk @@ -47,6 +47,8 @@ $(call soong_config_set_bool,ANDROID,GCOV_COVERAGE,$(NATIVE_COVERAGE)) $(call soong_config_set_bool,ANDROID,CLANG_COVERAGE,$(CLANG_COVERAGE)) $(call soong_config_set,ANDROID,SCUDO_ALLOCATION_RING_BUFFER_SIZE,$(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)) +$(call soong_config_set_bool,ANDROID,EMMA_INSTRUMENT,$(if $(filter true,$(EMMA_INSTRUMENT)),true,false)) + # PRODUCT_PRECOMPILED_SEPOLICY defaults to true. Explicitly check if it's "false" or not. $(call soong_config_set_bool,ANDROID,PRODUCT_PRECOMPILED_SEPOLICY,$(if $(filter false,$(PRODUCT_PRECOMPILED_SEPOLICY)),false,true)) @@ -182,3 +184,6 @@ endif # Add target_use_pan_display flag for hardware/libhardware:gralloc.default $(call soong_config_set_bool,gralloc,target_use_pan_display,$(if $(filter true,$(TARGET_USE_PAN_DISPLAY)),true,false)) + +# Add use_camera_v4l2_hal flag for hardware/libhardware/modules/camera/3_4:camera.v4l2 +$(call soong_config_set_bool,camera,use_camera_v4l2_hal,$(if $(filter true,$(USE_CAMERA_V4L2_HAL)),true,false)) diff --git a/core/main.mk b/core/main.mk index 8d0b465e0a..80ffec43b7 100644 --- a/core/main.mk +++ b/core/main.mk @@ -289,6 +289,9 @@ subdir_makefiles_total := $(words int $(subdir_makefiles) post finish) $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk))) +# Build bootloader.img/radio.img, and unpack the partitions. +include $(BUILD_SYSTEM)/tasks/tools/update_bootloader_radio_image.mk + # For an unbundled image, we can skip blueprint_tools because unbundled image # aims to remove a large number framework projects from the manifest, the # sources or dependencies for these tools may be missing from the tree. @@ -297,6 +300,9 @@ droid_targets : blueprint_tools checkbuild: blueprint_tests endif +# Create necessary directories and symlinks in the root filesystem +include system/core/rootdir/create_root_structure.mk + endif # dont_bother ifndef subdir_makefiles_total @@ -305,9 +311,6 @@ endif $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] finishing legacy Make module parsing ...) -# Create necessary directories and symlinks in the root filesystem -include system/core/rootdir/create_root_structure.mk - # ------------------------------------------------------------------- # All module makefiles have been included at this point. # ------------------------------------------------------------------- diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk index a77956bdea..4693bcd6d8 100644 --- a/core/packaging/flags.mk +++ b/core/packaging/flags.mk @@ -18,7 +18,7 @@ # # TODO: Should we do all of the images in $(IMAGES_TO_BUILD)? -_FLAG_PARTITIONS := product system system_ext vendor +_FLAG_PARTITIONS := product system vendor # ----------------------------------------------------------------- @@ -28,7 +28,6 @@ _FLAG_PARTITIONS := product system system_ext vendor # $(1): built aconfig flags file (out) # $(2): installed aconfig flags file (out) # $(3): the partition (in) -# $(4): input aconfig files for the partition (in) define generate-partition-aconfig-flag-file $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1))) $(eval $(strip $(1)): PRIVATE_IN := $(strip $(4))) @@ -36,12 +35,14 @@ $(strip $(1)): $(ACONFIG) $(strip $(4)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \ - --filter container:$$(strip $(3)) $$(addprefix --cache ,$$(PRIVATE_IN)), \ + --filter container:$(strip $(3)) \ + $$(addprefix --cache ,$$(PRIVATE_IN)), \ echo -n > $$(PRIVATE_OUT) \ ) $(call copy-one-file, $(1), $(2)) endef + # Create a summary file of build flags for each partition # $(1): built aconfig flags file (out) # $(2): installed aconfig flags file (out) @@ -59,16 +60,22 @@ $(strip $(1)): $(ACONFIG) $(strip $(3)) $(call copy-one-file, $(1), $(2)) endef - $(foreach partition, $(_FLAG_PARTITIONS), \ $(eval aconfig_flag_summaries_protobuf.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig_flags.pb) \ $(eval $(call generate-partition-aconfig-flag-file, \ - $(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \ - $(aconfig_flag_summaries_protobuf.$(partition)), \ - $(partition), \ - $(sort $(foreach m,$(call register-names-for-partition, $(partition)), \ + $(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \ + $(aconfig_flag_summaries_protobuf.$(partition)), \ + $(partition), \ + $(sort \ + $(foreach m, $(call register-names-for-partition, $(partition)), \ $(ALL_MODULES.$(m).ACONFIG_FILES) \ - )), \ + ) \ + $(if $(filter system, $(partition)), \ + $(foreach m, $(call register-names-for-partition, system_ext), \ + $(ALL_MODULES.$(m).ACONFIG_FILES) \ + ) \ + ) \ + ) \ )) \ ) @@ -175,4 +182,3 @@ $(foreach partition, $(_FLAG_PARTITIONS), \ $(eval aconfig_storage_flag_map.$(partition):=) \ $(eval aconfig_storage_flag_val.$(partition):=) \ ) - diff --git a/core/product.mk b/core/product.mk index 4c23e5dfdd..b07e6e0dc4 100644 --- a/core/product.mk +++ b/core/product.mk @@ -499,6 +499,10 @@ _product_single_value_vars += PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER # If set, build would generate system image from Soong-defined module. _product_single_value_vars += PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE +# List of stub libraries specific to the product that are already present in the system image and +# should be included in the system_linker_config. +_product_list_vars += PRODUCT_EXTRA_STUB_LIBRARIES + .KATI_READONLY := _product_single_value_vars _product_list_vars _product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars) diff --git a/core/soong_config.mk b/core/soong_config.mk index fddb500058..1e6388a5ba 100644 --- a/core/soong_config.mk +++ b/core/soong_config.mk @@ -236,6 +236,12 @@ $(call add_json_list, ProductPrivateSepolicyDirs, $(PRODUCT_PRIVATE_SEPOL $(call add_json_list, TargetFSConfigGen, $(TARGET_FS_CONFIG_GEN)) +# Although USE_SOONG_DEFINED_SYSTEM_IMAGE determines whether to use the system image specified by +# PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE, PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE is still used to compare +# installed files between make and soong, regardless of the USE_SOONG_DEFINED_SYSTEM_IMAGE setting. +$(call add_json_bool, UseSoongSystemImage, $(filter true,$(USE_SOONG_DEFINED_SYSTEM_IMAGE))) +$(call add_json_str, ProductSoongDefinedSystemImage, $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE)) + $(call add_json_map, VendorVars) $(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\ $(call add_json_map, $(namespace))\ diff --git a/core/tasks/sts-sdk.mk b/core/tasks/sts-sdk.mk index b8ce5bf4a5..4abbc29c5e 100644 --- a/core/tasks/sts-sdk.mk +++ b/core/tasks/sts-sdk.mk @@ -28,8 +28,7 @@ $(sts_sdk_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(sts_sdk_plugin_s rm -f $@ $(STS_SDK_ZIP)_filtered $(ZIP2ZIP) -i $(STS_SDK_ZIP) -o $(STS_SDK_ZIP)_filtered \ -x android-sts-sdk/tools/sts-tradefed-tests.jar \ - 'android-sts-sdk/tools/*:plugin/src/main/resources/sts-tradefed-tools/' \ - 'android-sts-sdk/jdk/**/*:plugin/src/main/resources/jdk/' + 'android-sts-sdk/tools/*:sts-sdk/src/main/resources/sts-tradefed-tools/' $(MERGE_ZIPS) $@ $(STS_SDK_ZIP)_filtered $(STS_SDK_PLUGIN_SKEL) rm -f $(STS_SDK_ZIP)_filtered diff --git a/core/tasks/tools/update_bootloader_radio_image.mk b/core/tasks/tools/update_bootloader_radio_image.mk new file mode 100644 index 0000000000..0ebf247213 --- /dev/null +++ b/core/tasks/tools/update_bootloader_radio_image.mk @@ -0,0 +1,17 @@ +# Copyright (C) 2024 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. + +ifeq ($(USES_DEVICE_GOOGLE_ZUMA),true) + -include vendor/google_devices/zuma/prebuilts/misc_bins/update_bootloader_radio_image.mk +endif diff --git a/target/product/base_system.mk b/target/product/base_system.mk index d806c061b6..563511fd12 100644 --- a/target/product/base_system.mk +++ b/target/product/base_system.mk @@ -345,6 +345,11 @@ ifeq ($(RELEASE_USE_WEBVIEW_BOOTSTRAP_MODULE),true) com.android.webview.bootstrap endif +ifneq (,$(RELEASE_RANGING_STACK)) + PRODUCT_PACKAGES += \ + com.android.ranging +endif + # VINTF data for system image PRODUCT_PACKAGES += \ system_manifest.xml \ diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk index 1a3f2cf0e8..668f054773 100644 --- a/target/product/default_art_config.mk +++ b/target/product/default_art_config.mk @@ -76,6 +76,7 @@ PRODUCT_APEX_BOOT_JARS := \ com.android.mediaprovider:framework-mediaprovider \ com.android.mediaprovider:framework-pdf \ com.android.mediaprovider:framework-pdf-v \ + com.android.mediaprovider:framework-photopicker \ com.android.ondevicepersonalization:framework-ondevicepersonalization \ com.android.os.statsd:framework-statsd \ com.android.permission:framework-permission \ @@ -113,6 +114,12 @@ ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true) endif +ifneq (,$(RELEASE_RANGING_STACK)) + PRODUCT_APEX_BOOT_JARS += \ + com.android.uwb:framework-ranging \ + $(call soong_config_set,bootclasspath,release_ranging_stack,true) +endif + # List of system_server classpath jars delivered via apex. # Keep the list sorted by module names and then library names. # Note: For modules available in Q, DO NOT add new entries here. @@ -168,6 +175,11 @@ ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true) endif +ifneq (,$(RELEASE_RANGING_STACK)) + PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \ + com.android.uwb:service-ranging +endif + # Overrides the (apex, jar) pairs above when determining the on-device location. The format is: # <old_apex>:<old_jar>:<new_apex>:<new_jar> PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES := \ diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp index 68521af91f..5037783fb5 100644 --- a/tools/aconfig/aconfig/Android.bp +++ b/tools/aconfig/aconfig/Android.bp @@ -68,6 +68,14 @@ aconfig_values { ], } +aconfig_values { + name: "aconfig.test.flag.second_values", + package: "com.android.aconfig.test", + srcs: [ + "tests/third.values", + ], +} + aconfig_value_set { name: "aconfig.test.flag.value_set", values: [ diff --git a/tools/aconfig/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs index 2c569da8f6..7a9c382bc7 100644 --- a/tools/aconfig/aconfig/src/codegen/cpp.rs +++ b/tools/aconfig/aconfig/src/codegen/cpp.rs @@ -283,39 +283,23 @@ public: virtual ~flag_provider_interface() = default; virtual bool disabled_ro() = 0; - - virtual void disabled_ro(bool val) = 0; - virtual bool disabled_rw() = 0; - - virtual void disabled_rw(bool val) = 0; - virtual bool disabled_rw_exported() = 0; - - virtual void disabled_rw_exported(bool val) = 0; - virtual bool disabled_rw_in_other_namespace() = 0; - - virtual void disabled_rw_in_other_namespace(bool val) = 0; - virtual bool enabled_fixed_ro() = 0; - - virtual void enabled_fixed_ro(bool val) = 0; - virtual bool enabled_fixed_ro_exported() = 0; - - virtual void enabled_fixed_ro_exported(bool val) = 0; - virtual bool enabled_ro() = 0; - - virtual void enabled_ro(bool val) = 0; - virtual bool enabled_ro_exported() = 0; - - virtual void enabled_ro_exported(bool val) = 0; - virtual bool enabled_rw() = 0; + virtual void disabled_ro(bool val) = 0; + virtual void disabled_rw(bool val) = 0; + virtual void disabled_rw_exported(bool val) = 0; + virtual void disabled_rw_in_other_namespace(bool val) = 0; + virtual void enabled_fixed_ro(bool val) = 0; + virtual void enabled_fixed_ro_exported(bool val) = 0; + virtual void enabled_ro(bool val) = 0; + virtual void enabled_ro_exported(bool val) = 0; virtual void enabled_rw(bool val) = 0; virtual void reset_flags() {} diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs index dbc4ab500b..1ac58c1b84 100644 --- a/tools/aconfig/aconfig/src/codegen/java.rs +++ b/tools/aconfig/aconfig/src/codegen/java.rs @@ -745,10 +745,8 @@ mod tests { boolean val; try { val = reader.getBooleanFlagValue(1); - if (val == disabledRw) { - Log.i(TAG, String.format(SUCCESS_LOG, "disabledRw")); - } else { - Log.i(TAG, String.format(MISMATCH_LOG, "disabledRw", val, disabledRw)); + if (val != disabledRw) { + Log.w(TAG, String.format(MISMATCH_LOG, "disabledRw", val, disabledRw)); } if (useNewStorageValueAndDiscardOld) { @@ -756,10 +754,8 @@ mod tests { } val = reader.getBooleanFlagValue(2); - if (val == disabledRwExported) { - Log.i(TAG, String.format(SUCCESS_LOG, "disabledRwExported")); - } else { - Log.i(TAG, String.format(MISMATCH_LOG, "disabledRwExported", val, disabledRwExported)); + if (val != disabledRwExported) { + Log.w(TAG, String.format(MISMATCH_LOG, "disabledRwExported", val, disabledRwExported)); } if (useNewStorageValueAndDiscardOld) { @@ -767,10 +763,8 @@ mod tests { } val = reader.getBooleanFlagValue(8); - if (val == enabledRw) { - Log.i(TAG, String.format(SUCCESS_LOG, "enabledRw")); - } else { - Log.i(TAG, String.format(MISMATCH_LOG, "enabledRw", val, enabledRw)); + if (val != enabledRw) { + Log.w(TAG, String.format(MISMATCH_LOG, "enabledRw", val, enabledRw)); } if (useNewStorageValueAndDiscardOld) { @@ -804,10 +798,8 @@ mod tests { boolean val; try { val = reader.getBooleanFlagValue(3); - if (val == disabledRwInOtherNamespace) { - Log.i(TAG, String.format(SUCCESS_LOG, "disabledRwInOtherNamespace")); - } else { - Log.i(TAG, String.format(MISMATCH_LOG, "disabledRwInOtherNamespace", val, disabledRwInOtherNamespace)); + if (val != disabledRwInOtherNamespace) { + Log.w(TAG, String.format(MISMATCH_LOG, "disabledRwInOtherNamespace", val, disabledRwInOtherNamespace)); } if (useNewStorageValueAndDiscardOld) { diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs index 6f3f7bfa8f..7bc34d6cfe 100644 --- a/tools/aconfig/aconfig/src/codegen/rust.rs +++ b/tools/aconfig/aconfig/src/codegen/rust.rs @@ -116,10 +116,6 @@ use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; -static STORAGE_MIGRATION_MARKER_FILE: &str = - "/metadata/aconfig_test_missions/mission_1"; -static MIGRATION_LOG_TAG: &str = "AconfigTestMission1"; - /// flag provider pub struct FlagProvider; @@ -260,13 +256,13 @@ use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; -static STORAGE_MIGRATION_MARKER_FILE: &str = - "/metadata/aconfig_test_missions/mission_1"; -static MIGRATION_LOG_TAG: &str = "AconfigTestMission1"; - /// flag provider pub struct FlagProvider; +static READ_FROM_NEW_STORAGE: LazyLock<bool> = LazyLock::new(|| unsafe { + Path::new("/metadata/aconfig/boot/enable_only_new_storage").exists() +}); + static PACKAGE_OFFSET: LazyLock<Result<Option<u32>, AconfigStorageError>> = LazyLock::new(|| unsafe { get_mapped_storage_file("system", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test")) @@ -279,24 +275,14 @@ static FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new /// flag value cache for disabled_rw static CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| { - let result = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.aconfig_test", - "com.android.aconfig.test.disabled_rw", - "false") == "true"; - - let use_new_storage_value = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; - - if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { + if *READ_FROM_NEW_STORAGE { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) + .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); - let aconfig_storage_result = FLAG_VAL_MAP + let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { @@ -314,54 +300,33 @@ static CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| { }) }); - match aconfig_storage_result { - Ok(storage_result) if storage_result == result => { - if use_new_storage_value { - return storage_result; - } else { - return result; - } - }, - Ok(storage_result) => { - log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw'. Legacy storage was {result}, new storage was {storage_result}"); - if use_new_storage_value { - return storage_result; - } else { - return result; - } + match flag_value_result { + Ok(flag_value) => { + return flag_value; }, Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: {err}"); - if use_new_storage_value { - panic!("failed to read flag value: {err}"); - } + log!(Level::Error, "aconfig_rust_codegen: error: {err}"); + panic!("failed to read flag value: {err}"); } } + } else { + flags_rust::GetServerConfigurableFlag( + "aconfig_flags.aconfig_test", + "com.android.aconfig.test.disabled_rw", + "false") == "true" } - - result }); /// flag value cache for disabled_rw_exported static CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| { - let result = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.aconfig_test", - "com.android.aconfig.test.disabled_rw_exported", - "false") == "true"; - - let use_new_storage_value = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; - - if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { + if *READ_FROM_NEW_STORAGE { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) + .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); - let aconfig_storage_result = FLAG_VAL_MAP + let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { @@ -379,54 +344,33 @@ static CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| { }) }); - match aconfig_storage_result { - Ok(storage_result) if storage_result == result => { - if use_new_storage_value { - return storage_result; - } else { - return result; - } - }, - Ok(storage_result) => { - log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_exported'. Legacy storage was {result}, new storage was {storage_result}"); - if use_new_storage_value { - return storage_result; - } else { - return result; - } + match flag_value_result { + Ok(flag_value) => { + return flag_value; }, Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: {err}"); - if use_new_storage_value { - panic!("failed to read flag value: {err}"); - } + log!(Level::Error, "aconfig_rust_codegen: error: {err}"); + panic!("failed to read flag value: {err}"); } } + } else { + flags_rust::GetServerConfigurableFlag( + "aconfig_flags.aconfig_test", + "com.android.aconfig.test.disabled_rw_exported", + "false") == "true" } - - result }); /// flag value cache for disabled_rw_in_other_namespace static CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| { - let result = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.other_namespace", - "com.android.aconfig.test.disabled_rw_in_other_namespace", - "false") == "true"; - - let use_new_storage_value = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; - - if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { + if *READ_FROM_NEW_STORAGE { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) + .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); - let aconfig_storage_result = FLAG_VAL_MAP + let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { @@ -444,55 +388,34 @@ static CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| }) }); - match aconfig_storage_result { - Ok(storage_result) if storage_result == result => { - if use_new_storage_value { - return storage_result; - } else { - return result; - } - }, - Ok(storage_result) => { - log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_in_other_namespace'. Legacy storage was {result}, new storage was {storage_result}"); - if use_new_storage_value { - return storage_result; - } else { - return result; - } + match flag_value_result { + Ok(flag_value) => { + return flag_value; }, Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: {err}"); - if use_new_storage_value { - panic!("failed to read flag value: {err}"); - } + log!(Level::Error, "aconfig_rust_codegen: error: {err}"); + panic!("failed to read flag value: {err}"); } } + } else { + flags_rust::GetServerConfigurableFlag( + "aconfig_flags.other_namespace", + "com.android.aconfig.test.disabled_rw_in_other_namespace", + "false") == "true" } - - result }); /// flag value cache for enabled_rw static CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| { - let result = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.aconfig_test", - "com.android.aconfig.test.enabled_rw", - "true") == "true"; - - let use_new_storage_value = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; - - if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { + if *READ_FROM_NEW_STORAGE { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) + .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); - let aconfig_storage_result = FLAG_VAL_MAP + let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { @@ -510,32 +433,21 @@ static CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| { }) }); - match aconfig_storage_result { - Ok(storage_result) if storage_result == result => { - if use_new_storage_value { - return storage_result; - } else { - return result; - } - }, - Ok(storage_result) => { - log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'enabled_rw'. Legacy storage was {result}, new storage was {storage_result}"); - if use_new_storage_value { - return storage_result; - } else { - return result; - } + match flag_value_result { + Ok(flag_value) => { + return flag_value; }, Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: {err}"); - if use_new_storage_value { - panic!("failed to read flag value: {err}"); - } + log!(Level::Error, "aconfig_rust_codegen: error: {err}"); + panic!("failed to read flag value: {err}"); } } + } else { + flags_rust::GetServerConfigurableFlag( + "aconfig_flags.aconfig_test", + "com.android.aconfig.test.enabled_rw", + "true") == "true" } - - result }); impl FlagProvider { @@ -596,65 +508,7 @@ pub static PROVIDER: FlagProvider = FlagProvider; /// query flag disabled_ro #[inline(always)] pub fn disabled_ro() -> bool { - - - let result = false; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe { - let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) { - Ok(file) => file, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") { - Ok(Some(context)) => context, - Ok(None) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': did not get context"); - return result; - }, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) { - Ok(val_map) => val_map, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, 0 + package_read_context.boolean_start_index) { - Ok(val) => val, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}"); - return result; - } - }; - - if result != value { - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'disabled_ro'. Legacy storage was {result}, new storage was {value}"); - } else { - let default_value = false; - } - } - - result - + false } /// query flag disabled_rw @@ -678,257 +532,25 @@ pub fn disabled_rw_in_other_namespace() -> bool { /// query flag enabled_fixed_ro #[inline(always)] pub fn enabled_fixed_ro() -> bool { - - - let result = true; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe { - let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) { - Ok(file) => file, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") { - Ok(Some(context)) => context, - Ok(None) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': did not get context"); - return result; - }, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) { - Ok(val_map) => val_map, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, 4 + package_read_context.boolean_start_index) { - Ok(val) => val, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}"); - return result; - } - }; - - if result != value { - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro'. Legacy storage was {result}, new storage was {value}"); - } else { - let default_value = true; - } - } - - result - + true } /// query flag enabled_fixed_ro_exported #[inline(always)] pub fn enabled_fixed_ro_exported() -> bool { - - - let result = true; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe { - let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) { - Ok(file) => file, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") { - Ok(Some(context)) => context, - Ok(None) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': did not get context"); - return result; - }, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) { - Ok(val_map) => val_map, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, 5 + package_read_context.boolean_start_index) { - Ok(val) => val, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}"); - return result; - } - }; - - if result != value { - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro_exported'. Legacy storage was {result}, new storage was {value}"); - } else { - let default_value = true; - } - } - - result - + true } /// query flag enabled_ro #[inline(always)] pub fn enabled_ro() -> bool { - - - let result = true; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe { - let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) { - Ok(file) => file, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") { - Ok(Some(context)) => context, - Ok(None) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': did not get context"); - return result; - }, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) { - Ok(val_map) => val_map, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, 6 + package_read_context.boolean_start_index) { - Ok(val) => val, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}"); - return result; - } - }; - - if result != value { - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro'. Legacy storage was {result}, new storage was {value}"); - } else { - let default_value = true; - } - } - - result - + true } /// query flag enabled_ro_exported #[inline(always)] pub fn enabled_ro_exported() -> bool { - - - let result = true; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() { - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe { - let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) { - Ok(file) => file, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") { - Ok(Some(context)) => context, - Ok(None) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': did not get context"); - return result; - }, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) { - Ok(val_map) => val_map, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, 7 + package_read_context.boolean_start_index) { - Ok(val) => val, - Err(err) => { - log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}"); - return result; - } - }; - - if result != value { - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro_exported'. Legacy storage was {result}, new storage was {value}"); - } else { - let default_value = true; - } - } - - result - + true } /// query flag enabled_rw @@ -1203,10 +825,6 @@ use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; -static STORAGE_MIGRATION_MARKER_FILE: &str = - "/metadata/aconfig_test_missions/mission_1"; -static MIGRATION_LOG_TAG: &str = "AconfigTestMission1"; - /// flag provider pub struct FlagProvider; @@ -1275,10 +893,6 @@ use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; -static STORAGE_MIGRATION_MARKER_FILE: &str = - "/metadata/aconfig_test_missions/mission_1"; -static MIGRATION_LOG_TAG: &str = "AconfigTestMission1"; - /// flag provider pub struct FlagProvider; diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs index 1a14f6415a..b5854165bc 100644 --- a/tools/aconfig/aconfig/src/commands.rs +++ b/tools/aconfig/aconfig/src/commands.rs @@ -17,7 +17,7 @@ use anyhow::{bail, ensure, Context, Result}; use itertools::Itertools; use protobuf::Message; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hasher; use std::io::Read; use std::path::PathBuf; @@ -79,8 +79,18 @@ pub fn parse_flags( .read_to_string(&mut contents) .with_context(|| format!("failed to read {}", input.source))?; - let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents) - .with_context(|| input.error_context())?; + let mut flag_declarations = + aconfig_protos::flag_declarations::try_from_text_proto(&contents) + .with_context(|| input.error_context())?; + + // system_ext flags should be treated as system flags as we are combining /system_ext + // and /system as one container + // TODO: remove this logic when we start enforcing that system_ext cannot be set as + // container in aconfig declaration files. + if flag_declarations.container() == "system_ext" { + flag_declarations.set_container(String::from("system")); + } + ensure!( package == flag_declarations.package(), "failed to parse {}: expected package {}, got {}", @@ -412,25 +422,32 @@ where Ok(flag_ids) } -#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to - // protect hardcoded offset reads. -pub fn compute_flag_offsets_fingerprint(flags_map: &HashMap<String, u16>) -> Result<u64> { - let mut hasher = SipHasher13::new(); - - // Need to sort to ensure the data is added to the hasher in the same order - // each run. - let sorted_map: BTreeMap<&String, &u16> = flags_map.iter().collect(); +// Creates a fingerprint of the flag names. Sorts the vector. +pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> Result<u64> { + flag_names.sort(); - for (flag, offset) in sorted_map { - // See https://docs.rs/siphasher/latest/siphasher/#note for use of write - // over write_i16. Similarly, use to_be_bytes rather than to_ne_bytes to - // ensure consistency. + let mut hasher = SipHasher13::new(); + for flag in flag_names { hasher.write(flag.as_bytes()); - hasher.write(&offset.to_be_bytes()); } Ok(hasher.finish()) } +#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to + // protect hardcoded offset reads. +fn compute_fingerprint_from_parsed_flags(flags: ProtoParsedFlags) -> Result<u64> { + let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>(); + + // All flags must belong to the same package as the fingerprint is per-package. + let Some(_package) = find_unique_package(&separated_flags) else { + bail!("No parsed flags, or the parsed flags use different packages."); + }; + + let mut flag_names = + separated_flags.into_iter().map(|flag| flag.name.unwrap()).collect::<Vec<_>>(); + compute_flags_fingerprint(&mut flag_names) +} + #[cfg(test)] mod tests { use super::*; @@ -439,16 +456,47 @@ mod tests { #[test] fn test_offset_fingerprint() { let parsed_flags = crate::test::parse_test_flags(); - let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string(); - let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap(); - let expected_fingerprint = 10709892481002252132u64; + let expected_fingerprint: u64 = 5801144784618221668; - let hash_result = compute_flag_offsets_fingerprint(&flag_ids); + let hash_result = compute_fingerprint_from_parsed_flags(parsed_flags); assert_eq!(hash_result.unwrap(), expected_fingerprint); } #[test] + fn test_offset_fingerprint_matches_from_package() { + let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); + + // All test flags are in the same package, so fingerprint from all of them. + let result_from_parsed_flags = compute_fingerprint_from_parsed_flags(parsed_flags.clone()); + + let mut flag_names_vec = parsed_flags + .parsed_flag + .clone() + .into_iter() + .map(|flag| flag.name.unwrap()) + .map(String::from) + .collect::<Vec<_>>(); + let result_from_names = compute_flags_fingerprint(&mut flag_names_vec); + + // Assert the same hash is generated for each case. + assert_eq!(result_from_parsed_flags.unwrap(), result_from_names.unwrap()); + } + + #[test] + fn test_offset_fingerprint_different_packages_does_not_match() { + // Parse flags from two packages. + let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); + let second_parsed_flags = crate::test::parse_second_package_flags(); + + let result_from_parsed_flags = compute_fingerprint_from_parsed_flags(parsed_flags).unwrap(); + let second_result = compute_fingerprint_from_parsed_flags(second_parsed_flags).unwrap(); + + // Different flags should have a different fingerprint. + assert_ne!(result_from_parsed_flags, second_result); + } + + #[test] fn test_parse_flags() { let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap(); diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs index 73339f24b3..efce24e422 100644 --- a/tools/aconfig/aconfig/src/storage/mod.rs +++ b/tools/aconfig/aconfig/src/storage/mod.rs @@ -25,12 +25,14 @@ use crate::storage::{ flag_table::create_flag_table, flag_value::create_flag_value, package_table::create_package_table, }; -use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags}; +use aconfig_protos::ProtoParsedFlag; +use aconfig_protos::ProtoParsedFlags; use aconfig_storage_file::StorageFileType; pub struct FlagPackage<'a> { pub package_name: &'a str, pub package_id: u32, + pub fingerprint: u64, pub flag_names: HashSet<&'a str>, pub boolean_flags: Vec<&'a ProtoParsedFlag>, // The index of the first boolean flag in this aconfig package among all boolean @@ -43,6 +45,7 @@ impl<'a> FlagPackage<'a> { FlagPackage { package_name, package_id, + fingerprint: 0, flag_names: HashSet::new(), boolean_flags: vec![], boolean_start_index: 0, @@ -78,6 +81,8 @@ where for p in packages.iter_mut() { p.boolean_start_index = boolean_start_index; boolean_start_index += p.boolean_flags.len() as u32; + + // TODO: b/316357686 - Calculate fingerprint and add to package. } packages @@ -115,6 +120,8 @@ mod tests { use super::*; use crate::Input; + use aconfig_protos::ProtoParsedFlags; + pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> { let aconfig_files = [ ( diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs index c53602f9cb..33bb0774e0 100644 --- a/tools/aconfig/aconfig/src/storage/package_table.rs +++ b/tools/aconfig/aconfig/src/storage/package_table.rs @@ -48,6 +48,7 @@ impl PackageTableNodeWrapper { let node = PackageTableNode { package_name: String::from(package.package_name), package_id: package.package_id, + fingerprint: package.fingerprint, boolean_start_index: package.boolean_start_index, next_offset: None, }; diff --git a/tools/aconfig/aconfig/src/test.rs b/tools/aconfig/aconfig/src/test.rs index 7409cda6e8..a19b372aac 100644 --- a/tools/aconfig/aconfig/src/test.rs +++ b/tools/aconfig/aconfig/src/test.rs @@ -295,6 +295,24 @@ parsed_flag { aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() } + pub fn parse_second_package_flags() -> ProtoParsedFlags { + let bytes = crate::commands::parse_flags( + "com.android.aconfig.second_test", + Some("system"), + vec![Input { + source: "tests/test_second_package.aconfig".to_string(), + reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()), + }], + vec![Input { + source: "tests/third.values".to_string(), + reader: Box::new(include_bytes!("../tests/third.values").as_slice()), + }], + crate::commands::DEFAULT_FLAG_PERMISSION, + ) + .unwrap(); + aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() + } + pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> { let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template index 9970b1f300..bc01aa4bab 100644 --- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template +++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template @@ -90,10 +90,8 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ -if flag.is_read_write }} val = reader.getBooleanFlagValue({flag.flag_offset}); - if (val == {flag.method_name}) \{ - Log.i(TAG, String.format(SUCCESS_LOG, "{flag.method_name}")); - } else \{ - Log.i(TAG, String.format(MISMATCH_LOG, "{flag.method_name}", val, {flag.method_name})); + if (val != {flag.method_name}) \{ + Log.w(TAG, String.format(MISMATCH_LOG, "{flag.method_name}", val, {flag.method_name})); } if (useNewStorageValueAndDiscardOld) \{ diff --git a/tools/aconfig/aconfig/templates/cpp_exported_header.template b/tools/aconfig/aconfig/templates/cpp_exported_header.template index 0f7853e405..4643c9775c 100644 --- a/tools/aconfig/aconfig/templates/cpp_exported_header.template +++ b/tools/aconfig/aconfig/templates/cpp_exported_header.template @@ -27,12 +27,13 @@ public: {{ -for item in class_elements}} virtual bool {item.flag_name}() = 0; + {{ -endfor }} + {{ -if is_test_mode }} + {{ -for item in class_elements}} virtual void {item.flag_name}(bool val) = 0; - {{ -endif }} {{ -endfor }} - {{ -if is_test_mode }} virtual void reset_flags() \{} {{ -endif }} }; diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template index b6012e7a0d..623034a87b 100644 --- a/tools/aconfig/aconfig/templates/cpp_source_file.template +++ b/tools/aconfig/aconfig/templates/cpp_source_file.template @@ -6,7 +6,7 @@ #include "aconfig_storage/aconfig_storage_read_api.hpp" #include <android/log.h> #define LOG_TAG "aconfig_cpp_codegen" -#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) {{ -endif }} {{ endif }} @@ -76,13 +76,14 @@ namespace {cpp_namespace} \{ : boolean_start_index_() {{ -endif }} , flag_value_file_(nullptr) - , read_from_new_storage_(false) - , use_new_storage_value(false) \{ + , read_from_new_storage_(false) \{ struct stat buffer; - if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) == 0) \{ + if (stat("/metadata/aconfig/boot/enable_only_new_storage", &buffer) == 0) \{ read_from_new_storage_ = true; - } else \{ + } + + if (!read_from_new_storage_) \{ return; } @@ -90,15 +91,13 @@ namespace {cpp_namespace} \{ "{container}", aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) \{ - ALOGI("error: failed to get package map file: %s", package_map_file.error().c_str()); - return; + ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); } auto context = aconfig_storage::get_package_read_context( **package_map_file, "{package}"); if (!context.ok()) \{ - ALOGI("error: failed to get package read context: %s", context.error().c_str()); - return; + ALOGE("error: failed to get package read context: %s", context.error().c_str()); } // cache package boolean flag start index @@ -111,18 +110,13 @@ namespace {cpp_namespace} \{ "{container}", aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) \{ - ALOGI("error: failed to get flag value file: %s", flag_value_file.error().c_str()); - return; + ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); } // cache flag value file flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>( *flag_value_file); - use_new_storage_value = server_configurable_flags::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; } {{ -endif }} {{ -endif }} @@ -131,44 +125,30 @@ namespace {cpp_namespace} \{ virtual bool {item.flag_name}() override \{ {{ -if item.readwrite }} if (cache_[{item.readwrite_idx}] == -1) \{ - cache_[{item.readwrite_idx}] = server_configurable_flags::GetServerConfigurableFlag( - "aconfig_flags.{item.device_config_namespace}", - "{item.device_config_flag}", - "{item.default_value}") == "true"; - } - - {{ if allow_instrumentation- }} - if (read_from_new_storage_) \{ - if (!flag_value_file_) \{ - ALOGI("error: failed to get flag {item.flag_name}: flag value file is null"); - return cache_[{item.readwrite_idx}]; - } - - auto value = aconfig_storage::get_boolean_flag_value( - *flag_value_file_, - boolean_start_index_ + {item.flag_offset}); + if (read_from_new_storage_) \{ + auto value = aconfig_storage::get_boolean_flag_value( + *flag_value_file_, + boolean_start_index_ + {item.flag_offset}); - if (!value.ok()) \{ - ALOGI("error: failed to read flag value: %s", value.error().c_str()); - return cache_[{item.readwrite_idx}]; - } - - bool expected_value = cache_[{item.readwrite_idx}]; - if (*value != expected_value) \{ - ALOGI("error: {item.flag_name} value mismatch, new storage value is %s, old storage value is %s", - *value ? "true" : "false", expected_value ? "true" : "false"); - } + if (!value.ok()) \{ + ALOGE("error: failed to read flag value: %s", value.error().c_str()); + } - if (use_new_storage_value) \{ - return *value; + cache_[{item.readwrite_idx}] = *value; } else \{ - return expected_value; + cache_[{item.readwrite_idx}] = server_configurable_flags::GetServerConfigurableFlag( + "aconfig_flags.{item.device_config_namespace}", + "{item.device_config_flag}", + "{item.default_value}") == "true"; } - } + {{ -else- }} + cache_[{item.readwrite_idx}] = server_configurable_flags::GetServerConfigurableFlag( + "aconfig_flags.{item.device_config_namespace}", + "{item.device_config_flag}", + "{item.default_value}") == "true"; {{ -endif }} - - + } return cache_[{item.readwrite_idx}]; {{ -else }} {{ -if item.is_fixed_read_only }} @@ -189,7 +169,6 @@ namespace {cpp_namespace} \{ std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_; bool read_from_new_storage_; - bool use_new_storage_value; {{ -endif }} {{ -endif }} diff --git a/tools/aconfig/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template index ea1c60081c..c2f162fcc8 100644 --- a/tools/aconfig/aconfig/templates/rust.template +++ b/tools/aconfig/aconfig/templates/rust.template @@ -5,15 +5,15 @@ use std::io::Write; use std::sync::LazyLock; use log::\{log, LevelFilter, Level}; -static STORAGE_MIGRATION_MARKER_FILE: &str = - "/metadata/aconfig_test_missions/mission_1"; -static MIGRATION_LOG_TAG: &str = "AconfigTestMission1"; - /// flag provider pub struct FlagProvider; {{ if has_readwrite- }} {{ if allow_instrumentation }} +static READ_FROM_NEW_STORAGE: LazyLock<bool> = LazyLock::new(|| unsafe \{ + Path::new("/metadata/aconfig/boot/enable_only_new_storage").exists() +}); + static PACKAGE_OFFSET: LazyLock<Result<Option<u32>, AconfigStorageError>> = LazyLock::new(|| unsafe \{ get_mapped_storage_file("{container}", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "{package}")) @@ -30,24 +30,15 @@ static FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new /// flag value cache for {flag.name} {{ if allow_instrumentation }} static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \{ - let result = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.{flag.device_config_namespace}", - "{flag.device_config_flag}", - "{flag.default_value}") == "true"; - - let use_new_storage_value = flags_rust::GetServerConfigurableFlag( - "aconfig_flags.core_experiments_team_internal", - "com.android.providers.settings.use_new_storage_value", - "false") == "true"; - if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{ + if *READ_FROM_NEW_STORAGE \{ // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) + .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); - let aconfig_storage_result = FLAG_VAL_MAP + let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: \{err}")) .and_then(|flag_val_map| \{ @@ -65,33 +56,23 @@ static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \{ }) }); - match aconfig_storage_result \{ - Ok(storage_result) if storage_result == result => \{ - if use_new_storage_value \{ - return storage_result; - } else \{ - return result; - } - }, - Ok(storage_result) => \{ - log!(Level::Error, "AconfigTestMission1: error: mismatch for flag '{flag.name}'. Legacy storage was \{result}, new storage was \{storage_result}"); - if use_new_storage_value \{ - return storage_result; - } else \{ - return result; - } + match flag_value_result \{ + Ok(flag_value) => \{ + return flag_value; }, Err(err) => \{ - log!(Level::Error, "AconfigTestMission1: error: \{err}"); - if use_new_storage_value \{ - panic!("failed to read flag value: \{err}"); - } + log!(Level::Error, "aconfig_rust_codegen: error: \{err}"); + panic!("failed to read flag value: \{err}"); } } + } else \{ + flags_rust::GetServerConfigurableFlag( + "aconfig_flags.{flag.device_config_namespace}", + "{flag.device_config_flag}", + "{flag.default_value}") == "true" } - result - }); +}); {{ else }} static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| flags_rust::GetServerConfigurableFlag( "aconfig_flags.{flag.device_config_namespace}", @@ -123,72 +104,11 @@ pub static PROVIDER: FlagProvider = FlagProvider; {{ for flag in template_flags }} /// query flag {flag.name} #[inline(always)] -{{ -if flag.readwrite }} pub fn {flag.name}() -> bool \{ +{{ -if flag.readwrite }} PROVIDER.{flag.name}() {{ -else }} -pub fn {flag.name}() -> bool \{ - {{ if not allow_instrumentation }} {flag.default_value} - {{ else }} - - let result = {flag.default_value}; - if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{ - return result; - } - - // This will be called multiple times. Subsequent calls after the first - // are noops. - logger::init( - logger::Config::default() - .with_tag_on_device(MIGRATION_LOG_TAG) - .with_max_level(LevelFilter::Info), - ); - - unsafe \{ - let package_map = match get_mapped_storage_file("{flag.container}", StorageFileType::PackageMap) \{ - Ok(file) => file, - Err(err) => \{ - log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}"); - return result; - } - }; - - let package_read_context = match get_package_read_context(&package_map, "{package}") \{ - Ok(Some(context)) => context, - Ok(None) => \{ - log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': did not get context"); - return result; - }, - Err(err) => \{ - log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}"); - return result; - } - }; - let flag_val_map = match get_mapped_storage_file("{flag.container}", StorageFileType::FlagVal) \{ - Ok(val_map) => val_map, - Err(err) => \{ - log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}"); - return result; - } - }; - let value = match get_boolean_flag_value(&flag_val_map, {flag.flag_offset} + package_read_context.boolean_start_index) \{ - Ok(val) => val, - Err(err) => \{ - log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}"); - return result; - } - }; - - if result != value \{ - log!(Level::Error, "AconfigTestMission1: error: flag mismatch for '{flag.name}'. Legacy storage was \{result}, new storage was \{value}"); - } else \{ - let default_value = {flag.default_value}; - } - } - - result - {{ endif }} {{ -endif }} } {{ endfor }} diff --git a/tools/aconfig/aconfig/tests/test.aconfig b/tools/aconfig/aconfig/tests/test.aconfig index c11508aabc..a818b2332e 100644 --- a/tools/aconfig/aconfig/tests/test.aconfig +++ b/tools/aconfig/aconfig/tests/test.aconfig @@ -86,4 +86,4 @@ flag { bug: "111" is_fixed_read_only: true is_exported: true -}
\ No newline at end of file +} diff --git a/tools/aconfig/aconfig/tests/test_second_package.aconfig b/tools/aconfig/aconfig/tests/test_second_package.aconfig new file mode 100644 index 0000000000..a8740b85dd --- /dev/null +++ b/tools/aconfig/aconfig/tests/test_second_package.aconfig @@ -0,0 +1,12 @@ +package: "com.android.aconfig.second_test" +container: "system" + +flag { + name: "testing_flag" + namespace: "another_namespace" + description: "This is a flag for testing." + bug: "123" + metadata { + purpose: PURPOSE_UNSPECIFIED + } +} diff --git a/tools/aconfig/aconfig/tests/third.values b/tools/aconfig/aconfig/tests/third.values new file mode 100644 index 0000000000..675832a4bc --- /dev/null +++ b/tools/aconfig/aconfig/tests/third.values @@ -0,0 +1,6 @@ +flag_value { + package: "com.android.aconfig.second_test" + name: "testing_flag" + state: DISABLED + permission: READ_WRITE +} diff --git a/tools/aconfig/aconfig_device_paths/Android.bp b/tools/aconfig/aconfig_device_paths/Android.bp index 95cecf4ada..932dfbfc28 100644 --- a/tools/aconfig/aconfig_device_paths/Android.bp +++ b/tools/aconfig/aconfig_device_paths/Android.bp @@ -51,4 +51,8 @@ java_library { static_libs: [ "libaconfig_java_proto_nano", ], + sdk_version: "core_platform", + apex_available: [ + "//apex_available:platform", + ], } diff --git a/tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java b/tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java index 58c58deb47..4d4119947f 100644 --- a/tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java +++ b/tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java @@ -29,7 +29,7 @@ import java.util.List; * @hide */ public class DeviceProtos { - static final String[] PATHS = { + public static final String[] PATHS = { TEMPLATE }; @@ -50,10 +50,11 @@ public class DeviceProtos { ArrayList<parsed_flag> result = new ArrayList(); for (String path : parsedFlagsProtoPaths()) { - FileInputStream inputStream = new FileInputStream(path); - parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes()); - for (parsed_flag flag : parsedFlags.parsedFlag) { - result.add(flag); + try (FileInputStream inputStream = new FileInputStream(path)) { + parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes()); + for (parsed_flag flag : parsedFlags.parsedFlag) { + result.add(flag); + } } } @@ -64,7 +65,7 @@ public class DeviceProtos { * Returns the list of all on-device aconfig protos paths. * @hide */ - private static List<String> parsedFlagsProtoPaths() { + public static List<String> parsedFlagsProtoPaths() { ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS)); File apexDirectory = new File(APEX_DIR); diff --git a/tools/aconfig/aconfig_flags/Android.bp b/tools/aconfig/aconfig_flags/Android.bp new file mode 100644 index 0000000000..e327ced26c --- /dev/null +++ b/tools/aconfig/aconfig_flags/Android.bp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 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. + */ + +rust_library { + name: "libaconfig_flags", + crate_name: "aconfig_flags", + srcs: [ + "src/lib.rs", + ], + rustlibs: [ + "libaconfig_flags_rust", + ], + host_supported: true, +} + +aconfig_declarations { + name: "aconfig_flags", + package: "com.android.aconfig.flags", + container: "system", + srcs: ["flags.aconfig"], +} + +rust_aconfig_library { + name: "libaconfig_flags_rust", + crate_name: "aconfig_flags_rust", + aconfig_declarations: "aconfig_flags", + host_supported: true, +} + +cc_aconfig_library { + name: "libaconfig_flags_cc", + aconfig_declarations: "aconfig_flags", +} diff --git a/tools/aconfig/aconfig_flags/Cargo.toml b/tools/aconfig/aconfig_flags/Cargo.toml new file mode 100644 index 0000000000..6eb9f14058 --- /dev/null +++ b/tools/aconfig/aconfig_flags/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "aconfig_flags" +version = "0.1.0" +edition = "2021" + +[features] +default = ["cargo"] +cargo = [] + +[dependencies]
\ No newline at end of file diff --git a/tools/aconfig/aconfig_flags/flags.aconfig b/tools/aconfig/aconfig_flags/flags.aconfig new file mode 100644 index 0000000000..db8b1b7904 --- /dev/null +++ b/tools/aconfig/aconfig_flags/flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.aconfig.flags" +container: "system" + +flag { + name: "enable_only_new_storage" + namespace: "core_experiments_team_internal" + bug: "312235596" + description: "When enabled, aconfig flags are read from the new aconfig storage only." +} diff --git a/tools/aconfig/aconfig_flags/src/lib.rs b/tools/aconfig/aconfig_flags/src/lib.rs new file mode 100644 index 0000000000..a607efb7d4 --- /dev/null +++ b/tools/aconfig/aconfig_flags/src/lib.rs @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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. + */ + +//! `aconfig_flags` is a crate for reading aconfig flags from Rust +// When building with the Android tool-chain +// +// - the flag functions will read from aconfig_flags_inner +// - the feature "cargo" will be disabled +// +// When building with cargo +// +// - the flag functions will all return some trivial value, like true +// - the feature "cargo" will be enabled +// +// This module hides these differences from the rest of aconfig. + +/// Module used when building with the Android tool-chain +#[cfg(not(feature = "cargo"))] +pub mod auto_generated { + /// Returns the value for the enable_only_new_storage flag. + pub fn enable_only_new_storage() -> bool { + aconfig_flags_rust::enable_only_new_storage() + } +} + +/// Module used when building with cargo +#[cfg(feature = "cargo")] +pub mod auto_generated { + /// Returns a placeholder value for the enable_only_new_storage flag. + pub fn enable_only_new_storage() -> bool { + // Used only to enable typechecking and testing with cargo + true + } +} diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp index 40b4464167..e875c7be6a 100644 --- a/tools/aconfig/aconfig_storage_file/Android.bp +++ b/tools/aconfig/aconfig_storage_file/Android.bp @@ -14,6 +14,7 @@ rust_defaults { "libclap", "libcxx", "libaconfig_storage_protos", + "libserde", ], } @@ -36,7 +37,10 @@ rust_binary_host { name: "aconfig-storage", defaults: ["aconfig_storage_file.defaults"], srcs: ["src/main.rs"], - rustlibs: ["libaconfig_storage_file"], + rustlibs: [ + "libaconfig_storage_file", + "libserde_json", + ], } rust_test_host { diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml index 192dfad40a..a40557803f 100644 --- a/tools/aconfig/aconfig_storage_file/Cargo.toml +++ b/tools/aconfig/aconfig_storage_file/Cargo.toml @@ -14,6 +14,8 @@ tempfile = "3.9.0" thiserror = "1.0.56" clap = { version = "4.1.8", features = ["derive"] } cxx = "1.0" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" [[bin]] name = "aconfig-storage" diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs index beac38d156..a49756d012 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs @@ -20,10 +20,11 @@ use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; +use serde::{Deserialize, Serialize}; use std::fmt; /// Flag info header struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagInfoHeader { pub version: u32, pub container: String, @@ -89,7 +90,7 @@ impl FlagInfoHeader { } /// bit field for flag info -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum FlagInfoBit { HasServerOverride = 1 << 0, IsReadWrite = 1 << 1, @@ -97,7 +98,7 @@ pub enum FlagInfoBit { } /// Flag info node struct -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Serialize, Deserialize)] pub struct FlagInfoNode { pub attributes: u8, } @@ -138,7 +139,7 @@ impl FlagInfoNode { } /// Flag info list struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagInfoList { pub header: FlagInfoHeader, pub nodes: Vec<FlagInfoNode>, @@ -226,7 +227,7 @@ mod tests { let bytes = &flag_info_list.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + assert_eq!(version, 2); } #[test] diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs index 660edac0c7..be82c63028 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs @@ -23,10 +23,11 @@ use crate::{ }; use crate::{AconfigStorageError, StorageFileType, StoredFlagType}; use anyhow::anyhow; +use serde::{Deserialize, Serialize}; use std::fmt; /// Flag table header struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagTableHeader { pub version: u32, pub container: String, @@ -95,7 +96,7 @@ impl FlagTableHeader { } /// Flag table node struct -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Serialize, Deserialize)] pub struct FlagTableNode { pub package_id: u32, pub flag_name: String, @@ -154,7 +155,7 @@ impl FlagTableNode { } } -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagTable { pub header: FlagTableHeader, pub buckets: Vec<Option<u32>>, @@ -252,7 +253,7 @@ mod tests { let bytes = &flag_table.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + assert_eq!(version, 2); } #[test] diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs index 506924b339..c4cf29451e 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs @@ -20,10 +20,11 @@ use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; +use serde::{Deserialize, Serialize}; use std::fmt; /// Flag value header struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagValueHeader { pub version: u32, pub container: String, @@ -89,7 +90,7 @@ impl FlagValueHeader { } /// Flag value list struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct FlagValueList { pub header: FlagValueHeader, pub booleans: Vec<bool>, @@ -158,7 +159,7 @@ mod tests { let bytes = &flag_value_list.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + assert_eq!(version, 2); } #[test] diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs index b6367ffa35..19d0e51e61 100644 --- a/tools/aconfig/aconfig_storage_file/src/lib.rs +++ b/tools/aconfig/aconfig_storage_file/src/lib.rs @@ -41,6 +41,7 @@ pub mod sip_hasher13; pub mod test_utils; use anyhow::anyhow; +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fs::File; use std::hash::Hasher; @@ -57,7 +58,7 @@ use crate::AconfigStorageError::{ }; /// Storage file version -pub const FILE_VERSION: u32 = 1; +pub const FILE_VERSION: u32 = 2; /// Good hash table prime number pub(crate) const HASH_PRIMES: [u32; 29] = [ @@ -107,7 +108,7 @@ impl TryFrom<u8> for StorageFileType { /// Flag type enum as stored by storage file /// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum StoredFlagType { ReadWriteBoolean = 0, ReadOnlyBoolean = 1, @@ -253,6 +254,16 @@ pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigS Ok(val) } +// Read and parse bytes as u64 +pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> { + let val = + u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| { + BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg)) + })?); + *head += 8; + Ok(val) +} + /// Read and parse bytes as string pub(crate) fn read_str_from_bytes( buf: &[u8], diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs index 8b9e38da02..a9cfd19066 100644 --- a/tools/aconfig/aconfig_storage_file/src/main.rs +++ b/tools/aconfig/aconfig_storage_file/src/main.rs @@ -20,9 +20,29 @@ use aconfig_storage_file::{ list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList, PackageTable, StorageFileType, }; - use clap::{builder::ArgAction, Arg, Command}; +use serde::Serialize; +use serde_json; +use std::fmt; +use std::fs; +use std::fs::File; +use std::io::Write; +/** + * Usage Examples + * + * Print file: + * $ aconfig-storage print --file=path/to/flag.map --type=flag_map + * + * List flags: + * $ aconfig-storage list --flag-map=path/to/flag.map \ + * --flag-val=path/to/flag.val --package-map=path/to/package.map + * + * Write binary file for testing: + * $ aconfig-storage print --file=path/to/flag.map --type=flag_map --format=json > flag_map.json + * $ vim flag_map.json // Manually make updates + * $ aconfig-storage write-bytes --input-file=flag_map.json --output-file=path/to/flag.map --type=flag_map + */ fn cli() -> Command { Command::new("aconfig-storage") .subcommand_required(true) @@ -34,7 +54,8 @@ fn cli() -> Command { .long("type") .required(true) .value_parser(|s: &str| StorageFileType::try_from(s)), - ), + ) + .arg(Arg::new("format").long("format").required(false).action(ArgAction::Set)), ) .subcommand( Command::new("list") @@ -50,41 +71,75 @@ fn cli() -> Command { Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set), ), ) + .subcommand( + Command::new("write-bytes") + // Where to write the output bytes. Suggest to use the StorageFileType names (e.g. flag.map). + .arg( + Arg::new("output-file") + .long("output-file") + .required(true) + .action(ArgAction::Set), + ) + // Input file should be json. + .arg( + Arg::new("input-file").long("input-file").required(true).action(ArgAction::Set), + ) + .arg( + Arg::new("type") + .long("type") + .required(true) + .value_parser(|s: &str| StorageFileType::try_from(s)), + ), + ) } fn print_storage_file( file_path: &str, file_type: &StorageFileType, + as_json: bool, ) -> Result<(), AconfigStorageError> { let bytes = read_file_to_bytes(file_path)?; match file_type { StorageFileType::PackageMap => { let package_table = PackageTable::from_bytes(&bytes)?; - println!("{:?}", package_table); + println!("{}", to_print_format(package_table, as_json)); } StorageFileType::FlagMap => { let flag_table = FlagTable::from_bytes(&bytes)?; - println!("{:?}", flag_table); + println!("{}", to_print_format(flag_table, as_json)); } StorageFileType::FlagVal => { let flag_value = FlagValueList::from_bytes(&bytes)?; - println!("{:?}", flag_value); + println!("{}", to_print_format(flag_value, as_json)); } StorageFileType::FlagInfo => { let flag_info = FlagInfoList::from_bytes(&bytes)?; - println!("{:?}", flag_info); + println!("{}", to_print_format(flag_info, as_json)); } } Ok(()) } +fn to_print_format<T>(file_contents: T, as_json: bool) -> String +where + T: Serialize + fmt::Debug, +{ + if as_json { + serde_json::to_string(&file_contents).unwrap() + } else { + format!("{:?}", file_contents) + } +} + fn main() -> Result<(), AconfigStorageError> { let matches = cli().get_matches(); match matches.subcommand() { Some(("print", sub_matches)) => { let file_path = sub_matches.get_one::<String>("file").unwrap(); let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap(); - print_storage_file(file_path, file_type)? + let format = sub_matches.get_one::<String>("format"); + let as_json: bool = format == Some(&"json".to_string()); + print_storage_file(file_path, file_type, as_json)? } Some(("list", sub_matches)) => { let package_map = sub_matches.get_one::<String>("package-map").unwrap(); @@ -96,10 +151,10 @@ fn main() -> Result<(), AconfigStorageError> { let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?; for flag in flags.iter() { println!( - "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}", - flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, - flag.is_readwrite, flag.has_server_override, flag.has_local_override, - ); + "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}", + flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, + flag.is_readwrite, flag.has_server_override, flag.has_local_override, + ); } } None => { @@ -113,6 +168,40 @@ fn main() -> Result<(), AconfigStorageError> { } } } + // Converts JSON of the file into raw bytes (as is used on-device). + // Intended to generate/easily update these files for testing. + Some(("write-bytes", sub_matches)) => { + let input_file_path = sub_matches.get_one::<String>("input-file").unwrap(); + let input_json = fs::read_to_string(input_file_path).unwrap(); + + let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap(); + let output_bytes: Vec<u8>; + match file_type { + StorageFileType::FlagVal => { + let list: FlagValueList = serde_json::from_str(&input_json).unwrap(); + output_bytes = list.into_bytes(); + } + StorageFileType::FlagInfo => { + let list: FlagInfoList = serde_json::from_str(&input_json).unwrap(); + output_bytes = list.into_bytes(); + } + StorageFileType::FlagMap => { + let table: FlagTable = serde_json::from_str(&input_json).unwrap(); + output_bytes = table.into_bytes(); + } + StorageFileType::PackageMap => { + let table: PackageTable = serde_json::from_str(&input_json).unwrap(); + output_bytes = table.into_bytes(); + } + } + + let output_file_path = sub_matches.get_one::<String>("output-file").unwrap(); + let file = File::create(output_file_path); + if file.is_err() { + panic!("can't make file"); + } + let _ = file.unwrap().write_all(&output_bytes); + } _ => unreachable!(), } Ok(()) diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs index 007f86ed1a..350f072df2 100644 --- a/tools/aconfig/aconfig_storage_file/src/package_table.rs +++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs @@ -17,13 +17,17 @@ //! package table module defines the package table file format and methods for serialization //! and deserialization -use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; +use crate::{ + get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u64_from_bytes, + read_u8_from_bytes, +}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; +use serde::{Deserialize, Serialize}; use std::fmt; /// Package table header struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTableHeader { pub version: u32, pub container: String, @@ -92,10 +96,11 @@ impl PackageTableHeader { } /// Package table node struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTableNode { pub package_name: String, pub package_id: u32, + pub fingerprint: u64, // The index of the first boolean flag in this aconfig package among all boolean // flags in this container. pub boolean_start_index: u32, @@ -107,8 +112,12 @@ impl fmt::Debug for PackageTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, - "Package: {}, Id: {}, Boolean flag start index: {}, Next: {:?}", - self.package_name, self.package_id, self.boolean_start_index, self.next_offset + "Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}", + self.package_name, + self.package_id, + self.fingerprint, + self.boolean_start_index, + self.next_offset )?; Ok(()) } @@ -122,6 +131,7 @@ impl PackageTableNode { result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(name_bytes); result.extend_from_slice(&self.package_id.to_le_bytes()); + result.extend_from_slice(&self.fingerprint.to_le_bytes()); result.extend_from_slice(&self.boolean_start_index.to_le_bytes()); result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes()); result @@ -133,6 +143,7 @@ impl PackageTableNode { let node = Self { package_name: read_str_from_bytes(bytes, &mut head)?, package_id: read_u32_from_bytes(bytes, &mut head)?, + fingerprint: read_u64_from_bytes(bytes, &mut head)?, boolean_start_index: read_u32_from_bytes(bytes, &mut head)?, next_offset: match read_u32_from_bytes(bytes, &mut head)? { 0 => None, @@ -151,7 +162,7 @@ impl PackageTableNode { } /// Package table struct -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTable { pub header: PackageTableHeader, pub buckets: Vec<Option<u32>>, @@ -250,7 +261,7 @@ mod tests { let bytes = &package_table.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + assert_eq!(version, 2); } #[test] diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs index 106666c47f..11e2dc688e 100644 --- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs +++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs @@ -26,30 +26,33 @@ use tempfile::NamedTempFile; pub fn create_test_package_table() -> PackageTable { let header = PackageTableHeader { - version: 1, + version: 2, container: String::from("mockup"), file_type: StorageFileType::PackageMap as u8, - file_size: 209, + file_size: 233, num_packages: 3, bucket_offset: 31, node_offset: 59, }; - let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None]; + let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(117), None, None, None]; let first_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_2"), package_id: 1, + fingerprint: 0, boolean_start_index: 3, next_offset: None, }; let second_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_1"), package_id: 0, + fingerprint: 0, boolean_start_index: 0, - next_offset: Some(159), + next_offset: Some(175), }; let third_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_4"), package_id: 2, + fingerprint: 0, boolean_start_index: 6, next_offset: None, }; @@ -78,7 +81,7 @@ impl FlagTableNode { pub fn create_test_flag_table() -> FlagTable { let header = FlagTableHeader { - version: 1, + version: 2, container: String::from("mockup"), file_type: StorageFileType::FlagMap as u8, file_size: 321, @@ -120,7 +123,7 @@ pub fn create_test_flag_table() -> FlagTable { pub fn create_test_flag_value_list() -> FlagValueList { let header = FlagValueHeader { - version: 1, + version: 2, container: String::from("mockup"), file_type: StorageFileType::FlagVal as u8, file_size: 35, @@ -133,7 +136,7 @@ pub fn create_test_flag_value_list() -> FlagValueList { pub fn create_test_flag_info_list() -> FlagInfoList { let header = FlagInfoHeader { - version: 1, + version: 2, container: String::from("mockup"), file_type: StorageFileType::FlagInfo as u8, file_size: 35, diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java index 4bea0836f0..44a82ee1c7 100644 --- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java +++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java @@ -37,6 +37,10 @@ public class ByteBufferReader { return Short.toUnsignedInt(mByteBuffer.getShort()); } + public long readLong() { + return mByteBuffer.getLong(); + } + public int readInt() { return this.mByteBuffer.getInt(); } diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java index 773b882f4a..f1288f5ea6 100644 --- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java +++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java @@ -118,6 +118,7 @@ public class PackageTable { private String mPackageName; private int mPackageId; + private long mFingerprint; private int mBooleanStartIndex; private int mNextOffset; @@ -125,6 +126,7 @@ public class PackageTable { Node node = new Node(); node.mPackageName = reader.readString(); node.mPackageId = reader.readInt(); + node.mFingerprint = reader.readLong(); node.mBooleanStartIndex = reader.readInt(); node.mNextOffset = reader.readInt(); node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset; @@ -150,6 +152,7 @@ public class PackageTable { return Objects.equals(mPackageName, other.mPackageName) && mPackageId == other.mPackageId && mBooleanStartIndex == other.mBooleanStartIndex + && mFingerprint == other.mFingerprint && mNextOffset == other.mNextOffset; } @@ -165,6 +168,10 @@ public class PackageTable { return mBooleanStartIndex; } + public long getFingerprint() { + return mFingerprint; + } + public int getNextOffset() { return mNextOffset; } diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.info b/tools/aconfig/aconfig_storage_file/tests/flag.info Binary files differindex 6223edf369..9db7fde7ae 100644 --- a/tools/aconfig/aconfig_storage_file/tests/flag.info +++ b/tools/aconfig/aconfig_storage_file/tests/flag.info diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map Binary files differindex e868f53d7e..cf4685ceb4 100644 --- a/tools/aconfig/aconfig_storage_file/tests/flag.map +++ b/tools/aconfig/aconfig_storage_file/tests/flag.map diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val Binary files differindex ed203d4d13..37d4750206 100644 --- a/tools/aconfig/aconfig_storage_file/tests/flag.val +++ b/tools/aconfig/aconfig_storage_file/tests/flag.val diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map Binary files differindex 6c46a0339c..358010cf37 100644 --- a/tools/aconfig/aconfig_storage_file/tests/package.map +++ b/tools/aconfig/aconfig_storage_file/tests/package.map diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java index fd40d4c4ef..e3b02cd666 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java @@ -33,7 +33,7 @@ public class FlagTableTest { public void testFlagTable_rightHeader() throws Exception { FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer()); FlagTable.Header header = flagTable.getHeader(); - assertEquals(1, header.getVersion()); + assertEquals(2, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.FLAG_MAP, header.getFileType()); assertEquals(321, header.getFileSize()); diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java index 1b0de630c7..ebc231c2dd 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java @@ -36,7 +36,7 @@ public class FlagValueListTest { FlagValueList flagValueList = FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer()); FlagValueList.Header header = flagValueList.getHeader(); - assertEquals(1, header.getVersion()); + assertEquals(2, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.FLAG_VAL, header.getFileType()); assertEquals(35, header.getFileSize()); diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java index e7e19d8d51..6d56ceed96 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import android.aconfig.storage.FileType; import android.aconfig.storage.PackageTable; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -28,42 +27,40 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class PackageTableTest { - @Test - public void testPackageTable_rightHeader() throws Exception { - PackageTable packageTable = - PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); - PackageTable.Header header = packageTable.getHeader(); - assertEquals(1, header.getVersion()); - assertEquals("mockup", header.getContainer()); - assertEquals(FileType.PACKAGE_MAP, header.getFileType()); - assertEquals(209, header.getFileSize()); - assertEquals(3, header.getNumPackages()); - assertEquals(31, header.getBucketOffset()); - assertEquals(59, header.getNodeOffset()); - } + @Test + public void testPackageTable_rightHeader() throws Exception { + PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); + PackageTable.Header header = packageTable.getHeader(); + assertEquals(2, header.getVersion()); + assertEquals("mockup", header.getContainer()); + assertEquals(FileType.PACKAGE_MAP, header.getFileType()); + assertEquals(209, header.getFileSize()); + assertEquals(3, header.getNumPackages()); + assertEquals(31, header.getBucketOffset()); + assertEquals(59, header.getNodeOffset()); + } - @Test - public void testPackageTable_rightNode() throws Exception { - PackageTable packageTable = - PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); + @Test + public void testPackageTable_rightNode() throws Exception { + PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); - PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1"); - PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2"); - PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4"); + PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1"); + PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2"); + PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4"); - assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName()); - assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName()); - assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName()); + assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName()); + assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName()); + assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName()); - assertEquals(0, node1.getPackageId()); - assertEquals(1, node2.getPackageId()); - assertEquals(2, node4.getPackageId()); + assertEquals(0, node1.getPackageId()); + assertEquals(1, node2.getPackageId()); + assertEquals(2, node4.getPackageId()); - assertEquals(0, node1.getBooleanStartIndex()); - assertEquals(3, node2.getBooleanStartIndex()); - assertEquals(6, node4.getBooleanStartIndex()); + assertEquals(0, node1.getBooleanStartIndex()); + assertEquals(3, node2.getBooleanStartIndex()); + assertEquals(6, node4.getBooleanStartIndex()); - assertEquals(159, node1.getNextOffset()); + assertEquals(175, node1.getNextOffset()); assertEquals(-1, node2.getNextOffset()); assertEquals(-1, node4.getNextOffset()); } diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs index d76cf3fe4e..59aa749c74 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs @@ -507,9 +507,9 @@ mod tests { #[test] // this test point locks down flag storage file version number query api fn test_storage_version_query() { - assert_eq!(get_storage_file_version("./tests/package.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/flag.val").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/flag.info").unwrap(), 1); + assert_eq!(get_storage_file_version("./tests/package.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./tests/flag.val").unwrap(), 2); + assert_eq!(get_storage_file_version("./tests/flag.info").unwrap(), 2); } } diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info Binary files differindex 6223edf369..9db7fde7ae 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/flag.info +++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map Binary files differindex e868f53d7e..cf4685ceb4 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/flag.map +++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.val b/tools/aconfig/aconfig_storage_read_api/tests/flag.val Binary files differindex ed203d4d13..37d4750206 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/flag.val +++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.val diff --git a/tools/aconfig/aconfig_storage_read_api/tests/package.map b/tools/aconfig/aconfig_storage_read_api/tests/package.map Binary files differindex 6c46a0339c..358010cf37 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/package.map +++ b/tools/aconfig/aconfig_storage_read_api/tests/package.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp index 6d29045efe..58460d1ac7 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp @@ -80,16 +80,16 @@ class AconfigStorageTest : public ::testing::Test { TEST_F(AconfigStorageTest, test_storage_version_query) { auto version = api::get_storage_file_version(package_map); ASSERT_TRUE(version.ok()); - ASSERT_EQ(*version, 1); + ASSERT_EQ(*version, 2); version = api::get_storage_file_version(flag_map); ASSERT_TRUE(version.ok()); - ASSERT_EQ(*version, 1); + ASSERT_EQ(*version, 2); version = api::get_storage_file_version(flag_val); ASSERT_TRUE(version.ok()); - ASSERT_EQ(*version, 1); + ASSERT_EQ(*version, 2); version = api::get_storage_file_version(flag_info); ASSERT_TRUE(version.ok()); - ASSERT_EQ(*version, 1); + ASSERT_EQ(*version, 2); } /// Negative test to lock down the error when mapping none exist storage files diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs index afc44d4d70..bd1b5843f1 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs @@ -200,9 +200,9 @@ mod aconfig_storage_rust_test { #[test] fn test_storage_version_query() { - assert_eq!(get_storage_file_version("./package.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./flag.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./flag.val").unwrap(), 1); - assert_eq!(get_storage_file_version("./flag.info").unwrap(), 1); + assert_eq!(get_storage_file_version("./package.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./flag.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./flag.val").unwrap(), 2); + assert_eq!(get_storage_file_version("./flag.info").unwrap(), 2); } } diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp index c48585aed9..2040cc635b 100644 --- a/tools/aconfig/aflags/Android.bp +++ b/tools/aconfig/aflags/Android.bp @@ -10,6 +10,7 @@ rust_defaults { srcs: ["src/main.rs"], rustlibs: [ "libaconfig_device_paths", + "libaconfig_flags", "libaconfig_protos", "libaconfigd_protos", "libaconfig_storage_read_api", @@ -24,6 +25,7 @@ rust_defaults { rust_binary { name: "aflags", + host_supported: true, defaults: ["aflags.defaults"], } diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml index 7dc343668d..7efce6dc96 100644 --- a/tools/aconfig/aflags/Cargo.toml +++ b/tools/aconfig/aflags/Cargo.toml @@ -15,3 +15,4 @@ aconfig_storage_file = { version = "0.1.0", path = "../aconfig_storage_file" } aconfig_storage_read_api = { version = "0.1.0", path = "../aconfig_storage_read_api" } clap = {version = "4.5.2" } aconfig_device_paths = { version = "0.1.0", path = "../aconfig_device_paths" } +aconfig_flags = { version = "0.1.0", path = "../aconfig_flags" }
\ No newline at end of file diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs index b2fd3c92ac..68edf7d3ac 100644 --- a/tools/aconfig/aflags/src/aconfig_storage_source.rs +++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs @@ -1,3 +1,4 @@ +use crate::load_protos; use crate::{Flag, FlagSource}; use crate::{FlagPermission, FlagValue, ValuePickedFrom}; use aconfigd_protos::{ @@ -9,13 +10,18 @@ use anyhow::anyhow; use anyhow::Result; use protobuf::Message; use protobuf::SpecialFields; +use std::collections::HashMap; use std::io::{Read, Write}; use std::net::Shutdown; use std::os::unix::net::UnixStream; pub struct AconfigStorageSource {} -fn convert(msg: ProtoFlagQueryReturnMessage) -> Result<Flag> { +fn load_flag_to_container() -> Result<HashMap<String, String>> { + Ok(load_protos::load()?.into_iter().map(|p| (p.qualified_name(), p.container)).collect()) +} + +fn convert(msg: ProtoFlagQueryReturnMessage, containers: &HashMap<String, String>) -> Result<Flag> { let (value, value_picked_from) = match ( &msg.boot_flag_value, msg.default_flag_value, @@ -55,15 +61,21 @@ fn convert(msg: ProtoFlagQueryReturnMessage) -> Result<Flag> { None => return Err(anyhow!("missing permission")), }; + let name = msg.flag_name.ok_or(anyhow!("missing flag name"))?; + let package = msg.package_name.ok_or(anyhow!("missing package name"))?; + let qualified_name = format!("{package}.{name}"); Ok(Flag { - name: msg.flag_name.ok_or(anyhow!("missing flag name"))?, - package: msg.package_name.ok_or(anyhow!("missing package name"))?, + name, + package, value, permission, value_picked_from, staged_value, - container: "-".to_string(), - + container: containers + .get(&qualified_name) + .cloned() + .unwrap_or_else(|| "<no container>".to_string()) + .to_string(), // TODO: remove once DeviceConfig is not in the CLI. namespace: "-".to_string(), }) @@ -114,9 +126,13 @@ fn read_from_socket() -> Result<Vec<ProtoFlagQueryReturnMessage>> { impl FlagSource for AconfigStorageSource { fn list_flags() -> Result<Vec<Flag>> { + let containers = load_flag_to_container()?; read_from_socket() .map(|query_messages| { - query_messages.iter().map(|message| convert(message.clone())).collect::<Vec<_>>() + query_messages + .iter() + .map(|message| convert(message.clone(), &containers)) + .collect::<Vec<_>>() })? .into_iter() .collect() diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs index d8912a946e..07b7243ab4 100644 --- a/tools/aconfig/aflags/src/main.rs +++ b/tools/aconfig/aflags/src/main.rs @@ -116,9 +116,10 @@ impl Flag { } fn display_staged_value(&self) -> String { - match self.staged_value { - Some(v) => format!("(->{})", v), - None => "-".to_string(), + match (&self.permission, self.staged_value) { + (FlagPermission::ReadOnly, _) => "-".to_string(), + (FlagPermission::ReadWrite, None) => "-".to_string(), + (FlagPermission::ReadWrite, Some(v)) => format!("(->{})", v), } } } @@ -164,10 +165,6 @@ struct Cli { enum Command { /// List all aconfig flags on this device. List { - /// Read from the new flag storage. - #[clap(long)] - use_new_storage: bool, - /// Optionally filter by container name. #[clap(short = 'c', long = "container")] container: Option<String>, @@ -184,6 +181,9 @@ enum Command { /// <package>.<flag_name> qualified_name: String, }, + + /// Display which flag storage backs aconfig flags. + WhichBacking, } struct PaddingInfo { @@ -282,21 +282,31 @@ fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String Ok(result) } +fn display_which_backing() -> String { + if aconfig_flags::auto_generated::enable_only_new_storage() { + "aconfig_storage".to_string() + } else { + "device_config".to_string() + } +} + fn main() -> Result<()> { ensure!(nix::unistd::Uid::current().is_root(), "must be root"); let cli = Cli::parse(); let output = match cli.command { - Command::List { use_new_storage: true, container } => { - list(FlagSourceType::AconfigStorage, container) - .map_err(|err| anyhow!("storage may not be enabled: {err}")) - .map(Some) - } - Command::List { use_new_storage: false, container } => { - list(FlagSourceType::DeviceConfig, container).map(Some) + Command::List { container } => { + if aconfig_flags::auto_generated::enable_only_new_storage() { + list(FlagSourceType::AconfigStorage, container) + .map_err(|err| anyhow!("storage may not be enabled: {err}")) + .map(Some) + } else { + list(FlagSourceType::DeviceConfig, container).map(Some) + } } Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None), Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None), + Command::WhichBacking => Ok(Some(display_which_backing())), }; match output { Ok(Some(text)) => println!("{text}"), diff --git a/tools/aconfig/fake_device_config/src/android/util/Log.java b/tools/aconfig/fake_device_config/src/android/util/Log.java index 3e7fd0f386..79de68060e 100644 --- a/tools/aconfig/fake_device_config/src/android/util/Log.java +++ b/tools/aconfig/fake_device_config/src/android/util/Log.java @@ -5,6 +5,10 @@ public final class Log { return 0; } + public static int w(String tag, String msg) { + return 0; + } + public static int e(String tag, String msg) { return 0; } diff --git a/tools/edit_monitor/Android.bp b/tools/edit_monitor/Android.bp new file mode 100644 index 0000000000..80437c00d4 --- /dev/null +++ b/tools/edit_monitor/Android.bp @@ -0,0 +1,21 @@ +// Copyright 2024 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. + +// Set of error prone rules to ensure code quality +// PackageLocation check requires the androidCompatible=false otherwise it does not do anything. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_adte", +} diff --git a/tools/edit_monitor/OWNERS b/tools/edit_monitor/OWNERS new file mode 100644 index 0000000000..8f0f3646dd --- /dev/null +++ b/tools/edit_monitor/OWNERS @@ -0,0 +1 @@ +include platform/tools/asuite:/OWNERS_ADTE_TEAM
\ No newline at end of file diff --git a/tools/filelistdiff/allowlist b/tools/filelistdiff/allowlist index c4a464dd16..120045e3b2 100644 --- a/tools/filelistdiff/allowlist +++ b/tools/filelistdiff/allowlist @@ -43,7 +43,9 @@ etc/aconfig/flag.val etc/aconfig/package.map etc/bpf/uprobestats/BitmapAllocation.o etc/bpf/uprobestats/GenericInstrumentation.o +etc/bpf/uprobestats/ProcessManagement.o etc/init/UprobeStats.rc lib/libuprobestats_client.so lib64/libuprobestats_client.so -priv-app/DeviceDiagnostics/DeviceDiagnostics.apk
\ No newline at end of file +priv-app/DeviceDiagnostics/DeviceDiagnostics.apk + diff --git a/tools/releasetools/ota_from_raw_img.py b/tools/releasetools/ota_from_raw_img.py index 03b44f15d6..3b9374ab13 100644 --- a/tools/releasetools/ota_from_raw_img.py +++ b/tools/releasetools/ota_from_raw_img.py @@ -105,9 +105,6 @@ def main(argv): if args.package_key: logger.info("Signing payload...") - # TODO: remove OPTIONS when no longer used as fallback in payload_signer - common.OPTIONS.payload_signer_args = None - common.OPTIONS.payload_signer_maximum_signature_size = None signer = PayloadSigner(args.package_key, args.private_key_suffix, key_passwords[args.package_key], payload_signer=args.payload_signer, diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index 985cd56cb0..6446e1ff59 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -264,6 +264,10 @@ A/B OTA specific options --compression_factor Specify the maximum block size to be compressed at once during OTA. supported options: 4k, 8k, 16k, 32k, 64k, 128k, 256k + + --full_ota_partitions + Specify list of partitions should be updated in full OTA fashion, even if + an incremental OTA is about to be generated """ from __future__ import print_function @@ -283,7 +287,7 @@ import common import ota_utils import payload_signer from ota_utils import (VABC_COMPRESSION_PARAM_SUPPORT, FinalizeMetadata, GetPackageMetadata, - PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir) + PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir, TARGET_FILES_IMAGES_SUBDIR) from common import DoesInputFileContain, IsSparseImage import target_files_diff from non_ab_ota import GenerateNonAbOtaPackage @@ -337,6 +341,7 @@ OPTIONS.security_patch_level = None OPTIONS.max_threads = None OPTIONS.vabc_cow_version = None OPTIONS.compression_factor = None +OPTIONS.full_ota_partitions = None POSTINSTALL_CONFIG = 'META/postinstall_config.txt' @@ -892,6 +897,14 @@ def GenerateAbOtaPackage(target_file, output_file, source_file=None): if source_file is not None: source_file = ExtractTargetFiles(source_file) + if OPTIONS.full_ota_partitions: + for partition in OPTIONS.full_ota_partitions: + for subdir in TARGET_FILES_IMAGES_SUBDIR: + image_path = os.path.join(source_file, subdir, partition + ".img") + if os.path.exists(image_path): + logger.info( + "Ignoring source image %s for partition %s because it is configured to use full OTA", image_path, partition) + os.remove(image_path) assert "ab_partitions" in OPTIONS.source_info_dict, \ "META/ab_partitions.txt is required for ab_update." assert "ab_partitions" in OPTIONS.target_info_dict, \ @@ -1193,7 +1206,7 @@ def GenerateAbOtaPackage(target_file, output_file, source_file=None): def main(argv): - def option_handler(o, a): + def option_handler(o, a: str): if o in ("-i", "--incremental_from"): OPTIONS.incremental_source = a elif o == "--full_radio": @@ -1320,6 +1333,9 @@ def main(argv): else: raise ValueError("Cannot parse value %r for option %r - only " "integers are allowed." % (a, o)) + elif o == "--full_ota_partitions": + OPTIONS.full_ota_partitions = set( + a.strip().strip("\"").strip("'").split(",")) else: return False return True @@ -1370,6 +1386,7 @@ def main(argv): "max_threads=", "vabc_cow_version=", "compression_factor=", + "full_ota_partitions=", ], extra_option_handler=[option_handler, payload_signer.signer_options]) common.InitLogging() diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py index a72342f2d7..4ad97e0108 100755 --- a/tools/releasetools/sign_target_files_apks.py +++ b/tools/releasetools/sign_target_files_apks.py @@ -184,6 +184,7 @@ import re import shutil import stat import sys +import shlex import tempfile import zipfile from xml.etree import ElementTree @@ -582,6 +583,24 @@ def IsBuildPropFile(filename): filename.endswith("/prop.default") +def GetOtaSigningArgs(): + args = [] + if OPTIONS.package_key: + args.extend(["--package_key", OPTIONS.package_key]) + if OPTIONS.payload_signer: + args.extend(["--payload_signer=" + OPTIONS.payload_signer]) + if OPTIONS.payload_signer_args: + args.extend(["--payload_signer_args=" + shlex.join(OPTIONS.payload_signer_args)]) + if OPTIONS.search_path: + args.extend(["--search_path", OPTIONS.search_path]) + if OPTIONS.payload_signer_maximum_signature_size: + args.extend(["--payload_signer_maximum_signature_size", + OPTIONS.payload_signer_maximum_signature_size]) + if OPTIONS.private_key_suffix: + args.extend(["--private_key_suffix", OPTIONS.private_key_suffix]) + return args + + def RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info): """Re-generate boot and dtbo partitions using new signing configuration""" files_to_unzip = [ @@ -648,9 +667,9 @@ def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota): if os.path.exists(signed_16k_dtbo_image): signed_dtbo_image += ":" + signed_16k_dtbo_image - - args = ["ota_from_raw_img", "--package_key", OPTIONS.package_key, + args = ["ota_from_raw_img", "--max_timestamp", timestamp, "--output", input_ota.name] + args.extend(GetOtaSigningArgs()) if "dtbo" in partitions: args.extend(["--partition_name", "boot,dtbo", signed_boot_image, signed_dtbo_image]) |