Merge "Revert "Add fingerprint to packages.map."" into main
diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py
index 933e43e..b8c4a38 100644
--- a/ci/build_test_suites.py
+++ b/ci/build_test_suites.py
@@ -22,6 +22,7 @@
import pathlib
import subprocess
import sys
+from typing import Callable
from build_context import BuildContext
import optimized_targets
@@ -68,7 +69,7 @@
return BuildPlan(set(self.args.extra_targets), set())
build_targets = set()
- packaging_commands = []
+ packaging_commands_getters = []
for target in self.args.extra_targets:
if self._unused_target_exclusion_enabled(
target
@@ -84,9 +85,11 @@
target, self.build_context, self.args
)
build_targets.update(target_optimizer.get_build_targets())
- packaging_commands.extend(target_optimizer.get_package_outputs_commands())
+ packaging_commands_getters.append(
+ target_optimizer.get_package_outputs_commands
+ )
- return BuildPlan(build_targets, packaging_commands)
+ return BuildPlan(build_targets, packaging_commands_getters)
def _unused_target_exclusion_enabled(self, target: str) -> bool:
return (
@@ -98,7 +101,7 @@
@dataclass(frozen=True)
class BuildPlan:
build_targets: set[str]
- packaging_commands: list[list[str]]
+ packaging_commands_getters: list[Callable[[], list[list[str]]]]
def build_test_suites(argv: list[str]) -> int:
@@ -180,9 +183,10 @@
except subprocess.CalledProcessError as e:
raise BuildFailureError(e.returncode) from e
- for packaging_command in build_plan.packaging_commands:
+ for packaging_commands_getter in build_plan.packaging_commands_getters:
try:
- run_command(packaging_command)
+ for packaging_command in packaging_commands_getter():
+ run_command(packaging_command)
except subprocess.CalledProcessError as e:
raise BuildFailureError(e.returncode) from e
diff --git a/ci/build_test_suites_test.py b/ci/build_test_suites_test.py
index fd06a3a..2afaab7 100644
--- a/ci/build_test_suites_test.py
+++ b/ci/build_test_suites_test.py
@@ -276,7 +276,8 @@
build_plan = build_planner.create_build_plan()
- self.assertEqual(len(build_plan.packaging_commands), 0)
+ for packaging_command in self.run_packaging_commands(build_plan):
+ self.assertEqual(len(packaging_command), 0)
def test_build_optimization_on_optimizes_target(self):
build_targets = {'target_1', 'target_2'}
@@ -306,7 +307,7 @@
build_plan = build_planner.create_build_plan()
- self.assertIn([f'packaging {optimized_target_name}'], build_plan.packaging_commands)
+ self.assertIn(packaging_commands, self.run_packaging_commands(build_plan))
def test_individual_build_optimization_off_doesnt_optimize(self):
build_targets = {'target_1', 'target_2'}
@@ -328,7 +329,8 @@
build_plan = build_planner.create_build_plan()
- self.assertFalse(build_plan.packaging_commands)
+ for packaging_command in self.run_packaging_commands(build_plan):
+ self.assertEqual(len(packaging_command), 0)
def test_target_output_used_target_built(self):
build_target = 'test_target'
@@ -485,6 +487,12 @@
],
}
+ def run_packaging_commands(self, build_plan: build_test_suites.BuildPlan):
+ return [
+ packaging_command_getter()
+ for packaging_command_getter in build_plan.packaging_commands_getters
+ ]
+
def wait_until(
condition_function: Callable[[], bool],
diff --git a/ci/optimized_targets.py b/ci/optimized_targets.py
index 4bee401..688bdd8 100644
--- a/ci/optimized_targets.py
+++ b/ci/optimized_targets.py
@@ -121,13 +121,13 @@
process_result = subprocess.run(
args=[
f'{src_top / self._SOONG_UI_BASH_PATH}',
- '--dumpvar-mode',
- '--abs',
- soong_vars,
+ '--dumpvars-mode',
+ f'--abs-vars={" ".join(soong_vars)}',
],
env=os.environ,
check=False,
capture_output=True,
+ text=True,
)
if not process_result.returncode == 0:
logging.error('soong dumpvars command failed! stderr:')
@@ -142,7 +142,7 @@
try:
return {
line.split('=')[0]: line.split('=')[1].strip("'")
- for line in process_result.stdout.split('\n')
+ for line in process_result.stdout.strip().split('\n')
}
except IndexError as e:
raise RuntimeError(
@@ -214,10 +214,13 @@
normally built.
"""
- # List of modules that are always required to be in general-tests.zip.
- _REQUIRED_MODULES = frozenset(
- ['cts-tradefed', 'vts-tradefed', 'compatibility-host-util']
- )
+ # List of modules that are built alongside general-tests as dependencies.
+ _REQUIRED_MODULES = frozenset([
+ 'cts-tradefed',
+ 'vts-tradefed',
+ 'compatibility-host-util',
+ 'general-tests-shared-libs',
+ ])
def get_build_targets_impl(self) -> set[str]:
change_info_file_path = os.environ.get('CHANGE_INFO')
@@ -286,6 +289,10 @@
host_config_files = []
target_config_files = []
for module in self.modules_to_build:
+ # The required modules are handled separately, no need to package.
+ if module in self._REQUIRED_MODULES:
+ continue
+
host_path = host_out_testcases / module
if os.path.exists(host_path):
host_paths.append(host_path)
@@ -303,6 +310,7 @@
zip_commands.extend(
self._get_zip_test_configs_zips_commands(
+ src_top,
dist_dir,
host_out,
product_out,
@@ -311,27 +319,27 @@
)
)
- zip_command = self._base_zip_command(
- host_out, dist_dir, 'general-tests.zip'
- )
+ zip_command = self._base_zip_command(src_top, dist_dir, 'general-tests.zip')
# Add host testcases.
- zip_command.extend(
- self._generate_zip_options_for_items(
- prefix='host',
- relative_root=f'{src_top / soong_host_out}',
- directories=host_paths,
- )
- )
+ if host_paths:
+ zip_command.extend(
+ self._generate_zip_options_for_items(
+ prefix='host',
+ relative_root=f'{src_top / soong_host_out}',
+ directories=host_paths,
+ )
+ )
# Add target testcases.
- zip_command.extend(
- self._generate_zip_options_for_items(
- prefix='target',
- relative_root=f'{src_top / product_out}',
- directories=target_paths,
- )
- )
+ if target_paths:
+ zip_command.extend(
+ self._generate_zip_options_for_items(
+ prefix='target',
+ relative_root=f'{src_top / product_out}',
+ directories=target_paths,
+ )
+ )
# TODO(lucafarsi): Push this logic into a general-tests-minimal build command
# Add necessary tools. These are also hardcoded in general-tests.mk.
@@ -365,6 +373,7 @@
def _get_zip_test_configs_zips_commands(
self,
+ src_top: pathlib.Path,
dist_dir: pathlib.Path,
host_out: pathlib.Path,
product_out: pathlib.Path,
@@ -428,7 +437,7 @@
zip_commands = []
tests_config_zip_command = self._base_zip_command(
- host_out, dist_dir, 'general-tests_configs.zip'
+ src_top, dist_dir, 'general-tests_configs.zip'
)
tests_config_zip_command.extend(
self._generate_zip_options_for_items(
@@ -442,16 +451,14 @@
self._generate_zip_options_for_items(
prefix='target',
relative_root=str(product_out),
- list_files=[
- f"{product_out / 'target_general-tests_list'}"
- ],
+ list_files=[f"{product_out / 'target_general-tests_list'}"],
),
)
zip_commands.append(tests_config_zip_command)
tests_list_zip_command = self._base_zip_command(
- host_out, dist_dir, 'general-tests_list.zip'
+ src_top, dist_dir, 'general-tests_list.zip'
)
tests_list_zip_command.extend(
self._generate_zip_options_for_items(
diff --git a/ci/optimized_targets_test.py b/ci/optimized_targets_test.py
index 762b62e..0b0c0ec 100644
--- a/ci/optimized_targets_test.py
+++ b/ci/optimized_targets_test.py
@@ -220,18 +220,6 @@
):
package_commands = optimizer.get_package_outputs_commands()
- @mock.patch('subprocess.run')
- def test_no_build_outputs_packaging_fails(self, subprocess_run):
- subprocess_run.return_value = self._get_soong_vars_output()
- optimizer = self._create_general_tests_optimizer()
-
- targets = optimizer.get_build_targets()
-
- with self.assertRaisesRegex(
- RuntimeError, 'No items specified to be added to zip'
- ):
- package_commands = optimizer.get_package_outputs_commands()
-
def _create_general_tests_optimizer(self, build_context: BuildContext = None):
if not build_context:
build_context = self._create_build_context()
@@ -321,7 +309,7 @@
"""
for command in commands:
self.assertEqual(
- '/tmp/top/host_out/prebuilts/build-tools/linux-x86/bin/soong_zip',
+ '/tmp/top/prebuilts/build-tools/linux-x86/bin/soong_zip',
command[0],
)
self.assertEqual('-d', command[1])
diff --git a/core/Makefile b/core/Makefile
index b0392cd..2cdb24f 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1964,7 +1964,7 @@
installed_system_dlkm_notice_xml_gz := $(TARGET_OUT_SYSTEM_DLKM)/etc/NOTICE.xml.gz
ALL_INSTALLED_NOTICE_FILES := \
- $(installed_notice_html_or_xml_gz) \
+ $(if $(USE_SOONG_DEFINED_SYSTEM_IMAGE),,$(installed_notice_html_or_xml_gz)) \
$(installed_vendor_notice_xml_gz) \
$(installed_product_notice_xml_gz) \
$(installed_system_ext_notice_xml_gz) \
@@ -2051,7 +2051,9 @@
endif # PRODUCT_NOTICE_SPLIT
+ifneq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true)
ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz)
+endif
need_vendor_notice:=false
ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
@@ -3511,6 +3513,8 @@
--output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)"
$(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key requireLibs \
--value "$(foreach lib,$(LLNDK_MOVED_TO_APEX_LIBRARIES), $(lib).so)"
+ $(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key provideLibs \
+ --value "$(foreach lib,$(PRODUCT_EXTRA_STUB_LIBRARIES), $(lib).so)"
$(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),)
$(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE))
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 48667ac..75e9f77 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -184,3 +184,6 @@
# Add target_use_pan_display flag for hardware/libhardware:gralloc.default
$(call soong_config_set_bool,gralloc,target_use_pan_display,$(if $(filter true,$(TARGET_USE_PAN_DISPLAY)),true,false))
+
+# Add use_camera_v4l2_hal flag for hardware/libhardware/modules/camera/3_4:camera.v4l2
+$(call soong_config_set_bool,camera,use_camera_v4l2_hal,$(if $(filter true,$(USE_CAMERA_V4L2_HAL)),true,false))
diff --git a/core/combo/arch/arm64/armv9-2a.mk b/core/combo/arch/arm64/armv9-2a.mk
new file mode 100644
index 0000000..69ffde0
--- /dev/null
+++ b/core/combo/arch/arm64/armv9-2a.mk
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+# .mk file required to support build for the ARMv9.2-A arch variant.
+# The file just needs to be present, it does not need to contain anything.
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index ab2d5c1..c0f2c68 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -284,9 +284,9 @@
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
+ my_cflags += -Xclang -target-feature -Xclang +mte
+ my_ldflags += -Xclang -target-feature -Xclang +mte
+ my_asflags += -Xclang -target-feature -Xclang +mte
my_sanitize := $(filter-out memtag_stack,$(my_sanitize))
endif
diff --git a/core/envsetup.mk b/core/envsetup.mk
index c063f60..f82e861 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -417,6 +417,7 @@
HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest64
HOST_OUT_COVERAGE := $(HOST_OUT)/coverage
HOST_OUT_TESTCASES := $(HOST_OUT)/testcases
+HOST_OUT_ETC := $(HOST_OUT)/etc
.KATI_READONLY := \
HOST_OUT_EXECUTABLES \
HOST_OUT_SHARED_LIBRARIES \
@@ -425,7 +426,8 @@
HOST_OUT_SDK_ADDON \
HOST_OUT_NATIVE_TESTS \
HOST_OUT_COVERAGE \
- HOST_OUT_TESTCASES
+ HOST_OUT_TESTCASES \
+ HOST_OUT_ETC
HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT)/bin
HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib
diff --git a/core/main.mk b/core/main.mk
index 80ffec4..e5f5b9d 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -687,12 +687,12 @@
# Scan all modules in general-tests, device-tests and other selected suites and
# flatten the shared library dependencies.
define update-host-shared-libs-deps-for-suites
-$(foreach suite,general-tests device-tests vts tvts art-host-tests host-unit-tests,\
+$(foreach suite,general-tests device-tests vts tvts art-host-tests host-unit-tests camera-hal-tests,\
$(foreach m,$(COMPATIBILITY.$(suite).MODULES),\
$(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\
$(foreach dep,$(my_deps),\
$(foreach f,$(ALL_MODULES.$(dep).HOST_SHARED_LIBRARY_FILES),\
- $(if $(filter $(suite),device-tests general-tests art-host-tests host-unit-tests),\
+ $(if $(filter $(suite),device-tests general-tests art-host-tests host-unit-tests camera-hal-tests),\
$(eval my_testcases := $(HOST_OUT_TESTCASES)),\
$(eval my_testcases := $$(COMPATIBILITY_TESTCASES_OUT_$(suite))))\
$(eval target := $(my_testcases)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\
diff --git a/core/os_licensing.mk b/core/os_licensing.mk
index 1e1b7df..d15a3d0 100644
--- a/core/os_licensing.mk
+++ b/core/os_licensing.mk
@@ -17,13 +17,17 @@
$(eval $(call text-notice-rule,$(target_notice_file_txt),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))
+ifneq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true)
$(installed_notice_html_or_xml_gz): $(target_notice_file_xml_gz)
$(copy-file-to-target)
endif
+endif
$(call declare-1p-target,$(target_notice_file_xml_gz))
+ifneq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true)
$(call declare-1p-target,$(installed_notice_html_or_xml_gz))
endif
+endif
.PHONY: vendorlicense
vendorlicense: $(call corresponding-license-metadata, $(VENDOR_NOTICE_DEPS)) reportmissinglicenses
diff --git a/core/product.mk b/core/product.mk
index 4c23e5d..b07e6e0 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -499,6 +499,10 @@
# If set, build would generate system image from Soong-defined module.
_product_single_value_vars += PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE
+# List of stub libraries specific to the product that are already present in the system image and
+# should be included in the system_linker_config.
+_product_list_vars += PRODUCT_EXTRA_STUB_LIBRARIES
+
.KATI_READONLY := _product_single_value_vars _product_list_vars
_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
diff --git a/core/tasks/device-tests.mk b/core/tasks/device-tests.mk
index 5850c4e..6164c2e 100644
--- a/core/tasks/device-tests.mk
+++ b/core/tasks/device-tests.mk
@@ -14,6 +14,7 @@
.PHONY: device-tests
+.PHONY: device-tests-host-shared-libs
device-tests-zip := $(PRODUCT_OUT)/device-tests.zip
# Create an artifact to include a list of test config files in device-tests.
@@ -23,37 +24,45 @@
my_host_shared_lib_for_device_tests := $(call copy-many-files,$(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES))
device_tests_host_shared_libs_zip := $(PRODUCT_OUT)/device-tests_host-shared-libs.zip
-$(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip) $(device_tests_host_shared_libs_zip)
+$(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip)
$(device-tests-zip) : PRIVATE_device_tests_list := $(PRODUCT_OUT)/device-tests_list
$(device-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests)
-$(device-tests-zip) : PRIVATE_device_host_shared_libs_zip := $(device_tests_host_shared_libs_zip)
$(device-tests-zip) : $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(my_host_shared_lib_for_device_tests) $(SOONG_ZIP)
- rm -f $@-shared-libs.list
echo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@.list
grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true
$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
echo $$shared_lib >> $@-host.list; \
- echo $$shared_lib >> $@-shared-libs.list; \
done
- grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true
grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true
$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256
$(hide) $(SOONG_ZIP) -d -o $(device-tests-configs-zip) \
-P host -C $(HOST_OUT) -l $@-host-test-configs.list \
-P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list
- $(SOONG_ZIP) -d -o $(PRIVATE_device_host_shared_libs_zip) \
- -P host -C $(HOST_OUT) -l $@-host-shared-libs.list
rm -f $(PRIVATE_device_tests_list)
$(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_tests_list)
$(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_tests_list)
$(hide) $(SOONG_ZIP) -d -o $(device-tests-list-zip) -C $(dir $@) -f $(PRIVATE_device_tests_list)
rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \
- $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_device_tests_list)
+ $(PRIVATE_device_tests_list)
+
+$(device_tests_host_shared_libs_zip) : PRIVATE_device_host_shared_libs_zip := $(device_tests_host_shared_libs_zip)
+$(device_tests_host_shared_libs_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests)
+$(device_tests_host_shared_libs_zip) : $(my_host_shared_lib_for_device_tests) $(SOONG_ZIP)
+ rm -f $@-shared-libs.list
+ $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+ echo $$shared_lib >> $@-shared-libs.list; \
+ done
+ grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true
+ $(SOONG_ZIP) -d -o $(PRIVATE_device_host_shared_libs_zip) \
+ -P host -C $(HOST_OUT) -l $@-host-shared-libs.list
device-tests: $(device-tests-zip)
+device-tests-host-shared-libs: $(device_tests_host_shared_libs_zip)
+
$(call dist-for-goals, device-tests, $(device-tests-zip) $(device-tests-list-zip) $(device-tests-configs-zip) $(device_tests_host_shared_libs_zip))
+$(call dist-for-goals, device-tests-host-shared-libs, $(device_tests_host_shared_libs_zip))
$(call declare-1p-container,$(device-tests-zip),)
$(call declare-container-license-deps,$(device-tests-zip),$(COMPATIBILITY.device-tests.FILES) $(my_host_shared_lib_for_device_tests),$(PRODUCT_OUT)/:/)
diff --git a/core/tasks/mke2fs-dist.mk b/core/tasks/mke2fs-dist.mk
new file mode 100644
index 0000000..3540c1f
--- /dev/null
+++ b/core/tasks/mke2fs-dist.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 Google Inc.
+#
+# 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.
+
+# TODO: After Soong's recovery partition variation can be set to selectable
+# and the meta_lic file duplication issue is resolved, move it to the
+# dist section of the corresponding module's Android.bp.
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
+$(call dist-for-goals,dist_files sdk,$(my_dist_files))
+my_dist_files :=
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index d806c06..563511f 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -345,6 +345,11 @@
com.android.webview.bootstrap
endif
+ifneq (,$(RELEASE_RANGING_STACK))
+ PRODUCT_PACKAGES += \
+ com.android.ranging
+endif
+
# VINTF data for system image
PRODUCT_PACKAGES += \
system_manifest.xml \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 61d7235..668f054 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -114,6 +114,12 @@
endif
+ifneq (,$(RELEASE_RANGING_STACK))
+ PRODUCT_APEX_BOOT_JARS += \
+ com.android.uwb:framework-ranging \
+ $(call soong_config_set,bootclasspath,release_ranging_stack,true)
+endif
+
# List of system_server classpath jars delivered via apex.
# Keep the list sorted by module names and then library names.
# Note: For modules available in Q, DO NOT add new entries here.
@@ -169,6 +175,11 @@
endif
+ifneq (,$(RELEASE_RANGING_STACK))
+ PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \
+ com.android.uwb:service-ranging
+endif
+
# Overrides the (apex, jar) pairs above when determining the on-device location. The format is:
# <old_apex>:<old_jar>:<new_apex>:<new_jar>
PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES := \
diff --git a/teams/Android.bp b/teams/Android.bp
index 94585fc..0f5b475 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4440,3 +4440,10 @@
// go/trendy/manage/engineers/5097003746426880
trendy_team_id: "5097003746426880",
}
+
+team {
+ name: "trendy_team_desktop_firmware",
+
+ // go/trendy/manage/engineers/5787938454863872
+ trendy_team_id: "5787938454863872",
+}
diff --git a/tools/aconfig/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
index 2c569da..7a9c382 100644
--- a/tools/aconfig/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -283,39 +283,23 @@
virtual ~flag_provider_interface() = default;
virtual bool disabled_ro() = 0;
-
- virtual void disabled_ro(bool val) = 0;
-
virtual bool disabled_rw() = 0;
-
- virtual void disabled_rw(bool val) = 0;
-
virtual bool disabled_rw_exported() = 0;
-
- virtual void disabled_rw_exported(bool val) = 0;
-
virtual bool disabled_rw_in_other_namespace() = 0;
-
- virtual void disabled_rw_in_other_namespace(bool val) = 0;
-
virtual bool enabled_fixed_ro() = 0;
-
- virtual void enabled_fixed_ro(bool val) = 0;
-
virtual bool enabled_fixed_ro_exported() = 0;
-
- virtual void enabled_fixed_ro_exported(bool val) = 0;
-
virtual bool enabled_ro() = 0;
-
- virtual void enabled_ro(bool val) = 0;
-
virtual bool enabled_ro_exported() = 0;
-
- virtual void enabled_ro_exported(bool val) = 0;
-
virtual bool enabled_rw() = 0;
+ virtual void disabled_ro(bool val) = 0;
+ virtual void disabled_rw(bool val) = 0;
+ virtual void disabled_rw_exported(bool val) = 0;
+ virtual void disabled_rw_in_other_namespace(bool val) = 0;
+ virtual void enabled_fixed_ro(bool val) = 0;
+ virtual void enabled_fixed_ro_exported(bool val) = 0;
+ virtual void enabled_ro(bool val) = 0;
+ virtual void enabled_ro_exported(bool val) = 0;
virtual void enabled_rw(bool val) = 0;
virtual void reset_flags() {}
diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
index 6f3f7bf..7bc34d6 100644
--- a/tools/aconfig/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -116,10 +116,6 @@
use std::sync::LazyLock;
use log::{log, LevelFilter, Level};
-static STORAGE_MIGRATION_MARKER_FILE: &str =
- "/metadata/aconfig_test_missions/mission_1";
-static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
-
/// flag provider
pub struct FlagProvider;
@@ -260,13 +256,13 @@
use std::sync::LazyLock;
use log::{log, LevelFilter, Level};
-static STORAGE_MIGRATION_MARKER_FILE: &str =
- "/metadata/aconfig_test_missions/mission_1";
-static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
-
/// flag provider
pub struct FlagProvider;
+static READ_FROM_NEW_STORAGE: LazyLock<bool> = LazyLock::new(|| unsafe {
+ Path::new("/metadata/aconfig/boot/enable_only_new_storage").exists()
+});
+
static PACKAGE_OFFSET: LazyLock<Result<Option<u32>, AconfigStorageError>> = LazyLock::new(|| unsafe {
get_mapped_storage_file("system", StorageFileType::PackageMap)
.and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test"))
@@ -279,24 +275,14 @@
/// flag value cache for disabled_rw
static CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| {
- let result = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.aconfig_test",
- "com.android.aconfig.test.disabled_rw",
- "false") == "true";
-
- let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
-
- if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
+ if *READ_FROM_NEW_STORAGE {
// This will be called multiple times. Subsequent calls after the first are noops.
logger::init(
logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
+ .with_tag_on_device("aconfig_rust_codegen")
.with_max_level(LevelFilter::Info));
- let aconfig_storage_result = FLAG_VAL_MAP
+ let flag_value_result = FLAG_VAL_MAP
.as_ref()
.map_err(|err| format!("failed to get flag val map: {err}"))
.and_then(|flag_val_map| {
@@ -314,54 +300,33 @@
})
});
- match aconfig_storage_result {
- Ok(storage_result) if storage_result == result => {
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
- },
- Ok(storage_result) => {
- log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
+ match flag_value_result {
+ Ok(flag_value) => {
+ return flag_value;
},
Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: {err}");
- if use_new_storage_value {
- panic!("failed to read flag value: {err}");
- }
+ log!(Level::Error, "aconfig_rust_codegen: error: {err}");
+ panic!("failed to read flag value: {err}");
}
}
+ } else {
+ flags_rust::GetServerConfigurableFlag(
+ "aconfig_flags.aconfig_test",
+ "com.android.aconfig.test.disabled_rw",
+ "false") == "true"
}
-
- result
});
/// flag value cache for disabled_rw_exported
static CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| {
- let result = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.aconfig_test",
- "com.android.aconfig.test.disabled_rw_exported",
- "false") == "true";
-
- let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
-
- if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
+ if *READ_FROM_NEW_STORAGE {
// This will be called multiple times. Subsequent calls after the first are noops.
logger::init(
logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
+ .with_tag_on_device("aconfig_rust_codegen")
.with_max_level(LevelFilter::Info));
- let aconfig_storage_result = FLAG_VAL_MAP
+ let flag_value_result = FLAG_VAL_MAP
.as_ref()
.map_err(|err| format!("failed to get flag val map: {err}"))
.and_then(|flag_val_map| {
@@ -379,54 +344,33 @@
})
});
- match aconfig_storage_result {
- Ok(storage_result) if storage_result == result => {
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
- },
- Ok(storage_result) => {
- log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_exported'. Legacy storage was {result}, new storage was {storage_result}");
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
+ match flag_value_result {
+ Ok(flag_value) => {
+ return flag_value;
},
Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: {err}");
- if use_new_storage_value {
- panic!("failed to read flag value: {err}");
- }
+ log!(Level::Error, "aconfig_rust_codegen: error: {err}");
+ panic!("failed to read flag value: {err}");
}
}
+ } else {
+ flags_rust::GetServerConfigurableFlag(
+ "aconfig_flags.aconfig_test",
+ "com.android.aconfig.test.disabled_rw_exported",
+ "false") == "true"
}
-
- result
});
/// flag value cache for disabled_rw_in_other_namespace
static CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| {
- let result = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.other_namespace",
- "com.android.aconfig.test.disabled_rw_in_other_namespace",
- "false") == "true";
-
- let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
-
- if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
+ if *READ_FROM_NEW_STORAGE {
// This will be called multiple times. Subsequent calls after the first are noops.
logger::init(
logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
+ .with_tag_on_device("aconfig_rust_codegen")
.with_max_level(LevelFilter::Info));
- let aconfig_storage_result = FLAG_VAL_MAP
+ let flag_value_result = FLAG_VAL_MAP
.as_ref()
.map_err(|err| format!("failed to get flag val map: {err}"))
.and_then(|flag_val_map| {
@@ -444,55 +388,34 @@
})
});
- match aconfig_storage_result {
- Ok(storage_result) if storage_result == result => {
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
- },
- Ok(storage_result) => {
- log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_in_other_namespace'. Legacy storage was {result}, new storage was {storage_result}");
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
+ match flag_value_result {
+ Ok(flag_value) => {
+ return flag_value;
},
Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: {err}");
- if use_new_storage_value {
- panic!("failed to read flag value: {err}");
- }
+ log!(Level::Error, "aconfig_rust_codegen: error: {err}");
+ panic!("failed to read flag value: {err}");
}
}
+ } else {
+ flags_rust::GetServerConfigurableFlag(
+ "aconfig_flags.other_namespace",
+ "com.android.aconfig.test.disabled_rw_in_other_namespace",
+ "false") == "true"
}
-
- result
});
/// flag value cache for enabled_rw
static CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| {
- let result = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.aconfig_test",
- "com.android.aconfig.test.enabled_rw",
- "true") == "true";
-
- let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
-
- if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
+ if *READ_FROM_NEW_STORAGE {
// This will be called multiple times. Subsequent calls after the first are noops.
logger::init(
logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
+ .with_tag_on_device("aconfig_rust_codegen")
.with_max_level(LevelFilter::Info));
- let aconfig_storage_result = FLAG_VAL_MAP
+ let flag_value_result = FLAG_VAL_MAP
.as_ref()
.map_err(|err| format!("failed to get flag val map: {err}"))
.and_then(|flag_val_map| {
@@ -510,32 +433,21 @@
})
});
- match aconfig_storage_result {
- Ok(storage_result) if storage_result == result => {
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
- },
- Ok(storage_result) => {
- log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'enabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
- if use_new_storage_value {
- return storage_result;
- } else {
- return result;
- }
+ match flag_value_result {
+ Ok(flag_value) => {
+ return flag_value;
},
Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: {err}");
- if use_new_storage_value {
- panic!("failed to read flag value: {err}");
- }
+ log!(Level::Error, "aconfig_rust_codegen: error: {err}");
+ panic!("failed to read flag value: {err}");
}
}
+ } else {
+ flags_rust::GetServerConfigurableFlag(
+ "aconfig_flags.aconfig_test",
+ "com.android.aconfig.test.enabled_rw",
+ "true") == "true"
}
-
- result
});
impl FlagProvider {
@@ -596,65 +508,7 @@
/// query flag disabled_ro
#[inline(always)]
pub fn disabled_ro() -> bool {
-
-
- let result = false;
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe {
- let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
- Ok(file) => file,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
- Ok(Some(context)) => context,
- Ok(None) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': did not get context");
- return result;
- },
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
- Ok(val_map) => val_map,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, 0 + package_read_context.boolean_start_index) {
- Ok(val) => val,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
- return result;
- }
- };
-
- if result != value {
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'disabled_ro'. Legacy storage was {result}, new storage was {value}");
- } else {
- let default_value = false;
- }
- }
-
- result
-
+ false
}
/// query flag disabled_rw
@@ -678,257 +532,25 @@
/// query flag enabled_fixed_ro
#[inline(always)]
pub fn enabled_fixed_ro() -> bool {
-
-
- let result = true;
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe {
- let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
- Ok(file) => file,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
- Ok(Some(context)) => context,
- Ok(None) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': did not get context");
- return result;
- },
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
- Ok(val_map) => val_map,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, 4 + package_read_context.boolean_start_index) {
- Ok(val) => val,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
- return result;
- }
- };
-
- if result != value {
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro'. Legacy storage was {result}, new storage was {value}");
- } else {
- let default_value = true;
- }
- }
-
- result
-
+ true
}
/// query flag enabled_fixed_ro_exported
#[inline(always)]
pub fn enabled_fixed_ro_exported() -> bool {
-
-
- let result = true;
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe {
- let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
- Ok(file) => file,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
- Ok(Some(context)) => context,
- Ok(None) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': did not get context");
- return result;
- },
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
- Ok(val_map) => val_map,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, 5 + package_read_context.boolean_start_index) {
- Ok(val) => val,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
- return result;
- }
- };
-
- if result != value {
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro_exported'. Legacy storage was {result}, new storage was {value}");
- } else {
- let default_value = true;
- }
- }
-
- result
-
+ true
}
/// query flag enabled_ro
#[inline(always)]
pub fn enabled_ro() -> bool {
-
-
- let result = true;
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe {
- let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
- Ok(file) => file,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
- Ok(Some(context)) => context,
- Ok(None) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': did not get context");
- return result;
- },
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
- Ok(val_map) => val_map,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, 6 + package_read_context.boolean_start_index) {
- Ok(val) => val,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
- return result;
- }
- };
-
- if result != value {
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro'. Legacy storage was {result}, new storage was {value}");
- } else {
- let default_value = true;
- }
- }
-
- result
-
+ true
}
/// query flag enabled_ro_exported
#[inline(always)]
pub fn enabled_ro_exported() -> bool {
-
-
- let result = true;
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe {
- let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
- Ok(file) => file,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
- Ok(Some(context)) => context,
- Ok(None) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': did not get context");
- return result;
- },
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
- Ok(val_map) => val_map,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, 7 + package_read_context.boolean_start_index) {
- Ok(val) => val,
- Err(err) => {
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
- return result;
- }
- };
-
- if result != value {
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro_exported'. Legacy storage was {result}, new storage was {value}");
- } else {
- let default_value = true;
- }
- }
-
- result
-
+ true
}
/// query flag enabled_rw
@@ -1203,10 +825,6 @@
use std::sync::LazyLock;
use log::{log, LevelFilter, Level};
-static STORAGE_MIGRATION_MARKER_FILE: &str =
- "/metadata/aconfig_test_missions/mission_1";
-static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
-
/// flag provider
pub struct FlagProvider;
@@ -1275,10 +893,6 @@
use std::sync::LazyLock;
use log::{log, LevelFilter, Level};
-static STORAGE_MIGRATION_MARKER_FILE: &str =
- "/metadata/aconfig_test_missions/mission_1";
-static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
-
/// flag provider
pub struct FlagProvider;
diff --git a/tools/aconfig/aconfig/templates/cpp_exported_header.template b/tools/aconfig/aconfig/templates/cpp_exported_header.template
index 0f7853e..4643c97 100644
--- a/tools/aconfig/aconfig/templates/cpp_exported_header.template
+++ b/tools/aconfig/aconfig/templates/cpp_exported_header.template
@@ -27,12 +27,13 @@
{{ -for item in class_elements}}
virtual bool {item.flag_name}() = 0;
- {{ -if is_test_mode }}
- virtual void {item.flag_name}(bool val) = 0;
- {{ -endif }}
{{ -endfor }}
{{ -if is_test_mode }}
+ {{ -for item in class_elements}}
+ virtual void {item.flag_name}(bool val) = 0;
+ {{ -endfor }}
+
virtual void reset_flags() \{}
{{ -endif }}
};
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index b6012e7..852b905 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -2,11 +2,11 @@
{{ if allow_instrumentation }}
{{ if readwrite- }}
-#include <sys/stat.h>
+#include <unistd.h>
#include "aconfig_storage/aconfig_storage_read_api.hpp"
#include <android/log.h>
#define LOG_TAG "aconfig_cpp_codegen"
-#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
{{ -endif }}
{{ endif }}
@@ -76,13 +76,13 @@
: boolean_start_index_()
{{ -endif }}
, flag_value_file_(nullptr)
- , read_from_new_storage_(false)
- , use_new_storage_value(false) \{
+ , read_from_new_storage_(false) \{
- struct stat buffer;
- if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) == 0) \{
+ if (access("/metadata/aconfig/boot/enable_only_new_storage", F_OK) == 0) \{
read_from_new_storage_ = true;
- } else \{
+ }
+
+ if (!read_from_new_storage_) \{
return;
}
@@ -90,15 +90,13 @@
"{container}",
aconfig_storage::StorageFileType::package_map);
if (!package_map_file.ok()) \{
- ALOGI("error: failed to get package map file: %s", package_map_file.error().c_str());
- return;
+ ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str());
}
auto context = aconfig_storage::get_package_read_context(
**package_map_file, "{package}");
if (!context.ok()) \{
- ALOGI("error: failed to get package read context: %s", context.error().c_str());
- return;
+ ALOGE("error: failed to get package read context: %s", context.error().c_str());
}
// cache package boolean flag start index
@@ -111,18 +109,13 @@
"{container}",
aconfig_storage::StorageFileType::flag_val);
if (!flag_value_file.ok()) \{
- ALOGI("error: failed to get flag value file: %s", flag_value_file.error().c_str());
- return;
+ ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str());
}
// cache flag value file
flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(
*flag_value_file);
- use_new_storage_value = server_configurable_flags::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
}
{{ -endif }}
{{ -endif }}
@@ -131,44 +124,30 @@
virtual bool {item.flag_name}() override \{
{{ -if item.readwrite }}
if (cache_[{item.readwrite_idx}] == -1) \{
+ {{ if allow_instrumentation- }}
+ if (read_from_new_storage_) \{
+ auto value = aconfig_storage::get_boolean_flag_value(
+ *flag_value_file_,
+ boolean_start_index_ + {item.flag_offset});
+
+ if (!value.ok()) \{
+ ALOGE("error: failed to read flag value: %s", value.error().c_str());
+ }
+
+ cache_[{item.readwrite_idx}] = *value;
+ } else \{
+ cache_[{item.readwrite_idx}] = server_configurable_flags::GetServerConfigurableFlag(
+ "aconfig_flags.{item.device_config_namespace}",
+ "{item.device_config_flag}",
+ "{item.default_value}") == "true";
+ }
+ {{ -else- }}
cache_[{item.readwrite_idx}] = server_configurable_flags::GetServerConfigurableFlag(
"aconfig_flags.{item.device_config_namespace}",
"{item.device_config_flag}",
"{item.default_value}") == "true";
- }
-
-
- {{ if allow_instrumentation- }}
- if (read_from_new_storage_) \{
- if (!flag_value_file_) \{
- ALOGI("error: failed to get flag {item.flag_name}: flag value file is null");
- return cache_[{item.readwrite_idx}];
- }
-
- auto value = aconfig_storage::get_boolean_flag_value(
- *flag_value_file_,
- boolean_start_index_ + {item.flag_offset});
-
- if (!value.ok()) \{
- ALOGI("error: failed to read flag value: %s", value.error().c_str());
- return cache_[{item.readwrite_idx}];
- }
-
- bool expected_value = cache_[{item.readwrite_idx}];
- if (*value != expected_value) \{
- ALOGI("error: {item.flag_name} value mismatch, new storage value is %s, old storage value is %s",
- *value ? "true" : "false", expected_value ? "true" : "false");
- }
-
- if (use_new_storage_value) \{
- return *value;
- } else \{
- return expected_value;
- }
- }
{{ -endif }}
-
-
+ }
return cache_[{item.readwrite_idx}];
{{ -else }}
{{ -if item.is_fixed_read_only }}
@@ -189,7 +168,6 @@
std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;
bool read_from_new_storage_;
- bool use_new_storage_value;
{{ -endif }}
{{ -endif }}
diff --git a/tools/aconfig/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template
index ea1c600..c2f162f 100644
--- a/tools/aconfig/aconfig/templates/rust.template
+++ b/tools/aconfig/aconfig/templates/rust.template
@@ -5,15 +5,15 @@
use std::sync::LazyLock;
use log::\{log, LevelFilter, Level};
-static STORAGE_MIGRATION_MARKER_FILE: &str =
- "/metadata/aconfig_test_missions/mission_1";
-static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
-
/// flag provider
pub struct FlagProvider;
{{ if has_readwrite- }}
{{ if allow_instrumentation }}
+static READ_FROM_NEW_STORAGE: LazyLock<bool> = LazyLock::new(|| unsafe \{
+ Path::new("/metadata/aconfig/boot/enable_only_new_storage").exists()
+});
+
static PACKAGE_OFFSET: LazyLock<Result<Option<u32>, AconfigStorageError>> = LazyLock::new(|| unsafe \{
get_mapped_storage_file("{container}", StorageFileType::PackageMap)
.and_then(|package_map| get_package_read_context(&package_map, "{package}"))
@@ -30,24 +30,15 @@
/// flag value cache for {flag.name}
{{ if allow_instrumentation }}
static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \{
- let result = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.{flag.device_config_namespace}",
- "{flag.device_config_flag}",
- "{flag.default_value}") == "true";
- let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
- "aconfig_flags.core_experiments_team_internal",
- "com.android.providers.settings.use_new_storage_value",
- "false") == "true";
-
- if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{
+ if *READ_FROM_NEW_STORAGE \{
// This will be called multiple times. Subsequent calls after the first are noops.
logger::init(
logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
+ .with_tag_on_device("aconfig_rust_codegen")
.with_max_level(LevelFilter::Info));
- let aconfig_storage_result = FLAG_VAL_MAP
+ let flag_value_result = FLAG_VAL_MAP
.as_ref()
.map_err(|err| format!("failed to get flag val map: \{err}"))
.and_then(|flag_val_map| \{
@@ -65,33 +56,23 @@
})
});
- match aconfig_storage_result \{
- Ok(storage_result) if storage_result == result => \{
- if use_new_storage_value \{
- return storage_result;
- } else \{
- return result;
- }
- },
- Ok(storage_result) => \{
- log!(Level::Error, "AconfigTestMission1: error: mismatch for flag '{flag.name}'. Legacy storage was \{result}, new storage was \{storage_result}");
- if use_new_storage_value \{
- return storage_result;
- } else \{
- return result;
- }
+ match flag_value_result \{
+ Ok(flag_value) => \{
+ return flag_value;
},
Err(err) => \{
- log!(Level::Error, "AconfigTestMission1: error: \{err}");
- if use_new_storage_value \{
- panic!("failed to read flag value: \{err}");
- }
+ log!(Level::Error, "aconfig_rust_codegen: error: \{err}");
+ panic!("failed to read flag value: \{err}");
}
}
+ } else \{
+ flags_rust::GetServerConfigurableFlag(
+ "aconfig_flags.{flag.device_config_namespace}",
+ "{flag.device_config_flag}",
+ "{flag.default_value}") == "true"
}
- result
- });
+});
{{ else }}
static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| flags_rust::GetServerConfigurableFlag(
"aconfig_flags.{flag.device_config_namespace}",
@@ -123,72 +104,11 @@
{{ for flag in template_flags }}
/// query flag {flag.name}
#[inline(always)]
-{{ -if flag.readwrite }}
pub fn {flag.name}() -> bool \{
+{{ -if flag.readwrite }}
PROVIDER.{flag.name}()
{{ -else }}
-pub fn {flag.name}() -> bool \{
- {{ if not allow_instrumentation }}
{flag.default_value}
- {{ else }}
-
- let result = {flag.default_value};
- if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{
- return result;
- }
-
- // This will be called multiple times. Subsequent calls after the first
- // are noops.
- logger::init(
- logger::Config::default()
- .with_tag_on_device(MIGRATION_LOG_TAG)
- .with_max_level(LevelFilter::Info),
- );
-
- unsafe \{
- let package_map = match get_mapped_storage_file("{flag.container}", StorageFileType::PackageMap) \{
- Ok(file) => file,
- Err(err) => \{
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
- return result;
- }
- };
-
- let package_read_context = match get_package_read_context(&package_map, "{package}") \{
- Ok(Some(context)) => context,
- Ok(None) => \{
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': did not get context");
- return result;
- },
- Err(err) => \{
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
- return result;
- }
- };
- let flag_val_map = match get_mapped_storage_file("{flag.container}", StorageFileType::FlagVal) \{
- Ok(val_map) => val_map,
- Err(err) => \{
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
- return result;
- }
- };
- let value = match get_boolean_flag_value(&flag_val_map, {flag.flag_offset} + package_read_context.boolean_start_index) \{
- Ok(val) => val,
- Err(err) => \{
- log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
- return result;
- }
- };
-
- if result != value \{
- log!(Level::Error, "AconfigTestMission1: error: flag mismatch for '{flag.name}'. Legacy storage was \{result}, new storage was \{value}");
- } else \{
- let default_value = {flag.default_value};
- }
- }
-
- result
- {{ endif }}
{{ -endif }}
}
{{ endfor }}
diff --git a/tools/aconfig/aconfig_device_paths/Android.bp b/tools/aconfig/aconfig_device_paths/Android.bp
index 932dfbf..dda7a55 100644
--- a/tools/aconfig/aconfig_device_paths/Android.bp
+++ b/tools/aconfig/aconfig_device_paths/Android.bp
@@ -56,3 +56,16 @@
"//apex_available:platform",
],
}
+
+genrule {
+ name: "libaconfig_java_host_device_paths_src",
+ srcs: ["src/HostDeviceProtosTemplate.java"],
+ out: ["HostDeviceProtos.java"],
+ tool_files: ["partition_aconfig_flags_paths.txt"],
+ cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)",
+}
+
+java_library_host {
+ name: "aconfig_host_device_paths_java",
+ srcs: [":libaconfig_java_host_device_paths_src"],
+}
diff --git a/tools/aconfig/aconfig_device_paths/src/HostDeviceProtosTemplate.java b/tools/aconfig/aconfig_device_paths/src/HostDeviceProtosTemplate.java
new file mode 100644
index 0000000..844232b
--- /dev/null
+++ b/tools/aconfig/aconfig_device_paths/src/HostDeviceProtosTemplate.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package android.aconfig;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A host lib that can read all aconfig proto file paths on a given device.
+ */
+public class HostDeviceProtos {
+ /**
+ * An interface that executes ADB command and return the result.
+ */
+ public static interface AdbCommandExecutor {
+ /** Executes the ADB command. */
+ String executeAdbCommand(String command);
+ }
+
+ static final String[] PATHS = {
+ TEMPLATE
+ };
+
+ private static final String APEX_DIR = "/apex";
+ private static final String RECURSIVELY_LIST_APEX_DIR_COMMAND = "shell find /apex | grep aconfig_flags";
+ private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
+
+
+ /**
+ * Returns the list of all on-device aconfig proto paths from host side.
+ */
+ public static List<String> parsedFlagsProtoPaths(AdbCommandExecutor adbCommandExecutor) {
+ ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));
+
+ String adbCommandOutput = adbCommandExecutor.executeAdbCommand(
+ RECURSIVELY_LIST_APEX_DIR_COMMAND);
+
+ if (adbCommandOutput == null) {
+ return paths;
+ }
+
+ Set<String> allFiles = new HashSet<>(Arrays.asList(adbCommandOutput.split("\n")));
+
+ Set<String> subdirs = allFiles.stream().map(file -> {
+ String[] filePaths = file.split("/");
+ // The first element is "", the second element is "apex".
+ return filePaths.length > 2 ? filePaths[2] : "";
+ }).collect(Collectors.toSet());
+
+ for (String prefix : subdirs) {
+ // For each mainline modules, there are two directories, one <modulepackage>/,
+ // and one <modulepackage>@<versioncode>/. Just read the former.
+ if (prefix.contains("@")) {
+ continue;
+ }
+
+ String protoPath = APEX_DIR + "/" + prefix + APEX_ACONFIG_PATH_SUFFIX;
+ if (allFiles.contains(protoPath)) {
+ paths.add(protoPath);
+ }
+ }
+ return paths;
+ }
+}
diff --git a/tools/edit_monitor/Android.bp b/tools/edit_monitor/Android.bp
new file mode 100644
index 0000000..b939633
--- /dev/null
+++ b/tools/edit_monitor/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Set of error prone rules to ensure code quality
+// PackageLocation check requires the androidCompatible=false otherwise it does not do anything.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_adte",
+}
+
+python_library_host {
+ name: "edit_monitor_lib",
+ pkg_path: "edit_monitor",
+ srcs: [
+ "daemon_manager.py",
+ ],
+}
+
+python_test_host {
+ name: "daemon_manager_test",
+ main: "daemon_manager_test.py",
+ pkg_path: "edit_monitor",
+ srcs: [
+ "daemon_manager_test.py",
+ ],
+ libs: [
+ "edit_monitor_lib",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/tools/edit_monitor/OWNERS b/tools/edit_monitor/OWNERS
new file mode 100644
index 0000000..8f0f364
--- /dev/null
+++ b/tools/edit_monitor/OWNERS
@@ -0,0 +1 @@
+include platform/tools/asuite:/OWNERS_ADTE_TEAM
\ No newline at end of file
diff --git a/tools/edit_monitor/daemon_manager.py b/tools/edit_monitor/daemon_manager.py
new file mode 100644
index 0000000..c09c321
--- /dev/null
+++ b/tools/edit_monitor/daemon_manager.py
@@ -0,0 +1,165 @@
+# 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 hashlib
+import logging
+import multiprocessing
+import os
+import pathlib
+import signal
+import subprocess
+import tempfile
+import time
+
+
+DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 1
+
+
+def default_daemon_target():
+ """Place holder for the default daemon target."""
+ print("default daemon target")
+
+
+class DaemonManager:
+ """Class to manage and monitor the daemon run as a subprocess."""
+
+ def __init__(
+ self,
+ binary_path: str,
+ daemon_target: callable = default_daemon_target,
+ daemon_args: tuple = (),
+ ):
+ self.binary_path = binary_path
+ self.daemon_target = daemon_target
+ self.daemon_args = daemon_args
+
+ self.pid = os.getpid()
+ self.daemon_process = None
+
+ pid_file_dir = pathlib.Path(tempfile.gettempdir()).joinpath("edit_monitor")
+ pid_file_dir.mkdir(parents=True, exist_ok=True)
+ self.pid_file_path = self._get_pid_file_path(pid_file_dir)
+
+ def start(self):
+ """Writes the pidfile and starts the daemon proces."""
+ try:
+ self._write_pid_to_pidfile()
+ self._start_daemon_process()
+ except Exception as e:
+ logging.exception("Failed to start daemon manager with error %s", e)
+
+ def stop(self):
+ """Stops the daemon process and removes the pidfile."""
+
+ logging.debug("in daemon manager cleanup.")
+ try:
+ if self.daemon_process and self.daemon_process.is_alive():
+ self._terminate_process(self.daemon_process.pid)
+ self._remove_pidfile()
+ except Exception as e:
+ logging.exception("Failed to stop daemon manager with error %s", e)
+
+ def _write_pid_to_pidfile(self):
+ """Creates a pidfile and writes the current pid to the file.
+
+ Raise FileExistsError if the pidfile already exists.
+ """
+ try:
+ # Use the 'x' mode to open the file for exclusive creation
+ with open(self.pid_file_path, "x") as f:
+ f.write(f"{self.pid}")
+ except FileExistsError as e:
+ # This could be caused due to race condition that a user is trying
+ # to start two edit monitors at the same time. Or because there is
+ # already an existing edit monitor running and we can not kill it
+ # for some reason.
+ logging.exception("pidfile %s already exists.", self.pid_file_path)
+ raise e
+
+ def _start_daemon_process(self):
+ """Starts a subprocess to run the daemon."""
+ p = multiprocessing.Process(
+ target=self.daemon_target, args=self.daemon_args
+ )
+ p.start()
+
+ logging.info("Start subprocess with PID %d", p.pid)
+ self.daemon_process = p
+
+ def _terminate_process(
+ self, pid: int, timeout: int = DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS
+ ):
+ """Terminates a process with given pid.
+
+ It first sends a SIGTERM to the process to allow it for proper
+ termination with a timeout. If the process is not terminated within
+ the timeout, kills it forcefully.
+ """
+ try:
+ os.kill(pid, signal.SIGTERM)
+ if not self._wait_for_process_terminate(pid, timeout):
+ logging.warning(
+ "Process %d not terminated within timeout, try force kill", pid
+ )
+ os.kill(pid, signal.SIGKILL)
+ except ProcessLookupError:
+ logging.info("Process with PID %d not found (already terminated)", pid)
+
+ def _wait_for_process_terminate(self, pid: int, timeout: int) -> bool:
+ start_time = time.time()
+
+ while time.time() < start_time + timeout:
+ if not self._is_process_alive(pid):
+ return True
+ time.sleep(1)
+
+ logging.error("Process %d not terminated within %d seconds.", pid, timeout)
+ return False
+
+ def _is_process_alive(self, pid: int) -> bool:
+ try:
+ output = subprocess.check_output(
+ ["ps", "-p", str(pid), "-o", "state="], text=True
+ ).strip()
+ state = output.split()[0]
+ return state != "Z" # Check if the state is not 'Z' (zombie)
+ except subprocess.CalledProcessError:
+ # Process not found (already dead).
+ return False
+ except (FileNotFoundError, OSError, ValueError) as e:
+ logging.warning(
+ "Unable to check the status for process %d with error: %s.", pid, e
+ )
+ return True
+
+ def _remove_pidfile(self):
+ try:
+ os.remove(self.pid_file_path)
+ except FileNotFoundError:
+ logging.info("pid file %s already removed.", self.pid_file_path)
+
+ def _get_pid_file_path(self, pid_file_dir: pathlib.Path) -> pathlib.Path:
+ """Generates the path to store the pidfile.
+
+ The file path should have the format of "/tmp/edit_monitor/xxxx.lock"
+ where xxxx is a hashed value based on the binary path that starts the
+ process.
+ """
+ hash_object = hashlib.sha256()
+ hash_object.update(self.binary_path.encode("utf-8"))
+ pid_file_path = pid_file_dir.joinpath(hash_object.hexdigest() + ".lock")
+ logging.info("pid_file_path: %s", pid_file_path)
+
+ return pid_file_path
diff --git a/tools/edit_monitor/daemon_manager_test.py b/tools/edit_monitor/daemon_manager_test.py
new file mode 100644
index 0000000..5be4bee
--- /dev/null
+++ b/tools/edit_monitor/daemon_manager_test.py
@@ -0,0 +1,197 @@
+# 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.
+
+"""Unittests for DaemonManager."""
+
+import logging
+import multiprocessing
+import os
+import pathlib
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import unittest
+from unittest import mock
+from edit_monitor import daemon_manager
+
+TEST_BINARY_FILE = '/path/to/test_binary'
+TEST_PID_FILE_PATH = (
+ '587239c2d1050afdf54512e2d799f3b929f86b43575eb3c7b4bab105dd9bd25e.lock'
+)
+
+
+def example_daemon(output_file):
+ with open(output_file, 'w') as f:
+ f.write('running daemon target')
+
+
+def long_running_daemon():
+ while True:
+ time.sleep(1)
+
+
+class DaemonManagerTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ # Configure to print logging to stdout.
+ logging.basicConfig(filename=None, level=logging.DEBUG)
+ console = logging.StreamHandler(sys.stdout)
+ logging.getLogger('').addHandler(console)
+
+ def setUp(self):
+ super().setUp()
+ self.original_tempdir = tempfile.tempdir
+ self.working_dir = tempfile.TemporaryDirectory()
+ # Sets the tempdir under the working dir so any temp files created during
+ # tests will be cleaned.
+ tempfile.tempdir = self.working_dir.name
+
+ def tearDown(self):
+ # Cleans up any child processes left by the tests.
+ self._cleanup_child_processes()
+ self.working_dir.cleanup()
+ # Restores tempdir.
+ tempfile.tempdir = self.original_tempdir
+ super().tearDown()
+
+ def test_start_success(self):
+ damone_output_file = tempfile.NamedTemporaryFile(
+ dir=self.working_dir.name, delete=False
+ )
+ dm = daemon_manager.DaemonManager(
+ TEST_BINARY_FILE,
+ daemon_target=example_daemon,
+ daemon_args=(damone_output_file.name,),
+ )
+ dm.start()
+ dm.daemon_process.join()
+
+ # Verifies the expected pid file is created.
+ expected_pid_file_path = pathlib.Path(self.working_dir.name).joinpath(
+ 'edit_monitor', TEST_PID_FILE_PATH
+ )
+ self.assertEqual(dm.pid_file_path, expected_pid_file_path)
+ self.assertTrue(expected_pid_file_path.exists())
+
+ # Verifies the daemon process is executed successfully.
+ with open(damone_output_file.name, 'r') as f:
+ contents = f.read()
+ self.assertEqual(contents, 'running daemon target')
+
+ def test_start_failed_to_write_pidfile(self):
+ pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(
+ 'edit_monitor'
+ )
+ pid_file_path_dir.mkdir(parents=True, exist_ok=True)
+ # Makes the directory read-only so write pidfile will fail.
+ os.chmod(pid_file_path_dir, 0o555)
+
+ dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
+ dm.start()
+
+ # Verifies no daemon process is started.
+ self.assertIsNone(dm.daemon_process)
+
+ def test_start_failed_to_start_daemon_process(self):
+ dm = daemon_manager.DaemonManager(
+ TEST_BINARY_FILE, daemon_target='wrong_target', daemon_args=(1)
+ )
+ dm.start()
+
+ # Verifies no daemon process is started.
+ self.assertIsNone(dm.daemon_process)
+
+ def test_stop_success(self):
+ dm = daemon_manager.DaemonManager(
+ TEST_BINARY_FILE, daemon_target=long_running_daemon
+ )
+ dm.start()
+ dm.stop()
+
+ self.assert_no_subprocess_running()
+ self.assertFalse(dm.pid_file_path.exists())
+
+ @mock.patch('os.kill')
+ def test_stop_failed_to_kill_daemon_process(self, mock_kill):
+ mock_kill.side_effect = OSError('Unknown OSError')
+ dm = daemon_manager.DaemonManager(
+ TEST_BINARY_FILE, daemon_target=long_running_daemon
+ )
+ dm.start()
+ dm.stop()
+
+ self.assertTrue(dm.daemon_process.is_alive())
+ self.assertTrue(dm.pid_file_path.exists())
+
+ @mock.patch('os.remove')
+ def test_stop_failed_to_remove_pidfile(self, mock_remove):
+ mock_remove.side_effect = OSError('Unknown OSError')
+
+ dm = daemon_manager.DaemonManager(
+ TEST_BINARY_FILE, daemon_target=long_running_daemon
+ )
+ dm.start()
+ dm.stop()
+
+ self.assert_no_subprocess_running()
+ self.assertTrue(dm.pid_file_path.exists())
+
+ def assert_no_subprocess_running(self):
+ child_pids = self._get_child_processes(os.getpid())
+ for child_pid in child_pids:
+ self.assertFalse(
+ self._is_process_alive(child_pid), f'process {child_pid} still alive'
+ )
+
+ def _get_child_processes(self, parent_pid):
+ try:
+ output = subprocess.check_output(
+ ['ps', '-o', 'pid,ppid', '--no-headers'], text=True
+ )
+
+ child_processes = []
+ for line in output.splitlines():
+ pid, ppid = line.split()
+ if int(ppid) == parent_pid:
+ child_processes.append(int(pid))
+ return child_processes
+ except subprocess.CalledProcessError as e:
+ self.fail(f'failed to get child process, error: {e}')
+
+ def _is_process_alive(self, pid):
+ try:
+ output = subprocess.check_output(
+ ['ps', '-p', str(pid), '-o', 'state='], text=True
+ ).strip()
+ state = output.split()[0]
+ return state != 'Z' # Check if the state is not 'Z' (zombie)
+ except subprocess.CalledProcessError:
+ return False
+
+ def _cleanup_child_processes(self):
+ child_pids = self._get_child_processes(os.getpid())
+ for child_pid in child_pids:
+ try:
+ os.kill(child_pid, signal.SIGKILL)
+ except ProcessLookupError:
+ # process already terminated
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/filelistdiff/OWNERS b/tools/filelistdiff/OWNERS
new file mode 100644
index 0000000..690fb17
--- /dev/null
+++ b/tools/filelistdiff/OWNERS
@@ -0,0 +1 @@
+per-file allowlist = justinyun@google.com, jeongik@google.com, kiyoungkim@google.com, inseob@google.com
diff --git a/tools/filelistdiff/allowlist b/tools/filelistdiff/allowlist
index c4a464d..120045e 100644
--- a/tools/filelistdiff/allowlist
+++ b/tools/filelistdiff/allowlist
@@ -43,7 +43,9 @@
etc/aconfig/package.map
etc/bpf/uprobestats/BitmapAllocation.o
etc/bpf/uprobestats/GenericInstrumentation.o
+etc/bpf/uprobestats/ProcessManagement.o
etc/init/UprobeStats.rc
lib/libuprobestats_client.so
lib64/libuprobestats_client.so
-priv-app/DeviceDiagnostics/DeviceDiagnostics.apk
\ No newline at end of file
+priv-app/DeviceDiagnostics/DeviceDiagnostics.apk
+
diff --git a/tools/releasetools/ota_from_raw_img.py b/tools/releasetools/ota_from_raw_img.py
index 03b44f1..3b9374a 100644
--- a/tools/releasetools/ota_from_raw_img.py
+++ b/tools/releasetools/ota_from_raw_img.py
@@ -105,9 +105,6 @@
if args.package_key:
logger.info("Signing payload...")
- # TODO: remove OPTIONS when no longer used as fallback in payload_signer
- common.OPTIONS.payload_signer_args = None
- common.OPTIONS.payload_signer_maximum_signature_size = None
signer = PayloadSigner(args.package_key, args.private_key_suffix,
key_passwords[args.package_key],
payload_signer=args.payload_signer,
diff --git a/tools/sbom/Android.bp b/tools/sbom/Android.bp
index 6901b06..74b3d62 100644
--- a/tools/sbom/Android.bp
+++ b/tools/sbom/Android.bp
@@ -109,3 +109,17 @@
"sbom_lib",
],
}
+
+python_binary_host {
+ name: "gen_notice_xml",
+ srcs: [
+ "gen_notice_xml.py",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+ libs: [
+ ],
+}
diff --git a/tools/sbom/gen_notice_xml.py b/tools/sbom/gen_notice_xml.py
new file mode 100644
index 0000000..eaa6e5a
--- /dev/null
+++ b/tools/sbom/gen_notice_xml.py
@@ -0,0 +1,81 @@
+# !/usr/bin/env python3
+#
+# 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.
+
+"""
+Generate NOTICE.xml.gz of a partition.
+Usage example:
+ gen_notice_xml.py --output_file out/soong/.intermediate/.../NOTICE.xml.gz \
+ --metadata out/soong/compliance-metadata/aosp_cf_x86_64_phone/compliance-metadata.db \
+ --partition system \
+ --product_out out/target/vsoc_x86_64 \
+ --soong_out out/soong
+"""
+
+import argparse
+
+
+FILE_HEADER = '''\
+<?xml version="1.0" encoding="utf-8"?>
+<licenses>
+'''
+FILE_FOOTER = '''\
+</licenses>
+'''
+
+
+def get_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')
+ parser.add_argument('-d', '--debug', action='store_true', default=True, help='Debug mode')
+ parser.add_argument('--output_file', required=True, help='The path of the generated NOTICE.xml.gz file.')
+ parser.add_argument('--partition', required=True, help='The name of partition for which the NOTICE.xml.gz is generated.')
+ parser.add_argument('--metadata', required=True, help='The path of compliance metadata DB file.')
+ parser.add_argument('--product_out', required=True, help='The path of PRODUCT_OUT, e.g. out/target/product/vsoc_x86_64.')
+ parser.add_argument('--soong_out', required=True, help='The path of Soong output directory, e.g. out/soong')
+
+ return parser.parse_args()
+
+
+def log(*info):
+ if args.verbose:
+ for i in info:
+ print(i)
+
+
+def new_file_name_tag(file_metadata, package_name):
+ file_path = file_metadata['installed_file'].removeprefix(args.product_out)
+ lib = 'Android'
+ if package_name:
+ lib = package_name
+ return f'<file-name contentId="" lib="{lib}">{file_path}</file-name>\n'
+
+
+def new_file_content_tag():
+ pass
+
+
+def main():
+ global args
+ args = get_args()
+ log('Args:', vars(args))
+
+ with open(args.output_file, 'w', encoding="utf-8") as notice_xml_file:
+ notice_xml_file.write(FILE_HEADER)
+ notice_xml_file.write(FILE_FOOTER)
+
+
+if __name__ == '__main__':
+ main()