Merge "Create empty /linkerconfig/ld.config.txt in ramdisk." into main
diff --git a/ci/build_test_suites b/ci/build_test_suites
new file mode 100755
index 0000000..89ecefe
--- /dev/null
+++ b/ci/build_test_suites
@@ -0,0 +1,23 @@
+#!prebuilts/build-tools/linux-x86/bin/py3-cmd
+# 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.
+
+import sys
+
+import build_test_suites
+
+if __name__ == '__main__':
+ sys.dont_write_bytecode = True
+
+ build_test_suites.main(sys.argv)
diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py
new file mode 100644
index 0000000..5798f2b
--- /dev/null
+++ b/ci/build_test_suites.py
@@ -0,0 +1,408 @@
+# 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.
+
+"""Script to build only the necessary modules for general-tests along
+
+with whatever other targets are passed in.
+"""
+
+import argparse
+from collections.abc import Sequence
+import json
+import os
+import pathlib
+import re
+import subprocess
+import sys
+from typing import Any
+
+import test_mapping_module_retriever
+
+
+# List of modules that are always required to be in general-tests.zip
+REQUIRED_MODULES = frozenset(
+ ['cts-tradefed', 'vts-tradefed', 'compatibility-host-util', 'soong_zip']
+)
+
+
+def build_test_suites(argv):
+ args = parse_args(argv)
+
+ if not os.environ.get('BUILD_NUMBER')[0] == 'P':
+ build_everything(args)
+ return
+
+ # Call the class to map changed files to modules to build.
+ # TODO(lucafarsi): Move this into a replaceable class.
+ build_affected_modules(args)
+
+
+def parse_args(argv):
+ argparser = argparse.ArgumentParser()
+ argparser.add_argument(
+ 'extra_targets', nargs='*', help='Extra test suites to build.'
+ )
+ argparser.add_argument('--target_product')
+ argparser.add_argument('--target_release')
+ argparser.add_argument(
+ '--with_dexpreopt_boot_img_and_system_server_only', action='store_true'
+ )
+ argparser.add_argument('--dist_dir')
+ argparser.add_argument('--change_info', nargs='?')
+ argparser.add_argument('--extra_required_modules', nargs='*')
+
+ return argparser.parse_args()
+
+
+def build_everything(args: argparse.Namespace):
+ build_command = base_build_command(args)
+ build_command.append('general-tests')
+
+ run_command(build_command, print_output=True)
+
+
+def build_affected_modules(args: argparse.Namespace):
+ modules_to_build = find_modules_to_build(
+ pathlib.Path(args.change_info), args.extra_required_modules
+ )
+
+ # Call the build command with everything.
+ build_command = base_build_command(args)
+ build_command.extend(modules_to_build)
+ # When not building general-tests we also have to build the general tests
+ # shared libs.
+ build_command.append('general-tests-shared-libs')
+
+ run_command(build_command, print_output=True)
+
+ zip_build_outputs(modules_to_build, args.dist_dir, args.target_release)
+
+
+def base_build_command(args: argparse.Namespace) -> list:
+ build_command = []
+ build_command.append('time')
+ build_command.append('./build/soong/soong_ui.bash')
+ build_command.append('--make-mode')
+ build_command.append('dist')
+ build_command.append('DIST_DIR=' + args.dist_dir)
+ build_command.append('TARGET_PRODUCT=' + args.target_product)
+ build_command.append('TARGET_RELEASE=' + args.target_release)
+ if args.with_dexpreopt_boot_img_and_system_server_only:
+ build_command.append('WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true')
+ build_command.extend(args.extra_targets)
+
+ return build_command
+
+
+def run_command(
+ args: list[str],
+ env: dict[str, str] = os.environ,
+ print_output: bool = False,
+) -> str:
+ result = subprocess.run(
+ args=args,
+ text=True,
+ capture_output=True,
+ check=False,
+ env=env,
+ )
+ # If the process failed, print its stdout and propagate the exception.
+ if not result.returncode == 0:
+ print('Build command failed! output:')
+ print('stdout: ' + result.stdout)
+ print('stderr: ' + result.stderr)
+
+ result.check_returncode()
+
+ if print_output:
+ print(result.stdout)
+
+ return result.stdout
+
+
+def find_modules_to_build(
+ change_info: pathlib.Path, extra_required_modules: list[str]
+) -> set[str]:
+ changed_files = find_changed_files(change_info)
+
+ test_mappings = test_mapping_module_retriever.GetTestMappings(
+ changed_files, set()
+ )
+
+ # Soong_zip is required to generate the output zip so always build it.
+ modules_to_build = set(REQUIRED_MODULES)
+ if extra_required_modules:
+ modules_to_build.update(extra_required_modules)
+
+ modules_to_build.update(find_affected_modules(test_mappings, changed_files))
+
+ return modules_to_build
+
+
+def find_changed_files(change_info: pathlib.Path) -> set[str]:
+ with open(change_info) as change_info_file:
+ change_info_contents = json.load(change_info_file)
+
+ changed_files = set()
+
+ for change in change_info_contents['changes']:
+ project_path = change.get('projectPath') + '/'
+
+ for revision in change.get('revisions'):
+ for file_info in revision.get('fileInfos'):
+ changed_files.add(project_path + file_info.get('path'))
+
+ return changed_files
+
+
+def find_affected_modules(
+ test_mappings: dict[str, Any], changed_files: set[str]
+) -> set[str]:
+ modules = set()
+
+ # The test_mappings object returned by GetTestMappings is organized as
+ # follows:
+ # {
+ # 'test_mapping_file_path': {
+ # 'group_name' : [
+ # 'name': 'module_name',
+ # ],
+ # }
+ # }
+ for test_mapping in test_mappings.values():
+ for group in test_mapping.values():
+ for entry in group:
+ module_name = entry.get('name', None)
+
+ if not module_name:
+ continue
+
+ file_patterns = entry.get('file_patterns')
+ if not file_patterns:
+ modules.add(module_name)
+ continue
+
+ if matches_file_patterns(file_patterns, changed_files):
+ modules.add(module_name)
+ continue
+
+ return modules
+
+
+# TODO(lucafarsi): Share this logic with the original logic in
+# test_mapping_test_retriever.py
+def matches_file_patterns(
+ file_patterns: list[set], changed_files: set[str]
+) -> bool:
+ for changed_file in changed_files:
+ for pattern in file_patterns:
+ if re.search(pattern, changed_file):
+ return True
+
+ return False
+
+
+def zip_build_outputs(
+ modules_to_build: set[str], dist_dir: str, target_release: str
+):
+ src_top = os.environ.get('TOP', os.getcwd())
+
+ # Call dumpvars to get the necessary things.
+ # TODO(lucafarsi): Don't call soong_ui 4 times for this, --dumpvars-mode can
+ # do it but it requires parsing.
+ host_out_testcases = pathlib.Path(
+ get_soong_var('HOST_OUT_TESTCASES', target_release)
+ )
+ target_out_testcases = pathlib.Path(
+ get_soong_var('TARGET_OUT_TESTCASES', target_release)
+ )
+ product_out = pathlib.Path(get_soong_var('PRODUCT_OUT', target_release))
+ soong_host_out = pathlib.Path(get_soong_var('SOONG_HOST_OUT', target_release))
+ host_out = pathlib.Path(get_soong_var('HOST_OUT', target_release))
+
+ # Call the class to package the outputs.
+ # TODO(lucafarsi): Move this code into a replaceable class.
+ host_paths = []
+ target_paths = []
+ host_config_files = []
+ target_config_files = []
+ for module in modules_to_build:
+ host_path = os.path.join(host_out_testcases, module)
+ if os.path.exists(host_path):
+ host_paths.append(host_path)
+ collect_config_files(src_top, host_path, host_config_files)
+
+ target_path = os.path.join(target_out_testcases, module)
+ if os.path.exists(target_path):
+ target_paths.append(target_path)
+ collect_config_files(src_top, target_path, target_config_files)
+
+ zip_test_configs_zips(
+ dist_dir, host_out, product_out, host_config_files, target_config_files
+ )
+
+ zip_command = base_zip_command(host_out, dist_dir, 'general-tests.zip')
+
+ # Add host testcases.
+ zip_command.append('-C')
+ zip_command.append(os.path.join(src_top, soong_host_out))
+ zip_command.append('-P')
+ zip_command.append('host/')
+ for path in host_paths:
+ zip_command.append('-D')
+ zip_command.append(path)
+
+ # Add target testcases.
+ zip_command.append('-C')
+ zip_command.append(os.path.join(src_top, product_out))
+ zip_command.append('-P')
+ zip_command.append('target')
+ for path in target_paths:
+ zip_command.append('-D')
+ zip_command.append(path)
+
+ # 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 = os.path.join(soong_host_out, 'framework')
+
+ zip_command.append('-C')
+ zip_command.append(framework_path)
+ zip_command.append('-P')
+ zip_command.append('host/tools')
+ zip_command.append('-f')
+ zip_command.append(os.path.join(framework_path, 'cts-tradefed.jar'))
+ zip_command.append('-f')
+ zip_command.append(
+ os.path.join(framework_path, 'compatibility-host-util.jar')
+ )
+ zip_command.append('-f')
+ zip_command.append(os.path.join(framework_path, 'vts-tradefed.jar'))
+
+ run_command(zip_command, print_output=True)
+
+
+def collect_config_files(
+ src_top: pathlib.Path, root_dir: pathlib.Path, config_files: list[str]
+):
+ for root, dirs, files in os.walk(os.path.join(src_top, root_dir)):
+ for file in files:
+ if file.endswith('.config'):
+ config_files.append(os.path.join(root_dir, file))
+
+
+def base_zip_command(
+ host_out: pathlib.Path, dist_dir: pathlib.Path, name: str
+) -> list[str]:
+ return [
+ 'time',
+ os.path.join(host_out, 'bin', 'soong_zip'),
+ '-d',
+ '-o',
+ os.path.join(dist_dir, name),
+ ]
+
+
+# generate general-tests_configs.zip which contains all of the .config files
+# that were built and general-tests_list.zip which contains a text file which
+# lists all of the .config files that are in general-tests_configs.zip.
+#
+# general-tests_comfigs.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.
+def zip_test_configs_zips(
+ dist_dir: pathlib.Path,
+ host_out: pathlib.Path,
+ product_out: pathlib.Path,
+ host_config_files: list[str],
+ target_config_files: list[str],
+):
+ with open(
+ os.path.join(host_out, 'host_general-tests_list'), 'w'
+ ) as host_list_file, open(
+ os.path.join(product_out, 'target_general-tests_list'), 'w'
+ ) as target_list_file, open(
+ os.path.join(host_out, 'general-tests_list'), 'w'
+ ) as list_file:
+
+ for config_file in host_config_files:
+ host_list_file.write(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(config_file + '\n')
+ list_file.write(
+ 'target/' + os.path.relpath(config_file, product_out) + '\n'
+ )
+
+ tests_config_zip_command = base_zip_command(
+ host_out, dist_dir, 'general-tests_configs.zip'
+ )
+ tests_config_zip_command.append('-P')
+ tests_config_zip_command.append('host')
+ tests_config_zip_command.append('-C')
+ tests_config_zip_command.append(host_out)
+ tests_config_zip_command.append('-l')
+ tests_config_zip_command.append(
+ os.path.join(host_out, 'host_general-tests_list')
+ )
+ tests_config_zip_command.append('-P')
+ tests_config_zip_command.append('target')
+ tests_config_zip_command.append('-C')
+ tests_config_zip_command.append(product_out)
+ tests_config_zip_command.append('-l')
+ tests_config_zip_command.append(
+ os.path.join(product_out, 'target_general-tests_list')
+ )
+ run_command(tests_config_zip_command, print_output=True)
+
+ tests_list_zip_command = base_zip_command(
+ host_out, dist_dir, 'general-tests_list.zip'
+ )
+ tests_list_zip_command.append('-C')
+ tests_list_zip_command.append(host_out)
+ tests_list_zip_command.append('-f')
+ tests_list_zip_command.append(os.path.join(host_out, 'general-tests_list'))
+ run_command(tests_list_zip_command, print_output=True)
+
+
+def get_soong_var(var: str, target_release: str) -> str:
+ new_env = os.environ.copy()
+ new_env['TARGET_RELEASE'] = target_release
+
+ value = run_command(
+ ['./build/soong/soong_ui.bash', '--dumpvar-mode', '--abs', var],
+ env=new_env,
+ ).strip()
+ if not value:
+ raise RuntimeError('Necessary soong variable ' + var + ' not found.')
+
+ return value
+
+
+def main(argv):
+ build_test_suites(sys.argv)
diff --git a/ci/test_mapping_module_retriever.py b/ci/test_mapping_module_retriever.py
new file mode 100644
index 0000000..d2c13c0
--- /dev/null
+++ b/ci/test_mapping_module_retriever.py
@@ -0,0 +1,125 @@
+# 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.
+
+"""
+Simple parsing code to scan test_mapping files and determine which
+modules are needed to build for the given list of changed files.
+TODO(lucafarsi): Deduplicate from artifact_helper.py
+"""
+
+from typing import Any, Dict, Set, Text
+import json
+import os
+import re
+
+# Regex to extra test name from the path of test config file.
+TEST_NAME_REGEX = r'(?:^|.*/)([^/]+)\.config'
+
+# Key name for TEST_MAPPING imports
+KEY_IMPORTS = 'imports'
+KEY_IMPORT_PATH = 'path'
+
+# Name of TEST_MAPPING file.
+TEST_MAPPING = 'TEST_MAPPING'
+
+# Pattern used to identify double-quoted strings and '//'-format comments in
+# TEST_MAPPING file, but only double-quoted strings are included within the
+# matching group.
+_COMMENTS_RE = re.compile(r'(\"(?:[^\"\\]|\\.)*\"|(?=//))(?://.*)?')
+
+
+def FilterComments(test_mapping_file: Text) -> Text:
+ """Remove comments in TEST_MAPPING file to valid format.
+
+ Only '//' is regarded as comments.
+
+ Args:
+ test_mapping_file: Path to a TEST_MAPPING file.
+
+ Returns:
+ Valid json string without comments.
+ """
+ return re.sub(_COMMENTS_RE, r'\1', test_mapping_file)
+
+def GetTestMappings(paths: Set[Text],
+ checked_paths: Set[Text]) -> Dict[Text, Dict[Text, Any]]:
+ """Get the affected TEST_MAPPING files.
+
+ TEST_MAPPING files in source code are packaged into a build artifact
+ `test_mappings.zip`. Inside the zip file, the path of each TEST_MAPPING file
+ is preserved. From all TEST_MAPPING files in the source code, this method
+ locates the affected TEST_MAPPING files based on the given paths list.
+
+ A TEST_MAPPING file may also contain `imports` that import TEST_MAPPING files
+ from a different location, e.g.,
+ "imports": [
+ {
+ "path": "../folder2"
+ }
+ ]
+ In that example, TEST_MAPPING files inside ../folder2 (relative to the
+ TEST_MAPPING file containing that imports section) and its parent directories
+ will also be included.
+
+ Args:
+ paths: A set of paths with related TEST_MAPPING files for given changes.
+ checked_paths: A set of paths that have been checked for TEST_MAPPING file
+ already. The set is updated after processing each TEST_MAPPING file. It's
+ used to prevent infinite loop when the method is called recursively.
+
+ Returns:
+ A dictionary of Test Mapping containing the content of the affected
+ TEST_MAPPING files, indexed by the path containing the TEST_MAPPING file.
+ """
+ test_mappings = {}
+
+ # Search for TEST_MAPPING files in each modified path and its parent
+ # directories.
+ all_paths = set()
+ for path in paths:
+ dir_names = path.split(os.path.sep)
+ all_paths |= set(
+ [os.path.sep.join(dir_names[:i + 1]) for i in range(len(dir_names))])
+ # Add root directory to the paths to search for TEST_MAPPING file.
+ all_paths.add('')
+
+ all_paths.difference_update(checked_paths)
+ checked_paths |= all_paths
+ # Try to load TEST_MAPPING file in each possible path.
+ for path in all_paths:
+ try:
+ test_mapping_file = os.path.join(os.path.join(os.getcwd(), path), 'TEST_MAPPING')
+ # Read content of TEST_MAPPING file.
+ content = FilterComments(open(test_mapping_file, "r").read())
+ test_mapping = json.loads(content)
+ test_mappings[path] = test_mapping
+
+ import_paths = set()
+ for import_detail in test_mapping.get(KEY_IMPORTS, []):
+ import_path = import_detail[KEY_IMPORT_PATH]
+ # Try the import path as absolute path.
+ import_paths.add(import_path)
+ # Try the import path as relative path based on the test mapping file
+ # containing the import.
+ norm_import_path = os.path.normpath(os.path.join(path, import_path))
+ import_paths.add(norm_import_path)
+ import_paths.difference_update(checked_paths)
+ if import_paths:
+ import_test_mappings = GetTestMappings(import_paths, checked_paths)
+ test_mappings.update(import_test_mappings)
+ except (KeyError, FileNotFoundError, NotADirectoryError):
+ # TEST_MAPPING file doesn't exist in path
+ pass
+
+ return test_mappings
diff --git a/cogsetup.sh b/cogsetup.sh
index 3005d58..6439af0 100644
--- a/cogsetup.sh
+++ b/cogsetup.sh
@@ -35,31 +35,6 @@
ln -s ${DEFAULT_OUTPUT_DIR} `pwd`/out
}
-# This function moves the reclient binaries into a directory that exists in a
-# non-cog part of the overall filesystem. This is to workaround the problem
-# described in b/289391270.
-function _copy_reclient_binaries_from_cog() {
- if [[ "${OUT_DIR}" == "" ]]; then
- OUT_DIR="out"
- fi
- local RECLIENT_VERSION=`readlink prebuilts/remoteexecution-client/live`
-
- local NONCOG_RECLIENT_BIN_DIR_BASE="${OUT_DIR}/.reclient"
- local NONCOG_RECLIENT_BIN_DIR="${NONCOG_RECLIENT_BIN_DIR_BASE}/${RECLIENT_VERSION}"
-
- # Create the non cog directory and setup live symlink.
- mkdir -p ${NONCOG_RECLIENT_BIN_DIR}
-
- if [ `ls ${NONCOG_RECLIENT_BIN_DIR} | wc -l` -lt 8 ]; then
- # Not all binaries exist, copy them from the Cog directory.
- local TOP=$(gettop)
- cp ${TOP}/prebuilts/remoteexecution-client/live/* ${NONCOG_RECLIENT_BIN_DIR}
- fi
-
- ln -sfn ${RECLIENT_VERSION} ${NONCOG_RECLIENT_BIN_DIR_BASE}/live
- export RBE_DIR="${NONCOG_RECLIENT_BIN_DIR_BASE}/live"
-}
-
# This function sets up the build environment to be appropriate for Cog.
function _setup_cog_env() {
_create_out_symlink_for_cog
@@ -67,7 +42,6 @@
echo -e "\e[0;33mWARNING:\e[00m Cog environment setup failed!"
return 1
fi
- _copy_reclient_binaries_from_cog
export ANDROID_BUILD_ENVIRONMENT_CONFIG="googler-cog"
diff --git a/core/Makefile b/core/Makefile
index 3cc3f4c..750db69 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -287,6 +287,11 @@
endif
endif
+# Do this early because sysprop.mk depends on BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC.
+ifeq (default,$(ENABLE_UFFD_GC))
+BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC := $(OUT_DIR)/soong/dexpreopt/kernel_version_for_uffd_gc.txt
+endif # ENABLE_UFFD_GC
+
include $(BUILD_SYSTEM)/sysprop.mk
# ----------------------------------------------------------------
@@ -2094,11 +2099,6 @@
# Get a colon-separated list of search paths.
INTERNAL_USERIMAGES_BINARY_PATHS := $(subst $(space),:,$(sort $(dir $(INTERNAL_USERIMAGES_DEPS))))
-# Collects file_contexts files from modules to be installed
-$(call merge-fc-files, \
- $(sort $(foreach m,$(product_MODULES),$(ALL_MODULES.$(m).FILE_CONTEXTS))),\
- $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.modules.tmp)
-
SELINUX_FC := $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.bin
INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)
@@ -3239,7 +3239,6 @@
exit 1; \
fi
ln -sfn $2 $1
-$1: .KATI_SYMLINK_OUTPUTS := $1
)
$(eval PARTITION_COMPAT_SYMLINKS += $1)
$1
@@ -3398,6 +3397,14 @@
FULL_SYSTEMIMAGE_DEPS += $(INTERNAL_ROOT_FILES) $(INSTALLED_FILES_FILE_ROOT)
+define write-file-lines
+$(1):
+ @echo Writing $$@
+ rm -f $$@
+ echo -n > $$@
+ $$(foreach f,$(2),echo "$$(f)" >> $$@$$(newline))
+endef
+
# -----------------------------------------------------------------
ifdef BUILDING_SYSTEM_IMAGE
@@ -3451,14 +3458,6 @@
$(systemimage_intermediates)/staging_dir.stamp: $(FULL_SYSTEMIMAGE_DEPS)
touch $@
-define write-file-lines
-$(1):
- @echo Writing $$@
- rm -f $$@
- echo -n > $$@
- $$(foreach f,$(2),echo "$$(f)" >> $$@$$(newline))
-endef
-
# $(1): output file
define build-systemimage-target
@echo "Target system fs image: $(1)"
@@ -5112,8 +5111,11 @@
$(error EMPTY_VENDOR_SKU_PLACEHOLDER is an internal variable and cannot be used for DEIVCE_MANIFEST_SKUS)
endif
-# -- Check system manifest / matrix including fragments (excluding other framework manifests / matrices, e.g. product);
-check_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/%, $(check_vintf_common_srcs))
+# -- Check system and system_ext manifests / matrices including fragments (excluding other framework manifests / matrices, e.g. product);
+ifdef BUILDING_SYSTEM_IMAGE
+check_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/% \
+ $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/%, \
+ $(check_vintf_common_srcs))
ifneq ($(check_vintf_system_deps),)
check_vintf_has_system := true
@@ -5140,9 +5142,12 @@
endif # check_vintf_system_deps
check_vintf_system_deps :=
+endif # BUILDING_SYSTEM_IMAGE
+
# -- Check vendor manifest / matrix including fragments (excluding other device manifests / matrices)
-check_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/%, $(check_vintf_common_srcs))
-check_vintf_vendor_deps += $(filter $(TARGET_OUT_VENDOR)/apex/%, $(check_vintf_common_srcs))
+check_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/% \
+ $(TARGET_OUT_VENDOR)/apex/%, \
+ $(check_vintf_common_srcs))
ifneq ($(strip $(check_vintf_vendor_deps)),)
check_vintf_has_vendor := true
check_vintf_vendor_log := $(intermediates)/check_vintf_vendor.log
@@ -5264,6 +5269,34 @@
endif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
+ifeq (default,$(ENABLE_UFFD_GC))
+
+ifneq (,$(BUILT_KERNEL_VERSION_FILE))
+$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC): $(BUILT_KERNEL_VERSION_FILE)
+$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC):
+ cp $(BUILT_KERNEL_VERSION_FILE) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)
+else
+# We make this a warning rather than an error to avoid breaking too many builds. When it happens,
+# we use a placeholder as the kernel version, which is consumed by uffd_gc_utils.py.
+$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC):
+ echo $$'\
+Unable to determine UFFD GC flag because the kernel version is not available and\n\
+PRODUCT_ENABLE_UFFD_GC is "default".\n\
+You can fix this by:\n\
+ 1. [Recommended] Making the kernel version available.\n\
+ (1). Set PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS to "true".\n\
+ (2). If you are still getting this message after doing so, see the warning about\n\
+ PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS in the build logs.\n\
+ or\n\
+ 2. Explicitly setting PRODUCT_ENABLE_UFFD_GC to "true" or "false" based on the kernel version.\n\
+ (1). Set PRODUCT_ENABLE_UFFD_GC to "true" if the kernel is a GKI kernel and is android12-5.4\n\
+ or above, or a non-GKI kernel that supports userfaultfd(2) and MREMAP_DONTUNMAP.\n\
+ (2). Set PRODUCT_ENABLE_UFFD_GC to "false" otherwise.'\
+ && echo '<unknown-kernel>' > $@
+endif # BUILT_KERNEL_VERSION_FILE
+
+endif # ENABLE_UFFD_GC == "default"
+
# -- Check VINTF compatibility of build.
# Skip partial builds; only check full builds. Only check if:
# - PRODUCT_ENFORCE_VINTF_MANIFEST is true
diff --git a/core/OWNERS b/core/OWNERS
index 88f6d06..36951a9 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -3,7 +3,10 @@
per-file proguard*.flags = jdduke@google.com
# For version updates
-per-file version_defaults.mk = aseaton@google.com,lubomir@google.com,pscovanner@google.com,bkhalife@google.com,jainne@google.com
+per-file version_defaults.mk = ankurbakshi@google.com,bkhalife@google.com,jainne@google.com,lokeshgoel@google.com,lubomir@google.com,pscovanner@google.com
# For sdk extensions version updates
per-file version_defaults.mk = amhk@google.com,gurpreetgs@google.com,mkhokhlova@google.com,robertogil@google.com
+
+# For Ravenwood test configs
+per-file ravenwood_test_config_template.xml = jsharkey@google.com,omakoto@google.com
diff --git a/core/android_manifest.mk b/core/android_manifest.mk
index ff49262..7f46903 100644
--- a/core/android_manifest.mk
+++ b/core/android_manifest.mk
@@ -51,6 +51,9 @@
my_target_sdk_version := $(my_target_sdk_version).$$(cat $(API_FINGERPRINT))
my_min_sdk_version := $(my_min_sdk_version).$$(cat $(API_FINGERPRINT))
$(fixed_android_manifest): $(API_FINGERPRINT)
+ else ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA
+ my_target_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA)
+ my_min_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA)
endif
endif
endif
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6af6f08..9f43a3e 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -152,10 +152,6 @@
$(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED))
endif
-ifdef PRODUCT_AVF_KERNEL_MODULES_ENABLED
-$(call add_soong_config_var_value,ANDROID,avf_kernel_modules_enabled,$(PRODUCT_AVF_KERNEL_MODULES_ENABLED))
-endif
-
$(call add_soong_config_var_value,ANDROID,release_avf_allow_preinstalled_apps,$(RELEASE_AVF_ALLOW_PREINSTALLED_APPS))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_device_assignment,$(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_dice_changes,$(RELEASE_AVF_ENABLE_DICE_CHANGES))
@@ -163,6 +159,8 @@
$(call add_soong_config_var_value,ANDROID,release_avf_enable_multi_tenant_microdroid_vm,$(RELEASE_AVF_ENABLE_MULTI_TENANT_MICRODROID_VM))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_remote_attestation,$(RELEASE_AVF_ENABLE_REMOTE_ATTESTATION))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_vendor_modules,$(RELEASE_AVF_ENABLE_VENDOR_MODULES))
+$(call add_soong_config_var_value,ANDROID,release_avf_enable_virt_cpufreq,$(RELEASE_AVF_ENABLE_VIRT_CPUFREQ))
+$(call add_soong_config_var_value,ANDROID,release_avf_microdroid_kernel_version,$(RELEASE_AVF_MICRODROID_KERNEL_VERSION))
$(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI))
@@ -189,6 +187,9 @@
$(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA)
$(call add_soong_config_var,ANDROID,FULL_SYSTEM_OPTIMIZE_JAVA)
+# TODO(b/319697968): Remove this build flag support when metalava fully supports flagged api
+$(call soong_config_set,ANDROID,release_hidden_api_exportable_stubs,$(RELEASE_HIDDEN_API_EXPORTABLE_STUBS))
+
# Check for SupplementalApi module.
ifeq ($(wildcard packages/modules/SupplementalApi),)
$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,false)
@@ -196,3 +197,14 @@
$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,true)
endif
+# Add crashrecovery build flag to soong
+$(call soong_config_set,ANDROID,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
+ifeq (true,$(RELEASE_CRASHRECOVERY_FILE_MOVE))
+ $(call soong_config_set,ANDROID,crashrecovery_files_in_module,true)
+ $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,false)
+else
+ $(call soong_config_set,ANDROID,crashrecovery_files_in_module,false)
+ $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,true)
+endif
+# Weirdly required because platform_bootclasspath is using AUTO namespace
+$(call soong_config_set,AUTO,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
diff --git a/core/art_config.mk b/core/art_config.mk
index f47a8e2..54bfd6b 100644
--- a/core/art_config.mk
+++ b/core/art_config.mk
@@ -12,38 +12,15 @@
# ENABLE_UFFD_GC: Whether to use userfaultfd GC.
config_enable_uffd_gc := \
- $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC))
+ $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)
-ifeq (,$(filter-out default,$(config_enable_uffd_gc)))
- ENABLE_UFFD_GC := true
-
- # Disable userfaultfd GC if the device doesn't support it (i.e., if
- # `min(ro.board.api_level ?? ro.board.first_api_level ?? MAX_VALUE,
- # ro.product.first_api_level ?? ro.build.version.sdk ?? MAX_VALUE) < 31`)
- # This logic aligns with how `ro.vendor.api_level` is calculated in
- # `system/core/init/property_service.cpp`.
- # We omit the check on `ro.build.version.sdk` here because we are on the latest build system.
- board_api_level := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL))
- ifneq (,$(board_api_level))
- ifeq (true,$(call math_lt,$(board_api_level),31))
- ENABLE_UFFD_GC := false
- endif
- endif
-
- ifneq (,$(PRODUCT_SHIPPING_API_LEVEL))
- ifeq (true,$(call math_lt,$(PRODUCT_SHIPPING_API_LEVEL),31))
- ENABLE_UFFD_GC := false
- endif
- endif
-else ifeq (true,$(config_enable_uffd_gc))
- ENABLE_UFFD_GC := true
-else ifeq (false,$(config_enable_uffd_gc))
- ENABLE_UFFD_GC := false
-else
+ifeq (,$(filter default true false,$(config_enable_uffd_gc)))
$(error Unknown PRODUCT_ENABLE_UFFD_GC value: $(config_enable_uffd_gc))
endif
-ADDITIONAL_PRODUCT_PROPERTIES += ro.dalvik.vm.enable_uffd_gc=$(ENABLE_UFFD_GC)
+ENABLE_UFFD_GC := $(config_enable_uffd_gc)
+# If the value is "default", it will be mangled by post_process_props.py.
+ADDITIONAL_PRODUCT_PROPERTIES += ro.dalvik.vm.enable_uffd_gc=$(config_enable_uffd_gc)
# Create APEX_BOOT_JARS_EXCLUDED which is a list of jars to be removed from
# ApexBoorJars when built from mainline prebuilts.
diff --git a/core/autogen_test_config.mk b/core/autogen_test_config.mk
index 137b118..b69f694 100644
--- a/core/autogen_test_config.mk
+++ b/core/autogen_test_config.mk
@@ -29,7 +29,7 @@
ifeq (true,$(LOCAL_VENDOR_MODULE))
autogen_test_install_base = /data/local/tests/vendor
endif
- ifeq (true,$(LOCAL_USE_VNDK))
+ ifeq (true,$(call module-in-vendor-or-product))
autogen_test_install_base = /data/local/tests/vendor
endif
endif
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 254bfeb..b8aa5fe 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -120,7 +120,7 @@
$(LOCAL_VENDOR_MODULE) \
$(LOCAL_PROPRIETARY_MODULE))
-include $(BUILD_SYSTEM)/local_vndk.mk
+include $(BUILD_SYSTEM)/local_vendor_product.mk
# local_current_sdk needs to run before local_systemsdk because the former may override
# LOCAL_SDK_VERSION which is used by the latter.
@@ -806,7 +806,7 @@
ifneq (,$(test_config))
$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
$(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \
- $(test_config):$(dir)/$(LOCAL_MODULE).config)))
+ $(test_config):$(dir)/$(LOCAL_MODULE).config$(LOCAL_TEST_CONFIG_SUFFIX))))
endif
ifneq (,$(LOCAL_EXTRA_FULL_TEST_CONFIGS))
@@ -1017,6 +1017,11 @@
my_required_modules += $(LOCAL_REQUIRED_MODULES_$($(my_prefix)OS))
endif
+ifdef LOCAL_ACONFIG_FILES
+ ALL_MODULES.$(my_register_name).ACONFIG_FILES := \
+ $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES)
+endif
+
ifndef LOCAL_SOONG_MODULE_INFO_JSON
ALL_MAKE_MODULE_INFO_JSON_MODULES += $(my_register_name)
ALL_MODULES.$(my_register_name).SHARED_LIBS := \
@@ -1055,9 +1060,6 @@
$(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS) \
$(filter-out $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS),$(my_supported_variant))
- ALL_MODULES.$(my_register_name).ACONFIG_FILES := \
- $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES)
-
ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \
$(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) $(LOCAL_COMPATIBILITY_SUITE)
ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE)
@@ -1093,10 +1095,10 @@
## When compiling against API imported module, use API import stub
## libraries.
##########################################################################
-ifneq ($(LOCAL_USE_VNDK),)
+ifneq ($(call module-in-vendor-or-product),)
ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
apiimport_postfix := .apiimport
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
apiimport_postfix := .apiimport.product
else
apiimport_postfix := .apiimport.vendor
@@ -1111,7 +1113,7 @@
## When compiling against the VNDK, add the .vendor or .product suffix to
## required modules.
##########################################################################
-ifneq ($(LOCAL_USE_VNDK),)
+ifneq ($(call module-in-vendor-or-product),)
#####################################################
## Soong modules may be built three times, once for
## /system, once for /vendor and once for /product.
@@ -1122,7 +1124,7 @@
# We don't do this renaming for soong-defined modules since they already
# have correct names (with .vendor or .product suffix when necessary) in
# their LOCAL_*_LIBRARIES.
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
my_required_modules := $(foreach l,$(my_required_modules),\
$(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))
else
@@ -1187,7 +1189,6 @@
endif
ALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross)
ifndef LOCAL_IS_HOST_MODULE
-ALL_MODULES.$(my_register_name).FILE_CONTEXTS := $(LOCAL_FILE_CONTEXTS)
ALL_MODULES.$(my_register_name).APEX_KEYS_FILE := $(LOCAL_APEX_KEY_PATH)
endif
test_config :=
diff --git a/core/binary.mk b/core/binary.mk
index 7998a5a..6dab49c 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -145,15 +145,21 @@
ifneq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
ifeq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
my_native_coverage := true
+ my_clang_coverage := true
else
my_native_coverage := false
+ my_clang_coverage := false
endif
else
my_native_coverage := false
+ my_clang_coverage := false
endif
ifneq ($(NATIVE_COVERAGE),true)
my_native_coverage := false
endif
+ifneq ($(CLANG_COVERAGE),true)
+ my_clang_coverage := false
+endif
# Exclude directories from checking allowed manual binder interface lists.
# TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths.
@@ -270,6 +276,7 @@
ifneq ($(my_ndk_api),current)
ifeq ($(call math_lt, $(my_ndk_api),23),true)
my_native_coverage := false
+ my_clang_coverage := false
endif
endif
endif
@@ -295,10 +302,40 @@
endif
endif
-ifneq ($(LOCAL_USE_VNDK),)
+ifeq ($(CLANG_COVERAGE),true)
+ ifndef LOCAL_IS_HOST_MODULE
+ my_ldflags += $(CLANG_COVERAGE_HOST_LDFLAGS)
+ ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)
+ my_whole_static_libraries += libclang_rt.profile
+ ifeq ($(LOCAL_SDK_VERSION),)
+ my_whole_static_libraries += libprofile-clang-extras
+ else
+ my_whole_static_libraries += libprofile-clang-extras_ndk
+ endif
+ endif
+ endif
+ ifeq ($(my_clang_coverage),true)
+ my_profile_instr_generate := $(CLANG_COVERAGE_INSTR_PROFILE)
+ ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
+ my_cflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS)
+ my_ldflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS)
+ endif
+
+ my_profile_instr_generate += $(CLANG_COVERAGE_CONFIG_COMMFLAGS)
+ my_cflags += $(CLANG_COVERAGE_INSTR_PROFILE) $(CLANG_COVERAGE_CONFIG_CFLAGS) $(CLANG_COVERAGE_CONFIG_COMMFLAGS)
+ my_ldflags += $(CLANG_COVERAGE_CONFIG_COMMFLAGS)
+
+ ifneq ($(filter hwaddress,$(my_sanitize)),)
+ my_cflags += $(CLANG_COVERAGE_HWASAN_FLAGS)
+ my_ldflags += $(CLANG_COVERAGE_HWASAN_FLAGS)
+ endif
+ endif
+endif
+
+ifneq ($(call module-in-vendor-or-product),)
my_cflags += -D__ANDROID_VNDK__
- ifneq ($(LOCAL_USE_VNDK_VENDOR),)
- # Vendor modules have LOCAL_USE_VNDK_VENDOR
+ ifneq ($(LOCAL_IN_VENDOR),)
+ # Vendor modules have LOCAL_IN_VENDOR
my_cflags += -D__ANDROID_VENDOR__
ifeq ($(BOARD_API_LEVEL),)
@@ -308,8 +345,8 @@
else
my_cflags += -D__ANDROID_VENDOR_API__=$(BOARD_API_LEVEL)
endif
- else ifneq ($(LOCAL_USE_VNDK_PRODUCT),)
- # Product modules have LOCAL_USE_VNDK_PRODUCT
+ else ifneq ($(LOCAL_IN_PRODUCT),)
+ # Product modules have LOCAL_IN_PRODUCT
my_cflags += -D__ANDROID_PRODUCT__
endif
endif
@@ -1137,8 +1174,8 @@
apiimport_postfix := .apiimport
-ifneq ($(LOCAL_USE_VNDK),)
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ifneq ($(call module-in-vendor-or-product),)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
apiimport_postfix := .apiimport.product
else
apiimport_postfix := .apiimport.vendor
@@ -1155,14 +1192,14 @@
###########################################################
## When compiling against the VNDK, use LL-NDK libraries
###########################################################
-ifneq ($(LOCAL_USE_VNDK),)
+ifneq ($(call module-in-vendor-or-product),)
#####################################################
## Soong modules may be built three times, once for
## /system, once for /vendor and once for /product.
## If we're using the VNDK, switch all soong
## libraries over to the /vendor or /product variant.
#####################################################
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\
$(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l)))
my_static_libraries := $(foreach l,$(my_static_libraries),\
@@ -1189,7 +1226,7 @@
# Platform can use vendor public libraries. If a required shared lib is one of
# the vendor public libraries, the lib is switched to the stub version of the lib.
-ifeq ($(LOCAL_USE_VNDK),)
+ifeq ($(call module-in-vendor-or-product),)
my_shared_libraries := $(foreach l,$(my_shared_libraries),\
$(if $(filter $(l),$(VENDOR_PUBLIC_LIBRARIES)),$(l).vendorpublic,$(l)))
endif
@@ -1241,7 +1278,7 @@
my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type)
my_warn_types := $(my_warn_ndk_types)
my_allowed_types := $(my_allowed_ndk_types)
-else ifdef LOCAL_USE_VNDK
+else ifeq ($(call module-in-vendor-or-product),true)
_name := $(patsubst %.vendor,%,$(LOCAL_MODULE))
_name := $(patsubst %.product,%,$(LOCAL_MODULE))
ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),)
@@ -1252,7 +1289,7 @@
endif
my_warn_types :=
my_allowed_types := native:vndk native:vndk_private
- else ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ else ifeq ($(LOCAL_IN_PRODUCT),true)
# Modules installed to /product cannot directly depend on modules marked
# with vendor_available: false
my_link_type := native:product
@@ -1555,7 +1592,7 @@
###########################################################
ifndef LOCAL_IS_HOST_MODULE
-ifdef LOCAL_USE_VNDK
+ifeq ($(call module-in-vendor-or-product),true)
my_target_global_c_includes :=
my_target_global_c_system_includes := $(TARGET_OUT_HEADERS)
else ifdef LOCAL_SDK_VERSION
@@ -1649,7 +1686,7 @@
####################################################
imported_includes :=
-ifdef LOCAL_USE_VNDK
+ifeq (true,$(call module-in-vendor-or-product))
imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))
else
# everything else should manually specify headers
diff --git a/core/board_config.mk b/core/board_config.mk
index 5a1a781..8c23f93 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -965,12 +965,15 @@
$(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found))
endef
+ifeq ($(KEEP_VNDK),true)
ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
$(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
endif
ifneq ($(BOARD_VNDK_VERSION),current)
$(call check_vndk_version,$(BOARD_VNDK_VERSION))
endif
+endif
+
TARGET_VENDOR_TEST_SUFFIX := /vendor
ifeq (,$(TARGET_BUILD_UNBUNDLED))
@@ -994,11 +997,6 @@
endif
BOARD_API_LEVEL := $(RELEASE_BOARD_API_LEVEL)
.KATI_READONLY := BOARD_API_LEVEL
-
- ifdef RELEASE_BOARD_API_LEVEL_FROZEN
- BOARD_API_LEVEL_FROZEN := true
- .KATI_READONLY := BOARD_API_LEVEL_FROZEN
- endif
endif
###########################################
diff --git a/core/cc_prebuilt_internal.mk b/core/cc_prebuilt_internal.mk
index 000159a..e34e110 100644
--- a/core/cc_prebuilt_internal.mk
+++ b/core/cc_prebuilt_internal.mk
@@ -80,7 +80,7 @@
ifdef LOCAL_SDK_VERSION
my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type)
-else ifdef LOCAL_USE_VNDK
+else ifeq ($(call module-in-vendor-or-product),true)
_name := $(patsubst %.vendor,%,$(LOCAL_MODULE))
_name := $(patsubst %.product,%,$(LOCAL_MODULE))
ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),)
@@ -90,7 +90,7 @@
my_link_type := native:vndk_private
endif
else
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
my_link_type := native:product
else
my_link_type := native:vendor
@@ -139,8 +139,8 @@
# When compiling against API imported module, use API import stub libraries.
apiimport_postfix := .apiimport
-ifneq ($(LOCAL_USE_VNDK),)
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ifeq ($(call module-in-vendor-or-product),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
apiimport_postfix := .apiimport.product
else
apiimport_postfix := .apiimport.vendor
@@ -158,8 +158,8 @@
endif #my_system_shared_libraries
ifdef my_shared_libraries
-ifdef LOCAL_USE_VNDK
- ifeq ($(LOCAL_USE_VNDK_PRODUCT),true)
+ifeq ($(call module-in-vendor-or-product),true)
+ ifeq ($(LOCAL_IN_PRODUCT),true)
my_shared_libraries := $(foreach l,$(my_shared_libraries),\
$(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))
else
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 2b84fcd..5481d50 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -87,7 +87,6 @@
LOCAL_EXTRA_FULL_TEST_CONFIGS:=
LOCAL_EXTRACT_APK:=
LOCAL_EXTRACT_DPI_APK:=
-LOCAL_FILE_CONTEXTS:=
LOCAL_FINDBUGS_FLAGS:=
LOCAL_FORCE_STATIC_EXECUTABLE:=
LOCAL_FULL_CLASSES_JACOCO_JAR:=
@@ -292,6 +291,7 @@
LOCAL_SYSTEM_SHARED_LIBRARIES:=none
LOCAL_TARGET_REQUIRED_MODULES:=
LOCAL_TEST_CONFIG:=
+LOCAL_TEST_CONFIG_SUFFIX:=
LOCAL_TEST_DATA:=
LOCAL_TEST_DATA_BINS:=
LOCAL_TEST_MAINLINE_MODULES:=
@@ -305,7 +305,8 @@
LOCAL_USE_AAPT2:=
LOCAL_USE_CLANG_LLD:=
LOCAL_USE_VNDK:=
-LOCAL_USE_VNDK_PRODUCT:=
+LOCAL_IN_VENDOR:=
+LOCAL_IN_PRODUCT:=
LOCAL_USES_LIBRARIES:=
LOCAL_VENDOR_MODULE:=
LOCAL_VINTF_FRAGMENTS:=
diff --git a/core/combo/arch/x86/goldmont-without-xsaves.mk b/core/combo/arch/x86/goldmont-without-sha-xsaves.mk
similarity index 100%
rename from core/combo/arch/x86/goldmont-without-xsaves.mk
rename to core/combo/arch/x86/goldmont-without-sha-xsaves.mk
diff --git a/core/combo/arch/x86_64/goldmont-without-xsaves.mk b/core/combo/arch/x86_64/goldmont-without-sha-xsaves.mk
similarity index 100%
rename from core/combo/arch/x86_64/goldmont-without-xsaves.mk
rename to core/combo/arch/x86_64/goldmont-without-sha-xsaves.mk
diff --git a/core/config.mk b/core/config.mk
index 469be30..dbee0a0 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -374,16 +374,6 @@
endif
-include $(ANDROID_BUILDSPEC)
-# Starting in Android U, non-VNDK devices not supported
-# WARNING: DO NOT CHANGE: if you are downstream of AOSP, and you change this, without
-# letting upstream know it's important to you, we may do cleanup which breaks this
-# significantly. Please let us know if you are changing this.
-ifndef BOARD_VNDK_VERSION
-# READ WARNING - DO NOT CHANGE
-BOARD_VNDK_VERSION := current
-# READ WARNING - DO NOT CHANGE
-endif
-
# ---------------------------------------------------------------
# Define most of the global variables. These are the ones that
# are specific to the user's build configuration.
@@ -813,13 +803,6 @@
requirements :=
-# Set default value of KEEP_VNDK.
-ifeq ($(RELEASE_DEPRECATE_VNDK),true)
- KEEP_VNDK ?= false
-else
- KEEP_VNDK ?= true
-endif
-
# BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of
# partitions is supported. But the early-mount must be supported for full
# treble products, and so BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED should be set
@@ -888,21 +871,9 @@
# SEPolicy versions
-# PLATFORM_SEPOLICY_VERSION is a number of the form "YYYYMM.0" with "YYYYMM"
-# mapping to vFRC version. This value will be set to 1000000.0 to represent
-# tip-of-tree development that is inherently unstable and thus designed not to
-# work with any shipping vendor policy. This is similar in spirit to how
-# DEFAULT_APP_TARGET_SDK is set.
-sepolicy_vers := $(BOARD_API_LEVEL).0
-
-TOT_SEPOLICY_VERSION := 1000000.0
-ifeq (true,$(BOARD_API_LEVEL_FROZEN))
- PLATFORM_SEPOLICY_VERSION := $(sepolicy_vers)
-else
- PLATFORM_SEPOLICY_VERSION := $(TOT_SEPOLICY_VERSION)
-endif
-sepolicy_vers :=
-
+# PLATFORM_SEPOLICY_VERSION is a number of the form "YYYYMM" with "YYYYMM"
+# mapping to vFRC version.
+PLATFORM_SEPOLICY_VERSION := $(BOARD_API_LEVEL)
BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION)
.KATI_READONLY := PLATFORM_SEPOLICY_VERSION BOARD_SEPOLICY_VERS
@@ -919,7 +890,6 @@
.KATI_READONLY := \
PLATFORM_SEPOLICY_COMPAT_VERSIONS \
PLATFORM_SEPOLICY_VERSION \
- TOT_SEPOLICY_VERSION \
ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
@@ -1209,7 +1179,6 @@
TARGET_AVAILABLE_SDK_VERSIONS := $(filter-out %/module-lib %/system-server,$(TARGET_AVAILABLE_SDK_VERSIONS))
TARGET_AVAIALBLE_SDK_VERSIONS := $(call numerically_sort,$(TARGET_AVAILABLE_SDK_VERSIONS))
-TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_8_SUPPORT := $(call numbers_less_than,24,$(TARGET_AVAILABLE_SDK_VERSIONS))
TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT := $(call numbers_less_than,30,$(TARGET_AVAILABLE_SDK_VERSIONS))
TARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT := $(call numbers_less_than,32,$(TARGET_AVAILABLE_SDK_VERSIONS))
TARGET_SDK_VERSIONS_WITHOUT_JAVA_17_SUPPORT := $(call numbers_less_than,34,$(TARGET_AVAILABLE_SDK_VERSIONS))
@@ -1294,6 +1263,15 @@
include $(BUILD_SYSTEM)/dumpvar.mk
+ifneq ($(KEEP_VNDK),true)
+ifdef BOARD_VNDK_VERSION
+BOARD_VNDK_VERSION=
+endif
+ifdef PLATFORM_VNDK_VERSION
+PLATFORM_VNDK_VERSION=
+endif
+endif
+
ifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA))
ifeq (,$(SYSTEM_OPTIMIZE_JAVA))
$(error SYSTEM_OPTIMIZE_JAVA must be enabled when FULL_SYSTEM_OPTIMIZE_JAVA is enabled)
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 3507961..ab2d5c1 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -261,10 +261,10 @@
ifneq ($(filter memtag_heap memtag_stack memtag_globals,$(my_sanitize)),)
ifneq ($(filter memtag_heap,$(my_sanitize_diag)),)
- my_cflags += -fsanitize-memtag-mode=sync
+ my_ldflags += -fsanitize-memtag-mode=sync
my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag))
else
- my_cflags += -fsanitize-memtag-mode=async
+ my_ldflags += -fsanitize-memtag-mode=async
endif
endif
@@ -277,11 +277,13 @@
ifneq ($(filter memtag_heap,$(my_sanitize)),)
my_cflags += -fsanitize=memtag-heap
+ my_ldflags += -fsanitize=memtag-heap
my_sanitize := $(filter-out memtag_heap,$(my_sanitize))
endif
ifneq ($(filter memtag_stack,$(my_sanitize)),)
my_cflags += -fsanitize=memtag-stack
+ my_ldflags += -fsanitize=memtag-stack
my_cflags += -march=armv8a+memtag
my_ldflags += -march=armv8a+memtag
my_asflags += -march=armv8a+memtag
@@ -290,6 +292,7 @@
ifneq ($(filter memtag_globals,$(my_sanitize)),)
my_cflags += -fsanitize=memtag-globals
+ my_ldflags += -fsanitize=memtag-globals
# TODO(mitchp): For now, enable memtag-heap with memtag-globals because the
# linker isn't new enough
# (https://reviews.llvm.org/differential/changeset/?ref=4243566).
diff --git a/core/copy_headers.mk b/core/copy_headers.mk
index c457eb0..397ea62 100644
--- a/core/copy_headers.mk
+++ b/core/copy_headers.mk
@@ -13,13 +13,12 @@
$(call pretty-error,Modules using LOCAL_SDK_VERSION may not use LOCAL_COPY_HEADERS)
endif
-include $(BUILD_SYSTEM)/local_vndk.mk
+include $(BUILD_SYSTEM)/local_vendor_product.mk
-# If we're using the VNDK, only vendor modules using the VNDK may use
-# LOCAL_COPY_HEADERS. Platform libraries will not have the include path
-# present.
-ifndef LOCAL_USE_VNDK
- $(call pretty-error,Only vendor modules using LOCAL_USE_VNDK may use LOCAL_COPY_HEADERS)
+# Modules in vendor or product may use LOCAL_COPY_HEADERS.
+# Platform libraries will not have the include path present.
+ifeq ($(call module-in-vendor-or-product),)
+ $(call pretty-error,Only modules in vendor or product may use LOCAL_COPY_HEADERS)
endif
# Clean up LOCAL_COPY_HEADERS_TO, since soong_ui will be comparing cleaned
diff --git a/core/definitions.mk b/core/definitions.mk
index 1f2d011..40b7980 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2774,6 +2774,10 @@
$(if $(LOCAL_MIN_SDK_VERSION),$(LOCAL_MIN_SDK_VERSION),$(call module-target-sdk-version))
endef
+# Checks if module is in vendor or product
+define module-in-vendor-or-product
+$(if $(filter true,$(LOCAL_IN_VENDOR) $(LOCAL_IN_PRODUCT)),true)
+endef
define transform-classes.jar-to-dex
@echo "target Dex: $(PRIVATE_MODULE)"
@@ -3306,7 +3310,6 @@
@mkdir -p $$(dir $$@)
@rm -rf $$@
$(hide) ln -sf $(2) $$@
-$(3): .KATI_SYMLINK_OUTPUTS := $(3)
endef
# Copy an apk to a target location while removing classes*.dex
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 37a389f..08311ca 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -57,6 +57,7 @@
# Build the boot.zip which contains the boot jars and their compilation output
# We can do this only if preopt is enabled and if the product uses libart config (which sets the
# default properties for preopting).
+# At the time of writing, this is only for ART Cloud.
ifeq ($(WITH_DEXPREOPT), true)
ifneq ($(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY), true)
ifeq ($(PRODUCT_USES_DEFAULT_ART_CONFIG), true)
@@ -95,15 +96,16 @@
bootclasspath_locations_arg := $(subst $(space),:,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS))
boot_images := $(subst :,$(space),$(DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE$(DEXPREOPT_INFIX)))
boot_image_arg := $(subst $(space),:,$(patsubst /%,%,$(boot_images)))
-dex2oat_extra_args := $(if $(filter true,$(ENABLE_UFFD_GC)),--runtime-arg -Xgc:CMC)
+uffd_gc_flag_txt := $(OUT_DIR)/soong/dexpreopt/uffd_gc_flag.txt
boot_zip_metadata_txt := $(dir $(boot_zip))boot_zip/METADATA.txt
+$(boot_zip_metadata_txt): $(uffd_gc_flag_txt)
$(boot_zip_metadata_txt):
rm -f $@
echo "bootclasspath = $(bootclasspath_arg)" >> $@
echo "bootclasspath-locations = $(bootclasspath_locations_arg)" >> $@
echo "boot-image = $(boot_image_arg)" >> $@
- echo "extra-args = $(dex2oat_extra_args)" >> $@
+ echo "extra-args = `cat $(uffd_gc_flag_txt)`" >> $@
$(call dist-for-goals, droidcore, $(boot_zip_metadata_txt))
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 10fbe8f..d51de33 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -122,7 +122,7 @@
$(call add_json_str, Dex2oatXmx, $(DEX2OAT_XMX))
$(call add_json_str, Dex2oatXms, $(DEX2OAT_XMS))
$(call add_json_str, EmptyDirectory, $(OUT_DIR)/empty)
- $(call add_json_bool, EnableUffdGc, $(filter true,$(ENABLE_UFFD_GC)))
+ $(call add_json_str, EnableUffdGc, $(ENABLE_UFFD_GC))
ifdef TARGET_ARCH
$(call add_json_map, CpuVariant)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index cfb8a66..30a6c06 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -50,6 +50,25 @@
# Release config
include $(BUILD_SYSTEM)/release_config.mk
+# Set default value of KEEP_VNDK.
+ifeq ($(RELEASE_DEPRECATE_VNDK),true)
+ KEEP_VNDK ?= false
+else
+ KEEP_VNDK ?= true
+endif
+
+ifeq ($(KEEP_VNDK),true)
+ # Starting in Android U, non-VNDK devices not supported
+ # WARNING: DO NOT CHANGE: if you are downstream of AOSP, and you change this, without
+ # letting upstream know it's important to you, we may do cleanup which breaks this
+ # significantly. Please let us know if you are changing this.
+ ifndef BOARD_VNDK_VERSION
+ # READ WARNING - DO NOT CHANGE
+ BOARD_VNDK_VERSION := current
+ # READ WARNING - DO NOT CHANGE
+ endif
+endif
+
# ---------------------------------------------------------------
# Set up version information
include $(BUILD_SYSTEM)/version_util.mk
diff --git a/core/executable_internal.mk b/core/executable_internal.mk
index fb14cce..fecf4f6 100644
--- a/core/executable_internal.mk
+++ b/core/executable_internal.mk
@@ -45,7 +45,7 @@
my_target_crtbegin_dynamic_o :=
my_target_crtbegin_static_o :=
my_target_crtend_o :=
-else ifdef LOCAL_USE_VNDK
+else ifeq (true,$(call module-in-vendor-or-product))
my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.vendor)
my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.vendor)
my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.vendor)
diff --git a/core/instrumentation_test_config_template.xml b/core/instrumentation_test_config_template.xml
index 9dfc001..379126c 100644
--- a/core/instrumentation_test_config_template.xml
+++ b/core/instrumentation_test_config_template.xml
@@ -24,7 +24,7 @@
</target_preparer>
<test class="com.android.tradefed.testtype.{TEST_TYPE}" >
- <option name="package" value="{PACKAGE}" />
+ {EXTRA_TEST_RUNNER_CONFIGS}<option name="package" value="{PACKAGE}" />
<option name="runner" value="{RUNNER}" />
</test>
</configuration>
diff --git a/core/java_common.mk b/core/java_common.mk
index c1ccd1a..65feb15 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -10,9 +10,7 @@
## Java version
###########################################################
# Use the LOCAL_JAVA_LANGUAGE_VERSION if it is set, otherwise
-# use one based on the LOCAL_SDK_VERSION. If it is < 24
-# pass "1.7" to the tools, if it is unset, >= 24 or "current"
-# pass "1.8".
+# use one based on the LOCAL_SDK_VERSION.
#
# The LOCAL_SDK_VERSION behavior is to ensure that, by default,
# code that is expected to run on older releases of Android
@@ -25,9 +23,7 @@
# Host modules always default to 1.9
LOCAL_JAVA_LANGUAGE_VERSION := 1.9
else
- ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_8_SUPPORT)))
- LOCAL_JAVA_LANGUAGE_VERSION := 1.7
- else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT)))
+ ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT)))
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT)))
LOCAL_JAVA_LANGUAGE_VERSION := 1.9
diff --git a/core/local_vendor_product.mk b/core/local_vendor_product.mk
new file mode 100644
index 0000000..75982cd
--- /dev/null
+++ b/core/local_vendor_product.mk
@@ -0,0 +1,22 @@
+# LOCAL_USE_VNDK is not the variable which set by module directly, but there are some modules do so.
+# Set those as LOCAL_IN_VENDOR to make those modules work as expected.
+ifeq (true,$(LOCAL_USE_VNDK))
+ $(warning LOCAL_USE_VNDK must not be used. Please use LOCAL_VENDOR_MODULE or LOCAL_PRODUCT_MODULE instead.)
+ LOCAL_IN_VENDOR:=true
+endif
+
+# Set LOCAL_IN_VENDOR for modules going into vendor or odm partition and LOCAL_IN_PRODUCT for product
+# except for host modules. If LOCAL_SDK_VERSION is set, thats a more restrictive set, so they don't need
+# LOCAL_IN_VENDOR or LOCAL_IN_PRODUCT
+ifndef LOCAL_IS_HOST_MODULE
+ifndef LOCAL_SDK_VERSION
+ ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_OEM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
+ LOCAL_IN_VENDOR:=true
+ # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already
+ # set correctly before this is included.
+ endif
+ ifeq (true,$(LOCAL_PRODUCT_MODULE))
+ LOCAL_IN_PRODUCT:=true
+ endif
+endif
+endif
diff --git a/core/local_vndk.mk b/core/local_vndk.mk
deleted file mode 100644
index eb8f2c0..0000000
--- a/core/local_vndk.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-
-#Set LOCAL_USE_VNDK for modules going into product, vendor or odm partition, except for host modules
-#If LOCAL_SDK_VERSION is set, thats a more restrictive set, so they dont need LOCAL_USE_VNDK
-ifndef LOCAL_IS_HOST_MODULE
-ifndef LOCAL_SDK_VERSION
- ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_OEM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
- LOCAL_USE_VNDK:=true
- LOCAL_USE_VNDK_VENDOR:=true
- # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already
- # set correctly before this is included.
- endif
- ifdef PRODUCT_PRODUCT_VNDK_VERSION
- # Product modules also use VNDK when PRODUCT_PRODUCT_VNDK_VERSION is defined.
- ifeq (true,$(LOCAL_PRODUCT_MODULE))
- LOCAL_USE_VNDK:=true
- LOCAL_USE_VNDK_PRODUCT:=true
- endif
- endif
-endif
-endif
-
-# Verify LOCAL_USE_VNDK usage, and set LOCAL_SDK_VERSION if necessary
-
-ifdef LOCAL_IS_HOST_MODULE
- ifdef LOCAL_USE_VNDK
- $(shell echo $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Do not use LOCAL_USE_VNDK with host modules >&2)
- $(error done)
- endif
-endif
-ifdef LOCAL_USE_VNDK
- ifneq ($(LOCAL_USE_VNDK),true)
- $(shell echo '$(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_USE_VNDK must be "true" or empty, not "$(LOCAL_USE_VNDK)"' >&2)
- $(error done)
- endif
-
- ifdef LOCAL_SDK_VERSION
- $(shell echo $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_USE_VNDK must not be used with LOCAL_SDK_VERSION >&2)
- $(error done)
- endif
-endif
-
diff --git a/core/main.mk b/core/main.mk
index 348a964..b4ca2a4 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -225,20 +225,14 @@
# ADDITIONAL_VENDOR_PROPERTIES will be installed in vendor/build.prop if
# property_overrides_split_enabled is true. Otherwise it will be installed in
# /system/build.prop
+ifeq ($(KEEP_VNDK),true)
ifdef BOARD_VNDK_VERSION
- ifeq ($(KEEP_VNDK),true)
ifeq ($(BOARD_VNDK_VERSION),current)
ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION)
else
ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(BOARD_VNDK_VERSION)
endif
- endif
-
- # TODO(b/290159430): ro.vndk.deprecate is a temporal variable for deprecating VNDK.
- # This variable will be removed once ro.vndk.version can be removed.
- ifneq ($(KEEP_VNDK),true)
- ADDITIONAL_SYSTEM_PROPERTIES += ro.vndk.deprecate=true
- endif
+endif
endif
# Add cpu properties for bionic and ART.
@@ -311,10 +305,10 @@
ADDITIONAL_VENDOR_PROPERTIES += \
ro.board.api_level=$(BOARD_API_LEVEL)
endif
-# BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.
-ifdef BOARD_API_LEVEL_FROZEN
+# RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.
+ifdef RELEASE_BOARD_API_LEVEL_FROZEN
ADDITIONAL_VENDOR_PROPERTIES += \
- ro.board.api_frozen=$(BOARD_API_LEVEL_FROZEN)
+ ro.board.api_frozen=$(RELEASE_BOARD_API_LEVEL_FROZEN)
endif
# Set build prop. This prop is read by ota_from_target_files when generating OTA,
@@ -440,6 +434,8 @@
# To speedup startup of non-preopted builds, don't verify or compile the boot image.
ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.image-dex2oat-filter=extract
endif
+# b/323566535
+ADDITIONAL_SYSTEM_PROPERTIES += init.svc_debug.no_fatal.zygote=true
endif
## asan ##
@@ -1676,6 +1672,7 @@
$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \
$(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \
$(INSTALLED_PRODUCTIMAGE_TARGET) \
+ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \
$(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \
@@ -1721,10 +1718,8 @@
# dist_files only for putting your library into the dist directory with a full build.
.PHONY: dist_files
-ifeq ($(SOONG_COLLECT_JAVA_DEPS), true)
- $(call dist-for-goals, dist_files, $(SOONG_OUT_DIR)/module_bp_java_deps.json)
- $(call dist-for-goals, dist_files, $(PRODUCT_OUT)/module-info.json)
-endif
+$(call dist-for-goals, dist_files, $(SOONG_OUT_DIR)/module_bp_java_deps.json)
+$(call dist-for-goals, dist_files, $(PRODUCT_OUT)/module-info.json)
.PHONY: apps_only
ifeq ($(HOST_OS),darwin)
@@ -2002,17 +1997,6 @@
.PHONY: findbugs
findbugs: $(INTERNAL_FINDBUGS_HTML_TARGET) $(INTERNAL_FINDBUGS_XML_TARGET)
-LSDUMP_PATHS_FILE := $(PRODUCT_OUT)/lsdump_paths.txt
-
-.PHONY: findlsdumps
-# LSDUMP_PATHS is a list of tag:path.
-findlsdumps: $(LSDUMP_PATHS_FILE) $(foreach p,$(LSDUMP_PATHS),$(call word-colon,2,$(p)))
-
-$(LSDUMP_PATHS_FILE): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)
-$(LSDUMP_PATHS_FILE):
- @echo "Generate $@"
- @rm -rf $@ && echo -e "$(subst :,:$(space),$(subst $(space),\n,$(PRIVATE_LSDUMP_PATHS)))" > $@
-
.PHONY: check-elf-files
check-elf-files:
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index 57df911..62ef3df 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -97,6 +97,79 @@
)) \
)
+# Collect the on-device flags into a single file, similar to all_aconfig_declarations.
+required_aconfig_flags_files := \
+ $(sort $(foreach partition, $(filter $(IMAGES_TO_BUILD), $(_FLAG_PARTITIONS)), \
+ $(aconfig_flag_summaries_protobuf.$(partition)) \
+ ))
+
+.PHONY: device_aconfig_declarations
+device_aconfig_declarations: $(PRODUCT_OUT)/device_aconfig_declarations.pb
+$(eval $(call generate-partition-aconfig-flag-file, \
+ $(TARGET_OUT_FLAGS)/device_aconfig_declarations.pb, \
+ $(PRODUCT_OUT)/device_aconfig_declarations.pb, \
+ $(sort $(required_aconfig_flags_files)) \
+)) \
+
+# Create a set of storage file for each partition
+# $(1): built aconfig flags storage package map file (out)
+# $(2): built aconfig flags storage flag map file (out)
+# $(3): built aconfig flags storage flag val file (out)
+# $(4): installed aconfig flags storage package map file (out)
+# $(5): installed aconfig flags storage flag map file (out)
+# $(6): installed aconfig flags storage flag value file (out)
+# $(7): input aconfig files for the partition (in)
+define generate-partition-aconfig-storage-file
+$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
+$(eval $(strip $(1)): PRIVATE_IN := $(strip $(7)))
+$(strip $(1)): $(ACONFIG) $(strip $(7))
+ mkdir -p $$(dir $$(PRIVATE_OUT))
+ $$(if $$(PRIVATE_IN), \
+ $$(ACONFIG) create-storage --container "" --file package_map --out $$(PRIVATE_OUT) \
+ $$(addprefix --cache ,$$(PRIVATE_IN)), \
+ )
+ touch $$(PRIVATE_OUT)
+$(eval $(strip $(2)): PRIVATE_OUT := $(strip $(2)))
+$(eval $(strip $(2)): PRIVATE_IN := $(strip $(7)))
+$(strip $(2)): $(ACONFIG) $(strip $(7))
+ mkdir -p $$(dir $$(PRIVATE_OUT))
+ $$(if $$(PRIVATE_IN), \
+ $$(ACONFIG) create-storage --container "" --file flag_map --out $$(PRIVATE_OUT) \
+ $$(addprefix --cache ,$$(PRIVATE_IN)), \
+ )
+ touch $$(PRIVATE_OUT)
+$(eval $(strip $(3)): PRIVATE_OUT := $(strip $(3)))
+$(eval $(strip $(3)): PRIVATE_IN := $(strip $(7)))
+$(strip $(3)): $(ACONFIG) $(strip $(7))
+ mkdir -p $$(dir $$(PRIVATE_OUT))
+ $$(if $$(PRIVATE_IN), \
+ $$(ACONFIG) create-storage --container "" --file flag_val --out $$(PRIVATE_OUT) \
+ $$(addprefix --cache ,$$(PRIVATE_IN)), \
+ )
+ touch $$(PRIVATE_OUT)
+$(call copy-one-file, $(strip $(1)), $(4))
+$(call copy-one-file, $(strip $(2)), $(5))
+$(call copy-one-file, $(strip $(3)), $(6))
+endef
+
+ifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true)
+$(foreach partition, $(_FLAG_PARTITIONS), \
+ $(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/package.map) \
+ $(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/flag.map) \
+ $(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/flag.val) \
+ $(eval $(call generate-partition-aconfig-storage-file, \
+ $(TARGET_OUT_FLAGS)/$(partition)/package.map, \
+ $(TARGET_OUT_FLAGS)/$(partition)/flag.map, \
+ $(TARGET_OUT_FLAGS)/$(partition)/flag.val, \
+ $(aconfig_storage_package_map.$(partition)), \
+ $(aconfig_storage_flag_map.$(partition)), \
+ $(aconfig_storage_flag_val.$(partition)), \
+ $(sort $(foreach m,$(call register-names-for-partition, $(partition)), \
+ $(ALL_MODULES.$(m).ACONFIG_FILES) \
+ )), \
+ )) \
+)
+endif
# -----------------------------------------------------------------
# Install the ones we need for the configured product
@@ -104,6 +177,9 @@
$(sort $(foreach partition, $(filter $(IMAGES_TO_BUILD), $(_FLAG_PARTITIONS)), \
$(build_flag_summaries.$(partition)) \
$(aconfig_flag_summaries_protobuf.$(partition)) \
+ $(aconfig_storage_package_map.$(partition)) \
+ $(aconfig_storage_flag_map.$(partition)) \
+ $(aconfig_storage_flag_val.$(partition)) \
))
ALL_DEFAULT_INSTALLED_MODULES += $(required_flags_files)
@@ -116,8 +192,12 @@
# Clean up
required_flags_files:=
+required_aconfig_flags_files:=
$(foreach partition, $(_FLAG_PARTITIONS), \
$(eval build_flag_summaries.$(partition):=) \
$(eval aconfig_flag_summaries_protobuf.$(partition):=) \
+ $(eval aconfig_storage_package_map.$(partition):=) \
+ $(eval aconfig_storage_flag_map.$(partition):=) \
+ $(eval aconfig_storage_flag_val.$(partition):=) \
)
diff --git a/core/product.mk b/core/product.mk
index 5515a8a..60cab47 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -446,7 +446,6 @@
_product_list_vars += PRODUCT_AFDO_PROFILES
-_product_single_value_vars += PRODUCT_NEXT_RELEASE_HIDE_FLAGGED_API
_product_single_value_vars += PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE
_product_list_vars += PRODUCT_RELEASE_CONFIG_MAPS
@@ -455,6 +454,12 @@
_product_single_value_vars += PRODUCT_BUILD_FROM_SOURCE_STUB
+_product_list_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS
+
+_product_single_value_vars += PRODUCT_HIDDEN_API_EXPORTABLE_STUBS
+
+_product_single_value_vars += PRODUCT_EXPORT_RUNTIME_APIS
+
.KATI_READONLY := _product_single_value_vars _product_list_vars
_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
diff --git a/core/proguard.flags b/core/proguard.flags
index 9cbba0f..aa406b9 100644
--- a/core/proguard.flags
+++ b/core/proguard.flags
@@ -15,6 +15,13 @@
@com.android.internal.annotations.VisibleForTesting *;
}
+# Keep classes and members with platform @TestApi annotations, similar to
+# @VisibleForTesting.
+-keep @android.annotation.TestApi class *
+-keepclassmembers class * {
+ @android.annotation.TestApi *;
+}
+
# Keep classes and members with non-platform @VisibleForTesting annotations, but
# only within platform-defined packages. This avoids keeping external, library-specific
# test code that isn't actually needed for platform testing.
diff --git a/core/python_binary_host_mobly_test_config_template.xml b/core/python_binary_host_mobly_test_config_template.xml
index a6576cd..a986df2 100644
--- a/core/python_binary_host_mobly_test_config_template.xml
+++ b/core/python_binary_host_mobly_test_config_template.xml
@@ -13,13 +13,9 @@
<configuration description="Config for {MODULE} mobly test">
{EXTRA_CONFIGS}
- <device name="device1"></device>
- <device name="device2"></device>
+ <device name="AndroidRealDevice"></device>
+ <device name="AndroidRealDevice"></device>
- <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
- <!-- The mobly-par-file-name should match the module name -->
- <option name="mobly-par-file-name" value="{MODULE}" />
- <!-- Timeout limit in milliseconds for all test cases of the python binary -->
- <option name="mobly-test-timeout" value="300000" />
- </test>
+ <option name="mobly_pkg" key="file" value="{MODULE}" />
+ <test class="MoblyAospPackageTest" />
</configuration>
diff --git a/core/ravenwood_test_config_template.xml b/core/ravenwood_test_config_template.xml
new file mode 100644
index 0000000..16a22c0
--- /dev/null
+++ b/core/ravenwood_test_config_template.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<!-- This test config file is auto-generated. -->
+<configuration description="Runs {MODULE}">
+ <option name="test-suite-tag" value="ravenwood" />
+ <option name="test-suite-tag" value="ravenwood-tests" />
+
+ <option name="java-folder" value="prebuilts/jdk/jdk17/linux-x86/" />
+ <option name="use-ravenwood-resources" value="true" />
+ <option name="exclude-paths" value="java" />
+ <option name="socket-timeout" value="10000" />
+ <option name="null-device" value="true" />
+
+ {EXTRA_CONFIGS}
+
+ <test class="com.android.tradefed.testtype.IsolatedHostTest" >
+ <option name="jar" value="{MODULE}.jar" />
+ <option name="java-flags" value="--add-modules=jdk.compiler"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"/>
+ <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"/>
+
+ <!-- Needed for supporting ParcelFileDescriptor internals -->
+ <option name="java-flags" value="--add-exports=java.base/jdk.internal.access=ALL-UNNAMED"/>
+ </test>
+</configuration>
diff --git a/core/release_config.mk b/core/release_config.mk
index 1fb5747..8d19bc7 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -59,15 +59,51 @@
$(if $(filter $(map),$(config_map_files)),,$(eval config_map_files += $(map))) \
)
+# Declare an alias release-config
+#
+# This should be used to declare a release as an alias of another, meaning no
+# release config files should be present.
+#
+# $1 config name
+# $2 release config for which it is an alias
+define alias-release-config
+ $(call _declare-release-config,$(1),,$(2),true)
+endef
+
# Declare or extend a release-config.
#
+# The order of processing is:
+# 1. Recursively apply any overridden release configs. Only apply each config
+# the first time we reach it.
+# 2. Apply any files for this release config, in the order they were added to
+# the declaration.
+#
+# Example:
+# With these declarations:
+# $(declare-release-config foo, foo.scl)
+# $(declare-release-config bar, bar.scl, foo)
+# $(declare-release-config baz, baz.scl, bar)
+# $(declare-release-config bif, bif.scl, foo baz)
+# $(declare-release-config bop, bop.scl, bar baz)
+#
+# TARGET_RELEASE:
+# - bar will use: foo.scl bar.scl
+# - baz will use: foo.scl bar.scl baz.scl
+# - bif will use: foo.scl bar.scl baz.scl bif.scl
+# - bop will use: foo.scl bar.scl baz.scl bop.scl
+#
# $1 config name
# $2 release config files
-# $3 overridden release config. Only applied for $(TARGET_RELEASE), not in depth.
+# $3 overridden release config
define declare-release-config
- $(if $(strip $(2)),, \
- $(error declare-release-config: config $(strip $(1)) must have release config files) \
+ $(call _declare-release-config,$(1),$(2),$(3),)
+endef
+
+define _declare-release-config
+ $(if $(strip $(2)$(3)),, \
+ $(error declare-release-config: config $(strip $(1)) must have release config files, override another release config, or both) \
)
+ $(if $(strip $(4)),$(eval _all_release_configs.$(strip $(1)).ALIAS := true))
$(eval _all_release_configs := $(sort $(_all_release_configs) $(strip $(1))))
$(if $(strip $(3)), \
$(if $(filter $(_all_release_configs), $(strip $(3))),
@@ -113,16 +149,30 @@
# Don't sort this, use it in the order they gave us.
# Do allow duplicate entries, retaining only the first usage.
flag_value_files :=
-$(foreach r,$(_all_release_configs.$(TARGET_RELEASE).OVERRIDES) $(TARGET_RELEASE), \
+
+# Apply overrides recursively
+#
+# $1 release config that we override
+applied_releases :=
+define _apply-release-config-overrides
+$(foreach r,$(1), \
+ $(if $(filter $(r),$(applied_releases)),, \
+ $(foreach o,$(_all_release_configs.$(r).OVERRIDES),$(call _apply-release-config-overrides,$(o)))\
+ $(eval applied_releases += $(r))\
$(foreach f,$(_all_release_configs.$(r).FILES), \
$(if $(filter $(f),$(flag_value_files)),,$(eval flag_value_files += $(f)))\
)\
+ )\
)
-
+endef
+$(call _apply-release-config-overrides,$(TARGET_RELEASE))
# Unset variables so they can't use them
define declare-release-config
$(error declare-release-config can only be called from inside release_config_map.mk files)
endef
+define apply-release-config-overrides
+$(error invalid use of apply-release-config-overrides)
+endef
# TODO: Remove this check after enough people have sourced lunch that we don't
# need to worry about it trying to do get_build_vars TARGET_RELEASE. Maybe after ~9/2023
@@ -135,6 +185,11 @@
endif
.KATI_READONLY := TARGET_RELEASE
+# Verify that alias configs do not have config files.
+$(foreach r,$(_all_release_configs),\
+ $(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\
+ $(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\
+)))
$(foreach config, $(_all_release_configs), \
$(eval _all_release_configs.$(config).DECLARED_IN:= ) \
@@ -142,6 +197,7 @@
)
_all_release_configs:=
config_map_files:=
+applied_releases:=
# -----------------------------------------------------------------
diff --git a/core/shared_library_internal.mk b/core/shared_library_internal.mk
index 139de10..2f510d9 100644
--- a/core/shared_library_internal.mk
+++ b/core/shared_library_internal.mk
@@ -42,7 +42,7 @@
ifeq ($(LOCAL_NO_CRT),true)
my_target_crtbegin_so_o :=
my_target_crtend_so_o :=
-else ifdef LOCAL_USE_VNDK
+else ifeq ($(call module-in-vendor-or-product),true)
my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.vendor)
my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.vendor)
else
diff --git a/core/soong_cc_rust_prebuilt.mk b/core/soong_cc_rust_prebuilt.mk
index 94e1115..943ed30 100644
--- a/core/soong_cc_rust_prebuilt.mk
+++ b/core/soong_cc_rust_prebuilt.mk
@@ -99,7 +99,7 @@
include $(BUILD_SYSTEM)/link_type.mk
endif
-ifdef LOCAL_USE_VNDK
+ifeq ($(call module-in-vendor-or-product),true)
ifneq ($(LOCAL_VNDK_DEPEND_ON_CORE_VARIANT),true)
name_without_suffix := $(patsubst %.vendor,%,$(LOCAL_MODULE))
ifneq ($(name_without_suffix),$(LOCAL_MODULE))
@@ -128,8 +128,8 @@
ifdef LOCAL_INSTALLED_MODULE
ifdef LOCAL_SHARED_LIBRARIES
my_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
- ifdef LOCAL_USE_VNDK
- ifdef LOCAL_USE_VNDK_PRODUCT
+ ifeq ($(call module-in-vendor-or-product),true)
+ ifdef LOCAL_IN_PRODUCT
my_shared_libraries := $(foreach l,$(my_shared_libraries),\
$(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))
else
@@ -143,8 +143,8 @@
ifdef LOCAL_DYLIB_LIBRARIES
my_dylibs := $(LOCAL_DYLIB_LIBRARIES)
# Treat these as shared library dependencies for installation purposes.
- ifdef LOCAL_USE_VNDK
- ifdef LOCAL_USE_VNDK_PRODUCT
+ ifeq ($(call module-in-vendor-or-product),true)
+ ifdef LOCAL_IN_PRODUCT
my_dylibs := $(foreach l,$(my_dylibs),\
$(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))
else
diff --git a/core/soong_config.mk b/core/soong_config.mk
index b6ce2a7..b43a952 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -47,7 +47,6 @@
$(call add_json_str, Platform_version_known_codenames, $(PLATFORM_VERSION_KNOWN_CODENAMES))
$(call add_json_bool, Release_aidl_use_unfrozen, $(RELEASE_AIDL_USE_UNFROZEN))
-$(call add_json_bool, Release_expose_flagged_api, $(RELEASE_EXPOSE_FLAGGED_API))
$(call add_json_str, Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))
@@ -148,8 +147,10 @@
$(call add_json_str, BtConfigIncludeDir, $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))
$(call add_json_list, DeviceKernelHeaders, $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
$(call add_json_str, VendorApiLevel, $(BOARD_API_LEVEL))
+ifeq ($(KEEP_VNDK),true)
$(call add_json_str, DeviceVndkVersion, $(BOARD_VNDK_VERSION))
$(call add_json_str, Platform_vndk_version, $(PLATFORM_VNDK_VERSION))
+endif
$(call add_json_list, ExtraVndkVersions, $(PRODUCT_EXTRA_VNDK_VERSIONS))
$(call add_json_list, DeviceSystemSdkVersions, $(BOARD_SYSTEMSDK_VERSIONS))
$(call add_json_str, RecoverySnapshotVersion, $(RECOVERY_SNAPSHOT_VERSION))
@@ -229,7 +230,6 @@
$(call add_json_str, ProductSepolicyPrebuiltApiDir, $(BOARD_PRODUCT_PREBUILT_DIR))
$(call add_json_str, PlatformSepolicyVersion, $(PLATFORM_SEPOLICY_VERSION))
-$(call add_json_str, TotSepolicyVersion, $(TOT_SEPOLICY_VERSION))
$(call add_json_list, PlatformSepolicyCompatVersions, $(PLATFORM_SEPOLICY_COMPAT_VERSIONS))
$(call add_json_bool, ForceApexSymlinkOptimization, $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION)))
@@ -335,6 +335,8 @@
$(call add_json_bool, CheckVendorSeappViolations, $(filter true,$(CHECK_VENDOR_SEAPP_VIOLATIONS)))
+$(call add_json_list, BuildIgnoreApexContributionContents, $(sort $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS)))
+
$(call add_json_map, PartitionVarsForBazelMigrationOnlyDoNotUse)
$(call add_json_str, ProductDirectory, $(dir $(INTERNAL_PRODUCT)))
@@ -396,10 +398,12 @@
$(call add_json_list, ProductPackages, $(sort $(PRODUCT_PACKAGES)))
$(call end_json_map)
-$(call add_json_bool, NextReleaseHideFlaggedApi, $(filter true,$(PRODUCT_NEXT_RELEASE_HIDE_FLAGGED_API)))
-
$(call add_json_bool, BuildFromSourceStub, $(findstring true,$(PRODUCT_BUILD_FROM_SOURCE_STUB) $(BUILD_FROM_SOURCE_STUB)))
+$(call add_json_bool, HiddenapiExportableStubs, $(filter true,$(PRODUCT_HIDDEN_API_EXPORTABLE_STUBS)))
+
+$(call add_json_bool, ExportRuntimeApis, $(filter true,$(PRODUCT_EXPORT_RUNTIME_APIS)))
+
$(call json_end)
$(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 4e8e976..652ca97 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -124,7 +124,7 @@
$(eval _option := --allow-dup)\
)
-$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6)
+$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)
$(hide) echo Building $$@
$(hide) mkdir -p $$(dir $$@)
$(hide) rm -f $$@ && touch $$@
@@ -148,7 +148,10 @@
echo "$$(line)" >> $$@;\
)\
)
- $(hide) $(POST_PROCESS_PROPS) $$(_option) --sdk-version $(PLATFORM_SDK_VERSION) $$@ $(5)
+ $(hide) $(POST_PROCESS_PROPS) $$(_option) \
+ --sdk-version $(PLATFORM_SDK_VERSION) \
+ --kernel-version-file-for-uffd-gc "$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)" \
+ $$@ $(5)
$(hide) $(foreach file,$(strip $(6)),\
if [ -f "$(file)" ]; then\
cat $(file) >> $$@;\
diff --git a/core/tasks/fontchain_lint.mk b/core/tasks/fontchain_lint.mk
new file mode 100644
index 0000000..a4c396d
--- /dev/null
+++ b/core/tasks/fontchain_lint.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 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.
+
+# Run sanity tests on fonts on checkbuild
+checkbuild: fontchain_lint
+
+FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter
+ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
+CHECK_EMOJI := false
+else
+CHECK_EMOJI := true
+endif
+
+fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp
+
+.PHONY: fontchain_lint
+fontchain_lint: $(fontchain_lint_timestamp)
+
+fontchain_lint_deps := \
+ external/unicode/DerivedAge.txt \
+ external/unicode/emoji-data.txt \
+ external/unicode/emoji-sequences.txt \
+ external/unicode/emoji-variation-sequences.txt \
+ external/unicode/emoji-zwj-sequences.txt \
+ external/unicode/additions/emoji-data.txt \
+ external/unicode/additions/emoji-sequences.txt \
+ external/unicode/additions/emoji-zwj-sequences.txt \
+
+$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)
+ @echo Running fontchain lint
+ $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+ touch $@
diff --git a/core/tasks/general-tests-shared-libs.mk b/core/tasks/general-tests-shared-libs.mk
new file mode 100644
index 0000000..2405140
--- /dev/null
+++ b/core/tasks/general-tests-shared-libs.mk
@@ -0,0 +1,52 @@
+# 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.
+
+.PHONY: general-tests-shared-libs
+
+intermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests-shared-libs)
+
+general_tests_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip
+
+# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES,
+# to avoid warning about overriding commands.
+my_host_shared_lib_for_general_tests := \
+ $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
+ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))
+my_general_tests_shared_lib_files := \
+ $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
+ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES))
+
+my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files))
+
+$(general_tests_shared_libs_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(general_tests_shared_libs_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests)
+$(general_tests_shared_libs_zip) : PRIVATE_general_host_shared_libs_zip := $(general_tests_shared_libs_zip)
+$(general_tests_shared_libs_zip) : $(my_host_shared_lib_for_general_tests) $(SOONG_ZIP)
+ rm -rf $(PRIVATE_INTERMEDIATES_DIR)
+ mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools
+ $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \
+ done
+ grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true
+ $(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \
+ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list
+
+general-tests-shared-libs: $(general_tests_shared_libs_zip)
+$(call dist-for-goals, general-tests-shared-libs, $(general_tests_shared_libs_zip))
+
+$(call declare-1p-container,$(general_tests_shared_libs_zip),)
+$(call declare-container-license-deps,$(general_tests_shared_libs_zip),$(my_host_shared_lib_for_general_tests),$(PRODUCT_OUT)/:/)
+
+intermediates_dir :=
+general_tests_shared_libs_zip :=
diff --git a/core/tasks/general-tests.mk b/core/tasks/general-tests.mk
index fb2a6be..cae71e4 100644
--- a/core/tasks/general-tests.mk
+++ b/core/tasks/general-tests.mk
@@ -24,21 +24,8 @@
# Create an artifact to include a list of test config files in general-tests.
general_tests_list_zip := $(PRODUCT_OUT)/general-tests_list.zip
-# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES,
-# to avoid warning about overriding commands.
-my_host_shared_lib_for_general_tests := \
- $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
- $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))
-my_general_tests_shared_lib_files := \
- $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
- $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES))
-
-my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files))
-
# Create an artifact to include all test config files in general-tests.
general_tests_configs_zip := $(PRODUCT_OUT)/general-tests_configs.zip
-# Create an artifact to include all shared librariy files in general-tests.
-general_tests_host_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip
# Copy kernel test modules to testcases directories
include $(BUILD_SYSTEM)/tasks/tools/vts-kernel-tests.mk
@@ -50,16 +37,17 @@
.PHONY: vts_kernel_ltp_tests
vts_kernel_ltp_tests: $(copy_ltp_tests)
+general_tests_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip
+
+$(general_tests_zip) : $(general_tests_shared_libs_zip)
$(general_tests_zip) : $(copy_ltp_tests)
$(general_tests_zip) : PRIVATE_KERNEL_LTP_HOST_OUT := $(kernel_ltp_host_out)
$(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip)
-$(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_host_shared_libs_zip)
+$(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip)
$(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools)
$(general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
-$(general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests)
$(general_tests_zip) : PRIVATE_general_tests_configs_zip := $(general_tests_configs_zip)
-$(general_tests_zip) : PRIVATE_general_host_shared_libs_zip := $(general_tests_host_shared_libs_zip)
-$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(general_tests_tools) $(my_host_shared_lib_for_general_tests) $(SOONG_ZIP)
+$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(general_tests_tools) $(SOONG_ZIP)
rm -rf $(PRIVATE_INTERMEDIATES_DIR)
rm -f $@ $(PRIVATE_general_tests_list_zip)
mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools
@@ -69,11 +57,6 @@
grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true
grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true
grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true
- $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
- echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/host.list; \
- echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \
- done
- grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true
cp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/
$(SOONG_ZIP) -d -o $@ \
-P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \
@@ -83,21 +66,19 @@
$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_configs_zip) \
-P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \
-P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list
- $(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \
- -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list
grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list
grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list
$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list
general-tests: $(general_tests_zip)
-$(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_host_shared_libs_zip))
+$(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_shared_libs_zip))
$(call declare-1p-container,$(general_tests_zip),)
-$(call declare-container-license-deps,$(general_tests_zip),$(COMPATIBILITY.general-tests.FILES) $(general_tests_tools) $(my_host_shared_lib_for_general_tests),$(PRODUCT_OUT)/:/)
+$(call declare-container-license-deps,$(general_tests_zip),$(COMPATIBILITY.general-tests.FILES) $(general_tests_tools),$(PRODUCT_OUT)/:/)
intermediates_dir :=
general_tests_tools :=
general_tests_zip :=
general_tests_list_zip :=
general_tests_configs_zip :=
-general_tests_host_shared_libs_zip :=
+general_tests_shared_libs_zip :=
diff --git a/core/version_util.mk b/core/version_util.mk
index dfa0277..6cda0fc 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -31,6 +31,7 @@
# PLATFORM_VNDK_VERSION
# PLATFORM_SYSTEMSDK_VERSIONS
# PLATFORM_VERSION_LAST_STABLE
+# PLATFORM_VERSION_KNOWN_CODENAMES
#
# Look for an optional file containing overrides of the defaults,
@@ -95,17 +96,10 @@
PLATFORM_VERSION_LAST_STABLE := $(RELEASE_PLATFORM_VERSION_LAST_STABLE)
.KATI_READONLY := PLATFORM_VERSION_LAST_STABLE
-
-# This are all known codenames. Should this move into the release config?
-PLATFORM_VERSION_KNOWN_CODENAMES := \
-Base Base11 Cupcake Donut Eclair Eclair01 EclairMr1 Froyo Gingerbread GingerbreadMr1 \
-Honeycomb HoneycombMr1 HoneycombMr2 IceCreamSandwich IceCreamSandwichMr1 \
-JellyBean JellyBeanMr1 JellyBeanMr2 Kitkat KitkatWatch Lollipop LollipopMr1 M N NMr1 O OMr1 P \
-Q R S Sv2 Tiramisu UpsideDownCake VanillaIceCream
-
-# Convert from space separated list to comma separated
-PLATFORM_VERSION_KNOWN_CODENAMES := \
- $(call normalize-comma-list,$(PLATFORM_VERSION_KNOWN_CODENAMES))
+ifdef PLATFORM_VERSION_KNOWN_CODENAMES
+ $(error Do not set PLATFORM_VERSION_KNOWN_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES. value: $(PLATFORM_VERSION_KNOWN_CODENAMES))
+endif
+PLATFORM_VERSION_KNOWN_CODENAMES := $(RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES)
.KATI_READONLY := PLATFORM_VERSION_KNOWN_CODENAMES
ifndef PLATFORM_VERSION
@@ -157,21 +151,23 @@
endif
.KATI_READONLY := DEFAULT_APP_TARGET_SDK
-ifndef PLATFORM_VNDK_VERSION
- # This is the definition of the VNDK version for the current VNDK libraries.
- # With trunk stable, VNDK will not be frozen but deprecated.
- # This version will be removed with the VNDK deprecation.
- ifeq (REL,$(PLATFORM_VERSION_CODENAME))
- ifdef RELEASE_PLATFORM_VNDK_VERSION
- PLATFORM_VNDK_VERSION := $(RELEASE_PLATFORM_VNDK_VERSION)
+ifeq ($(KEEP_VNDK),true)
+ ifndef PLATFORM_VNDK_VERSION
+ # This is the definition of the VNDK version for the current VNDK libraries.
+ # With trunk stable, VNDK will not be frozen but deprecated.
+ # This version will be removed with the VNDK deprecation.
+ ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+ ifdef RELEASE_PLATFORM_VNDK_VERSION
+ PLATFORM_VNDK_VERSION := $(RELEASE_PLATFORM_VNDK_VERSION)
+ else
+ PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
+ endif
else
- PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
+ PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
endif
- else
- PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
endif
+ .KATI_READONLY := PLATFORM_VNDK_VERSION
endif
-.KATI_READONLY := PLATFORM_VNDK_VERSION
ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
# This is the oldest version of system SDK that the platform supports. Contrary
diff --git a/envsetup.sh b/envsetup.sh
index 5aa11c7..db21188 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -254,7 +254,7 @@
# Note: on windows/cygwin, ANDROID_LUNCH_BUILD_PATHS will contain spaces
# due to "C:\Program Files" being in the path.
- # Handle compat with the old ANDROID_BUILD_PATHS variable.
+ # Handle compat with the old ANDROID_BUILD_PATHS variable.
# TODO: Remove this after we think everyone has lunched again.
if [ -z "$ANDROID_LUNCH_BUILD_PATHS" -a -n "$ANDROID_BUILD_PATHS" ] ; then
ANDROID_LUNCH_BUILD_PATHS="$ANDROID_BUILD_PATHS"
@@ -366,7 +366,8 @@
fi
# And in with the new...
- ANDROID_GLOBAL_BUILD_PATHS=$T/build/bazel/bin
+ ANDROID_GLOBAL_BUILD_PATHS=$T/build/soong/bin
+ ANDROID_GLOBAL_BUILD_PATHS+=:$T/build/bazel/bin
ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools
@@ -1886,6 +1887,11 @@
>&2 echo "Couldn't locate the top of the tree. Try setting TOP."
return 1
fi
+ local ret=$?
+ if [[ ret -eq 0 && -z "${ANDROID_QUIET_BUILD:-}" && -n "${ANDROID_BUILD_BANNER}" ]]; then
+ echo "${ANDROID_BUILD_BANNER}"
+ fi
+ return $ret
)
function m()
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index 01ebe56..c3878b8 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -21,8 +21,10 @@
# the devices with metadata parition
BOARD_USES_METADATA_PARTITION := true
+ifeq ($(KEEP_VNDK),true)
# Default is current, but allow devices to override vndk version if needed.
BOARD_VNDK_VERSION ?= current
+endif
# 64 bit mediadrmserver
TARGET_ENABLE_MEDIADRM_64 := true
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
index 76b1c58..07eb96d 100644
--- a/target/product/AndroidProducts.mk
+++ b/target/product/AndroidProducts.mk
@@ -67,6 +67,7 @@
$(LOCAL_DIR)/mainline_system_x86_arm.mk \
$(LOCAL_DIR)/ndk.mk \
$(LOCAL_DIR)/sdk.mk \
+ $(LOCAL_DIR)/sdk_with_runtime_apis.mk \
endif
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 098ed27..3840e1f 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -18,6 +18,7 @@
PRODUCT_PACKAGES += \
abx \
adbd_system_api \
+ aflags \
am \
android.hidl.base-V1.0-java \
android.hidl.manager-V1.0-java \
@@ -70,7 +71,7 @@
com.android.scheduling \
com.android.sdkext \
com.android.tethering \
- com.android.tzdata \
+ $(RELEASE_PACKAGE_TZDATA_MODULE) \
com.android.uwb \
com.android.virt \
com.android.wifi \
@@ -94,7 +95,6 @@
framework-graphics \
framework-minus-apex \
framework-minus-apex-install-dependencies \
- framework-nfc \
framework-res \
framework-sysconfig.xml \
fsck.erofs \
@@ -127,6 +127,7 @@
ip \
iptables \
javax.obex \
+ kcmdlinectrl \
keystore2 \
credstore \
ld.mc \
@@ -201,6 +202,7 @@
libui \
libusbhost \
libutils \
+ libvintf_jni \
libvulkan \
libwilhelm \
linker \
@@ -222,6 +224,7 @@
mke2fs \
mkfs.erofs \
monkey \
+ misctrl \
mtectrl \
ndc \
netd \
@@ -288,6 +291,13 @@
wifi.rc \
wm \
+# When we release crashrecovery module
+ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)
+ PRODUCT_PACKAGES += \
+ com.android.crashrecovery \
+
+endif
+
# These packages are not used on Android TV
ifneq ($(PRODUCT_IS_ATV),true)
PRODUCT_PACKAGES += \
@@ -302,6 +312,21 @@
endif
+# Check if the build supports NFC apex or not
+ifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci)
+ PRODUCT_PACKAGES += \
+ framework-nfc \
+ NfcNci
+else
+ PRODUCT_PACKAGES += \
+ com.android.nfcservices
+endif
+
+ifeq ($(RELEASE_USE_WEBVIEW_BOOTSTRAP_MODULE),true)
+ PRODUCT_PACKAGES += \
+ com.android.webview.bootstrap
+endif
+
# VINTF data for system image
PRODUCT_PACKAGES += \
system_manifest.xml \
@@ -424,6 +449,7 @@
logpersist.start \
logtagd.rc \
ot-cli-ftd \
+ ot-ctl \
procrank \
profcollectd \
profcollectctl \
diff --git a/target/product/base_system_ext.mk b/target/product/base_system_ext.mk
index d8c1863..76f008f 100644
--- a/target/product/base_system_ext.mk
+++ b/target/product/base_system_ext.mk
@@ -20,6 +20,7 @@
fs_config_files_system_ext \
group_system_ext \
passwd_system_ext \
+ SatelliteClient \
selinux_policy_system_ext \
system_ext_manifest.xml \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 55fcf2f..dca9baa 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -50,7 +50,6 @@
PRODUCT_BOOT_JARS += \
framework-minus-apex \
framework-graphics \
- framework-nfc \
ext \
telephony-common \
voip-common \
@@ -75,6 +74,7 @@
com.android.media:updatable-media \
com.android.mediaprovider:framework-mediaprovider \
com.android.mediaprovider:framework-pdf \
+ com.android.mediaprovider:framework-pdf-v \
com.android.ondevicepersonalization:framework-ondevicepersonalization \
com.android.os.statsd:framework-statsd \
com.android.permission:framework-permission \
@@ -88,11 +88,28 @@
com.android.virt:framework-virtualization \
com.android.wifi:framework-wifi \
+# When we release crashrecovery module
+ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)
+ PRODUCT_APEX_BOOT_JARS += \
+ com.android.crashrecovery:framework-crashrecovery \
+
+endif
+
+# Check if the build supports NFC apex or not
+ifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci)
+ PRODUCT_BOOT_JARS += \
+ framework-nfc
+else
+ PRODUCT_APEX_BOOT_JARS := \
+ com.android.nfcservices:framework-nfc
+endif
+
# TODO(b/308174306): Adjust this after multiple prebuilts version is supported.
# APEX boot jars that are not in prebuilt apexes.
# Keep the list sorted by module names and then library names.
PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY := \
com.android.mediaprovider:framework-pdf \
+ com.android.mediaprovider:framework-pdf-v \
# List of system_server classpath jars delivered via apex.
# Keep the list sorted by module names and then library names.
@@ -109,6 +126,17 @@
com.android.permission:service-permission \
com.android.rkpd:service-rkp \
+# When we release crashrecovery module
+ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)
+ PRODUCT_APEX_SYSTEM_SERVER_JARS += \
+ com.android.crashrecovery:service-crashrecovery \
+
+endif
+
+ifeq ($(RELEASE_AVF_ENABLE_LLPVM_CHANGES),true)
+ PRODUCT_APEX_SYSTEM_SERVER_JARS += com.android.virt:service-virtualization
+endif
+
# Use $(wildcard) to avoid referencing the profile in thin manifests that don't have the
# art project.
ifneq (,$(wildcard art))
diff --git a/target/product/fullmte.mk b/target/product/fullmte.mk
index 5e2a694..b622496 100644
--- a/target/product/fullmte.mk
+++ b/target/product/fullmte.mk
@@ -20,8 +20,7 @@
# For more details, see:
# https://source.android.com/docs/security/test/memory-safety/arm-mte
ifeq ($(filter memtag_heap,$(SANITIZE_TARGET)),)
- # TODO(b/292478827): Re-enable memtag_stack when new toolchain rolls.
- SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap)
+ SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap memtag_stack)
SANITIZE_TARGET_DIAG := $(strip $(SANITIZE_TARGET_DIAG) memtag_heap)
endif
PRODUCT_PRODUCT_PROPERTIES += persist.arm64.memtag.default=sync
diff --git a/target/product/generic_system.mk b/target/product/generic_system.mk
index 38efde4..19ec86d 100644
--- a/target/product/generic_system.mk
+++ b/target/product/generic_system.mk
@@ -103,6 +103,12 @@
libaudiopolicyengineconfigurable \
libpolicy-subsystem
+# Add all of the packages used to support older/upgrading devices
+# These can be removed as we drop support for the older API levels
+PRODUCT_PACKAGES += \
+ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29) \
+ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33) \
+ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)
# Include all zygote init scripts. "ro.zygote" will select one of them.
PRODUCT_COPY_FILES += \
diff --git a/target/product/go_defaults_common.mk b/target/product/go_defaults_common.mk
index ba0912c..5218f29 100644
--- a/target/product/go_defaults_common.mk
+++ b/target/product/go_defaults_common.mk
@@ -49,6 +49,3 @@
# use the go specific handheld_core_hardware.xml from frameworks
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/go_handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml
-
-# Dedupe VNDK libraries with identical core variants.
-TARGET_VNDK_USE_CORE_VARIANT := true
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 007aabd..f348fbb 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -7,6 +7,7 @@
#####################################################################
# This is the up-to-date list of vndk libs.
LATEST_VNDK_LIB_LIST := $(LOCAL_PATH)/current.txt
+ifeq ($(KEEP_VNDK),true)
UNFROZEN_VNDK := true
ifeq (REL,$(PLATFORM_VERSION_CODENAME))
# Use frozen vndk lib list only if "34 >= PLATFORM_VNDK_VERSION"
@@ -18,6 +19,7 @@
UNFROZEN_VNDK :=
endif
endif
+endif
#####################################################################
# Check the generate list against the latest list stored in the
@@ -35,6 +37,8 @@
check-vndk-list: ;
else ifeq ($(TARGET_SKIP_CURRENT_VNDK),true)
check-vndk-list: ;
+else ifeq ($(BOARD_VNDK_VERSION),)
+check-vndk-list: ;
else
check-vndk-list: $(check-vndk-list-timestamp)
ifneq ($(SKIP_ABI_CHECKS),true)
@@ -105,6 +109,38 @@
@chmod a+x $@
#####################################################################
+# ABI reference dumps.
+
+# LSDUMP_PATHS is a list of tag:path. They are written to LSDUMP_PATHS_FILE.
+LSDUMP_PATHS_FILE := $(PRODUCT_OUT)/lsdump_paths.txt
+
+$(LSDUMP_PATHS_FILE): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)
+$(LSDUMP_PATHS_FILE):
+ @echo "Generate $@"
+ @rm -rf $@ && echo -e "$(subst :,:$(space),$(subst $(space),\n,$(PRIVATE_LSDUMP_PATHS)))" > $@
+
+# $(1): A list of tags.
+# $(2): A list of tag:path.
+# Return the file paths of the ABI dumps that match the tags.
+define filter-abi-dump-paths
+$(eval tag_patterns := $(addsuffix :%,$(1)))
+$(patsubst $(tag_patterns),%,$(filter $(tag_patterns),$(2)))
+endef
+
+# Subsets of LSDUMP_PATHS.
+.PHONY: findlsdumps_LLNDK
+findlsdumps_LLNDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,LLNDK,$(LSDUMP_PATHS))
+
+.PHONY: findlsdumps_NDK
+findlsdumps_NDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,NDK,$(LSDUMP_PATHS))
+
+.PHONY: findlsdumps_PLATFORM
+findlsdumps_PLATFORM: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,PLATFORM,$(LSDUMP_PATHS))
+
+.PHONY: findlsdumps
+findlsdumps: $(LSDUMP_PATHS_FILE) $(foreach p,$(LSDUMP_PATHS),$(call word-colon,2,$(p)))
+
+#####################################################################
# Check that all ABI reference dumps have corresponding
# NDK/VNDK/PLATFORM libraries.
@@ -119,12 +155,15 @@
# $(1): A list of tags.
# $(2): A list of tag:path.
# Return the file names of the ABI dumps that match the tags.
-define filter-abi-dump-paths
-$(eval tag_patterns := $(foreach tag,$(1),$(tag):%))
-$(notdir $(patsubst $(tag_patterns),%,$(filter $(tag_patterns),$(2))))
+define filter-abi-dump-names
+$(notdir $(call filter-abi-dump-paths,$(1),$(2)))
endef
-VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(PLATFORM_VNDK_VERSION)
+ifdef RELEASE_BOARD_API_LEVEL
+ VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(RELEASE_BOARD_API_LEVEL)
+else
+ VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(PLATFORM_VNDK_VERSION)
+endif
ifeq (REL,$(PLATFORM_VERSION_CODENAME))
NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/$(PLATFORM_SDK_VERSION)
PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_SDK_VERSION)
@@ -145,20 +184,21 @@
$(check-vndk-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES)
$(check-vndk-abi-dump-list-timestamp):
$(eval added_vndk_abi_dumps := $(strip $(sort $(filter-out \
- $(call filter-abi-dump-paths,VNDK-SP VNDK-core,$(PRIVATE_LSDUMP_PATHS)), \
+ $(call filter-abi-dump-names,LLNDK VNDK-SP VNDK-core,$(PRIVATE_LSDUMP_PATHS)), \
$(notdir $(VNDK_ABI_DUMPS))))))
$(if $(added_vndk_abi_dumps), \
echo -e "Found unexpected ABI reference dump files under $(VNDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(VNDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_vndk_abi_dumps)) ')' -delete\` to delete the dump files.")
$(eval added_ndk_abi_dumps := $(strip $(sort $(filter-out \
- $(call filter-abi-dump-paths,NDK,$(PRIVATE_LSDUMP_PATHS)) \
+ $(call filter-abi-dump-names,NDK,$(PRIVATE_LSDUMP_PATHS)) \
$(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)), \
$(notdir $(NDK_ABI_DUMPS))))))
$(if $(added_ndk_abi_dumps), \
echo -e "Found unexpected ABI reference dump files under $(NDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(NDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_ndk_abi_dumps)) ')' -delete\` to delete the dump files.")
+ # TODO(b/314010764): Remove LLNDK tag after PLATFORM_SDK_VERSION is upgraded to 35.
$(eval added_platform_abi_dumps := $(strip $(sort $(filter-out \
- $(call filter-abi-dump-paths,LLNDK PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \
+ $(call filter-abi-dump-names,LLNDK PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \
$(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)), \
$(notdir $(PLATFORM_ABI_DUMPS))))))
$(if $(added_platform_abi_dumps), \
@@ -199,25 +239,14 @@
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
-_vndk_versions :=
-ifeq ($(filter com.android.vndk.current.on_vendor, $(PRODUCT_PACKAGES)),)
- _vndk_versions += $(if $(call math_is_number,$(PLATFORM_VNDK_VERSION)),\
- $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),\
- $(if $(call math_lt,$(vndk_ver),$(PLATFORM_VNDK_VERSION)),$(vndk_ver))),\
- $(PRODUCT_EXTRA_VNDK_VERSIONS))
-endif
-ifneq ($(BOARD_VNDK_VERSION),current)
- _vndk_versions += $(BOARD_VNDK_VERSION)
-endif
+
LOCAL_MODULE := vndk_apex_snapshot_package
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_REQUIRED_MODULES := $(foreach vndk_ver,$(_vndk_versions),com.android.vndk.v$(vndk_ver))
+LOCAL_REQUIRED_MODULES := $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver))
include $(BUILD_PHONY_PACKAGE)
-_vndk_versions :=
-
#####################################################################
# Define Phony module to install LLNDK modules which are installed in
# the system image
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index fc5db6a..a581324 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -89,4 +89,13 @@
ro.crypto.metadata_init_delete_all_keys.enabled=false \
# Window Extensions
-$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
\ No newline at end of file
+ifneq ($(PRODUCT_IS_ATV),true)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
+endif
+
+# A GSI is to be mixed with different boot images. That means we can't determine
+# the kernel version when building a GSI.
+# Assume the device supports UFFD. If it doesn't, the ART runtime will fall back
+# to CC, and odrefresh will regenerate core dexopt artifacts on the first boot,
+# so this is okay.
+PRODUCT_ENABLE_UFFD_GC := true
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index 3acf1e6..b5292d2 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -73,7 +73,6 @@
UserDictionaryProvider \
VpnDialogs \
vr \
- $(RELEASE_PACKAGE_NFC_STACK)
PRODUCT_SYSTEM_SERVER_APPS += \
diff --git a/target/product/mainline_sdk.mk b/target/product/mainline_sdk.mk
index cb23bc8..10bb0a0 100644
--- a/target/product/mainline_sdk.mk
+++ b/target/product/mainline_sdk.mk
@@ -17,6 +17,4 @@
PRODUCT_BRAND := Android
PRODUCT_DEVICE := mainline_sdk
-PRODUCT_NEXT_RELEASE_HIDE_FLAGGED_API := true
-
PRODUCT_BUILD_FROM_SOURCE_STUB := true
\ No newline at end of file
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index b9ccad3..650f8e9 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -29,6 +29,4 @@
PRODUCT_BRAND := Android
PRODUCT_DEVICE := mainline_x86
-PRODUCT_NEXT_RELEASE_HIDE_FLAGGED_API := true
-
PRODUCT_BUILD_FROM_SOURCE_STUB := true
\ No newline at end of file
diff --git a/target/product/sdk_with_runtime_apis.mk b/target/product/sdk_with_runtime_apis.mk
new file mode 100644
index 0000000..e80b4fb
--- /dev/null
+++ b/target/product/sdk_with_runtime_apis.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
+
+PRODUCT_NAME := sdk_with_runtime_apis
+
+PRODUCT_HIDDEN_API_EXPORTABLE_STUBS := true
+PRODUCT_EXPORT_RUNTIME_APIS := true
\ No newline at end of file
diff --git a/target/product/security/nfc.pk8 b/target/product/security/nfc.pk8
new file mode 100644
index 0000000..4a5e1b7
--- /dev/null
+++ b/target/product/security/nfc.pk8
Binary files differ
diff --git a/target/product/security/nfc.x509.pem b/target/product/security/nfc.x509.pem
new file mode 100644
index 0000000..e6bff6a
--- /dev/null
+++ b/target/product/security/nfc.x509.pem
@@ -0,0 +1,29 @@
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIUC94q348hFaPm2jow3R84ZjNFc3EwDQYJKoZIhvcNAQELBQAwfDELMAkG
+A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDAS
+BgNVBAoTC0dvb2dsZSBJbmMuMRAwDgYDVQQLEwdBbmRyb2lkMRgwFgYDVQQDDA9jb21fYW5kcm9p
+ZF9uZmMwIBcNMjMxMTAxMjEzNzE1WhgPMjA1MzExMDEyMTM3MTVaMHwxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29n
+bGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEYMBYGA1UEAwwPY29tX2FuZHJvaWRfbmZjMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArpgwKyLDl8M3KRb1Fxs3P2mnt81sB3uGZs44R6C6
+CwFhiiACOmEQBcm79BUKBMrE9dUbyOL/GKluNzD026UsDE+N2wDQ8siTxaljDAwhZBpurhOu4uH8
+BKJOzoczAlJFMHpFIMCKQXwotMjT93BuhlSo024Q5QDd2j7Gajk21TkkyQNlBOiyEpKkrRPBuArw
+2knqhuX+nLYkJ5roANaJVDsiKMDG/mKnjwAndrgVbBiKaOdfuRd+pJleN3LUkAfYHHBqlOJnPGSI
+jfYK+9TjsIEYVEOb4SMI3CbWwHfOdEIBgz3IPqMtamEnbZHNlfVWURTNGAF2co+DF3TDGDEjraK4
+R5pXDk/W+4Ex77wQPCIT+d981zkbTpgsPXvZmsBzCYMw6tYksPj86fSVJUrJhlibDk4YHVFsF7OK
+arNf044yPhZ+WUIDqWJ6GB0GU8LWGbbe8iaP0ro9Q1DYgYc6buYWIcX81XZO+hHgWtUb2rNqIUsp
+/4DmT1vgz7TiMWcY7pjrHlNHtVf4jC+OU2c+p8u4XUGQxdIKGgZSoHldtAcnwqGuIpat9lS+gtVl
+vJUp8w3Z2gv4q/bBVZ3NNasA1d3HXVQUWiwszcjiVvoSRa/AlMVUGureGRbsiKsyHisYp9rxk1DB
+dPS9h7tMs/5rV6RM2nZfdfQr71zX9ieSoz0CAwEAAaNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4E
+FgQU9v9SL0QIU9fq7aB70/jqVBLmZJUwHwYDVR0jBBgwFoAU9v9SL0QIU9fq7aB70/jqVBLmZJUw
+DQYJKoZIhvcNAQELBQADggIBAExt2/NFt+2IhC5+/cgi8hzvwZKQyml1MQ9pjrkfQy0JGzGTOPDr
++NPuT5jh/SOfGzdBsGVs3hvK7hFvXsFEWwVQaDOkKhcruks+g7FxldhXC2z9iKgjNrOeUXoE7SiE
+zXA/p1KiBcRL3MSMbg/iQjQVxlJky4BDo39SoEgDeL9+i7L4cBwZQ2LBBLPIOdE7G/cG1Q6UE+KN
+/mnz0kk3+FIg0q4szXDxH+o2V4ZYHSOy8P6TBW8gEQ71RGOnh0wtaTIx2RD/zqJAi+BzWMLsC636
+TNMmqKassG4MH445ul2w0ZzOClJ4gkl1e7dtK7/Kes4kcLOI/i4JHLOcydEqum+t8gQtMYyGM1Kv
+mVpC3hEv2pYwFfhg8hg31MljZmLD761kLOLfw98N350h6dNdQ0jci/3rqbbjVinVQoQSVEzJcA9Q
+gQhRLKHiO7oRmht6ilRLFtGZd/PwIMWMNqksTfVM5frMIIZXdfew+efHIJ7X+ZgJu3tGWcbFYFte
+K/BbmPLnp3aAGg/wwU1dqwCANf53oUc4ZzqRm9eovlVsrFiRM/DGt2/t4ujorU6Uwwt2+n05QU7b
+7PXhc7bTP6adUWMNMxSNIPo6wHmsTb2pCg+K5LuNMFJzXcoI3uBW9Qu4M/tLRv4kRKZzphqUbX+e
+/5hW2myw2BvbdwWFrz6XBgkz
+-----END CERTIFICATE-----
diff --git a/target/product/virtual_ab_ota/android_t_baseline.mk b/target/product/virtual_ab_ota/android_t_baseline.mk
index f862485..af0f7a9 100644
--- a/target/product/virtual_ab_ota/android_t_baseline.mk
+++ b/target/product/virtual_ab_ota/android_t_baseline.mk
@@ -21,4 +21,4 @@
# All U+ launching devices should instead use vabc_features.mk.
$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk)
-PRODUCT_VIRTUAL_AB_COW_VERSION := 2
+PRODUCT_VIRTUAL_AB_COW_VERSION ?= 2
diff --git a/teams/Android.bp b/teams/Android.bp
new file mode 100644
index 0000000..bae8e80
--- /dev/null
+++ b/teams/Android.bp
@@ -0,0 +1,4366 @@
+//
+// Copyright (C) 2020 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+team {
+ name: "trendy_team_qmc_pss",
+
+ // go/trendy/manage/engineers/6342544841375744
+ trendy_team_id: "6342544841375744",
+}
+
+team {
+ name: "trendy_team_cpu_team",
+
+ // go/trendy/manage/engineers/5119059747307520
+ trendy_team_id: "5119059747307520",
+}
+
+team {
+ name: "trendy_team_pwg_mobile",
+
+ // go/trendy/manage/engineers/4869274588315648
+ trendy_team_id: "4869274588315648",
+}
+
+team {
+ name: "trendy_team_pce_weu",
+
+ // go/trendy/manage/engineers/5205725968891904
+ trendy_team_id: "5205725968891904",
+}
+
+team {
+ name: "trendy_team_peeps_t_pgm_android_engprod",
+
+ // go/trendy/manage/engineers/6288284960358400
+ trendy_team_id: "6288284960358400",
+}
+
+team {
+ name: "trendy_team_appsearch",
+
+ // go/trendy/manage/engineers/5075661716815872
+ trendy_team_id: "5075661716815872",
+}
+
+team {
+ name: "trendy_team_shayba_team",
+
+ // go/trendy/manage/engineers/6213135020228608
+ trendy_team_id: "6213135020228608",
+}
+
+team {
+ name: "trendy_team_wear_wear_cloud_platform",
+
+ // go/trendy/manage/engineers/5917762526281728
+ trendy_team_id: "5917762526281728",
+}
+
+team {
+ name: "trendy_team_pixel_system_software",
+
+ // go/trendy/manage/engineers/4856005120622592
+ trendy_team_id: "4856005120622592",
+}
+
+team {
+ name: "trendy_team_platform_enabler_framework_make_pixel_",
+
+ // go/trendy/manage/engineers/5893944097243136
+ trendy_team_id: "5893944097243136",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_power_sw",
+
+ // go/trendy/manage/engineers/6703184655286272
+ trendy_team_id: "6703184655286272",
+}
+
+team {
+ name: "trendy_team_marvinpaul_team",
+
+ // go/trendy/manage/engineers/4800689692901376
+ trendy_team_id: "4800689692901376",
+}
+
+team {
+ name: "trendy_team_interactive_tv",
+
+ // go/trendy/manage/engineers/6150577853661184
+ trendy_team_id: "6150577853661184",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_sensor_framework",
+
+ // go/trendy/manage/engineers/5005310567284736
+ trendy_team_id: "5005310567284736",
+}
+
+team {
+ name: "trendy_team_wsd_arch",
+
+ // go/trendy/manage/engineers/6173769806512128
+ trendy_team_id: "6173769806512128",
+}
+
+team {
+ name: "trendy_team_lanechr_team",
+
+ // go/trendy/manage/engineers/5674594204811264
+ trendy_team_id: "5674594204811264",
+}
+
+team {
+ name: "trendy_team_test_eng_android_for_work",
+
+ // go/trendy/manage/engineers/5909887015845888
+ trendy_team_id: "5909887015845888",
+}
+
+team {
+ name: "trendy_team_camera_app",
+
+ // go/trendy/manage/engineers/5216644934533120
+ trendy_team_id: "5216644934533120",
+}
+
+team {
+ name: "trendy_team_vamaraju_team",
+
+ // go/trendy/manage/engineers/5150510960771072
+ trendy_team_id: "5150510960771072",
+}
+
+team {
+ name: "trendy_team_android_media_audio_framework",
+
+ // go/trendy/manage/engineers/5823575353065472
+ trendy_team_id: "5823575353065472",
+}
+
+team {
+ name: "trendy_team_superglue",
+
+ // go/trendy/manage/engineers/5211667882999808
+ trendy_team_id: "5211667882999808",
+}
+
+team {
+ name: "trendy_team_display_framework",
+
+ // go/trendy/manage/engineers/6035600925163520
+ trendy_team_id: "6035600925163520",
+}
+
+team {
+ name: "trendy_team_ananthak_team",
+
+ // go/trendy/manage/engineers/6706043301298176
+ trendy_team_id: "6706043301298176",
+}
+
+team {
+ name: "trendy_team_qmc_pqm",
+
+ // go/trendy/manage/engineers/4715267632267264
+ trendy_team_id: "4715267632267264",
+}
+
+team {
+ name: "trendy_team_search_allapps",
+
+ // go/trendy/manage/engineers/4926160670195712
+ trendy_team_id: "4926160670195712",
+}
+
+team {
+ name: "trendy_team_communal",
+
+ // go/trendy/manage/engineers/6380669942530048
+ trendy_team_id: "6380669942530048",
+}
+
+team {
+ name: "trendy_team_nova",
+
+ // go/trendy/manage/engineers/5418955074043904
+ trendy_team_id: "5418955074043904",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_o_o",
+
+ // go/trendy/manage/engineers/5999497213509632
+ trendy_team_id: "5999497213509632",
+}
+
+team {
+ name: "trendy_team_android_go",
+
+ // go/trendy/manage/engineers/6543205713444864
+ trendy_team_id: "6543205713444864",
+}
+
+team {
+ name: "trendy_team_wear_wear_frameworks",
+
+ // go/trendy/manage/engineers/5138392408817664
+ trendy_team_id: "5138392408817664",
+}
+
+team {
+ name: "trendy_team_ssd_sensor",
+
+ // go/trendy/manage/engineers/5084703539200000
+ trendy_team_id: "5084703539200000",
+}
+
+team {
+ name: "trendy_team_dontek_team",
+
+ // go/trendy/manage/engineers/5746076285042688
+ trendy_team_id: "5746076285042688",
+}
+
+team {
+ name: "trendy_team_carrier_field_test",
+
+ // go/trendy/manage/engineers/6409766640975872
+ trendy_team_id: "6409766640975872",
+}
+
+team {
+ name: "trendy_team_pmw_standards",
+
+ // go/trendy/manage/engineers/6428806822526976
+ trendy_team_id: "6428806822526976",
+}
+
+team {
+ name: "trendy_team_build_infra",
+
+ // go/trendy/manage/engineers/4516184164433920
+ trendy_team_id: "4516184164433920",
+}
+
+team {
+ name: "trendy_team_qmc_gft",
+
+ // go/trendy/manage/engineers/5454139446132736
+ trendy_team_id: "5454139446132736",
+}
+
+team {
+ name: "trendy_team_android_storage",
+
+ // go/trendy/manage/engineers/6301594936049664
+ trendy_team_id: "6301594936049664",
+}
+
+team {
+ name: "trendy_team_pixel_mobile_wireless",
+
+ // go/trendy/manage/engineers/4821918175887360
+ trendy_team_id: "4821918175887360",
+}
+
+team {
+ name: "trendy_team_camera_from_google",
+
+ // go/trendy/manage/engineers/4799694104854528
+ trendy_team_id: "4799694104854528",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_settings",
+
+ // go/trendy/manage/engineers/5622496450871296
+ trendy_team_id: "5622496450871296",
+}
+
+team {
+ name: "trendy_team_androidbugtool_abt_",
+
+ // go/trendy/manage/engineers/6531817781493760
+ trendy_team_id: "6531817781493760",
+}
+
+team {
+ name: "trendy_team_wear_wear_security",
+
+ // go/trendy/manage/engineers/6325699325362176
+ trendy_team_id: "6325699325362176",
+}
+
+team {
+ name: "trendy_team_pascallouis_team",
+
+ // go/trendy/manage/engineers/5111238276317184
+ trendy_team_id: "5111238276317184",
+}
+
+team {
+ name: "trendy_team_android_camera_ecosystem_enabling",
+
+ // go/trendy/manage/engineers/4529290269327360
+ trendy_team_id: "4529290269327360",
+}
+
+team {
+ name: "trendy_team_calendar",
+
+ // go/trendy/manage/engineers/6719127573889024
+ trendy_team_id: "6719127573889024",
+}
+
+team {
+ name: "trendy_team_cgc",
+
+ // go/trendy/manage/engineers/4590315499061248
+ trendy_team_id: "4590315499061248",
+}
+
+team {
+ name: "trendy_team_diagnostic_tool",
+
+ // go/trendy/manage/engineers/4689924564746240
+ trendy_team_id: "4689924564746240",
+}
+
+team {
+ name: "trendy_team_pixel_camera_system_software",
+
+ // go/trendy/manage/engineers/6386525306486784
+ trendy_team_id: "6386525306486784",
+}
+
+team {
+ name: "trendy_team_credential_manager",
+
+ // go/trendy/manage/engineers/5276403428655104
+ trendy_team_id: "5276403428655104",
+}
+
+team {
+ name: "trendy_team_wear_wti_wear_tools_and_infra_",
+
+ // go/trendy/manage/engineers/6225571306438656
+ trendy_team_id: "6225571306438656",
+}
+
+team {
+ name: "trendy_team_pixel_biometrics_face",
+
+ // go/trendy/manage/engineers/5028705926742016
+ trendy_team_id: "5028705926742016",
+}
+
+team {
+ name: "trendy_team_location_time",
+
+ // go/trendy/manage/engineers/4883807600017408
+ trendy_team_id: "4883807600017408",
+}
+
+team {
+ name: "trendy_team_android_hardware_backed_security",
+
+ // go/trendy/manage/engineers/6398595556343808
+ trendy_team_id: "6398595556343808",
+}
+
+team {
+ name: "trendy_team_play_newsstand",
+
+ // go/trendy/manage/engineers/5171015201980416
+ trendy_team_id: "5171015201980416",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_jaggies",
+
+ // go/trendy/manage/engineers/5753206608887808
+ trendy_team_id: "5753206608887808",
+}
+
+team {
+ name: "trendy_team_make_pixel_tpgm",
+
+ // go/trendy/manage/engineers/6061069864665088
+ trendy_team_id: "6061069864665088",
+}
+
+team {
+ name: "trendy_team_make_transformer",
+
+ // go/trendy/manage/engineers/6224539427438592
+ trendy_team_id: "6224539427438592",
+}
+
+team {
+ name: "trendy_team_wittrock_team",
+
+ // go/trendy/manage/engineers/5707412083474432
+ trendy_team_id: "5707412083474432",
+}
+
+team {
+ name: "trendy_team_wear_wear_android_companion_sdk",
+
+ // go/trendy/manage/engineers/4864923637022720
+ trendy_team_id: "4864923637022720",
+}
+
+team {
+ name: "trendy_team_assistant_sysui_integration",
+
+ // go/trendy/manage/engineers/4884282575060992
+ trendy_team_id: "4884282575060992",
+}
+
+team {
+ name: "trendy_team_things",
+
+ // go/trendy/manage/engineers/5206199574069248
+ trendy_team_id: "5206199574069248",
+}
+
+team {
+ name: "trendy_team_wsd_w13",
+
+ // go/trendy/manage/engineers/5612469120532480
+ trendy_team_id: "5612469120532480",
+}
+
+team {
+ name: "trendy_team_iqbalasif_team",
+
+ // go/trendy/manage/engineers/4912049813094400
+ trendy_team_id: "4912049813094400",
+}
+
+team {
+ name: "trendy_team_biometric_security",
+
+ // go/trendy/manage/engineers/5797911960649728
+ trendy_team_id: "5797911960649728",
+}
+
+team {
+ name: "trendy_team_silberst_team",
+
+ // go/trendy/manage/engineers/5710892584042496
+ trendy_team_id: "5710892584042496",
+}
+
+team {
+ name: "trendy_team_pmw_telephony",
+
+ // go/trendy/manage/engineers/6029121444151296
+ trendy_team_id: "6029121444151296",
+}
+
+team {
+ name: "trendy_team_zzz",
+
+ // go/trendy/manage/engineers/6351340934397952
+ trendy_team_id: "6351340934397952",
+}
+
+team {
+ name: "trendy_team_lite_team",
+
+ // go/trendy/manage/engineers/5647925813346304
+ trendy_team_id: "5647925813346304",
+}
+
+team {
+ name: "trendy_team_gms_core",
+
+ // go/trendy/manage/engineers/5735614422843392
+ trendy_team_id: "5735614422843392",
+}
+
+team {
+ name: "trendy_team_dialer_make_pixel_",
+
+ // go/trendy/manage/engineers/5126396509978624
+ trendy_team_id: "5126396509978624",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_color",
+
+ // go/trendy/manage/engineers/5489236125581312
+ trendy_team_id: "5489236125581312",
+}
+
+team {
+ name: "trendy_team_wear_wear_notifications_alerts_attention_management",
+
+ // go/trendy/manage/engineers/6267643681996800
+ trendy_team_id: "6267643681996800",
+}
+
+team {
+ name: "trendy_team_fwk_nfc",
+
+ // go/trendy/manage/engineers/5962312512864256
+ trendy_team_id: "5962312512864256",
+}
+
+team {
+ name: "trendy_team_wear_personalization_developer_surfaces",
+
+ // go/trendy/manage/engineers/4819890988810240
+ trendy_team_id: "4819890988810240",
+}
+
+team {
+ name: "trendy_team_srajkumar_team",
+
+ // go/trendy/manage/engineers/5170053894012928
+ trendy_team_id: "5170053894012928",
+}
+
+team {
+ name: "trendy_team_in_market_tpm",
+
+ // go/trendy/manage/engineers/5352549888196608
+ trendy_team_id: "5352549888196608",
+}
+
+team {
+ name: "trendy_team_leannogasawara_team",
+
+ // go/trendy/manage/engineers/4905467198472192
+ trendy_team_id: "4905467198472192",
+}
+
+team {
+ name: "trendy_team_zurikemp_team",
+
+ // go/trendy/manage/engineers/4559796603879424
+ trendy_team_id: "4559796603879424",
+}
+
+team {
+ name: "trendy_team_android_telemetry_infra",
+
+ // go/trendy/manage/engineers/5295809771732992
+ trendy_team_id: "5295809771732992",
+}
+
+team {
+ name: "trendy_team_system_ui_sensors",
+
+ // go/trendy/manage/engineers/5647653492621312
+ trendy_team_id: "5647653492621312",
+}
+
+team {
+ name: "trendy_team_windowing_animations_transitions",
+
+ // go/trendy/manage/engineers/4803040337362944
+ trendy_team_id: "4803040337362944",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_jjaggi",
+
+ // go/trendy/manage/engineers/6471742270898176
+ trendy_team_id: "6471742270898176",
+}
+
+team {
+ name: "trendy_team_accessibility_hearing_aids",
+
+ // go/trendy/manage/engineers/4661226340253696
+ trendy_team_id: "4661226340253696",
+}
+
+team {
+ name: "trendy_team_performance",
+
+ // go/trendy/manage/engineers/5842000521625600
+ trendy_team_id: "5842000521625600",
+}
+
+team {
+ name: "trendy_team_cloud_android",
+
+ // go/trendy/manage/engineers/5980255760023552
+ trendy_team_id: "5980255760023552",
+}
+
+team {
+ name: "trendy_team_visual_design",
+
+ // go/trendy/manage/engineers/4504161399734272
+ trendy_team_id: "4504161399734272",
+}
+
+team {
+ name: "trendy_team_wilkinsonclay_team",
+
+ // go/trendy/manage/engineers/5680997128634368
+ trendy_team_id: "5680997128634368",
+}
+
+team {
+ name: "trendy_team_tccyp_nadiae",
+
+ // go/trendy/manage/engineers/6556518831652864
+ trendy_team_id: "6556518831652864",
+}
+
+team {
+ name: "trendy_team_accessibility_settings",
+
+ // go/trendy/manage/engineers/5641806510587904
+ trendy_team_id: "5641806510587904",
+}
+
+team {
+ name: "trendy_team_hansmuller_team",
+
+ // go/trendy/manage/engineers/5069192257765376
+ trendy_team_id: "5069192257765376",
+}
+
+team {
+ name: "trendy_team_retail_demo_mode",
+
+ // go/trendy/manage/engineers/6520787531235328
+ trendy_team_id: "6520787531235328",
+}
+
+team {
+ name: "trendy_team_lse_dreams",
+
+ // go/trendy/manage/engineers/6317558842097664
+ trendy_team_id: "6317558842097664",
+}
+
+team {
+ name: "trendy_team_android_usb",
+
+ // go/trendy/manage/engineers/5090707854426112
+ trendy_team_id: "5090707854426112",
+}
+
+team {
+ name: "trendy_team_curtisgalloway_team",
+
+ // go/trendy/manage/engineers/5706857730703360
+ trendy_team_id: "5706857730703360",
+}
+
+team {
+ name: "trendy_team_camera_algorithms",
+
+ // go/trendy/manage/engineers/6544854980886528
+ trendy_team_id: "6544854980886528",
+}
+
+team {
+ name: "trendy_team_cast_3p",
+
+ // go/trendy/manage/engineers/6585564972875776
+ trendy_team_id: "6585564972875776",
+}
+
+team {
+ name: "trendy_team_test_eng_android_wear",
+
+ // go/trendy/manage/engineers/4979150422933504
+ trendy_team_id: "4979150422933504",
+}
+
+team {
+ name: "trendy_team_mesch_team",
+
+ // go/trendy/manage/engineers/5205465899368448
+ trendy_team_id: "5205465899368448",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_audio_arch",
+
+ // go/trendy/manage/engineers/5560501377073152
+ trendy_team_id: "5560501377073152",
+}
+
+team {
+ name: "trendy_team_defunct_use_controls_quick_settings",
+
+ // go/trendy/manage/engineers/4667861043412992
+ trendy_team_id: "4667861043412992",
+}
+
+team {
+ name: "trendy_team_wear_wear_developer_devx",
+
+ // go/trendy/manage/engineers/4894890764697600
+ trendy_team_id: "4894890764697600",
+}
+
+team {
+ name: "trendy_team_android_rust",
+
+ // go/trendy/manage/engineers/4844600586305536
+ trendy_team_id: "4844600586305536",
+}
+
+team {
+ name: "trendy_team_deprecated_systemui_gfx",
+
+ // go/trendy/manage/engineers/6673470538285056
+ trendy_team_id: "6673470538285056",
+}
+
+team {
+ name: "trendy_team_wear_wear_connectivity",
+
+ // go/trendy/manage/engineers/6245149466263552
+ trendy_team_id: "6245149466263552",
+}
+
+team {
+ name: "trendy_team_android_core_experiments",
+
+ // go/trendy/manage/engineers/5709654965780480
+ trendy_team_id: "5709654965780480",
+}
+
+team {
+ name: "trendy_team_native_tools_libraries",
+
+ // go/trendy/manage/engineers/5920332376309760
+ trendy_team_id: "5920332376309760",
+}
+
+team {
+ name: "trendy_team_app_compat",
+
+ // go/trendy/manage/engineers/4907132411314176
+ trendy_team_id: "4907132411314176",
+}
+
+team {
+ name: "trendy_team_zra_team",
+
+ // go/trendy/manage/engineers/6227615267586048
+ trendy_team_id: "6227615267586048",
+}
+
+team {
+ name: "trendy_team_pixel_watch_system_software",
+
+ // go/trendy/manage/engineers/5295994500972544
+ trendy_team_id: "5295994500972544",
+}
+
+team {
+ name: "trendy_team_surfaces_engprod",
+
+ // go/trendy/manage/engineers/6154478176600064
+ trendy_team_id: "6154478176600064",
+}
+
+team {
+ name: "trendy_team_android_permissions",
+
+ // go/trendy/manage/engineers/5533977340313600
+ trendy_team_id: "5533977340313600",
+}
+
+team {
+ name: "trendy_team_platform_program_mgrs",
+
+ // go/trendy/manage/engineers/4766394922958848
+ trendy_team_id: "4766394922958848",
+}
+
+team {
+ name: "trendy_team_deprecated_system_health",
+
+ // go/trendy/manage/engineers/4864801213644800
+ trendy_team_id: "4864801213644800",
+}
+
+team {
+ name: "trendy_team_messages",
+
+ // go/trendy/manage/engineers/5137480097333248
+ trendy_team_id: "5137480097333248",
+}
+
+team {
+ name: "trendy_team_palmer_team",
+
+ // go/trendy/manage/engineers/5643570052235264
+ trendy_team_id: "5643570052235264",
+}
+
+team {
+ name: "trendy_team_android_video_image_codecs",
+
+ // go/trendy/manage/engineers/5733246110433280
+ trendy_team_id: "5733246110433280",
+}
+
+team {
+ name: "trendy_team_play_music",
+
+ // go/trendy/manage/engineers/6015440132112384
+ trendy_team_id: "6015440132112384",
+}
+
+team {
+ name: "trendy_team_system_clockwork_internal_",
+
+ // go/trendy/manage/engineers/6509670608797696
+ trendy_team_id: "6509670608797696",
+}
+
+team {
+ name: "trendy_team_multitasking_windowing",
+
+ // go/trendy/manage/engineers/5149185436975104
+ trendy_team_id: "5149185436975104",
+}
+
+team {
+ name: "trendy_team_vr",
+
+ // go/trendy/manage/engineers/4854355853180928
+ trendy_team_id: "4854355853180928",
+}
+
+team {
+ name: "trendy_team_maruel_team",
+
+ // go/trendy/manage/engineers/6302551810146304
+ trendy_team_id: "6302551810146304",
+}
+
+team {
+ name: "trendy_team_tv_os",
+
+ // go/trendy/manage/engineers/4662491074134016
+ trendy_team_id: "4662491074134016",
+}
+
+team {
+ name: "trendy_team_auto_engprod",
+
+ // go/trendy/manage/engineers/6199949475479552
+ trendy_team_id: "6199949475479552",
+}
+
+team {
+ name: "trendy_team_sarahcobb_team",
+
+ // go/trendy/manage/engineers/5755692179947520
+ trendy_team_id: "5755692179947520",
+}
+
+team {
+ name: "trendy_team_accessibility_services",
+
+ // go/trendy/manage/engineers/6367283853000704
+ trendy_team_id: "6367283853000704",
+}
+
+team {
+ name: "trendy_team_documentsui",
+
+ // go/trendy/manage/engineers/5805983167021056
+ trendy_team_id: "5805983167021056",
+}
+
+team {
+ name: "trendy_team_carrier_cert_follow_up",
+
+ // go/trendy/manage/engineers/6751912099741696
+ trendy_team_id: "6751912099741696",
+}
+
+team {
+ name: "trendy_team_mobile_device_partners",
+
+ // go/trendy/manage/engineers/5833057717092352
+ trendy_team_id: "5833057717092352",
+}
+
+team {
+ name: "trendy_team_activity_recognition",
+
+ // go/trendy/manage/engineers/6304701268000768
+ trendy_team_id: "6304701268000768",
+}
+
+team {
+ name: "trendy_team_jasoncampbell_team",
+
+ // go/trendy/manage/engineers/4834972524511232
+ trendy_team_id: "4834972524511232",
+}
+
+team {
+ name: "trendy_team_wear_wallet_on_wear",
+
+ // go/trendy/manage/engineers/5724960437731328
+ trendy_team_id: "5724960437731328",
+}
+
+team {
+ name: "trendy_team_glanceables",
+
+ // go/trendy/manage/engineers/4658222004600832
+ trendy_team_id: "4658222004600832",
+}
+
+team {
+ name: "trendy_team_android_safe_browsing",
+
+ // go/trendy/manage/engineers/6685713244782592
+ trendy_team_id: "6685713244782592",
+}
+
+team {
+ name: "trendy_team_android_input",
+
+ // go/trendy/manage/engineers/5141994775805952
+ trendy_team_id: "5141994775805952",
+}
+
+team {
+ name: "trendy_team_android_rust_toolchain",
+
+ // go/trendy/manage/engineers/6530590989975552
+ trendy_team_id: "6530590989975552",
+}
+
+team {
+ name: "trendy_team_exo",
+
+ // go/trendy/manage/engineers/5631545248088064
+ trendy_team_id: "5631545248088064",
+}
+
+team {
+ name: "trendy_team_camerax",
+
+ // go/trendy/manage/engineers/5272590669479936
+ trendy_team_id: "5272590669479936",
+}
+
+team {
+ name: "trendy_team_accessibility_sound_amplifier",
+
+ // go/trendy/manage/engineers/5674840312020992
+ trendy_team_id: "5674840312020992",
+}
+
+team {
+ name: "trendy_team_android_printing",
+
+ // go/trendy/manage/engineers/6257528146067456
+ trendy_team_id: "6257528146067456",
+}
+
+team {
+ name: "trendy_team_dtiselice_team",
+
+ // go/trendy/manage/engineers/5177934253031424
+ trendy_team_id: "5177934253031424",
+}
+
+team {
+ name: "trendy_team_personal_safety",
+
+ // go/trendy/manage/engineers/6222285147111424
+ trendy_team_id: "6222285147111424",
+}
+
+team {
+ name: "trendy_team_notifications",
+
+ // go/trendy/manage/engineers/5993521355587584
+ trendy_team_id: "5993521355587584",
+}
+
+team {
+ name: "trendy_team_java_core_libraries",
+
+ // go/trendy/manage/engineers/4768044190400512
+ trendy_team_id: "4768044190400512",
+}
+
+team {
+ name: "trendy_team_updatable_sdk_apis",
+
+ // go/trendy/manage/engineers/4840215139483648
+ trendy_team_id: "4840215139483648",
+}
+
+team {
+ name: "trendy_team_wear_low_power_mcu_experiences",
+
+ // go/trendy/manage/engineers/6172878013628416
+ trendy_team_id: "6172878013628416",
+}
+
+team {
+ name: "trendy_team_biometrics_framework",
+
+ // go/trendy/manage/engineers/6205415425998848
+ trendy_team_id: "6205415425998848",
+}
+
+team {
+ name: "trendy_team_pesto",
+
+ // go/trendy/manage/engineers/5551098528825344
+ trendy_team_id: "5551098528825344",
+}
+
+team {
+ name: "trendy_team_wear_engineering_foundations",
+
+ // go/trendy/manage/engineers/5366936275681280
+ trendy_team_id: "5366936275681280",
+}
+
+team {
+ name: "trendy_team_wear_wcs_developer",
+
+ // go/trendy/manage/engineers/5114199579459584
+ trendy_team_id: "5114199579459584",
+}
+
+team {
+ name: "trendy_team_aaos_framework",
+
+ // go/trendy/manage/engineers/6547794223333376
+ trendy_team_id: "6547794223333376",
+}
+
+team {
+ name: "trendy_team_wear_3xp",
+
+ // go/trendy/manage/engineers/5692317612539904
+ trendy_team_id: "5692317612539904",
+}
+
+team {
+ name: "trendy_team_clockwork",
+
+ // go/trendy/manage/engineers/4908781678755840
+ trendy_team_id: "4908781678755840",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_wifi_drivers_firmware",
+
+ // go/trendy/manage/engineers/4583326236934144
+ trendy_team_id: "4583326236934144",
+}
+
+team {
+ name: "trendy_team_games",
+
+ // go/trendy/manage/engineers/6736719759933440
+ trendy_team_id: "6736719759933440",
+}
+
+team {
+ name: "trendy_team_systems_n4_",
+
+ // go/trendy/manage/engineers/6474486236708864
+ trendy_team_id: "6474486236708864",
+}
+
+team {
+ name: "trendy_team_android_kvm",
+
+ // go/trendy/manage/engineers/6529318184714240
+ trendy_team_id: "6529318184714240",
+}
+
+team {
+ name: "trendy_team_wsd_w22",
+
+ // go/trendy/manage/engineers/6580039352975360
+ trendy_team_id: "6580039352975360",
+}
+
+team {
+ name: "trendy_team_android_sudo",
+
+ // go/trendy/manage/engineers/5329344876380160
+ trendy_team_id: "5329344876380160",
+}
+
+team {
+ name: "trendy_team_wear_wear_system_health_and_power",
+
+ // go/trendy/manage/engineers/5219147457658880
+ trendy_team_id: "5219147457658880",
+}
+
+team {
+ name: "trendy_team_android_build_release_tools",
+
+ // go/trendy/manage/engineers/6260558107803648
+ trendy_team_id: "6260558107803648",
+}
+
+team {
+ name: "trendy_team_fused_presence_provider",
+
+ // go/trendy/manage/engineers/6536344753307648
+ trendy_team_id: "6536344753307648",
+}
+
+team {
+ name: "trendy_team_agsa",
+
+ // go/trendy/manage/engineers/6157826887909376
+ trendy_team_id: "6157826887909376",
+}
+
+team {
+ name: "trendy_team_wear_wear_developer_tiles",
+
+ // go/trendy/manage/engineers/6633777396613120
+ trendy_team_id: "6633777396613120",
+}
+
+team {
+ name: "trendy_team_essential_applications",
+
+ // go/trendy/manage/engineers/4926373864800256
+ trendy_team_id: "4926373864800256",
+}
+
+team {
+ name: "trendy_team_pixel_mobile_data",
+
+ // go/trendy/manage/engineers/4996742608977920
+ trendy_team_id: "4996742608977920",
+}
+
+team {
+ name: "trendy_team_wsd_w52",
+
+ // go/trendy/manage/engineers/6280972190220288
+ trendy_team_id: "6280972190220288",
+}
+
+team {
+ name: "trendy_team_pixel_mobile_connectivity",
+
+ // go/trendy/manage/engineers/6754311945977856
+ trendy_team_id: "6754311945977856",
+}
+
+team {
+ name: "trendy_team_essentialapps_clock_calculator",
+
+ // go/trendy/manage/engineers/5270363728674816
+ trendy_team_id: "5270363728674816",
+}
+
+team {
+ name: "trendy_team_ssd_system_health",
+
+ // go/trendy/manage/engineers/6456894050664448
+ trendy_team_id: "6456894050664448",
+}
+
+team {
+ name: "trendy_team_pixel_continuity",
+
+ // go/trendy/manage/engineers/4786635551309824
+ trendy_team_id: "4786635551309824",
+}
+
+team {
+ name: "trendy_team_wear_software_nti",
+
+ // go/trendy/manage/engineers/5164973558759424
+ trendy_team_id: "5164973558759424",
+}
+
+team {
+ name: "trendy_team_machine_learning",
+
+ // go/trendy/manage/engineers/5276568318246912
+ trendy_team_id: "5276568318246912",
+}
+
+team {
+ name: "trendy_team_pixel_ml",
+
+ // go/trendy/manage/engineers/5339883108990976
+ trendy_team_id: "5339883108990976",
+}
+
+team {
+ name: "trendy_team_ex_enterprise",
+
+ // go/trendy/manage/engineers/6738369027375104
+ trendy_team_id: "6738369027375104",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_aoc",
+
+ // go/trendy/manage/engineers/4712464983425024
+ trendy_team_id: "4712464983425024",
+}
+
+team {
+ name: "trendy_team_android_platform_communications",
+
+ // go/trendy/manage/engineers/6577505415102464
+ trendy_team_id: "6577505415102464",
+}
+
+team {
+ name: "trendy_team_sunshine",
+
+ // go/trendy/manage/engineers/6105050329776128
+ trendy_team_id: "6105050329776128",
+}
+
+team {
+ name: "trendy_team_qmc_iqt_tao",
+
+ // go/trendy/manage/engineers/5065462085713920
+ trendy_team_id: "5065462085713920",
+}
+
+team {
+ name: "trendy_team_mckillop_team",
+
+ // go/trendy/manage/engineers/5926589599744000
+ trendy_team_id: "5926589599744000",
+}
+
+team {
+ name: "trendy_team_pixel_process_experience",
+
+ // go/trendy/manage/engineers/5745436633235456
+ trendy_team_id: "5745436633235456",
+}
+
+team {
+ name: "trendy_team_wsd_l1",
+
+ // go/trendy/manage/engineers/5119887911288832
+ trendy_team_id: "5119887911288832",
+}
+
+team {
+ name: "trendy_team_foldables",
+
+ // go/trendy/manage/engineers/5149421392920576
+ trendy_team_id: "5149421392920576",
+}
+
+team {
+ name: "trendy_team_arc_next",
+
+ // go/trendy/manage/engineers/6238917659361280
+ trendy_team_id: "6238917659361280",
+}
+
+team {
+ name: "trendy_team_android_rubidium",
+
+ // go/trendy/manage/engineers/5098012529295360
+ trendy_team_id: "5098012529295360",
+}
+
+team {
+ name: "trendy_team_wear_wear_power_emulator",
+
+ // go/trendy/manage/engineers/5160338936725504
+ trendy_team_id: "5160338936725504",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_svetoslavganov",
+
+ // go/trendy/manage/engineers/6404117492531200
+ trendy_team_id: "6404117492531200",
+}
+
+team {
+ name: "trendy_team_gregsimon_team",
+
+ // go/trendy/manage/engineers/5702018510520320
+ trendy_team_id: "5702018510520320",
+}
+
+team {
+ name: "trendy_team_wear_opus",
+
+ // go/trendy/manage/engineers/5098351636676608
+ trendy_team_id: "5098351636676608",
+}
+
+team {
+ name: "trendy_team_text_to_speech",
+
+ // go/trendy/manage/engineers/6368933120442368
+ trendy_team_id: "6368933120442368",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_audio",
+
+ // go/trendy/manage/engineers/6492078422753280
+ trendy_team_id: "6492078422753280",
+}
+
+team {
+ name: "trendy_team_transformer",
+
+ // go/trendy/manage/engineers/5964312841420800
+ trendy_team_id: "5964312841420800",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_video",
+
+ // go/trendy/manage/engineers/6442361728696320
+ trendy_team_id: "6442361728696320",
+}
+
+team {
+ name: "trendy_team_lse_app_compat",
+
+ // go/trendy/manage/engineers/5180827749154816
+ trendy_team_id: "5180827749154816",
+}
+
+team {
+ name: "trendy_team_android_media_leads",
+
+ // go/trendy/manage/engineers/5487674550779904
+ trendy_team_id: "5487674550779904",
+}
+
+team {
+ name: "trendy_team_kousha_team",
+
+ // go/trendy/manage/engineers/5157338676887552
+ trendy_team_id: "5157338676887552",
+}
+
+team {
+ name: "trendy_team_security",
+
+ // go/trendy/manage/engineers/5241383946158080
+ trendy_team_id: "5241383946158080",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_battery_life_system_power_",
+
+ // go/trendy/manage/engineers/4512957492756480
+ trendy_team_id: "4512957492756480",
+}
+
+team {
+ name: "trendy_team_eggs",
+
+ // go/trendy/manage/engineers/4568929198309376
+ trendy_team_id: "4568929198309376",
+}
+
+team {
+ name: "trendy_team_jeremymanson_team",
+
+ // go/trendy/manage/engineers/5095869749297152
+ trendy_team_id: "5095869749297152",
+}
+
+team {
+ name: "trendy_team_exchange_active_sync_in_gmail",
+
+ // go/trendy/manage/engineers/5382121434513408
+ trendy_team_id: "5382121434513408",
+}
+
+team {
+ name: "trendy_team_ios_backup_restore_make_pixel_",
+
+ // go/trendy/manage/engineers/5752160863420416
+ trendy_team_id: "5752160863420416",
+}
+
+team {
+ name: "trendy_team_deprecated_location",
+
+ // go/trendy/manage/engineers/6228195632087040
+ trendy_team_id: "6228195632087040",
+}
+
+team {
+ name: "trendy_team_input_framework",
+
+ // go/trendy/manage/engineers/4999436357238784
+ trendy_team_id: "4999436357238784",
+}
+
+team {
+ name: "trendy_team_wear_developer_foundation",
+
+ // go/trendy/manage/engineers/5239127108648960
+ trendy_team_id: "5239127108648960",
+}
+
+team {
+ name: "trendy_team_tpm_tvc",
+
+ // go/trendy/manage/engineers/5390683333230592
+ trendy_team_id: "5390683333230592",
+}
+
+team {
+ name: "trendy_team_wear_wear_ux",
+
+ // go/trendy/manage/engineers/5782097411080192
+ trendy_team_id: "5782097411080192",
+}
+
+team {
+ name: "trendy_team_lse_desktop_os_experience",
+
+ // go/trendy/manage/engineers/5125234900434944
+ trendy_team_id: "5125234900434944",
+}
+
+team {
+ name: "trendy_team_android_for_india_device_experiences",
+
+ // go/trendy/manage/engineers/5395413652111360
+ trendy_team_id: "5395413652111360",
+}
+
+team {
+ name: "trendy_team_pixel_zombie",
+
+ // go/trendy/manage/engineers/5074646910533632
+ trendy_team_id: "5074646910533632",
+}
+
+team {
+ name: "trendy_team_android_onboarding",
+
+ // go/trendy/manage/engineers/5152271974367232
+ trendy_team_id: "5152271974367232",
+}
+
+team {
+ name: "trendy_team_pixel_audio",
+
+ // go/trendy/manage/engineers/5436547260088320
+ trendy_team_id: "5436547260088320",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_bt",
+
+ // go/trendy/manage/engineers/6328035423453184
+ trendy_team_id: "6328035423453184",
+}
+
+team {
+ name: "trendy_team_wsd_w12",
+
+ // go/trendy/manage/engineers/6333748748353536
+ trendy_team_id: "6333748748353536",
+}
+
+team {
+ name: "trendy_team_qmc_mvt",
+
+ // go/trendy/manage/engineers/4572880876470272
+ trendy_team_id: "4572880876470272",
+}
+
+team {
+ name: "trendy_team_switch_access_voice_access",
+
+ // go/trendy/manage/engineers/4794432469467136
+ trendy_team_id: "4794432469467136",
+}
+
+team {
+ name: "trendy_team_mainline_modularization",
+
+ // go/trendy/manage/engineers/5845084143386624
+ trendy_team_id: "5845084143386624",
+}
+
+team {
+ name: "trendy_team_fwk_telecom",
+
+ // go/trendy/manage/engineers/5330994143821824
+ trendy_team_id: "5330994143821824",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_akulian",
+
+ // go/trendy/manage/engineers/5323210872750080
+ trendy_team_id: "5323210872750080",
+}
+
+team {
+ name: "trendy_team_wear_wear_identity",
+
+ // go/trendy/manage/engineers/6017732386390016
+ trendy_team_id: "6017732386390016",
+}
+
+team {
+ name: "trendy_team_android_pdf",
+
+ // go/trendy/manage/engineers/5175136433045504
+ trendy_team_id: "5175136433045504",
+}
+
+team {
+ name: "trendy_team_developer_relations",
+
+ // go/trendy/manage/engineers/5709226143776768
+ trendy_team_id: "5709226143776768",
+}
+
+team {
+ name: "trendy_team_system_intelligence",
+
+ // go/trendy/manage/engineers/5849675995709440
+ trendy_team_id: "5849675995709440",
+}
+
+team {
+ name: "trendy_team_mainline_updates",
+
+ // go/trendy/manage/engineers/4845810809995264
+ trendy_team_id: "4845810809995264",
+}
+
+team {
+ name: "trendy_team_n_a_1",
+
+ // go/trendy/manage/engineers/5946720655376384
+ trendy_team_id: "5946720655376384",
+}
+
+team {
+ name: "trendy_team_google_drive_docs_sheets_and_slides",
+
+ // go/trendy/manage/engineers/6613574457622528
+ trendy_team_id: "6613574457622528",
+}
+
+team {
+ name: "trendy_team_deprecated_awareness_health_experiences",
+
+ // go/trendy/manage/engineers/6627866395967488
+ trendy_team_id: "6627866395967488",
+}
+
+team {
+ name: "trendy_team_context_infrastructure",
+
+ // go/trendy/manage/engineers/4701268040646656
+ trendy_team_id: "4701268040646656",
+}
+
+team {
+ name: "trendy_team_android_media_solutions",
+
+ // go/trendy/manage/engineers/4750452004356096
+ trendy_team_id: "4750452004356096",
+}
+
+team {
+ name: "trendy_team_wear_device_and_infrastructure",
+
+ // go/trendy/manage/engineers/6358069369798656
+ trendy_team_id: "6358069369798656",
+}
+
+team {
+ name: "trendy_team_pixel_biometrics",
+
+ // go/trendy/manage/engineers/5780875748737024
+ trendy_team_id: "5780875748737024",
+}
+
+team {
+ name: "trendy_team_app_knowledge_platform",
+
+ // go/trendy/manage/engineers/6272266390634496
+ trendy_team_id: "6272266390634496",
+}
+
+team {
+ name: "trendy_team_wsd",
+
+ // go/trendy/manage/engineers/4680083260178432
+ trendy_team_id: "4680083260178432",
+}
+
+team {
+ name: "trendy_team_seg",
+
+ // go/trendy/manage/engineers/5067111353155584
+ trendy_team_id: "5067111353155584",
+}
+
+team {
+ name: "trendy_team_devinlawson_team",
+
+ // go/trendy/manage/engineers/4805900971442176
+ trendy_team_id: "4805900971442176",
+}
+
+team {
+ name: "trendy_team_camera_hardware",
+
+ // go/trendy/manage/engineers/6087458143731712
+ trendy_team_id: "6087458143731712",
+}
+
+team {
+ name: "trendy_team_camera_image_quality",
+
+ // go/trendy/manage/engineers/5401362887999488
+ trendy_team_id: "5401362887999488",
+}
+
+team {
+ name: "trendy_team_wear_wear_assistant",
+
+ // go/trendy/manage/engineers/5848075306172416
+ trendy_team_id: "5848075306172416",
+}
+
+team {
+ name: "trendy_team_android_power_and_comms_infra",
+
+ // go/trendy/manage/engineers/5325547653332992
+ trendy_team_id: "5325547653332992",
+}
+
+team {
+ name: "trendy_team_pmw_pmo",
+
+ // go/trendy/manage/engineers/4656299270504448
+ trendy_team_id: "4656299270504448",
+}
+
+team {
+ name: "trendy_team_filament",
+
+ // go/trendy/manage/engineers/6031425915486208
+ trendy_team_id: "6031425915486208",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_bspcore",
+
+ // go/trendy/manage/engineers/6508021341356032
+ trendy_team_id: "6508021341356032",
+}
+
+team {
+ name: "trendy_team_powermanager_framework",
+
+ // go/trendy/manage/engineers/5116162121564160
+ trendy_team_id: "5116162121564160",
+}
+
+team {
+ name: "trendy_team_wear_romanesco",
+
+ // go/trendy/manage/engineers/5112520062697472
+ trendy_team_id: "5112520062697472",
+}
+
+team {
+ name: "trendy_team_deprecated_theming",
+
+ // go/trendy/manage/engineers/5179308179881984
+ trendy_team_id: "5179308179881984",
+}
+
+team {
+ name: "trendy_team_recorder",
+
+ // go/trendy/manage/engineers/5085035337383936
+ trendy_team_id: "5085035337383936",
+}
+
+team {
+ name: "trendy_team_framework_accessibility",
+
+ // go/trendy/manage/engineers/5474751170019328
+ trendy_team_id: "5474751170019328",
+}
+
+team {
+ name: "trendy_team_windowing_infra_",
+
+ // go/trendy/manage/engineers/4578440609431552
+ trendy_team_id: "4578440609431552",
+}
+
+team {
+ name: "trendy_team_pmw_mcs",
+
+ // go/trendy/manage/engineers/5864733550608384
+ trendy_team_id: "5864733550608384",
+}
+
+team {
+ name: "trendy_team_wear_wear_sysui_ctrl_carousel_tiles_recents_launcher_",
+
+ // go/trendy/manage/engineers/4820131976740864
+ trendy_team_id: "4820131976740864",
+}
+
+team {
+ name: "trendy_team_wear_wear_accessibility_compliance",
+
+ // go/trendy/manage/engineers/5381719553114112
+ trendy_team_id: "5381719553114112",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_performance_thermal",
+
+ // go/trendy/manage/engineers/5146276190355456
+ trendy_team_id: "5146276190355456",
+}
+
+team {
+ name: "trendy_team_neelsa_team",
+
+ // go/trendy/manage/engineers/5736750978334720
+ trendy_team_id: "5736750978334720",
+}
+
+team {
+ name: "trendy_team_pixel_camera_engineering_experience",
+
+ // go/trendy/manage/engineers/5190256655466496
+ trendy_team_id: "5190256655466496",
+}
+
+team {
+ name: "trendy_team_embedded_web_on_android",
+
+ // go/trendy/manage/engineers/630061306576896
+ trendy_team_id: "630061306576896",
+}
+
+team {
+ name: "trendy_team_gantry",
+
+ // go/trendy/manage/engineers/5677019153858560
+ trendy_team_id: "5677019153858560",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_battery",
+
+ // go/trendy/manage/engineers/6052273771642880
+ trendy_team_id: "6052273771642880",
+}
+
+team {
+ name: "trendy_team_enigma",
+
+ // go/trendy/manage/engineers/5396338361597952
+ trendy_team_id: "5396338361597952",
+}
+
+team {
+ name: "trendy_team_pixel_gps_power",
+
+ // go/trendy/manage/engineers/5075907446177792
+ trendy_team_id: "5075907446177792",
+}
+
+team {
+ name: "trendy_team_tool_frank",
+
+ // go/trendy/manage/engineers/6200209976360960
+ trendy_team_id: "6200209976360960",
+}
+
+team {
+ name: "trendy_team_rginda_team",
+
+ // go/trendy/manage/engineers/6031367105314816
+ trendy_team_id: "6031367105314816",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_soc_power",
+
+ // go/trendy/manage/engineers/5400771358785536
+ trendy_team_id: "5400771358785536",
+}
+
+team {
+ name: "trendy_team_android_crumpet",
+
+ // go/trendy/manage/engineers/5199704478351360
+ trendy_team_id: "5199704478351360",
+}
+
+team {
+ name: "trendy_team_wallpapers",
+
+ // go/trendy/manage/engineers/5125411306373120
+ trendy_team_id: "5125411306373120",
+}
+
+team {
+ name: "trendy_team_deprecated_volta",
+
+ // go/trendy/manage/engineers/6316156562309120
+ trendy_team_id: "6316156562309120",
+}
+
+team {
+ name: "trendy_team_camera_machine_intelligence",
+
+ // go/trendy/manage/engineers/6578390085533696
+ trendy_team_id: "6578390085533696",
+}
+
+team {
+ name: "trendy_team_sheepo_team",
+
+ // go/trendy/manage/engineers/5068061372743680
+ trendy_team_id: "5068061372743680",
+}
+
+team {
+ name: "trendy_team_android_profile_experiences",
+
+ // go/trendy/manage/engineers/5914919462404096
+ trendy_team_id: "5914919462404096",
+}
+
+team {
+ name: "trendy_team_review_platform",
+
+ // go/trendy/manage/engineers/5952905574514688
+ trendy_team_id: "5952905574514688",
+}
+
+team {
+ name: "trendy_team_abarth_team",
+
+ // go/trendy/manage/engineers/4857528786780160
+ trendy_team_id: "4857528786780160",
+}
+
+team {
+ name: "trendy_team_treble",
+
+ // go/trendy/manage/engineers/5452490178691072
+ trendy_team_id: "5452490178691072",
+}
+
+team {
+ name: "trendy_team_jsasinowski_team",
+
+ // go/trendy/manage/engineers/6239259762786304
+ trendy_team_id: "6239259762786304",
+}
+
+team {
+ name: "trendy_team_vaas_team",
+
+ // go/trendy/manage/engineers/5106754296905728
+ trendy_team_id: "5106754296905728",
+}
+
+team {
+ name: "trendy_team_internationalization",
+
+ // go/trendy/manage/engineers/5911536283287552
+ trendy_team_id: "5911536283287552",
+}
+
+team {
+ name: "trendy_team_android_safer_apps",
+
+ // go/trendy/manage/engineers/5943179005034496
+ trendy_team_id: "5943179005034496",
+}
+
+team {
+ name: "trendy_team_connectivity_telemetry",
+
+ // go/trendy/manage/engineers/5084491349393408
+ trendy_team_id: "5084491349393408",
+}
+
+team {
+ name: "trendy_team_eseidel_team",
+
+ // go/trendy/manage/engineers/5453997738721280
+ trendy_team_id: "5453997738721280",
+}
+
+team {
+ name: "trendy_team_test_eng_infrastructure",
+
+ // go/trendy/manage/engineers/5981905027465216
+ trendy_team_id: "5981905027465216",
+}
+
+team {
+ name: "trendy_team_wear_wear_wcs_notification",
+
+ // go/trendy/manage/engineers/4805871527690240
+ trendy_team_id: "4805871527690240",
+}
+
+team {
+ name: "trendy_team_konkers_team",
+
+ // go/trendy/manage/engineers/5751147701895168
+ trendy_team_id: "5751147701895168",
+}
+
+team {
+ name: "trendy_team_mkearney_team",
+
+ // go/trendy/manage/engineers/5082590844452864
+ trendy_team_id: "5082590844452864",
+}
+
+team {
+ name: "trendy_team_android_kernel",
+
+ // go/trendy/manage/engineers/5014334795022336
+ trendy_team_id: "5014334795022336",
+}
+
+team {
+ name: "trendy_team_chrome",
+
+ // go/trendy/manage/engineers/6439301864620032
+ trendy_team_id: "6439301864620032",
+}
+
+team {
+ name: "trendy_team_wear_wear_dialer_messages",
+
+ // go/trendy/manage/engineers/4906732878725120
+ trendy_team_id: "4906732878725120",
+}
+
+team {
+ name: "trendy_team_android_tv_engprod",
+
+ // go/trendy/manage/engineers/5538081157185536
+ trendy_team_id: "5538081157185536",
+}
+
+team {
+ name: "trendy_team_nicoh_team",
+
+ // go/trendy/manage/engineers/5662292009189376
+ trendy_team_id: "5662292009189376",
+}
+
+team {
+ name: "trendy_team_wear_apps_ecosystem",
+
+ // go/trendy/manage/engineers/4908413871882240
+ trendy_team_id: "4908413871882240",
+}
+
+team {
+ name: "trendy_team_cellular_security",
+
+ // go/trendy/manage/engineers/6529004011683840
+ trendy_team_id: "6529004011683840",
+}
+
+team {
+ name: "trendy_team_dhaloni_team",
+
+ // go/trendy/manage/engineers/6213556497448960
+ trendy_team_id: "6213556497448960",
+}
+
+team {
+ name: "trendy_team_applications_google_wide_",
+
+ // go/trendy/manage/engineers/6120993248378880
+ trendy_team_id: "6120993248378880",
+}
+
+team {
+ name: "trendy_team_defunct_system_ui_intelligence_dfeng",
+
+ // go/trendy/manage/engineers/5577284748443648
+ trendy_team_id: "5577284748443648",
+}
+
+team {
+ name: "trendy_team_oslo",
+
+ // go/trendy/manage/engineers/5779594887954432
+ trendy_team_id: "5779594887954432",
+}
+
+team {
+ name: "trendy_team_bluetooth",
+
+ // go/trendy/manage/engineers/6226546364645376
+ trendy_team_id: "6226546364645376",
+}
+
+team {
+ name: "trendy_team_localization",
+
+ // go/trendy/manage/engineers/5751557341446144
+ trendy_team_id: "5751557341446144",
+}
+
+team {
+ name: "trendy_team_ssd_security",
+
+ // go/trendy/manage/engineers/4608065248559104
+ trendy_team_id: "4608065248559104",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_usb",
+
+ // go/trendy/manage/engineers/5100646457802752
+ trendy_team_id: "5100646457802752",
+}
+
+team {
+ name: "trendy_team_hiroshi_team",
+
+ // go/trendy/manage/engineers/5756564662288384
+ trendy_team_id: "5756564662288384",
+}
+
+team {
+ name: "trendy_team_contacts",
+
+ // go/trendy/manage/engineers/4732859818311680
+ trendy_team_id: "4732859818311680",
+}
+
+team {
+ name: "trendy_team_wsd_w2",
+
+ // go/trendy/manage/engineers/5945071387934720
+ trendy_team_id: "5945071387934720",
+}
+
+team {
+ name: "trendy_team_wsd_w51",
+
+ // go/trendy/manage/engineers/5698780783312896
+ trendy_team_id: "5698780783312896",
+}
+
+team {
+ name: "trendy_team_erahm_team",
+
+ // go/trendy/manage/engineers/5666347807309824
+ trendy_team_id: "5666347807309824",
+}
+
+team {
+ name: "trendy_team_wsd_w53",
+
+ // go/trendy/manage/engineers/5841167539109888
+ trendy_team_id: "5841167539109888",
+}
+
+team {
+ name: "trendy_team_framework_overground",
+
+ // go/trendy/manage/engineers/5135830829891584
+ trendy_team_id: "5135830829891584",
+}
+
+team {
+ name: "trendy_team_android_performance_console",
+
+ // go/trendy/manage/engineers/5761662355963904
+ trendy_team_id: "5761662355963904",
+}
+
+team {
+ name: "trendy_team_partner_modem",
+
+ // go/trendy/manage/engineers/6502710329376768
+ trendy_team_id: "6502710329376768",
+}
+
+team {
+ name: "trendy_team_scd_tool",
+
+ // go/trendy/manage/engineers/5225441027555328
+ trendy_team_id: "5225441027555328",
+}
+
+team {
+ name: "trendy_team_gtw_sw",
+
+ // go/trendy/manage/engineers/6069865957687296
+ trendy_team_id: "6069865957687296",
+}
+
+team {
+ name: "trendy_team_wearables",
+
+ // go/trendy/manage/engineers/6122642515820544
+ trendy_team_id: "6122642515820544",
+}
+
+team {
+ name: "trendy_team_android_text",
+
+ // go/trendy/manage/engineers/5194085585289216
+ trendy_team_id: "5194085585289216",
+}
+
+team {
+ name: "trendy_team_android_health",
+
+ // go/trendy/manage/engineers/5177772706004992
+ trendy_team_id: "5177772706004992",
+}
+
+team {
+ name: "trendy_team_wsd_w23",
+
+ // go/trendy/manage/engineers/6191361992556544
+ trendy_team_id: "6191361992556544",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_networking",
+
+ // go/trendy/manage/engineers/6685592469241856
+ trendy_team_id: "6685592469241856",
+}
+
+team {
+ name: "trendy_team_ppi_team",
+
+ // go/trendy/manage/engineers/5171933646848000
+ trendy_team_id: "5171933646848000",
+}
+
+team {
+ name: "trendy_team_ssd_mm_peripheral",
+
+ // go/trendy/manage/engineers/6624019818086400
+ trendy_team_id: "6624019818086400",
+}
+
+team {
+ name: "trendy_team_n_a",
+
+ // go/trendy/manage/engineers/4891189492711424
+ trendy_team_id: "4891189492711424",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_hid_driver",
+
+ // go/trendy/manage/engineers/4534130425102336
+ trendy_team_id: "4534130425102336",
+}
+
+team {
+ name: "trendy_team_wear_wearable_motion_algorithms",
+
+ // go/trendy/manage/engineers/5397550198587392
+ trendy_team_id: "5397550198587392",
+}
+
+team {
+ name: "trendy_team_wear_wear_sysui_notifications",
+
+ // go/trendy/manage/engineers/5256257183055872
+ trendy_team_id: "5256257183055872",
+}
+
+team {
+ name: "trendy_team_android_camera_engprod",
+
+ // go/trendy/manage/engineers/5594382843281408
+ trendy_team_id: "5594382843281408",
+}
+
+team {
+ name: "trendy_team_lockscreen_aod",
+
+ // go/trendy/manage/engineers/5503979641012224
+ trendy_team_id: "5503979641012224",
+}
+
+team {
+ name: "trendy_team_windowing_sdk",
+
+ // go/trendy/manage/engineers/5683037008723968
+ trendy_team_id: "5683037008723968",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_inmarket_power",
+
+ // go/trendy/manage/engineers/6675891331170304
+ trendy_team_id: "6675891331170304",
+}
+
+team {
+ name: "trendy_team_betterbug",
+
+ // go/trendy/manage/engineers/4910400652607488
+ trendy_team_id: "4910400652607488",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_security",
+
+ // go/trendy/manage/engineers/5030277713625088
+ trendy_team_id: "5030277713625088",
+}
+
+team {
+ name: "trendy_team_pixel_energizer",
+
+ // go/trendy/manage/engineers/4970605270302720
+ trendy_team_id: "4970605270302720",
+}
+
+team {
+ name: "trendy_team_fwk_core_networking",
+
+ // go/trendy/manage/engineers/5559692562399232
+ trendy_team_id: "5559692562399232",
+}
+
+team {
+ name: "trendy_team_chromium_webview",
+
+ // go/trendy/manage/engineers/5630061306576896
+ trendy_team_id: "5630061306576896",
+}
+
+team {
+ name: "trendy_team_framework_cdm",
+
+ // go/trendy/manage/engineers/4793721887031296
+ trendy_team_id: "4793721887031296",
+}
+
+team {
+ name: "trendy_team_system_walleye_",
+
+ // go/trendy/manage/engineers/5665245678665728
+ trendy_team_id: "5665245678665728",
+}
+
+team {
+ name: "trendy_team_system_marlin_sailfish_",
+
+ // go/trendy/manage/engineers/4713618364825600
+ trendy_team_id: "4713618364825600",
+}
+
+team {
+ name: "trendy_team_qmc",
+
+ // go/trendy/manage/engineers/5207848841510912
+ trendy_team_id: "5207848841510912",
+}
+
+team {
+ name: "trendy_team_android_wallet_integration",
+
+ // go/trendy/manage/engineers/5785777995153408
+ trendy_team_id: "5785777995153408",
+}
+
+team {
+ name: "trendy_team_noreent_team",
+
+ // go/trendy/manage/engineers/5766299843198976
+ trendy_team_id: "5766299843198976",
+}
+
+team {
+ name: "trendy_team_ink",
+
+ // go/trendy/manage/engineers/6620225162608640
+ trendy_team_id: "6620225162608640",
+}
+
+team {
+ name: "trendy_team_make_pixel",
+
+ // go/trendy/manage/engineers/6140234701864960
+ trendy_team_id: "6140234701864960",
+}
+
+team {
+ name: "trendy_team_chillers_team",
+
+ // go/trendy/manage/engineers/5631647887294464
+ trendy_team_id: "5631647887294464",
+}
+
+team {
+ name: "trendy_team_system_experience",
+
+ // go/trendy/manage/engineers/5083633521950720
+ trendy_team_id: "5083633521950720",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_roosa",
+
+ // go/trendy/manage/engineers/6708067074998272
+ trendy_team_id: "6708067074998272",
+}
+
+team {
+ name: "trendy_team_build",
+
+ // go/trendy/manage/engineers/5542100376354816
+ trendy_team_id: "5542100376354816",
+}
+
+team {
+ name: "trendy_team_play_store",
+
+ // go/trendy/manage/engineers/4803228562489344
+ trendy_team_id: "4803228562489344",
+}
+
+team {
+ name: "trendy_team_clocks",
+
+ // go/trendy/manage/engineers/6327058391007232
+ trendy_team_id: "6327058391007232",
+}
+
+team {
+ name: "trendy_team_asafi_team",
+
+ // go/trendy/manage/engineers/6217735399964672
+ trendy_team_id: "6217735399964672",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_storage",
+
+ // go/trendy/manage/engineers/4644898888089600
+ trendy_team_id: "4644898888089600",
+}
+
+team {
+ name: "trendy_team_play_movies",
+
+ // go/trendy/manage/engineers/4838412934578176
+ trendy_team_id: "4838412934578176",
+}
+
+team {
+ name: "trendy_team_system_hammerhead_camera_",
+
+ // go/trendy/manage/engineers/6597631539019776
+ trendy_team_id: "6597631539019776",
+}
+
+team {
+ name: "trendy_team_wear_wear_sysui_applications",
+
+ // go/trendy/manage/engineers/4929833494544384
+ trendy_team_id: "4929833494544384",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_tpm",
+
+ // go/trendy/manage/engineers/4612922981122048
+ trendy_team_id: "4612922981122048",
+}
+
+team {
+ name: "trendy_team_qmc_script_automation",
+
+ // go/trendy/manage/engineers/5047869899669504
+ trendy_team_id: "5047869899669504",
+}
+
+team {
+ name: "trendy_team_pixel_sw_tpm",
+
+ // go/trendy/manage/engineers/5506916004265984
+ trendy_team_id: "5506916004265984",
+}
+
+team {
+ name: "trendy_team_device_and_factory_tpm",
+
+ // go/trendy/manage/engineers/4574530143911936
+ trendy_team_id: "4574530143911936",
+}
+
+team {
+ name: "trendy_team_pmw_mss",
+
+ // go/trendy/manage/engineers/4525262032896000
+ trendy_team_id: "4525262032896000",
+}
+
+team {
+ name: "trendy_team_wear_wear_esim_and_carriers",
+
+ // go/trendy/manage/engineers/5045168113614848
+ trendy_team_id: "5045168113614848",
+}
+
+team {
+ name: "trendy_team_android_pixel_context_hub",
+
+ // go/trendy/manage/engineers/5375970200944640
+ trendy_team_id: "5375970200944640",
+}
+
+team {
+ name: "trendy_team_pixel_setting_exp",
+
+ // go/trendy/manage/engineers/5758010936295424
+ trendy_team_id: "5758010936295424",
+}
+
+team {
+ name: "trendy_team_defunct_system_ui_intelligence_praveenj",
+
+ // go/trendy/manage/engineers/6648758829711360
+ trendy_team_id: "6648758829711360",
+}
+
+team {
+ name: "trendy_team_system_bullhead_",
+
+ // go/trendy/manage/engineers/4592122329956352
+ trendy_team_id: "4592122329956352",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_fingerprint",
+
+ // go/trendy/manage/engineers/5380181285568512
+ trendy_team_id: "5380181285568512",
+}
+
+team {
+ name: "trendy_team_android_sdlc",
+
+ // go/trendy/manage/engineers/6492896504152064
+ trendy_team_id: "6492896504152064",
+}
+
+team {
+ name: "trendy_team_android_core_graphics_stack",
+
+ // go/trendy/manage/engineers/5260625399644160
+ trendy_team_id: "5260625399644160",
+}
+
+team {
+ name: "trendy_team_accessibility_switch_access",
+
+ // go/trendy/manage/engineers/6026869039857664
+ trendy_team_id: "6026869039857664",
+}
+
+team {
+ name: "trendy_team_sqa_make_pixel_",
+
+ // go/trendy/manage/engineers/5610819853090816
+ trendy_team_id: "5610819853090816",
+}
+
+team {
+ name: "trendy_team_controls",
+
+ // go/trendy/manage/engineers/5005994102259712
+ trendy_team_id: "5005994102259712",
+}
+
+team {
+ name: "trendy_team_renderscript_nnapi",
+
+ // go/trendy/manage/engineers/6527262794842112
+ trendy_team_id: "6527262794842112",
+}
+
+team {
+ name: "trendy_team_test_infrastructure",
+
+ // go/trendy/manage/engineers/5130189115654144
+ trendy_team_id: "5130189115654144",
+}
+
+team {
+ name: "trendy_team_ssd_peripheral",
+
+ // go/trendy/manage/engineers/6314507294867456
+ trendy_team_id: "6314507294867456",
+}
+
+team {
+ name: "trendy_team_device_connectivity_experiences_make_pixel_",
+
+ // go/trendy/manage/engineers/5348586329866240
+ trendy_team_id: "5348586329866240",
+}
+
+team {
+ name: "trendy_team_nandunair_team",
+
+ // go/trendy/manage/engineers/4874500384129024
+ trendy_team_id: "4874500384129024",
+}
+
+team {
+ name: "trendy_team_godofredoc_team",
+
+ // go/trendy/manage/engineers/4892528710156288
+ trendy_team_id: "4892528710156288",
+}
+
+team {
+ name: "trendy_team_gtw_misc",
+
+ // go/trendy/manage/engineers/6437652597178368
+ trendy_team_id: "6437652597178368",
+}
+
+team {
+ name: "trendy_team_perception_virtualization",
+
+ // go/trendy/manage/engineers/5133931925897216
+ trendy_team_id: "5133931925897216",
+}
+
+team {
+ name: "trendy_team_safety_els_earthquake",
+
+ // go/trendy/manage/engineers/6508498165071872
+ trendy_team_id: "6508498165071872",
+}
+
+team {
+ name: "trendy_team_xr_framework",
+
+ // go/trendy/manage/engineers/4798040542445568
+ trendy_team_id: "4798040542445568",
+}
+
+team {
+ name: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+
+ // go/trendy/manage/engineers/6402468225089536
+ trendy_team_id: "6402468225089536",
+}
+
+team {
+ name: "trendy_team_android_media_reliability",
+
+ // go/trendy/manage/engineers/5489323818221568
+ trendy_team_id: "5489323818221568",
+}
+
+team {
+ name: "trendy_team_wear_wear_services",
+
+ // go/trendy/manage/engineers/5140566757179392
+ trendy_team_id: "5140566757179392",
+}
+
+team {
+ name: "trendy_team_qmc_pda",
+
+ // go/trendy/manage/engineers/5155072283377664
+ trendy_team_id: "5155072283377664",
+}
+
+team {
+ name: "trendy_team_vsl",
+
+ // go/trendy/manage/engineers/6562447166930944
+ trendy_team_id: "6562447166930944",
+}
+
+team {
+ name: "trendy_team_android_release_metrics",
+
+ // go/trendy/manage/engineers/6018925759201280
+ trendy_team_id: "6018925759201280",
+}
+
+team {
+ name: "trendy_team_testing",
+
+ // go/trendy/manage/engineers/5892294829801472
+ trendy_team_id: "5892294829801472",
+}
+
+team {
+ name: "trendy_team_deprecated_wallet_integration",
+
+ // go/trendy/manage/engineers/5286726042288128
+ trendy_team_id: "5286726042288128",
+}
+
+team {
+ name: "trendy_team_leonardchan_team",
+
+ // go/trendy/manage/engineers/6260994579005440
+ trendy_team_id: "6260994579005440",
+}
+
+team {
+ name: "trendy_team_system_performance",
+
+ // go/trendy/manage/engineers/5188607388024832
+ trendy_team_id: "5188607388024832",
+}
+
+team {
+ name: "trendy_team_system_power",
+
+ // go/trendy/manage/engineers/4820820748533760
+ trendy_team_id: "4820820748533760",
+}
+
+team {
+ name: "trendy_team_deprecated_gdm_location_ads_marketplaces",
+
+ // go/trendy/manage/engineers/5261636812570624
+ trendy_team_id: "5261636812570624",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_digital_key",
+
+ // go/trendy/manage/engineers/6444896766033920
+ trendy_team_id: "6444896766033920",
+}
+
+team {
+ name: "trendy_team_aosp",
+
+ // go/trendy/manage/engineers/4860855378018304
+ trendy_team_id: "4860855378018304",
+}
+
+team {
+ name: "trendy_team_launcher",
+
+ // go/trendy/manage/engineers/5102295725244416
+ trendy_team_id: "5102295725244416",
+}
+
+team {
+ name: "trendy_team_ime",
+
+ // go/trendy/manage/engineers/6085808876290048
+ trendy_team_id: "6085808876290048",
+}
+
+team {
+ name: "trendy_team_jyotiraju_team",
+
+ // go/trendy/manage/engineers/5720977167253504
+ trendy_team_id: "5720977167253504",
+}
+
+team {
+ name: "trendy_team_camera",
+
+ // go/trendy/manage/engineers/5718022236798976
+ trendy_team_id: "5718022236798976",
+}
+
+team {
+ name: "trendy_team_wear_wear_backup_restore",
+
+ // go/trendy/manage/engineers/4875982171176960
+ trendy_team_id: "4875982171176960",
+}
+
+team {
+ name: "trendy_team_wear_wear_developer_watch_faces_complications",
+
+ // go/trendy/manage/engineers/5638213037096960
+ trendy_team_id: "5638213037096960",
+}
+
+team {
+ name: "trendy_team_mainline_reach",
+
+ // go/trendy/manage/engineers/5701386012098560
+ trendy_team_id: "5701386012098560",
+}
+
+team {
+ name: "trendy_team_ssd_bsp",
+
+ // go/trendy/manage/engineers/5876351911198720
+ trendy_team_id: "5876351911198720",
+}
+
+team {
+ name: "trendy_team_ux_design",
+
+ // go/trendy/manage/engineers/4678433992736768
+ trendy_team_id: "4678433992736768",
+}
+
+team {
+ name: "trendy_team_accessibility_fw_make_pixel",
+
+ // go/trendy/manage/engineers/5522858922868736
+ trendy_team_id: "5522858922868736",
+}
+
+team {
+ name: "trendy_team_wear_wear_media",
+
+ // go/trendy/manage/engineers/5365411545513984
+ trendy_team_id: "5365411545513984",
+}
+
+team {
+ name: "trendy_team_system_angler_",
+
+ // go/trendy/manage/engineers/5593227667046400
+ trendy_team_id: "5593227667046400",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_tools",
+
+ // go/trendy/manage/engineers/6460475572912128
+ trendy_team_id: "6460475572912128",
+}
+
+team {
+ name: "trendy_team_platform_build",
+
+ // go/trendy/manage/engineers/5774403578920960
+ trendy_team_id: "5774403578920960",
+}
+
+team {
+ name: "trendy_team_pchitoor_team",
+
+ // go/trendy/manage/engineers/5962577315266560
+ trendy_team_id: "5962577315266560",
+}
+
+team {
+ name: "trendy_team_multi_device_platform",
+
+ // go/trendy/manage/engineers/5850153090711552
+ trendy_team_id: "5850153090711552",
+}
+
+team {
+ name: "trendy_team_safetynet",
+
+ // go/trendy/manage/engineers/4748802736914432
+ trendy_team_id: "4748802736914432",
+}
+
+team {
+ name: "trendy_team_android_resources",
+
+ // go/trendy/manage/engineers/4678767020703744
+ trendy_team_id: "4678767020703744",
+}
+
+team {
+ name: "trendy_team_joshconner_team",
+
+ // go/trendy/manage/engineers/6226828248383488
+ trendy_team_id: "6226828248383488",
+}
+
+team {
+ name: "trendy_team_qmc_ait",
+
+ // go/trendy/manage/engineers/6175419073953792
+ trendy_team_id: "6175419073953792",
+}
+
+team {
+ name: "trendy_team_pixel_global",
+
+ // go/trendy/manage/engineers/4609714516000768
+ trendy_team_id: "4609714516000768",
+}
+
+team {
+ name: "trendy_team_qa_automation",
+
+ // go/trendy/manage/engineers/6159878303678464
+ trendy_team_id: "6159878303678464",
+}
+
+team {
+ name: "trendy_team_android_gpu",
+
+ // go/trendy/manage/engineers/6105848565104640
+ trendy_team_id: "6105848565104640",
+}
+
+team {
+ name: "trendy_team_qmc_ate",
+
+ // go/trendy/manage/engineers/4819171481092096
+ trendy_team_id: "4819171481092096",
+}
+
+team {
+ name: "trendy_team_hangouts",
+
+ // go/trendy/manage/engineers/6263380004175872
+ trendy_team_id: "6263380004175872",
+}
+
+team {
+ name: "trendy_team_cross_device_control",
+
+ // go/trendy/manage/engineers/5888607197757440
+ trendy_team_id: "5888607197757440",
+}
+
+team {
+ name: "trendy_team_interactions_frameworks",
+
+ // go/trendy/manage/engineers/4795124029489152
+ trendy_team_id: "4795124029489152",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_santoscordon",
+
+ // go/trendy/manage/engineers/6049242537951232
+ trendy_team_id: "6049242537951232",
+}
+
+team {
+ name: "trendy_team_authentication",
+
+ // go/trendy/manage/engineers/5901909380988928
+ trendy_team_id: "5901909380988928",
+}
+
+team {
+ name: "trendy_team_stylus",
+
+ // go/trendy/manage/engineers/5685003218747392
+ trendy_team_id: "5685003218747392",
+}
+
+team {
+ name: "trendy_team_linus_team",
+
+ // go/trendy/manage/engineers/6210035100844032
+ trendy_team_id: "6210035100844032",
+}
+
+team {
+ name: "trendy_team_virtualization",
+
+ // go/trendy/manage/engineers/5117131519787008
+ trendy_team_id: "5117131519787008",
+}
+
+team {
+ name: "trendy_team_billstevenson_team",
+
+ // go/trendy/manage/engineers/5631064744820736
+ trendy_team_id: "5631064744820736",
+}
+
+team {
+ name: "trendy_team_android_smartos",
+
+ // go/trendy/manage/engineers/5637973325414400
+ trendy_team_id: "5637973325414400",
+}
+
+team {
+ name: "trendy_team_art_performance",
+
+ // go/trendy/manage/engineers/6210603446042624
+ trendy_team_id: "6210603446042624",
+}
+
+team {
+ name: "trendy_team_ssd",
+
+ // go/trendy/manage/engineers/5858759725154304
+ trendy_team_id: "5858759725154304",
+}
+
+team {
+ name: "trendy_team_abc_engops",
+
+ // go/trendy/manage/engineers/5273578928504832
+ trendy_team_id: "5273578928504832",
+}
+
+team {
+ name: "trendy_team_wear_weather_android_app",
+
+ // go/trendy/manage/engineers/6496415568003072
+ trendy_team_id: "6496415568003072",
+}
+
+team {
+ name: "trendy_team_rubidium_sdk_runtime",
+
+ // go/trendy/manage/engineers/6286508355911680
+ trendy_team_id: "6286508355911680",
+}
+
+team {
+ name: "trendy_team_borthakur_team",
+
+ // go/trendy/manage/engineers/4962243059023872
+ trendy_team_id: "4962243059023872",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_graphics",
+
+ // go/trendy/manage/engineers/5791644907110400
+ trendy_team_id: "5791644907110400",
+}
+
+team {
+ name: "trendy_team_make_creative_android_key_experience",
+
+ // go/trendy/manage/engineers/5716738515173376
+ trendy_team_id: "5716738515173376",
+}
+
+team {
+ name: "trendy_team_pmw_l1rf",
+
+ // go/trendy/manage/engineers/5302906915684352
+ trendy_team_id: "5302906915684352",
+}
+
+team {
+ name: "trendy_team_test_eng_comms_power",
+
+ // go/trendy/manage/engineers/6632815911108608
+ trendy_team_id: "6632815911108608",
+}
+
+team {
+ name: "trendy_team_wear_wear_architecture_group",
+
+ // go/trendy/manage/engineers/5609928060207104
+ trendy_team_id: "5609928060207104",
+}
+
+team {
+ name: "trendy_team_wear_wear_esim_carriers",
+
+ // go/trendy/manage/engineers/5928361498935296
+ trendy_team_id: "5928361498935296",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_gps",
+
+ // go/trendy/manage/engineers/4920660539899904
+ trendy_team_id: "4920660539899904",
+}
+
+team {
+ name: "trendy_team_adversarial_code_ai_arc_ai_",
+
+ // go/trendy/manage/engineers/4850213657673728
+ trendy_team_id: "4850213657673728",
+}
+
+team {
+ name: "trendy_team_android_binary_transparency",
+
+ // go/trendy/manage/engineers/6585365243002880
+ trendy_team_id: "6585365243002880",
+}
+
+team {
+ name: "trendy_team_test_eng_automotive_tv",
+
+ // go/trendy/manage/engineers/6156177620467712
+ trendy_team_id: "6156177620467712",
+}
+
+team {
+ name: "trendy_team_tgosselaar_team",
+
+ // go/trendy/manage/engineers/4897638077071360
+ trendy_team_id: "4897638077071360",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_wifi",
+
+ // go/trendy/manage/engineers/4776219313340416
+ trendy_team_id: "4776219313340416",
+}
+
+team {
+ name: "trendy_team_setup_wizard",
+
+ // go/trendy/manage/engineers/5417305806602240
+ trendy_team_id: "5417305806602240",
+}
+
+team {
+ name: "trendy_team_security_validation_engineering_sve_",
+
+ // go/trendy/manage/engineers/5850943050907648
+ trendy_team_id: "5850943050907648",
+}
+
+team {
+ name: "trendy_team_wsd_function",
+
+ // go/trendy/manage/engineers/6650408097153024
+ trendy_team_id: "6650408097153024",
+}
+
+team {
+ name: "trendy_team_platform_product_mgrs",
+
+ // go/trendy/manage/engineers/6483282329731072
+ trendy_team_id: "6483282329731072",
+}
+
+team {
+ name: "trendy_team_partner_telephony",
+
+ // go/trendy/manage/engineers/5767882120265728
+ trendy_team_id: "5767882120265728",
+}
+
+team {
+ name: "trendy_team_crjohns_team",
+
+ // go/trendy/manage/engineers/4804101473992704
+ trendy_team_id: "4804101473992704",
+}
+
+team {
+ name: "trendy_team_wsd_ims",
+
+ // go/trendy/manage/engineers/6050624504201216
+ trendy_team_id: "6050624504201216",
+}
+
+team {
+ name: "trendy_team_wear_wear_weather",
+
+ // go/trendy/manage/engineers/5464164419928064
+ trendy_team_id: "5464164419928064",
+}
+
+team {
+ name: "trendy_team_guptaritu_team",
+
+ // go/trendy/manage/engineers/5142679624777728
+ trendy_team_id: "5142679624777728",
+}
+
+team {
+ name: "trendy_team_wear_wear_developer_tools",
+
+ // go/trendy/manage/engineers/6228915878002688
+ trendy_team_id: "6228915878002688",
+}
+
+team {
+ name: "trendy_team_play_books",
+
+ // go/trendy/manage/engineers/5769149527490560
+ trendy_team_id: "5769149527490560",
+}
+
+team {
+ name: "trendy_team_melissadaniels_team",
+
+ // go/trendy/manage/engineers/5715112926281728
+ trendy_team_id: "5715112926281728",
+}
+
+team {
+ name: "trendy_team_wear_shared_context_state",
+
+ // go/trendy/manage/engineers/5329107344588800
+ trendy_team_id: "5329107344588800",
+}
+
+team {
+ name: "trendy_team_motion",
+
+ // go/trendy/manage/engineers/6331351269277696
+ trendy_team_id: "6331351269277696",
+}
+
+team {
+ name: "trendy_team_gpp_on_device",
+
+ // go/trendy/manage/engineers/5181961504980992
+ trendy_team_id: "5181961504980992",
+}
+
+team {
+ name: "trendy_team_android_settings_app",
+
+ // go/trendy/manage/engineers/6204400884154368
+ trendy_team_id: "6204400884154368",
+}
+
+team {
+ name: "trendy_team_l1_inmarket",
+
+ // go/trendy/manage/engineers/5172846450737152
+ trendy_team_id: "5172846450737152",
+}
+
+team {
+ name: "trendy_team_wear_wearflow",
+
+ // go/trendy/manage/engineers/5947250429558784
+ trendy_team_id: "5947250429558784",
+}
+
+team {
+ name: "trendy_team_enterprise",
+
+ // go/trendy/manage/engineers/5366178515910656
+ trendy_team_id: "5366178515910656",
+}
+
+team {
+ name: "trendy_team_deprecated_framework_michaelwr",
+
+ // go/trendy/manage/engineers/4574277124915200
+ trendy_team_id: "4574277124915200",
+}
+
+team {
+ name: "trendy_team_color",
+
+ // go/trendy/manage/engineers/6305208086724608
+ trendy_team_id: "6305208086724608",
+}
+
+team {
+ name: "trendy_team_osbornc_team",
+
+ // go/trendy/manage/engineers/6319504650043392
+ trendy_team_id: "6319504650043392",
+}
+
+team {
+ name: "trendy_team_dialer",
+
+ // go/trendy/manage/engineers/6595982271578112
+ trendy_team_id: "6595982271578112",
+}
+
+team {
+ name: "trendy_team_framework_bpm",
+
+ // go/trendy/manage/engineers/5498119911243776
+ trendy_team_id: "5498119911243776",
+}
+
+team {
+ name: "trendy_team_wsd_core_team",
+
+ // go/trendy/manage/engineers/6683943201800192
+ trendy_team_id: "6683943201800192",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_ril",
+
+ // go/trendy/manage/engineers/4995093341536256
+ trendy_team_id: "4995093341536256",
+}
+
+team {
+ name: "trendy_team_perfetto",
+
+ // go/trendy/manage/engineers/4783987109003264
+ trendy_team_id: "4783987109003264",
+}
+
+team {
+ name: "trendy_team_partner_devrel",
+
+ // go/trendy/manage/engineers/4537696504381440
+ trendy_team_id: "4537696504381440",
+}
+
+team {
+ name: "trendy_team_fwk_thread_network",
+
+ // go/trendy/manage/engineers/5094685775134720
+ trendy_team_id: "5094685775134720",
+}
+
+team {
+ name: "trendy_team_thatguy_team",
+
+ // go/trendy/manage/engineers/5688369775542272
+ trendy_team_id: "5688369775542272",
+}
+
+team {
+ name: "trendy_team_finder",
+
+ // go/trendy/manage/engineers/6492831025364992
+ trendy_team_id: "6492831025364992",
+}
+
+team {
+ name: "trendy_team_boot_time",
+
+ // go/trendy/manage/engineers/6017089399554048
+ trendy_team_id: "6017089399554048",
+}
+
+team {
+ name: "trendy_team_wear_wcs_beto",
+
+ // go/trendy/manage/engineers/4910945436336128
+ trendy_team_id: "4910945436336128",
+}
+
+team {
+ name: "trendy_team_responsible_apis",
+
+ // go/trendy/manage/engineers/5651962854178816
+ trendy_team_id: "5651962854178816",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_corebsp",
+
+ // go/trendy/manage/engineers/4626673699553280
+ trendy_team_id: "4626673699553280",
+}
+
+team {
+ name: "trendy_team_education",
+
+ // go/trendy/manage/engineers/4889540225269760
+ trendy_team_id: "4889540225269760",
+}
+
+team {
+ name: "trendy_team_alarm_clock",
+
+ // go/trendy/manage/engineers/6193011259998208
+ trendy_team_id: "6193011259998208",
+}
+
+team {
+ name: "trendy_team_pmw_tvc",
+
+ // go/trendy/manage/engineers/4526288764960768
+ trendy_team_id: "4526288764960768",
+}
+
+team {
+ name: "trendy_team_deprecated_pixel_wifi",
+
+ // go/trendy/manage/engineers/5741405413900288
+ trendy_team_id: "5741405413900288",
+}
+
+team {
+ name: "trendy_team_abdulla_team",
+
+ // go/trendy/manage/engineers/6223585145421824
+ trendy_team_id: "6223585145421824",
+}
+
+team {
+ name: "trendy_team_hardware",
+
+ // go/trendy/manage/engineers/5357382422888448
+ trendy_team_id: "5357382422888448",
+}
+
+team {
+ name: "trendy_team_status_bar",
+
+ // go/trendy/manage/engineers/6329516043173888
+ trendy_team_id: "6329516043173888",
+}
+
+team {
+ name: "trendy_team_wear_wear_sysui_big_picture",
+
+ // go/trendy/manage/engineers/5081356719521792
+ trendy_team_id: "5081356719521792",
+}
+
+team {
+ name: "trendy_team_wear_material_design_for_wearos",
+
+ // go/trendy/manage/engineers/4792942333952000
+ trendy_team_id: "4792942333952000",
+}
+
+team {
+ name: "trendy_team_platform_security",
+
+ // go/trendy/manage/engineers/5243033213599744
+ trendy_team_id: "5243033213599744",
+}
+
+team {
+ name: "trendy_team_llvm_and_toolchains",
+
+ // go/trendy/manage/engineers/5990701120487424
+ trendy_team_id: "5990701120487424",
+}
+
+team {
+ name: "trendy_team_jmccandless_team",
+
+ // go/trendy/manage/engineers/5227794226380800
+ trendy_team_id: "5227794226380800",
+}
+
+team {
+ name: "trendy_team_safety_center",
+
+ // go/trendy/manage/engineers/5930273843609600
+ trendy_team_id: "5930273843609600",
+}
+
+team {
+ name: "trendy_team_wear_wear_systems_engineering_and_devices",
+
+ // go/trendy/manage/engineers/5407847298793472
+ trendy_team_id: "5407847298793472",
+}
+
+team {
+ name: "trendy_team_framework_android_multiuser",
+
+ // go/trendy/manage/engineers/5981525732392960
+ trendy_team_id: "5981525732392960",
+}
+
+team {
+ name: "trendy_team_deprecated_test2",
+
+ // go/trendy/manage/engineers/4590958690074624
+ trendy_team_id: "4590958690074624",
+}
+
+team {
+ name: "trendy_team_qmc_iqt_tpe",
+
+ // go/trendy/manage/engineers/6668000283197440
+ trendy_team_id: "6668000283197440",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_factory",
+
+ // go/trendy/manage/engineers/6267032573739008
+ trendy_team_id: "6267032573739008",
+}
+
+team {
+ name: "trendy_team_automotive",
+
+ // go/trendy/manage/engineers/5770798794932224
+ trendy_team_id: "5770798794932224",
+}
+
+team {
+ name: "trendy_team_camera_htc_lg_qualcomm",
+
+ // go/trendy/manage/engineers/6332099480911872
+ trendy_team_id: "6332099480911872",
+}
+
+team {
+ name: "trendy_team_rkp_keystore",
+
+ // go/trendy/manage/engineers/5634304374505472
+ trendy_team_id: "5634304374505472",
+}
+
+team {
+ name: "trendy_team_wear_wear_watch_faces",
+
+ // go/trendy/manage/engineers/5885708195495936
+ trendy_team_id: "5885708195495936",
+}
+
+team {
+ name: "trendy_team_arc_app_compat",
+
+ // go/trendy/manage/engineers/4811894441279488
+ trendy_team_id: "4811894441279488",
+}
+
+team {
+ name: "trendy_team_psohn_team",
+
+ // go/trendy/manage/engineers/4852673947009024
+ trendy_team_id: "4852673947009024",
+}
+
+team {
+ name: "trendy_team_hollande_team",
+
+ // go/trendy/manage/engineers/5356533186723840
+ trendy_team_id: "5356533186723840",
+}
+
+team {
+ name: "trendy_team_wear_wear_partner_programs_and_engineering_team",
+
+ // go/trendy/manage/engineers/4934997571960832
+ trendy_team_id: "4934997571960832",
+}
+
+team {
+ name: "trendy_team_pixel_connectivity_nfc",
+
+ // go/trendy/manage/engineers/5631272051965952
+ trendy_team_id: "5631272051965952",
+}
+
+team {
+ name: "trendy_team_wear_wear_ios_connectivity",
+
+ // go/trendy/manage/engineers/4702455644192768
+ trendy_team_id: "4702455644192768",
+}
+
+team {
+ name: "trendy_team_arc_",
+
+ // go/trendy/manage/engineers/4556937957867520
+ trendy_team_id: "4556937957867520",
+}
+
+team {
+ name: "trendy_team_android_one",
+
+ // go/trendy/manage/engineers/6272176097198080
+ trendy_team_id: "6272176097198080",
+}
+
+team {
+ name: "trendy_team_ccherubino_team",
+
+ // go/trendy/manage/engineers/4846471192150016
+ trendy_team_id: "4846471192150016",
+}
+
+team {
+ name: "trendy_team_deprecated_bluetooth_and_nfc",
+
+ // go/trendy/manage/engineers/5259280851435520
+ trendy_team_id: "5259280851435520",
+}
+
+team {
+ name: "trendy_team_test_eng_afw_auth_location_camera_media",
+
+ // go/trendy/manage/engineers/6298564376264704
+ trendy_team_id: "6298564376264704",
+}
+
+team {
+ name: "trendy_team_frousseau_team",
+
+ // go/trendy/manage/engineers/5718296436572160
+ trendy_team_id: "5718296436572160",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_touch_haptic",
+
+ // go/trendy/manage/engineers/6469264330096640
+ trendy_team_id: "6469264330096640",
+}
+
+team {
+ name: "trendy_team_partner_eng",
+
+ // go/trendy/manage/engineers/5172664469422080
+ trendy_team_id: "5172664469422080",
+}
+
+team {
+ name: "trendy_team_dogfooders",
+
+ // go/trendy/manage/engineers/4643249620647936
+ trendy_team_id: "4643249620647936",
+}
+
+team {
+ name: "trendy_team_system_external_",
+
+ // go/trendy/manage/engineers/5558043294957568
+ trendy_team_id: "5558043294957568",
+}
+
+team {
+ name: "trendy_team_foundations",
+
+ // go/trendy/manage/engineers/6216250952679424
+ trendy_team_id: "6216250952679424",
+}
+
+team {
+ name: "trendy_team_camera_framework",
+
+ // go/trendy/manage/engineers/6455244783222784
+ trendy_team_id: "6455244783222784",
+}
+
+team {
+ name: "trendy_team_bugjuggler",
+
+ // go/trendy/manage/engineers/6472836969267200
+ trendy_team_id: "6472836969267200",
+}
+
+team {
+ name: "trendy_team_cligh_team",
+
+ // go/trendy/manage/engineers/6273778455314432
+ trendy_team_id: "6273778455314432",
+}
+
+team {
+ name: "trendy_team_android_testing_experiences",
+
+ // go/trendy/manage/engineers/5653137056366592
+ trendy_team_id: "5653137056366592",
+}
+
+team {
+ name: "trendy_team_dx",
+
+ // go/trendy/manage/engineers/5677977168281600
+ trendy_team_id: "5677977168281600",
+}
+
+team {
+ name: "trendy_team_framework_backstage_power",
+
+ // go/trendy/manage/engineers/6314066964283392
+ trendy_team_id: "6314066964283392",
+}
+
+team {
+ name: "trendy_team_rlb_team",
+
+ // go/trendy/manage/engineers/5206858878058496
+ trendy_team_id: "5206858878058496",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_pts",
+
+ // go/trendy/manage/engineers/5553725663510528
+ trendy_team_id: "5553725663510528",
+}
+
+team {
+ name: "trendy_team_keir_team",
+
+ // go/trendy/manage/engineers/5731700089978880
+ trendy_team_id: "5731700089978880",
+}
+
+team {
+ name: "trendy_team_system_taimen_",
+
+ // go/trendy/manage/engineers/6421709678575616
+ trendy_team_id: "6421709678575616",
+}
+
+team {
+ name: "trendy_team_security_response",
+
+ // go/trendy/manage/engineers/5031926981066752
+ trendy_team_id: "5031926981066752",
+}
+
+team {
+ name: "trendy_team_preload_safety",
+
+ // go/trendy/manage/engineers/4584609580744704
+ trendy_team_id: "4584609580744704",
+}
+
+team {
+ name: "trendy_team_pixel_customizations_make_",
+
+ // go/trendy/manage/engineers/5155643836366848
+ trendy_team_id: "5155643836366848",
+}
+
+team {
+ name: "trendy_team_tooltopia",
+
+ // go/trendy/manage/engineers/5456472883101696
+ trendy_team_id: "5456472883101696",
+}
+
+team {
+ name: "trendy_team_accessibility_live_transcribe",
+
+ // go/trendy/manage/engineers/6299695642345472
+ trendy_team_id: "6299695642345472",
+}
+
+team {
+ name: "trendy_team_trusty",
+
+ // go/trendy/manage/engineers/5109319549616128
+ trendy_team_id: "5109319549616128",
+}
+
+team {
+ name: "trendy_team_amathes_team",
+
+ // go/trendy/manage/engineers/5157715862257664
+ trendy_team_id: "5157715862257664",
+}
+
+team {
+ name: "trendy_team_wear_wear_developer_android_devrel_",
+
+ // go/trendy/manage/engineers/5861820594028544
+ trendy_team_id: "5861820594028544",
+}
+
+team {
+ name: "trendy_team_overview",
+
+ // go/trendy/manage/engineers/5071790575550464
+ trendy_team_id: "5071790575550464",
+}
+
+team {
+ name: "trendy_team_android_sensors",
+
+ // go/trendy/manage/engineers/4776371090259968
+ trendy_team_id: "4776371090259968",
+}
+
+team {
+ name: "trendy_team_ui_toolkit",
+
+ // go/trendy/manage/engineers/5638857399599104
+ trendy_team_id: "5638857399599104",
+}
+
+team {
+ name: "trendy_team_gesture_nav",
+
+ // go/trendy/manage/engineers/6304405391310848
+ trendy_team_id: "6304405391310848",
+}
+
+team {
+ name: "trendy_team_qmc_wifi_storage",
+
+ // go/trendy/manage/engineers/4924724597358592
+ trendy_team_id: "4924724597358592",
+}
+
+team {
+ name: "trendy_team_wsd_w11",
+
+ // go/trendy/manage/engineers/5929128469331968
+ trendy_team_id: "5929128469331968",
+}
+
+team {
+ name: "trendy_team_fsamuel_team",
+
+ // go/trendy/manage/engineers/5753514497310720
+ trendy_team_id: "5753514497310720",
+}
+
+team {
+ name: "trendy_team_pixel_haptic",
+
+ // go/trendy/manage/engineers/5919013003493376
+ trendy_team_id: "5919013003493376",
+}
+
+team {
+ name: "trendy_team_pixel_retention",
+
+ // go/trendy/manage/engineers/5647985290805248
+ trendy_team_id: "5647985290805248",
+}
+
+team {
+ name: "trendy_team_pixel_onboarding",
+
+ // go/trendy/manage/engineers/5531340811960320
+ trendy_team_id: "5531340811960320",
+}
+
+team {
+ name: "trendy_team_wsd_standard",
+
+ // go/trendy/manage/engineers/6296915108823040
+ trendy_team_id: "6296915108823040",
+}
+
+team {
+ name: "trendy_team_art_mainline",
+
+ // go/trendy/manage/engineers/5733965155401728
+ trendy_team_id: "5733965155401728",
+}
+
+team {
+ name: "trendy_team_shade",
+
+ // go/trendy/manage/engineers/5646715170226176
+ trendy_team_id: "5646715170226176",
+}
+
+team {
+ name: "trendy_team_gchips_compute_sw",
+
+ // go/trendy/manage/engineers/6245787818131456
+ trendy_team_id: "6245787818131456",
+}
+
+team {
+ name: "trendy_team_haptics_framework",
+
+ // go/trendy/manage/engineers/5895438509441024
+ trendy_team_id: "5895438509441024",
+}
+
+team {
+ name: "trendy_team_accessibility_braille",
+
+ // go/trendy/manage/engineers/4992530205933568
+ trendy_team_id: "4992530205933568",
+}
+
+team {
+ name: "trendy_team_qmc_utd",
+
+ // go/trendy/manage/engineers/5524508190310400
+ trendy_team_id: "5524508190310400",
+}
+
+team {
+ name: "trendy_team_android_on",
+
+ // go/trendy/manage/engineers/4539345771823104
+ trendy_team_id: "4539345771823104",
+}
+
+team {
+ name: "trendy_team_fit",
+
+ // go/trendy/manage/engineers/5628412039135232
+ trendy_team_id: "5628412039135232",
+}
+
+team {
+ name: "trendy_team_fwk_uwb",
+
+ // go/trendy/manage/engineers/5983733408235520
+ trendy_team_id: "5983733408235520",
+}
+
+team {
+ name: "trendy_team_aidroid",
+
+ // go/trendy/manage/engineers/4697675446222848
+ trendy_team_id: "4697675446222848",
+}
+
+team {
+ name: "trendy_team_deprecated_test1",
+
+ // go/trendy/manage/engineers/6212752467460096
+ trendy_team_id: "6212752467460096",
+}
+
+team {
+ name: "trendy_team_wear_wear_releases_telemetry_and_analytics",
+
+ // go/trendy/manage/engineers/5892057298010112
+ trendy_team_id: "5892057298010112",
+}
+
+team {
+ name: "trendy_team_context_hub",
+
+ // go/trendy/manage/engineers/5080704467501056
+ trendy_team_id: "5080704467501056",
+}
+
+team {
+ name: "trendy_team_carrier_follow_up",
+
+ // go/trendy/manage/engineers/6615223725064192
+ trendy_team_id: "6615223725064192",
+}
+
+team {
+ name: "trendy_team_accessibility_live_caption",
+
+ // go/trendy/manage/engineers/4764529665409024
+ trendy_team_id: "4764529665409024",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_display",
+
+ // go/trendy/manage/engineers/6261730736734208
+ trendy_team_id: "6261730736734208",
+}
+
+team {
+ name: "trendy_team_foundation_security_rust_pkvm_",
+
+ // go/trendy/manage/engineers/5071354421084160
+ trendy_team_id: "5071354421084160",
+}
+
+team {
+ name: "trendy_team_pixel_repair_mode",
+
+ // go/trendy/manage/engineers/6083775867813888
+ trendy_team_id: "6083775867813888",
+}
+
+team {
+ name: "trendy_team_capture_and_share",
+
+ // go/trendy/manage/engineers/5644523746787328
+ trendy_team_id: "5644523746787328",
+}
+
+team {
+ name: "trendy_team_keep",
+
+ // go/trendy/manage/engineers/5839518271668224
+ trendy_team_id: "5839518271668224",
+}
+
+team {
+ name: "trendy_team_aaos_security",
+
+ // go/trendy/manage/engineers/6264394363076608
+ trendy_team_id: "6264394363076608",
+}
+
+team {
+ name: "trendy_team_zero_jank",
+
+ // go/trendy/manage/engineers/4764874133897216
+ trendy_team_id: "4764874133897216",
+}
+
+team {
+ name: "trendy_team_android_unified_core_infrastructure",
+
+ // go/trendy/manage/engineers/5842172961914880
+ trendy_team_id: "5842172961914880",
+}
+
+team {
+ name: "trendy_team_android_media_codec_framework",
+
+ // go/trendy/manage/engineers/4943966050844672
+ trendy_team_id: "4943966050844672",
+}
+
+team {
+ name: "trendy_team_system_gn_",
+
+ // go/trendy/manage/engineers/4785636376444928
+ trendy_team_id: "4785636376444928",
+}
+
+team {
+ name: "trendy_team_deprecated_android_auth_client",
+
+ // go/trendy/manage/engineers/5471731632177152
+ trendy_team_id: "5471731632177152",
+}
+
+team {
+ name: "trendy_team_wear_wear_ios_companion_sdk",
+
+ // go/trendy/manage/engineers/5737044865089536
+ trendy_team_id: "5737044865089536",
+}
+
+team {
+ name: "trendy_team_nearby",
+
+ // go/trendy/manage/engineers/4959908969447424
+ trendy_team_id: "4959908969447424",
+}
+
+team {
+ name: "trendy_team_camerax_make_pixel_",
+
+ // go/trendy/manage/engineers/4521753585778688
+ trendy_team_id: "4521753585778688",
+}
+
+team {
+ name: "trendy_team_wear_wear_health_services",
+
+ // go/trendy/manage/engineers/6526182686097408
+ trendy_team_id: "6526182686097408",
+}
+
+team {
+ name: "trendy_team_media_volume",
+
+ // go/trendy/manage/engineers/6360142070841344
+ trendy_team_id: "6360142070841344",
+}
+
+team {
+ name: "trendy_team_large_screen_experiences_sysui",
+
+ // go/trendy/manage/engineers/5855214130069504
+ trendy_team_id: "5855214130069504",
+}
+
+team {
+ name: "trendy_team_mainline_engprod",
+
+ // go/trendy/manage/engineers/5474634789847040
+ trendy_team_id: "5474634789847040",
+}
+
+team {
+ name: "trendy_team_wear_wear_power_foundations",
+
+ // go/trendy/manage/engineers/6292909196214272
+ trendy_team_id: "6292909196214272",
+}
+
+team {
+ name: "trendy_team_windowing_tools",
+
+ // go/trendy/manage/engineers/6382778382188544
+ trendy_team_id: "6382778382188544",
+}
+
+team {
+ name: "trendy_team_android_framework_appcompat",
+
+ // go/trendy/manage/engineers/5383770701955072
+ trendy_team_id: "5383770701955072",
+}
+
+team {
+ name: "trendy_team_fitbit",
+
+ // go/trendy/manage/engineers/6497885327360000
+ trendy_team_id: "6497885327360000",
+}
+
+team {
+ name: "trendy_team_overdrive",
+
+ // go/trendy/manage/engineers/4961558236889088
+ trendy_team_id: "4961558236889088",
+}
+
+team {
+ name: "trendy_team_framework_android_packages",
+
+ // go/trendy/manage/engineers/5989762407104512
+ trendy_team_id: "5989762407104512",
+}
+
+team {
+ name: "trendy_team_tkilbourn_team",
+
+ // go/trendy/manage/engineers/4856646707871744
+ trendy_team_id: "4856646707871744",
+}
+
+team {
+ name: "trendy_team_large_screen_experiences_platform",
+
+ // go/trendy/manage/engineers/4826462937317376
+ trendy_team_id: "4826462937317376",
+}
+
+team {
+ name: "trendy_team_pixel_system_service",
+
+ // go/trendy/manage/engineers/5802643790135296
+ trendy_team_id: "5802643790135296",
+}
+
+team {
+ name: "trendy_team_android_developer_tools",
+
+ // go/trendy/manage/engineers/6201807353020416
+ trendy_team_id: "6201807353020416",
+}
+
+team {
+ name: "trendy_team_autofill",
+
+ // go/trendy/manage/engineers/4676203460329472
+ trendy_team_id: "4676203460329472",
+}
+
+team {
+ name: "trendy_team_wsd_w5",
+
+ // go/trendy/manage/engineers/4627306702045184
+ trendy_team_id: "4627306702045184",
+}
+
+team {
+ name: "trendy_team_wear_wear_xfood_xwear_and_logistics",
+
+ // go/trendy/manage/engineers/6599089311350784
+ trendy_team_id: "6599089311350784",
+}
+
+team {
+ name: "trendy_team_android_media_drm",
+
+ // go/trendy/manage/engineers/5311752690335744
+ trendy_team_id: "5311752690335744",
+}
+
+team {
+ name: "trendy_team_nsylvain_team",
+
+ // go/trendy/manage/engineers/5129062893912064
+ trendy_team_id: "5129062893912064",
+}
+
+team {
+ name: "trendy_team_fwk_wifi_hal",
+
+ // go/trendy/manage/engineers/5470082364735488
+ trendy_team_id: "5470082364735488",
+}
+
+team {
+ name: "trendy_team_tombergan_team",
+
+ // go/trendy/manage/engineers/5764031194497024
+ trendy_team_id: "5764031194497024",
+}
+
+team {
+ name: "trendy_team_wear_wear_platform_program_partner_eng_",
+
+ // go/trendy/manage/engineers/5472261070815232
+ trendy_team_id: "5472261070815232",
+}
+
+team {
+ name: "trendy_team_masd_pixel_key_experiences",
+
+ // go/trendy/manage/engineers/4873597306667008
+ trendy_team_id: "4873597306667008",
+}
+
+team {
+ name: "trendy_team_external",
+
+ // go/trendy/manage/engineers/6033032318156800
+ trendy_team_id: "6033032318156800",
+}
+
+team {
+ name: "trendy_team_fwk_telephony",
+
+ // go/trendy/manage/engineers/5663596411224064
+ trendy_team_id: "5663596411224064",
+}
+
+team {
+ name: "trendy_team_customization_picker",
+
+ // go/trendy/manage/engineers/6311142173081600
+ trendy_team_id: "6311142173081600",
+}
+
+team {
+ name: "trendy_team_android_test_surfaces",
+
+ // go/trendy/manage/engineers/4879149651099648
+ trendy_team_id: "4879149651099648",
+}
+
+team {
+ name: "trendy_team_instant_apps",
+
+ // go/trendy/manage/engineers/6720776841330688
+ trendy_team_id: "6720776841330688",
+}
+
+team {
+ name: "trendy_team_accessibility_aas",
+
+ // go/trendy/manage/engineers/6043198228299776
+ trendy_team_id: "6043198228299776",
+}
+
+team {
+ name: "trendy_team_android_engprod_lon",
+
+ // go/trendy/manage/engineers/5432243163791360
+ trendy_team_id: "5432243163791360",
+}
+
+team {
+ name: "trendy_team_pmw_mce",
+
+ // go/trendy/manage/engineers/6424723868909568
+ trendy_team_id: "6424723868909568",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_kernel",
+
+ // go/trendy/manage/engineers/5436802711846912
+ trendy_team_id: "5436802711846912",
+}
+
+team {
+ name: "trendy_team_nga",
+
+ // go/trendy/manage/engineers/5594876934488064
+ trendy_team_id: "5594876934488064",
+}
+
+team {
+ name: "trendy_team_web_on_android_performance",
+
+ // go/trendy/manage/engineers/5864851748847616
+ trendy_team_id: "5864851748847616",
+}
+
+team {
+ name: "trendy_team_reveman_team",
+
+ // go/trendy/manage/engineers/5113274057261056
+ trendy_team_id: "5113274057261056",
+}
+
+team {
+ name: "trendy_team_test_eng_ota_framework_sysui_suw_abvt_cts",
+
+ // go/trendy/manage/engineers/4653694981111808
+ trendy_team_id: "4653694981111808",
+}
+
+team {
+ name: "trendy_team_backup_restore",
+
+ // go/trendy/manage/engineers/5049519167111168
+ trendy_team_id: "5049519167111168",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_bringup_and_factory",
+
+ // go/trendy/manage/engineers/5999752665268224
+ trendy_team_id: "5999752665268224",
+}
+
+team {
+ name: "trendy_team_wear_wear_calling_messaging",
+
+ // go/trendy/manage/engineers/5401274807648256
+ trendy_team_id: "5401274807648256",
+}
+
+team {
+ name: "trendy_team_android_imaging",
+
+ // go/trendy/manage/engineers/5838538113384448
+ trendy_team_id: "5838538113384448",
+}
+
+team {
+ name: "trendy_team_pixel_system_sw_sensor",
+
+ // go/trendy/manage/engineers/4904417557643264
+ trendy_team_id: "4904417557643264",
+}
+
+team {
+ name: "trendy_team_dogfood_triage",
+
+ // go/trendy/manage/engineers/5130823981236224
+ trendy_team_id: "5130823981236224",
+}
+
+team {
+ name: "trendy_team_input_method_framework",
+
+ // go/trendy/manage/engineers/6394201770459136
+ trendy_team_id: "6394201770459136",
+}
+
+team {
+ name: "trendy_team_wear_wear_platform_dev_lead_device_program_",
+
+ // go/trendy/manage/engineers/4642393194135552
+ trendy_team_id: "4642393194135552",
+}
+
+team {
+ name: "trendy_team_wear_bona_companion",
+
+ // go/trendy/manage/engineers/4721932784009216
+ trendy_team_id: "4721932784009216",
+}
+
+team {
+ name: "trendy_team_wear_wear_compose",
+
+ // go/trendy/manage/engineers/4958404271308800
+ trendy_team_id: "4958404271308800",
+}
+
+team {
+ name: "trendy_team_system_fugu_",
+
+ // go/trendy/manage/engineers/5682837864710144
+ trendy_team_id: "5682837864710144",
+}
+
+team {
+ name: "trendy_team_tvolkert_team",
+
+ // go/trendy/manage/engineers/5093014696525824
+ trendy_team_id: "5093014696525824",
+}
+
+team {
+ name: "trendy_team_media_framework_drm",
+
+ // go/trendy/manage/engineers/5311752690335744
+ trendy_team_id: "5311752690335744",
+}
+
+team {
+ name: "trendy_team_media_framework_audio",
+
+ // go/trendy/manage/engineers/5823575353065472
+ trendy_team_id: "5823575353065472",
+}
+
+team {
+ name: "trendy_team_ar_sensors_context_hub",
+
+ // go/trendy/manage/engineers/4776371090259968
+ trendy_team_id: "4776371090259968",
+}
+
+
+team {
+ name: "trendy_team_media_codec_framework",
+
+ // go/trendy/manage/engineers/4943966050844672
+ trendy_team_id: "4943966050844672",
+}
diff --git a/teams/OWNERS b/teams/OWNERS
new file mode 100644
index 0000000..85e69f3
--- /dev/null
+++ b/teams/OWNERS
@@ -0,0 +1,3 @@
+dariofreni@google.com
+ronish@google.com
+caditya@google.com
diff --git a/tools/Android.bp b/tools/Android.bp
index 5c54fcf..0a55ed4 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -18,56 +18,65 @@
}
python_binary_host {
- name: "generate-self-extracting-archive",
- srcs: ["generate-self-extracting-archive.py"],
+ name: "generate-self-extracting-archive",
+ srcs: ["generate-self-extracting-archive.py"],
}
python_binary_host {
- name: "post_process_props",
- srcs: ["post_process_props.py"],
+ name: "post_process_props",
+ srcs: ["post_process_props.py"],
+ libs: [
+ "uffd_gc_utils",
+ ],
}
python_test_host {
- name: "post_process_props_unittest",
- main: "test_post_process_props.py",
- srcs: [
- "post_process_props.py",
- "test_post_process_props.py",
- ],
- test_config: "post_process_props_unittest.xml",
- test_suites: ["general-tests"],
+ name: "post_process_props_unittest",
+ main: "test_post_process_props.py",
+ srcs: [
+ "post_process_props.py",
+ "test_post_process_props.py",
+ ],
+ libs: [
+ "uffd_gc_utils",
+ ],
+ test_config: "post_process_props_unittest.xml",
+ test_suites: ["general-tests"],
}
python_binary_host {
- name: "extract_kernel",
- srcs: ["extract_kernel.py"],
+ name: "extract_kernel",
+ srcs: ["extract_kernel.py"],
}
genrule_defaults {
- name: "extract_kernel_release_defaults",
- tools: ["extract_kernel", "lz4"],
- out: ["kernel_release.txt"],
- cmd: "$(location) --tools lz4:$(location lz4) --input $(in) --output-release > $(out)"
+ name: "extract_kernel_release_defaults",
+ tools: [
+ "extract_kernel",
+ "lz4",
+ ],
+ out: ["kernel_release.txt"],
+ cmd: "$(location) --tools lz4:$(location lz4) --input $(in) --output-release > $(out)",
}
cc_binary_host {
- name: "build-runfiles",
- srcs: ["build-runfiles.cc"],
+ name: "build-runfiles",
+ srcs: ["build-runfiles.cc"],
}
python_binary_host {
- name: "check_radio_versions",
- srcs: ["check_radio_versions.py"],
+ name: "check_radio_versions",
+ srcs: ["check_radio_versions.py"],
}
python_binary_host {
- name: "check_elf_file",
- srcs: ["check_elf_file.py"],
+ name: "check_elf_file",
+ srcs: ["check_elf_file.py"],
}
python_binary_host {
- name: "generate_gts_shared_report",
- srcs: ["generate_gts_shared_report.py"],
+ name: "generate_gts_shared_report",
+ srcs: ["generate_gts_shared_report.py"],
}
python_binary_host {
@@ -77,10 +86,10 @@
"list_files.py",
],
version: {
- py3: {
- embedded_launcher: true,
- }
- }
+ py3: {
+ embedded_launcher: true,
+ },
+ },
}
python_test_host {
@@ -98,11 +107,11 @@
}
python_binary_host {
- name: "characteristics_rro_generator",
- srcs: ["characteristics_rro_generator.py"],
- version: {
- py3: {
- embedded_launcher: true,
+ name: "characteristics_rro_generator",
+ srcs: ["characteristics_rro_generator.py"],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
},
- },
}
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 7b58e94..95f1215 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -1,22 +1,11 @@
-[package]
-name = "aconfig"
-version = "0.1.0"
-edition = "2021"
-build = "build.rs"
+[workspace]
-[features]
-default = ["cargo"]
-cargo = []
+members = [
+ "aconfig",
+ "aconfig_protos",
+ "aconfig_storage_file",
+ "aflags",
+ "printflags"
+]
-[dependencies]
-anyhow = "1.0.69"
-clap = { version = "4.1.8", features = ["derive"] }
-itertools = "0.10.5"
-paste = "1.0.11"
-protobuf = "3.2.0"
-serde = { version = "1.0.152", features = ["derive"] }
-serde_json = "1.0.93"
-tinytemplate = "1.2.1"
-
-[build-dependencies]
-protobuf-codegen = "3.2.0"
+resolver = "2"
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index de8d932..e42b5d3 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -1,25 +1,6 @@
{
"presubmit": [
{
- // Ensure changes on aconfig auto generated library is compatible with
- // test testing filtering logic. Breakage on this test means all tests
- // that using the flag annotations to do filtering will get affected.
- "name": "FlagAnnotationTests",
- "options": [
- {
- "include-filter": "android.cts.flags.tests.FlagAnnotationTest"
- }
- ]
- },
- {
- // Ensure changes on aconfig auto generated library is compatible with
- // test testing filtering logic. Breakage on this test means all tests
- // that using the flag macros to do filtering will get affected.
- "name": "FlagMacrosTests"
- }
- ],
- "postsubmit": [
- {
// aconfig unit tests
"name": "aconfig.test"
},
@@ -58,6 +39,46 @@
{
// printflags unit tests
"name": "printflags.test"
+ },
+ {
+ // aconfig_protos unit tests
+ "name": "aconfig_protos.test"
+ },
+ {
+ // aconfig_storage_file unit tests
+ "name": "aconfig_storage_file.test"
+ },
+ {
+ // Ensure changes on aconfig auto generated library is compatible with
+ // test testing filtering logic. Breakage on this test means all tests
+ // that using the flag annotations to do filtering will get affected.
+ "name": "FlagAnnotationTests",
+ "options": [
+ {
+ "include-filter": "android.cts.flags.tests.FlagAnnotationTest"
+ }
+ ]
+ },
+ {
+ // Ensure changes on aconfig auto generated library is compatible with
+ // test testing filtering logic. Breakage on this test means all tests
+ // that using the flag macros to do filtering will get affected.
+ "name": "FlagMacrosTests"
+ }
+ ],
+ "postsubmit": [
+ {
+ // aconfig_storage read api rust integration tests
+ "name": "aconfig_storage.test.rust"
+ },
+ {
+ // aconfig_storage read api cpp integration tests
+ "name": "aconfig_storage.test.cpp"
+ },
+ {
+ // aflags CLI unit tests
+ // TODO(b/326062088): add to presubmit once proven in postsubmit.
+ "name": "aflags.test"
}
]
}
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
similarity index 83%
rename from tools/aconfig/Android.bp
rename to tools/aconfig/aconfig/Android.bp
index d5b5b8f..164bfe7 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -2,51 +2,6 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// proto libraries for consumers of `aconfig dump --format=protobuf` output
-
-java_library {
- name: "libaconfig_java_proto_lite",
- host_supported: true,
- srcs: ["protos/aconfig.proto"],
- static_libs: ["libprotobuf-java-lite"],
- proto: {
- type: "lite",
- },
- sdk_version: "current",
- min_sdk_version: "UpsideDownCake",
- apex_available: [
- "com.android.configinfrastructure",
- "//apex_available:platform",
- ]
-}
-
-java_library_host {
- name: "libaconfig_java_proto_full",
- srcs: ["protos/aconfig.proto"],
- static_libs: ["libprotobuf-java-full"],
- proto: {
- type: "full",
- },
-}
-
-python_library_host {
- name: "libaconfig_python_proto",
- srcs: ["protos/aconfig.proto"],
- proto: {
- canonical_path_from_root: false,
- },
-}
-
-// host binary: aconfig
-
-rust_protobuf {
- name: "libaconfig_protos",
- protos: ["protos/aconfig.proto"],
- crate_name: "aconfig_protos",
- source_stem: "aconfig_protos",
- host_supported: true,
-}
-
rust_defaults {
name: "aconfig.defaults",
edition: "2021",
@@ -55,6 +10,7 @@
srcs: ["src/main.rs"],
rustlibs: [
"libaconfig_protos",
+ "libaconfig_storage_file",
"libanyhow",
"libclap",
"libitertools",
@@ -63,9 +19,6 @@
"libserde_json",
"libtinytemplate",
],
- proc_macros: [
- "libpaste",
- ]
}
rust_binary_host {
@@ -87,18 +40,21 @@
aconfig_declarations {
name: "aconfig.test.flags",
package: "com.android.aconfig.test",
+ container: "system",
srcs: ["tests/test.aconfig"],
}
aconfig_declarations {
name: "aconfig.test.exported.flags",
package: "com.android.aconfig.test.exported",
+ container: "system",
srcs: ["tests/test_exported.aconfig"],
}
aconfig_declarations {
name: "aconfig.test.forcereadonly.flags",
package: "com.android.aconfig.test.forcereadonly",
+ container: "system",
srcs: ["tests/test_force_read_only.aconfig"],
}
@@ -267,7 +223,7 @@
rust_test {
name: "aconfig.prod_mode.test.rust",
srcs: [
- "tests/aconfig_prod_mode_test.rs"
+ "tests/aconfig_prod_mode_test.rs",
],
rustlibs: [
"libaconfig_test_rust_library",
@@ -285,7 +241,7 @@
rust_test {
name: "aconfig.test_mode.test.rust",
srcs: [
- "tests/aconfig_test_mode_test.rs"
+ "tests/aconfig_test_mode_test.rs",
],
rustlibs: [
"libaconfig_test_rust_library_with_test_mode",
@@ -303,7 +259,7 @@
rust_test {
name: "aconfig.exported_mode.test.rust",
srcs: [
- "tests/aconfig_exported_mode_test.rs"
+ "tests/aconfig_exported_mode_test.rs",
],
rustlibs: [
"libaconfig_test_rust_library_with_exported_mode",
@@ -321,7 +277,7 @@
rust_test {
name: "aconfig.force_read_only_mode.test.rust",
srcs: [
- "tests/aconfig_force_read_only_mode_test.rs"
+ "tests/aconfig_force_read_only_mode_test.rs",
],
rustlibs: [
"libaconfig_test_rust_library_with_force_read_only_mode",
diff --git a/tools/aconfig/aconfig/Cargo.toml b/tools/aconfig/aconfig/Cargo.toml
new file mode 100644
index 0000000..abd3ee0
--- /dev/null
+++ b/tools/aconfig/aconfig/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "aconfig"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+clap = { version = "4.1.8", features = ["derive"] }
+itertools = "0.10.5"
+protobuf = "3.2.0"
+serde = { version = "1.0.152", features = ["derive"] }
+serde_json = "1.0.93"
+tinytemplate = "1.2.1"
+aconfig_protos = { path = "../aconfig_protos" }
+aconfig_storage_file = { path = "../aconfig_storage_file" }
diff --git a/tools/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
similarity index 99%
rename from tools/aconfig/src/codegen/cpp.rs
rename to tools/aconfig/aconfig/src/codegen/cpp.rs
index 1279d8e..cd71b10 100644
--- a/tools/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -19,10 +19,11 @@
use std::path::PathBuf;
use tinytemplate::TinyTemplate;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
use crate::codegen;
use crate::codegen::CodegenMode;
use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_cpp_code<I>(
package: &str,
@@ -136,7 +137,7 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::protos::ProtoParsedFlags;
+ use aconfig_protos::ProtoParsedFlags;
use std::collections::HashMap;
const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"
diff --git a/tools/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
similarity index 94%
rename from tools/aconfig/src/codegen/java.rs
rename to tools/aconfig/aconfig/src/codegen/java.rs
index 78e892b..a18f9a8 100644
--- a/tools/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -20,10 +20,11 @@
use std::path::PathBuf;
use tinytemplate::TinyTemplate;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
use crate::codegen;
use crate::codegen::CodegenMode;
use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_java_code<I>(
package: &str,
@@ -277,8 +278,11 @@
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
+ import java.util.Arrays;
import java.util.HashMap;
+ import java.util.HashSet;
import java.util.Map;
+ import java.util.Set;
/** @hide */
public class FakeFeatureFlagsImpl implements FeatureFlags {
public FakeFeatureFlagsImpl() {
@@ -340,6 +344,13 @@
entry.setValue(null);
}
}
+ public boolean isFlagReadOnlyOptimized(String flagName) {
+ if (mReadOnlyFlagsSet.contains(flagName) &&
+ isOptimizationEnabled()) {
+ return true;
+ }
+ return false;
+ }
private boolean getValue(String flagName) {
Boolean value = this.mFlagMap.get(flagName);
if (value == null) {
@@ -347,6 +358,10 @@
}
return value;
}
+ @com.android.aconfig.annotations.AssumeTrueForR8
+ private boolean isOptimizationEnabled() {
+ return false;
+ }
private Map<String, Boolean> mFlagMap = new HashMap<>(
Map.ofEntries(
Map.entry(Flags.FLAG_DISABLED_RO, false),
@@ -360,6 +375,16 @@
Map.entry(Flags.FLAG_ENABLED_RW, false)
)
);
+ private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+ Arrays.asList(
+ Flags.FLAG_DISABLED_RO,
+ Flags.FLAG_ENABLED_FIXED_RO,
+ Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+ Flags.FLAG_ENABLED_RO,
+ Flags.FLAG_ENABLED_RO_EXPORTED,
+ ""
+ )
+ );
}
"#;
@@ -643,8 +668,11 @@
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
+ import java.util.Arrays;
import java.util.HashMap;
+ import java.util.HashSet;
import java.util.Map;
+ import java.util.Set;
/** @hide */
public class FakeFeatureFlagsImpl implements FeatureFlags {
public FakeFeatureFlagsImpl() {
@@ -676,6 +704,13 @@
entry.setValue(null);
}
}
+ public boolean isFlagReadOnlyOptimized(String flagName) {
+ if (mReadOnlyFlagsSet.contains(flagName) &&
+ isOptimizationEnabled()) {
+ return true;
+ }
+ return false;
+ }
private boolean getValue(String flagName) {
Boolean value = this.mFlagMap.get(flagName);
if (value == null) {
@@ -683,6 +718,10 @@
}
return value;
}
+ @com.android.aconfig.annotations.AssumeTrueForR8
+ private boolean isOptimizationEnabled() {
+ return false;
+ }
private Map<String, Boolean> mFlagMap = new HashMap<>(
Map.ofEntries(
Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
@@ -690,6 +729,11 @@
Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false)
)
);
+ private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+ Arrays.asList(
+ ""
+ )
+ );
}
"#;
@@ -963,8 +1007,11 @@
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
+ import java.util.Arrays;
import java.util.HashMap;
+ import java.util.HashSet;
import java.util.Map;
+ import java.util.Set;
/** @hide */
public class FakeFeatureFlagsImpl implements FeatureFlags {
public FakeFeatureFlagsImpl() {
@@ -1011,6 +1058,13 @@
entry.setValue(null);
}
}
+ public boolean isFlagReadOnlyOptimized(String flagName) {
+ if (mReadOnlyFlagsSet.contains(flagName) &&
+ isOptimizationEnabled()) {
+ return true;
+ }
+ return false;
+ }
private boolean getValue(String flagName) {
Boolean value = this.mFlagMap.get(flagName);
if (value == null) {
@@ -1018,6 +1072,10 @@
}
return value;
}
+ @com.android.aconfig.annotations.AssumeTrueForR8
+ private boolean isOptimizationEnabled() {
+ return false;
+ }
private Map<String, Boolean> mFlagMap = new HashMap<>(
Map.ofEntries(
Map.entry(Flags.FLAG_DISABLED_RO, false),
@@ -1028,6 +1086,17 @@
Map.entry(Flags.FLAG_ENABLED_RW, false)
)
);
+ private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+ Arrays.asList(
+ Flags.FLAG_DISABLED_RO,
+ Flags.FLAG_DISABLED_RW,
+ Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+ Flags.FLAG_ENABLED_FIXED_RO,
+ Flags.FLAG_ENABLED_RO,
+ Flags.FLAG_ENABLED_RW,
+ ""
+ )
+ );
}
"#;
let mut file_set = HashMap::from([
diff --git a/tools/aconfig/src/codegen/mod.rs b/tools/aconfig/aconfig/src/codegen/mod.rs
similarity index 86%
rename from tools/aconfig/src/codegen/mod.rs
rename to tools/aconfig/aconfig/src/codegen/mod.rs
index 64ffa8b..1ea3b37 100644
--- a/tools/aconfig/src/codegen/mod.rs
+++ b/tools/aconfig/aconfig/src/codegen/mod.rs
@@ -18,35 +18,10 @@
pub mod java;
pub mod rust;
+use aconfig_protos::{is_valid_name_ident, is_valid_package_ident};
use anyhow::{ensure, Result};
use clap::ValueEnum;
-pub fn is_valid_name_ident(s: &str) -> bool {
- // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
- if s.contains("__") {
- return false;
- }
- let mut chars = s.chars();
- let Some(first) = chars.next() else {
- return false;
- };
- if !first.is_ascii_lowercase() {
- return false;
- }
- chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
-}
-
-pub fn is_valid_package_ident(s: &str) -> bool {
- if !s.contains('.') {
- return false;
- }
- s.split('.').all(is_valid_name_ident)
-}
-
-pub fn is_valid_container_ident(s: &str) -> bool {
- s.split('.').all(is_valid_name_ident)
-}
-
pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {
ensure!(is_valid_package_ident(package), "bad package");
ensure!(is_valid_name_ident(flag_name), "bad flag name");
@@ -75,6 +50,7 @@
#[cfg(test)]
mod tests {
use super::*;
+ use aconfig_protos::is_valid_container_ident;
#[test]
fn test_is_valid_name_ident() {
diff --git a/tools/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
similarity index 99%
rename from tools/aconfig/src/codegen/rust.rs
rename to tools/aconfig/aconfig/src/codegen/rust.rs
index 8a88ffe..33c3d37 100644
--- a/tools/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -18,10 +18,11 @@
use serde::Serialize;
use tinytemplate::TinyTemplate;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
use crate::codegen;
use crate::codegen::CodegenMode;
use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_rust_code<I>(
package: &str,
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
similarity index 92%
rename from tools/aconfig/src/commands.rs
rename to tools/aconfig/aconfig/src/commands.rs
index 1a8872b..59f349b 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -26,11 +26,12 @@
use crate::codegen::rust::generate_rust_code;
use crate::codegen::CodegenMode;
use crate::dump::{DumpFormat, DumpPredicate};
-use crate::protos::{
+use crate::storage::generate_storage_file;
+use aconfig_protos::{
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
ProtoParsedFlags, ProtoTracepoint,
};
-use crate::storage::generate_storage_files;
+use aconfig_storage_file::StorageFileSelection;
pub struct Input {
pub source: String,
@@ -43,7 +44,7 @@
self.reader
.read_to_end(&mut buffer)
.with_context(|| format!("failed to read {}", self.source))?;
- crate::protos::parsed_flags::try_from_binary_proto(&buffer)
+ aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
.with_context(|| self.error_context())
}
@@ -76,7 +77,7 @@
.read_to_string(&mut contents)
.with_context(|| format!("failed to read {}", input.source))?;
- let flag_declarations = crate::protos::flag_declarations::try_from_text_proto(&contents)
+ let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents)
.with_context(|| input.error_context())?;
ensure!(
package == flag_declarations.package(),
@@ -95,7 +96,7 @@
);
}
for mut flag_declaration in flag_declarations.flag.into_iter() {
- crate::protos::flag_declaration::verify_fields(&flag_declaration)
+ aconfig_protos::flag_declaration::verify_fields(&flag_declaration)
.with_context(|| input.error_context())?;
// create ParsedFlag using FlagDeclaration and default values
@@ -129,7 +130,7 @@
parsed_flag.metadata = Some(metadata).into();
// verify ParsedFlag looks reasonable
- crate::protos::parsed_flag::verify_fields(&parsed_flag)?;
+ aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;
// verify ParsedFlag can be added
ensure!(
@@ -150,10 +151,10 @@
.reader
.read_to_string(&mut contents)
.with_context(|| format!("failed to read {}", input.source))?;
- let flag_values = crate::protos::flag_values::try_from_text_proto(&contents)
+ let flag_values = aconfig_protos::flag_values::try_from_text_proto(&contents)
.with_context(|| input.error_context())?;
for flag_value in flag_values.flag_value.into_iter() {
- crate::protos::flag_value::verify_fields(&flag_value)
+ aconfig_protos::flag_value::verify_fields(&flag_value)
.with_context(|| input.error_context())?;
let Some(parsed_flag) = parsed_flags
@@ -183,8 +184,8 @@
}
// Create a sorted parsed_flags
- crate::protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
- crate::protos::parsed_flags::verify_fields(&parsed_flags)?;
+ aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
+ aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?;
let mut output = Vec::new();
parsed_flags.write_to_vec(&mut output)?;
Ok(output)
@@ -223,7 +224,11 @@
generate_rust_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
}
-pub fn create_storage(caches: Vec<Input>, container: &str) -> Result<Vec<OutputFile>> {
+pub fn create_storage(
+ caches: Vec<Input>,
+ container: &str,
+ file: &StorageFileSelection,
+) -> Result<Vec<u8>> {
let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
.into_iter()
.map(|mut input| input.try_parse_flags())
@@ -231,7 +236,7 @@
.into_iter()
.filter(|pfs| find_unique_container(pfs) == Some(container))
.collect();
- generate_storage_files(container, parsed_flags_vec.iter())
+ generate_storage_file(container, parsed_flags_vec.iter(), file)
}
pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
@@ -286,7 +291,7 @@
let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =
input.iter_mut().map(|i| i.try_parse_flags()).collect();
let parsed_flags: ProtoParsedFlags =
- crate::protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
+ aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {
vec![Box::new(|_| true)]
} else {
@@ -361,7 +366,7 @@
Ok(modified_parsed_flags)
}
-pub fn assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result<HashMap<String, u32>>
+pub fn assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result<HashMap<String, u16>>
where
I: Iterator<Item = &'a ProtoParsedFlag> + Clone,
{
@@ -371,7 +376,13 @@
if package != pf.package() {
return Err(anyhow::anyhow!("encountered a flag not in current package"));
}
- flag_ids.insert(pf.name().to_string(), id_to_assign);
+
+ // put a cap on how many flags a package can contain to 65535
+ if id_to_assign > u16::MAX as u32 {
+ return Err(anyhow::anyhow!("the number of flags in a package cannot exceed 65535"));
+ }
+
+ flag_ids.insert(pf.name().to_string(), id_to_assign as u16);
}
Ok(flag_ids)
}
@@ -379,16 +390,16 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::protos::ProtoFlagPurpose;
+ use aconfig_protos::ProtoFlagPurpose;
#[test]
fn test_parse_flags() {
let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
- crate::protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
+ aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
let enabled_ro =
parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap();
- assert!(crate::protos::parsed_flag::verify_fields(enabled_ro).is_ok());
+ assert!(aconfig_protos::parsed_flag::verify_fields(enabled_ro).is_ok());
assert_eq!("com.android.aconfig.test", enabled_ro.package());
assert_eq!("enabled_ro", enabled_ro.name());
assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description());
@@ -455,7 +466,7 @@
)
.unwrap();
let parsed_flags =
- crate::protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
+ aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
assert_eq!(1, parsed_flags.parsed_flag.len());
let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
@@ -595,7 +606,7 @@
)
.unwrap();
let parsed_flags =
- crate::protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
+ aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
assert_eq!(1, parsed_flags.parsed_flag.len());
let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose());
@@ -693,15 +704,15 @@
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_flag_ids = HashMap::from([
- (String::from("disabled_ro"), 0_u32),
- (String::from("disabled_rw"), 1_u32),
- (String::from("disabled_rw_exported"), 2_u32),
- (String::from("disabled_rw_in_other_namespace"), 3_u32),
- (String::from("enabled_fixed_ro"), 4_u32),
- (String::from("enabled_fixed_ro_exported"), 5_u32),
- (String::from("enabled_ro"), 6_u32),
- (String::from("enabled_ro_exported"), 7_u32),
- (String::from("enabled_rw"), 8_u32),
+ (String::from("disabled_ro"), 0_u16),
+ (String::from("disabled_rw"), 1_u16),
+ (String::from("disabled_rw_exported"), 2_u16),
+ (String::from("disabled_rw_in_other_namespace"), 3_u16),
+ (String::from("enabled_fixed_ro"), 4_u16),
+ (String::from("enabled_fixed_ro_exported"), 5_u16),
+ (String::from("enabled_ro"), 6_u16),
+ (String::from("enabled_ro_exported"), 7_u16),
+ (String::from("enabled_rw"), 8_u16),
]);
assert_eq!(flag_ids, expected_flag_ids);
}
diff --git a/tools/aconfig/src/dump.rs b/tools/aconfig/aconfig/src/dump.rs
similarity index 98%
rename from tools/aconfig/src/dump.rs
rename to tools/aconfig/aconfig/src/dump.rs
index 37368ee..2a29c2b 100644
--- a/tools/aconfig/src/dump.rs
+++ b/tools/aconfig/aconfig/src/dump.rs
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-use crate::protos::{
+use aconfig_protos::{
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint,
};
-use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
+use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
use anyhow::{anyhow, bail, Context, Result};
use protobuf::Message;
@@ -197,8 +197,8 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::protos::ProtoParsedFlags;
use crate::test::parse_test_flags;
+ use aconfig_protos::ProtoParsedFlags;
use protobuf::Message;
fn parse_enabled_ro_flag() -> ProtoParsedFlag {
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
similarity index 91%
rename from tools/aconfig/src/main.rs
rename to tools/aconfig/aconfig/src/main.rs
index 6c4e241..5a4f23c 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -27,9 +27,9 @@
mod codegen;
mod commands;
mod dump;
-mod protos;
mod storage;
+use aconfig_storage_file::StorageFileSelection;
use codegen::CodegenMode;
use dump::DumpFormat;
@@ -56,8 +56,8 @@
.arg(
Arg::new("default-permission")
.long("default-permission")
- .value_parser(protos::flag_permission::parse_from_str)
- .default_value(protos::flag_permission::to_string(
+ .value_parser(aconfig_protos::flag_permission::parse_from_str)
+ .default_value(aconfig_protos::flag_permission::to_string(
&commands::DEFAULT_FLAG_PERMISSION,
)),
)
@@ -135,7 +135,12 @@
.required(true)
.help("The target container for the generated storage file."),
)
- .arg(Arg::new("cache").long("cache").required(true))
+ .arg(
+ Arg::new("file")
+ .long("file")
+ .value_parser(|s: &str| StorageFileSelection::try_from(s)),
+ )
+ .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
.arg(Arg::new("out").long("out").required(true)),
)
}
@@ -208,8 +213,10 @@
get_optional_arg::<String>(sub_matches, "container").map(|c| c.as_str());
let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
let values = open_zero_or_more_files(sub_matches, "values")?;
- let default_permission =
- get_required_arg::<protos::ProtoFlagPermission>(sub_matches, "default-permission")?;
+ let default_permission = get_required_arg::<aconfig_protos::ProtoFlagPermission>(
+ sub_matches,
+ "default-permission",
+ )?;
let output = commands::parse_flags(
package,
container,
@@ -278,14 +285,14 @@
write_output_to_file_or_stdout(path, &output)?;
}
Some(("create-storage", sub_matches)) => {
+ let file = get_required_arg::<StorageFileSelection>(sub_matches, "file")
+ .context("Invalid storage file selection")?;
let cache = open_zero_or_more_files(sub_matches, "cache")?;
let container = get_required_arg::<String>(sub_matches, "container")?;
- let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
- let generated_files = commands::create_storage(cache, container)
+ let path = get_required_arg::<String>(sub_matches, "out")?;
+ let output = commands::create_storage(cache, container, file)
.context("failed to create storage files")?;
- generated_files
- .iter()
- .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;
+ write_output_to_file_or_stdout(path, &output)?;
}
_ => unreachable!(),
}
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
new file mode 100644
index 0000000..1381e89
--- /dev/null
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+use crate::commands::assign_flag_ids;
+use crate::storage::FlagPackage;
+use aconfig_storage_file::{
+ get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
+};
+use anyhow::{anyhow, Result};
+
+fn new_header(container: &str, num_flags: u32) -> FlagTableHeader {
+ FlagTableHeader {
+ version: FILE_VERSION,
+ container: String::from(container),
+ file_size: 0,
+ num_flags,
+ bucket_offset: 0,
+ node_offset: 0,
+ }
+}
+
+// a struct that contains FlagTableNode and a bunch of other information to help
+// flag table creation
+#[derive(PartialEq, Debug, Clone)]
+struct FlagTableNodeWrapper {
+ pub node: FlagTableNode,
+ pub bucket_index: u32,
+}
+
+impl FlagTableNodeWrapper {
+ fn new(
+ package_id: u32,
+ flag_name: &str,
+ flag_type: u16,
+ flag_id: u16,
+ num_buckets: u32,
+ ) -> Self {
+ let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
+ let node = FlagTableNode {
+ package_id,
+ flag_name: flag_name.to_string(),
+ flag_type,
+ flag_id,
+ next_offset: None,
+ };
+ Self { node, bucket_index }
+ }
+
+ fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<Self>> {
+ let flag_ids =
+ assign_flag_ids(package.package_name, package.boolean_flags.iter().copied())?;
+ package
+ .boolean_flags
+ .iter()
+ .map(|&pf| {
+ let fid = flag_ids
+ .get(pf.name())
+ .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
+ // all flags are boolean value at the moment, thus using the last bit.
+ // When more flag value types are supported, flag value type information
+ // should come from the parsed flag, and we will set the flag_type bit
+ // mask properly.
+ let flag_type = 1;
+ Ok(Self::new(package.package_id, pf.name(), flag_type, *fid, num_buckets))
+ })
+ .collect::<Result<Vec<_>>>()
+ }
+}
+
+pub fn create_flag_table(container: &str, packages: &[FlagPackage]) -> Result<FlagTable> {
+ // create table
+ let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
+ let num_buckets = get_table_size(num_flags)?;
+
+ let mut header = new_header(container, num_flags);
+ let mut buckets = vec![None; num_buckets as usize];
+ let mut node_wrappers = packages
+ .iter()
+ .map(|pkg| FlagTableNodeWrapper::create_nodes(pkg, num_buckets))
+ .collect::<Result<Vec<_>>>()?
+ .concat();
+
+ // initialize all header fields
+ header.bucket_offset = header.as_bytes().len() as u32;
+ header.node_offset = header.bucket_offset + num_buckets * 4;
+ header.file_size = header.node_offset
+ + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+
+ // sort nodes by bucket index for efficiency
+ node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
+
+ // fill all node offset
+ let mut offset = header.node_offset;
+ for i in 0..node_wrappers.len() {
+ let node_bucket_idx = node_wrappers[i].bucket_index;
+ let next_node_bucket_idx = if i + 1 < node_wrappers.len() {
+ Some(node_wrappers[i + 1].bucket_index)
+ } else {
+ None
+ };
+
+ if buckets[node_bucket_idx as usize].is_none() {
+ buckets[node_bucket_idx as usize] = Some(offset);
+ }
+ offset += node_wrappers[i].node.as_bytes().len() as u32;
+
+ if let Some(index) = next_node_bucket_idx {
+ if index == node_bucket_idx {
+ node_wrappers[i].node.next_offset = Some(offset);
+ }
+ }
+ }
+
+ let table =
+ FlagTable { header, buckets, nodes: node_wrappers.into_iter().map(|nw| nw.node).collect() };
+
+ Ok(table)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
+
+ // create test baseline, syntactic sugar
+ fn new_expected_node(
+ package_id: u32,
+ flag_name: &str,
+ flag_type: u16,
+ flag_id: u16,
+ next_offset: Option<u32>,
+ ) -> FlagTableNode {
+ FlagTableNode {
+ package_id,
+ flag_name: flag_name.to_string(),
+ flag_type,
+ flag_id,
+ next_offset,
+ }
+ }
+
+ fn create_test_flag_table() -> Result<FlagTable> {
+ let caches = parse_all_test_flags();
+ let packages = group_flags_by_package(caches.iter());
+ create_flag_table("system", &packages)
+ }
+
+ #[test]
+ // this test point locks down the table creation and each field
+ fn test_table_contents() {
+ let flag_table = create_test_flag_table();
+ assert!(flag_table.is_ok());
+
+ let header: &FlagTableHeader = &flag_table.as_ref().unwrap().header;
+ let expected_header = FlagTableHeader {
+ version: FILE_VERSION,
+ container: String::from("system"),
+ file_size: 320,
+ num_flags: 8,
+ bucket_offset: 30,
+ node_offset: 98,
+ };
+ assert_eq!(header, &expected_header);
+
+ let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
+ let expected_bucket: Vec<Option<u32>> = vec![
+ Some(98),
+ Some(124),
+ None,
+ None,
+ None,
+ Some(177),
+ None,
+ Some(203),
+ None,
+ Some(261),
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(293),
+ None,
+ ];
+ assert_eq!(buckets, &expected_bucket);
+
+ let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes;
+ assert_eq!(nodes.len(), 8);
+
+ assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None));
+ assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(150)));
+ assert_eq!(nodes[2], new_expected_node(1, "disabled_ro", 1, 0, None));
+ assert_eq!(nodes[3], new_expected_node(2, "enabled_ro", 1, 1, None));
+ assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)));
+ assert_eq!(nodes[5], new_expected_node(1, "enabled_ro", 1, 2, None));
+ assert_eq!(nodes[6], new_expected_node(2, "enabled_fixed_ro", 1, 0, None));
+ assert_eq!(nodes[7], new_expected_node(0, "disabled_rw", 1, 0, None));
+ }
+}
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
new file mode 100644
index 0000000..0d4b5b4
--- /dev/null
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+use crate::commands::assign_flag_ids;
+use crate::storage::FlagPackage;
+use aconfig_protos::ProtoFlagState;
+use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION};
+use anyhow::{anyhow, Result};
+
+fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
+ FlagValueHeader {
+ version: FILE_VERSION,
+ container: String::from(container),
+ file_size: 0,
+ num_flags,
+ boolean_value_offset: 0,
+ }
+}
+
+pub fn create_flag_value(container: &str, packages: &[FlagPackage]) -> Result<FlagValueList> {
+ // create list
+ let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
+
+ let mut list = FlagValueList {
+ header: new_header(container, num_flags),
+ booleans: vec![false; num_flags as usize],
+ };
+
+ for pkg in packages.iter() {
+ let start_offset = pkg.boolean_offset as usize;
+ let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
+ for pf in pkg.boolean_flags.iter() {
+ let fid = flag_ids
+ .get(pf.name())
+ .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
+
+ list.booleans[start_offset + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
+ }
+ }
+
+ // initialize all header fields
+ list.header.boolean_value_offset = list.header.as_bytes().len() as u32;
+ list.header.file_size = list.header.boolean_value_offset + num_flags;
+
+ Ok(list)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
+
+ pub fn create_test_flag_value_list() -> Result<FlagValueList> {
+ let caches = parse_all_test_flags();
+ let packages = group_flags_by_package(caches.iter());
+ create_flag_value("system", &packages)
+ }
+
+ #[test]
+ // this test point locks down the flag value creation and each field
+ fn test_list_contents() {
+ let flag_value_list = create_test_flag_value_list();
+ assert!(flag_value_list.is_ok());
+
+ let header: &FlagValueHeader = &flag_value_list.as_ref().unwrap().header;
+ let expected_header = FlagValueHeader {
+ version: FILE_VERSION,
+ container: String::from("system"),
+ file_size: 34,
+ num_flags: 8,
+ boolean_value_offset: 26,
+ };
+ assert_eq!(header, &expected_header);
+
+ let booleans: &Vec<bool> = &flag_value_list.as_ref().unwrap().booleans;
+ let expected_booleans: Vec<bool> = vec![false; header.num_flags as usize];
+ assert_eq!(booleans, &expected_booleans);
+ }
+}
diff --git a/tools/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
similarity index 65%
rename from tools/aconfig/src/storage/mod.rs
rename to tools/aconfig/aconfig/src/storage/mod.rs
index 76835e0..29eb9c8 100644
--- a/tools/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -15,47 +15,26 @@
*/
pub mod flag_table;
+pub mod flag_value;
pub mod package_table;
-use anyhow::{anyhow, Result};
-use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
-use std::hash::{Hash, Hasher};
-use std::path::PathBuf;
+use anyhow::Result;
+use std::collections::{HashMap, HashSet};
-use crate::commands::OutputFile;
-use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
-use crate::storage::{flag_table::FlagTable, package_table::PackageTable};
-
-pub const FILE_VERSION: u32 = 1;
-
-pub const HASH_PRIMES: [u32; 29] = [
- 7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241,
- 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,
- 402653189, 805306457, 1610612741,
-];
-
-/// Get the right hash table size given number of entries in the table. Use a
-/// load factor of 0.5 for performance.
-pub fn get_table_size(entries: u32) -> Result<u32> {
- HASH_PRIMES
- .iter()
- .find(|&&num| num >= 2 * entries)
- .copied()
- .ok_or(anyhow!("Number of packages is too large"))
-}
-
-/// Get the corresponding bucket index given the key and number of buckets
-pub fn get_bucket_index<T: Hash>(val: &T, num_buckets: u32) -> u32 {
- let mut s = DefaultHasher::new();
- val.hash(&mut s);
- (s.finish() % num_buckets as u64) as u32
-}
+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_storage_file::StorageFileSelection;
pub struct FlagPackage<'a> {
pub package_name: &'a str,
pub package_id: u32,
pub flag_names: HashSet<&'a str>,
pub boolean_flags: Vec<&'a ProtoParsedFlag>,
+ // offset of the first boolean flag in this flag package with respect to the start of
+ // boolean flag value array in the flag value file
pub boolean_offset: u32,
}
@@ -95,39 +74,40 @@
}
// calculate package flag value start offset, in flag value file, each boolean
- // is stored as two bytes, the first byte will be the flag value. the second
- // byte is flag info byte, which is a bitmask to indicate the status of a flag
+ // is stored as a single byte
let mut boolean_offset = 0;
for p in packages.iter_mut() {
p.boolean_offset = boolean_offset;
- boolean_offset += 2 * p.boolean_flags.len() as u32;
+ boolean_offset += p.boolean_flags.len() as u32;
}
packages
}
-pub fn generate_storage_files<'a, I>(
+pub fn generate_storage_file<'a, I>(
container: &str,
parsed_flags_vec_iter: I,
-) -> Result<Vec<OutputFile>>
+ file: &StorageFileSelection,
+) -> Result<Vec<u8>>
where
I: Iterator<Item = &'a ProtoParsedFlags>,
{
let packages = group_flags_by_package(parsed_flags_vec_iter);
- // create and serialize package map
- let package_table = PackageTable::new(container, &packages)?;
- let package_table_file_path = PathBuf::from("package.map");
- let package_table_file =
- OutputFile { contents: package_table.as_bytes(), path: package_table_file_path };
-
- // create and serialize flag map
- let flag_table = FlagTable::new(container, &packages)?;
- let flag_table_file_path = PathBuf::from("flag.map");
- let flag_table_file =
- OutputFile { contents: flag_table.as_bytes(), path: flag_table_file_path };
-
- Ok(vec![package_table_file, flag_table_file])
+ match file {
+ StorageFileSelection::PackageMap => {
+ let package_table = create_package_table(container, &packages)?;
+ Ok(package_table.as_bytes())
+ }
+ StorageFileSelection::FlagMap => {
+ let flag_table = create_flag_table(container, &packages)?;
+ Ok(flag_table.as_bytes())
+ }
+ StorageFileSelection::FlagVal => {
+ let flag_value = create_flag_value(container, &packages)?;
+ Ok(flag_value.as_bytes())
+ }
+ }
}
#[cfg(test)]
@@ -135,21 +115,6 @@
use super::*;
use crate::Input;
- /// Read and parse bytes as u32
- pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32> {
- let val = u32::from_le_bytes(buf[*head..*head + 4].try_into()?);
- *head += 4;
- Ok(val)
- }
-
- /// Read and parse bytes as string
- pub fn read_str_from_bytes(buf: &[u8], head: &mut usize) -> Result<String> {
- let num_bytes = read_u32_from_bytes(buf, head)? as usize;
- let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())?;
- *head += num_bytes;
- Ok(val)
- }
-
pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {
let aconfig_files = [
(
@@ -183,7 +148,7 @@
crate::commands::DEFAULT_FLAG_PERMISSION,
)
.unwrap();
- crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
+ aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
})
.collect()
}
@@ -218,13 +183,13 @@
assert!(packages[1].flag_names.contains("enabled_ro"));
assert!(packages[1].flag_names.contains("disabled_ro"));
assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
- assert_eq!(packages[1].boolean_offset, 6);
+ assert_eq!(packages[1].boolean_offset, 3);
assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
assert_eq!(packages[2].package_id, 2);
assert_eq!(packages[2].flag_names.len(), 2);
assert!(packages[2].flag_names.contains("enabled_ro"));
assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
- assert_eq!(packages[2].boolean_offset, 12);
+ assert_eq!(packages[2].boolean_offset, 6);
}
}
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
new file mode 100644
index 0000000..4c08129
--- /dev/null
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use anyhow::Result;
+
+use aconfig_storage_file::{
+ get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION,
+};
+
+use crate::storage::FlagPackage;
+
+fn new_header(container: &str, num_packages: u32) -> PackageTableHeader {
+ PackageTableHeader {
+ version: FILE_VERSION,
+ container: String::from(container),
+ file_size: 0,
+ num_packages,
+ bucket_offset: 0,
+ node_offset: 0,
+ }
+}
+
+// a struct that contains PackageTableNode and a bunch of other information to help
+// package table creation
+#[derive(PartialEq, Debug)]
+struct PackageTableNodeWrapper {
+ pub node: PackageTableNode,
+ pub bucket_index: u32,
+}
+
+impl PackageTableNodeWrapper {
+ fn new(package: &FlagPackage, num_buckets: u32) -> Self {
+ let node = PackageTableNode {
+ package_name: String::from(package.package_name),
+ package_id: package.package_id,
+ boolean_offset: package.boolean_offset,
+ next_offset: None,
+ };
+ let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);
+ Self { node, bucket_index }
+ }
+}
+
+pub fn create_package_table(container: &str, packages: &[FlagPackage]) -> Result<PackageTable> {
+ // create table
+ let num_packages = packages.len() as u32;
+ let num_buckets = get_table_size(num_packages)?;
+ let mut header = new_header(container, num_packages);
+ let mut buckets = vec![None; num_buckets as usize];
+ let mut node_wrappers: Vec<_> =
+ packages.iter().map(|pkg| PackageTableNodeWrapper::new(pkg, num_buckets)).collect();
+
+ // initialize all header fields
+ header.bucket_offset = header.as_bytes().len() as u32;
+ header.node_offset = header.bucket_offset + num_buckets * 4;
+ header.file_size = header.node_offset
+ + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+
+ // sort node_wrappers by bucket index for efficiency
+ node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
+
+ // fill all node offset
+ let mut offset = header.node_offset;
+ for i in 0..node_wrappers.len() {
+ let node_bucket_idx = node_wrappers[i].bucket_index;
+ let next_node_bucket_idx = if i + 1 < node_wrappers.len() {
+ Some(node_wrappers[i + 1].bucket_index)
+ } else {
+ None
+ };
+
+ if buckets[node_bucket_idx as usize].is_none() {
+ buckets[node_bucket_idx as usize] = Some(offset);
+ }
+ offset += node_wrappers[i].node.as_bytes().len() as u32;
+
+ if let Some(index) = next_node_bucket_idx {
+ if index == node_bucket_idx {
+ node_wrappers[i].node.next_offset = Some(offset);
+ }
+ }
+ }
+
+ let table = PackageTable {
+ header,
+ buckets,
+ nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),
+ };
+ Ok(table)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
+
+ pub fn create_test_package_table() -> Result<PackageTable> {
+ let caches = parse_all_test_flags();
+ let packages = group_flags_by_package(caches.iter());
+ create_package_table("system", &packages)
+ }
+
+ #[test]
+ // this test point locks down the table creation and each field
+ fn test_table_contents() {
+ let package_table = create_test_package_table();
+ assert!(package_table.is_ok());
+
+ let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
+ let expected_header = PackageTableHeader {
+ version: FILE_VERSION,
+ container: String::from("system"),
+ file_size: 208,
+ num_packages: 3,
+ bucket_offset: 30,
+ node_offset: 58,
+ };
+ assert_eq!(header, &expected_header);
+
+ let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
+ let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+ assert_eq!(buckets, &expected);
+
+ let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
+ assert_eq!(nodes.len(), 3);
+ let first_node_expected = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_2"),
+ package_id: 1,
+ boolean_offset: 3,
+ next_offset: None,
+ };
+ assert_eq!(nodes[0], first_node_expected);
+ let second_node_expected = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_1"),
+ package_id: 0,
+ boolean_offset: 0,
+ next_offset: Some(158),
+ };
+ assert_eq!(nodes[1], second_node_expected);
+ let third_node_expected = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_4"),
+ package_id: 2,
+ boolean_offset: 6,
+ next_offset: None,
+ };
+ assert_eq!(nodes[2], third_node_expected);
+ }
+}
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/aconfig/src/test.rs
similarity index 97%
rename from tools/aconfig/src/test.rs
rename to tools/aconfig/aconfig/src/test.rs
index cbb95b8..7409cda 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/aconfig/src/test.rs
@@ -15,9 +15,12 @@
*/
#[cfg(test)]
+pub use test_utils::*;
+
+#[cfg(test)]
pub mod test_utils {
use crate::commands::Input;
- use crate::protos::ProtoParsedFlags;
+ use aconfig_protos::ProtoParsedFlags;
use itertools;
pub const TEST_PACKAGE: &str = "com.android.aconfig.test";
@@ -265,7 +268,7 @@
crate::commands::DEFAULT_FLAG_PERMISSION,
)
.unwrap();
- crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
+ aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}
pub fn parse_test_flags() -> ProtoParsedFlags {
@@ -289,7 +292,7 @@
crate::commands::DEFAULT_FLAG_PERMISSION,
)
.unwrap();
- crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
+ aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}
pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
@@ -340,6 +343,3 @@
);
}
}
-
-#[cfg(test)]
-pub use test_utils::*;
diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
similarity index 62%
rename from tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
rename to tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
index 933d6a7..28dddd8 100644
--- a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -2,8 +2,11 @@
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
/** @hide */
public class FakeFeatureFlagsImpl implements FeatureFlags \{
@@ -31,6 +34,14 @@
}
}
+ public boolean isFlagReadOnlyOptimized(String flagName) \{
+ if (mReadOnlyFlagsSet.contains(flagName) &&
+ isOptimizationEnabled()) \{
+ return true;
+ }
+ return false;
+ }
+
private boolean getValue(String flagName) \{
Boolean value = this.mFlagMap.get(flagName);
if (value == null) \{
@@ -39,12 +50,28 @@
return value;
}
+ @com.android.aconfig.annotations.AssumeTrueForR8
+ private boolean isOptimizationEnabled() \{
+ return false;
+ }
+
private Map<String, Boolean> mFlagMap = new HashMap<>(
Map.ofEntries(
- {{-for item in flag_elements}}
+ {{ -for item in flag_elements }}
Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false)
{{ -if not @last }},{{ endif }}
{{ -endfor }}
)
);
+
+ private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+ Arrays.asList(
+ {{ -for item in flag_elements }}
+ {{ -if not item.is_read_write }}
+ Flags.FLAG_{item.flag_name_constant_suffix},
+ {{ -endif }}
+ {{ -endfor }}
+ ""{# The empty string here is to resolve the ending comma #}
+ )
+ );
}
diff --git a/tools/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
similarity index 100%
rename from tools/aconfig/templates/FeatureFlags.java.template
rename to tools/aconfig/aconfig/templates/FeatureFlags.java.template
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
similarity index 100%
rename from tools/aconfig/templates/FeatureFlagsImpl.java.template
rename to tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
similarity index 100%
rename from tools/aconfig/templates/Flags.java.template
rename to tools/aconfig/aconfig/templates/Flags.java.template
diff --git a/tools/aconfig/templates/cpp_exported_header.template b/tools/aconfig/aconfig/templates/cpp_exported_header.template
similarity index 100%
rename from tools/aconfig/templates/cpp_exported_header.template
rename to tools/aconfig/aconfig/templates/cpp_exported_header.template
diff --git a/tools/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
similarity index 100%
rename from tools/aconfig/templates/cpp_source_file.template
rename to tools/aconfig/aconfig/templates/cpp_source_file.template
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template
similarity index 100%
rename from tools/aconfig/templates/rust.template
rename to tools/aconfig/aconfig/templates/rust.template
diff --git a/tools/aconfig/templates/rust_test.template b/tools/aconfig/aconfig/templates/rust_test.template
similarity index 100%
rename from tools/aconfig/templates/rust_test.template
rename to tools/aconfig/aconfig/templates/rust_test.template
diff --git a/tools/aconfig/tests/AconfigHostTest.java b/tools/aconfig/aconfig/tests/AconfigHostTest.java
similarity index 100%
rename from tools/aconfig/tests/AconfigHostTest.java
rename to tools/aconfig/aconfig/tests/AconfigHostTest.java
diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/aconfig/tests/AconfigTest.java
similarity index 100%
rename from tools/aconfig/tests/AconfigTest.java
rename to tools/aconfig/aconfig/tests/AconfigTest.java
diff --git a/tools/aconfig/tests/AndroidManifest.xml b/tools/aconfig/aconfig/tests/AndroidManifest.xml
similarity index 100%
rename from tools/aconfig/tests/AndroidManifest.xml
rename to tools/aconfig/aconfig/tests/AndroidManifest.xml
diff --git a/tools/aconfig/tests/aconfig_exported_mode_test.cpp b/tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_exported_mode_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp
diff --git a/tools/aconfig/tests/aconfig_exported_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_exported_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_force_read_only_mode_test.cpp b/tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_force_read_only_mode_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp
diff --git a/tools/aconfig/tests/aconfig_force_read_only_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_force_read_only_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_prod_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_prod_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_test.cpp b/tools/aconfig/aconfig/tests/aconfig_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_test.cpp
diff --git a/tools/aconfig/tests/aconfig_test_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_test_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_test_test_variant.cpp b/tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_test_test_variant.cpp
rename to tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp
diff --git a/tools/aconfig/tests/first.values b/tools/aconfig/aconfig/tests/first.values
similarity index 100%
rename from tools/aconfig/tests/first.values
rename to tools/aconfig/aconfig/tests/first.values
diff --git a/tools/aconfig/tests/read_only_test.aconfig b/tools/aconfig/aconfig/tests/read_only_test.aconfig
similarity index 100%
rename from tools/aconfig/tests/read_only_test.aconfig
rename to tools/aconfig/aconfig/tests/read_only_test.aconfig
diff --git a/tools/aconfig/tests/read_only_test.values b/tools/aconfig/aconfig/tests/read_only_test.values
similarity index 100%
rename from tools/aconfig/tests/read_only_test.values
rename to tools/aconfig/aconfig/tests/read_only_test.values
diff --git a/tools/aconfig/tests/second.values b/tools/aconfig/aconfig/tests/second.values
similarity index 100%
rename from tools/aconfig/tests/second.values
rename to tools/aconfig/aconfig/tests/second.values
diff --git a/tools/aconfig/tests/storage_test_1.aconfig b/tools/aconfig/aconfig/tests/storage_test_1.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_1.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_1.aconfig
diff --git a/tools/aconfig/tests/storage_test_2.aconfig b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_2.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_2.aconfig
diff --git a/tools/aconfig/tests/storage_test_4.aconfig b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_4.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_4.aconfig
diff --git a/tools/aconfig/tests/test.aconfig b/tools/aconfig/aconfig/tests/test.aconfig
similarity index 100%
rename from tools/aconfig/tests/test.aconfig
rename to tools/aconfig/aconfig/tests/test.aconfig
diff --git a/tools/aconfig/tests/test_exported.aconfig b/tools/aconfig/aconfig/tests/test_exported.aconfig
similarity index 100%
rename from tools/aconfig/tests/test_exported.aconfig
rename to tools/aconfig/aconfig/tests/test_exported.aconfig
diff --git a/tools/aconfig/tests/test_force_read_only.aconfig b/tools/aconfig/aconfig/tests/test_force_read_only.aconfig
similarity index 100%
rename from tools/aconfig/tests/test_force_read_only.aconfig
rename to tools/aconfig/aconfig/tests/test_force_read_only.aconfig
diff --git a/tools/aconfig/aconfig_protos/Android.bp b/tools/aconfig/aconfig_protos/Android.bp
new file mode 100644
index 0000000..18c545a
--- /dev/null
+++ b/tools/aconfig/aconfig_protos/Android.bp
@@ -0,0 +1,75 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// proto libraries for consumers of `aconfig dump --format=protobuf` output
+
+java_library {
+ name: "libaconfig_java_proto_lite",
+ host_supported: true,
+ srcs: ["protos/aconfig.proto"],
+ static_libs: ["libprotobuf-java-lite"],
+ proto: {
+ type: "lite",
+ },
+ sdk_version: "current",
+ min_sdk_version: "UpsideDownCake",
+ apex_available: [
+ "com.android.configinfrastructure",
+ "//apex_available:platform",
+ ]
+}
+
+java_library_host {
+ name: "libaconfig_java_proto_full",
+ srcs: ["protos/aconfig.proto"],
+ static_libs: ["libprotobuf-java-full"],
+ proto: {
+ type: "full",
+ },
+}
+
+python_library_host {
+ name: "libaconfig_python_proto",
+ srcs: ["protos/aconfig.proto"],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
+
+rust_protobuf {
+ name: "libaconfig_rust_proto",
+ protos: ["protos/aconfig.proto"],
+ crate_name: "aconfig_rust_proto",
+ source_stem: "aconfig_rust_proto",
+ host_supported: true,
+}
+
+rust_defaults {
+ name: "aconfig_protos.defaults",
+ edition: "2021",
+ clippy_lints: "android",
+ lints: "android",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libaconfig_rust_proto",
+ "libanyhow",
+ "libprotobuf",
+ ],
+ proc_macros: [
+ "libpaste",
+ ]
+}
+
+rust_library {
+ name: "libaconfig_protos",
+ crate_name: "aconfig_protos",
+ host_supported: true,
+ defaults: ["aconfig_protos.defaults"],
+}
+
+rust_test_host {
+ name: "aconfig_protos.test",
+ test_suites: ["general-tests"],
+ defaults: ["aconfig_protos.defaults"],
+}
diff --git a/tools/aconfig/aconfig_protos/Cargo.toml b/tools/aconfig/aconfig_protos/Cargo.toml
new file mode 100644
index 0000000..114cf80
--- /dev/null
+++ b/tools/aconfig/aconfig_protos/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "aconfig_protos"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+paste = "1.0.11"
+protobuf = "3.2.0"
+
+[build-dependencies]
+protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/build.rs b/tools/aconfig/aconfig_protos/build.rs
similarity index 100%
rename from tools/aconfig/build.rs
rename to tools/aconfig/aconfig_protos/build.rs
diff --git a/tools/aconfig/aconfig_protos/protos/aconfig.proto b/tools/aconfig/aconfig_protos/protos/aconfig.proto
new file mode 100644
index 0000000..8833722
--- /dev/null
+++ b/tools/aconfig/aconfig_protos/protos/aconfig.proto
@@ -0,0 +1,171 @@
+// Copyright (C) 2023 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
+
+// This is the schema definition for aconfig files. Modifications need to be
+// either backwards compatible, or include updates to all aconfig files in the
+// Android tree.
+
+syntax = "proto2";
+
+package android.aconfig;
+
+// This protobuf file defines messages used to represent and manage flags in the "aconfig" system
+// The following format requirements apply across various message fields:
+// # name: a lowercase string in snake_case format, no consecutive underscores, and no leading digit
+// For example adjust_rate is a valid name, while AdjustRate, adjust__rate, and
+// 2adjust_rate are invalid
+//
+// # namespace: a lowercase string in snake_case format, no consecutive underscores, and no leading
+// digit. For example android_bar_system
+//
+// # package: lowercase strings in snake_case format, delimited by dots, no consecutive underscores
+// and no leading digit in each string. For example com.android.mypackage is a valid name
+// while com.android.myPackage, com.android.1mypackage are invalid
+
+// messages used in both aconfig input and output
+
+enum flag_state {
+ ENABLED = 1;
+ DISABLED = 2;
+}
+
+enum flag_permission {
+ READ_ONLY = 1;
+ READ_WRITE = 2;
+}
+
+// aconfig input messages: flag declarations and values
+
+message flag_declaration {
+ // Name of the flag (required)
+ // See # name for format detail
+ optional string name = 1;
+
+ // Namespace the flag belongs to (required)
+ // See # namespace for format detail
+ optional string namespace = 2;
+
+ // Textual description of the flag's purpose (required)
+ optional string description = 3;
+
+ // Single bug id related to the flag (required)
+ repeated string bug = 4;
+
+ // Indicates if the flag is permanently read-only and cannot be changed
+ // via release configs (optional)
+ // Default value false
+ optional bool is_fixed_read_only = 5;
+
+ // Indicates if the flag is exported and accessible beyond its originating container (optional)
+ // Default value false
+ optional bool is_exported = 6;
+
+ // Additional information about the flag, including its purpose and form factors (optional)
+ optional flag_metadata metadata = 7;
+};
+
+// Optional metadata about the flag, such as its purpose and its intended form factors.
+// Can influence the applied policies and testing strategy.
+message flag_metadata {
+ enum flag_purpose {
+ PURPOSE_UNSPECIFIED = 0;
+ PURPOSE_FEATURE = 1;
+ PURPOSE_BUGFIX = 2;
+ }
+
+ optional flag_purpose purpose = 1;
+
+ // TODO(b/315025930): Add field to designate intended target device form factor(s), such as phone, watch or other.
+}
+
+message flag_declarations {
+ // Package to which the flag belongs (required)
+ // See # package for format detail
+ optional string package = 1;
+
+ // List of flag_declaration objects (required)
+ repeated flag_declaration flag = 2;
+
+ // Container the flag belongs to (optional)
+ optional string container = 3;
+};
+
+message flag_value {
+ // Package to which the flag belongs (required)
+ // See # package for format detail
+ optional string package = 1;
+
+ // Name of the flag (required)
+ // See # name for format detail
+ optional string name = 2;
+
+ optional flag_state state = 3;
+ optional flag_permission permission = 4;
+};
+
+message flag_values {
+ repeated flag_value flag_value = 1;
+};
+
+// aconfig output messages: parsed and verified flag declarations and values
+
+message tracepoint {
+ // path to declaration or value file relative to $TOP
+ optional string source = 1;
+ optional flag_state state = 2;
+ optional flag_permission permission = 3;
+}
+
+message parsed_flag {
+ // Package to which the flag belongs (required)
+ // See # package for format detail
+ optional string package = 1;
+
+ // Name of the flag (required)
+ // See # name for format detail
+ optional string name = 2;
+
+ // Namespace the flag belongs to (required)
+ // See # namespace for format detail
+ optional string namespace = 3;
+
+ // Textual description of the flag's purpose (required)
+ optional string description = 4;
+
+ // Single bug id related to the flag (required)
+ repeated string bug = 5;
+
+ optional flag_state state = 6;
+ optional flag_permission permission = 7;
+ repeated tracepoint trace = 8;
+
+ // Indicates if the flag is permanently read-only and cannot be changed
+ // via release configs (optional)
+ // Default value false
+ optional bool is_fixed_read_only = 9;
+
+ // Indicates if the flag is exported and accessible beyond its originating container (optional)
+ // Default value false
+ optional bool is_exported = 10;
+
+ // Container the flag belongs to (optional)
+ optional string container = 11;
+
+ // Additional information about the flag, including its purpose and form factors (optional)
+ optional flag_metadata metadata = 12;
+}
+
+message parsed_flags {
+ repeated parsed_flag parsed_flag = 1;
+}
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/aconfig_protos/src/lib.rs
similarity index 83%
rename from tools/aconfig/src/protos.rs
rename to tools/aconfig/aconfig_protos/src/lib.rs
index 2684d20..81bbd7e 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/aconfig_protos/src/lib.rs
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+//! `aconfig_protos` is a crate for the protos defined for aconfig
// When building with the Android tool-chain
//
// - an external crate `aconfig_protos` will be generated
@@ -29,17 +30,17 @@
// ---- When building with the Android tool-chain ----
#[cfg(not(feature = "cargo"))]
mod auto_generated {
- pub use aconfig_protos::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
- pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
- pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
- pub use aconfig_protos::aconfig::Flag_metadata as ProtoFlagMetadata;
- pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
- pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
- pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
- pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
- pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
- pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
- pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
+ pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
+ pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
+ pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
+ pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
+ pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
+ pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
+ pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
+ pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
+ pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
+ pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
+ pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
}
// ---- When building with cargo ----
@@ -68,6 +69,38 @@
use anyhow::Result;
use paste::paste;
+/// Path to proto file
+const ACONFIG_PROTO_PATH: &str = "//build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto";
+
+/// Check if the name identifier is valid
+pub fn is_valid_name_ident(s: &str) -> bool {
+ // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
+ if s.contains("__") {
+ return false;
+ }
+ let mut chars = s.chars();
+ let Some(first) = chars.next() else {
+ return false;
+ };
+ if !first.is_ascii_lowercase() {
+ return false;
+ }
+ chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
+}
+
+/// Check if the package identifier is valid
+pub fn is_valid_package_ident(s: &str) -> bool {
+ if !s.contains('.') {
+ return false;
+ }
+ s.split('.').all(is_valid_name_ident)
+}
+
+/// Check if the container identifier is valid
+pub fn is_valid_container_ident(s: &str) -> bool {
+ s.split('.').all(is_valid_name_ident)
+}
+
fn try_from_text_proto<T>(s: &str) -> Result<T>
where
T: protobuf::MessageFull,
@@ -85,16 +118,27 @@
};
}
+/// Utility module for flag_declaration proto
pub mod flag_declaration {
use super::*;
- use crate::codegen;
use anyhow::ensure;
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
- ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
- ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
+ ensure!(
+ is_valid_name_ident(pdf.name()),
+ "bad flag declaration: bad name {} expected snake_case string; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pdf.name()
+ );
+ ensure!(
+ is_valid_name_ident(pdf.namespace()),
+ "bad flag declaration: bad namespace {} expected snake_case string; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pdf.namespace()
+ );
ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
@@ -102,27 +146,30 @@
}
}
+/// Utility module for flag_declarations proto
pub mod flag_declarations {
use super::*;
- use crate::codegen;
use anyhow::ensure;
+ /// Construct a proto instance from a textproto string content
pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
verify_fields(&pdf)?;
Ok(pdf)
}
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
ensure_required_fields!("flag declarations", pdf, "package");
// TODO(b/312769710): Make the container field required.
-
ensure!(
- codegen::is_valid_package_ident(pdf.package()),
- "bad flag declarations: bad package"
+ is_valid_package_ident(pdf.package()),
+ "bad flag declarations: bad package {} expected snake_case strings delimited by dots; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pdf.package()
);
ensure!(
- !pdf.has_container() || codegen::is_valid_container_ident(pdf.container()),
+ !pdf.has_container() || is_valid_container_ident(pdf.container()),
"bad flag declarations: bad container"
);
for flag_declaration in pdf.flag.iter() {
@@ -133,30 +180,44 @@
}
}
+/// Utility module for flag_value proto
pub mod flag_value {
use super::*;
- use crate::codegen;
use anyhow::ensure;
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
- ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
- ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
+ ensure!(
+ is_valid_package_ident(fv.package()),
+ "bad flag value: bad package {} expected snake_case strings delimited by dots; \
+ see {ACONFIG_PROTO_PATH} for details",
+ fv.package()
+ );
+ ensure!(
+ is_valid_name_ident(fv.name()),
+ "bad flag value: bad name {} expected snake_case string; \
+ see {ACONFIG_PROTO_PATH} for details",
+ fv.name()
+ );
Ok(())
}
}
+/// Utility module for flag_values proto
pub mod flag_values {
use super::*;
+ /// Construct a proto instance from a textproto string content
pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
verify_fields(&pfv)?;
Ok(pfv)
}
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
for flag_value in pfv.flag_value.iter() {
super::flag_value::verify_fields(flag_value)?;
@@ -165,10 +226,12 @@
}
}
+/// Utility module for flag_permission proto enum
pub mod flag_permission {
use super::*;
use anyhow::bail;
+ /// Construct a flag permission proto enum from string
pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
match permission.to_ascii_lowercase().as_str() {
"read_write" => Ok(ProtoFlagPermission::READ_WRITE),
@@ -177,6 +240,7 @@
}
}
+ /// Serialize flag permission proto enum to string
pub fn to_string(permission: &ProtoFlagPermission) -> &str {
match permission {
ProtoFlagPermission::READ_WRITE => "read_write",
@@ -185,10 +249,12 @@
}
}
+/// Utility module for tracepoint proto
pub mod tracepoint {
use super::*;
use anyhow::ensure;
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
@@ -198,11 +264,12 @@
}
}
+/// Utility module for parsed_flag proto
pub mod parsed_flag {
use super::*;
- use crate::codegen;
use anyhow::ensure;
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
ensure_required_fields!(
"parsed flag",
@@ -215,13 +282,28 @@
"permission"
);
- ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
ensure!(
- !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
+ is_valid_package_ident(pf.package()),
+ "bad parsed flag: bad package {} expected snake_case strings delimited by dots; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pf.package()
+ );
+ ensure!(
+ !pf.has_container() || is_valid_container_ident(pf.container()),
"bad parsed flag: bad container"
);
- ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
- ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
+ ensure!(
+ is_valid_name_ident(pf.name()),
+ "bad parsed flag: bad name {} expected snake_case string; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pf.name()
+ );
+ ensure!(
+ is_valid_name_ident(pf.namespace()),
+ "bad parsed flag: bad namespace {} expected snake_case string; \
+ see {ACONFIG_PROTO_PATH} for details",
+ pf.namespace()
+ );
ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
for tp in pf.trace.iter() {
@@ -243,25 +325,29 @@
Ok(())
}
+ /// Get the file path of the corresponding flag declaration
pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
debug_assert!(!pf.trace.is_empty());
pf.trace[0].source()
}
}
+/// Utility module for parsed_flags proto
pub mod parsed_flags {
use super::*;
use anyhow::bail;
use std::cmp::Ordering;
+ /// Construct a proto instance from a binary proto bytes
pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
verify_fields(&message)?;
Ok(message)
}
+ /// Ensure the proto instance is valid by checking its fields
pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
- use crate::protos::parsed_flag::path_to_declaration;
+ use crate::parsed_flag::path_to_declaration;
let mut previous: Option<&ProtoParsedFlag> = None;
for parsed_flag in pf.parsed_flag.iter() {
@@ -287,6 +373,7 @@
Ok(())
}
+ /// Merge multipe parsed_flags proto
pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
let mut merged = ProtoParsedFlags::new();
for mut pfs in parsed_flags.into_iter() {
@@ -303,6 +390,7 @@
Ok(merged)
}
+ /// Sort parsed flags
pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
pf.parsed_flag.sort_by_key(create_sorting_key);
}
@@ -312,7 +400,9 @@
}
}
+/// ParsedFlagExt trait
pub trait ParsedFlagExt {
+ /// Return the fully qualified name
fn fully_qualified_name(&self) -> String;
}
@@ -847,10 +937,7 @@
"#;
let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
let parsed_flag = &parsed_flags.parsed_flag[0];
- assert_eq!(
- crate::protos::parsed_flag::path_to_declaration(parsed_flag),
- "flags.declarations"
- );
+ assert_eq!(crate::parsed_flag::path_to_declaration(parsed_flag), "flags.declarations");
}
#[test]
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
new file mode 100644
index 0000000..8922ba4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -0,0 +1,116 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "aconfig_storage_file.defaults",
+ edition: "2021",
+ lints: "none",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libanyhow",
+ "libaconfig_storage_protos",
+ "libonce_cell",
+ "libprotobuf",
+ "libtempfile",
+ "libmemmap2",
+ "libcxx",
+ "libthiserror",
+ ],
+}
+
+rust_library {
+ name: "libaconfig_storage_file",
+ crate_name: "aconfig_storage_file",
+ host_supported: true,
+ defaults: ["aconfig_storage_file.defaults"],
+}
+
+genrule {
+ name: "ro.package.map",
+ out: ["tests/tmp.ro.package.map"],
+ srcs: ["tests/package.map"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
+
+genrule {
+ name: "ro.flag.map",
+ out: ["tests/tmp.ro.flag.map"],
+ srcs: ["tests/flag.map"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
+
+genrule {
+ name: "ro.flag.val",
+ out: ["tests/tmp.ro.flag.val"],
+ srcs: ["tests/flag.val"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
+
+rust_test_host {
+ name: "aconfig_storage_file.test",
+ test_suites: ["general-tests"],
+ defaults: ["aconfig_storage_file.defaults"],
+ data: [
+ "tests/package.map",
+ "tests/flag.map",
+ "tests/flag.val",
+ ],
+}
+
+rust_protobuf {
+ name: "libaconfig_storage_protos",
+ protos: ["protos/aconfig_storage_metadata.proto"],
+ crate_name: "aconfig_storage_protos",
+ source_stem: "aconfig_storage_protos",
+ host_supported: true,
+}
+
+cc_library_static {
+ name: "libaconfig_storage_protos_cc",
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["protos/aconfig_storage_metadata.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ host_supported: true,
+}
+
+genrule {
+ name: "libcxx_aconfig_storage_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) > $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["aconfig_storage/lib.rs.cc"],
+}
+
+genrule {
+ name: "libcxx_aconfig_storage_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header > $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["aconfig_storage/lib.rs.h"],
+}
+
+rust_ffi_static {
+ name: "libaconfig_storage_cxx_bridge",
+ crate_name: "aconfig_storage_cxx_bridge",
+ host_supported: true,
+ defaults: ["aconfig_storage_file.defaults"],
+}
+
+cc_library_static {
+ name: "libaconfig_storage_cc",
+ srcs: ["aconfig_storage.cpp"],
+ generated_headers: [
+ "cxx-bridge-header",
+ "libcxx_aconfig_storage_bridge_header"
+ ],
+ generated_sources: ["libcxx_aconfig_storage_bridge_code"],
+ whole_static_libs: ["libaconfig_storage_cxx_bridge"],
+ export_include_dirs: ["include"],
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
new file mode 100644
index 0000000..c4e2670
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "aconfig_storage_file"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+memmap2 = "0.8.0"
+protobuf = "3.2.0"
+once_cell = "1.19.0"
+tempfile = "3.9.0"
+cxx = "1.0"
+thiserror = "1.0.56"
+
+[build-dependencies]
+protobuf-codegen = "3.2.0"
+cxx-build = "1.0"
diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
new file mode 100644
index 0000000..ac64093
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
@@ -0,0 +1,106 @@
+#include "aconfig_storage/aconfig_storage.hpp"
+
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+
+namespace aconfig_storage {
+
+/// Get package offset
+PackageOffsetQuery get_package_offset(
+ std::string const& container,
+ std::string const& package) {
+ auto offset_cxx = get_package_offset_cxx(
+ rust::Str(container.c_str()),
+ rust::Str(package.c_str()));
+ auto offset = PackageOffsetQuery();
+ offset.query_success = offset_cxx.query_success;
+ offset.error_message = std::string(offset_cxx.error_message.c_str());
+ offset.package_exists = offset_cxx.package_exists;
+ offset.package_id = offset_cxx.package_id;
+ offset.boolean_offset = offset_cxx.boolean_offset;
+ return offset;
+}
+
+/// Get flag offset
+FlagOffsetQuery get_flag_offset(
+ std::string const& container,
+ uint32_t package_id,
+ std::string const& flag_name) {
+ auto offset_cxx = get_flag_offset_cxx(
+ rust::Str(container.c_str()),
+ package_id,
+ rust::Str(flag_name.c_str()));
+ auto offset = FlagOffsetQuery();
+ offset.query_success = offset_cxx.query_success;
+ offset.error_message = std::string(offset_cxx.error_message.c_str());
+ offset.flag_exists = offset_cxx.flag_exists;
+ offset.flag_offset = offset_cxx.flag_offset;
+ return offset;
+}
+
+/// Get boolean flag value
+BooleanFlagValueQuery get_boolean_flag_value(
+ std::string const& container,
+ uint32_t offset) {
+ auto value_cxx = get_boolean_flag_value_cxx(
+ rust::Str(container.c_str()),
+ offset);
+ auto value = BooleanFlagValueQuery();
+ value.query_success = value_cxx.query_success;
+ value.error_message = std::string(value_cxx.error_message.c_str());
+ value.flag_value = value_cxx.flag_value;
+ return value;
+}
+
+namespace test_only_api {
+PackageOffsetQuery get_package_offset_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ std::string const& package) {
+ auto offset_cxx = get_package_offset_cxx_impl(
+ rust::Str(pb_file.c_str()),
+ rust::Str(container.c_str()),
+ rust::Str(package.c_str()));
+ auto offset = PackageOffsetQuery();
+ offset.query_success = offset_cxx.query_success;
+ offset.error_message = std::string(offset_cxx.error_message.c_str());
+ offset.package_exists = offset_cxx.package_exists;
+ offset.package_id = offset_cxx.package_id;
+ offset.boolean_offset = offset_cxx.boolean_offset;
+ return offset;
+}
+
+FlagOffsetQuery get_flag_offset_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ uint32_t package_id,
+ std::string const& flag_name) {
+ auto offset_cxx = get_flag_offset_cxx_impl(
+ rust::Str(pb_file.c_str()),
+ rust::Str(container.c_str()),
+ package_id,
+ rust::Str(flag_name.c_str()));
+ auto offset = FlagOffsetQuery();
+ offset.query_success = offset_cxx.query_success;
+ offset.error_message = std::string(offset_cxx.error_message.c_str());
+ offset.flag_exists = offset_cxx.flag_exists;
+ offset.flag_offset = offset_cxx.flag_offset;
+ return offset;
+}
+
+BooleanFlagValueQuery get_boolean_flag_value_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ uint32_t offset) {
+ auto value_cxx = get_boolean_flag_value_cxx_impl(
+ rust::Str(pb_file.c_str()),
+ rust::Str(container.c_str()),
+ offset);
+ auto value = BooleanFlagValueQuery();
+ value.query_success = value_cxx.query_success;
+ value.error_message = std::string(value_cxx.error_message.c_str());
+ value.flag_value = value_cxx.flag_value;
+ return value;
+}
+} // namespace test_only_api
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
new file mode 100644
index 0000000..894b71c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -0,0 +1,20 @@
+use protobuf_codegen::Codegen;
+
+fn main() {
+ let proto_files = vec!["protos/aconfig_storage_metadata.proto"];
+
+ // tell cargo to only re-run the build script if any of the proto files has changed
+ for path in &proto_files {
+ println!("cargo:rerun-if-changed={}", path);
+ }
+
+ Codegen::new()
+ .pure()
+ .include("protos")
+ .inputs(proto_files)
+ .cargo_out_dir("aconfig_storage_protos")
+ .run_from_script();
+
+ let _ = cxx_build::bridge("src/lib.rs");
+ println!("cargo:rerun-if-changed=src/lib.rs");
+}
diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
new file mode 100644
index 0000000..636fb7e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace aconfig_storage {
+
+/// Package offset query result
+struct PackageOffsetQuery {
+ bool query_success;
+ std::string error_message;
+ bool package_exists;
+ uint32_t package_id;
+ uint32_t boolean_offset;
+};
+
+/// Flag offset query result
+struct FlagOffsetQuery {
+ bool query_success;
+ std::string error_message;
+ bool flag_exists;
+ uint16_t flag_offset;
+};
+
+/// Boolean flag value query result
+struct BooleanFlagValueQuery {
+ bool query_success;
+ std::string error_message;
+ bool flag_value;
+};
+
+/// Get package offset
+/// \input container: the flag container name
+/// \input package: the flag package name
+/// \returns a PackageOffsetQuery
+PackageOffsetQuery get_package_offset(
+ std::string const& container,
+ std::string const& package);
+
+/// Get flag offset
+/// \input container: the flag container name
+/// \input package_id: the flag package id obtained from package offset query
+/// \input flag_name: flag name
+/// \returns a FlagOffsetQuery
+FlagOffsetQuery get_flag_offset(
+ std::string const& container,
+ uint32_t package_id,
+ std::string const& flag_name);
+
+/// Get boolean flag value
+/// \input container: the flag container name
+/// \input offset: the boolean flag value byte offset in the file
+/// \returns a BooleanFlagValueQuery
+BooleanFlagValueQuery get_boolean_flag_value(
+ std::string const& container,
+ uint32_t offset);
+
+/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE, TEST ONLY
+namespace test_only_api {
+PackageOffsetQuery get_package_offset_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ std::string const& package);
+
+FlagOffsetQuery get_flag_offset_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ uint32_t package_id,
+ std::string const& flag_name);
+
+BooleanFlagValueQuery get_boolean_flag_value_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ uint32_t offset);
+} // namespace test_only_api
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
new file mode 100644
index 0000000..c6728bd
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
@@ -0,0 +1,34 @@
+// 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
+
+// This is the schema definition for aconfig files. Modifications need to be
+// either backwards compatible, or include updates to all aconfig files in the
+// Android tree.
+
+syntax = "proto2";
+
+package android.aconfig_storage_metadata;
+
+message storage_file_info {
+ optional uint32 version = 1;
+ optional string container = 2;
+ optional string package_map = 3;
+ optional string flag_map = 4;
+ optional string flag_val = 5;
+ optional int64 timestamp = 6;
+}
+
+message storage_files {
+ repeated storage_file_info files = 1;
+};
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
new file mode 100644
index 0000000..108804e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+//! flag table module defines the flag table file format and methods for serialization
+//! and deserialization
+
+use crate::AconfigStorageError::{self, BytesParseFail, HigherStorageFileVersion};
+use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
+use anyhow::anyhow;
+pub type FlagOffset = u16;
+
+/// Flag table header struct
+#[derive(PartialEq, Debug)]
+pub struct FlagTableHeader {
+ pub version: u32,
+ pub container: String,
+ pub file_size: u32,
+ pub num_flags: u32,
+ pub bucket_offset: u32,
+ pub node_offset: u32,
+}
+
+impl FlagTableHeader {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ let mut result = Vec::new();
+ result.extend_from_slice(&self.version.to_le_bytes());
+ let container_bytes = self.container.as_bytes();
+ result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
+ result.extend_from_slice(container_bytes);
+ result.extend_from_slice(&self.file_size.to_le_bytes());
+ result.extend_from_slice(&self.num_flags.to_le_bytes());
+ result.extend_from_slice(&self.bucket_offset.to_le_bytes());
+ result.extend_from_slice(&self.node_offset.to_le_bytes());
+ result
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let mut head = 0;
+ Ok(Self {
+ version: read_u32_from_bytes(bytes, &mut head)?,
+ container: read_str_from_bytes(bytes, &mut head)?,
+ file_size: read_u32_from_bytes(bytes, &mut head)?,
+ num_flags: read_u32_from_bytes(bytes, &mut head)?,
+ bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
+ node_offset: read_u32_from_bytes(bytes, &mut head)?,
+ })
+ }
+}
+
+/// Flag table node struct
+#[derive(PartialEq, Debug, Clone)]
+pub struct FlagTableNode {
+ pub package_id: u32,
+ pub flag_name: String,
+ pub flag_type: u16,
+ pub flag_id: u16,
+ pub next_offset: Option<u32>,
+}
+
+impl FlagTableNode {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ let mut result = Vec::new();
+ result.extend_from_slice(&self.package_id.to_le_bytes());
+ let name_bytes = self.flag_name.as_bytes();
+ result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
+ result.extend_from_slice(name_bytes);
+ result.extend_from_slice(&self.flag_type.to_le_bytes());
+ result.extend_from_slice(&self.flag_id.to_le_bytes());
+ result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
+ result
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let mut head = 0;
+ let node = Self {
+ package_id: read_u32_from_bytes(bytes, &mut head)?,
+ flag_name: read_str_from_bytes(bytes, &mut head)?,
+ flag_type: read_u16_from_bytes(bytes, &mut head)?,
+ flag_id: read_u16_from_bytes(bytes, &mut head)?,
+ next_offset: match read_u32_from_bytes(bytes, &mut head)? {
+ 0 => None,
+ val => Some(val),
+ },
+ };
+ Ok(node)
+ }
+
+ /// Calculate node bucket index
+ pub fn find_bucket_index(package_id: u32, flag_name: &str, num_buckets: u32) -> u32 {
+ let full_flag_name = package_id.to_string() + "/" + flag_name;
+ get_bucket_index(&full_flag_name, num_buckets)
+ }
+}
+
+#[derive(PartialEq, Debug)]
+pub struct FlagTable {
+ pub header: FlagTableHeader,
+ pub buckets: Vec<Option<u32>>,
+ pub nodes: Vec<FlagTableNode>,
+}
+
+/// Flag table struct
+impl FlagTable {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ [
+ self.header.as_bytes(),
+ self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
+ self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+ ]
+ .concat()
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let header = FlagTableHeader::from_bytes(bytes)?;
+ let num_flags = header.num_flags;
+ let num_buckets = crate::get_table_size(num_flags)?;
+ let mut head = header.as_bytes().len();
+ let buckets = (0..num_buckets)
+ .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
+ 0 => None,
+ val => Some(val),
+ })
+ .collect();
+ let nodes = (0..num_flags)
+ .map(|_| {
+ let node = FlagTableNode::from_bytes(&bytes[head..])?;
+ head += node.as_bytes().len();
+ Ok(node)
+ })
+ .collect::<Result<Vec<_>, AconfigStorageError>>()
+ .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg)))?;
+
+ let table = Self { header, buckets, nodes };
+ Ok(table)
+ }
+}
+
+/// Query flag within package offset
+pub fn find_flag_offset(
+ buf: &[u8],
+ package_id: u32,
+ flag: &str,
+) -> Result<Option<FlagOffset>, AconfigStorageError> {
+ let interpreted_header = FlagTableHeader::from_bytes(buf)?;
+ if interpreted_header.version > crate::FILE_VERSION {
+ return Err(HigherStorageFileVersion(anyhow!(
+ "Cannot read storage file with a higher version of {} with lib version {}",
+ interpreted_header.version,
+ crate::FILE_VERSION
+ )));
+ }
+
+ let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
+ let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets);
+
+ let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
+ let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
+ if flag_node_offset < interpreted_header.node_offset as usize
+ || flag_node_offset >= interpreted_header.file_size as usize
+ {
+ return Ok(None);
+ }
+
+ loop {
+ let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
+ if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {
+ return Ok(Some(interpreted_node.flag_id));
+ }
+ match interpreted_node.next_offset {
+ Some(offset) => flag_node_offset = offset as usize,
+ None => return Ok(None),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ impl FlagTableNode {
+ // create test baseline, syntactic sugar
+ fn new_expected(
+ package_id: u32,
+ flag_name: &str,
+ flag_type: u16,
+ flag_id: u16,
+ next_offset: Option<u32>,
+ ) -> Self {
+ Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
+ }
+ }
+
+ pub fn create_test_flag_table() -> FlagTable {
+ let header = FlagTableHeader {
+ version: crate::FILE_VERSION,
+ container: String::from("system"),
+ file_size: 320,
+ num_flags: 8,
+ bucket_offset: 30,
+ node_offset: 98,
+ };
+ let buckets: Vec<Option<u32>> = vec![
+ Some(98),
+ Some(124),
+ None,
+ None,
+ None,
+ Some(177),
+ None,
+ Some(203),
+ None,
+ Some(261),
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(293),
+ None,
+ ];
+ let nodes = vec![
+ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
+ FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
+ FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
+ FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
+ FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
+ FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
+ ];
+ FlagTable { header, buckets, nodes }
+ }
+
+ #[test]
+ // this test point locks down the table serialization
+ fn test_serialization() {
+ let flag_table = create_test_flag_table();
+
+ let header: &FlagTableHeader = &flag_table.header;
+ let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
+ assert!(reinterpreted_header.is_ok());
+ assert_eq!(header, &reinterpreted_header.unwrap());
+
+ let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
+ for node in nodes.iter() {
+ let reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes()).unwrap();
+ assert_eq!(node, &reinterpreted_node);
+ }
+
+ let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
+ assert!(reinterpreted_table.is_ok());
+ assert_eq!(&flag_table, &reinterpreted_table.unwrap());
+ }
+
+ #[test]
+ // this test point locks down table query
+ fn test_flag_query() {
+ let flag_table = create_test_flag_table().as_bytes();
+ let baseline = vec![
+ (0, "enabled_ro", 1u16),
+ (0, "enabled_rw", 2u16),
+ (1, "disabled_ro", 0u16),
+ (2, "enabled_ro", 1u16),
+ (1, "enabled_fixed_ro", 1u16),
+ (1, "enabled_ro", 2u16),
+ (2, "enabled_fixed_ro", 0u16),
+ (0, "disabled_rw", 0u16),
+ ];
+ for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+ let flag_offset =
+ find_flag_offset(&flag_table[..], package_id, flag_name).unwrap().unwrap();
+ assert_eq!(flag_offset, expected_offset);
+ }
+ }
+
+ #[test]
+ // this test point locks down table query of a non exist flag
+ fn test_not_existed_flag_query() {
+ let flag_table = create_test_flag_table().as_bytes();
+ let flag_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
+ assert_eq!(flag_offset, None);
+ let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
+ assert_eq!(flag_offset, None);
+ }
+
+ #[test]
+ // this test point locks down query error when file has a higher version
+ fn test_higher_version_storage_file() {
+ let mut table = create_test_flag_table();
+ table.header.version = crate::FILE_VERSION + 1;
+ let flag_table = table.as_bytes();
+ let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+ crate::FILE_VERSION + 1,
+ crate::FILE_VERSION
+ )
+ );
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
new file mode 100644
index 0000000..0a6a37f
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+//! flag value module defines the flag value file format and methods for serialization
+//! and deserialization
+
+use crate::AconfigStorageError::{self, HigherStorageFileVersion, InvalidStorageFileOffset};
+use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use anyhow::anyhow;
+
+/// Flag value header struct
+#[derive(PartialEq, Debug)]
+pub struct FlagValueHeader {
+ pub version: u32,
+ pub container: String,
+ pub file_size: u32,
+ pub num_flags: u32,
+ pub boolean_value_offset: u32,
+}
+
+impl FlagValueHeader {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ let mut result = Vec::new();
+ result.extend_from_slice(&self.version.to_le_bytes());
+ let container_bytes = self.container.as_bytes();
+ result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
+ result.extend_from_slice(container_bytes);
+ result.extend_from_slice(&self.file_size.to_le_bytes());
+ result.extend_from_slice(&self.num_flags.to_le_bytes());
+ result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());
+ result
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let mut head = 0;
+ Ok(Self {
+ version: read_u32_from_bytes(bytes, &mut head)?,
+ container: read_str_from_bytes(bytes, &mut head)?,
+ file_size: read_u32_from_bytes(bytes, &mut head)?,
+ num_flags: read_u32_from_bytes(bytes, &mut head)?,
+ boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,
+ })
+ }
+}
+
+/// Flag value list struct
+#[derive(PartialEq, Debug)]
+pub struct FlagValueList {
+ pub header: FlagValueHeader,
+ pub booleans: Vec<bool>,
+}
+
+impl FlagValueList {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ [
+ self.header.as_bytes(),
+ self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::<Vec<_>>().concat(),
+ ]
+ .concat()
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let header = FlagValueHeader::from_bytes(bytes)?;
+ let num_flags = header.num_flags;
+ let mut head = header.as_bytes().len();
+ let booleans =
+ (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect();
+ let list = Self { header, booleans };
+ Ok(list)
+ }
+}
+
+/// Query flag value
+pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
+ let interpreted_header = FlagValueHeader::from_bytes(buf)?;
+ if interpreted_header.version > crate::FILE_VERSION {
+ return Err(HigherStorageFileVersion(anyhow!(
+ "Cannot read storage file with a higher version of {} with lib version {}",
+ interpreted_header.version,
+ crate::FILE_VERSION
+ )));
+ }
+
+ let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
+
+ // TODO: right now, there is only boolean flags, with more flag value types added
+ // later, the end of boolean flag value section should be updated (b/322826265).
+ if head >= interpreted_header.file_size as usize {
+ return Err(InvalidStorageFileOffset(anyhow!(
+ "Flag value offset goes beyond the end of the file."
+ )));
+ }
+
+ let val = read_u8_from_bytes(buf, &mut head)?;
+ Ok(val == 1)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ pub fn create_test_flag_value_list() -> FlagValueList {
+ let header = FlagValueHeader {
+ version: crate::FILE_VERSION,
+ container: String::from("system"),
+ file_size: 34,
+ num_flags: 8,
+ boolean_value_offset: 26,
+ };
+ let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+ FlagValueList { header, booleans }
+ }
+
+ #[test]
+ // this test point locks down the value list serialization
+ fn test_serialization() {
+ let flag_value_list = create_test_flag_value_list();
+
+ let header: &FlagValueHeader = &flag_value_list.header;
+ let reinterpreted_header = FlagValueHeader::from_bytes(&header.as_bytes());
+ assert!(reinterpreted_header.is_ok());
+ assert_eq!(header, &reinterpreted_header.unwrap());
+
+ let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_list.as_bytes());
+ assert!(reinterpreted_value_list.is_ok());
+ assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
+ }
+
+ #[test]
+ // this test point locks down flag value query
+ fn test_flag_value_query() {
+ let flag_value_list = create_test_flag_value_list().as_bytes();
+ let baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+ for (offset, expected_value) in baseline.into_iter().enumerate() {
+ let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
+ assert_eq!(flag_value, expected_value);
+ }
+ }
+
+ #[test]
+ // this test point locks down query beyond the end of boolean section
+ fn test_boolean_out_of_range() {
+ let flag_value_list = create_test_flag_value_list().as_bytes();
+ let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
+ );
+ }
+
+ #[test]
+ // this test point locks down query error when file has a higher version
+ fn test_higher_version_storage_file() {
+ let mut value_list = create_test_flag_value_list();
+ value_list.header.version = crate::FILE_VERSION + 1;
+ let flag_value = value_list.as_bytes();
+ let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+ crate::FILE_VERSION + 1,
+ crate::FILE_VERSION
+ )
+ );
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
new file mode 100644
index 0000000..84e0e90
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2023 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_storage_file` is a crate that defines aconfig storage file format, it
+//! also includes apis to read flags from storage files. It provides three apis to
+//! interface with storage files:
+//!
+//! 1, function to get package flag value start offset
+//! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`
+//!
+//! 2, function to get flag offset within a specific package
+//! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`
+//!
+//! 3, function to get the actual flag value given the global offset (combined package and
+//! flag offset).
+//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
+//!
+//! Note these are low level apis that are expected to be only used in auto generated flag
+//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
+//! please refer to the g3doc go/android-flags
+
+pub mod flag_table;
+pub mod flag_value;
+pub mod mapped_file;
+pub mod package_table;
+pub mod protos;
+
+#[cfg(test)]
+mod test_utils;
+
+use anyhow::anyhow;
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
+
+pub use crate::flag_table::{FlagOffset, FlagTable, FlagTableHeader, FlagTableNode};
+pub use crate::flag_value::{FlagValueHeader, FlagValueList};
+pub use crate::package_table::{PackageOffset, PackageTable, PackageTableHeader, PackageTableNode};
+pub use crate::protos::ProtoStorageFiles;
+
+use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit};
+
+/// Storage file version
+pub const FILE_VERSION: u32 = 1;
+
+/// Good hash table prime number
+pub(crate) const HASH_PRIMES: [u32; 29] = [
+ 7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241,
+ 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,
+ 402653189, 805306457, 1610612741,
+];
+
+/// Storage file location pb file
+pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/storage_files.pb";
+
+/// Storage file type enum
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum StorageFileSelection {
+ PackageMap,
+ FlagMap,
+ FlagVal,
+}
+
+impl TryFrom<&str> for StorageFileSelection {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ match value {
+ "package_map" => Ok(Self::PackageMap),
+ "flag_map" => Ok(Self::FlagMap),
+ "flag_val" => Ok(Self::FlagVal),
+ _ => Err(anyhow!("Invalid storage file to create")),
+ }
+ }
+}
+
+/// Get the right hash table size given number of entries in the table. Use a
+/// load factor of 0.5 for performance.
+pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
+ HASH_PRIMES
+ .iter()
+ .find(|&&num| num >= 2 * entries)
+ .copied()
+ .ok_or(HashTableSizeLimit(anyhow!("Number of items in a hash table exceeds limit")))
+}
+
+/// Get the corresponding bucket index given the key and number of buckets
+pub(crate) fn get_bucket_index<T: Hash>(val: &T, num_buckets: u32) -> u32 {
+ let mut s = DefaultHasher::new();
+ val.hash(&mut s);
+ (s.finish() % num_buckets as u64) as u32
+}
+
+/// Read and parse bytes as u8
+pub(crate) fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {
+ let val =
+ u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg))
+ })?);
+ *head += 1;
+ Ok(val)
+}
+
+/// Read and parse bytes as u16
+pub(crate) fn read_u16_from_bytes(
+ buf: &[u8],
+ head: &mut usize,
+) -> Result<u16, AconfigStorageError> {
+ let val =
+ u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
+ })?);
+ *head += 2;
+ Ok(val)
+}
+
+/// Read and parse bytes as u32
+pub(crate) fn read_u32_from_bytes(
+ buf: &[u8],
+ head: &mut usize,
+) -> Result<u32, AconfigStorageError> {
+ let val =
+ u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
+ })?);
+ *head += 4;
+ Ok(val)
+}
+
+/// Read and parse bytes as string
+pub(crate) fn read_str_from_bytes(
+ buf: &[u8],
+ head: &mut usize,
+) -> Result<String, AconfigStorageError> {
+ let num_bytes = read_u32_from_bytes(buf, head)? as usize;
+ let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())
+ .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
+ *head += num_bytes;
+ Ok(val)
+}
+
+/// Storage query api error
+#[non_exhaustive]
+#[derive(thiserror::Error, Debug)]
+pub enum AconfigStorageError {
+ #[error("failed to read the file")]
+ FileReadFail(#[source] anyhow::Error),
+
+ #[error("fail to parse protobuf")]
+ ProtobufParseFail(#[source] anyhow::Error),
+
+ #[error("storage files not found for this container")]
+ StorageFileNotFound(#[source] anyhow::Error),
+
+ #[error("fail to map storage file")]
+ MapFileFail(#[source] anyhow::Error),
+
+ #[error("number of items in hash table exceed limit")]
+ HashTableSizeLimit(#[source] anyhow::Error),
+
+ #[error("failed to parse bytes into data")]
+ BytesParseFail(#[source] anyhow::Error),
+
+ #[error("cannot parse storage files with a higher version")]
+ HigherStorageFileVersion(#[source] anyhow::Error),
+
+ #[error("invalid storage file byte offset")]
+ InvalidStorageFileOffset(#[source] anyhow::Error),
+}
+
+/// Get package start offset implementation
+pub fn get_package_offset_impl(
+ pb_file: &str,
+ container: &str,
+ package: &str,
+) -> Result<Option<PackageOffset>, AconfigStorageError> {
+ let mapped_file =
+ crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::PackageMap)?;
+ crate::package_table::find_package_offset(&mapped_file, package)
+}
+
+/// Get flag offset implementation
+pub fn get_flag_offset_impl(
+ pb_file: &str,
+ container: &str,
+ package_id: u32,
+ flag: &str,
+) -> Result<Option<FlagOffset>, AconfigStorageError> {
+ let mapped_file =
+ crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::FlagMap)?;
+ crate::flag_table::find_flag_offset(&mapped_file, package_id, flag)
+}
+
+/// Get boolean flag value implementation
+pub fn get_boolean_flag_value_impl(
+ pb_file: &str,
+ container: &str,
+ offset: u32,
+) -> Result<bool, AconfigStorageError> {
+ let mapped_file =
+ crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::FlagVal)?;
+ crate::flag_value::find_boolean_flag_value(&mapped_file, offset)
+}
+
+/// Get package start offset for flags given the container and package name.
+///
+/// This function would map the corresponding package map file if has not been mapped yet,
+/// and then look for the target package in this mapped file.
+///
+/// If a package is found, it returns Ok(Some(PackageOffset))
+/// If a package is not found, it returns Ok(None)
+/// If errors out such as no such package map file is found, it returns an Err(errmsg)
+pub fn get_package_offset(
+ container: &str,
+ package: &str,
+) -> Result<Option<PackageOffset>, AconfigStorageError> {
+ get_package_offset_impl(STORAGE_LOCATION_FILE, container, package)
+}
+
+/// Get flag offset within a package given the container name, package id and flag name.
+///
+/// This function would map the corresponding flag map file if has not been mapped yet,
+/// and then look for the target flag in this mapped file.
+///
+/// If a flag is found, it returns Ok(Some(u16))
+/// If a flag is not found, it returns Ok(None)
+/// If errors out such as no such flag map file is found, it returns an Err(errmsg)
+pub fn get_flag_offset(
+ container: &str,
+ package_id: u32,
+ flag: &str,
+) -> Result<Option<FlagOffset>, AconfigStorageError> {
+ get_flag_offset_impl(STORAGE_LOCATION_FILE, container, package_id, flag)
+}
+
+/// Get the boolean flag value given the container name and flag global offset
+///
+/// This function would map the corresponding flag value file if has not been mapped yet,
+/// and then look for the target flag value at the specified offset.
+///
+/// If flag value file is successfully mapped and the provide offset is valid, it returns
+/// the boolean flag value, otherwise it returns the error message.
+pub fn get_boolean_flag_value(container: &str, offset: u32) -> Result<bool, AconfigStorageError> {
+ get_boolean_flag_value_impl(STORAGE_LOCATION_FILE, container, offset)
+}
+
+#[cxx::bridge]
+mod ffi {
+ // Package table query return for cc interlop
+ pub struct PackageOffsetQueryCXX {
+ pub query_success: bool,
+ pub error_message: String,
+ pub package_exists: bool,
+ pub package_id: u32,
+ pub boolean_offset: u32,
+ }
+
+ // Flag table query return for cc interlop
+ pub struct FlagOffsetQueryCXX {
+ pub query_success: bool,
+ pub error_message: String,
+ pub flag_exists: bool,
+ pub flag_offset: u16,
+ }
+
+ // Flag value query return for cc interlop
+ pub struct BooleanFlagValueQueryCXX {
+ pub query_success: bool,
+ pub error_message: String,
+ pub flag_value: bool,
+ }
+
+ // Rust export to c++
+ extern "Rust" {
+ pub fn get_package_offset_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ package: &str,
+ ) -> PackageOffsetQueryCXX;
+
+ pub fn get_flag_offset_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ package_id: u32,
+ flag: &str,
+ ) -> FlagOffsetQueryCXX;
+
+ pub fn get_boolean_flag_value_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ offset: u32,
+ ) -> BooleanFlagValueQueryCXX;
+
+ pub fn get_package_offset_cxx(container: &str, package: &str) -> PackageOffsetQueryCXX;
+
+ pub fn get_flag_offset_cxx(
+ container: &str,
+ package_id: u32,
+ flag: &str,
+ ) -> FlagOffsetQueryCXX;
+
+ pub fn get_boolean_flag_value_cxx(container: &str, offset: u32)
+ -> BooleanFlagValueQueryCXX;
+ }
+}
+
+/// Get package start offset impl cc interlop
+pub fn get_package_offset_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ package: &str,
+) -> ffi::PackageOffsetQueryCXX {
+ ffi::PackageOffsetQueryCXX::new(get_package_offset_impl(pb_file, container, package))
+}
+
+/// Get flag start offset impl cc interlop
+pub fn get_flag_offset_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ package_id: u32,
+ flag: &str,
+) -> ffi::FlagOffsetQueryCXX {
+ ffi::FlagOffsetQueryCXX::new(get_flag_offset_impl(pb_file, container, package_id, flag))
+}
+
+/// Get boolean flag value impl cc interlop
+pub fn get_boolean_flag_value_cxx_impl(
+ pb_file: &str,
+ container: &str,
+ offset: u32,
+) -> ffi::BooleanFlagValueQueryCXX {
+ ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value_impl(pb_file, container, offset))
+}
+
+/// Get package start offset cc interlop
+pub fn get_package_offset_cxx(container: &str, package: &str) -> ffi::PackageOffsetQueryCXX {
+ ffi::PackageOffsetQueryCXX::new(get_package_offset(container, package))
+}
+
+/// Get flag start offset cc interlop
+pub fn get_flag_offset_cxx(
+ container: &str,
+ package_id: u32,
+ flag: &str,
+) -> ffi::FlagOffsetQueryCXX {
+ ffi::FlagOffsetQueryCXX::new(get_flag_offset(container, package_id, flag))
+}
+
+/// Get boolean flag value cc interlop
+pub fn get_boolean_flag_value_cxx(container: &str, offset: u32) -> ffi::BooleanFlagValueQueryCXX {
+ ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value(container, offset))
+}
+
+impl ffi::PackageOffsetQueryCXX {
+ pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
+ match offset_result {
+ Ok(offset_opt) => match offset_opt {
+ Some(offset) => Self {
+ query_success: true,
+ error_message: String::from(""),
+ package_exists: true,
+ package_id: offset.package_id,
+ boolean_offset: offset.boolean_offset,
+ },
+ None => Self {
+ query_success: true,
+ error_message: String::from(""),
+ package_exists: false,
+ package_id: 0,
+ boolean_offset: 0,
+ },
+ },
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ package_exists: false,
+ package_id: 0,
+ boolean_offset: 0,
+ },
+ }
+ }
+}
+
+impl ffi::FlagOffsetQueryCXX {
+ pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
+ match offset_result {
+ Ok(offset_opt) => match offset_opt {
+ Some(offset) => Self {
+ query_success: true,
+ error_message: String::from(""),
+ flag_exists: true,
+ flag_offset: offset,
+ },
+ None => Self {
+ query_success: true,
+ error_message: String::from(""),
+ flag_exists: false,
+ flag_offset: 0,
+ },
+ },
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ flag_exists: false,
+ flag_offset: 0,
+ },
+ }
+ }
+}
+
+impl ffi::BooleanFlagValueQueryCXX {
+ pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
+ match value_result {
+ Ok(value) => {
+ Self { query_success: true, error_message: String::from(""), flag_value: value }
+ }
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ flag_value: false,
+ },
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
+
+ fn create_test_storage_files(read_only: bool) -> TestStorageFileSet {
+ TestStorageFileSet::new(
+ "./tests/package.map",
+ "./tests/flag.map",
+ "./tests/flag.val",
+ read_only,
+ )
+ .unwrap()
+ }
+
+ #[test]
+ // this test point locks down flag package offset query
+ fn test_package_offset_query() {
+ let ro_files = create_test_storage_files(true);
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_1",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+ assert_eq!(package_offset, expected_package_offset);
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_2",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+ assert_eq!(package_offset, expected_package_offset);
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_4",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+ assert_eq!(package_offset, expected_package_offset);
+ }
+
+ #[test]
+ // this test point locks down flag offset query
+ fn test_flag_offset_query() {
+ let ro_files = create_test_storage_files(true);
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let baseline = vec![
+ (0, "enabled_ro", 1u16),
+ (0, "enabled_rw", 2u16),
+ (1, "disabled_ro", 0u16),
+ (2, "enabled_ro", 1u16),
+ (1, "enabled_fixed_ro", 1u16),
+ (1, "enabled_ro", 2u16),
+ (2, "enabled_fixed_ro", 0u16),
+ (0, "disabled_rw", 0u16),
+ ];
+ for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+ let flag_offset =
+ get_flag_offset_impl(&file_full_path, "system", package_id, flag_name)
+ .unwrap()
+ .unwrap();
+ assert_eq!(flag_offset, expected_offset);
+ }
+ }
+
+ #[test]
+ // this test point locks down flag offset query
+ fn test_flag_value_query() {
+ let ro_files = create_test_storage_files(true);
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let baseline: Vec<bool> = vec![false; 8];
+ for (offset, expected_value) in baseline.into_iter().enumerate() {
+ let flag_value =
+ get_boolean_flag_value_impl(&file_full_path, "system", offset as u32).unwrap();
+ assert_eq!(flag_value, expected_value);
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/mapped_file.rs b/tools/aconfig/aconfig_storage_file/src/mapped_file.rs
new file mode 100644
index 0000000..d8f2570
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/mapped_file.rs
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+use std::collections::HashMap;
+use std::fs::File;
+use std::io::{BufReader, Read};
+use std::sync::{Arc, Mutex};
+
+use anyhow::anyhow;
+use memmap2::Mmap;
+use once_cell::sync::Lazy;
+
+use crate::protos::{
+ storage_files::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
+};
+use crate::AconfigStorageError::{
+ self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
+};
+use crate::StorageFileSelection;
+
+/// Cache for already mapped files
+static ALL_MAPPED_FILES: Lazy<Mutex<HashMap<String, MappedStorageFileSet>>> = Lazy::new(|| {
+ let mapped_files = HashMap::new();
+ Mutex::new(mapped_files)
+});
+
+/// Mapped storage files for a particular container
+#[derive(Debug)]
+struct MappedStorageFileSet {
+ package_map: Arc<Mmap>,
+ flag_map: Arc<Mmap>,
+ flag_val: Arc<Mmap>,
+}
+
+/// Find where storage files are stored for a particular container
+fn find_container_storage_location(
+ location_pb_file: &str,
+ container: &str,
+) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
+ let file = File::open(location_pb_file).map_err(|errmsg| {
+ FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
+ })?;
+ let mut reader = BufReader::new(file);
+ let mut bytes = Vec::new();
+ reader.read_to_end(&mut bytes).map_err(|errmsg| {
+ FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
+ })?;
+ let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
+ ProtobufParseFail(anyhow!(
+ "Failed to parse storage location pb file {}: {}",
+ location_pb_file,
+ errmsg
+ ))
+ })?;
+ for location_info in storage_locations.files.iter() {
+ if location_info.container() == container {
+ return Ok(location_info.clone());
+ }
+ }
+ Err(StorageFileNotFound(anyhow!("Storage file does not exist for {}", container)))
+}
+
+/// Verify the file is read only and then map it
+fn verify_read_only_and_map(file_path: &str) -> Result<Mmap, AconfigStorageError> {
+ let file = File::open(file_path)
+ .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
+ let metadata = file.metadata().map_err(|errmsg| {
+ FileReadFail(anyhow!("Failed to find metadata for {}: {}", file_path, errmsg))
+ })?;
+
+ // ensure storage file is read only
+ if !metadata.permissions().readonly() {
+ return Err(MapFileFail(anyhow!("fail to map non read only storage file {}", file_path)));
+ }
+
+ // SAFETY:
+ //
+ // Mmap constructors are unsafe as it would have undefined behaviors if the file
+ // is modified after mapped (https://docs.rs/memmap2/latest/memmap2/struct.Mmap.html).
+ //
+ // We either have to make this api unsafe or ensure that the file will not be modified
+ // which means it is read only. Here in the code, we check explicitly that the file
+ // being mapped must only have read permission, otherwise, error out, thus making sure
+ // it is safe.
+ //
+ // We should remove this restriction if we need to support mmap non read only file in
+ // the future (by making this api unsafe). But for now, all flags are boot stable, so
+ // the boot flag file copy should be readonly.
+ unsafe {
+ let mapped_file = Mmap::map(&file).map_err(|errmsg| {
+ MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
+ })?;
+ Ok(mapped_file)
+ }
+}
+
+/// Map all storage files for a particular container
+fn map_container_storage_files(
+ location_pb_file: &str,
+ container: &str,
+) -> Result<MappedStorageFileSet, AconfigStorageError> {
+ let files_location = find_container_storage_location(location_pb_file, container)?;
+ let package_map = Arc::new(verify_read_only_and_map(files_location.package_map())?);
+ let flag_map = Arc::new(verify_read_only_and_map(files_location.flag_map())?);
+ let flag_val = Arc::new(verify_read_only_and_map(files_location.flag_val())?);
+ Ok(MappedStorageFileSet { package_map, flag_map, flag_val })
+}
+
+/// Get a mapped storage file given the container and file type
+pub(crate) fn get_mapped_file(
+ location_pb_file: &str,
+ container: &str,
+ file_selection: StorageFileSelection,
+) -> Result<Arc<Mmap>, AconfigStorageError> {
+ let mut all_mapped_files = ALL_MAPPED_FILES.lock().unwrap();
+ match all_mapped_files.get(container) {
+ Some(mapped_files) => Ok(match file_selection {
+ StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
+ StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
+ StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
+ }),
+ None => {
+ let mapped_files = map_container_storage_files(location_pb_file, container)?;
+ let file_ptr = match file_selection {
+ StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
+ StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
+ StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
+ };
+ all_mapped_files.insert(container.to_string(), mapped_files);
+ Ok(file_ptr)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
+
+ #[test]
+ fn test_find_storage_file_location() {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: "/system/etc/flag.map"
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+files {
+ version: 1
+ container: "product"
+ package_map: "/product/etc/package.map"
+ flag_map: "/product/etc/flag.map"
+ flag_val: "/metadata/aconfig/product.val"
+ timestamp: 54321
+}
+"#;
+ let file = write_storage_text_to_temp_file(text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let file_info = find_container_storage_location(&file_full_path, "system").unwrap();
+ assert_eq!(file_info.version(), 0);
+ assert_eq!(file_info.container(), "system");
+ assert_eq!(file_info.package_map(), "/system/etc/package.map");
+ assert_eq!(file_info.flag_map(), "/system/etc/flag.map");
+ assert_eq!(file_info.flag_val(), "/metadata/aconfig/system.val");
+ assert_eq!(file_info.timestamp(), 12345);
+
+ let file_info = find_container_storage_location(&file_full_path, "product").unwrap();
+ assert_eq!(file_info.version(), 1);
+ assert_eq!(file_info.container(), "product");
+ assert_eq!(file_info.package_map(), "/product/etc/package.map");
+ assert_eq!(file_info.flag_map(), "/product/etc/flag.map");
+ assert_eq!(file_info.flag_val(), "/metadata/aconfig/product.val");
+ assert_eq!(file_info.timestamp(), 54321);
+
+ let err = find_container_storage_location(&file_full_path, "vendor").unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "StorageFileNotFound(Storage file does not exist for vendor)"
+ );
+ }
+
+ fn map_and_verify(
+ location_pb_file: &str,
+ file_selection: StorageFileSelection,
+ actual_file: &str,
+ ) {
+ let mut opened_file = File::open(actual_file).unwrap();
+ let mut content = Vec::new();
+ opened_file.read_to_end(&mut content).unwrap();
+
+ let mmaped_file = get_mapped_file(location_pb_file, "system", file_selection).unwrap();
+ assert_eq!(mmaped_file[..], content[..]);
+ }
+
+ fn create_test_storage_files(read_only: bool) -> TestStorageFileSet {
+ TestStorageFileSet::new(
+ "./tests/package.map",
+ "./tests/flag.map",
+ "./tests/flag.val",
+ read_only,
+ )
+ .unwrap()
+ }
+
+ #[test]
+ fn test_mapped_file_contents() {
+ let ro_files = create_test_storage_files(true);
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ map_and_verify(
+ &file_full_path,
+ StorageFileSelection::PackageMap,
+ &ro_files.package_map.name,
+ );
+ map_and_verify(&file_full_path, StorageFileSelection::FlagMap, &ro_files.flag_map.name);
+ map_and_verify(&file_full_path, StorageFileSelection::FlagVal, &ro_files.flag_val.name);
+ }
+
+ #[test]
+ fn test_map_non_read_only_file() {
+ let ro_files = create_test_storage_files(true);
+ let rw_files = create_test_storage_files(false);
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ rw_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "MapFileFail(fail to map non read only storage file {})",
+ rw_files.package_map.name
+ )
+ );
+
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, rw_files.flag_map.name, ro_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "MapFileFail(fail to map non read only storage file {})",
+ rw_files.flag_map.name
+ )
+ );
+
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "{}"
+ flag_map: "{}"
+ flag_val: "{}"
+ timestamp: 12345
+}}
+"#,
+ ro_files.package_map.name, ro_files.flag_map.name, rw_files.flag_val.name
+ );
+
+ let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file_full_path = file.path().display().to_string();
+ let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "MapFileFail(fail to map non read only storage file {})",
+ rw_files.flag_val.name
+ )
+ );
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
new file mode 100644
index 0000000..7308d7b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! package table module defines the package table file format and methods for serialization
+//! and deserialization
+
+use crate::AconfigStorageError::{self, BytesParseFail, HigherStorageFileVersion};
+use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes};
+use anyhow::anyhow;
+
+/// Package table header struct
+#[derive(PartialEq, Debug)]
+pub struct PackageTableHeader {
+ pub version: u32,
+ pub container: String,
+ pub file_size: u32,
+ pub num_packages: u32,
+ pub bucket_offset: u32,
+ pub node_offset: u32,
+}
+
+impl PackageTableHeader {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ let mut result = Vec::new();
+ result.extend_from_slice(&self.version.to_le_bytes());
+ let container_bytes = self.container.as_bytes();
+ result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
+ result.extend_from_slice(container_bytes);
+ result.extend_from_slice(&self.file_size.to_le_bytes());
+ result.extend_from_slice(&self.num_packages.to_le_bytes());
+ result.extend_from_slice(&self.bucket_offset.to_le_bytes());
+ result.extend_from_slice(&self.node_offset.to_le_bytes());
+ result
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let mut head = 0;
+ Ok(Self {
+ version: read_u32_from_bytes(bytes, &mut head)?,
+ container: read_str_from_bytes(bytes, &mut head)?,
+ file_size: read_u32_from_bytes(bytes, &mut head)?,
+ num_packages: read_u32_from_bytes(bytes, &mut head)?,
+ bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
+ node_offset: read_u32_from_bytes(bytes, &mut head)?,
+ })
+ }
+}
+
+/// Package table node struct
+#[derive(PartialEq, Debug)]
+pub struct PackageTableNode {
+ pub package_name: String,
+ pub package_id: u32,
+ // offset of the first boolean flag in this flag package with respect to the start of
+ // boolean flag value array in the flag value file
+ pub boolean_offset: u32,
+ pub next_offset: Option<u32>,
+}
+
+impl PackageTableNode {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ let mut result = Vec::new();
+ let name_bytes = self.package_name.as_bytes();
+ 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.boolean_offset.to_le_bytes());
+ result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
+ result
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let mut head = 0;
+ let node = Self {
+ package_name: read_str_from_bytes(bytes, &mut head)?,
+ package_id: read_u32_from_bytes(bytes, &mut head)?,
+ boolean_offset: read_u32_from_bytes(bytes, &mut head)?,
+ next_offset: match read_u32_from_bytes(bytes, &mut head)? {
+ 0 => None,
+ val => Some(val),
+ },
+ };
+ Ok(node)
+ }
+
+ /// Get the bucket index for a package table node, defined it here so the
+ /// construction side (aconfig binary) and consumption side (flag read lib)
+ /// use the same method of hashing
+ pub fn find_bucket_index(package: &str, num_buckets: u32) -> u32 {
+ get_bucket_index(&package, num_buckets)
+ }
+}
+
+/// Package table struct
+#[derive(PartialEq, Debug)]
+pub struct PackageTable {
+ pub header: PackageTableHeader,
+ pub buckets: Vec<Option<u32>>,
+ pub nodes: Vec<PackageTableNode>,
+}
+
+impl PackageTable {
+ /// Serialize to bytes
+ pub fn as_bytes(&self) -> Vec<u8> {
+ [
+ self.header.as_bytes(),
+ self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
+ self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+ ]
+ .concat()
+ }
+
+ /// Deserialize from bytes
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+ let header = PackageTableHeader::from_bytes(bytes)?;
+ let num_packages = header.num_packages;
+ let num_buckets = crate::get_table_size(num_packages)?;
+ let mut head = header.as_bytes().len();
+ let buckets = (0..num_buckets)
+ .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
+ 0 => None,
+ val => Some(val),
+ })
+ .collect();
+ let nodes = (0..num_packages)
+ .map(|_| {
+ let node = PackageTableNode::from_bytes(&bytes[head..])?;
+ head += node.as_bytes().len();
+ Ok(node)
+ })
+ .collect::<Result<Vec<_>, AconfigStorageError>>()
+ .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse package table: {}", errmsg)))?;
+
+ let table = Self { header, buckets, nodes };
+ Ok(table)
+ }
+}
+
+/// Package table query return
+#[derive(PartialEq, Debug)]
+pub struct PackageOffset {
+ pub package_id: u32,
+ pub boolean_offset: u32,
+}
+
+/// Query package id and start offset
+pub fn find_package_offset(
+ buf: &[u8],
+ package: &str,
+) -> Result<Option<PackageOffset>, AconfigStorageError> {
+ let interpreted_header = PackageTableHeader::from_bytes(buf)?;
+ if interpreted_header.version > crate::FILE_VERSION {
+ return Err(HigherStorageFileVersion(anyhow!(
+ "Cannot read storage file with a higher version of {} with lib version {}",
+ interpreted_header.version,
+ crate::FILE_VERSION
+ )));
+ }
+
+ let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
+ let bucket_index = PackageTableNode::find_bucket_index(package, num_buckets);
+
+ let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
+ let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
+ if package_node_offset < interpreted_header.node_offset as usize
+ || package_node_offset >= interpreted_header.file_size as usize
+ {
+ return Ok(None);
+ }
+
+ loop {
+ let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..])?;
+ if interpreted_node.package_name == package {
+ return Ok(Some(PackageOffset {
+ package_id: interpreted_node.package_id,
+ boolean_offset: interpreted_node.boolean_offset,
+ }));
+ }
+ match interpreted_node.next_offset {
+ Some(offset) => package_node_offset = offset as usize,
+ None => return Ok(None),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ pub fn create_test_package_table() -> PackageTable {
+ let header = PackageTableHeader {
+ version: crate::FILE_VERSION,
+ container: String::from("system"),
+ file_size: 208,
+ num_packages: 3,
+ bucket_offset: 30,
+ node_offset: 58,
+ };
+ let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+ let first_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_2"),
+ package_id: 1,
+ boolean_offset: 3,
+ next_offset: None,
+ };
+ let second_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_1"),
+ package_id: 0,
+ boolean_offset: 0,
+ next_offset: Some(158),
+ };
+ let third_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_4"),
+ package_id: 2,
+ boolean_offset: 6,
+ next_offset: None,
+ };
+ let nodes = vec![first_node, second_node, third_node];
+ PackageTable { header, buckets, nodes }
+ }
+
+ #[test]
+ // this test point locks down the table serialization
+ fn test_serialization() {
+ let package_table = create_test_package_table();
+ let header: &PackageTableHeader = &package_table.header;
+ let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
+ assert!(reinterpreted_header.is_ok());
+ assert_eq!(header, &reinterpreted_header.unwrap());
+
+ let nodes: &Vec<PackageTableNode> = &package_table.nodes;
+ for node in nodes.iter() {
+ let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes()).unwrap();
+ assert_eq!(node, &reinterpreted_node);
+ }
+
+ let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
+ assert!(reinterpreted_table.is_ok());
+ assert_eq!(&package_table, &reinterpreted_table.unwrap());
+ }
+
+ #[test]
+ // this test point locks down table query
+ fn test_package_query() {
+ let package_table = create_test_package_table().as_bytes();
+ let package_offset =
+ find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+ assert_eq!(package_offset, expected_package_offset);
+ let package_offset =
+ find_package_offset(&package_table[..], "com.android.aconfig.storage.test_2")
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+ assert_eq!(package_offset, expected_package_offset);
+ let package_offset =
+ find_package_offset(&package_table[..], "com.android.aconfig.storage.test_4")
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+ assert_eq!(package_offset, expected_package_offset);
+ }
+
+ #[test]
+ // this test point locks down table query of a non exist package
+ fn test_not_existed_package_query() {
+ // this will land at an empty bucket
+ let package_table = create_test_package_table().as_bytes();
+ let package_offset =
+ find_package_offset(&package_table[..], "com.android.aconfig.storage.test_3").unwrap();
+ assert_eq!(package_offset, None);
+ // this will land at the end of a linked list
+ let package_offset =
+ find_package_offset(&package_table[..], "com.android.aconfig.storage.test_5").unwrap();
+ assert_eq!(package_offset, None);
+ }
+
+ #[test]
+ // this test point locks down query error when file has a higher version
+ fn test_higher_version_storage_file() {
+ let mut table = create_test_package_table();
+ table.header.version = crate::FILE_VERSION + 1;
+ let package_table = table.as_bytes();
+ let error = find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+ .unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+ crate::FILE_VERSION + 1,
+ crate::FILE_VERSION
+ )
+ );
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/protos.rs b/tools/aconfig/aconfig_storage_file/src/protos.rs
new file mode 100644
index 0000000..37df3e1
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/protos.rs
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+// When building with the Android tool-chain
+//
+// - an external crate `aconfig_storage_metadata_protos` will be generated
+// - the feature "cargo" will be disabled
+//
+// When building with cargo
+//
+// - a local sub-module will be generated in OUT_DIR and included in this file
+// - the feature "cargo" will be enabled
+//
+// This module hides these differences from the rest of the codebase.
+
+// ---- When building with the Android tool-chain ----
+#[cfg(not(feature = "cargo"))]
+mod auto_generated {
+ pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage;
+ pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo;
+ pub use ProtoStorage::Storage_files as ProtoStorageFiles;
+}
+
+// ---- When building with cargo ----
+#[cfg(feature = "cargo")]
+mod auto_generated {
+ // include! statements should be avoided (because they import file contents verbatim), but
+ // because this is only used during local development, and only if using cargo instead of the
+ // Android tool-chain, we allow it
+ include!(concat!(env!("OUT_DIR"), "/aconfig_storage_protos/mod.rs"));
+ pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo;
+ pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles;
+}
+
+// ---- Common for both the Android tool-chain and cargo ----
+pub use auto_generated::*;
+
+use anyhow::Result;
+
+pub mod storage_files {
+ use super::*;
+ use anyhow::ensure;
+
+ pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoStorageFiles> {
+ let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?;
+ verify_fields(&message)?;
+ Ok(message)
+ }
+
+ pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> {
+ for storage_file_info in storage_files.files.iter() {
+ ensure!(
+ !storage_file_info.package_map().is_empty(),
+ "invalid storage file record: missing package map file for container {}",
+ storage_file_info.container()
+ );
+ ensure!(
+ !storage_file_info.flag_map().is_empty(),
+ "invalid storage file record: missing flag map file for container {}",
+ storage_file_info.container()
+ );
+ ensure!(
+ !storage_file_info.flag_val().is_empty(),
+ "invalid storage file record: missing flag val file for container {}",
+ storage_file_info.container()
+ );
+ }
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::test_utils::get_binary_storage_proto_bytes;
+
+ #[test]
+ fn test_parse_storage_files() {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: "/system/etc/flag.map"
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+files {
+ version: 1
+ container: "product"
+ package_map: "/product/etc/package.map"
+ flag_map: "/product/etc/flag.map"
+ flag_val: "/metadata/aconfig/product.val"
+ timestamp: 54321
+}
+"#;
+ let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
+ let storage_files = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap();
+ assert_eq!(storage_files.files.len(), 2);
+ let system_file = &storage_files.files[0];
+ assert_eq!(system_file.version(), 0);
+ assert_eq!(system_file.container(), "system");
+ assert_eq!(system_file.package_map(), "/system/etc/package.map");
+ assert_eq!(system_file.flag_map(), "/system/etc/flag.map");
+ assert_eq!(system_file.flag_val(), "/metadata/aconfig/system.val");
+ assert_eq!(system_file.timestamp(), 12345);
+ let product_file = &storage_files.files[1];
+ assert_eq!(product_file.version(), 1);
+ assert_eq!(product_file.container(), "product");
+ assert_eq!(product_file.package_map(), "/product/etc/package.map");
+ assert_eq!(product_file.flag_map(), "/product/etc/flag.map");
+ assert_eq!(product_file.flag_val(), "/metadata/aconfig/product.val");
+ assert_eq!(product_file.timestamp(), 54321);
+ }
+
+ #[test]
+ fn test_parse_invalid_storage_files() {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: ""
+ flag_map: "/system/etc/flag.map"
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
+ let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing package map file for container system"
+ );
+
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: ""
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
+ let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing flag map file for container system"
+ );
+
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: "/system/etc/flag.map"
+ flag_val: ""
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
+ let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing flag val file for container system"
+ );
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
new file mode 100644
index 0000000..7905d51
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use crate::protos::ProtoStorageFiles;
+use anyhow::Result;
+use protobuf::Message;
+use std::fs;
+use std::io::Write;
+use tempfile::NamedTempFile;
+
+pub(crate) fn get_binary_storage_proto_bytes(text_proto: &str) -> Result<Vec<u8>> {
+ let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
+ let mut binary_proto = Vec::new();
+ storage_files.write_to_vec(&mut binary_proto)?;
+ Ok(binary_proto)
+}
+
+pub(crate) fn write_storage_text_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
+ let bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
+ let mut file = NamedTempFile::new()?;
+ let _ = file.write_all(&bytes);
+ Ok(file)
+}
+
+fn set_file_read_only(file: &NamedTempFile) {
+ let mut perms = fs::metadata(file.path()).unwrap().permissions();
+ if !perms.readonly() {
+ perms.set_readonly(true);
+ fs::set_permissions(file.path(), perms).unwrap();
+ }
+}
+
+fn set_file_read_write(file: &NamedTempFile) {
+ let mut perms = fs::metadata(file.path()).unwrap().permissions();
+ if perms.readonly() {
+ perms.set_readonly(false);
+ fs::set_permissions(file.path(), perms).unwrap();
+ }
+}
+
+pub(crate) struct TestStorageFile {
+ pub file: NamedTempFile,
+ pub name: String,
+}
+
+impl TestStorageFile {
+ pub(crate) fn new(source_file: &str, read_only: bool) -> Result<Self> {
+ let file = NamedTempFile::new()?;
+ fs::copy(source_file, file.path())?;
+ if read_only {
+ set_file_read_only(&file);
+ } else {
+ set_file_read_write(&file);
+ }
+ let name = file.path().display().to_string();
+ Ok(Self { file, name })
+ }
+}
+
+pub(crate) struct TestStorageFileSet {
+ pub package_map: TestStorageFile,
+ pub flag_map: TestStorageFile,
+ pub flag_val: TestStorageFile,
+}
+
+impl TestStorageFileSet {
+ pub(crate) fn new(
+ package_map_path: &str,
+ flag_map_path: &str,
+ flag_val_path: &str,
+ read_only: bool,
+ ) -> Result<Self> {
+ Ok(Self {
+ package_map: TestStorageFile::new(package_map_path, read_only)?,
+ flag_map: TestStorageFile::new(flag_map_path, read_only)?,
+ flag_val: TestStorageFile::new(flag_val_path, read_only)?,
+ })
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
new file mode 100644
index 0000000..b951273
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -0,0 +1,42 @@
+rust_test {
+ name: "aconfig_storage.test.rust",
+ srcs: [
+ "storage_lib_rust_test.rs"
+ ],
+ rustlibs: [
+ "libanyhow",
+ "libaconfig_storage_file",
+ "libprotobuf",
+ "libtempfile",
+ ],
+ data: [
+ ":ro.package.map",
+ ":ro.flag.map",
+ ":ro.flag.val",
+ ],
+ test_suites: ["general-tests"],
+}
+
+cc_test {
+ name: "aconfig_storage.test.cpp",
+ srcs: [
+ "storage_lib_cc_test.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libaconfig_storage_protos_cc",
+ "libprotobuf-cpp-lite",
+ "libaconfig_storage_cc",
+ "libbase",
+ "liblog",
+ ],
+ data: [
+ ":ro.package.map",
+ ":ro.flag.map",
+ ":ro.flag.val",
+ ],
+ test_suites: [
+ "device-tests",
+ "general-tests",
+ ],
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map
new file mode 100644
index 0000000..43b6f9a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val
new file mode 100644
index 0000000..f39f8d3
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map
new file mode 100644
index 0000000..8ed4767
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/package.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp
new file mode 100644
index 0000000..7d5ba0a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+
+#include "aconfig_storage/aconfig_storage.hpp"
+#include <gtest/gtest.h>
+#include <protos/aconfig_storage_metadata.pb.h>
+#include <android-base/file.h>
+
+using android::aconfig_storage_metadata::storage_files;
+using ::android::base::WriteStringToFile;
+using ::aconfig_storage::test_only_api::get_package_offset_impl;
+using ::aconfig_storage::test_only_api::get_flag_offset_impl;
+using ::aconfig_storage::test_only_api::get_boolean_flag_value_impl;
+
+void write_storage_location_pb_to_file(std::string const& file_path) {
+ auto const test_dir = android::base::GetExecutableDirectory();
+ auto proto = storage_files();
+ auto* info = proto.add_files();
+ info->set_version(0);
+ info->set_container("system");
+ info->set_package_map(test_dir + "/tests/tmp.ro.package.map");
+ info->set_flag_map(test_dir + "/tests/tmp.ro.flag.map");
+ info->set_flag_val(test_dir + "/tests/tmp.ro.flag.val");
+ info->set_timestamp(12345);
+
+ auto content = std::string();
+ proto.SerializeToString(&content);
+ ASSERT_TRUE(WriteStringToFile(content, file_path))
+ << "Failed to write a file: " << file_path;
+}
+
+TEST(AconfigStorageTest, test_package_offset_query) {
+ auto pb_file = std::string("/tmp/test_package_offset_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+
+ auto query = get_package_offset_impl(
+ pb_file, "system", "com.android.aconfig.storage.test_1");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_TRUE(query.package_exists);
+ ASSERT_EQ(query.package_id, 0);
+ ASSERT_EQ(query.boolean_offset, 0);
+
+ query = get_package_offset_impl(
+ pb_file, "system", "com.android.aconfig.storage.test_2");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_TRUE(query.package_exists);
+ ASSERT_EQ(query.package_id, 1);
+ ASSERT_EQ(query.boolean_offset, 3);
+
+ query = get_package_offset_impl(
+ pb_file, "system", "com.android.aconfig.storage.test_4");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_TRUE(query.package_exists);
+ ASSERT_EQ(query.package_id, 2);
+ ASSERT_EQ(query.boolean_offset, 6);
+}
+
+TEST(AconfigStorageTest, test_invalid_package_offset_query) {
+ auto pb_file = std::string("/tmp/test_package_offset_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+
+ auto query = get_package_offset_impl(
+ pb_file, "system", "com.android.aconfig.storage.test_3");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_FALSE(query.package_exists);
+
+ query = get_package_offset_impl(
+ pb_file, "vendor", "com.android.aconfig.storage.test_1");
+ ASSERT_EQ(query.error_message,
+ std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
+ ASSERT_FALSE(query.query_success);
+}
+
+TEST(AconfigStorageTest, test_flag_offset_query) {
+ auto pb_file = std::string("/tmp/test_package_offset_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+
+ auto baseline = std::vector<std::tuple<int, std::string, int>>{
+ {0, "enabled_ro", 1},
+ {0, "enabled_rw", 2},
+ {1, "disabled_ro", 0},
+ {2, "enabled_ro", 1},
+ {1, "enabled_fixed_ro", 1},
+ {1, "enabled_ro", 2},
+ {2, "enabled_fixed_ro", 0},
+ {0, "disabled_rw", 0},
+ };
+ for (auto const&[package_id, flag_name, expected_offset] : baseline) {
+ auto query = get_flag_offset_impl(pb_file, "system", package_id, flag_name);
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_TRUE(query.flag_exists);
+ ASSERT_EQ(query.flag_offset, expected_offset);
+ }
+}
+
+TEST(AconfigStorageTest, test_invalid_flag_offset_query) {
+ auto pb_file = std::string("/tmp/test_invalid_package_offset_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+
+ auto query = get_flag_offset_impl(pb_file, "system", 0, "none_exist");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_FALSE(query.flag_exists);
+
+ query = get_flag_offset_impl(pb_file, "system", 3, "enabled_ro");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_FALSE(query.flag_exists);
+
+ query = get_flag_offset_impl(pb_file, "vendor", 0, "enabled_ro");
+ ASSERT_EQ(query.error_message,
+ std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
+ ASSERT_FALSE(query.query_success);
+}
+
+TEST(AconfigStorageTest, test_boolean_flag_value_query) {
+ auto pb_file = std::string("/tmp/test_boolean_flag_value_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+ for (int offset = 0; offset < 8; ++offset) {
+ auto query = get_boolean_flag_value_impl(pb_file, "system", offset);
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_FALSE(query.flag_value);
+ }
+}
+
+TEST(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
+ auto pb_file = std::string("/tmp/test_invalid_boolean_flag_value_query.pb");
+ write_storage_location_pb_to_file(pb_file);
+
+ auto query = get_boolean_flag_value_impl(pb_file, "vendor", 0);
+ ASSERT_EQ(query.error_message,
+ std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
+ ASSERT_FALSE(query.query_success);
+
+ query = get_boolean_flag_value_impl(pb_file, "system", 8);
+ ASSERT_EQ(query.error_message,
+ std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
+ ASSERT_FALSE(query.query_success);
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs b/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs
new file mode 100644
index 0000000..9916915
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs
@@ -0,0 +1,174 @@
+#[cfg(not(feature = "cargo"))]
+mod aconfig_storage_rust_test {
+ use aconfig_storage_file::{
+ get_boolean_flag_value_impl, get_flag_offset_impl, get_package_offset_impl, PackageOffset,
+ ProtoStorageFiles,
+ };
+ use protobuf::Message;
+ use std::io::Write;
+ use tempfile::NamedTempFile;
+
+ fn write_storage_location_file() -> NamedTempFile {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "./tests/tmp.ro.package.map"
+ flag_map: "./tests/tmp.ro.flag.map"
+ flag_val: "./tests/tmp.ro.flag.val"
+ timestamp: 12345
+}
+"#;
+ let storage_files: ProtoStorageFiles =
+ protobuf::text_format::parse_from_str(text_proto).unwrap();
+ let mut binary_proto_bytes = Vec::new();
+ storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
+ let mut file = NamedTempFile::new().unwrap();
+ file.write_all(&binary_proto_bytes).unwrap();
+ file
+ }
+
+ #[test]
+ fn test_package_offset_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_1",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+ assert_eq!(package_offset, expected_package_offset);
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_2",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+ assert_eq!(package_offset, expected_package_offset);
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_4",
+ )
+ .unwrap()
+ .unwrap();
+ let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+ assert_eq!(package_offset, expected_package_offset);
+
+ let package_offset = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_3",
+ )
+ .unwrap();
+ assert_eq!(package_offset, None);
+ }
+
+ #[test]
+ fn test_invalid_package_offset_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let package_offset_option = get_package_offset_impl(
+ &file_full_path,
+ "system",
+ "com.android.aconfig.storage.test_3",
+ )
+ .unwrap();
+ assert_eq!(package_offset_option, None);
+
+ let err = get_package_offset_impl(
+ &file_full_path,
+ "vendor",
+ "com.android.aconfig.storage.test_1",
+ )
+ .unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "StorageFileNotFound(Storage file does not exist for vendor)"
+ );
+ }
+
+ #[test]
+ fn test_flag_offset_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let baseline = vec![
+ (0, "enabled_ro", 1u16),
+ (0, "enabled_rw", 2u16),
+ (1, "disabled_ro", 0u16),
+ (2, "enabled_ro", 1u16),
+ (1, "enabled_fixed_ro", 1u16),
+ (1, "enabled_ro", 2u16),
+ (2, "enabled_fixed_ro", 0u16),
+ (0, "disabled_rw", 0u16),
+ ];
+ for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+ let flag_offset =
+ get_flag_offset_impl(&file_full_path, "system", package_id, flag_name)
+ .unwrap()
+ .unwrap();
+ assert_eq!(flag_offset, expected_offset);
+ }
+ }
+
+ #[test]
+ fn test_invalid_flag_offset_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let flag_offset_option =
+ get_flag_offset_impl(&file_full_path, "system", 0, "none_exist").unwrap();
+ assert_eq!(flag_offset_option, None);
+
+ let flag_offset_option =
+ get_flag_offset_impl(&file_full_path, "system", 3, "enabled_ro").unwrap();
+ assert_eq!(flag_offset_option, None);
+
+ let err = get_flag_offset_impl(&file_full_path, "vendor", 0, "enabled_ro").unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "StorageFileNotFound(Storage file does not exist for vendor)"
+ );
+ }
+
+ #[test]
+ fn test_boolean_flag_value_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let baseline: Vec<bool> = vec![false; 8];
+ for (offset, expected_value) in baseline.into_iter().enumerate() {
+ let flag_value =
+ get_boolean_flag_value_impl(&file_full_path, "system", offset as u32).unwrap();
+ assert_eq!(flag_value, expected_value);
+ }
+ }
+
+ #[test]
+ fn test_invalid_boolean_flag_value_query() {
+ let file = write_storage_location_file();
+ let file_full_path = file.path().display().to_string();
+
+ let err = get_boolean_flag_value_impl(&file_full_path, "vendor", 0u32).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "StorageFileNotFound(Storage file does not exist for vendor)"
+ );
+
+ let err = get_boolean_flag_value_impl(&file_full_path, "system", 8u32).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
+ );
+ }
+}
diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp
new file mode 100644
index 0000000..c65da97
--- /dev/null
+++ b/tools/aconfig/aflags/Android.bp
@@ -0,0 +1,29 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "aflags.defaults",
+ edition: "2021",
+ clippy_lints: "android",
+ lints: "android",
+ srcs: ["src/main.rs"],
+ rustlibs: [
+ "libaconfig_protos",
+ "libanyhow",
+ "libclap",
+ "libprotobuf",
+ "libregex",
+ ],
+}
+
+rust_binary {
+ name: "aflags",
+ defaults: ["aflags.defaults"],
+}
+
+rust_test_host {
+ name: "aflags.test",
+ defaults: ["aflags.defaults"],
+ test_suites: ["general-tests"],
+}
diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml
new file mode 100644
index 0000000..3350a6cd
--- /dev/null
+++ b/tools/aconfig/aflags/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "aflags"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.69"
+paste = "1.0.11"
+clap = { version = "4", features = ["derive"] }
+protobuf = "3.2.0"
+regex = "1.10.3"
+aconfig_protos = { path = "../aconfig_protos" }
diff --git a/tools/aconfig/aflags/src/device_config_source.rs b/tools/aconfig/aflags/src/device_config_source.rs
new file mode 100644
index 0000000..12a62cf
--- /dev/null
+++ b/tools/aconfig/aflags/src/device_config_source.rs
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+use crate::{Flag, FlagPermission, FlagSource, ValuePickedFrom};
+use aconfig_protos::ProtoFlagPermission as ProtoPermission;
+use aconfig_protos::ProtoFlagState as ProtoState;
+use aconfig_protos::ProtoParsedFlag;
+use aconfig_protos::ProtoParsedFlags;
+use anyhow::{anyhow, bail, Result};
+use regex::Regex;
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::process::Command;
+use std::{fs, str};
+
+pub struct DeviceConfigSource {}
+
+fn convert_parsed_flag(flag: &ProtoParsedFlag) -> Flag {
+ let namespace = flag.namespace().to_string();
+ let package = flag.package().to_string();
+ let name = flag.name().to_string();
+
+ let container = if flag.container().is_empty() {
+ "system".to_string()
+ } else {
+ flag.container().to_string()
+ };
+
+ let value = match flag.state() {
+ ProtoState::ENABLED => "true",
+ ProtoState::DISABLED => "false",
+ }
+ .to_string();
+
+ let permission = match flag.permission() {
+ ProtoPermission::READ_ONLY => FlagPermission::ReadOnly,
+ ProtoPermission::READ_WRITE => FlagPermission::ReadWrite,
+ };
+
+ Flag {
+ namespace,
+ package,
+ name,
+ container,
+ value,
+ permission,
+ value_picked_from: ValuePickedFrom::Default,
+ }
+}
+
+fn read_pb_files() -> Result<Vec<Flag>> {
+ let mut flags: BTreeMap<String, Flag> = BTreeMap::new();
+ for partition in ["system", "system_ext", "product", "vendor"] {
+ let path = format!("/{}/etc/aconfig_flags.pb", partition);
+ let Ok(bytes) = fs::read(&path) else {
+ eprintln!("warning: failed to read {}", path);
+ continue;
+ };
+ let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?;
+ for flag in parsed_flags.parsed_flag {
+ let key = format!("{}.{}", flag.package(), flag.name());
+ let container = if flag.container().is_empty() {
+ "system".to_string()
+ } else {
+ flag.container().to_string()
+ };
+
+ if container.eq(partition) {
+ flags.insert(key, convert_parsed_flag(&flag));
+ }
+ }
+ }
+ Ok(flags.values().cloned().collect())
+}
+
+fn parse_device_config(raw: &str) -> Result<HashMap<String, String>> {
+ let mut flags = HashMap::new();
+ let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$")?;
+ for capture in regex.captures_iter(raw) {
+ let key =
+ capture.get(1).ok_or(anyhow!("invalid device_config output"))?.as_str().to_string();
+ let value = capture.get(2).ok_or(anyhow!("invalid device_config output"))?.as_str();
+ flags.insert(key, value.to_string());
+ }
+ Ok(flags)
+}
+
+fn read_device_config_output(command: &str) -> Result<String> {
+ let output = Command::new("/system/bin/device_config").arg(command).output()?;
+ if !output.status.success() {
+ let reason = match output.status.code() {
+ Some(code) => format!("exit code {}", code),
+ None => "terminated by signal".to_string(),
+ };
+ bail!("failed to execute device_config: {}", reason);
+ }
+ Ok(str::from_utf8(&output.stdout)?.to_string())
+}
+
+fn read_device_config_flags() -> Result<HashMap<String, String>> {
+ let list_output = read_device_config_output("list")?;
+ parse_device_config(&list_output)
+}
+
+fn reconcile(pb_flags: &[Flag], dc_flags: HashMap<String, String>) -> Vec<Flag> {
+ pb_flags
+ .iter()
+ .map(|f| {
+ dc_flags
+ .get(&format!("{}/{}.{}", f.namespace, f.package, f.name))
+ .map(|value| {
+ if value.eq(&f.value) {
+ Flag { value_picked_from: ValuePickedFrom::Default, ..f.clone() }
+ } else {
+ Flag {
+ value_picked_from: ValuePickedFrom::Server,
+ value: value.to_string(),
+ ..f.clone()
+ }
+ }
+ })
+ .unwrap_or(f.clone())
+ })
+ .collect()
+}
+
+impl FlagSource for DeviceConfigSource {
+ fn list_flags() -> Result<Vec<Flag>> {
+ let pb_flags = read_pb_files()?;
+ let dc_flags = read_device_config_flags()?;
+
+ let flags = reconcile(&pb_flags, dc_flags);
+ Ok(flags)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_parse_device_config() {
+ let input = r#"
+namespace_one/com.foo.bar.flag_one=true
+namespace_one/com.foo.bar.flag_two=false
+random_noise;
+namespace_two/android.flag_one=true
+namespace_two/android.flag_two=nonsense
+"#;
+ let expected = HashMap::from([
+ ("namespace_one/com.foo.bar.flag_one".to_string(), "true".to_string()),
+ ("namespace_one/com.foo.bar.flag_two".to_string(), "false".to_string()),
+ ("namespace_two/android.flag_one".to_string(), "true".to_string()),
+ ]);
+ let actual = parse_device_config(input).unwrap();
+ assert_eq!(expected, actual);
+ }
+}
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
new file mode 100644
index 0000000..1e2a7a0
--- /dev/null
+++ b/tools/aconfig/aflags/src/main.rs
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+//! `aflags` is a device binary to read and write aconfig flags.
+
+use anyhow::Result;
+use clap::Parser;
+
+mod device_config_source;
+use device_config_source::DeviceConfigSource;
+
+#[derive(Clone)]
+enum FlagPermission {
+ ReadOnly,
+ ReadWrite,
+}
+
+impl ToString for FlagPermission {
+ fn to_string(&self) -> String {
+ match &self {
+ Self::ReadOnly => "read-only".into(),
+ Self::ReadWrite => "read-write".into(),
+ }
+ }
+}
+
+#[derive(Clone)]
+enum ValuePickedFrom {
+ Default,
+ Server,
+}
+
+impl ToString for ValuePickedFrom {
+ fn to_string(&self) -> String {
+ match &self {
+ Self::Default => "default".into(),
+ Self::Server => "server".into(),
+ }
+ }
+}
+
+#[derive(Clone)]
+struct Flag {
+ namespace: String,
+ name: String,
+ package: String,
+ container: String,
+ value: String,
+ permission: FlagPermission,
+ value_picked_from: ValuePickedFrom,
+}
+
+trait FlagSource {
+ fn list_flags() -> Result<Vec<Flag>>;
+}
+
+const ABOUT_TEXT: &str = "Tool for reading and writing flags.
+
+Rows in the table from the `list` command follow this format:
+
+ package flag_name value provenance permission container
+
+ * `package`: package set for this flag in its .aconfig definition.
+ * `flag_name`: flag name, also set in definition.
+ * `value`: the value read from the flag.
+ * `provenance`: one of:
+ + `default`: the flag value comes from its build-time default.
+ + `server`: the flag value comes from a server override.
+ * `permission`: read-write or read-only.
+ * `container`: the container for the flag, configured in its definition.
+";
+
+#[derive(Parser, Debug)]
+#[clap(long_about=ABOUT_TEXT)]
+struct Cli {
+ #[clap(subcommand)]
+ command: Command,
+}
+
+#[derive(Parser, Debug)]
+enum Command {
+ /// List all aconfig flags on this device.
+ List,
+}
+
+struct PaddingInfo {
+ longest_package_col: usize,
+ longest_name_col: usize,
+ longest_val_col: usize,
+ longest_value_picked_from_col: usize,
+ longest_permission_col: usize,
+}
+
+fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
+ let pkg = &flag.package;
+ let p0 = info.longest_package_col + 1;
+
+ let name = &flag.name;
+ let p1 = info.longest_name_col + 1;
+
+ let val = flag.value.to_string();
+ let p2 = info.longest_val_col + 1;
+
+ let value_picked_from = flag.value_picked_from.to_string();
+ let p3 = info.longest_value_picked_from_col + 1;
+
+ let perm = flag.permission.to_string();
+ let p4 = info.longest_permission_col + 1;
+
+ let container = &flag.container;
+
+ format!("{pkg:p0$}{name:p1$}{val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n")
+}
+
+fn list() -> Result<String> {
+ let flags = DeviceConfigSource::list_flags()?;
+ let padding_info = PaddingInfo {
+ longest_package_col: flags.iter().map(|f| f.package.len()).max().unwrap_or(0),
+ longest_name_col: flags.iter().map(|f| f.name.len()).max().unwrap_or(0),
+ longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
+ longest_value_picked_from_col: flags
+ .iter()
+ .map(|f| f.value_picked_from.to_string().len())
+ .max()
+ .unwrap_or(0),
+ longest_permission_col: flags
+ .iter()
+ .map(|f| f.permission.to_string().len())
+ .max()
+ .unwrap_or(0),
+ };
+
+ let mut result = String::from("");
+ for flag in flags {
+ let row = format_flag_row(&flag, &padding_info);
+ result.push_str(&row);
+ }
+ Ok(result)
+}
+
+fn main() {
+ let cli = Cli::parse();
+ let output = match cli.command {
+ Command::List => list(),
+ };
+ match output {
+ Ok(text) => println!("{text}"),
+ Err(msg) => println!("Error: {}", msg),
+ }
+}
diff --git a/tools/aconfig/fake_device_config/Android.bp b/tools/aconfig/fake_device_config/Android.bp
index 7420aa8..4566bf9 100644
--- a/tools/aconfig/fake_device_config/Android.bp
+++ b/tools/aconfig/fake_device_config/Android.bp
@@ -15,7 +15,8 @@
java_library {
name: "fake_device_config",
srcs: ["src/**/*.java"],
- sdk_version: "core_current",
+ sdk_version: "none",
+ system_modules: "core-all-system-modules",
host_supported: true,
}
diff --git a/tools/aconfig/printflags/Cargo.toml b/tools/aconfig/printflags/Cargo.toml
new file mode 100644
index 0000000..7313f5d
--- /dev/null
+++ b/tools/aconfig/printflags/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "printflags"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+paste = "1.0.11"
+protobuf = "3.2.0"
+regex = "1.10.3"
+aconfig_protos = { path = "../aconfig_protos" }
diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs
index 4110317..a0c9ee8 100644
--- a/tools/aconfig/printflags/src/main.rs
+++ b/tools/aconfig/printflags/src/main.rs
@@ -16,10 +16,11 @@
//! `printflags` is a device binary to print feature flags.
-use aconfig_protos::aconfig::Flag_state as State;
-use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
+use aconfig_protos::ProtoFlagState as State;
+use aconfig_protos::ProtoParsedFlags;
use anyhow::{bail, Context, Result};
use regex::Regex;
+use std::collections::BTreeMap;
use std::collections::HashMap;
use std::process::Command;
use std::{fs, str};
@@ -66,7 +67,7 @@
let device_config_flags = parse_device_config(dc_stdout);
// read aconfig_flags.pb files
- let mut flags: HashMap<String, Vec<String>> = HashMap::new();
+ let mut flags: BTreeMap<String, Vec<String>> = BTreeMap::new();
for partition in ["system", "system_ext", "product", "vendor"] {
let path = format!("/{}/etc/aconfig_flags.pb", partition);
let Ok(bytes) = fs::read(&path) else {
@@ -86,11 +87,10 @@
// print flags
for (key, mut value) in flags {
- let (_, package_and_name) = key.split_once('/').unwrap();
if let Some(dc_value) = device_config_flags.get(&key) {
value.push(dc_value.to_string());
}
- println!("{}: {}", package_and_name, value.join(", "));
+ println!("{}: {}", key, value.join(", "));
}
Ok(())
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
deleted file mode 100644
index ed4b24c..0000000
--- a/tools/aconfig/protos/aconfig.proto
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2023 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
-
-// This is the schema definition for aconfig files. Modifications need to be
-// either backwards compatible, or include updates to all aconfig files in the
-// Android tree.
-
-syntax = "proto2";
-
-package android.aconfig;
-
-// messages used in both aconfig input and output
-
-enum flag_state {
- ENABLED = 1;
- DISABLED = 2;
-}
-
-enum flag_permission {
- READ_ONLY = 1;
- READ_WRITE = 2;
-}
-
-// aconfig input messages: flag declarations and values
-
-message flag_declaration {
- optional string name = 1;
- optional string namespace = 2;
- optional string description = 3;
- repeated string bug = 4;
- optional bool is_fixed_read_only = 5;
- optional bool is_exported = 6;
- optional flag_metadata metadata = 7;
-};
-
-// Optional metadata about the flag, such as its purpose and its intended form factors.
-// Can influence the applied policies and testing strategy.
-message flag_metadata {
- enum flag_purpose {
- PURPOSE_UNSPECIFIED = 0;
- PURPOSE_FEATURE = 1;
- PURPOSE_BUGFIX = 2;
- }
-
- optional flag_purpose purpose = 1;
-
- // TODO(b/315025930): Add field to designate intended target device form factor(s), such as phone, watch or other.
-}
-
-message flag_declarations {
- optional string package = 1;
- repeated flag_declaration flag = 2;
- optional string container = 3;
-};
-
-message flag_value {
- optional string package = 1;
- optional string name = 2;
- optional flag_state state = 3;
- optional flag_permission permission = 4;
-};
-
-message flag_values {
- repeated flag_value flag_value = 1;
-};
-
-// aconfig output messages: parsed and verified flag declarations and values
-
-message tracepoint {
- // path to declaration or value file relative to $TOP
- optional string source = 1;
- optional flag_state state = 2;
- optional flag_permission permission = 3;
-}
-
-message parsed_flag {
- optional string package = 1;
- optional string name = 2;
- optional string namespace = 3;
- optional string description = 4;
- repeated string bug = 5;
- optional flag_state state = 6;
- optional flag_permission permission = 7;
- repeated tracepoint trace = 8;
- optional bool is_fixed_read_only = 9;
- optional bool is_exported = 10;
- optional string container = 11;
- optional flag_metadata metadata = 12;
-}
-
-message parsed_flags {
- repeated parsed_flag parsed_flag = 1;
-}
diff --git a/tools/aconfig/src/storage/flag_table.rs b/tools/aconfig/src/storage/flag_table.rs
deleted file mode 100644
index 46753f0..0000000
--- a/tools/aconfig/src/storage/flag_table.rs
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * 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.
- */
-
-use crate::commands::assign_flag_ids;
-use crate::storage::{self, FlagPackage};
-use anyhow::{anyhow, Result};
-
-#[derive(PartialEq, Debug)]
-pub struct FlagTableHeader {
- pub version: u32,
- pub container: String,
- pub file_size: u32,
- pub num_flags: u32,
- pub bucket_offset: u32,
- pub node_offset: u32,
-}
-
-impl FlagTableHeader {
- fn new(container: &str, num_flags: u32) -> Self {
- Self {
- version: storage::FILE_VERSION,
- container: String::from(container),
- file_size: 0,
- num_flags,
- bucket_offset: 0,
- node_offset: 0,
- }
- }
-
- fn as_bytes(&self) -> Vec<u8> {
- let mut result = Vec::new();
- result.extend_from_slice(&self.version.to_le_bytes());
- let container_bytes = self.container.as_bytes();
- result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
- result.extend_from_slice(container_bytes);
- result.extend_from_slice(&self.file_size.to_le_bytes());
- result.extend_from_slice(&self.num_flags.to_le_bytes());
- result.extend_from_slice(&self.bucket_offset.to_le_bytes());
- result.extend_from_slice(&self.node_offset.to_le_bytes());
- result
- }
-}
-
-#[derive(PartialEq, Debug, Clone)]
-pub struct FlagTableNode {
- pub package_id: u32,
- pub flag_name: String,
- pub flag_id: u32,
- pub next_offset: Option<u32>,
- pub bucket_index: u32,
-}
-
-impl FlagTableNode {
- fn new(package_id: u32, flag_name: &str, flag_id: u32, num_buckets: u32) -> Self {
- let full_flag_name = package_id.to_string() + "/" + flag_name;
- let bucket_index = storage::get_bucket_index(&full_flag_name, num_buckets);
- Self {
- package_id,
- flag_name: flag_name.to_string(),
- flag_id,
- next_offset: None,
- bucket_index,
- }
- }
-
- fn as_bytes(&self) -> Vec<u8> {
- let mut result = Vec::new();
- result.extend_from_slice(&self.package_id.to_le_bytes());
- let name_bytes = self.flag_name.as_bytes();
- result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
- result.extend_from_slice(name_bytes);
- result.extend_from_slice(&self.flag_id.to_le_bytes());
- result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
- result
- }
-}
-
-#[derive(PartialEq, Debug)]
-pub struct FlagTable {
- pub header: FlagTableHeader,
- pub buckets: Vec<Option<u32>>,
- pub nodes: Vec<FlagTableNode>,
-}
-
-impl FlagTable {
- fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<FlagTableNode>> {
- let flag_names = package.boolean_flags.iter().map(|pf| pf.name()).collect::<Vec<_>>();
- println!("{:?}", flag_names);
- let flag_ids =
- assign_flag_ids(package.package_name, package.boolean_flags.iter().copied())?;
- package
- .boolean_flags
- .iter()
- .map(|&pf| {
- let fid = flag_ids
- .get(pf.name())
- .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
- Ok(FlagTableNode::new(package.package_id, pf.name(), *fid, num_buckets))
- })
- .collect::<Result<Vec<_>>>()
- }
-
- pub fn new(container: &str, packages: &[FlagPackage]) -> Result<Self> {
- // create table
- let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
- let num_buckets = storage::get_table_size(num_flags)?;
-
- let mut table = Self {
- header: FlagTableHeader::new(container, num_flags),
- buckets: vec![None; num_buckets as usize],
- nodes: packages
- .iter()
- .map(|pkg| FlagTable::create_nodes(pkg, num_buckets))
- .collect::<Result<Vec<_>>>()?
- .concat(),
- };
-
- // initialize all header fields
- table.header.bucket_offset = table.header.as_bytes().len() as u32;
- table.header.node_offset = table.header.bucket_offset + num_buckets * 4;
- table.header.file_size = table.header.node_offset
- + table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32;
-
- // sort nodes by bucket index for efficiency
- table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
-
- // fill all node offset
- let mut offset = table.header.node_offset;
- for i in 0..table.nodes.len() {
- let node_bucket_idx = table.nodes[i].bucket_index;
- let next_node_bucket_idx = if i + 1 < table.nodes.len() {
- Some(table.nodes[i + 1].bucket_index)
- } else {
- None
- };
-
- if table.buckets[node_bucket_idx as usize].is_none() {
- table.buckets[node_bucket_idx as usize] = Some(offset);
- }
- offset += table.nodes[i].as_bytes().len() as u32;
-
- if let Some(index) = next_node_bucket_idx {
- if index == node_bucket_idx {
- table.nodes[i].next_offset = Some(offset);
- }
- }
- }
-
- Ok(table)
- }
-
- pub fn as_bytes(&self) -> Vec<u8> {
- [
- self.header.as_bytes(),
- self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
- self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
- ]
- .concat()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::storage::{
- group_flags_by_package, tests::parse_all_test_flags, tests::read_str_from_bytes,
- tests::read_u32_from_bytes,
- };
-
- impl FlagTableHeader {
- // test only method to deserialize back into the header struct
- fn from_bytes(bytes: &[u8]) -> Result<Self> {
- let mut head = 0;
- Ok(Self {
- version: read_u32_from_bytes(bytes, &mut head)?,
- container: read_str_from_bytes(bytes, &mut head)?,
- file_size: read_u32_from_bytes(bytes, &mut head)?,
- num_flags: read_u32_from_bytes(bytes, &mut head)?,
- bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
- node_offset: read_u32_from_bytes(bytes, &mut head)?,
- })
- }
- }
-
- impl FlagTableNode {
- // test only method to deserialize back into the node struct
- fn from_bytes(bytes: &[u8], num_buckets: u32) -> Result<Self> {
- let mut head = 0;
- let mut node = Self {
- package_id: read_u32_from_bytes(bytes, &mut head)?,
- flag_name: read_str_from_bytes(bytes, &mut head)?,
- flag_id: read_u32_from_bytes(bytes, &mut head)?,
- next_offset: match read_u32_from_bytes(bytes, &mut head)? {
- 0 => None,
- val => Some(val),
- },
- bucket_index: 0,
- };
- let full_flag_name = node.package_id.to_string() + "/" + &node.flag_name;
- node.bucket_index = storage::get_bucket_index(&full_flag_name, num_buckets);
- Ok(node)
- }
-
- // create test baseline, syntactic sugar
- fn new_expected(
- package_id: u32,
- flag_name: &str,
- flag_id: u32,
- next_offset: Option<u32>,
- bucket_index: u32,
- ) -> Self {
- Self {
- package_id,
- flag_name: flag_name.to_string(),
- flag_id,
- next_offset,
- bucket_index,
- }
- }
- }
-
- impl FlagTable {
- // test only method to deserialize back into the table struct
- fn from_bytes(bytes: &[u8]) -> Result<Self> {
- let header = FlagTableHeader::from_bytes(bytes)?;
- let num_flags = header.num_flags;
- let num_buckets = storage::get_table_size(num_flags)?;
- let mut head = header.as_bytes().len();
- let buckets = (0..num_buckets)
- .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
- 0 => None,
- val => Some(val),
- })
- .collect();
- let nodes = (0..num_flags)
- .map(|_| {
- let node = FlagTableNode::from_bytes(&bytes[head..], num_buckets).unwrap();
- head += node.as_bytes().len();
- node
- })
- .collect();
-
- let table = Self { header, buckets, nodes };
- Ok(table)
- }
- }
-
- pub fn create_test_flag_table() -> Result<FlagTable> {
- let caches = parse_all_test_flags();
- let packages = group_flags_by_package(caches.iter());
- FlagTable::new("system", &packages)
- }
-
- #[test]
- // this test point locks down the table creation and each field
- fn test_table_contents() {
- let flag_table = create_test_flag_table();
- assert!(flag_table.is_ok());
-
- let header: &FlagTableHeader = &flag_table.as_ref().unwrap().header;
- let expected_header = FlagTableHeader {
- version: storage::FILE_VERSION,
- container: String::from("system"),
- file_size: 320,
- num_flags: 8,
- bucket_offset: 30,
- node_offset: 98,
- };
- assert_eq!(header, &expected_header);
-
- println!("{:?}", &flag_table.as_ref().unwrap().nodes);
-
- let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
- let expected_bucket: Vec<Option<u32>> = vec![
- Some(98),
- Some(124),
- None,
- None,
- None,
- Some(177),
- None,
- Some(203),
- None,
- Some(261),
- None,
- None,
- None,
- None,
- None,
- Some(293),
- None,
- ];
- assert_eq!(buckets, &expected_bucket);
-
- let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes;
- assert_eq!(nodes.len(), 8);
-
- assert_eq!(nodes[0], FlagTableNode::new_expected(0, "enabled_ro", 1, None, 0));
- assert_eq!(nodes[1], FlagTableNode::new_expected(0, "enabled_rw", 2, Some(150), 1));
- assert_eq!(nodes[2], FlagTableNode::new_expected(1, "disabled_ro", 0, None, 1));
- assert_eq!(nodes[3], FlagTableNode::new_expected(2, "enabled_ro", 1, None, 5));
- assert_eq!(nodes[4], FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, Some(235), 7));
- assert_eq!(nodes[5], FlagTableNode::new_expected(1, "enabled_ro", 2, None, 7));
- assert_eq!(nodes[6], FlagTableNode::new_expected(2, "enabled_fixed_ro", 0, None, 9));
- assert_eq!(nodes[7], FlagTableNode::new_expected(0, "disabled_rw", 0, None, 15));
- }
-
- #[test]
- // this test point locks down the table serialization
- fn test_serialization() {
- let flag_table = create_test_flag_table();
- assert!(flag_table.is_ok());
- let flag_table = flag_table.unwrap();
-
- let header: &FlagTableHeader = &flag_table.header;
- let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
- assert!(reinterpreted_header.is_ok());
- assert_eq!(header, &reinterpreted_header.unwrap());
-
- let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
- let num_buckets = storage::get_table_size(header.num_flags).unwrap();
- for node in nodes.iter() {
- let reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes(), num_buckets);
- assert!(reinterpreted_node.is_ok());
- assert_eq!(node, &reinterpreted_node.unwrap());
- }
-
- let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
- assert!(reinterpreted_table.is_ok());
- assert_eq!(&flag_table, &reinterpreted_table.unwrap());
- }
-}
diff --git a/tools/aconfig/src/storage/package_table.rs b/tools/aconfig/src/storage/package_table.rs
deleted file mode 100644
index 1a3bbc3..0000000
--- a/tools/aconfig/src/storage/package_table.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2023 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.
- */
-
-use crate::storage::{self, FlagPackage};
-use anyhow::Result;
-
-#[derive(PartialEq, Debug)]
-pub struct PackageTableHeader {
- pub version: u32,
- pub container: String,
- pub file_size: u32,
- pub num_packages: u32,
- pub bucket_offset: u32,
- pub node_offset: u32,
-}
-
-impl PackageTableHeader {
- fn new(container: &str, num_packages: u32) -> Self {
- Self {
- version: storage::FILE_VERSION,
- container: String::from(container),
- file_size: 0,
- num_packages,
- bucket_offset: 0,
- node_offset: 0,
- }
- }
-
- fn as_bytes(&self) -> Vec<u8> {
- let mut result = Vec::new();
- result.extend_from_slice(&self.version.to_le_bytes());
- let container_bytes = self.container.as_bytes();
- result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
- result.extend_from_slice(container_bytes);
- result.extend_from_slice(&self.file_size.to_le_bytes());
- result.extend_from_slice(&self.num_packages.to_le_bytes());
- result.extend_from_slice(&self.bucket_offset.to_le_bytes());
- result.extend_from_slice(&self.node_offset.to_le_bytes());
- result
- }
-}
-
-#[derive(PartialEq, Debug)]
-pub struct PackageTableNode {
- pub package_name: String,
- pub package_id: u32,
- pub boolean_offset: u32,
- pub next_offset: Option<u32>,
- pub bucket_index: u32,
-}
-
-impl PackageTableNode {
- fn new(package: &FlagPackage, num_buckets: u32) -> Self {
- let bucket_index =
- storage::get_bucket_index(&package.package_name.to_string(), num_buckets);
- Self {
- package_name: String::from(package.package_name),
- package_id: package.package_id,
- boolean_offset: package.boolean_offset,
- next_offset: None,
- bucket_index,
- }
- }
-
- fn as_bytes(&self) -> Vec<u8> {
- let mut result = Vec::new();
- let name_bytes = self.package_name.as_bytes();
- 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.boolean_offset.to_le_bytes());
- result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
- result
- }
-}
-
-#[derive(PartialEq, Debug)]
-pub struct PackageTable {
- pub header: PackageTableHeader,
- pub buckets: Vec<Option<u32>>,
- pub nodes: Vec<PackageTableNode>,
-}
-
-impl PackageTable {
- pub fn new(container: &str, packages: &[FlagPackage]) -> Result<Self> {
- // create table
- let num_packages = packages.len() as u32;
- let num_buckets = storage::get_table_size(num_packages)?;
- let mut table = Self {
- header: PackageTableHeader::new(container, num_packages),
- buckets: vec![None; num_buckets as usize],
- nodes: packages.iter().map(|pkg| PackageTableNode::new(pkg, num_buckets)).collect(),
- };
-
- // initialize all header fields
- table.header.bucket_offset = table.header.as_bytes().len() as u32;
- table.header.node_offset = table.header.bucket_offset + num_buckets * 4;
- table.header.file_size = table.header.node_offset
- + table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32;
-
- // sort nodes by bucket index for efficiency
- table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
-
- // fill all node offset
- let mut offset = table.header.node_offset;
- for i in 0..table.nodes.len() {
- let node_bucket_idx = table.nodes[i].bucket_index;
- let next_node_bucket_idx = if i + 1 < table.nodes.len() {
- Some(table.nodes[i + 1].bucket_index)
- } else {
- None
- };
-
- if table.buckets[node_bucket_idx as usize].is_none() {
- table.buckets[node_bucket_idx as usize] = Some(offset);
- }
- offset += table.nodes[i].as_bytes().len() as u32;
-
- if let Some(index) = next_node_bucket_idx {
- if index == node_bucket_idx {
- table.nodes[i].next_offset = Some(offset);
- }
- }
- }
-
- Ok(table)
- }
-
- pub fn as_bytes(&self) -> Vec<u8> {
- [
- self.header.as_bytes(),
- self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
- self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
- ]
- .concat()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::storage::{
- group_flags_by_package, tests::parse_all_test_flags, tests::read_str_from_bytes,
- tests::read_u32_from_bytes,
- };
-
- impl PackageTableHeader {
- // test only method to deserialize back into the header struct
- fn from_bytes(bytes: &[u8]) -> Result<Self> {
- let mut head = 0;
- Ok(Self {
- version: read_u32_from_bytes(bytes, &mut head)?,
- container: read_str_from_bytes(bytes, &mut head)?,
- file_size: read_u32_from_bytes(bytes, &mut head)?,
- num_packages: read_u32_from_bytes(bytes, &mut head)?,
- bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
- node_offset: read_u32_from_bytes(bytes, &mut head)?,
- })
- }
- }
-
- impl PackageTableNode {
- // test only method to deserialize back into the node struct
- fn from_bytes(bytes: &[u8], num_buckets: u32) -> Result<Self> {
- let mut head = 0;
- let mut node = Self {
- package_name: read_str_from_bytes(bytes, &mut head)?,
- package_id: read_u32_from_bytes(bytes, &mut head)?,
- boolean_offset: read_u32_from_bytes(bytes, &mut head)?,
- next_offset: match read_u32_from_bytes(bytes, &mut head)? {
- 0 => None,
- val => Some(val),
- },
- bucket_index: 0,
- };
- node.bucket_index = storage::get_bucket_index(&node.package_name, num_buckets);
- Ok(node)
- }
- }
-
- impl PackageTable {
- // test only method to deserialize back into the table struct
- fn from_bytes(bytes: &[u8]) -> Result<Self> {
- let header = PackageTableHeader::from_bytes(bytes)?;
- let num_packages = header.num_packages;
- let num_buckets = storage::get_table_size(num_packages)?;
- let mut head = header.as_bytes().len();
- let buckets = (0..num_buckets)
- .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
- 0 => None,
- val => Some(val),
- })
- .collect();
- let nodes = (0..num_packages)
- .map(|_| {
- let node = PackageTableNode::from_bytes(&bytes[head..], num_buckets).unwrap();
- head += node.as_bytes().len();
- node
- })
- .collect();
-
- let table = Self { header, buckets, nodes };
- Ok(table)
- }
- }
-
- pub fn create_test_package_table() -> Result<PackageTable> {
- let caches = parse_all_test_flags();
- let packages = group_flags_by_package(caches.iter());
- PackageTable::new("system", &packages)
- }
-
- #[test]
- // this test point locks down the table creation and each field
- fn test_table_contents() {
- let package_table = create_test_package_table();
- assert!(package_table.is_ok());
-
- let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
- let expected_header = PackageTableHeader {
- version: storage::FILE_VERSION,
- container: String::from("system"),
- file_size: 208,
- num_packages: 3,
- bucket_offset: 30,
- node_offset: 58,
- };
- assert_eq!(header, &expected_header);
-
- let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
- let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
- assert_eq!(buckets, &expected);
-
- let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
- assert_eq!(nodes.len(), 3);
- let first_node_expected = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_2"),
- package_id: 1,
- boolean_offset: 6,
- next_offset: None,
- bucket_index: 0,
- };
- assert_eq!(nodes[0], first_node_expected);
- let second_node_expected = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_1"),
- package_id: 0,
- boolean_offset: 0,
- next_offset: Some(158),
- bucket_index: 3,
- };
- assert_eq!(nodes[1], second_node_expected);
- let third_node_expected = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_4"),
- package_id: 2,
- boolean_offset: 12,
- next_offset: None,
- bucket_index: 3,
- };
- assert_eq!(nodes[2], third_node_expected);
- }
-
- #[test]
- // this test point locks down the table serialization
- fn test_serialization() {
- let package_table = create_test_package_table();
- assert!(package_table.is_ok());
- let package_table = package_table.unwrap();
-
- let header: &PackageTableHeader = &package_table.header;
- let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
- assert!(reinterpreted_header.is_ok());
- assert_eq!(header, &reinterpreted_header.unwrap());
-
- let nodes: &Vec<PackageTableNode> = &package_table.nodes;
- let num_buckets = storage::get_table_size(header.num_packages).unwrap();
- for node in nodes.iter() {
- let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes(), num_buckets);
- assert!(reinterpreted_node.is_ok());
- assert_eq!(node, &reinterpreted_node.unwrap());
- }
-
- let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
- assert!(reinterpreted_table.is_ok());
- assert_eq!(&package_table, &reinterpreted_table.unwrap());
- }
-}
diff --git a/tools/characteristics_rro_generator.py b/tools/characteristics_rro_generator.py
index 6489673..cf873ee 100644
--- a/tools/characteristics_rro_generator.py
+++ b/tools/characteristics_rro_generator.py
@@ -1,22 +1,14 @@
#!/usr/bin/env python3
import sys
-from xml.dom.minidom import parseString
-
-def parse_package(manifest):
- with open(manifest, 'r') as f:
- data = f.read()
- dom = parseString(data)
- return dom.documentElement.getAttribute('package')
if __name__ == '__main__':
if len(sys.argv) != 3:
- sys.exit(f"usage: {sys_argv[0]} target_package_manifest output\n")
- package_name = parse_package(sys.argv[1])
+ sys.exit(f"usage: {sys_argv[0]} target_package_name output\n")
with open(sys.argv[2], "w") as f:
f.write(f'''<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="{package_name}.auto_generated_characteristics_rro">
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="{sys.argv[1]}.auto_generated_characteristics_rro">
<application android:hasCode="false" />
- <overlay android:targetPackage="{package_name}"
+ <overlay android:targetPackage="{sys.argv[1]}"
android:isStatic="true"
android:priority="0" />
</manifest>
diff --git a/tools/find_static_candidates.py b/tools/find_static_candidates.py
index 7511b36..2e50627 100644
--- a/tools/find_static_candidates.py
+++ b/tools/find_static_candidates.py
@@ -119,28 +119,23 @@
# modify the module's shared_libs and static_libs with all of the transient
# dependencies required from all of the explicit dependencies
def flattenDeps(self, module, module_info):
- libs_snapshot = dict(shared_libs = set(module["shared_libs"]), static_libs = set(module["static_libs"]))
+ libs_snapshot = dict(shared_libs = set(module.get("shared_libs",{})), static_libs = set(module.get("static_libs",{})))
for lib_class in ["shared_libs", "static_libs"]:
for lib in libs_snapshot[lib_class]:
- if not lib or lib not in module_info:
+ if not lib or lib not in module_info or lib_class not in module:
continue
if lib in self.visited:
module[lib_class].update(self.visited[lib][lib_class])
else:
res = self.flattenDeps(module_info[lib], module_info)
- module[lib_class].update(res[lib_class])
- self.visited[lib][lib_class].update(res[lib_class])
+ module[lib_class].update(res.get(lib_class, {}))
+ self.visited[lib][lib_class].update(res.get(lib_class, {}))
return module
def main():
module_info = json.load(open(ANDROID_PRODUCT_OUT + "/module-info.json"))
- # turn all of the static_libs and shared_libs lists into sets to make them
- # easier to update
- for _, module in module_info.items():
- module["shared_libs"] = set(module["shared_libs"])
- module["static_libs"] = set(module["static_libs"])
args = parse_args()
@@ -149,6 +144,12 @@
print("Module {} does not exist".format(args.module))
exit(1)
+ # turn all of the static_libs and shared_libs lists into sets to make them
+ # easier to update
+ for _, module in module_info.items():
+ module["shared_libs"] = set(module.get("shared_libs", {}))
+ module["static_libs"] = set(module.get("static_libs", {}))
+
includedStatically = defaultdict(set)
includedSharedly = defaultdict(set)
includedBothly = defaultdict(set)
@@ -160,24 +161,28 @@
continue
module = transitive.flattenDeps(module, module_info)
# filter out fuzzers by their dependency on clang
- if "libclang_rt.fuzzer" in module["static_libs"]:
- continue
+ if "static_libs" in module:
+ if "libclang_rt.fuzzer" in module["static_libs"]:
+ continue
else:
if "NATIVE_TESTS" in module["class"]:
# We don't care about how tests are including libraries
continue
# count all of the shared and static libs included in this module
- for lib in module["shared_libs"]:
- includedSharedly[lib].add(name)
- for lib in module["static_libs"]:
- includedStatically[lib].add(name)
+ if "shared_libs" in module:
+ for lib in module["shared_libs"]:
+ includedSharedly[lib].add(name)
+ if "static_libs" in module:
+ for lib in module["static_libs"]:
+ includedStatically[lib].add(name)
- intersection = set(module["shared_libs"]).intersection(
- module["static_libs"]
- )
- if intersection:
- includedBothly[name] = intersection
+ if "shared_libs" in module and "static_libs" in module:
+ intersection = set(module["shared_libs"]).intersection(
+ module["static_libs"]
+ )
+ if intersection:
+ includedBothly[name] = intersection
if args.print_shared:
print(
diff --git a/tools/ide_query/go.mod b/tools/ide_query/go.mod
new file mode 100644
index 0000000..f9d727f
--- /dev/null
+++ b/tools/ide_query/go.mod
@@ -0,0 +1,7 @@
+module ide_query
+
+go 1.21
+
+require (
+ google.golang.org/protobuf v0.0.0
+)
diff --git a/tools/ide_query/go.work b/tools/ide_query/go.work
new file mode 100644
index 0000000..851f352
--- /dev/null
+++ b/tools/ide_query/go.work
@@ -0,0 +1,9 @@
+go 1.21
+
+use (
+ .
+)
+
+replace (
+ google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
+)
\ No newline at end of file
diff --git a/tools/ide_query/go.work.sum b/tools/ide_query/go.work.sum
new file mode 100644
index 0000000..cf42b48
--- /dev/null
+++ b/tools/ide_query/go.work.sum
@@ -0,0 +1,5 @@
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go
new file mode 100644
index 0000000..c1c4da0
--- /dev/null
+++ b/tools/ide_query/ide_query.go
@@ -0,0 +1,265 @@
+// Binary ide_query generates and analyzes build artifacts.
+// The produced result can be consumed by IDEs to provide language features.
+package main
+
+import (
+ "container/list"
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "slices"
+ "strings"
+
+ "google.golang.org/protobuf/proto"
+ pb "ide_query/ide_query_proto"
+)
+
+// Env contains information about the current environment.
+type Env struct {
+ LunchTarget LunchTarget
+ RepoDir string
+ OutDir string
+}
+
+// LunchTarget is a parsed Android lunch target.
+// Input format: <product_name>-<release_type>-<build_variant>
+type LunchTarget struct {
+ Product string
+ Release string
+ Variant string
+}
+
+var _ flag.Value = (*LunchTarget)(nil)
+
+// // Get implements flag.Value.
+// func (l *LunchTarget) Get() any {
+// return l
+// }
+
+// Set implements flag.Value.
+func (l *LunchTarget) Set(s string) error {
+ parts := strings.Split(s, "-")
+ if len(parts) != 3 {
+ return fmt.Errorf("invalid lunch target: %q, must have form <product_name>-<release_type>-<build_variant>", s)
+ }
+ *l = LunchTarget{
+ Product: parts[0],
+ Release: parts[1],
+ Variant: parts[2],
+ }
+ return nil
+}
+
+// String implements flag.Value.
+func (l *LunchTarget) String() string {
+ return fmt.Sprintf("%s-%s-%s", l.Product, l.Release, l.Variant)
+}
+
+func main() {
+ var env Env
+ env.OutDir = os.Getenv("OUT_DIR")
+ env.RepoDir = os.Getenv("ANDROID_BUILD_TOP")
+ flag.Var(&env.LunchTarget, "lunch_target", "The lunch target to query")
+ flag.Parse()
+ files := flag.Args()
+ if len(files) == 0 {
+ fmt.Println("No files provided.")
+ os.Exit(1)
+ return
+ }
+
+ var javaFiles []string
+ for _, f := range files {
+ switch {
+ case strings.HasSuffix(f, ".java") || strings.HasSuffix(f, ".kt"):
+ javaFiles = append(javaFiles, f)
+ default:
+ log.Printf("File %q is supported - will be skipped.", f)
+ }
+ }
+
+ ctx := context.Background()
+ javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json")
+ // TODO(michaelmerg): Figure out if module_bp_java_deps.json is outdated.
+ runMake(ctx, env, "nothing")
+
+ javaModules, err := loadJavaModules(javaDepsPath)
+ if err != nil {
+ log.Fatalf("Failed to load java modules: %v", err)
+ }
+
+ fileToModule := make(map[string]*javaModule) // file path -> module
+ for _, f := range javaFiles {
+ for _, m := range javaModules {
+ if !slices.Contains(m.Srcs, f) {
+ continue
+ }
+ if fileToModule[f] != nil {
+ // TODO(michaelmerg): Handle the case where a file is covered by multiple modules.
+ log.Printf("File %q found in module %q but is already covered by module %q", f, m.Name, fileToModule[f].Name)
+ continue
+ }
+ fileToModule[f] = m
+ }
+ }
+
+ var toMake []string
+ for _, m := range fileToModule {
+ toMake = append(toMake, m.Name)
+ }
+ fmt.Printf("Running make for modules: %v\n", strings.Join(toMake, ", "))
+ if err := runMake(ctx, env, toMake...); err != nil {
+ log.Fatalf("Failed to run make: %v", err)
+ }
+
+ var sources []*pb.SourceFile
+ type depsAndGenerated struct {
+ Deps []string
+ Generated []*pb.GeneratedFile
+ }
+ moduleToDeps := make(map[string]*depsAndGenerated)
+ for _, f := range files {
+ file := &pb.SourceFile{
+ Path: f,
+ WorkingDir: env.RepoDir,
+ }
+ sources = append(sources, file)
+
+ m := fileToModule[f]
+ if m == nil {
+ file.Status = &pb.Status{
+ Code: pb.Status_FAILURE,
+ Message: proto.String("File not found in any module."),
+ }
+ continue
+ }
+
+ file.Status = &pb.Status{Code: pb.Status_OK}
+ if moduleToDeps[m.Name] != nil {
+ file.Generated = moduleToDeps[m.Name].Generated
+ file.Deps = moduleToDeps[m.Name].Deps
+ continue
+ }
+
+ deps := transitiveDeps(m, javaModules)
+ var generated []*pb.GeneratedFile
+ outPrefix := env.OutDir + "/"
+ for _, d := range deps {
+ if relPath, ok := strings.CutPrefix(d, outPrefix); ok {
+ contents, err := os.ReadFile(d)
+ if err != nil {
+ fmt.Printf("Generated file %q not found - will be skipped.\n", d)
+ continue
+ }
+
+ generated = append(generated, &pb.GeneratedFile{
+ Path: relPath,
+ Contents: contents,
+ })
+ }
+ }
+ moduleToDeps[m.Name] = &depsAndGenerated{deps, generated}
+ file.Generated = generated
+ file.Deps = deps
+ }
+
+ res := &pb.IdeAnalysis{
+ BuildArtifactRoot: env.OutDir,
+ Sources: sources,
+ Status: &pb.Status{Code: pb.Status_OK},
+ }
+ data, err := proto.Marshal(res)
+ if err != nil {
+ log.Fatalf("Failed to marshal result proto: %v", err)
+ }
+
+ err = os.WriteFile(path.Join(env.OutDir, "ide_query.pb"), data, 0644)
+ if err != nil {
+ log.Fatalf("Failed to write result proto: %v", err)
+ }
+
+ for _, s := range sources {
+ fmt.Printf("%s: %v (Deps: %d, Generated: %d)\n", s.GetPath(), s.GetStatus(), len(s.GetDeps()), len(s.GetGenerated()))
+ }
+}
+
+// runMake runs Soong build for the given modules.
+func runMake(ctx context.Context, env Env, modules ...string) error {
+ args := []string{
+ "--make-mode",
+ "ANDROID_BUILD_ENVIRONMENT_CONFIG=googler-cog",
+ "TARGET_PRODUCT=" + env.LunchTarget.Product,
+ "TARGET_RELEASE=" + env.LunchTarget.Release,
+ "TARGET_BUILD_VARIANT=" + env.LunchTarget.Variant,
+ }
+ args = append(args, modules...)
+ cmd := exec.CommandContext(ctx, "build/soong/soong_ui.bash", args...)
+ cmd.Dir = env.RepoDir
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
+
+type javaModule struct {
+ Name string
+ Path []string `json:"path,omitempty"`
+ Deps []string `json:"dependencies,omitempty"`
+ Srcs []string `json:"srcs,omitempty"`
+ Jars []string `json:"jars,omitempty"`
+ SrcJars []string `json:"srcjars,omitempty"`
+}
+
+func loadJavaModules(path string) (map[string]*javaModule, error) {
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ var ret map[string]*javaModule // module name -> module
+ if err = json.Unmarshal(data, &ret); err != nil {
+ return nil, err
+ }
+
+ for name, module := range ret {
+ if strings.HasSuffix(name, "-jarjar") || strings.HasSuffix(name, ".impl") {
+ delete(ret, name)
+ continue
+ }
+
+ module.Name = name
+ }
+ return ret, nil
+}
+
+func transitiveDeps(m *javaModule, modules map[string]*javaModule) []string {
+ var ret []string
+ q := list.New()
+ q.PushBack(m.Name)
+ seen := make(map[string]bool) // module names -> true
+ for q.Len() > 0 {
+ name := q.Remove(q.Front()).(string)
+ mod := modules[name]
+ if mod == nil {
+ continue
+ }
+
+ ret = append(ret, mod.Srcs...)
+ ret = append(ret, mod.SrcJars...)
+ ret = append(ret, mod.Jars...)
+ for _, d := range mod.Deps {
+ if seen[d] {
+ continue
+ }
+ seen[d] = true
+ q.PushBack(d)
+ }
+ }
+ slices.Sort(ret)
+ ret = slices.Compact(ret)
+ return ret
+}
diff --git a/tools/ide_query/ide_query.sh b/tools/ide_query/ide_query.sh
new file mode 100755
index 0000000..663c4dc
--- /dev/null
+++ b/tools/ide_query/ide_query.sh
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+
+cd $(dirname $BASH_SOURCE)
+source $(pwd)/../../shell_utils.sh
+require_top
+
+# Ensure cogsetup (out/ will be symlink outside the repo)
+. ${TOP}/build/make/cogsetup.sh
+
+export ANDROID_BUILD_TOP=$TOP
+export OUT_DIR=${OUT_DIR}
+exec "${TOP}/prebuilts/go/linux-x86/bin/go" "run" "ide_query" "$@"
diff --git a/tools/ide_query/ide_query_proto/ide_query.pb.go b/tools/ide_query/ide_query_proto/ide_query.pb.go
new file mode 100644
index 0000000..30571cc
--- /dev/null
+++ b/tools/ide_query/ide_query_proto/ide_query.pb.go
@@ -0,0 +1,522 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.30.0
+// protoc v3.21.12
+// source: ide_query.proto
+
+package ide_query_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Status_Code int32
+
+const (
+ Status_OK Status_Code = 0
+ Status_FAILURE Status_Code = 1
+)
+
+// Enum value maps for Status_Code.
+var (
+ Status_Code_name = map[int32]string{
+ 0: "OK",
+ 1: "FAILURE",
+ }
+ Status_Code_value = map[string]int32{
+ "OK": 0,
+ "FAILURE": 1,
+ }
+)
+
+func (x Status_Code) Enum() *Status_Code {
+ p := new(Status_Code)
+ *p = x
+ return p
+}
+
+func (x Status_Code) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Status_Code) Descriptor() protoreflect.EnumDescriptor {
+ return file_ide_query_proto_enumTypes[0].Descriptor()
+}
+
+func (Status_Code) Type() protoreflect.EnumType {
+ return &file_ide_query_proto_enumTypes[0]
+}
+
+func (x Status_Code) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Status_Code.Descriptor instead.
+func (Status_Code) EnumDescriptor() ([]byte, []int) {
+ return file_ide_query_proto_rawDescGZIP(), []int{0, 0}
+}
+
+// Indicates the success/failure for analysis.
+type Status struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=cider.build.companion.Status_Code" json:"code,omitempty"`
+ // Details about the status, might be displayed to user.
+ Message *string `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"`
+}
+
+func (x *Status) Reset() {
+ *x = Status{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ide_query_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Status) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+ mi := &file_ide_query_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
+func (*Status) Descriptor() ([]byte, []int) {
+ return file_ide_query_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Status) GetCode() Status_Code {
+ if x != nil {
+ return x.Code
+ }
+ return Status_OK
+}
+
+func (x *Status) GetMessage() string {
+ if x != nil && x.Message != nil {
+ return *x.Message
+ }
+ return ""
+}
+
+type GeneratedFile struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Path to the file relative to IdeAnalysis.build_artifact_root.
+ Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+ // The text of the generated file, if not provided contents will be read
+ //
+ // from the path above in user's workstation.
+ Contents []byte `protobuf:"bytes,2,opt,name=contents,proto3,oneof" json:"contents,omitempty"`
+}
+
+func (x *GeneratedFile) Reset() {
+ *x = GeneratedFile{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ide_query_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GeneratedFile) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GeneratedFile) ProtoMessage() {}
+
+func (x *GeneratedFile) ProtoReflect() protoreflect.Message {
+ mi := &file_ide_query_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.
+func (*GeneratedFile) Descriptor() ([]byte, []int) {
+ return file_ide_query_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *GeneratedFile) GetPath() string {
+ if x != nil {
+ return x.Path
+ }
+ return ""
+}
+
+func (x *GeneratedFile) GetContents() []byte {
+ if x != nil {
+ return x.Contents
+ }
+ return nil
+}
+
+type SourceFile struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Repo root relative path to the source file in the tree.
+ Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+ // Working directory used by the build system. All the relative
+ // paths in compiler_arguments should be relative to this path.
+ // Relative to workspace root.
+ WorkingDir string `protobuf:"bytes,2,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
+ // Compiler arguments to compile the source file. If multiple variants
+ // of the module being compiled are possible, the query script will choose
+ // one.
+ CompilerArguments []string `protobuf:"bytes,3,rep,name=compiler_arguments,json=compilerArguments,proto3" json:"compiler_arguments,omitempty"`
+ // Any generated files that are used in compiling the file.
+ Generated []*GeneratedFile `protobuf:"bytes,4,rep,name=generated,proto3" json:"generated,omitempty"`
+ // Paths to all of the sources, like build files, code generators,
+ // proto files etc. that were used during analysis. Used to figure
+ // out when a set of build artifacts are stale and the query tool
+ // must be re-run.
+ // Relative to workspace root.
+ Deps []string `protobuf:"bytes,5,rep,name=deps,proto3" json:"deps,omitempty"`
+ // Represensts analysis status for this particular file. e.g. not part
+ // of the build graph.
+ Status *Status `protobuf:"bytes,6,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *SourceFile) Reset() {
+ *x = SourceFile{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ide_query_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SourceFile) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceFile) ProtoMessage() {}
+
+func (x *SourceFile) ProtoReflect() protoreflect.Message {
+ mi := &file_ide_query_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.
+func (*SourceFile) Descriptor() ([]byte, []int) {
+ return file_ide_query_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *SourceFile) GetPath() string {
+ if x != nil {
+ return x.Path
+ }
+ return ""
+}
+
+func (x *SourceFile) GetWorkingDir() string {
+ if x != nil {
+ return x.WorkingDir
+ }
+ return ""
+}
+
+func (x *SourceFile) GetCompilerArguments() []string {
+ if x != nil {
+ return x.CompilerArguments
+ }
+ return nil
+}
+
+func (x *SourceFile) GetGenerated() []*GeneratedFile {
+ if x != nil {
+ return x.Generated
+ }
+ return nil
+}
+
+func (x *SourceFile) GetDeps() []string {
+ if x != nil {
+ return x.Deps
+ }
+ return nil
+}
+
+func (x *SourceFile) GetStatus() *Status {
+ if x != nil {
+ return x.Status
+ }
+ return nil
+}
+
+type IdeAnalysis struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Path relative to workspace root, containing all the artifacts
+ // generated by the build system. GeneratedFile.path are always
+ // relative to this directory.
+ BuildArtifactRoot string `protobuf:"bytes,1,opt,name=build_artifact_root,json=buildArtifactRoot,proto3" json:"build_artifact_root,omitempty"`
+ Sources []*SourceFile `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"`
+ // Status representing overall analysis.
+ // Should fail only when no analysis can be performed, e.g. workspace
+ // isn't setup.
+ Status *Status `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *IdeAnalysis) Reset() {
+ *x = IdeAnalysis{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ide_query_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *IdeAnalysis) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IdeAnalysis) ProtoMessage() {}
+
+func (x *IdeAnalysis) ProtoReflect() protoreflect.Message {
+ mi := &file_ide_query_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.
+func (*IdeAnalysis) Descriptor() ([]byte, []int) {
+ return file_ide_query_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *IdeAnalysis) GetBuildArtifactRoot() string {
+ if x != nil {
+ return x.BuildArtifactRoot
+ }
+ return ""
+}
+
+func (x *IdeAnalysis) GetSources() []*SourceFile {
+ if x != nil {
+ return x.Sources
+ }
+ return nil
+}
+
+func (x *IdeAnalysis) GetStatus() *Status {
+ if x != nil {
+ return x.Status
+ }
+ return nil
+}
+
+var File_ide_query_proto protoreflect.FileDescriptor
+
+var file_ide_query_proto_rawDesc = []byte{
+ 0x0a, 0x0f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x15, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d,
+ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07,
+ 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f,
+ 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41,
+ 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x22, 0x51, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8f, 0x02, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72,
+ 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
+ 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+ 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72,
+ 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x42, 0x0a, 0x09, 0x67, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63,
+ 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61,
+ 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69,
+ 0x6c, 0x65, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a,
+ 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70,
+ 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a,
+ 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x0b, 0x49, 0x64, 0x65,
+ 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x75, 0x69, 0x6c,
+ 0x64, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x72, 0x74, 0x69,
+ 0x66, 0x61, 0x63, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x69, 0x64, 0x65,
+ 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01,
+ 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1b, 0x5a, 0x19,
+ 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
+ 0x65, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
+}
+
+var (
+ file_ide_query_proto_rawDescOnce sync.Once
+ file_ide_query_proto_rawDescData = file_ide_query_proto_rawDesc
+)
+
+func file_ide_query_proto_rawDescGZIP() []byte {
+ file_ide_query_proto_rawDescOnce.Do(func() {
+ file_ide_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_ide_query_proto_rawDescData)
+ })
+ return file_ide_query_proto_rawDescData
+}
+
+var file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_ide_query_proto_goTypes = []interface{}{
+ (Status_Code)(0), // 0: cider.build.companion.Status.Code
+ (*Status)(nil), // 1: cider.build.companion.Status
+ (*GeneratedFile)(nil), // 2: cider.build.companion.GeneratedFile
+ (*SourceFile)(nil), // 3: cider.build.companion.SourceFile
+ (*IdeAnalysis)(nil), // 4: cider.build.companion.IdeAnalysis
+}
+var file_ide_query_proto_depIdxs = []int32{
+ 0, // 0: cider.build.companion.Status.code:type_name -> cider.build.companion.Status.Code
+ 2, // 1: cider.build.companion.SourceFile.generated:type_name -> cider.build.companion.GeneratedFile
+ 1, // 2: cider.build.companion.SourceFile.status:type_name -> cider.build.companion.Status
+ 3, // 3: cider.build.companion.IdeAnalysis.sources:type_name -> cider.build.companion.SourceFile
+ 1, // 4: cider.build.companion.IdeAnalysis.status:type_name -> cider.build.companion.Status
+ 5, // [5:5] is the sub-list for method output_type
+ 5, // [5:5] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_ide_query_proto_init() }
+func file_ide_query_proto_init() {
+ if File_ide_query_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_ide_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Status); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*GeneratedFile); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ide_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SourceFile); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*IdeAnalysis); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ file_ide_query_proto_msgTypes[0].OneofWrappers = []interface{}{}
+ file_ide_query_proto_msgTypes[1].OneofWrappers = []interface{}{}
+ file_ide_query_proto_msgTypes[2].OneofWrappers = []interface{}{}
+ file_ide_query_proto_msgTypes[3].OneofWrappers = []interface{}{}
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_ide_query_proto_rawDesc,
+ NumEnums: 1,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_ide_query_proto_goTypes,
+ DependencyIndexes: file_ide_query_proto_depIdxs,
+ EnumInfos: file_ide_query_proto_enumTypes,
+ MessageInfos: file_ide_query_proto_msgTypes,
+ }.Build()
+ File_ide_query_proto = out.File
+ file_ide_query_proto_rawDesc = nil
+ file_ide_query_proto_goTypes = nil
+ file_ide_query_proto_depIdxs = nil
+}
diff --git a/tools/ide_query/ide_query_proto/ide_query.proto b/tools/ide_query/ide_query_proto/ide_query.proto
new file mode 100644
index 0000000..63eea39
--- /dev/null
+++ b/tools/ide_query/ide_query_proto/ide_query.proto
@@ -0,0 +1,66 @@
+syntax = "proto3";
+
+package ide_query;
+option go_package = "ide_query/ide_query_proto";
+
+// Indicates the success/failure for analysis.
+message Status {
+ enum Code {
+ OK = 0;
+ FAILURE = 1;
+ }
+ Code code = 1;
+ // Details about the status, might be displayed to user.
+ optional string message = 2;
+}
+
+message GeneratedFile {
+ // Path to the file relative to IdeAnalysis.build_artifact_root.
+ string path = 1;
+
+ // The text of the generated file, if not provided contents will be read
+ // from the path above in user's workstation.
+ optional bytes contents = 2;
+}
+
+message SourceFile {
+ // Path to the source file relative to repository root.
+ string path = 1;
+
+ // Working directory used by the build system. All the relative
+ // paths in compiler_arguments should be relative to this path.
+ // Relative to repository root.
+ string working_dir = 2;
+
+ // Compiler arguments to compile the source file. If multiple variants
+ // of the module being compiled are possible, the query script will choose
+ // one.
+ repeated string compiler_arguments = 3;
+
+ // Any generated files that are used in compiling the file.
+ repeated GeneratedFile generated = 4;
+
+ // Paths to all of the sources, like build files, code generators,
+ // proto files etc. that were used during analysis. Used to figure
+ // out when a set of build artifacts are stale and the query tool
+ // must be re-run.
+ // Relative to repository root.
+ repeated string deps = 5;
+
+ // Represents analysis status for this particular file. e.g. not part
+ // of the build graph.
+ optional Status status = 6;
+}
+
+message IdeAnalysis {
+ // Path relative to repository root, containing all the artifacts
+ // generated by the build system. GeneratedFile.path are always
+ // relative to this directory.
+ string build_artifact_root = 1;
+
+ repeated SourceFile sources = 2;
+
+ // Status representing overall analysis.
+ // Should fail only when no analysis can be performed.
+ optional Status status = 3;
+}
diff --git a/tools/ide_query/ide_query_proto/regen.sh b/tools/ide_query/ide_query_proto/regen.sh
new file mode 100755
index 0000000..eec4f37
--- /dev/null
+++ b/tools/ide_query/ide_query_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. ide_query.proto
diff --git a/tools/metadata/generator.go b/tools/metadata/generator.go
index d328876..b7668be 100644
--- a/tools/metadata/generator.go
+++ b/tools/metadata/generator.go
@@ -77,9 +77,18 @@
return string(data)
}
-func writeNewlineToOutputFile(outputFile string) {
+func writeEmptyOutputProto(outputFile string, metadataRule string) {
file, err := os.Create(outputFile)
- data := "\n"
+ if err != nil {
+ log.Fatal(err)
+ }
+ var message proto.Message
+ if metadataRule == "test_spec" {
+ message = &test_spec_proto.TestSpec{}
+ } else if metadataRule == "code_metadata" {
+ message = &code_metadata_proto.CodeMetadata{}
+ }
+ data, err := proto.Marshal(message)
if err != nil {
log.Fatal(err)
}
@@ -92,8 +101,8 @@
}
func processTestSpecProtobuf(
- filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
- errCh chan error, wg *sync.WaitGroup,
+ filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
+ errCh chan error, wg *sync.WaitGroup,
) {
defer wg.Done()
@@ -121,7 +130,7 @@
if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() {
errCh <- fmt.Errorf(
"Conflicting trendy team IDs found for %s at:\n%s with teamId"+
- ": %s,\n%s with teamId: %s",
+ ": %s,\n%s with teamId: %s",
key,
metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(),
existing.GetTrendyTeamId(),
@@ -147,8 +156,8 @@
// processCodeMetadataProtobuf processes CodeMetadata protobuf files
func processCodeMetadataProtobuf(
- filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap,
- errCh chan error, wg *sync.WaitGroup,
+ filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap,
+ errCh chan error, wg *sync.WaitGroup,
) {
defer wg.Done()
@@ -182,8 +191,8 @@
if attributes.TeamID != existing.TeamID && (!attributes.MultiOwnership || !existing.MultiOwnership) {
errCh <- fmt.Errorf(
"Conflict found for source file %s covered at %s with team ID: %s. Existing team ID: %s and path: %s."+
- " If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+
- "Multiple-ownership in general is discouraged though as it make infrastructure around android relying on this information pick up a random value when it needs only one.",
+ " If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+
+ "Multiple-ownership in general is discouraged though as it make infrastructure around android relying on this information pick up a random value when it needs only one.",
srcFile, internalMetadata.GetPath(), attributes.TeamID, existing.TeamID, existing.Path,
)
srcFileLock.Unlock()
@@ -235,7 +244,7 @@
inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n")
filePaths := strings.Split(inputFileData, " ")
if len(filePaths) == 1 && filePaths[0] == "" {
- writeNewlineToOutputFile(*outputFile)
+ writeEmptyOutputProto(*outputFile, *rule)
return
}
ownershipMetadataMap := &sync.Map{}
diff --git a/tools/metadata/testdata/generatedEmptyOutputFile.txt b/tools/metadata/testdata/generatedEmptyOutputFile.txt
index 8b13789..e69de29 100644
--- a/tools/metadata/testdata/generatedEmptyOutputFile.txt
+++ b/tools/metadata/testdata/generatedEmptyOutputFile.txt
@@ -1 +0,0 @@
-
diff --git a/tools/perf/benchmarks b/tools/perf/benchmarks
index e188858..6998ecd 100755
--- a/tools/perf/benchmarks
+++ b/tools/perf/benchmarks
@@ -29,6 +29,7 @@
import subprocess
import time
import uuid
+from typing import Optional
import pretty
import utils
@@ -80,6 +81,33 @@
undo: callable
"Function to revert the source tree to its previous condition in the most minimal way possible."
+_DUMPVARS_VARS=[
+ "COMMON_LUNCH_CHOICES",
+ "HOST_PREBUILT_TAG",
+ "print",
+ "PRODUCT_OUT",
+ "report_config",
+ "TARGET_ARCH",
+ "TARGET_BUILD_VARIANT",
+ "TARGET_DEVICE",
+ "TARGET_PRODUCT",
+]
+
+_DUMPVARS_ABS_VARS =[
+ "ANDROID_CLANG_PREBUILTS",
+ "ANDROID_JAVA_HOME",
+ "ANDROID_JAVA_TOOLCHAIN",
+ "ANDROID_PREBUILTS",
+ "HOST_OUT",
+ "HOST_OUT_EXECUTABLES",
+ "HOST_OUT_TESTCASES",
+ "OUT_DIR",
+ "print",
+ "PRODUCT_OUT",
+ "SOONG_HOST_OUT",
+ "SOONG_HOST_OUT_EXECUTABLES",
+ "TARGET_OUT_TESTCASES",
+]
@dataclasses.dataclass(frozen=True)
class Benchmark:
@@ -94,15 +122,47 @@
change: Change
"Source tree modification for the benchmark that will be measured"
- modules: list[str]
+ dumpvars: Optional[bool] = False
+ "If specified, soong will run in dumpvars mode rather than build-mode."
+
+ modules: Optional[list[str]] = None
"Build modules to build on soong command line"
- preroll: int
+ preroll: Optional[int] = 0
"Number of times to run the build command to stabilize"
- postroll: int
+ postroll: Optional[int] = 3
"Number of times to run the build command after reverting the action to stabilize"
+ def build_description(self):
+ "Short description of the benchmark's Soong invocation."
+ if self.dumpvars:
+ return "dumpvars"
+ elif self.modules:
+ return " ".join(self.modules)
+ return ""
+
+
+ def soong_command(self, root):
+ "Command line args to soong_ui for this benchmark."
+ if self.dumpvars:
+ return [
+ "--dumpvars-mode",
+ f"--vars=\"{' '.join(_DUMPVARS_VARS)}\"",
+ f"--abs-vars=\"{' '.join(_DUMPVARS_ABS_VARS)}\"",
+ "--var-prefix=var_cache_",
+ "--abs-var-prefix=abs_var_cache_",
+ ]
+ elif self.modules:
+ return [
+ "--build-mode",
+ "--all-modules",
+ f"--dir={root}",
+ "--skip-metrics-upload",
+ ] + self.modules
+ else:
+ raise Exception("Benchmark must specify dumpvars or modules")
+
@dataclasses.dataclass(frozen=True)
class FileSnapshot:
@@ -130,8 +190,15 @@
def Clean():
"""Remove the out directory."""
def remove_out():
- if os.path.exists("out"):
- shutil.rmtree("out")
+ out_dir = utils.get_out_dir()
+ #only remove actual contents, in case out is a symlink (as is the case for cog)
+ if os.path.exists(out_dir):
+ for filename in os.listdir(out_dir):
+ p = os.path.join(out_dir, filename)
+ if os.path.isfile(p) or os.path.islink(p):
+ os.remove(p)
+ elif os.path.isdir(p):
+ shutil.rmtree(p)
return Change(label="Remove out", change=remove_out, undo=lambda: None)
@@ -182,6 +249,21 @@
undo=lambda: orig.write()
)
+def ChangePublicApi():
+ change = AddJavaField("frameworks/base/core/java/android/provider/Settings.java",
+ "@android.annotation.SuppressLint(\"UnflaggedApi\") public")
+ orig_current_text = Snapshot("frameworks/base/core/api/current.txt")
+
+ def undo():
+ change.undo()
+ orig_current_text.write()
+
+ return Change(
+ label=change.label,
+ change=change.change,
+ undo=lambda: undo()
+ )
+
def AddJavaField(filename, prefix):
return Modify(filename,
lambda: f"{prefix} static final int BENCHMARK = {random.randint(0, 1000000)};\n",
@@ -235,6 +317,7 @@
"id": self.benchmark.id,
"title": self.benchmark.title,
"modules": self.benchmark.modules,
+ "dumpvars": self.benchmark.dumpvars,
"change": self.benchmark.change.label,
"iteration": self.iteration,
"log_dir": self.log_dir,
@@ -270,7 +353,7 @@
def _run_benchmark(self, lunch, benchmark, iteration):
"""Run a single benchmark."""
- benchmark_log_subdir = self._log_dir(lunch, benchmark, iteration)
+ benchmark_log_subdir = self._benchmark_log_dir(lunch, benchmark, iteration)
benchmark_log_dir = self._options.LogDir().joinpath(benchmark_log_subdir)
sys.stderr.write(f"STARTING BENCHMARK: {benchmark.id}\n")
@@ -283,7 +366,7 @@
# Preroll builds
for i in range(benchmark.preroll):
- ns = self._run_build(lunch, benchmark_log_dir.joinpath(f"pre_{i}"), benchmark.modules)
+ ns = self._run_build(lunch, benchmark_log_dir.joinpath(f"pre_{i}"), benchmark)
report.preroll_duration_ns.append(ns)
sys.stderr.write(f"PERFORMING CHANGE: {benchmark.change.label}\n")
@@ -292,18 +375,19 @@
try:
# Measured build
- ns = self._run_build(lunch, benchmark_log_dir.joinpath("measured"), benchmark.modules)
+ ns = self._run_build(lunch, benchmark_log_dir.joinpath("measured"), benchmark)
report.duration_ns = ns
dist_one = self._options.DistOne()
if dist_one:
# If we're disting just one benchmark, save the logs and we can stop here.
- self._dist(dist_one)
+ self._dist(utils.get_dist_dir(), benchmark.dumpvars)
else:
+ self._dist(benchmark_log_dir, benchmark.dumpvars, store_metrics_only=True)
# Postroll builds
- for i in range(benchmark.preroll):
+ for i in range(benchmark.postroll):
ns = self._run_build(lunch, benchmark_log_dir.joinpath(f"post_{i}"),
- benchmark.modules)
+ benchmark)
report.postroll_duration_ns.append(ns)
finally:
@@ -315,28 +399,24 @@
self._write_summary()
sys.stderr.write(f"FINISHED BENCHMARK: {benchmark.id}\n")
- def _log_dir(self, lunch, benchmark, iteration):
+ def _benchmark_log_dir(self, lunch, benchmark, iteration):
"""Construct the log directory fir a benchmark run."""
path = f"{lunch.Combine()}/{benchmark.id}"
# Zero pad to the correct length for correct alpha sorting
path += ("/%0" + str(len(str(self._options.Iterations()))) + "d") % iteration
return path
- def _run_build(self, lunch, build_log_dir, modules):
+ def _run_build(self, lunch, build_log_dir, benchmark):
"""Builds the modules. Saves interesting log files to log_dir. Raises FatalError
if the build fails.
"""
- sys.stderr.write(f"STARTING BUILD {modules}\n")
+ sys.stderr.write(f"STARTING BUILD {benchmark.build_description()}\n")
before_ns = time.perf_counter_ns()
if not self._options.DryRun():
cmd = [
"build/soong/soong_ui.bash",
- "--build-mode",
- "--all-modules",
- f"--dir={self._options.root}",
- "--skip-metrics-upload",
- ] + modules
+ ] + benchmark.soong_command(self._options.root)
env = dict(os.environ)
env["TARGET_PRODUCT"] = lunch.target_product
env["TARGET_RELEASE"] = lunch.target_release
@@ -350,20 +430,25 @@
# TODO: Copy some log files.
- sys.stderr.write(f"FINISHED BUILD {modules}\n")
+ sys.stderr.write(f"FINISHED BUILD {benchmark.build_description()}\n")
return after_ns - before_ns
- def _dist(self, dist_dir):
- out_dir = pathlib.Path("out")
- dest_dir = pathlib.Path(dist_dir).joinpath("logs")
+ def _dist(self, dist_dir, dumpvars, store_metrics_only=False):
+ out_dir = utils.get_out_dir()
+ dest_dir = dist_dir.joinpath("logs")
os.makedirs(dest_dir, exist_ok=True)
basenames = [
- "build.trace.gz",
- "soong.log",
"soong_build_metrics.pb",
"soong_metrics",
]
+ if not store_metrics_only:
+ basenames.extend([
+ "build.trace.gz",
+ "soong.log",
+ ])
+ if dumpvars:
+ basenames = ['dumpvars-'+b for b in basenames]
for base in basenames:
src = out_dir.joinpath(base)
if src.exists():
@@ -386,7 +471,7 @@
def benchmark_table(benchmarks):
rows = [("ID", "DESCRIPTION", "REBUILD"),]
- rows += [(benchmark.id, benchmark.title, " ".join(benchmark.modules)) for benchmark in
+ rows += [(benchmark.id, benchmark.title, benchmark.build_description()) for benchmark in
benchmarks]
return rows
@@ -570,6 +655,22 @@
"""Initialize the list of benchmarks."""
# Assumes that we've already chdired to the root of the tree.
self._benchmarks = [
+ Benchmark(
+ id="full_lunch",
+ title="Lunch from clean out",
+ change=Clean(),
+ dumpvars=True,
+ preroll=0,
+ postroll=0,
+ ),
+ Benchmark(
+ id="noop_lunch",
+ title="Lunch with no change",
+ change=NoChange(),
+ dumpvars=True,
+ preroll=1,
+ postroll=0,
+ ),
Benchmark(id="full",
title="Full build",
change=Clean(),
@@ -654,9 +755,8 @@
),
Benchmark(id="framework_api",
title="Add API to Settings.java",
- change=AddJavaField("frameworks/base/core/java/android/provider/Settings.java",
- "@android.annotation.SuppressLint(\"UnflaggedApi\") public"),
- modules=["framework-minus-apex"],
+ change=ChangePublicApi(),
+ modules=["api-stubs-docs-non-updatable-update-current-api", "framework-minus-apex"],
preroll=1,
postroll=2,
),
@@ -704,6 +804,7 @@
runner.Run()
except FatalError:
sys.stderr.write(f"FAILED\n")
+ sys.exit(1)
if __name__ == "__main__":
diff --git a/tools/perf/format_benchmarks b/tools/perf/format_benchmarks
index 845d73f..162c577 100755
--- a/tools/perf/format_benchmarks
+++ b/tools/perf/format_benchmarks
@@ -86,10 +86,12 @@
class Table:
- def __init__(self):
+ def __init__(self, row_title, fixed_titles=[]):
self._data = {}
self._rows = []
self._cols = []
+ self._fixed_cols = {}
+ self._titles = [row_title] + fixed_titles
def Set(self, column_key, row_key, data):
self._data[(column_key, row_key)] = data
@@ -98,19 +100,27 @@
if not row_key in self._rows:
self._rows.append(row_key)
+ def SetFixedCol(self, row_key, columns):
+ self._fixed_cols[row_key] = columns
+
def Write(self, out):
table = []
# Expand the column items
for row in zip(*self._cols):
if row.count(row[0]) == len(row):
continue
- table.append([""] + [col for col in row])
+ table.append([""] * len(self._titles) + [col for col in row])
if table:
+ # Update the last row of the header with title and add separator
+ for i in range(len(self._titles)):
+ table[len(table)-1][i] = self._titles[i]
table.append(pretty.SEPARATOR)
# Populate the data
for row in self._rows:
- table.append([str(row)] + [str(self._data.get((col, row), "")) for col in self._cols])
- out.write(pretty.FormatTable(table))
+ table.append([str(row)]
+ + self._fixed_cols[row]
+ + [str(self._data.get((col, row), "")) for col in self._cols])
+ out.write(pretty.FormatTable(table, alignments="LL"))
def format_duration_sec(ns):
@@ -173,11 +183,12 @@
in group_by(summary["benchmarks"], bm_key)]
# Build the table
- table = Table()
+ table = Table("Benchmark", ["Rebuild"])
for filename, summary in summaries:
for key, column in summary["columns"]:
for id, cell in column:
duration_ns = statistics.median([b["duration_ns"] for b in cell])
+ table.SetFixedCol(cell[0]["title"], [" ".join(cell[0]["modules"])])
table.Set(tuple([summary["date"].strftime("%Y-%m-%d"),
summary["branch"],
summary["tag"]]
diff --git a/tools/perf/pretty.py b/tools/perf/pretty.py
index 1b59098..14fdc9e 100644
--- a/tools/perf/pretty.py
+++ b/tools/perf/pretty.py
@@ -19,7 +19,7 @@
SEPARATOR = Sentinel()
-def FormatTable(data, prefix=""):
+def FormatTable(data, prefix="", alignments=[]):
"""Pretty print a table.
Prefixes each row with `prefix`.
@@ -40,10 +40,10 @@
else:
for i in range(len(row)):
cell = row[i] if row[i] else ""
- if i != 0:
+ if i >= len(alignments) or alignments[i] == "R":
result += " " * (widths[i] - len(cell))
result += cell
- if i == 0:
+ if i < len(alignments) and alignments[i] == "L":
result += " " * (widths[i] - len(cell))
result += colsep
result += "\n"
diff --git a/tools/perf/utils.py b/tools/perf/utils.py
index 08e393f..0e66d4c 100644
--- a/tools/perf/utils.py
+++ b/tools/perf/utils.py
@@ -19,12 +19,26 @@
def get_root():
top_dir = os.environ.get("ANDROID_BUILD_TOP")
- if top_dir:
- return pathlib.Path(top_dir).resolve()
d = pathlib.Path.cwd()
+ # with cog, someone may have a new workspace and new source tree top, but
+ # not run lunch yet, resulting in a misleading ANDROID_BUILD_TOP value
+ if top_dir and d.is_relative_to(top_dir):
+ return pathlib.Path(top_dir).resolve()
while True:
if d.joinpath("build", "soong", "soong_ui.bash").exists():
return d.resolve().absolute()
d = d.parent
if d == pathlib.Path("/"):
return None
+
+def get_dist_dir():
+ dist_dir = os.getenv("DIST_DIR")
+ if dist_dir:
+ return pathlib.Path(dist_dir).resolve()
+ return get_out_dir().joinpath("dist")
+
+def get_out_dir():
+ out_dir = os.getenv("OUT_DIR")
+ if not out_dir:
+ out_dir = "out"
+ return pathlib.Path(out_dir).resolve()
diff --git a/tools/post_process_props.py b/tools/post_process_props.py
index 32829c1..6f429fa 100755
--- a/tools/post_process_props.py
+++ b/tools/post_process_props.py
@@ -17,6 +17,8 @@
import argparse
import sys
+from uffd_gc_utils import should_enable_uffd_gc
+
# Usage: post_process_props.py file.prop [disallowed_key, ...]
# Disallowed keys are removed from the property file, if present
@@ -27,7 +29,7 @@
# Put the modifications that you need to make into the */build.prop into this
# function.
-def mangle_build_prop(prop_list):
+def mangle_build_prop(prop_list, kernel_version_file_for_uffd_gc):
# If ro.debuggable is 1, then enable adb on USB by default
# (this is for userdebug builds)
if prop_list.get_value("ro.debuggable") == "1":
@@ -38,6 +40,11 @@
else:
val = val + ",adb"
prop_list.put("persist.sys.usb.config", val)
+ if prop_list.get_value("ro.dalvik.vm.enable_uffd_gc") == "default":
+ assert kernel_version_file_for_uffd_gc != ""
+ enable_uffd_gc = should_enable_uffd_gc(kernel_version_file_for_uffd_gc)
+ prop_list.put("ro.dalvik.vm.enable_uffd_gc",
+ "true" if enable_uffd_gc else "false")
def validate_grf_props(prop_list):
"""Validate GRF properties if exist.
@@ -233,6 +240,7 @@
parser.add_argument("filename")
parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*")
parser.add_argument("--sdk-version", type=int, required=True)
+ parser.add_argument("--kernel-version-file-for-uffd-gc", required=True)
args = parser.parse_args()
if not args.filename.endswith("/build.prop"):
@@ -240,7 +248,7 @@
sys.exit(1)
props = PropList(args.filename)
- mangle_build_prop(props)
+ mangle_build_prop(props, args.kernel_version_file_for_uffd_gc)
if not override_optional_props(props, args.allow_dup):
sys.exit(1)
if not validate_grf_props(props):
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index bd8ce14..4941c71 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -168,7 +168,6 @@
"apexd_host",
"brillo_update_payload",
"checkvintf",
- "generate_gki_certificate",
"lz4",
"toybox",
"unpack_bootimg",
@@ -245,7 +244,6 @@
"boot_signer",
"brotli",
"bsdiff",
- "generate_gki_certificate",
"imgdiff",
"lz4",
"mkbootfs",
@@ -310,7 +308,6 @@
"brotli",
"bsdiff",
"deapexer",
- "generate_gki_certificate",
"imgdiff",
"lz4",
"mkbootfs",
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index fc4ab68..b39a82c 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -835,8 +835,7 @@
def AddApexInfo(output_zip):
- apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
- compressed_only=False)
+ apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp)
apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
apex_metadata_proto.apex_info.extend(apex_infos)
apex_info_bytes = apex_metadata_proto.SerializeToString()
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index 1ddffc1..3abef3b 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -534,22 +534,28 @@
'Failed to get type for {}:\n{}'.format(apex_file, e))
-def GetApexInfoFromTargetFiles(input_file, partition, compressed_only=True):
+def GetApexInfoFromTargetFiles(input_file):
"""
- Get information about system APEX stored in the input_file zip
+ Get information about APEXes stored in the input_file zip
Args:
input_file: The filename of the target build target-files zip or directory.
Return:
A list of ota_metadata_pb2.ApexInfo() populated using the APEX stored in
- /system partition of the input_file
+ each partition of the input_file
"""
# Extract the apex files so that we can run checks on them
if not isinstance(input_file, str):
raise RuntimeError("must pass filepath to target-files zip or directory")
+ apex_infos = []
+ for partition in ['system', 'system_ext', 'product', 'vendor']:
+ apex_infos.extend(GetApexInfoForPartition(input_file, partition))
+ return apex_infos
+
+def GetApexInfoForPartition(input_file, partition):
apex_subdir = os.path.join(partition.upper(), 'apex')
if os.path.isdir(input_file):
tmp_dir = input_file
@@ -607,7 +613,6 @@
'--output', decompressed_file_path])
apex_info.decompressed_size = os.path.getsize(decompressed_file_path)
- if not compressed_only or apex_info.is_compressed:
- apex_infos.append(apex_info)
+ apex_infos.append(apex_info)
return apex_infos
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index bde152f..a7b1d8c 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -260,7 +260,7 @@
if fs_type.startswith("ext4") and partition_headroom > reserved_size:
reserved_size = partition_headroom
- return size + reserved_size
+ return int(size * 1.1) + reserved_size
def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):
@@ -636,7 +636,7 @@
size = verity_image_builder.CalculateDynamicPartitionSize(size)
prop_dict["partition_size"] = str(size)
logger.info(
- "Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
+ "Allocating %d MB for %s", size // BYTES_IN_MB, out_file)
prop_dict["image_size"] = prop_dict["partition_size"]
@@ -979,7 +979,11 @@
parser.add_argument("target_out",
help="the path to $(TARGET_OUT). Certain tools will use this to look through multiple staging "
"directories for fs config files.")
+ parser.add_argument("-v", action="store_true",
+ help="Enable verbose logging", dest="verbose")
args = parser.parse_args()
+ if args.verbose:
+ OPTIONS.verbose = True
common.InitLogging()
diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py
index a7b3523..cdafb4b 100755
--- a/tools/releasetools/check_target_files_signatures.py
+++ b/tools/releasetools/check_target_files_signatures.py
@@ -58,22 +58,6 @@
logger = logging.getLogger(__name__)
-# Work around a bug in Python's zipfile module that prevents opening of zipfiles
-# if any entry has an extra field of between 1 and 3 bytes (which is common with
-# zipaligned APKs). This overrides the ZipInfo._decodeExtra() method (which
-# contains the bug) with an empty version (since we don't need to decode the
-# extra field anyway).
-# Issue #14315: https://bugs.python.org/issue14315, fixed in Python 2.7.8 and
-# Python 3.5.0 alpha 1.
-
-
-class MyZipInfo(zipfile.ZipInfo):
- def _decodeExtra(self):
- pass
-
-
-zipfile.ZipInfo = MyZipInfo
-
OPTIONS = common.OPTIONS
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 7451ccc..1990377 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1565,50 +1565,6 @@
pubkey_path=pubkey_path)
-def _HasGkiCertificationArgs():
- return ("gki_signing_key_path" in OPTIONS.info_dict and
- "gki_signing_algorithm" in OPTIONS.info_dict)
-
-
-def _GenerateGkiCertificate(image, image_name):
- key_path = OPTIONS.info_dict.get("gki_signing_key_path")
- algorithm = OPTIONS.info_dict.get("gki_signing_algorithm")
-
- key_path = ResolveAVBSigningPathArgs(key_path)
-
- # Checks key_path exists, before processing --gki_signing_* args.
- if not os.path.exists(key_path):
- raise ExternalError(
- 'gki_signing_key_path: "{}" not found'.format(key_path))
-
- output_certificate = tempfile.NamedTemporaryFile()
- cmd = [
- "generate_gki_certificate",
- "--name", image_name,
- "--algorithm", algorithm,
- "--key", key_path,
- "--output", output_certificate.name,
- image,
- ]
-
- signature_args = OPTIONS.info_dict.get("gki_signing_signature_args", "")
- signature_args = signature_args.strip()
- if signature_args:
- cmd.extend(["--additional_avb_args", signature_args])
-
- args = OPTIONS.info_dict.get("avb_boot_add_hash_footer_args", "")
- args = args.strip()
- if args:
- cmd.extend(["--additional_avb_args", args])
-
- RunAndCheckOutput(cmd)
-
- output_certificate.seek(os.SEEK_SET, 0)
- data = output_certificate.read()
- output_certificate.close()
- return data
-
-
def BuildVBMeta(image_path, partitions, name, needed_partitions,
resolve_rollback_index_location_conflict=False):
"""Creates a VBMeta image.
@@ -1831,29 +1787,6 @@
RunAndCheckOutput(cmd)
- if _HasGkiCertificationArgs():
- if not os.path.exists(img.name):
- raise ValueError("Cannot find GKI boot.img")
- if kernel_path is None or not os.path.exists(kernel_path):
- raise ValueError("Cannot find GKI kernel.img")
-
- # Certify GKI images.
- boot_signature_bytes = b''
- boot_signature_bytes += _GenerateGkiCertificate(img.name, "boot")
- boot_signature_bytes += _GenerateGkiCertificate(
- kernel_path, "generic_kernel")
-
- BOOT_SIGNATURE_SIZE = 16 * 1024
- if len(boot_signature_bytes) > BOOT_SIGNATURE_SIZE:
- raise ValueError(
- f"GKI boot_signature size must be <= {BOOT_SIGNATURE_SIZE}")
- boot_signature_bytes += (
- b'\0' * (BOOT_SIGNATURE_SIZE - len(boot_signature_bytes)))
- assert len(boot_signature_bytes) == BOOT_SIGNATURE_SIZE
-
- with open(img.name, 'ab') as f:
- f.write(boot_signature_bytes)
-
# Sign the image if vboot is non-empty.
if info_dict.get("vboot"):
path = "/" + partition_name
diff --git a/tools/releasetools/merge/merge_compatibility_checks.py b/tools/releasetools/merge/merge_compatibility_checks.py
index 207abe2..8c9993f 100644
--- a/tools/releasetools/merge/merge_compatibility_checks.py
+++ b/tools/releasetools/merge/merge_compatibility_checks.py
@@ -190,8 +190,8 @@
apex_packages = set()
for partition in partition_map.keys():
try:
- apex_info = apex_utils.GetApexInfoFromTargetFiles(
- target_files_dir, partition, compressed_only=False)
+ apex_info = apex_utils.GetApexInfoForPartition(
+ target_files_dir, partition)
except RuntimeError as err:
errors.append(str(err))
apex_info = []
diff --git a/tools/releasetools/merge/merge_meta.py b/tools/releasetools/merge/merge_meta.py
index 198c973..76582c0 100644
--- a/tools/releasetools/merge/merge_meta.py
+++ b/tools/releasetools/merge/merge_meta.py
@@ -53,23 +53,31 @@
MODULE_KEY_PATTERN = re.compile(r'name="(.+)\.(apex|apk)"')
-def MergeUpdateEngineConfig(input_metadir1, input_metadir2, merged_meta_dir):
- UPDATE_ENGINE_CONFIG_NAME = "update_engine_config.txt"
- config1_path = os.path.join(
- input_metadir1, UPDATE_ENGINE_CONFIG_NAME)
- config2_path = os.path.join(
- input_metadir2, UPDATE_ENGINE_CONFIG_NAME)
- config1 = ParseUpdateEngineConfig(config1_path)
- config2 = ParseUpdateEngineConfig(config2_path)
- # Copy older config to merged target files for maximum compatibility
- # update_engine in system partition is from system side, but
- # update_engine_sideload in recovery is from vendor side.
- if config1 < config2:
- shutil.copy(config1_path, os.path.join(
- merged_meta_dir, UPDATE_ENGINE_CONFIG_NAME))
+def MergeUpdateEngineConfig(framework_meta_dir, vendor_meta_dir,
+ merged_meta_dir):
+ """Merges META/update_engine_config.txt.
+
+ The output is the configuration for maximum compatibility.
+ """
+ _CONFIG_NAME = 'update_engine_config.txt'
+ framework_config_path = os.path.join(framework_meta_dir, _CONFIG_NAME)
+ vendor_config_path = os.path.join(vendor_meta_dir, _CONFIG_NAME)
+ merged_config_path = os.path.join(merged_meta_dir, _CONFIG_NAME)
+
+ if os.path.exists(framework_config_path):
+ framework_config = ParseUpdateEngineConfig(framework_config_path)
+ vendor_config = ParseUpdateEngineConfig(vendor_config_path)
+ # Copy older config to merged target files for maximum compatibility
+ # update_engine in system partition is from system side, but
+ # update_engine_sideload in recovery is from vendor side.
+ if framework_config < vendor_config:
+ shutil.copy(framework_config_path, merged_config_path)
+ else:
+ shutil.copy(vendor_config_path, merged_config_path)
else:
- shutil.copy(config2_path, os.path.join(
- merged_meta_dir, UPDATE_ENGINE_CONFIG_NAME))
+ if not OPTIONS.allow_partial_ab:
+ raise FileNotFoundError(framework_config_path)
+ shutil.copy(vendor_config_path, merged_config_path)
def MergeMetaFiles(temp_dir, merged_dir, framework_partitions):
@@ -125,8 +133,7 @@
if OPTIONS.merged_misc_info.get('ab_update') == 'true':
MergeUpdateEngineConfig(
- framework_meta_dir,
- vendor_meta_dir, merged_meta_dir)
+ framework_meta_dir, vendor_meta_dir, merged_meta_dir)
# Write the now-finalized OPTIONS.merged_misc_info.
merge_utils.WriteSortedData(
@@ -140,16 +147,24 @@
The output contains the union of the partition names.
"""
- with open(os.path.join(framework_meta_dir, 'ab_partitions.txt')) as f:
- # Filter out some partitions here to support the case that the
- # ab_partitions.txt of framework-target-files has non-framework partitions.
- # This case happens when we use a complete merged target files package as
- # the framework-target-files.
- framework_ab_partitions = [
- partition
- for partition in f.read().splitlines()
- if partition in framework_partitions
- ]
+ framework_ab_partitions = []
+ framework_ab_config = os.path.join(framework_meta_dir, 'ab_partitions.txt')
+ if os.path.exists(framework_ab_config):
+ with open(framework_ab_config) as f:
+ # Filter out some partitions here to support the case that the
+ # ab_partitions.txt of framework-target-files has non-framework
+ # partitions. This case happens when we use a complete merged target
+ # files package as the framework-target-files.
+ framework_ab_partitions.extend([
+ partition
+ for partition in f.read().splitlines()
+ if partition in framework_partitions
+ ])
+ else:
+ if not OPTIONS.allow_partial_ab:
+ raise FileNotFoundError(framework_ab_config)
+ logger.info('Use partial AB because framework ab_partitions.txt does not '
+ 'exist.')
with open(os.path.join(vendor_meta_dir, 'ab_partitions.txt')) as f:
vendor_ab_partitions = f.read().splitlines()
diff --git a/tools/releasetools/merge/merge_target_files.py b/tools/releasetools/merge/merge_target_files.py
index 4619246..fdba927 100755
--- a/tools/releasetools/merge/merge_target_files.py
+++ b/tools/releasetools/merge/merge_target_files.py
@@ -98,6 +98,10 @@
If provided, resolve the conflict AVB rollback index location when
necessary.
+ --allow-partial-ab
+ If provided, allow merging non-AB framework target files with AB vendor
+ target files, which means that only the vendor has AB partitions.
+
The following only apply when using the VSDK to perform dexopt on vendor apps:
--framework-dexpreopt-config
@@ -154,6 +158,7 @@
OPTIONS.rebuild_sepolicy = False
OPTIONS.keep_tmp = False
OPTIONS.avb_resolve_rollback_index_location_conflict = False
+OPTIONS.allow_partial_ab = False
OPTIONS.framework_dexpreopt_config = None
OPTIONS.framework_dexpreopt_tools = None
OPTIONS.vendor_dexpreopt_config = None
@@ -576,6 +581,8 @@
OPTIONS.keep_tmp = True
elif o == '--avb-resolve-rollback-index-location-conflict':
OPTIONS.avb_resolve_rollback_index_location_conflict = True
+ elif o == '--allow-partial-ab':
+ OPTIONS.allow_partial_ab = True
elif o == '--framework-dexpreopt-config':
OPTIONS.framework_dexpreopt_config = a
elif o == '--framework-dexpreopt-tools':
@@ -617,6 +624,7 @@
'rebuild-sepolicy',
'keep-tmp',
'avb-resolve-rollback-index-location-conflict',
+ 'allow-partial-ab',
],
extra_option_handler=option_handler)
diff --git a/tools/releasetools/merge_ota.py b/tools/releasetools/merge_ota.py
index 24d9ea9..fb5957a 100644
--- a/tools/releasetools/merge_ota.py
+++ b/tools/releasetools/merge_ota.py
@@ -243,8 +243,6 @@
# Get signing keys
key_passwords = common.GetKeyPasswords([args.package_key])
- generator = PayloadGenerator()
-
apex_info_bytes = ApexInfo(file_paths)
with tempfile.NamedTemporaryFile() as unsigned_payload:
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index b65764b..dbbbca2 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -259,6 +259,9 @@
--vabc_cow_version
Specify the VABC cow version to be used
+
+ --compression_factor
+ Specify the maximum block size to be compressed at once during OTA. supported options: 4k, 8k, 16k, 32k, 64k, 128k, 256k
"""
from __future__ import print_function
@@ -331,6 +334,7 @@
OPTIONS.security_patch_level = None
OPTIONS.max_threads = None
OPTIONS.vabc_cow_version = None
+OPTIONS.compression_factor = None
POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
@@ -393,17 +397,6 @@
"""
return ModifyKeyvalueList(content, "virtual_ab_compression_method", algo)
-def SetVABCCowVersion(content, cow_version):
- """ Update virtual_ab_cow_version in dynamic_partitions_info.txt
- Args:
- content: The string content of dynamic_partitions_info.txt
- algo: The cow version be used for VABC. See
- https://cs.android.com/android/platform/superproject/main/+/main:system/core/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h;l=36
- Returns:
- Updated content of dynamic_partitions_info.txt , updated cow version
- """
- return ModifyKeyvalueList(content, "virtual_ab_cow_version", cow_version)
-
def UpdatesInfoForSpecialUpdates(content, partitions_filter,
delete_keys=None):
@@ -610,7 +603,7 @@
This function modifies ab_partitions list with the desired partitions before
calling the brillo_update_payload script. It also cleans up the reference to
- the excluded partitions in the info file, e.g misc_info.txt.
+ the excluded partitions in the info file, e.g. misc_info.txt.
Args:
input_file: The input target-files.zip filename.
@@ -863,10 +856,10 @@
return ExtractTargetFiles(target_file)
-def ValidateCompressinParam(target_info):
+def ValidateCompressionParam(target_info):
vabc_compression_param = OPTIONS.vabc_compression_param
if vabc_compression_param:
- minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[vabc_compression_param]
+ minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[vabc_compression_param.split(",")[0]]
if target_info.vendor_api_level < minimum_api_level_required:
raise ValueError("Specified VABC compression param {} is only supported for API level >= {}, device is on API level {}".format(
vabc_compression_param, minimum_api_level_required, target_info.vendor_api_level))
@@ -879,7 +872,7 @@
target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
if OPTIONS.disable_vabc and target_info.is_release_key:
raise ValueError("Disabling VABC on release-key builds is not supported.")
- ValidateCompressinParam(target_info)
+ ValidateCompressionParam(target_info)
vabc_compression_param = target_info.vabc_compression_param
target_file = ExtractOrCopyTargetFiles(target_file)
@@ -1020,6 +1013,8 @@
target_file, vabc_compression_param)
if OPTIONS.vabc_cow_version:
target_file = ModifyTargetFilesDynamicPartitionInfo(target_file, "virtual_ab_cow_version", OPTIONS.vabc_cow_version)
+ if OPTIONS.compression_factor:
+ target_file = ModifyTargetFilesDynamicPartitionInfo(target_file, "virtual_ab_compression_factor", OPTIONS.compression_factor)
if OPTIONS.skip_postinstall:
target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
# Target_file may have been modified, reparse ab_partitions
@@ -1280,6 +1275,13 @@
else:
raise ValueError("Cannot parse value %r for option %r - only "
"integers are allowed." % (a, o))
+ elif o in ("--compression_factor"):
+ values = ["4k", "8k", "16k", "32k", "64k", "128k", "256k"]
+ if a[:-1].isdigit() and a in values and a.endswith("k"):
+ OPTIONS.compression_factor = str(int(a[:-1]) * 1024)
+ else:
+ raise ValueError("Please specify value from following options: 4k, 8k, 16k, 32k, 64k, 128k", "256k")
+
elif o == "--vabc_cow_version":
if a.isdigit():
OPTIONS.vabc_cow_version = a
@@ -1335,6 +1337,7 @@
"security_patch_level=",
"max_threads=",
"vabc_cow_version=",
+ "compression_factor=",
], 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 4356394..bf69dec 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -124,14 +124,8 @@
--gki_signing_algorithm <algorithm>
--gki_signing_key <key>
- Use the specified algorithm (e.g. SHA256_RSA4096) and the key to generate
- 'boot signature' in a v4 boot.img. Otherwise it uses the existing values
- in info dict.
-
--gki_signing_extra_args <args>
- Specify any additional args that are needed to generate 'boot signature'
- (e.g. --prop foo:bar). The args will be appended to the existing ones
- in info dict.
+ DEPRECATED Does nothing.
--android_jar_path <path>
Path to the android.jar to repack the apex file.
@@ -221,9 +215,6 @@
OPTIONS.avb_keys = {}
OPTIONS.avb_algorithms = {}
OPTIONS.avb_extra_args = {}
-OPTIONS.gki_signing_key = None
-OPTIONS.gki_signing_algorithm = None
-OPTIONS.gki_signing_extra_args = None
OPTIONS.android_jar_path = None
OPTIONS.vendor_partitions = set()
OPTIONS.vendor_otatools = None
@@ -595,7 +586,7 @@
[len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
except ValueError:
- # Sets this to zero for targets without APK files, e.g., gki_arm64.
+ # Sets this to zero for targets without APK files.
maxsize = 0
for info in input_tf_zip.infolist():
@@ -818,9 +809,6 @@
if misc_info.get('avb_enable') == 'true':
RewriteAvbProps(misc_info)
- # Replace the GKI signing key for boot.img, if any.
- ReplaceGkiSigningKey(misc_info)
-
# Write back misc_info with the latest values.
ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
@@ -1102,27 +1090,6 @@
misc_info[args_key] = result
-def ReplaceGkiSigningKey(misc_info):
- """Replaces the GKI signing key."""
-
- key = OPTIONS.gki_signing_key
- if not key:
- return
-
- algorithm = OPTIONS.gki_signing_algorithm
- if not algorithm:
- raise ValueError("Missing --gki_signing_algorithm")
-
- print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
- misc_info["gki_signing_algorithm"] = algorithm
- misc_info["gki_signing_key_path"] = key
-
- extra_args = OPTIONS.gki_signing_extra_args
- if extra_args:
- print('Setting GKI signing args: "%s"' % (extra_args))
- misc_info["gki_signing_signature_args"] = extra_args
-
-
def BuildKeyMap(misc_info, key_mapping_options):
for s, d in key_mapping_options:
if s is None: # -d option
@@ -1137,6 +1104,7 @@
devkeydir + "/shared": d + "/shared",
devkeydir + "/platform": d + "/platform",
devkeydir + "/networkstack": d + "/networkstack",
+ devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
})
else:
OPTIONS.key_map[s] = d
@@ -1476,12 +1444,6 @@
# 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
partition, extra_args = a.split("=", 1)
OPTIONS.avb_extra_args[partition] = extra_args
- elif o == "--gki_signing_key":
- OPTIONS.gki_signing_key = a
- elif o == "--gki_signing_algorithm":
- OPTIONS.gki_signing_algorithm = a
- elif o == "--gki_signing_extra_args":
- OPTIONS.gki_signing_extra_args = a
elif o == "--vendor_otatools":
OPTIONS.vendor_otatools = a
elif o == "--vendor_partitions":
@@ -1492,6 +1454,8 @@
OPTIONS.override_apk_keys = a
elif o == "--override_apex_keys":
OPTIONS.override_apex_keys = a
+ elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"):
+ print(f"{o} is deprecated and does nothing")
else:
return False
return True
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 9b2e667..2989338 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1605,40 +1605,6 @@
self.assertEqual(3, chained_partition_args.rollback_index_location)
self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
- def test_GenerateGkiCertificate_KeyPathNotFound(self):
- pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
- self.assertFalse(os.path.exists(pubkey))
-
- common.OPTIONS.info_dict = {
- 'gki_signing_key_path': pubkey,
- 'gki_signing_algorithm': 'SHA256_RSA4096',
- 'gki_signing_signature_args': '--prop foo:bar',
- }
- common.OPTIONS.search_path = None
- test_file = tempfile.NamedTemporaryFile()
- self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
- test_file.name, 'generic_kernel')
-
- def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
- pubkey = 'no_testkey_gki.pem'
- self.assertFalse(os.path.exists(pubkey))
-
- # Tests it should raise ExternalError if no key found under
- # OPTIONS.search_path.
- search_path_dir = common.MakeTempDir()
- search_pubkey = os.path.join(search_path_dir, pubkey)
- self.assertFalse(os.path.exists(search_pubkey))
-
- common.OPTIONS.search_path = search_path_dir
- common.OPTIONS.info_dict = {
- 'gki_signing_key_path': pubkey,
- 'gki_signing_algorithm': 'SHA256_RSA4096',
- 'gki_signing_signature_args': '--prop foo:bar',
- }
- test_file = tempfile.NamedTemporaryFile()
- self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
- test_file.name, 'generic_kernel')
-
class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
"""Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index d1e76b9..b6fcb18 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -299,7 +299,7 @@
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetApexInfoFromTargetFiles(self):
target_files = construct_target_files(compressedApex=True)
- apex_infos = GetApexInfoFromTargetFiles(target_files, 'system')
+ apex_infos = GetApexInfoFromTargetFiles(target_files)
self.assertEqual(len(apex_infos), 1)
self.assertEqual(apex_infos[0].package_name, "com.android.apex.compressed")
self.assertEqual(apex_infos[0].version, 1)
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 0cd7dac..9cc6df4 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -23,8 +23,7 @@
import test_utils
from sign_target_files_apks import (
CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo,
- ReplaceCerts, ReplaceGkiSigningKey, RewriteAvbProps, RewriteProps,
- WriteOtacerts)
+ ReplaceCerts, RewriteAvbProps, RewriteProps, WriteOtacerts)
class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
@@ -536,52 +535,3 @@
'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
'build/make/target/product/security/testkey', None),
}, keys_info)
-
- def test_ReplaceGkiSigningKey(self):
- common.OPTIONS.gki_signing_key = 'release_gki_key'
- common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
- common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
-
- misc_info = {
- 'gki_signing_key_path': 'default_gki_key',
- 'gki_signing_algorithm': 'default_gki_algorithm',
- 'gki_signing_signature_args': 'default_gki_signature_args',
- }
- expected_dict = {
- 'gki_signing_key_path': 'release_gki_key',
- 'gki_signing_algorithm': 'release_gki_algorithm',
- 'gki_signing_signature_args': 'release_gki_signature_extra_args',
- }
- ReplaceGkiSigningKey(misc_info)
- self.assertDictEqual(expected_dict, misc_info)
-
- def test_ReplaceGkiSigningKey_MissingSigningAlgorithm(self):
- common.OPTIONS.gki_signing_key = 'release_gki_key'
- common.OPTIONS.gki_signing_algorithm = None
- common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
-
- misc_info = {
- 'gki_signing_key_path': 'default_gki_key',
- 'gki_signing_algorithm': 'default_gki_algorithm',
- 'gki_signing_signature_args': 'default_gki_signature_args',
- }
- self.assertRaises(ValueError, ReplaceGkiSigningKey, misc_info)
-
- def test_ReplaceGkiSigningKey_MissingSigningKeyNop(self):
- common.OPTIONS.gki_signing_key = None
- common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
- common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
-
- # No change to misc_info if common.OPTIONS.gki_signing_key is missing.
- misc_info = {
- 'gki_signing_key_path': 'default_gki_key',
- 'gki_signing_algorithm': 'default_gki_algorithm',
- 'gki_signing_signature_args': 'default_gki_signature_args',
- }
- expected_dict = {
- 'gki_signing_key_path': 'default_gki_key',
- 'gki_signing_algorithm': 'default_gki_algorithm',
- 'gki_signing_signature_args': 'default_gki_signature_args',
- }
- ReplaceGkiSigningKey(misc_info)
- self.assertDictEqual(expected_dict, misc_info)
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 8da4fa2..88fd892 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -132,7 +132,7 @@
return
# Verify IMAGES/system.img if applicable.
- # Some targets, e.g., gki_arm64, gki_x86_64, etc., are system.img-less.
+ # Some targets are system.img-less.
if 'IMAGES/system.img' in input_zip.namelist():
CheckAllFiles('system')