diff options
author | Xin Li <delphij@google.com> | 2024-01-29 21:19:33 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-01-29 21:19:33 -0800 |
commit | 5da47bce8c0c1d22fb4801b5f773554df03c3e05 (patch) | |
tree | 901d28f60bbc5213a61abb55a527906eb6d7bb46 | |
parent | 76f16eedeb439084dcd71a8ae1132b77109949f7 (diff) | |
parent | dd6812f5375184cafc2834da6d5e4208b2f821ca (diff) | |
download | chre-5da47bce8c0c1d22fb4801b5f773554df03c3e05.tar.gz |
Merge Android 24Q1 Release (ab/11220357)
Bug: 319669529
Merged-In: I12e0fbad4cb34fa11eaf6e022ea61b59c4a0d7d3
Change-Id: If5c6f78ee9a08a1caae56c56426d329b0ec3b5ce
351 files changed, 25469 insertions, 6667 deletions
@@ -18,20 +18,6 @@ package { default_applicable_licenses: ["system_chre_license"], } -// Added automatically by a large-scale-change that took the approach of -// 'apply every license found to every target'. While this makes sure we respect -// every license restriction, it may not be entirely correct. -// -// e.g. GPL in an MIT project might only apply to the contrib/ directory. -// -// Please consider splitting the single license below into multiple licenses, -// taking care not to lose any license_kind information, and overriding the -// default license using the 'licenses: [...]' property on targets as needed. -// -// For unused files, consider creating a 'fileGroup' with "//visibility:private" -// to attach the license to, and including a comment whether the files may be -// used in the current project. -// See: http://go/android-license-faq license { name: "system_chre_license", visibility: [":__subpackages__"], @@ -45,22 +31,36 @@ license { } filegroup { - name: "contexthub_generic_aidl_hal_core", + name: "contexthub_hal_socket", + srcs: ["host/common/socket_server.cc"] +} + +filegroup { + name: "contexthub_hal_wifi", + srcs: ["host/common/wifi_ext_hal_handler.cc"] +} + +filegroup { + name: "contexthub_hal_core", srcs: [ - "host/common/preloaded_nanoapp_loader.cc", - "host/common/time_syncer.cc", + "host/common/bt_snoop_log_parser.cc", "host/common/config_util.cc", + "host/common/log.cc", "host/common/log_message_parser.cc", - "host/common/bt_snoop_log_parser.cc", - "host/hal_generic/common/permissions_util.cc", + "host/common/preloaded_nanoapp_loader.cc", + "host/common/time_syncer.cc", "host/hal_generic/common/hal_client_manager.cc", "host/hal_generic/common/multi_client_context_hub_base.cc", + "host/hal_generic/common/permissions_util.cc", ], } cc_library_static { name: "chre_client", vendor_available: true, + local_include_dirs: [ + "chre_api/include/chre_api", + ], export_include_dirs: [ "host/common/include", "platform/shared/include", @@ -69,17 +69,55 @@ cc_library_static { srcs: [ "host/common/file_stream.cc", "host/common/fragmented_load_transaction.cc", + "host/common/hal_client.cc", "host/common/host_protocol_host.cc", + "host/common/log.cc", + "host/common/pigweed/hal_channel_output.cc", + "host/common/pigweed/hal_rpc_client.cc", "host/common/socket_client.cc", "platform/shared/host_protocol_common.cc", ], header_libs: ["chre_flatbuffers"], export_header_lib_headers: ["chre_flatbuffers"], shared_libs: [ + "android.hardware.contexthub-V3-ndk", + "libbinder_ndk", "libcutils", "liblog", "libutils", ], + cflags: [ + "-DCHRE_IS_HOST_BUILD", + "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4000", // Needed to import CHRE APIs. + "-Wall", + "-Werror", + "-Wthread-safety", // Need to be explicitly set + ], + defaults: [ + "pw_rpc_cflags_chre", + "pw_rpc_nanopb_lib_dependencies", + ], +} + +cc_library { + name: "chre_metrics_reporter", + export_include_dirs: [ + "host/common/include", + ], + srcs: [ + "host/common/metrics_reporter.cc", + "host/common/log.cc", + ], + shared_libs: [ + "android.frameworks.stats-V1-ndk", + "chremetrics-cpp", + "chre_atoms_log", + "libbinder_ndk", + "libcutils", + "liblog", + "libutils", + ], + vendor: true, cflags: ["-Wall", "-Werror"] } @@ -99,7 +137,76 @@ cc_binary { "liblog", "libutils", ], - static_libs: ["chre_client"], + static_libs: [ + "chre_client", + "chre_host_common" + ], +} + +genrule { + name: "rpc_world_proto_header", + defaults: [ + "pw_rpc_generate_nanopb_proto", + ], + srcs: ["apps/rpc_world/rpc/rpc_world.proto"], + out: [ + "rpc_world.pb.h", + ], +} + +genrule { + name: "rpc_world_proto_source", + defaults: [ + "pw_rpc_generate_nanopb_proto", + ], + srcs: ["apps/rpc_world/rpc/rpc_world.proto"], + out: [ + "rpc_world.pb.c", + ], +} + +genrule { + name: "rpc_world_rpc_header", + defaults: [ + "pw_rpc_generate_nanopb_rpc_header", + ], + srcs: ["apps/rpc_world/rpc/rpc_world.proto"], + out: [ + "rpc_world.rpc.pb.h", + ], +} + +cc_binary { + name: "chre_test_rpc", + vendor: true, + local_include_dirs: [ + "chre_api/include/chre_api", + "util/include", + ], + srcs: [ + "host/common/test/chre_test_rpc.cc", + ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libcutils", + "liblog", + "libutils", + ], + static_libs: [ + "chre_client", + "libprotobuf-c-nano", + ], + generated_sources: [ + "rpc_world_proto_source", + ], + generated_headers: [ + "rpc_world_proto_header", + "rpc_world_rpc_header", + ], + defaults: [ + "pw_rpc_cflags_chre", + "pw_rpc_nanopb_lib_dependencies", + ], } cc_binary { @@ -130,23 +237,33 @@ filegroup { cc_binary { name: "chre_aidl_hal_client", vendor: true, + cpp_std: "c++20", local_include_dirs: [ "host/common/include", "chre_api/include", ], srcs: [ - "host/common/file_stream.cc", "host/common/chre_aidl_hal_client.cc", + "host/common/file_stream.cc", + "host/common/log.cc", ], shared_libs: [ - "android.hardware.contexthub-V2-ndk", + "android.hardware.contexthub-V3-ndk", "libbase", "libbinder_ndk", "libjsoncpp", "liblog", "libutils", ], - cflags: ["-Wall", "-Werror", "-fexceptions"], + static_libs: [ + "chre_client", + ], + cflags: [ + "-Wall", + "-Werror", + "-fexceptions", + "-DLOG_TAG=\"CHRE.HAL.CLIENT\"", + ], } cc_test { @@ -313,7 +430,7 @@ cc_library_static { "host/common/include", ], shared_libs: [ - "android.hardware.contexthub-V2-ndk", + "android.hardware.contexthub-V3-ndk", "libcutils", "libutils", ], @@ -327,27 +444,65 @@ cc_library_static { ], } +cc_library_static { + name: "chre_host_common", + vendor: true, + host_supported: true, + cpp_std: "c++20", + srcs: [ + "host/common/log.cc", + ], + local_include_dirs: [ + "util/include", + "host/common/include", + ], + shared_libs: [ + "libcutils", + "liblog", + "libutils", + ], + cflags: [ + "-Wall", + "-Werror", + "-DCHRE_IS_HOST_BUILD", + ], +} + cc_test_host { name: "hal_unit_tests", vendor: true, srcs: [ "host/test/**/*_test.cc", + "host/hal_generic/common/hal_client_manager.cc", + "host/common/fragmented_load_transaction.cc", + "host/common/hal_client.cc", ], local_include_dirs: [ - "util/include", "host/common/include", + "host/hal_generic/common/", + "util/include/", + "host/hal_generic/aidl/", + "platform/shared/include/", ], static_libs: [ - "android.hardware.contexthub-V2-ndk", + "android.hardware.contexthub-V3-ndk", + "chre_flags_c_lib", + "chre_host_common", "event_logger", "libgmock", ], shared_libs: [ "libcutils", "libutils", + "android.hardware.contexthub-V3-ndk", + "liblog", + "libjsoncpp", + "libbinder_ndk", + "server_configurable_flags", ], header_libs: [ "chre_flatbuffers", + "chre_api", ], defaults: [ "chre_linux_cflags", @@ -357,6 +512,9 @@ cc_test_host { "-Werror", "-DCHRE_IS_HOST_BUILD", ], + test_options: { + unit_test: true, + }, } genrule { @@ -504,15 +662,20 @@ cc_test_host { "platform/include", "platform/linux/include", "platform/shared/include", + "platform/shared/pw_trace/include", "util/include", ], cflags: [ "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096", "-DCHRE_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG", "-DCHRE_ASSERTIONS_ENABLED=true", + "-DCHRE_BLE_SUPPORT_ENABLED=true", "-DCHRE_FILENAME=__FILE__", "-DGTEST", ], + header_libs: [ + "chre_flatbuffers", + ], static_libs: [ "chre_linux", "libgmock", @@ -522,23 +685,39 @@ cc_test_host { }, } -// pw_rpc rules instantiation +// PW_RPC rules. cc_defaults { name: "pw_rpc_cflags_chre", cflags: [ "-DPW_RPC_USE_GLOBAL_MUTEX=0", - "-DPW_RPC_CLIENT_STREAM_END_CALLBACK", + "-DPW_RPC_COMPLETION_REQUEST_CALLBACK", "-DPW_RPC_DYNAMIC_ALLOCATION", ], } +// Lib dependencies for apps and libs using PW_RPC with nanopb. +cc_defaults { + name: "pw_rpc_nanopb_lib_dependencies", + static_libs: [ + "pw_containers", + "pw_protobuf", + "pw_rpc_chre", + "pw_rpc_nanopb_chre", + "pw_status", + "pw_stream", + "pw_varint", + ], +} + cc_library_static { name: "pw_rpc_chre", defaults: [ "pw_rpc_cflags_chre", "pw_rpc_defaults", ], + host_supported: true, + vendor_available: true, } cc_library_static { @@ -555,6 +734,8 @@ cc_library_static { "pw_rpc_raw_chre", "pw_rpc_chre", ], + host_supported: true, + vendor_available: true, } cc_library_static { @@ -566,6 +747,8 @@ cc_library_static { static_libs: [ "pw_rpc_chre", ], + host_supported: true, + vendor_available: true, } genrule { @@ -588,7 +771,6 @@ genrule { out: [ "rpc_test.pb.c", ], - } genrule { @@ -608,7 +790,9 @@ cc_test_host { isolated: false, test_suites: ["general-tests"], srcs: [ - "test/simulation/*.cc", + "test/simulation/test_base.cc", + "test/simulation/test_util.cc", + "test/simulation/*_test.cc", ], generated_sources: [ "rpc_test_proto_source", @@ -625,16 +809,11 @@ cc_test_host { "chre_linux", "chre_pal_linux", "libprotobuf-c-nano", - "pw_containers", - "pw_protobuf", - "pw_rpc_nanopb_chre", - "pw_rpc_chre", - "pw_stream", - "pw_varint", ], defaults: [ "chre_linux_cflags", "pw_rpc_cflags_chre", + "pw_rpc_nanopb_lib_dependencies", ], sanitize: { address: true, @@ -707,7 +886,6 @@ cc_library_static { "platform/shared/platform_gnss.cc", "platform/shared/platform_wifi.cc", "platform/shared/system_time.cc", - "platform/shared/tracing.cc", "platform/shared/version.cc", "platform/shared/sensor_pal/platform_sensor.cc", "platform/shared/sensor_pal/platform_sensor_type_helpers.cc", @@ -761,10 +939,11 @@ cc_defaults { "-DCHRE_SENSORS_SUPPORT_ENABLED", "-DCHRE_WIFI_SUPPORT_ENABLED", "-DCHRE_WIFI_NAN_SUPPORT_ENABLED", - "-DCHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS=3000000000", - "-DCHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS=3000000000", - "-DCHRE_TEST_ASYNC_RESULT_TIMEOUT_NS=3000000000", + "-DCHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS=300000000", + "-DCHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS=300000000", + "-DCHRE_TEST_ASYNC_RESULT_TIMEOUT_NS=300000000", "-DCHRE_BLE_READ_RSSI_SUPPORT_ENABLED", + "-Wextra-semi", ], } @@ -799,9 +978,12 @@ cc_defaults { "libjsoncpp", "libutils", "liblog", + "chre_metrics_reporter", + "server_configurable_flags", ], static_libs: [ "chre_config_util", + "chre_flags_c_lib", ] } @@ -855,8 +1037,12 @@ java_library_static { host_supported: true, proto: { type: "lite", + include_dirs: ["external/protobuf/src"], }, - srcs: ["apps/test/common/chre_api_test/rpc/chre_api_test.proto"], + srcs: [ + "apps/test/common/chre_api_test/rpc/chre_api_test.proto", + ":libprotobuf-internal-protos", + ], sdk_version: "current", } @@ -875,3 +1061,16 @@ cc_library_static { ], cflags: ["-Wall", "-Werror"] } + +aconfig_declarations { + name: "chre_flags", + package: "android.chre.flags", + srcs: ["chre_flags.aconfig"], +} + +cc_aconfig_library { + name: "chre_flags_c_lib", + aconfig_declarations: "chre_flags", + host_supported: true, + vendor: true, +} @@ -98,7 +98,12 @@ LOCAL_SHARED_LIBRARIES := \ chremetrics-cpp \ chre_atoms_log \ android.frameworks.stats-V1-ndk \ - libbinder_ndk + libbinder_ndk \ + chre_metrics_reporter \ + server_configurable_flags + +LOCAL_STATIC_LIBRARIES := \ + chre_flags_c_lib LOCAL_SRC_FILES += $(MSM_SRC_FILES) LOCAL_C_INCLUDES += $(MSM_INCLUDES) @@ -107,20 +112,33 @@ LOCAL_CPPFLAGS += -std=c++20 LOCAL_CFLAGS += -Wno-sign-compare LOCAL_CFLAGS += -Wno-c++11-narrowing LOCAL_CFLAGS += -Wno-deprecated-volatile -PIGWEED_DIR = external/pigweed -PIGWEED_DIR_RELPATH = ../../$(PIGWEED_DIR) -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_bytes/public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public_overrides -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/standard_library_public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_tokenizer/public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_varint/public -LOCAL_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public - -LOCAL_SRC_FILES += $(PIGWEED_DIR_RELPATH)/pw_tokenizer/detokenize.cc -LOCAL_SRC_FILES += $(PIGWEED_DIR_RELPATH)/pw_tokenizer/decode.cc -LOCAL_SRC_FILES += $(PIGWEED_DIR_RELPATH)/pw_varint/varint.cc + +# Pigweed (PW) +PW_DIR = external/pigweed +PW_DIR_RELPATH = ../../$(PW_DIR) +LOCAL_CFLAGS += -I$(PW_DIR)/pw_assert/assert_compatibility_public_overrides +LOCAL_CFLAGS += -I$(PW_DIR)/pw_assert/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_base64/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_bytes/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_containers/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_log_tokenized/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_log/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_polyfill/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_polyfill/public_overrides +LOCAL_CFLAGS += -I$(PW_DIR)/pw_polyfill/standard_library_public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_preprocessor/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_result/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_span/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_status/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_string/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_tokenizer/public +LOCAL_CFLAGS += -I$(PW_DIR)/pw_varint/public +LOCAL_CFLAGS += -I$(PW_DIR)/third_party/fuchsia/repo/sdk/lib/stdcompat/include + +LOCAL_SRC_FILES += $(PW_DIR_RELPATH)/pw_tokenizer/decode.cc +LOCAL_SRC_FILES += $(PW_DIR_RELPATH)/pw_tokenizer/detokenize.cc +LOCAL_SRC_FILES += $(PW_DIR_RELPATH)/pw_varint/varint_c.c +LOCAL_SRC_FILES += $(PW_DIR_RELPATH)/pw_varint/varint.cc ifeq ($(CHRE_DAEMON_USE_SDSPRPC),true) LOCAL_SHARED_LIBRARIES += libsdsprpc @@ -68,6 +68,12 @@ COMMON_CFLAGS += -DCHRE_TOKENIZED_LOGGING_ENABLED include $(CHRE_PREFIX)/external/pigweed/pw_tokenizer.mk endif +# Optional tokenized tracing support. +ifeq ($(CHRE_TRACING_ENABLED), true) +COMMON_CFLAGS += -DCHRE_TRACING_ENABLED +include $(CHRE_PREFIX)/external/pigweed/pw_trace.mk +endif + # Optional on-device unit tests support include $(CHRE_PREFIX)/test/test.mk diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 64a49d54..ca8b740e 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -11,4 +11,6 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py todo_checker_hook = ${REPO_ROOT}/system/chre/tools/todo_checker.py -run_sim = ${REPO_ROOT}/system/chre/run_sim.sh -b
\ No newline at end of file +run_sim = ${REPO_ROOT}/system/chre/run_sim.sh -b +run_tests = ${REPO_ROOT}/system/chre/run_tests.sh -b +run_pal_impl_tests = ${REPO_ROOT}/system/chre/run_pal_impl_tests.sh -b
\ No newline at end of file diff --git a/api_parser/.gitignore b/api_parser/.gitignore new file mode 100644 index 00000000..48fb1936 --- /dev/null +++ b/api_parser/.gitignore @@ -0,0 +1 @@ +/parser_cache diff --git a/chpp/api_parser/README.md b/api_parser/README.md index 8d775052..8d775052 100644 --- a/chpp/api_parser/README.md +++ b/api_parser/README.md diff --git a/api_parser/api_parser.py b/api_parser/api_parser.py new file mode 100644 index 00000000..343bcc90 --- /dev/null +++ b/api_parser/api_parser.py @@ -0,0 +1,150 @@ +#!/usr/bin/python3 +# +# 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. +# + +import os + +from collections import defaultdict +from pyclibrary import CParser + +from utils import system_chre_abs_path + + +class ApiParser: + """Given a file-specific set of annotations (extracted from JSON annotations file), parses a + single API header file into data structures suitable for use with code generation. This class + will contain the parsed representation of the headers when instantiated. + """ + + def __init__(self, json_obj): + """Initialize and parse the API file described in the provided JSON-derived object. + + :param json_obj: Extracted file-specific annotations from JSON + """ + + self.json = json_obj + self.structs_and_unions = {} + self._parse_annotations() + self._parse_api() + + def _parse_annotations(self): + # Converts annotations list to a more usable data structure: dict keyed by structure name, + # containing a dict keyed by field name, containing a list of annotations (as they + # appear in the JSON). In other words, we can easily get all of the annotations for the + # "version" field in "chreWwanCellInfoResult" via + # annotations['chreWwanCellInfoResult']['version']. This is also a defaultdict, so it's safe + # to access if there are no annotations for this structure + field; it'll just give you + # an empty list in that case. + + self.annotations = defaultdict(lambda: defaultdict(list)) + for struct_info in self.json['struct_info']: + for annotation in struct_info['annotations']: + self.annotations[struct_info['name'] + ][annotation['field']].append(annotation) + + def _files_to_parse(self): + """Returns a list of files to supply as input to CParser""" + + # Input paths for CParser are stored in JSON relative to <android_root>/system/chre + # Reformulate these to absolute paths, and add in some default includes that we always + # supply + chre_project_base_dir = system_chre_abs_path() + default_includes = ['api_parser/parser_defines.h', + 'chre_api/include/chre_api/chre/version.h'] + files = default_includes + \ + self.json['includes'] + [self.json['filename']] + return [os.path.join(chre_project_base_dir, file) for file in files] + + def _parse_structs_and_unions(self): + # Starts with the root structures (i.e. those that will appear at the top-level in one + # or more CHPP messages), build a data structure containing all of the information we'll + # need to emit the CHPP structure definition and conversion code. + + structs_and_unions_to_parse = self.json['root_structs'].copy() + while len(structs_and_unions_to_parse) > 0: + type_name = structs_and_unions_to_parse.pop() + if type_name in self.structs_and_unions: + continue + + entry = { + 'appears_in': set(), # Other types this type is nested within + 'dependencies': set(), # Types that are nested in this type + 'has_vla_member': False, # True if this type or any dependency has a VLA member + 'members': [], # Info about each member of this type + } + if type_name in self.parser.defs['structs']: + defs = self.parser.defs['structs'][type_name] + entry['is_union'] = False + elif type_name in self.parser.defs['unions']: + defs = self.parser.defs['unions'][type_name] + entry['is_union'] = True + else: + raise RuntimeError( + "Couldn't find {} in parsed structs/unions".format(type_name)) + + for member_name, member_type, _ in defs['members']: + member_info = { + 'name': member_name, + 'type': member_type, + 'annotations': self.annotations[type_name][member_name], + 'is_nested_type': False, + } + + if member_type.type_spec.startswith('struct ') or \ + member_type.type_spec.startswith('union '): + member_info['is_nested_type'] = True + member_type_name = member_type.type_spec.split(' ')[1] + member_info['nested_type_name'] = member_type_name + entry['dependencies'].add(member_type_name) + structs_and_unions_to_parse.append(member_type_name) + + entry['members'].append(member_info) + + # Flip a flag if this structure has at least one variable-length array member, which + # means that the encoded size can only be computed at runtime + if not entry['has_vla_member']: + for annotation in self.annotations[type_name][member_name]: + if annotation['annotation'] == 'var_len_array': + entry['has_vla_member'] = True + + self.structs_and_unions[type_name] = entry + + # Build reverse linkage of dependency chain (i.e. lookup between a type and the other types + # it appears in) + for type_name, type_info in self.structs_and_unions.items(): + for dependency in type_info['dependencies']: + self.structs_and_unions[dependency]['appears_in'].add( + type_name) + + # Bubble up "has_vla_member" to types each type it appears in, i.e. if this flag is set to + # True on a leaf node, then all its ancestors should also have the flag set to True + for type_name, type_info in self.structs_and_unions.items(): + if type_info['has_vla_member']: + types_to_mark = list(type_info['appears_in']) + while len(types_to_mark) > 0: + type_to_mark = types_to_mark.pop() + self.structs_and_unions[type_to_mark]['has_vla_member'] = True + types_to_mark.extend( + list(self.structs_and_unions[type_to_mark]['appears_in'])) + + def _parse_api(self): + """ + Parses the API and stores the structs and unions. + """ + + file_to_parse = self._files_to_parse() + self.parser = CParser(file_to_parse, cache='parser_cache') + self._parse_structs_and_unions() diff --git a/chpp/api_parser/chre_api_to_chpp.py b/api_parser/chpp_code_generator.py index f1bde02f..5861951e 100755..100644 --- a/chpp/api_parser/chre_api_to_chpp.py +++ b/api_parser/chpp_code_generator.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -# Copyright (C) 2020 The Android Open Source Project +# 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. @@ -15,43 +15,16 @@ # limitations under the License. # -import argparse -import json -import os.path +import os import subprocess -from collections import defaultdict + from datetime import datetime -from pyclibrary import CParser - -LICENSE_HEADER = """/* - * 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. - */ -""" - -# Paths for output, relative to system/chre -CHPP_PARSER_INCLUDE_PATH = "chpp/include/chpp/common" -CHPP_PARSER_SOURCE_PATH = "chpp/common" - - -def system_chre_abs_path(): - """Gets the absolute path to the system/chre directory containing this script.""" - script_dir = os.path.dirname(os.path.realpath(__file__)) - # Assuming we're at system/chre/chpp/api_parser (i.e. up 2 to get to system/chre) - chre_project_base_dir = os.path.normpath(script_dir + "/../..") - return chre_project_base_dir +from utils import CHPP_PARSER_INCLUDE_PATH +from utils import CHPP_PARSER_SOURCE_PATH +from utils import LICENSE_HEADER +from utils import android_build_top_abs_path +from utils import system_chre_abs_path class CodeGenerator: @@ -62,11 +35,12 @@ class CodeGenerator: """ :param api: ApiParser object """ + self.api = api self.json = api.json # Turn "chre_api/include/chre_api/chre/wwan.h" into "wwan" self.service_name = self.json['filename'].split('/')[-1].split('.')[0] - self.capitalized_service_name = self.service_name[0].upper() + self.service_name[1:] + self.capitalized_service_name = self.service_name.capitalize() self.commit_hash = commit_hash # ---------------------------------------------------------------------------------------------- @@ -75,27 +49,31 @@ class CodeGenerator: def _autogen_notice(self): out = [] - out.append("// This file was automatically generated by {}\n".format( + out.append('// This file was automatically generated by {}\n'.format( os.path.basename(__file__))) - out.append("// Date: {} UTC\n".format(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))) - out.append("// Source: {} @ commit {}\n\n".format(self.json['filename'], self.commit_hash)) - out.append("// DO NOT modify this file directly, as those changes will be lost the next\n") - out.append("// time the script is executed\n\n") + out.append( + '// Date: {} UTC\n'.format(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))) + out.append( + '// Source: {} @ commit {}\n\n'.format(self.json['filename'], self.commit_hash)) + out.append( + '// DO NOT modify this file directly, as those changes will be lost the next\n') + out.append('// time the script is executed\n\n') return out def _dump_to_file(self, output_filename, contents, dry_run, skip_clang_fomat): """Outputs contents to output_filename, or prints contents if dry_run is True""" + if dry_run: - print("---- {} ----".format(output_filename)) + print('---- {} ----'.format(output_filename)) print(contents) - print("---- end of {} ----\n".format(output_filename)) + print('---- end of {} ----\n'.format(output_filename)) else: with open(output_filename, 'w') as f: f.write(contents) if not skip_clang_fomat: - clang_format_path = os.path.normpath( - "../../../../prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format") + clang_format_path = (android_build_top_abs_path() + + '/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format') args = [clang_format_path, '-i', output_filename] result = subprocess.run(args) result.check_returncode() @@ -109,36 +87,40 @@ class CodeGenerator: return type_info.declarators[0][0] def _get_chpp_type_from_chre(self, chre_type): - """Given 'chreWwanCellInfo' returns 'struct ChppWwanCellInfo', etc.""" + """Returns 'struct ChppWwanCellInfo', etc. given 'chreWwanCellInfo'""" + prefix = self._get_struct_or_union_prefix(chre_type) # First see if we have an explicit name override (e.g. for anonymous types) - for annotation in self.api.annotations[chre_type]["."]: - if annotation['annotation'] == "rename_type": + for annotation in self.api.annotations[chre_type]['.']: + if annotation['annotation'] == 'rename_type': return prefix + annotation['type_override'] # Otherwise, use the existing type name, just replace the "chre" prefix with "Chpp" if chre_type.startswith('chre'): return prefix + 'Chpp' + chre_type[4:] else: - raise RuntimeError("Couldn't figure out new type name for {}".format(chre_type)) + raise RuntimeError( + "Couldn't figure out new type name for {}".format(chre_type)) def _get_chre_type_with_prefix(self, chre_type): - """Given 'chreWwanCellInfo' returns 'struct chreWwanCellInfo', etc.""" + """Returns 'struct chreWwanCellInfo', etc. given 'chreWwanCellInfo'""" + return self._get_struct_or_union_prefix(chre_type) + chre_type def _get_chpp_header_type_from_chre(self, chre_type): - """Given 'chreWwanCellInfo' returns 'struct ChppWwanCellInfoWithHeader', etc.""" + """Returns 'struct ChppWwanCellInfoWithHeader', etc. given 'chreWwanCellInfo'""" + return self._get_chpp_type_from_chre(chre_type) + 'WithHeader' def _get_member_comment(self, member_info): for annotation in member_info['annotations']: - if annotation['annotation'] == "fixed_value": - return " // Input ignored; always set to {}".format(annotation['value']) - elif annotation['annotation'] == "var_len_array": - return " // References {} instances of {}".format( + if annotation['annotation'] == 'fixed_value': + return ' // Input ignored; always set to {}'.format(annotation['value']) + elif annotation['annotation'] == 'var_len_array': + return ' // References {} instances of {}'.format( annotation['length_field'], self._get_member_type(member_info)) - return "" + return '' def _get_member_type(self, member_info, underlying_vla_type=False): """Gets the CHPP type specification prefix for a struct/union member. @@ -148,6 +130,7 @@ class CodeGenerator: 'struct ChppOffset', and True to output the type that ChppOffset references :return: type specification string that prefixes the field name, e.g. 'uint8_t' """ + # 4 cases to handle: # 1) Annotation gives explicit type that we should use # 2) Annotation says this is a variable length array (so use ChppOffset if @@ -155,16 +138,16 @@ class CodeGenerator: # 3) This is a struct/union type, so use the renamed (CHPP) type name # 4) Regular type, e.g. uint32_t, so just use the type spec as-is for annotation in member_info['annotations']: - if annotation['annotation'] == "rewrite_type": + if annotation['annotation'] == 'rewrite_type': return annotation['type_override'] - elif not underlying_vla_type and annotation['annotation'] in ["var_len_array", "string"]: - return "struct ChppOffset" + elif not underlying_vla_type and annotation['annotation'] in ['var_len_array', 'string']: + return 'struct ChppOffset' if not underlying_vla_type and len(member_info['type'].declarators) > 0 and \ - member_info['type'].declarators[0] == "*": + member_info['type'].declarators[0] == '*': # This case should either be handled by rewrite_type (e.g. to uint32_t as # opaque/ignored), or var_len_array - raise RuntimeError("Pointer types require annotation\n{}".format( + raise RuntimeError('Pointer types require annotation\n{}'.format( member_info)) if member_info['is_nested_type']: @@ -174,55 +157,62 @@ class CodeGenerator: def _get_member_type_suffix(self, member_info): if self._is_array_type(member_info['type']): - return "[{}]".format(self._get_array_len(member_info['type'])) - return "" + return '[{}]'.format(self._get_array_len(member_info['type'])) + return '' def _get_struct_or_union_prefix(self, chre_type): return 'struct ' if not self.api.structs_and_unions[chre_type]['is_union'] else 'union ' def _gen_header_includes(self): """Generates #include directives for use in <service>_types.h""" - out = ["#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\n"] - includes = ["chpp/app.h", "chpp/macros.h", "chre_api/chre/version.h"] + out = ['#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\n'] + + includes = ['chpp/app.h', 'chpp/macros.h', 'chre_api/chre/version.h'] includes.extend(self.json['output_includes']) for incl in sorted(includes): - out.append("#include \"{}\"\n".format(incl)) - out.append("\n") + out.append('#include "{}"\n'.format(incl)) + out.append('\n') return out def _gen_struct_or_union(self, name): - """Generates the definition for a single struct/union type""" + """Generates the definition for a single struct/union type.""" + out = [] if not name.startswith('anon'): - out.append("//! See {{@link {}}} for details\n".format(name)) - out.append("{} {{\n".format(self._get_chpp_type_from_chre(name))) + out.append('//! See {{@link {}}} for details\n'.format(name)) + out.append('{} {{\n'.format(self._get_chpp_type_from_chre(name))) for member_info in self.api.structs_and_unions[name]['members']: - out.append(" {} {}{};{}\n".format(self._get_member_type(member_info), + out.append(' {} {}{};{}\n'.format(self._get_member_type(member_info), member_info['name'], - self._get_member_type_suffix(member_info), + self._get_member_type_suffix( + member_info), self._get_member_comment(member_info))) - out.append("} CHPP_PACKED_ATTR;\n\n") + out.append('} CHPP_PACKED_ATTR;\n\n') return out def _gen_header_struct(self, chre_type): - """Generates the definition for the type with header (WithHeader)""" + """Generates the definition for the type with header (WithHeader).""" + out = [] - out.append("//! CHPP app header plus {}\n".format( + out.append('//! CHPP app header plus {}\n'.format( self._get_chpp_header_type_from_chre(chre_type))) - out.append("{} {{\n".format(self._get_chpp_header_type_from_chre(chre_type))) - out.append(" struct ChppAppHeader header;\n") - out.append(" {} payload;\n".format(self._get_chpp_type_from_chre(chre_type))) - out.append("} CHPP_PACKED_ATTR;\n\n") + out.append('{} {{\n'.format( + self._get_chpp_header_type_from_chre(chre_type))) + out.append(' struct ChppAppHeader header;\n') + out.append(' {} payload;\n'.format( + self._get_chpp_type_from_chre(chre_type))) + out.append('} CHPP_PACKED_ATTR;\n\n') return out def _gen_structs_and_unions(self): """Generates definitions for all struct/union types required for the root structs.""" + out = [] - out.append("CHPP_PACKED_START\n\n") + out.append('CHPP_PACKED_START\n\n') sorted_structs = self._sorted_structs(self.json['root_structs']) for type_name in sorted_structs: @@ -231,7 +221,7 @@ class CodeGenerator: for chre_type in self.json['root_structs']: out.extend(self._gen_header_struct(chre_type)) - out.append("CHPP_PACKED_END\n\n") + out.append('CHPP_PACKED_END\n\n') return out def _sorted_structs(self, root_nodes): @@ -241,6 +231,7 @@ class CodeGenerator: then B will appear before A in the returned list. :return: list of keys in self.api.structs_and_unions, sorted by dependency order """ + result = [] visited = set() @@ -266,20 +257,22 @@ class CodeGenerator: :param member_info: a dict element from self.api.structs_and_unions[struct]['members'] :return: string """ + type_name = None if member_info['is_nested_type']: chre_type = member_info['nested_type_name'] if self.api.structs_and_unions[chre_type]['has_vla_member']: - return "{}(in->{})".format(self._get_chpp_sizeof_function_name(chre_type), + return '{}(in->{})'.format(self._get_chpp_sizeof_function_name(chre_type), member_info['name']) else: type_name = self._get_chpp_type_from_chre(chre_type) else: type_name = member_info['type'].type_spec - return "sizeof({})".format(type_name) + return 'sizeof({})'.format(type_name) def _gen_chpp_sizeof_function(self, chre_type): """Generates a function to determine the encoded size of the CHRE struct, if necessary.""" + out = [] # Note that this function *should* work with unions as well, but at the time of writing @@ -289,25 +282,27 @@ class CodeGenerator: # No codegen necessary, just sizeof on the CHPP structure name is sufficient return out - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_type) + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_type) parameter_name = core_type_name[0].lower() + core_type_name[1:] chpp_type_name = self._get_chpp_header_type_from_chre(chre_type) - out.append("//! @return number of bytes required to represent the given\n" - "//! {} along with the CHPP header as\n" - "//! {}\n" + out.append('//! @return number of bytes required to represent the given\n' + '//! {} along with the CHPP header as\n' + '//! {}\n' .format(chre_type, chpp_type_name)) - out.append("static size_t {}(\n const {}{} *{}) {{\n" + out.append('static size_t {}(\n const {}{} *{}) {{\n' .format(self._get_chpp_sizeof_function_name(chre_type), - self._get_struct_or_union_prefix(chre_type), chre_type, + self._get_struct_or_union_prefix( + chre_type), chre_type, parameter_name)) # sizeof(this struct) - out.append(" size_t encodedSize = sizeof({});\n".format(chpp_type_name)) + out.append(' size_t encodedSize = sizeof({});\n'.format(chpp_type_name)) # Plus count * sizeof(type) for each var-len array included in this struct for member_info in self.api.structs_and_unions[chre_type]['members']: for annotation in member_info['annotations']: - if annotation['annotation'] == "var_len_array": + if annotation['annotation'] == 'var_len_array': # If the VLA field itself contains a VLA, then we'd need to generate a for # loop to calculate the size of each element individually - I don't think we # have any of these in the CHRE API today, so leaving this functionality out. @@ -317,58 +312,67 @@ class CodeGenerator: self.api.structs_and_unions[member_info['nested_type_name']][ 'has_vla_member']: raise RuntimeError( - "Nested variable-length arrays is not currently supported ({} " - "in {})".format(member_info['name'], chre_type)) + 'Nested variable-length arrays is not currently supported ({} ' + 'in {})'.format(member_info['name'], chre_type)) - out.append(" encodedSize += {}->{} * sizeof({});\n".format( + out.append(' encodedSize += {}->{} * sizeof({});\n'.format( parameter_name, annotation['length_field'], self._get_member_type(member_info, True))) - elif annotation['annotation'] == "string": - out.append(" if ({}->{} != NULL) {{".format( + elif annotation['annotation'] == 'string': + out.append(' if ({}->{} != NULL) {{'.format( parameter_name, annotation['field'])) - out.append(" encodedSize += strlen({}->{}) + 1;\n".format( + out.append(' encodedSize += strlen({}->{}) + 1;\n'.format( parameter_name, annotation['field'])) - out.append(" }\n") + out.append(' }\n') - out.append(" return encodedSize;\n}\n\n") + out.append(' return encodedSize;\n}\n\n') return out def _gen_chpp_sizeof_functions(self): """For each root struct, generate necessary functions to determine their encoded size.""" + out = [] for struct in self.json['root_structs']: out.extend(self._gen_chpp_sizeof_function(struct)) return out def _gen_conversion_includes(self): - """Generates #include directives for the conversion source file""" - out = ["#include \"chpp/macros.h\"\n" - "#include \"chpp/memory.h\"\n" - "#include \"chpp/common/{}_types.h\"\n\n".format(self.service_name)] - out.append("#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n") + """Generates #include directives for the conversion source file.""" + + out = ['#include "chpp/macros.h"\n' + '#include "chpp/memory.h"\n' + '#include "chpp/common/{}_types.h"\n\n'.format(self.service_name)] + out.append( + '#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n') return out def _get_chpp_sizeof_function_name(self, chre_struct): - """Function name used to compute the encoded size of the given struct at runtime""" - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_struct) - return "chpp{}SizeOf{}FromChre".format(self.capitalized_service_name, core_type_name) + """Returns the function name used to compute the encoded size of the given struct at + runtime. + """ + + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_struct) + return 'chpp{}SizeOf{}FromChre'.format(self.capitalized_service_name, core_type_name) def _get_encoding_function_name(self, chre_type): - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_type) - return "chpp{}Convert{}FromChre".format(self.capitalized_service_name, core_type_name) + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_type) + return 'chpp{}Convert{}FromChre'.format(self.capitalized_service_name, core_type_name) def _gen_encoding_function_signature(self, chre_type): out = [] - out.append("void {}(\n".format(self._get_encoding_function_name(chre_type))) - out.append(" const {}{} *in,\n".format( + out.append( + 'void {}(\n'.format(self._get_encoding_function_name(chre_type))) + out.append(' const {}{} *in,\n'.format( self._get_struct_or_union_prefix(chre_type), chre_type)) - out.append(" {} *out".format(self._get_chpp_type_from_chre(chre_type))) + out.append(' {} *out'.format(self._get_chpp_type_from_chre(chre_type))) if self.api.structs_and_unions[chre_type]['has_vla_member']: - out.append(",\n") - out.append(" uint8_t *payload,\n") - out.append(" size_t payloadSize,\n") - out.append(" uint16_t *vlaOffset") - out.append(")") + out.append(',\n') + out.append(' uint8_t *payload,\n') + out.append(' size_t payloadSize,\n') + out.append(' uint16_t *vlaOffset') + out.append(')') return out def _gen_string_encoding(self, member_info, annotation): @@ -377,18 +381,19 @@ class CodeGenerator: # in our API. We can assert here since there's currently no API that # does so. member_name = member_info['name'] - out.append(" if (in->{} != NULL) {{\n".format(member_name)) - out.append(" size_t strSize = strlen(in->{}) + 1;\n".format(member_name)) - out.append(" memcpy(&payload[*vlaOffset], in->{}, strSize);\n".format( + out.append(' if (in->{} != NULL) {{\n'.format(member_name)) + out.append( + ' size_t strSize = strlen(in->{}) + 1;\n'.format(member_name)) + out.append(' memcpy(&payload[*vlaOffset], in->{}, strSize);\n'.format( member_name)) - out.append(" out->{}.length = (uint16_t)(strSize);\n".format( + out.append(' out->{}.length = (uint16_t)(strSize);\n'.format( member_name)) - out.append(" out->{}.offset = *vlaOffset;\n".format(member_name)) - out.append(" *vlaOffset += out->{}.length;\n".format(member_name)) - out.append(" } else {\n") - out.append(" out->{}.length = 0;\n".format(member_name)) - out.append(" out->{}.offset = 0;\n".format(member_name)) - out.append(" }\n\n") + out.append(' out->{}.offset = *vlaOffset;\n'.format(member_name)) + out.append(' *vlaOffset += out->{}.length;\n'.format(member_name)) + out.append(' } else {\n') + out.append(' out->{}.length = 0;\n'.format(member_name)) + out.append(' out->{}.offset = 0;\n'.format(member_name)) + out.append(' }\n\n') return out @@ -399,36 +404,38 @@ class CodeGenerator: chpp_type = self._get_member_type(member_info, True) if member_info['is_nested_type']: - out.append("\n {} *{} = ({} *) &payload[*vlaOffset];\n".format( + out.append('\n {} *{} = ({} *) &payload[*vlaOffset];\n'.format( chpp_type, variable_name, chpp_type)) - out.append(" out->{}.length = (uint16_t)(in->{} * {});\n".format( + out.append(' out->{}.length = (uint16_t)(in->{} * {});\n'.format( member_info['name'], annotation['length_field'], self._get_chpp_member_sizeof_call(member_info))) - out.append(" CHPP_ASSERT((size_t)(*vlaOffset + out->{}.length) <= payloadSize);\n".format( + out.append(' CHPP_ASSERT((size_t)(*vlaOffset + out->{}.length) <= payloadSize);\n'.format( member_info['name'])) - out.append(" if (out->{}.length > 0 &&\n" - " *vlaOffset + out->{}.length <= payloadSize) {{\n".format( - member_info['name'], member_info['name'])) + out.append(' if (out->{}.length > 0 &&\n' + ' *vlaOffset + out->{}.length <= payloadSize) {{\n'.format( + member_info['name'], member_info['name'])) if member_info['is_nested_type']: - out.append(" for (size_t i = 0; i < in->{}; i++) {{\n".format( + out.append(' for (size_t i = 0; i < in->{}; i++) {{\n'.format( annotation['length_field'])) - out.append(" {}".format( + out.append(' {}'.format( self._get_assignment_statement_for_field(member_info, in_vla_loop=True))) - out.append(" }\n") + out.append(' }\n') else: - out.append("memcpy(&payload[*vlaOffset], in->{}, in->{} * sizeof({}));\n".format( + out.append('memcpy(&payload[*vlaOffset], in->{}, in->{} * sizeof({}));\n'.format( member_info['name'], annotation['length_field'], chpp_type)) - out.append(" out->{}.offset = *vlaOffset;\n".format(member_info['name'])) - out.append(" *vlaOffset += out->{}.length;\n".format(member_info['name'])) + out.append( + ' out->{}.offset = *vlaOffset;\n'.format(member_info['name'])) + out.append( + ' *vlaOffset += out->{}.length;\n'.format(member_info['name'])) - out.append(" } else {\n") - out.append(" out->{}.offset = 0;\n".format(member_info['name'])) - out.append(" }\n"); + out.append(' } else {\n') + out.append(' out->{}.offset = 0;\n'.format(member_info['name'])) + out.append(' }\n') return out @@ -451,45 +458,52 @@ class CodeGenerator: :param decode_mode: True converts from CHPP to CHRE. False from CHRE to CHPP :return: assignment statement as a string """ - array_index = "[i]" if in_vla_loop else "" - output_accessor = "" if in_vla_loop else "out->" - containing_field = containing_field_name + "." if containing_field_name is not None else "" - output_variable = "{}{}{}{}".format(output_accessor, containing_field, member_info['name'], + array_index = '[i]' if in_vla_loop else '' + output_accessor = '' if in_vla_loop else 'out->' + containing_field = containing_field_name + \ + '.' if containing_field_name is not None else '' + + output_variable = '{}{}{}{}'.format(output_accessor, containing_field, member_info['name'], array_index) - input_variable = "in->{}{}{}".format(containing_field, member_info['name'], array_index) + input_variable = 'in->{}{}{}'.format(containing_field, + member_info['name'], array_index) if decode_mode and in_vla_loop: - output_variable = "{}Out{}".format(member_info['name'], array_index) - input_variable = "{}In{}".format(member_info['name'], array_index) + output_variable = '{}Out{}'.format( + member_info['name'], array_index) + input_variable = '{}In{}'.format(member_info['name'], array_index) if member_info['is_nested_type']: chre_type = member_info['nested_type_name'] has_vla_member = self.api.structs_and_unions[chre_type]['has_vla_member'] if decode_mode: # Use decoding function - vla_params = ", inSize" if has_vla_member else "" - out = "if (!{}(&{}, &{}{})) {{\n".format( - self._get_decoding_function_name(chre_type), input_variable, + vla_params = ', inSize' if has_vla_member else '' + out = 'if (!{}(&{}, &{}{})) {{\n'.format( + self._get_decoding_function_name( + chre_type), input_variable, output_variable, vla_params) if has_vla_member: - out += " CHPP_FREE_AND_NULLIFY({}Out);\n".format(member_info['name']) - out += " return false;\n" - out += "}\n" + out += ' CHPP_FREE_AND_NULLIFY({}Out);\n'.format( + member_info['name']) + out += ' return false;\n' + out += '}\n' return out else: # Use encoding function - vla_params = ", payload, payloadSize, vlaOffset" if has_vla_member else "" - return "{}(&{}, &{}{});\n".format( - self._get_encoding_function_name(chre_type), input_variable, output_variable, + vla_params = ', payload, payloadSize, vlaOffset' if has_vla_member else '' + return '{}(&{}, &{}{});\n'.format( + self._get_encoding_function_name( + chre_type), input_variable, output_variable, vla_params) elif self._is_array_type(member_info['type']): # Array of primitive type (e.g. uint32_t[8]) - use memcpy - return "memcpy({}, {}, sizeof({}));\n".format(output_variable, input_variable, + return 'memcpy({}, {}, sizeof({}));\n'.format(output_variable, input_variable, output_variable) else: # Regular assignment - return "{} = {};\n".format(output_variable, input_variable) + return '{} = {};\n'.format(output_variable, input_variable) def _gen_union_variant_conversion_code(self, member_info, annotation, decode_mode): """Generates a switch statement to encode the "active"/"used" field within a union. @@ -505,23 +519,25 @@ class CodeGenerator: :param decode_mode: False encodes from CHRE to CHPP. True decodes from CHPP to CHRE :return: list of strings """ + out = [] chre_type = member_info['nested_type_name'] struct_info = self.api.structs_and_unions[chre_type] # Start off by zeroing out the union field so any padding is set to a consistent value - out.append(" memset(&out->{}, 0, sizeof(out->{}));\n".format(member_info['name'], + out.append(' memset(&out->{}, 0, sizeof(out->{}));\n'.format(member_info['name'], member_info['name'])) # Next, generate the switch statement that will copy over the proper values - out.append(" switch (in->{}) {{\n".format(annotation['discriminator'])) + out.append( + ' switch (in->{}) {{\n'.format(annotation['discriminator'])) for value, field_name in annotation['mapping']: - out.append(" case {}:\n".format(value)) + out.append(' case {}:\n'.format(value)) found = False for nested_member_info in struct_info['members']: if nested_member_info['name'] == field_name: - out.append(" {}".format( + out.append(' {}'.format( self._get_assignment_statement_for_field( nested_member_info, containing_field_name=member_info['name'], @@ -533,11 +549,11 @@ class CodeGenerator: raise RuntimeError("Invalid mapping - couldn't find target field {} in struct {}" .format(field_name, chre_type)) - out.append(" break;\n") + out.append(' break;\n') - out.append(" default:\n" - " CHPP_ASSERT(false);\n" - " }\n") + out.append(' default:\n' + ' CHPP_ASSERT(false);\n' + ' }\n') return out @@ -557,73 +573,81 @@ class CodeGenerator: return out already_generated.add(chre_type) - out.append("static ") + out.append('static ') if decode_mode: out.extend(self._gen_decoding_function_signature(chre_type)) else: out.extend(self._gen_encoding_function_signature(chre_type)) - out.append(" {\n") + out.append(' {\n') for member_info in self.api.structs_and_unions[chre_type]['members']: generated_by_annotation = False for annotation in member_info['annotations']: - if annotation['annotation'] == "fixed_value": + if annotation['annotation'] == 'fixed_value': if self._is_array_type(member_info['type']): - out.append(" memset(&out->{}, {}, sizeof(out->{}));\n".format( + out.append(' memset(&out->{}, {}, sizeof(out->{}));\n'.format( member_info['name'], annotation['value'], member_info['name'])) else: - out.append(" out->{} = {};\n".format(member_info['name'], + out.append(' out->{} = {};\n'.format(member_info['name'], annotation['value'])) generated_by_annotation = True break - elif annotation['annotation'] == "enum": - # TODO: generate range verification code? + elif annotation['annotation'] == 'enum': + # Note: We could generate range verification code here, but it has not + # been considered necessary thus far. pass - elif annotation['annotation'] == "var_len_array": + elif annotation['annotation'] == 'var_len_array': if decode_mode: - out.extend(self._gen_vla_decoding(member_info, annotation)) + out.extend(self._gen_vla_decoding( + member_info, annotation)) else: - out.extend(self._gen_vla_encoding(member_info, annotation)) + out.extend(self._gen_vla_encoding( + member_info, annotation)) generated_by_annotation = True break - elif annotation['annotation'] == "string": + elif annotation['annotation'] == 'string': if decode_mode: - out.extend(self._gen_string_decoding(member_info, annotation)) + out.extend(self._gen_string_decoding( + member_info, annotation)) else: - out.extend(self._gen_string_encoding(member_info, annotation)) + out.extend(self._gen_string_encoding( + member_info, annotation)) generated_by_annotation = True break - elif annotation['annotation'] == "union_variant": + elif annotation['annotation'] == 'union_variant': out.extend(self._gen_union_variant_conversion_code( member_info, annotation, decode_mode)) generated_by_annotation = True break if not generated_by_annotation: - out.append(" {}".format( + out.append(' {}'.format( self._get_assignment_statement_for_field(member_info, decode_mode=decode_mode))) if decode_mode: - out.append("\n return true;\n") + out.append('\n return true;\n') - out.append("}\n\n") + out.append('}\n\n') return out def _gen_conversion_functions(self, decode_mode): out = [] already_generated = set() for struct in self.json['root_structs']: - out.extend(self._gen_conversion_function(struct, already_generated, decode_mode)) + out.extend(self._gen_conversion_function( + struct, already_generated, decode_mode)) return out def _strip_prefix_and_service_from_chre_struct_name(self, struct): - """Strip 'chre' and service prefix, e.g. 'chreWwanCellResultInfo' -> 'CellResultInfo'""" + """Strips 'chre' and service prefix, e.g. 'chreWwanCellResultInfo' -> 'CellResultInfo'.""" + chre_stripped = struct[4:] - upcased_service_name = self.service_name[0].upper() + self.service_name[1:] + upcased_service_name = self.service_name[0].upper( + ) + self.service_name[1:] if not struct.startswith('chre') or not chre_stripped.startswith(upcased_service_name): # If this happens, we need to update the script to handle it. Right we assume struct # naming follows the pattern "chre<Service_name><Thing_name>" - raise RuntimeError("Unexpected structure name {}".format(struct)) + raise RuntimeError('Unexpected structure name {}'.format(struct)) return chre_stripped[len(self.service_name):] @@ -640,67 +664,71 @@ class CodeGenerator: :param chre_type: CHRE type name :return: string """ + if self.api.structs_and_unions[chre_type]['has_vla_member']: - return "{}(in)".format(self._get_chpp_sizeof_function_name(chre_type)) + return '{}(in)'.format(self._get_chpp_sizeof_function_name(chre_type)) else: - return "sizeof({})".format(self._get_chpp_header_type_from_chre(chre_type)) + return 'sizeof({})'.format(self._get_chpp_header_type_from_chre(chre_type)) def _get_encode_allocation_function_name(self, chre_type): - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_type) - return "chpp{}{}FromChre".format(self.capitalized_service_name, core_type_name) + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_type) + return 'chpp{}{}FromChre'.format(self.capitalized_service_name, core_type_name) def _gen_encode_allocation_function_signature(self, chre_type, gen_docs=False): out = [] if gen_docs: - out.append("/**\n" - " * Converts from given CHRE structure to serialized CHPP type.\n" - " *\n" - " * @param in Fully-formed CHRE structure.\n" - " * @param out Upon success, will point to a buffer allocated with " - "chppMalloc().\n" - " * It is the responsibility of the caller to set the values of the CHPP " - "app layer header, and to free the buffer when it is no longer needed via " - "chppFree() or CHPP_FREE_AND_NULLIFY().\n" - " * @param outSize Upon success, will be set to the size of the output " - "buffer, in bytes.\n" - " *\n" - " * @return true on success, false if memory allocation failed.\n" - " */\n") - out.append("bool {}(\n".format(self._get_encode_allocation_function_name(chre_type))) - out.append(" const {}{} *in,\n".format( + out.append('/**\n' + ' * Converts from given CHRE structure to serialized CHPP type.\n' + ' *\n' + ' * @param in Fully-formed CHRE structure.\n' + ' * @param out Upon success, will point to a buffer allocated with ' + 'chppMalloc().\n' + ' * It is the responsibility of the caller to set the values of the CHPP ' + 'app layer header, and to free the buffer when it is no longer needed via ' + 'chppFree() or CHPP_FREE_AND_NULLIFY().\n' + ' * @param outSize Upon success, will be set to the size of the output ' + 'buffer, in bytes.\n' + ' *\n' + ' * @return true on success, false if memory allocation failed.\n' + ' */\n') + out.append( + 'bool {}(\n'.format(self._get_encode_allocation_function_name(chre_type))) + out.append(' const {}{} *in,\n'.format( self._get_struct_or_union_prefix(chre_type), chre_type)) - out.append(" {} **out,\n".format(self._get_chpp_header_type_from_chre(chre_type))) - out.append(" size_t *outSize)") + out.append( + ' {} **out,\n'.format(self._get_chpp_header_type_from_chre(chre_type))) + out.append(' size_t *outSize)') return out def _gen_encode_allocation_function(self, chre_type): out = [] out.extend(self._gen_encode_allocation_function_signature(chre_type)) - out.append(" {\n") - out.append(" CHPP_NOT_NULL(out);\n") - out.append(" CHPP_NOT_NULL(outSize);\n\n") - out.append(" size_t payloadSize = {};\n".format( + out.append(' {\n') + out.append(' CHPP_NOT_NULL(out);\n') + out.append(' CHPP_NOT_NULL(outSize);\n\n') + out.append(' size_t payloadSize = {};\n'.format( self._get_chpp_sizeof_call(chre_type))) - out.append(" *out = chppMalloc(payloadSize);\n") + out.append(' *out = chppMalloc(payloadSize);\n') - out.append(" if (*out != NULL) {\n") + out.append(' if (*out != NULL) {\n') struct_info = self.api.structs_and_unions[chre_type] if struct_info['has_vla_member']: - out.append(" uint8_t *payload = (uint8_t *) &(*out)->payload;\n") - out.append(" uint16_t vlaOffset = sizeof({});\n".format( + out.append(' uint8_t *payload = (uint8_t *) &(*out)->payload;\n') + out.append(' uint16_t vlaOffset = sizeof({});\n'.format( self._get_chpp_type_from_chre(chre_type))) - out.append(" {}(in, &(*out)->payload".format( + out.append(' {}(in, &(*out)->payload'.format( self._get_encoding_function_name(chre_type))) if struct_info['has_vla_member']: - out.append(", payload, payloadSize, &vlaOffset") - out.append(");\n") - out.append(" *outSize = payloadSize;\n") - out.append(" return true;\n") - out.append(" }\n") + out.append(', payload, payloadSize, &vlaOffset') + out.append(');\n') + out.append(' *outSize = payloadSize;\n') + out.append(' return true;\n') + out.append(' }\n') - out.append(" return false;\n}\n\n") + out.append(' return false;\n}\n\n') return out def _gen_encode_allocation_functions(self): @@ -712,8 +740,9 @@ class CodeGenerator: def _gen_encode_allocation_function_signatures(self): out = [] for chre_type in self.json['root_structs']: - out.extend(self._gen_encode_allocation_function_signature(chre_type, True)) - out.append(";\n\n") + out.extend( + self._gen_encode_allocation_function_signature(chre_type, True)) + out.append(';\n\n') return out # ---------------------------------------------------------------------------------------------- @@ -721,147 +750,155 @@ class CodeGenerator: # ---------------------------------------------------------------------------------------------- def _get_decoding_function_name(self, chre_type): - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_type) - return "chpp{}Convert{}ToChre".format(self.capitalized_service_name, core_type_name) + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_type) + return 'chpp{}Convert{}ToChre'.format(self.capitalized_service_name, core_type_name) def _gen_decoding_function_signature(self, chre_type): out = [] - out.append("bool {}(\n".format(self._get_decoding_function_name(chre_type))) - out.append(" const {} *in,\n".format(self._get_chpp_type_from_chre(chre_type))) - out.append(" {} *out".format(self._get_chre_type_with_prefix(chre_type))) + out.append( + 'bool {}(\n'.format(self._get_decoding_function_name(chre_type))) + out.append( + ' const {} *in,\n'.format(self._get_chpp_type_from_chre(chre_type))) + out.append( + ' {} *out'.format(self._get_chre_type_with_prefix(chre_type))) if self.api.structs_and_unions[chre_type]['has_vla_member']: - out.append(",\n") - out.append(" size_t inSize") - out.append(")") + out.append(',\n') + out.append(' size_t inSize') + out.append(')') return out def _gen_string_decoding(self, member_info, annotation): out = [] variable_name = member_info['name'] - out.append("\n") - out.append(" if (in->{}.length == 0) {{\n".format(variable_name)) - out.append(" out->{} = NULL;\n".format(variable_name)) - out.append(" } else {\n") - out.append(" char *{}Out = chppMalloc(in->{}.length);\n".format( + out.append('\n') + out.append(' if (in->{}.length == 0) {{\n'.format(variable_name)) + out.append(' out->{} = NULL;\n'.format(variable_name)) + out.append(' } else {\n') + out.append(' char *{}Out = chppMalloc(in->{}.length);\n'.format( variable_name, variable_name)) - out.append(" if ({}Out == NULL) {{\n".format(variable_name)) - out.append(" return false;\n") - out.append(" }\n\n") - out.append(" memcpy({}Out, &((const uint8_t *)in)[in->{}.offset],\n".format( + out.append(' if ({}Out == NULL) {{\n'.format(variable_name)) + out.append(' return false;\n') + out.append(' }\n\n') + out.append(' memcpy({}Out, &((const uint8_t *)in)[in->{}.offset],\n'.format( variable_name, variable_name)) - out.append(" in->{}.length);\n".format(variable_name)) - out.append(" out->{} = {}Out;\n".format(variable_name, variable_name)) - out.append(" }\n") + out.append(' in->{}.length);\n'.format(variable_name)) + out.append(' out->{} = {}Out;\n'.format(variable_name, variable_name)) + out.append(' }\n') return out - def _gen_vla_decoding(self, member_info, annotation): out = [] variable_name = member_info['name'] chpp_type = self._get_member_type(member_info, True) if member_info['is_nested_type']: - chre_type = self._get_chre_type_with_prefix(member_info['nested_type_name']) + chre_type = self._get_chre_type_with_prefix( + member_info['nested_type_name']) else: chre_type = chpp_type - out.append("\n") - out.append(" if (in->{}.length == 0) {{\n".format(variable_name)) - out.append(" out->{} = NULL;\n".format(variable_name)) - out.append(" }\n") - out.append(" else {\n") - out.append(" if (in->{}.offset + in->{}.length > inSize ||\n".format( + out.append('\n') + out.append(' if (in->{}.length == 0) {{\n'.format(variable_name)) + out.append(' out->{} = NULL;\n'.format(variable_name)) + out.append(' }\n') + out.append(' else {\n') + out.append(' if (in->{}.offset + in->{}.length > inSize ||\n'.format( variable_name, variable_name)) - out.append(" in->{}.length != in->{} * sizeof({})) {{\n".format( + out.append(' in->{}.length != in->{} * sizeof({})) {{\n'.format( variable_name, annotation['length_field'], chpp_type)) - out.append(" return false;\n") - out.append(" }\n\n") + out.append(' return false;\n') + out.append(' }\n\n') if member_info['is_nested_type']: - out.append(" const {} *{}In =\n".format(chpp_type, variable_name)) - out.append(" (const {} *) &((const uint8_t *)in)[in->{}.offset];\n\n".format( + out.append( + ' const {} *{}In =\n'.format(chpp_type, variable_name)) + out.append(' (const {} *) &((const uint8_t *)in)[in->{}.offset];\n\n'.format( chpp_type, variable_name)) - out.append(" {} *{}Out = chppMalloc(in->{} * sizeof({}));\n".format( + out.append(' {} *{}Out = chppMalloc(in->{} * sizeof({}));\n'.format( chre_type, variable_name, annotation['length_field'], chre_type)) - out.append(" if ({}Out == NULL) {{\n".format(variable_name)) - out.append(" return false;\n") - out.append(" }\n\n") + out.append(' if ({}Out == NULL) {{\n'.format(variable_name)) + out.append(' return false;\n') + out.append(' }\n\n') if member_info['is_nested_type']: - out.append(" for (size_t i = 0; i < in->{}; i++) {{\n".format( + out.append(' for (size_t i = 0; i < in->{}; i++) {{\n'.format( annotation['length_field'], variable_name)) - out.append(" {}".format(self._get_assignment_statement_for_field( + out.append(' {}'.format(self._get_assignment_statement_for_field( member_info, in_vla_loop=True, decode_mode=True))) - out.append(" }\n") + out.append(' }\n') else: - out.append(" memcpy({}Out, &((const uint8_t *)in)[in->{}.offset],\n".format( + out.append(' memcpy({}Out, &((const uint8_t *)in)[in->{}.offset],\n'.format( variable_name, variable_name)) - out.append(" in->{} * sizeof({}));\n".format( + out.append(' in->{} * sizeof({}));\n'.format( annotation['length_field'], chre_type)) - out.append(" out->{} = {}Out;\n".format(variable_name, variable_name)) - out.append(" }\n\n") + out.append(' out->{} = {}Out;\n'.format(variable_name, variable_name)) + out.append(' }\n\n') return out def _get_decode_allocation_function_name(self, chre_type): - core_type_name = self._strip_prefix_and_service_from_chre_struct_name(chre_type) - return "chpp{}{}ToChre".format(self.capitalized_service_name, core_type_name) + core_type_name = self._strip_prefix_and_service_from_chre_struct_name( + chre_type) + return 'chpp{}{}ToChre'.format(self.capitalized_service_name, core_type_name) def _gen_decode_allocation_function_signature(self, chre_type, gen_docs=False): out = [] if gen_docs: - out.append("/**\n" - " * Converts from serialized CHPP structure to a CHRE type.\n" - " *\n" - " * @param in Fully-formed CHPP structure.\n" - " * @param in Size of the CHPP structure in bytes.\n" - " *\n" - " * @return If successful, a pointer to a CHRE structure allocated with " - "chppMalloc(). If unsuccessful, null.\n" - " * It is the responsibility of the caller to free the buffer when it is no " - "longer needed via chppFree() or CHPP_FREE_AND_NULLIFY().\n" - " */\n") - - out.append("{} *{}(\n".format( + out.append('/**\n' + ' * Converts from serialized CHPP structure to a CHRE type.\n' + ' *\n' + ' * @param in Fully-formed CHPP structure.\n' + ' * @param in Size of the CHPP structure in bytes.\n' + ' *\n' + ' * @return If successful, a pointer to a CHRE structure allocated with ' + 'chppMalloc(). If unsuccessful, null.\n' + ' * It is the responsibility of the caller to free the buffer when it is no ' + 'longer needed via chppFree() or CHPP_FREE_AND_NULLIFY().\n' + ' */\n') + + out.append('{} *{}(\n'.format( self._get_chre_type_with_prefix(chre_type), self._get_decode_allocation_function_name(chre_type))) - out.append(" const {} *in,\n".format(self._get_chpp_type_from_chre(chre_type))) - out.append(" size_t inSize)") + out.append( + ' const {} *in,\n'.format(self._get_chpp_type_from_chre(chre_type))) + out.append(' size_t inSize)') return out def _gen_decode_allocation_function(self, chre_type): out = [] out.extend(self._gen_decode_allocation_function_signature(chre_type)) - out.append(" {\n") + out.append(' {\n') - out.append(" {} *out = NULL;\n\n".format( + out.append(' {} *out = NULL;\n\n'.format( self._get_chre_type_with_prefix(chre_type))) - out.append(" if (inSize >= sizeof({})) {{\n".format( + out.append(' if (inSize >= sizeof({})) {{\n'.format( self._get_chpp_type_from_chre(chre_type))) - out.append(" out = chppMalloc(sizeof({}));\n".format( + out.append(' out = chppMalloc(sizeof({}));\n'.format( self._get_chre_type_with_prefix(chre_type))) - out.append(" if (out != NULL) {\n") + out.append(' if (out != NULL) {\n') struct_info = self.api.structs_and_unions[chre_type] - out.append(" if (!{}(in, out".format(self._get_decoding_function_name(chre_type))) + out.append(' if (!{}(in, out'.format( + self._get_decoding_function_name(chre_type))) if struct_info['has_vla_member']: - out.append(", inSize") - out.append(")) {") - out.append(" CHPP_FREE_AND_NULLIFY(out);\n") - out.append(" }\n") - - out.append(" }\n") - out.append(" }\n\n") - out.append(" return out;\n") - out.append("}\n") + out.append(', inSize') + out.append(')) {') + out.append(' CHPP_FREE_AND_NULLIFY(out);\n') + out.append(' }\n') + + out.append(' }\n') + out.append(' }\n\n') + out.append(' return out;\n') + out.append('}\n') return out def _gen_decode_allocation_functions(self): @@ -873,8 +910,9 @@ class CodeGenerator: def _gen_decode_allocation_function_signatures(self): out = [] for chre_type in self.json['root_structs']: - out.extend(self._gen_decode_allocation_function_signature(chre_type, True)) - out.append(";\n\n") + out.extend( + self._gen_decode_allocation_function_signature(chre_type, True)) + out.append(';\n\n') return out # ---------------------------------------------------------------------------------------------- @@ -883,219 +921,74 @@ class CodeGenerator: def generate_header_file(self, dry_run=False, skip_clang_format=False): """Creates a C header file for this API and writes it to the file indicated in the JSON.""" - filename = self.service_name + "_types.h" + + filename = self.service_name + '_types.h' if not dry_run: - print("Generating {} ... ".format(filename), end='', flush=True) - output_file = os.path.join(system_chre_abs_path(), CHPP_PARSER_INCLUDE_PATH, filename) + print('Generating {} ... '.format(filename), end='', flush=True) + output_file = os.path.join( + system_chre_abs_path(), CHPP_PARSER_INCLUDE_PATH, filename) header = self.generate_header_string() self._dump_to_file(output_file, header, dry_run, skip_clang_format) if not dry_run: - print("done") + print('done') def generate_header_string(self): """Returns a C header with structure definitions for this API.""" + # To defer concatenation (speed things up), build the file as a list of strings then only # concatenate once at the end out = [LICENSE_HEADER] - header_guard = "CHPP_{}_TYPES_H_".format(self.service_name.upper()) + header_guard = 'CHPP_{}_TYPES_H_'.format(self.service_name.upper()) - out.append("#ifndef {}\n#define {}\n\n".format(header_guard, header_guard)) + out.append('#ifndef {}\n#define {}\n\n'.format( + header_guard, header_guard)) out.extend(self._autogen_notice()) out.extend(self._gen_header_includes()) - out.append("#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n") + out.append('#ifdef __cplusplus\nextern "C" {\n#endif\n\n') out.extend(self._gen_structs_and_unions()) - out.append("\n// Encoding functions (CHRE --> CHPP)\n\n") + out.append('\n// Encoding functions (CHRE --> CHPP)\n\n') out.extend(self._gen_encode_allocation_function_signatures()) - out.append("\n// Decoding functions (CHPP --> CHRE)\n\n") + out.append('\n// Decoding functions (CHPP --> CHRE)\n\n') out.extend(self._gen_decode_allocation_function_signatures()) - out.append("#ifdef __cplusplus\n}\n#endif\n\n") - out.append("#endif // {}\n".format(header_guard)) + out.append('#ifdef __cplusplus\n}\n#endif\n\n') + out.append('#endif // {}\n'.format(header_guard)) return ''.join(out) def generate_conversion_file(self, dry_run=False, skip_clang_format=False): """Generates a .c file with functions for encoding CHRE structs into CHPP and vice versa.""" - filename = self.service_name + "_convert.c" + + filename = self.service_name + '_convert.c' if not dry_run: - print("Generating {} ... ".format(filename), end='', flush=True) + print('Generating {} ... '.format(filename), end='', flush=True) contents = self.generate_conversion_string() - output_file = os.path.join(system_chre_abs_path(), CHPP_PARSER_SOURCE_PATH, filename) + output_file = os.path.join( + system_chre_abs_path(), CHPP_PARSER_SOURCE_PATH, filename) self._dump_to_file(output_file, contents, dry_run, skip_clang_format) if not dry_run: - print("done") + print('done') def generate_conversion_string(self): """Returns C code for encoding CHRE structs into CHPP and vice versa.""" - out = [LICENSE_HEADER, "\n"] + + out = [LICENSE_HEADER, '\n'] out.extend(self._autogen_notice()) out.extend(self._gen_conversion_includes()) - out.append("\n// Encoding (CHRE --> CHPP) size functions\n\n") + out.append('\n// Encoding (CHRE --> CHPP) size functions\n\n') out.extend(self._gen_chpp_sizeof_functions()) - out.append("\n// Encoding (CHRE --> CHPP) conversion functions\n\n") + out.append('\n// Encoding (CHRE --> CHPP) conversion functions\n\n') out.extend(self._gen_conversion_functions(False)) - out.append("\n// Encoding (CHRE --> CHPP) top-level functions\n\n") + out.append('\n// Encoding (CHRE --> CHPP) top-level functions\n\n') out.extend(self._gen_encode_allocation_functions()) - out.append("\n// Decoding (CHPP --> CHRE) conversion functions\n\n") + out.append('\n// Decoding (CHPP --> CHRE) conversion functions\n\n') out.extend(self._gen_conversion_functions(True)) - out.append("\n// Decoding (CHPP --> CHRE) top-level functions\n\n") + out.append('\n// Decoding (CHPP --> CHRE) top-level functions\n\n') out.extend(self._gen_decode_allocation_functions()) return ''.join(out) - - -class ApiParser: - """Given a file-specific set of annotations (extracted from JSON annotations file), parses a - single API header file into data structures suitable for use with code generation. - """ - - def __init__(self, json_obj): - """Initialize and parse the API file described in the provided JSON-derived object. - - :param json_obj: Extracted file-specific annotations from JSON - """ - self.json = json_obj - self.structs_and_unions = {} - self._parse_annotations() - self._parse_api() - - def _parse_annotations(self): - # Convert annotations list to a more usable data structure: dict keyed by structure name, - # containing a dict keyed by field name, containing a list of annotations (as they - # appear in the JSON). In other words, we can easily get all of the annotations for the - # "version" field in "chreWwanCellInfoResult" via - # annotations['chreWwanCellInfoResult']['version']. This is also a defaultdict, so it's safe - # to access if there are no annotations for this structure + field; it'll just give you - # an empty list in that case. - self.annotations = defaultdict(lambda: defaultdict(list)) - for struct_info in self.json['struct_info']: - for annotation in struct_info['annotations']: - self.annotations[struct_info['name']][annotation['field']].append(annotation) - - def _files_to_parse(self): - """Returns a list of files to supply as input to CParser""" - # Input paths for CParser are stored in JSON relative to <android_root>/system/chre - # Reformulate these to absolute paths, and add in some default includes that we always - # supply - chre_project_base_dir = system_chre_abs_path() - default_includes = ["chpp/api_parser/parser_defines.h", - "chre_api/include/chre_api/chre/version.h"] - files = default_includes + self.json['includes'] + [self.json['filename']] - return [os.path.join(chre_project_base_dir, file) for file in files] - - def _parse_structs_and_unions(self): - # Starting with the root structures (i.e. those that will appear at the top-level in one - # or more CHPP messages), build a data structure containing all of the information we'll - # need to emit the CHPP structure definition and conversion code. - structs_and_unions_to_parse = self.json['root_structs'].copy() - while len(structs_and_unions_to_parse) > 0: - type_name = structs_and_unions_to_parse.pop() - if type_name in self.structs_and_unions: - continue - - entry = { - 'appears_in': set(), # Other types this type is nested within - 'dependencies': set(), # Types that are nested in this type - 'has_vla_member': False, # True if this type or any dependency has a VLA member - 'members': [], # Info about each member of this type - } - if type_name in self.parser.defs['structs']: - defs = self.parser.defs['structs'][type_name] - entry['is_union'] = False - elif type_name in self.parser.defs['unions']: - defs = self.parser.defs['unions'][type_name] - entry['is_union'] = True - else: - raise RuntimeError("Couldn't find {} in parsed structs/unions".format(type_name)) - - for member_name, member_type, _ in defs['members']: - member_info = { - 'name': member_name, - 'type': member_type, - 'annotations': self.annotations[type_name][member_name], - 'is_nested_type': False, - } - - if member_type.type_spec.startswith('struct ') or \ - member_type.type_spec.startswith('union '): - member_info['is_nested_type'] = True - member_type_name = member_type.type_spec.split(' ')[1] - member_info['nested_type_name'] = member_type_name - entry['dependencies'].add(member_type_name) - structs_and_unions_to_parse.append(member_type_name) - - entry['members'].append(member_info) - - # Flip a flag if this structure has at least one variable-length array member, which - # means that the encoded size can only be computed at runtime - if not entry['has_vla_member']: - for annotation in self.annotations[type_name][member_name]: - if annotation['annotation'] == "var_len_array": - entry['has_vla_member'] = True - - self.structs_and_unions[type_name] = entry - - # Build reverse linkage of dependency chain (i.e. lookup between a type and the other types - # it appears in) - for type_name, type_info in self.structs_and_unions.items(): - for dependency in type_info['dependencies']: - self.structs_and_unions[dependency]['appears_in'].add(type_name) - - # Bubble up "has_vla_member" to types each type it appears in, i.e. if this flag is set to - # True on a leaf node, then all its ancestors should also have the flag set to True - for type_name, type_info in self.structs_and_unions.items(): - if type_info['has_vla_member']: - types_to_mark = list(type_info['appears_in']) - while len(types_to_mark) > 0: - type_to_mark = types_to_mark.pop() - self.structs_and_unions[type_to_mark]['has_vla_member'] = True - types_to_mark.extend(list(self.structs_and_unions[type_to_mark]['appears_in'])) - - def _parse_api(self): - file_to_parse = self._files_to_parse() - self.parser = CParser(file_to_parse, cache='parser_cache') - self._parse_structs_and_unions() - - -def run(args): - with open('chre_api_annotations.json') as f: - js = json.load(f) - - commit_hash = subprocess.getoutput("git describe --always --long --dirty --exclude '*'") - for file in js: - if args.file_filter: - matched = False - for matcher in args.file_filter: - if matcher in file['filename']: - matched = True - break - if not matched: - print("Skipping {} - doesn't match filter(s) {}".format(file['filename'], - args.file_filter)) - continue - print("Parsing {} ... ".format(file['filename']), end='', flush=True) - api_parser = ApiParser(file) - code_gen = CodeGenerator(api_parser, commit_hash) - print("done") - code_gen.generate_header_file(args.dry_run, args.skip_clang_format) - code_gen.generate_conversion_file(args.dry_run, args.skip_clang_format) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Generate CHPP serialization code from CHRE APIs.') - parser.add_argument('-n', dest='dry_run', action='store_true', - help='Print the output instead of writing to a file') - parser.add_argument('--skip-clang-format', dest='skip_clang_format', action='store_true', - help='Skip running clang-format on the output files (doesn\'t apply to dry ' - 'runs)') - parser.add_argument('file_filter', nargs='*', - help='Filters the input files (filename field in the JSON) to generate a ' - 'subset of the typical output, e.g. "wifi" to just generate conversion' - ' routines for wifi.h') - args = parser.parse_args() - run(args) diff --git a/api_parser/chre_api_annotations.json b/api_parser/chre_api_annotations.json new file mode 100644 index 00000000..f54ff5c1 --- /dev/null +++ b/api_parser/chre_api_annotations.json @@ -0,0 +1,316 @@ +[ + { + "filename": "chre_api/include/chre_api/chre/wwan.h", + "includes": [ + "chre_api/include/chre_api/chre/common.h" + ], + "output_includes": [ + "chpp/common/common_types.h", + "chre_api/chre/wwan.h" + ], + "struct_info": [ + { + "name": "chreWwanCellInfoResult", + "annotations": [ + { + "field": "version", + "annotation": "fixed_value", + "value": "CHRE_WWAN_CELL_INFO_RESULT_VERSION" + }, + { + "field": "errorCode", + "annotation": "enum", + "enum_type": "chreError" + }, + { + "field": "cookie", + "annotation": "fixed_value", + "value": "0" + }, + { + "field": "cookie", + "annotation": "rewrite_type", + "type_override": "uint32_t" + }, + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + }, + { + "field": "cells", + "annotation": "var_len_array", + "length_field": "cellInfoCount" + } + ] + }, + { + "name": "chreWwanCellInfo", + "annotations": [ + { + "field": "cellInfoType", + "annotation": "enum", + "enum_type": "chreWwanCellInfoType" + }, + { + "field": "CellInfo", + "annotation": "union_variant", + "discriminator": "cellInfoType", + "mapping": [ + [ + "CHRE_WWAN_CELL_INFO_TYPE_GSM", + "gsm" + ], + [ + "CHRE_WWAN_CELL_INFO_TYPE_CDMA", + "cdma" + ], + [ + "CHRE_WWAN_CELL_INFO_TYPE_LTE", + "lte" + ], + [ + "CHRE_WWAN_CELL_INFO_TYPE_WCDMA", + "wcdma" + ], + [ + "CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA", + "tdscdma" + ], + [ + "CHRE_WWAN_CELL_INFO_TYPE_NR", + "nr" + ] + ] + }, + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + }, + { + "name": "chreWwanCellIdentityGsm", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + } + ], + "root_structs": [ + "chreWwanCellInfoResult" + ] + }, + { + "filename": "chre_api/include/chre_api/chre/wifi.h", + "includes": [ + "chre_api/include/chre_api/chre/common.h" + ], + "output_includes": [ + "chpp/common/common_types.h", + "chre_api/chre/wifi.h" + ], + "struct_info": [ + { + "name": "chreWifiScanEvent", + "annotations": [ + { + "field": "version", + "annotation": "fixed_value", + "value": "CHRE_WIFI_SCAN_EVENT_VERSION" + }, + { + "field": "scannedFreqList", + "annotation": "var_len_array", + "length_field": "scannedFreqListLen" + }, + { + "field": "results", + "annotation": "var_len_array", + "length_field": "resultCount" + } + ] + }, + { + "name": "chreWifiScanResult", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + }, + { + "name": "chreWifiScanParams", + "annotations": [ + { + "field": "frequencyList", + "annotation": "var_len_array", + "length_field": "frequencyListLen" + }, + { + "field": "ssidList", + "annotation": "var_len_array", + "length_field": "ssidListLen" + } + ] + }, + { + "name": "chreWifiRangingEvent", + "annotations": [ + { + "field": "version", + "annotation": "fixed_value", + "value": "CHRE_WIFI_RANGING_EVENT_VERSION" + }, + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + }, + { + "field": "results", + "annotation": "var_len_array", + "length_field": "resultCount" + } + ] + }, + { + "name": "chreWifiRangingResult", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + }, + { + "name": "chreWifiRangingParams", + "annotations": [ + { + "field": "targetList", + "annotation": "var_len_array", + "length_field": "targetListLen" + } + ] + }, + { + "name": "chreWifiRangingTarget", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + }, + { + "name": "chreWifiNanSubscribeConfig", + "annotations": [ + { + "field": "subscribeType", + "annotation": "enum", + "enum_type": "chreWifiNanSubscribeType" + }, + { + "field": "service", + "annotation": "string" + }, + { + "field": "serviceSpecificInfo", + "annotation": "var_len_array", + "length_field": "serviceSpecificInfoSize" + }, + { + "field": "matchFilter", + "annotation": "var_len_array", + "length_field": "matchFilterLength" + } + ] + }, + { + "name": "chreWifiNanDiscoveryEvent", + "annotations": [ + { + "field": "serviceSpecificInfo", + "annotation": "var_len_array", + "length_field": "serviceSpecificInfoSize" + } + ] + } + ], + "root_structs": [ + "chreWifiScanEvent", + "chreWifiScanParams", + "chreWifiRangingEvent", + "chreWifiRangingParams", + "chreWifiNanSubscribeConfig", + "chreWifiNanDiscoveryEvent", + "chreWifiNanSessionLostEvent", + "chreWifiNanSessionTerminatedEvent", + "chreWifiNanRangingParams" + ] + }, + { + "filename": "chre_api/include/chre_api/chre/gnss.h", + "includes": [ + "chre_api/include/chre_api/chre/common.h" + ], + "output_includes": [ + "chpp/common/common_types.h", + "chre_api/chre/gnss.h" + ], + "struct_info": [ + { + "name": "chreGnssDataEvent", + "annotations": [ + { + "field": "version", + "annotation": "fixed_value", + "value": "CHRE_GNSS_DATA_EVENT_VERSION" + }, + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + }, + { + "field": "measurements", + "annotation": "var_len_array", + "length_field": "measurement_count" + } + ] + }, + { + "name": "chreGnssLocationEvent", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + }, + { + "name": "chreGnssClock", + "annotations": [ + { + "field": "reserved", + "annotation": "fixed_value", + "value": "0" + } + ] + } + ], + "root_structs": [ + "chreGnssDataEvent", + "chreGnssLocationEvent" + ] + } +] diff --git a/api_parser/chre_api_to_chpp.py b/api_parser/chre_api_to_chpp.py new file mode 100755 index 00000000..c3302048 --- /dev/null +++ b/api_parser/chre_api_to_chpp.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +# +# 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. +# + +import argparse +import os +import json +import subprocess + +from api_parser import ApiParser +from chpp_code_generator import CodeGenerator +from utils import system_chre_abs_path + + +def run(args): + with open(system_chre_abs_path() + '/api_parser/chre_api_annotations.json') as f: + js = json.load(f) + + commit_hash = subprocess.getoutput( + "git describe --always --long --dirty --exclude '*'") + for file in js: + if args.file_filter: + matched = False + for matcher in args.file_filter: + if matcher in file['filename']: + matched = True + break + if not matched: + print("Skipping {} - doesn't match filter(s) {}".format(file['filename'], + args.file_filter)) + continue + print('Parsing {} ... '.format(file['filename']), end='', flush=True) + api_parser = ApiParser(file) + code_gen = CodeGenerator(api_parser, commit_hash) + print('done') + code_gen.generate_header_file(args.dry_run, args.skip_clang_format) + code_gen.generate_conversion_file(args.dry_run, args.skip_clang_format) + + +def main(): + parser = argparse.ArgumentParser( + description='Generate CHPP serialization code from CHRE APIs.') + parser.add_argument('-n', dest='dry_run', action='store_true', + help='Print the output instead of writing to a file') + parser.add_argument('--skip-clang-format', dest='skip_clang_format', action='store_true', + help='Skip running clang-format on the output files (doesn\'t apply to dry ' + 'runs)') + parser.add_argument('file_filter', nargs='*', + help='Filters the input files (filename field in the JSON) to generate a ' + 'subset of the typical output, e.g. "wifi" to just generate conversion' + ' routines for wifi.h') + args = parser.parse_args() + run(args) + + +if __name__ == '__main__': + main() diff --git a/chpp/api_parser/parser_defines.h b/api_parser/parser_defines.h index d12561e0..d12561e0 100644 --- a/chpp/api_parser/parser_defines.h +++ b/api_parser/parser_defines.h diff --git a/api_parser/requirements.txt b/api_parser/requirements.txt new file mode 100644 index 00000000..e61f6e6b --- /dev/null +++ b/api_parser/requirements.txt @@ -0,0 +1,2 @@ +future==0.18.3 +pyclibrary==0.2.1 diff --git a/api_parser/utils.py b/api_parser/utils.py new file mode 100644 index 00000000..51279b3f --- /dev/null +++ b/api_parser/utils.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +# +# 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. +# + +import sys +import os + +# Paths for output, relative to system/chre +CHPP_PARSER_INCLUDE_PATH = 'chpp/include/chpp/common' +CHPP_PARSER_SOURCE_PATH = 'chpp/common' + +LICENSE_HEADER = """/* + * 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. + */ +""" + + +def android_build_top_abs_path(): + """Gets the absolute path to the Android build top directory or exits the program.""" + + android_build_top = os.environ.get('ANDROID_BUILD_TOP') + if android_build_top == None or len(android_build_top) == 0: + print('ANDROID_BUILD_TOP not found. Please source build/envsetup.sh.') + sys.exit(1) + + return android_build_top + + +def system_chre_abs_path(): + """Gets the absolute path to the system/chre directory or exits this program.""" + + return android_build_top_abs_path() + '/system/chre' diff --git a/apps/ble_world/ble_world.cc b/apps/ble_world/ble_world.cc index 63bf5fac..0470a0e0 100644 --- a/apps/ble_world/ble_world.cc +++ b/apps/ble_world/ble_world.cc @@ -213,6 +213,10 @@ void handleBatchCompleteEvent(const chreBatchCompleteEvent *event) { event->eventType); } +void handleFlushCompleteEvent(const chreAsyncResult *event) { + LOGI("Received flush complete event with status 0x%" PRIx8, event->errorCode); +} + void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData) { LOGI("Received event 0x%" PRIx16 " from 0x%" PRIx32 " at time %" PRIu64 " ms", @@ -230,7 +234,7 @@ void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType, handleTimerEvent(eventData); break; case CHRE_EVENT_BLE_FLUSH_COMPLETE: - LOGI("Received flush complete"); + handleFlushCompleteEvent(static_cast<const chreAsyncResult *>(eventData)); break; case CHRE_EVENT_BLE_RSSI_READ: handleRssiEvent(static_cast<const chreBleReadRssiEvent *>(eventData)); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.cc index e7666393..6878e260 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.cc @@ -18,6 +18,7 @@ #include <utility> +#include "chre_api/chre.h" #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h" #define LOG_TAG "[NEARBY][ADV_CACHE]" @@ -47,18 +48,23 @@ void AdvReportCache::Refresh() { cache_expire_nanosec_) { // TODO(b/285043291): Refactor cache element by wrapper struct/class // which deallocates data in its destructor. - if (cache_reports_[index].data != nullptr) { - chreHeapFree(const_cast<uint8_t *>(cache_reports_[index].data)); - } - // Don't require to increase index because all elements after the indexed - // one are moved forward one position. - cache_reports_.erase(index); + chreHeapFree(const_cast<uint8_t *>(cache_reports_[index].data)); + // The index does not need to increase because the current element is + // replaced by the end of the element and the list is resized. + cache_reports_.swap(index, cache_reports_.size() - 1); + cache_reports_.resize(cache_reports_.size() - 1); } else { ++index; } } } +void AdvReportCache::RefreshIfNeeded() { + if (cache_reports_.size() > kRefreshCacheCountThreshold) { + Refresh(); + } +} + void AdvReportCache::Push(const chreBleAdvertisingReport &event_report) { #ifdef NEARBY_PROFILE ashProfileBegin(&profile_data_); @@ -97,6 +103,8 @@ void AdvReportCache::Push(const chreBleAdvertisingReport &event_report) { static_cast<uint8_t *>(chreHeapAlloc(sizeof(uint8_t) * dataLength)); if (data == nullptr) { LOGE("Memory allocation failed!"); + // Clean up expired cache elements for which heap memory is allocated. + Refresh(); return; } memcpy(data, event_report.data, dataLength); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.h index 55614835..400d0ce1 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.h +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/adv_report_cache.h @@ -89,6 +89,10 @@ class AdvReportCache { // Removes cached elements older than the cache timeout. void Refresh(); + // Removes cached elements older than the cache timeout if cache count is + // larger than threshold. + void RefreshIfNeeded(); + private: // Weight for a current data point in moving average. static constexpr float kMovingAverageWeight = 0.3f; @@ -98,6 +102,11 @@ class AdvReportCache { static constexpr uint64_t kMaxExpireTimeNanoSec = std::numeric_limits<uint64_t>::max(); + // Default value for threshold cache count to trigger refresh. + // At the worst case, roughly 2KB ( = 255 byte * 8) could be allocated for the + // cache elements exired. + static constexpr size_t kRefreshCacheCountThreshold = 8; + chre::DynamicVector<chreBleAdvertisingReport> cache_reports_; // Current cache timeout value. uint64_t cache_expire_nanosec_ = kMaxExpireTimeNanoSec; diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_main.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_main.cc index ff01df19..7d8fe02b 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_main.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_main.cc @@ -34,7 +34,7 @@ bool nanoappStart(void) { // Initialize the AppManager singleton. // Must be done before invoking AppManagerSingleton::get(). ::nearby::AppManagerSingleton::init(); - return true; + return ::nearby::AppManagerSingleton::get()->IsInitialized(); } void nanoappEnd(void) { diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc index f59a8d3a..491f69d8 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc @@ -22,7 +22,12 @@ #include <utility> +#include "chre_api/chre.h" #include "location/lbs/contexthub/nanoapps/common/math/macros.h" +#ifdef ENABLE_EXTENSION +#include "location/lbs/contexthub/nanoapps/nearby/nearby_extension.h" +#include "location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.nanopb.h" +#endif #include "location/lbs/contexthub/nanoapps/proto/filter.nanopb.h" #include "third_party/contexthub/chre/util/include/chre/util/macros.h" #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h" @@ -41,6 +46,10 @@ AppManager::AppManager() { false /* report_total_thread_cycles */, true /* printCsvFormat */); #endif } +bool AppManager::IsInitialized() { + // AppManager initialized successfully only when BLE scan is available. + return ble_scanner_.isAvailable(); +} void AppManager::HandleEvent(uint32_t sender_instance_id, uint16_t event_type, const void *event_data) { @@ -132,22 +141,22 @@ void AppManager::HandleMatchAdvReports(AdvReportCache &adv_reports_cache) { #ifdef ENABLE_EXTENSION // Matches extended filters. chre::DynamicVector<FilterExtensionResult> filter_extension_results; - chre::DynamicVector<FilterExtensionResult> screen_on_filter_extension_results; filter_extension_.Match(adv_reports_cache.GetAdvReports(), &filter_extension_results, - &screen_on_filter_extension_results); + &screen_on_filter_extension_results_); if (!filter_extension_results.empty()) { SendFilterExtensionResultToHost(filter_extension_results); } - if (!screen_on_filter_extension_results.empty()) { + if (!screen_on_filter_extension_results_.empty()) { if (screen_on_) { LOGD("Send screen on filter extension results back"); - SendFilterExtensionResultToHost(screen_on_filter_extension_results); - } else { - LOGD("Update filter extension result cache"); + SendFilterExtensionResultToHost(screen_on_filter_extension_results_); screen_on_filter_extension_results_.clear(); - screen_on_filter_extension_results_ = - std::move(screen_on_filter_extension_results); + } else { + for (auto &filter_result : screen_on_filter_extension_results_) { + filter_result.RefreshIfNeeded(); + } + LOGD("Updated filter extension result cache"); } } #endif @@ -179,10 +188,8 @@ void AppManager::HandleMessageFromHost(const chreMessageFromHostData *event) { event->messageSize); break; #ifdef ENABLE_EXTENSION - case lbs_FilterMessageType_MESSAGE_FILTER_EXTENSIONS: - if (UpdateFilterExtension(event)) { - UpdateBleScanState(); - } + case lbs_FilterMessageType_MESSAGE_EXT_CONFIG_REQUEST: + HandleHostExtConfigRequest(event); break; #endif } @@ -228,7 +235,9 @@ void AppManager::HandleHostConfigRequest(const uint8_t *message, LOGD("received screen config %d", screen_on_); if (screen_on_) { fp_screen_on_sent_ = false; - ble_scanner_.Flush(); + if (ble_scanner_.isScanning()) { + ble_scanner_.Flush(); + } // TODO(b/255338604): used the default report delay value only because // FP offload scan doesn't use low latency report delay. // when the flushed packet droping issue is resolved, try to reconfigure @@ -248,7 +257,7 @@ void AppManager::HandleHostConfigRequest(const uint8_t *message, } #ifdef ENABLE_EXTENSION if (!screen_on_filter_extension_results_.empty()) { - LOGD("send filter extension result from cache"); + LOGD("try to send filter extension result from cache"); SendFilterExtensionResultToHost(screen_on_filter_extension_results_); screen_on_filter_extension_results_.clear(); } @@ -428,43 +437,87 @@ bool AppManager::EncodeFilterResult(const nearby_BleFilterResult &filter_result, } #ifdef ENABLE_EXTENSION -bool AppManager::UpdateFilterExtension(const chreMessageFromHostData *event) { +void AppManager::HandleHostExtConfigRequest( + const chreMessageFromHostData *event) { chreHostEndpointInfo host_info; - chre::DynamicVector<chreBleGenericFilter> generic_filters; - nearby_extension_FilterConfigResult config_result = - nearby_extension_FilterConfigResult_init_zero; - if (chreGetHostEndpointInfo(event->hostEndpoint, &host_info)) { - if (host_info.isNameValid) { - LOGD("host package name %s", host_info.packageName); - // TODO(b/283035791) replace "android" with the package name of Nearby - // Mainline host. - // The event is sent from Nearby Mainline host, not OEM services. - if (strcmp(host_info.packageName, "android") == 0) { - return false; - } - filter_extension_.Update(host_info, *event, &generic_filters, - &config_result); - if (config_result.result == CHREX_NEARBY_RESULT_OK) { - if (!ble_scanner_.UpdateFilters(event->hostEndpoint, - &generic_filters)) { - config_result.result = CHREX_NEARBY_RESULT_INTERNAL_ERROR; + pb_istream_t stream = + pb_istream_from_buffer(static_cast<const uint8_t *>(event->message), + static_cast<size_t>(event->messageSize)); + nearby_extension_ExtConfigRequest config = + nearby_extension_ExtConfigRequest_init_default; + nearby_extension_ExtConfigResponse config_response = + nearby_extension_ExtConfigResponse_init_zero; + + if (!pb_decode(&stream, nearby_extension_ExtConfigRequest_fields, &config)) { + LOGE("Failed to decode extended config msg: %s", PB_GET_ERROR(&stream)); + } else if (!chreGetHostEndpointInfo(event->hostEndpoint, &host_info)) { + LOGE("Failed to get host info."); + config_response.result = CHREX_NEARBY_RESULT_INTERNAL_ERROR; + } else if (!host_info.isNameValid) { + LOGE("Failed to get package name"); + config_response.result = CHREX_NEARBY_RESULT_UNKNOWN_PACKAGE; + } else { + LOGD("*** Receiving %s extended config ***", + GetExtConfigNameFromTag(config.which_config)); + + switch (config.which_config) { + case nearby_extension_ExtConfigRequest_filter_config_tag: + if (!HandleExtFilterConfig(host_info, config.config.filter_config, + &config_response)) { + LOGE("Failed to handle extended filter config"); } - } - } else { - LOGE("host package name invalid."); - config_result.result = CHREX_NEARBY_RESULT_UNKNOWN_PACKAGE; + break; + case nearby_extension_ExtConfigRequest_service_config_tag: + if (!HandleExtServiceConfig(host_info, config.config.service_config, + &config_response)) { + LOGE("Failed to handle extended service config"); + } + break; + default: + LOGE("Unknown extended config %d", config.which_config); + config_response.result = CHREX_NEARBY_RESULT_FEATURE_NOT_SUPPORTED; + break; } - } else { - config_result.result = CHREX_NEARBY_RESULT_INTERNAL_ERROR; - LOGE("Failed to get host info."); } - SendFilterExtensionConfigResultToHost(event->hostEndpoint, config_result); + SendExtConfigResponseToHost(config.request_id, event->hostEndpoint, + config_response); +} + +bool AppManager::HandleExtFilterConfig( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_FilterConfig &config, + nearby_extension_ExtConfigResponse *config_response) { + chre::DynamicVector<chreBleGenericFilter> generic_filters; + + filter_extension_.Update(host_info, config, &generic_filters, + config_response); + if (config_response->result != CHREX_NEARBY_RESULT_OK) { + return false; + } + if (!ble_scanner_.UpdateFilters(host_info.hostEndpointId, &generic_filters)) { + config_response->result = CHREX_NEARBY_RESULT_INTERNAL_ERROR; + return false; + } + UpdateBleScanState(); return true; } -void AppManager::SendFilterExtensionConfigResultToHost( - uint16_t host_end_point, - const nearby_extension_FilterConfigResult &config_result) { +bool AppManager::HandleExtServiceConfig( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_ServiceConfig &config, + nearby_extension_ExtConfigResponse *config_response) { + filter_extension_.ConfigureService(host_info, config, config_response); + if (config_response->result != CHREX_NEARBY_RESULT_OK) { + return false; + } + return true; +} + +void AppManager::SendExtConfigResponseToHost( + uint32_t request_id, uint16_t host_end_point, + nearby_extension_ExtConfigResponse &config_response) { + config_response.has_request_id = true; + config_response.request_id = request_id; uint8_t *msg_buf = (uint8_t *)chreHeapAlloc(kFilterResultsBufSize); if (msg_buf == nullptr) { LOGE("Failed to allocate message buffer of size %zu for dispatch.", @@ -472,23 +525,23 @@ void AppManager::SendFilterExtensionConfigResultToHost( return; } size_t encoded_size; - if (!FilterExtension::EncodeConfigResult( - config_result, ByteArray(msg_buf, kFilterResultsBufSize), + if (!FilterExtension::EncodeConfigResponse( + config_response, ByteArray(msg_buf, kFilterResultsBufSize), &encoded_size)) { chreHeapFree(msg_buf); return; } - auto resp_type = (config_result.result == CHREX_NEARBY_RESULT_OK - ? lbs_FilterMessageType_MESSAGE_SUCCESS - : lbs_FilterMessageType_MESSAGE_FAILURE); - if (chreSendMessageWithPermissions( - msg_buf, encoded_size, resp_type, host_end_point, + msg_buf, encoded_size, + lbs_FilterMessageType_MESSAGE_EXT_CONFIG_RESPONSE, host_end_point, CHRE_MESSAGE_PERMISSION_BLE, [](void *msg, size_t /*size*/) { chreHeapFree(msg); })) { - LOGD("Successfully sent the filter extension config result."); + LOGD("Successfully sent the extended config response for request %" PRIu32 + ".", + request_id); } else { - LOGE("Failed to send filter extension config result."); + LOGE("Failed to send extended config response for request %" PRIu32 ".", + request_id); } } @@ -499,29 +552,43 @@ void AppManager::SendFilterExtensionResultToHost( if (reports.empty()) { continue; } - uint8_t *msg_buf = (uint8_t *)chreHeapAlloc(kFilterResultsBufSize); - if (msg_buf == nullptr) { - LOGE("Failed to allocate message buffer of size %zu for dispatch.", - kFilterResultsBufSize); - return; - } - size_t encoded_size; - if (!FilterExtension::Encode(reports, - ByteArray(msg_buf, kFilterResultsBufSize), - &encoded_size)) { - chreHeapFree(msg_buf); - return; - } - if (chreSendMessageWithPermissions( - msg_buf, encoded_size, lbs_FilterMessageType_MESSAGE_FILTER_RESULTS, - result.end_point, CHRE_MESSAGE_PERMISSION_BLE, - [](void *msg, size_t /*size*/) { chreHeapFree(msg); })) { - LOGD("Successfully sent the filter extension result."); - } else { - LOGE("Failed to send filter extension result."); + for (auto &report : reports) { + size_t encoded_size; + uint8_t *msg_buf = (uint8_t *)chreHeapAlloc(kFilterResultsBufSize); + if (msg_buf == nullptr) { + LOGE("Failed to allocate message buffer of size %zu for dispatch.", + kFilterResultsBufSize); + return; + } + if (!FilterExtension::EncodeAdvReport( + report, ByteArray(msg_buf, kFilterResultsBufSize), + &encoded_size)) { + chreHeapFree(msg_buf); + return; + } + if (chreSendMessageWithPermissions( + msg_buf, encoded_size, + lbs_FilterMessageType_MESSAGE_FILTER_RESULTS, result.end_point, + CHRE_MESSAGE_PERMISSION_BLE, + [](void *msg, size_t /*size*/) { chreHeapFree(msg); })) { + LOGD("Successfully sent the filter extension result."); + } else { + LOGE("Failed to send filter extension result."); + } } } } + +const char *AppManager::GetExtConfigNameFromTag(pb_size_t config_tag) { + switch (config_tag) { + case nearby_extension_ExtConfigRequest_filter_config_tag: + return "FilterConfig"; + case nearby_extension_ExtConfigRequest_service_config_tag: + return "ServiceConfig"; + default: + return "Unknown"; + } +} #endif } // namespace nearby diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h index bed03e43..27d0f585 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h @@ -40,6 +40,9 @@ class AppManager { public: AppManager(); + // Returns true if AppManager is initialized successfully. + bool IsInitialized(); + // Handles an event from CHRE. void HandleEvent(uint32_t sender_instance_id, uint16_t event_type, const void *event_data); @@ -47,10 +50,13 @@ class AppManager { private: // Handles a message from host. void HandleMessageFromHost(const chreMessageFromHostData *event); + // Acknowledge a host's SET_FILTER_REQUEST to indicate success or failure. void RespondHostSetFilterRequest(bool success); + // Handles config request from the host. void HandleHostConfigRequest(const uint8_t *message, uint32_t message_size); + // Handles advertise reports to match filters. // Advertise reports will be cleared at the end of this function. void HandleMatchAdvReports(AdvReportCache &adv_reports); @@ -94,12 +100,29 @@ class AppManager { size_t &encoded_size); #ifdef ENABLE_EXTENSION - static void SendFilterExtensionConfigResultToHost( - uint16_t host_end_point, - const nearby_extension_FilterConfigResult &config_result); + // Handles extended config request from the host. + void HandleHostExtConfigRequest(const chreMessageFromHostData *event); + + // Handles extended filter config request from the host. + bool HandleExtFilterConfig( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_FilterConfig &config, + nearby_extension_ExtConfigResponse *config_response); + + // Handles extended service config request from the host. + bool HandleExtServiceConfig( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_ServiceConfig &config, + nearby_extension_ExtConfigResponse *config_response); + + static void SendExtConfigResponseToHost( + uint32_t request_id, uint16_t host_end_point, + nearby_extension_ExtConfigResponse &config_response); static void SendFilterExtensionResultToHost( chre::DynamicVector<FilterExtensionResult> &filter_results); + + static const char *GetExtConfigNameFromTag(pb_size_t config_tag); #endif // TODO(b/193756395): Find the optimal size or compute the size in runtime. // Note: the nanopb API pb_get_encoded_size @@ -114,7 +137,11 @@ class AppManager { static constexpr size_t kFilterResultsBufSize = 300; // Default value for Fast Pair cache to expire. static constexpr uint64_t kFpFilterResultExpireTimeNanoSec = +#ifdef USE_SHORT_FP_CACHE_TO + 3 * chre::kOneSecondInNanoseconds; +#else 5 * chre::kOneSecondInNanoseconds; +#endif Filter filter_; #ifdef ENABLE_EXTENSION diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc index f77c0cf4..d4e09f4a 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc @@ -222,6 +222,7 @@ void BleScanner::Restart() { } } chreBleScanFilter scan_filter; + memset(&scan_filter, 0, sizeof(scan_filter)); scan_filter.rssiThreshold = CHRE_BLE_RSSI_THRESHOLD_NONE; scan_filter.scanFilters = generic_filters.data(); scan_filter.scanFilterCount = static_cast<uint8_t>(generic_filters.size()); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h index 08c51d1e..fb06f53c 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h @@ -64,6 +64,16 @@ class BleScanner { return is_batch_flushing_; } + // Returns whether BLE scan is running. + bool isScanning() { + return is_started_; + } + + // Returns true if BLE scan is available in the device. + bool isAvailable() { + return is_ble_scan_supported_; + } + // Returns whether BLE batch scan is supported. bool IsBatchSupported() { return is_batch_supported_; diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_account_data.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_account_data.cc index af8c51c2..b564cb81 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_account_data.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_account_data.cc @@ -99,7 +99,7 @@ FastPairAccountData FastPairAccountData::Parse(const ByteArray &service_data) { } // filter and salt are required. if (filter.length == 0 || salt.length == 0) { - LOGE( + LOGD( "Invalid Fast Pair service data with filter length %zu and salt length " "%zu.", filter.length, salt.length); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_filter.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_filter.cc index 94074953..f7513f33 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_filter.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/fast_pair_filter.cc @@ -154,6 +154,12 @@ bool MatchSubsequentPair(const uint8_t *account_key, kFastPairUuid); return false; } + if (service_data.length == kFastPairModelIdLength) { + LOGD( + "Initial Pair advertisements, not proceed to subsequent pair " + "filtering."); + return false; + } FastPairAccountData account_data = FastPairAccountData::Parse( ByteArray(const_cast<uint8_t *>(service_data.data), service_data.length)); if (!account_data.is_valid) { diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter.cc index 7aded384..9254217b 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter.cc @@ -20,8 +20,10 @@ #include <inttypes.h> #include <pb_decode.h> +#include <cstdint> #include <iterator> +#include "chre_api/chre.h" #include "location/lbs/contexthub/nanoapps/nearby/ble_scan_record.h" #include "location/lbs/contexthub/nanoapps/nearby/fast_pair_filter.h" #ifdef ENABLE_PRESENCE @@ -111,6 +113,10 @@ void Filter::MatchBle( // The buffer size has already been checked. static_assert(std::size(result.bluetooth_address) == CHRE_BLE_ADDRESS_LEN); memcpy(result.bluetooth_address, report.address, std::size(report.address)); + result.has_timestamp_ns = true; + result.timestamp_ns = + report.timestamp + + static_cast<uint64_t>(chreGetEstimatedHostTimeOffset()); if (MatchFastPair(ble_filters_.filter[filter_index], record, &result)) { LOGD("Add a matched Fast Pair filter result"); fp_filter_results->push_back(result); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.cc index 11e490c9..abc004e9 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.cc @@ -5,8 +5,12 @@ #include <pb_encode.h> #include <cstddef> +#include <cstdint> #include <utility> +#include "chre_api/chre.h" +#include "location/lbs/contexthub/nanoapps/nearby/nearby_extension.h" +#include "location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.nanopb.h" #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h" #define LOG_TAG "[NEARBY][FILTER_EXTENSION]" @@ -15,34 +19,23 @@ namespace nearby { const size_t kChreBleGenericFilterDataSize = 29; -constexpr nearby_extension_FilterConfig kEmptyFilterConfig = - nearby_extension_FilterConfig_init_zero; - constexpr nearby_extension_FilterResult kEmptyFilterResult = nearby_extension_FilterResult_init_zero; void FilterExtension::Update( - const chreHostEndpointInfo &host_info, const chreMessageFromHostData &event, + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_FilterConfig &filter_config, chre::DynamicVector<chreBleGenericFilter> *generic_filters, - nearby_extension_FilterConfigResult *config_result) { + nearby_extension_ExtConfigResponse *config_response) { LOGD("Update extension filter"); - nearby_extension_FilterConfig filter_config = kEmptyFilterConfig; - pb_istream_t stream = pb_istream_from_buffer( - static_cast<const uint8_t *>(event.message), event.messageSize); - if (!pb_decode(&stream, nearby_extension_FilterConfig_fields, - &filter_config)) { - LOGE("Failed to decode a Filters message."); - return; - } const int32_t host_index = FindOrCreateHostIndex(host_info); if (host_index < 0) { LOGE("Failed to find or create the host."); return; } - const chreHostEndpointInfo &host = - host_list_[static_cast<size_t>(host_index)]; - config_result->has_result = true; - config_result->has_vendor_status = true; + HostEndpointInfo &host = host_list_[static_cast<size_t>(host_index)]; + config_response->has_result = true; + config_response->has_vendor_status = true; // Returns hardware filters. for (int i = 0; i < filter_config.hardware_filter_count; i++) { @@ -64,32 +57,52 @@ void FilterExtension::Update( chrexNearbyExtendedFilterConfig config; config.data = filter_config.oem_filter; config.data_length = filter_config.oem_filter_length; + host.cache_expire_ms = filter_config.cache_expire_ms; - config_result->result = - static_cast<int32_t>(chrexNearbySetExtendedFilterConfig( - &host, &scan_filter, &config, &config_result->vendor_status)); - if (config_result->result != CHREX_NEARBY_RESULT_OK) { - LOGE("Failed to config filters, result %" PRId32, config_result->result); + config_response->result = static_cast<int32_t>( + chrexNearbySetExtendedFilterConfig(&host.host_info, &scan_filter, &config, + &config_response->vendor_status)); + if (config_response->result != CHREX_NEARBY_RESULT_OK) { + LOGE("Failed to config filters, result %" PRId32, config_response->result); host_list_.erase(static_cast<size_t>(host_index)); return; } // Removes the host if both hardware and oem filters are empty. if (filter_config.hardware_filter_count == 0 && filter_config.oem_filter_length == 0) { - LOGD("Remove host: id (%d), package name (%s)", host.hostEndpointId, - host.isNameValid ? host.packageName : "unknown"); + LOGD("Remove host: id (%d), package name (%s)", + host.host_info.hostEndpointId, + host.host_info.isNameValid ? host.host_info.packageName : "unknown"); host_list_.erase(static_cast<size_t>(host_index)); } } +void FilterExtension::ConfigureService( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_ServiceConfig &service_config, + nearby_extension_ExtConfigResponse *config_response) { + LOGD("Configure extension service"); + config_response->has_result = true; + config_response->has_vendor_status = true; + + chrexNearbyExtendedServiceConfig config; + config.data = service_config.data; + config.data_length = service_config.data_length; + + config_response->result = + static_cast<int32_t>(chrexNearbySetExtendedServiceConfig( + &host_info, &config, &config_response->vendor_status)); +} + int32_t FilterExtension::FindOrCreateHostIndex( const chreHostEndpointInfo &host_info) { for (size_t index = 0; index < host_list_.size(); index++) { - if (host_info.hostEndpointId == host_list_[index].hostEndpointId) { + if (host_info.hostEndpointId == + host_list_[index].host_info.hostEndpointId) { return static_cast<int32_t>(index); } } - if (!host_list_.push_back(host_info)) { + if (!host_list_.push_back(HostEndpointInfo(host_info))) { LOGE("Failed to add new host info."); return -1; } @@ -101,9 +114,10 @@ int32_t FilterExtension::FindOrCreateHostIndex( * Returns the index of the entry. */ size_t AddToFilterResults( - uint16_t endponit_id, + const HostEndpointInfo &host, chre::DynamicVector<FilterExtensionResult> *filter_results) { - FilterExtensionResult result(endponit_id); + FilterExtensionResult result(host.host_info.hostEndpointId, + host.cache_expire_ms); size_t idx = filter_results->find(result); if (filter_results->size() == idx) { filter_results->push_back(std::move(result)); @@ -115,12 +129,12 @@ void FilterExtension::Match( const chre::DynamicVector<chreBleAdvertisingReport> &ble_adv_list, chre::DynamicVector<FilterExtensionResult> *filter_results, chre::DynamicVector<FilterExtensionResult> *screen_on_filter_results) { - for (const chreHostEndpointInfo &host_info : host_list_) { - size_t idx = AddToFilterResults(host_info.hostEndpointId, filter_results); - size_t screen_on_idx = - AddToFilterResults(host_info.hostEndpointId, screen_on_filter_results); + for (const HostEndpointInfo &host : host_list_) { + size_t idx = AddToFilterResults(host, filter_results); + size_t screen_on_idx = AddToFilterResults(host, screen_on_filter_results); for (const auto &ble_adv_report : ble_adv_list) { - switch (chrexNearbyMatchExtendedFilter(&host_info, &ble_adv_report)) { + switch ( + chrexNearbyMatchExtendedFilter(&host.host_info, &ble_adv_report)) { case CHREX_NEARBY_FILTER_ACTION_IGNORE: continue; case CHREX_NEARBY_FILTER_ACTION_DELIVER_ON_WAKE: @@ -137,20 +151,20 @@ void FilterExtension::Match( } } -bool FilterExtension::EncodeConfigResult( - const nearby_extension_FilterConfigResult &config_result, +bool FilterExtension::EncodeConfigResponse( + const nearby_extension_ExtConfigResponse &config_response, ByteArray data_buf, size_t *encoded_size) { if (!pb_get_encoded_size(encoded_size, - nearby_extension_FilterConfigResult_fields, - &config_result)) { - LOGE("Failed to get filter config result size."); + nearby_extension_ExtConfigResponse_fields, + &config_response)) { + LOGE("Failed to get extended config response size."); return false; } pb_ostream_t ostream = pb_ostream_from_buffer(data_buf.data, data_buf.length); - if (!pb_encode(&ostream, nearby_extension_FilterConfigResult_fields, - &config_result)) { - LOGE("Unable to encode protobuf for FilterConfigResult, error %s", + if (!pb_encode(&ostream, nearby_extension_ExtConfigResponse_fields, + &config_response)) { + LOGE("Unable to encode protobuf for ExtConfigResponse, error %s", PB_GET_ERROR(&ostream)); return false; } @@ -207,4 +221,52 @@ bool FilterExtension::Encode( return true; } +bool FilterExtension::EncodeAdvReport(chreBleAdvertisingReport &report, + ByteArray data_buf, + size_t *encoded_size) { + nearby_extension_FilterResult filter_result = kEmptyFilterResult; + nearby_extension_ChreBleAdvertisingReport &report_proto = + filter_result.report[0]; + report_proto.has_timestamp = true; + report_proto.timestamp = + report.timestamp + + static_cast<uint64_t>(chreGetEstimatedHostTimeOffset()); + report_proto.has_event_type_and_data_status = true; + report_proto.event_type_and_data_status = report.eventTypeAndDataStatus; + report_proto.has_address = true; + for (size_t i = 0; i < 6; i++) { + report_proto.address[i] = report.address[i]; + } + report_proto.has_tx_power = true; + report_proto.tx_power = report.txPower; + report_proto.has_rssi = true; + report_proto.rssi = report.rssi; + report_proto.has_data_length = true; + report_proto.data_length = report.dataLength; + if (report.dataLength > 0) { + report_proto.has_data = true; + } + for (size_t i = 0; i < report.dataLength; i++) { + report_proto.data[i] = report.data[i]; + } + filter_result.report_count = 1; + filter_result.has_error_code = true; + filter_result.error_code = nearby_extension_FilterResult_ErrorCode_SUCCESS; + + if (!pb_get_encoded_size(encoded_size, nearby_extension_FilterResult_fields, + &filter_result)) { + LOGE("Failed to get filter extension result size."); + return false; + } + pb_ostream_t ostream = pb_ostream_from_buffer(data_buf.data, data_buf.length); + + if (!pb_encode(&ostream, nearby_extension_FilterResult_fields, + &filter_result)) { + LOGE("Unable to encode protobuf for FilterExtensionResults, error %s", + PB_GET_ERROR(&ostream)); + return false; + } + return true; +} + } // namespace nearby diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.h index 996e4b93..99c9b20c 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.h +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/filter_extension.h @@ -2,6 +2,7 @@ #define LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_FILTER_EXTENSION_H_ #include <chre.h> +#include <cstdint> #include <utility> #include "location/lbs/contexthub/nanoapps/nearby/adv_report_cache.h" @@ -13,16 +14,28 @@ namespace nearby { -struct FilterExtensionResult { - // Default value for filter extension result to expire. - static constexpr uint64_t kFilterExtensionReportExpireTimeMilliSec = - 5 * chre::kOneSecondInMilliseconds; +// Default value for filter extension result to expire. +static constexpr uint64_t kFilterExtensionReportExpireTimeMilliSec = + 5 * chre::kOneSecondInMilliseconds; + +struct HostEndpointInfo { + chreHostEndpointInfo host_info; + // Host-specific configurations. + uint32_t cache_expire_ms; + + explicit HostEndpointInfo(chreHostEndpointInfo host_info) + : host_info(std::move(host_info)) {} +}; +struct FilterExtensionResult { const uint16_t end_point; AdvReportCache reports; - explicit FilterExtensionResult(uint16_t end_point) : end_point(end_point) { - reports.SetCacheTimeout(kFilterExtensionReportExpireTimeMilliSec); + explicit FilterExtensionResult( + uint16_t end_point, + uint64_t expire_time_ms = kFilterExtensionReportExpireTimeMilliSec) + : end_point(end_point) { + reports.SetCacheTimeout(expire_time_ms); } FilterExtensionResult(FilterExtensionResult &&src) @@ -40,6 +53,17 @@ struct FilterExtensionResult { reports.Clear(); } + // Removes advertising reports older than the cache timeout. + void Refresh() { + reports.Refresh(); + } + + // Removes advertising reports older than the cache timeout if the cache size + // hits a threshold. + void RefreshIfNeeded() { + reports.RefreshIfNeeded(); + } + // Returns advertise reports in cache. chre::DynamicVector<chreBleAdvertisingReport> &GetAdvReports() { return reports.GetAdvReports(); @@ -60,14 +84,21 @@ struct FilterExtensionResult { class FilterExtension { public: - // Updates extended filters (passed in the event) for each end host. + // Updates extended filters for each end host. // Returns generic_filters, which can be used to restart BLE scan. - // If config_result->result is not CHREX_NEARBY_RESULT_OK, the returned + // If config_response->result is not CHREX_NEARBY_RESULT_OK, the returned // generic_filters should be ignored. - void Update(const chreHostEndpointInfo &host_info, - const chreMessageFromHostData &event, - chre::DynamicVector<chreBleGenericFilter> *generic_filters, - nearby_extension_FilterConfigResult *config_result); + void Update( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_FilterConfig &filter_config, + chre::DynamicVector<chreBleGenericFilter> *generic_filters, + nearby_extension_ExtConfigResponse *config_response); + + // Configures OEM service data. + void ConfigureService( + const chreHostEndpointInfo &host_info, + const nearby_extension_ExtConfigRequest_ServiceConfig &service_config, + nearby_extension_ExtConfigResponse *config_response); // Matches BLE advertisements. Returns matched advertisements in // filter_results. If the results is only delivered when screen is on, @@ -77,10 +108,10 @@ class FilterExtension { chre::DynamicVector<FilterExtensionResult> *filter_results, chre::DynamicVector<FilterExtensionResult> *screen_on_filter_results); - // Serializes config_result into data_buf. The encoded size is filled in - // encoded_size. Returns true for successful encoding. - static bool EncodeConfigResult( - const nearby_extension_FilterConfigResult &config_result, + // Serializes extended config response into data_buf. The encoded size is + // filled in encoded_size. Returns true for successful encoding. + static bool EncodeConfigResponse( + const nearby_extension_ExtConfigResponse &config_response, ByteArray data_buf, size_t *encoded_size); // Encodes reports into data_buf. The reports are converted to @@ -89,6 +120,11 @@ class FilterExtension { const chre::DynamicVector<chreBleAdvertisingReport> &reports, ByteArray data_buf, size_t *encoded_size); + // Encodes a single report into data_buf. The report are converted to + // nearby_extension_FilterResult before the serialization. + static bool EncodeAdvReport(chreBleAdvertisingReport &report, + ByteArray data_buf, size_t *encoded_size); + // Whether host list is empty. The host which doesn't have filter // configuration or was disconnected should be removed in the host list. bool IsEmpty() const { @@ -100,7 +136,7 @@ class FilterExtension { int32_t FindOrCreateHostIndex(const chreHostEndpointInfo &host_info); private: - chre::DynamicVector<chreHostEndpointInfo> host_list_; + chre::DynamicVector<HostEndpointInfo> host_list_; }; } // namespace nearby diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.c b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.c index 4b9370ea..54075924 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.c +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.c @@ -1,7 +1,9 @@ #include "location/lbs/contexthub/nanoapps/nearby/nearby_extension.h" -#include "stddef.h" +#include <string.h> +#include "chre_api/chre.h" +#include "stddef.h" #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h" #define LOG_TAG "[NEARBY][FILTER_EXTENSION]" @@ -28,6 +30,7 @@ static const uint16_t EXT_FILTER_CONFIG_DATA_MASK_INDEX = 1; static uint8_t EXT_FILTER_DATA = 0; static uint8_t EXT_FILTER_DATA_MASK = 0; #define MAX_GENERIC_FILTER_COUNT 10 +#define MAX_SERVICE_CONFIG_LEN 10 struct hwBleScanFilter { int8_t rssi_threshold; @@ -38,6 +41,7 @@ static struct hwBleScanFilter HW_SCAN_FILTER = { .rssi_threshold = CHRE_BLE_RSSI_THRESHOLD_NONE, .scan_filter_count = 0, }; +static uint8_t EXT_SERVICE_CONFIG[MAX_SERVICE_CONFIG_LEN] = {0}; const char kHostPackageName[] = "com.google.android.nearby.offload.reference"; uint32_t chrexNearbySetExtendedFilterConfig( @@ -50,7 +54,12 @@ uint32_t chrexNearbySetExtendedFilterConfig( LOGE("Invalid scan_filter configuration"); return CHREX_NEARBY_RESULT_INTERNAL_ERROR; } - // Performs a deep copy of the hardware scan filter structure + if (!host_info->isNameValid || + strcmp(host_info->packageName, kHostPackageName) != 0) { + LOGE("Unknown package: %s", host_info->packageName); + return CHREX_NEARBY_RESULT_UNKNOWN_PACKAGE; + } + // Performs a deep copy of the hardware scan filter structure. HW_SCAN_FILTER.rssi_threshold = scan_filter->rssiThreshold; HW_SCAN_FILTER.scan_filter_count = scan_filter->scanFilterCount; for (size_t i = 0; i < HW_SCAN_FILTER.scan_filter_count; ++i) { @@ -71,17 +80,33 @@ uint32_t chrexNearbySetExtendedFilterConfig( HW_SCAN_FILTER.scan_filters[i].type, HW_SCAN_FILTER.scan_filters[i].len); } - if (host_info->isNameValid && - strcmp(host_info->packageName, kHostPackageName) == 0) { - EXT_FILTER_DATA = config->data[EXT_FILTER_CONFIG_DATA_INDEX]; - EXT_FILTER_DATA_MASK = config->data[EXT_FILTER_CONFIG_DATA_MASK_INDEX]; - } + EXT_FILTER_DATA = config->data[EXT_FILTER_CONFIG_DATA_INDEX]; + EXT_FILTER_DATA_MASK = config->data[EXT_FILTER_CONFIG_DATA_MASK_INDEX]; *vendorStatusCode = 0; LOGD("Set EXT_FILTER_DATA 0x%02X", EXT_FILTER_DATA); LOGD("Set EXT_FILTER_DATA_MASK 0x%02X", EXT_FILTER_DATA_MASK); return CHREX_NEARBY_RESULT_OK; } +uint32_t chrexNearbySetExtendedServiceConfig( + const struct chreHostEndpointInfo *host_info, + const struct chrexNearbyExtendedServiceConfig *config, + uint32_t *vendorStatusCode) { + if (!host_info->isNameValid || + strcmp(host_info->packageName, kHostPackageName) != 0) { + LOGE("Unknown package: %s", host_info->packageName); + return CHREX_NEARBY_RESULT_UNKNOWN_PACKAGE; + } + if (config->data_length > MAX_SERVICE_CONFIG_LEN) { + return CHREX_NEARBY_RESULT_OUT_OF_RESOURCES; + } + // Performs a deep copy of the service configuration. + memcpy(EXT_SERVICE_CONFIG, config->data, config->data_length); + *vendorStatusCode = 0; + LOGD("Set EXT_SERVICE_CONFIG 0x%02X", EXT_SERVICE_CONFIG[0]); + return CHREX_NEARBY_RESULT_OK; +} + uint32_t chrexNearbyMatchExtendedFilter( const struct chreHostEndpointInfo *host_info, const struct chreBleAdvertisingReport *report) { diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.h index 7d26d68b..a34a1d90 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.h +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension.h @@ -13,6 +13,12 @@ struct chrexNearbyExtendedFilterConfig { const uint8_t *data; //!< Vendor-defined payload }; +//! Contains vendor-defined data for configuring vendor service in library +struct chrexNearbyExtendedServiceConfig { + size_t data_length; //!< Number of bytes in data + const uint8_t *data; //!< Vendor-defined payload +}; + enum chrexNearbyResult { //! Operation completed successfully CHREX_NEARBY_RESULT_OK = 0, @@ -73,6 +79,32 @@ uint32_t chrexNearbySetExtendedFilterConfig( const struct chrexNearbyExtendedFilterConfig *config, uint32_t *vendorStatusCode); +/** + * Configures vendor-defined service data sent by a vendor/OEM service on the + * host. This is called by the Nearby nanoapp when it receives a + * ChreNearbyExtendedService message, and the result is sent back to the host + * endpoint that made the request. The pointers supplied for the + * parameters are not guaranteed to be valid after this call. The OEM library + * should perform a deep copy of the structure if we want to store them in the + * library. + * + * @param host_info The meta data for a host end point that sent the message, + * obtained from chreHostEndpointInfo. The implementation must ensure that + * messages from a given host end point are only provided to the vendor + * library explicitly associated with that host end point. + * @param config Configuration data in a vendor-defined format. + * @param[out] vendorStatusCode Optional vendor-defined status code that will be + * returned to the vendor service regardless of the return value of this + * function. The value 0 is reserved to indicate that a vendor status code + * was not provided or is not relevant. All other values have a vendor-defined + * meaning. + * @return A value from enum chrexNearbyResult. + */ +uint32_t chrexNearbySetExtendedServiceConfig( + const struct chreHostEndpointInfo *host_info, + const struct chrexNearbyExtendedServiceConfig *config, + uint32_t *vendorStatusCode); + enum chrexNearbyFilterAction { //! Ignore/drop this advertising report CHREX_NEARBY_FILTER_ACTION_IGNORE = 0, @@ -111,6 +143,7 @@ enum chrexNearbyFilterAction { * @param report Contains data for a BLE advertisement. * @return A value from enum chrexNearbyFilterAction. */ +// TODO(b/305277310): Pass OEM extension API version to OEM library uint32_t chrexNearbyMatchExtendedFilter( const struct chreHostEndpointInfo *host_info, const struct chreBleAdvertisingReport *report); diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension_support_lib.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension_support_lib.cc index 2445bd42..62634e32 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension_support_lib.cc +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/nearby_extension_support_lib.cc @@ -49,6 +49,17 @@ uint32_t chrexNearbySetExtendedFilterConfig( } WEAK_SYMBOL +uint32_t chrexNearbySetExtendedServiceConfig( + const struct chreHostEndpointInfo *host_info, + const struct chrexNearbyExtendedServiceConfig *config, + uint32_t *vendorStatusCode) { + auto *fptr = CHRE_NSL_LAZY_LOOKUP(chrexNearbySetExtendedServiceConfig); + return (fptr != nullptr) + ? fptr(host_info, config, vendorStatusCode) + : chrexNearbyResult::CHREX_NEARBY_RESULT_FEATURE_NOT_SUPPORTED; +} + +WEAK_SYMBOL uint32_t chrexNearbyMatchExtendedFilter( const struct chreHostEndpointInfo *host_info, const struct chreBleAdvertisingReport *report) { diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.proto b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.proto index 0f938c4a..6a9e734d 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.proto +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.proto @@ -114,6 +114,9 @@ message BleFilterResult { repeated DataElement data_element = 7; optional bytes ble_service_data = 8; optional ResultType result_type = 9; + // Timestamp the advertisement was received, in nanoseconds, relative to + // Android SystemClock.elapsedRealtimeNanos(). + optional uint64 timestamp_ns = 10; } message BleFilterResults { diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.options b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.options index 769841b6..b0c8d254 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.options +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.options @@ -11,11 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -nearby_extension.FilterConfig.oem_filter max_size:512 -nearby_extension.FilterConfig.oem_filter type:FT_INLINE +nearby_extension.ExtConfigRequest.FilterConfig.oem_filter max_size:800 +nearby_extension.ExtConfigRequest.FilterConfig.oem_filter type:FT_INLINE -nearby_extension.FilterConfig.hardware_filter max_count:10 -nearby_extension.FilterConfig.hardware_filter type:FT_STATIC +nearby_extension.ExtConfigRequest.FilterConfig.hardware_filter max_count:10 +nearby_extension.ExtConfigRequest.FilterConfig.hardware_filter type:FT_STATIC + +nearby_extension.ExtConfigRequest.ServiceConfig.data max_size:800 +nearby_extension.ExtConfigRequest.ServiceConfig.data type:FT_INLINE nearby_extension.ChreBleGenericFilter.data max_size:29 nearby_extension.ChreBleGenericFilter.data type:FT_INLINE @@ -23,13 +26,13 @@ nearby_extension.ChreBleGenericFilter.data type:FT_INLINE nearby_extension.ChreBleGenericFilter.data_mask max_size:29 nearby_extension.ChreBleGenericFilter.data_mask type:FT_INLINE -nearby_extension.FilterResult.report max_count:20 +nearby_extension.FilterResult.report max_count:1 nearby_extension.FilterResult.report type:FT_STATIC nearby_extension.ChreBleAdvertisingReport.address max_size:6 nearby_extension.ChreBleAdvertisingReport.address type:FT_INLINE -nearby_extension.ChreBleAdvertisingReport.data max_size:29 +nearby_extension.ChreBleAdvertisingReport.data max_size:255 nearby_extension.ChreBleAdvertisingReport.data type:FT_INLINE diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto index f289c57f..6be83528 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto +++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto @@ -21,39 +21,73 @@ package nearby_extension; option java_package = "com.google.location.lbs.contexthub"; option java_outer_classname = "NearbyOffloadExtension"; -message FilterConfig { - // Vendor-specific configuration data for extended filter. Byte array opaque - // to Nearby nanoapp, which will be forwarded through - // chrexNearbySetExtendedFilterConfig(). - // If the OEM service wishes to send more data than can fit in a single - // message, or update previous configuration, it can send additional messages. - optional bytes oem_filter = 1; - optional uint32 oem_filter_length = 2; - - // List of Hardware filters (follows chreBleScanFilter defined in CHRE BLE - // API). Resource for hardware filters is constrained in CHRE, and hardware - // filtering is best effort, i.e. advertisements may still be forwarded for - // inspection if they do not match the configured hardware filters. It is - // expected that an OEM service will include at least one hardware filter in - // the first message. Subsequent messages that do not include this field will - // not impact previously configured filters. But if this field is populated in - // a subsequent message, its contents will replace any previously set filters. - // To remove all previously set hardware filters, reset extended filtering by - // closing the ContextHubClient connection. - repeated ChreBleGenericFilter hardware_filter = 3; - - // Maximum time to batch BLE scan results before processing in the nanoapp, in - // milliseconds. For optimal power, specify the longest value that the use - // case permits. If not provided, either the last provided value will continue - // to be used, or if no previous value was given, defaults defined in the - // Nearby nanoapp will be used. - optional uint32 report_delay_ms = 4; - - // BLE scan modes identify functional scan levels without specifying or - // guaranteeing particular scan parameters (e.g. duty cycle, interval, radio - // chain). The actual scan parameters may be platform dependent and may change - // without notice in real time based on contextual cues, etc. - optional ChreBleScanMode scan_mode = 5; +message ExtConfigRequest { + message FilterConfig { + // Vendor-specific configuration data for extended filter. Byte array opaque + // to Nearby nanoapp, which will be forwarded through + // chrexNearbySetExtendedFilterConfig(). + // If both hardware and oem filters are empty, the scan requested by this + // host will stop. Otherwise, the scan will start with the scan filters. + // If the OEM service wishes to send more data than can fit in a single + // message, or update previous configuration, it can send additional + // messages. + optional bytes oem_filter = 1; + optional uint32 oem_filter_length = 2; + + // List of Hardware filters (follows chreBleScanFilter defined in CHRE BLE + // API). Resource for hardware filters is constrained in CHRE, and hardware + // filtering is best effort, i.e. advertisements may still be forwarded for + // inspection if they do not match the configured hardware filters. It is + // expected that an OEM service will include at least one hardware filter in + // the first message. Subsequent messages that do not include this field + // will not impact previously configured filters. But if this field is + // populated in a subsequent message, its contents will replace any + // previously set filters. To remove all previously set hardware filters, + // reset extended filtering by closing the ContextHubClient connection. + repeated ChreBleGenericFilter hardware_filter = 3; + + // Maximum time to batch BLE scan results before processing in the nanoapp, + // in milliseconds. For optimal power, specify the longest value that the + // use case permits. If not provided, either the last provided value will + // continue to be used, or if no previous value was given, defaults defined + // in the Nearby nanoapp will be used. + optional uint32 report_delay_ms = 4; + + // BLE scan modes identify functional scan levels without specifying or + // guaranteeing particular scan parameters (e.g. duty cycle, interval, radio + // chain). The actual scan parameters may be platform dependent and may + // change without notice in real time based on contextual cues, etc. + optional ChreBleScanMode scan_mode = 5; + + // BLE advertising report cache expires after this time period. + // The expired reports are descarded and not delivered to the OEM service. + optional uint32 cache_expire_ms = 6 [default = 5000]; + } + + message ServiceConfig { + // Vendor-specific configuration data for OEM service. Byte array opaque + // to Nearby nanoapp, which will be forwarded through + // chrexNearbySetServiceConfig(). + // If OEM service cannot pass service config data through oem_filter in + // FilterConfig, or if OEM service want to pass it at another time, it can + // pass the service config data to the OEM library through ServiceConfig. + // ServiceConfig is only responsible for passing service config data to the + // OEM library, regardless of the LE scan start/stop behavior and + // hardware_filter. + // If the OEM service wishes to send more data than can fit in a single + // message, or update previous configuration, it can send additional + // messages. + optional bytes data = 1; + optional uint32 data_length = 2; + } + + // Request ID specified by the client to pair Request/Response messages. + optional uint32 request_id = 1; + + oneof config { + FilterConfig filter_config = 2; + ServiceConfig service_config = 3; + } } message ChreBleGenericFilter { @@ -95,13 +129,14 @@ enum ChreBleScanMode { CHRE_BLE_SCAN_MODE_AGGRESSIVE = 3; } -// Sent in response to FilterConfig -message FilterConfigResult { - // Value from enum chrexNearbyResult that was returned by - // chrexNearbySetExtendedFilterConfig() - optional int32 result = 1; - // Vendor-defined status code provided in chrexNearbySetExtendedFilterConfig() - optional uint32 vendor_status = 2; +// Sent in response to ExtConfigRequest +message ExtConfigResponse { + // Request ID of the corresponding Request message. + optional uint32 request_id = 1; + // Value from enum chrexNearbyResult that was returned from OEM library. + optional int32 result = 2; + // Vendor-defined status code provided from OEM library. + optional uint32 vendor_status = 3; } // Sent when one or more advertisements matched an extended filter diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/proto/filter.proto b/apps/nearby/location/lbs/contexthub/nanoapps/proto/filter.proto index f051f54d..34be88e7 100644 --- a/apps/nearby/location/lbs/contexthub/nanoapps/proto/filter.proto +++ b/apps/nearby/location/lbs/contexthub/nanoapps/proto/filter.proto @@ -20,6 +20,8 @@ enum FilterMessageType { MESSAGE_FILTER_RESULTS = 4; // Config the filtering, including start/stop filtering. MESSAGE_CONFIG = 5; - // Message from host to CHRE to set Filter extensions. - MESSAGE_FILTER_EXTENSIONS = 6; + // Message from host to CHRE to request extended configuration. + MESSAGE_EXT_CONFIG_REQUEST = 6; + // Message from CHRE to host for the response of extended configuration. + MESSAGE_EXT_CONFIG_RESPONSE = 7; } diff --git a/apps/rpc_world/src/rpc_world_manager.cc b/apps/rpc_world/src/rpc_world_manager.cc index d658678e..182e0288 100644 --- a/apps/rpc_world/src/rpc_world_manager.cc +++ b/apps/rpc_world/src/rpc_world_manager.cc @@ -103,7 +103,7 @@ bool RpcWorldManager::start() { mAddCall.Write(addRequest); mAddCall.Write(addRequest); mAddCall.Write(addRequest); - mAddCall.CloseClientStream(); + mAddCall.RequestCompletion(); } else { LOGE("Error while retrieving the client"); } @@ -146,6 +146,8 @@ void RpcWorldManager::handleEvent(uint32_t senderInstanceId, uint16_t eventType, } void RpcWorldManager::end() { + mServer.close(); + mClient.close(); if (mTimerId != CHRE_TIMER_INVALID) { chreTimerCancel(mTimerId); } @@ -168,7 +170,7 @@ void RpcWorldManager::addStart( reader.set_on_next([](const chre_rpc_NumberMessage &request) { RpcWorldManagerSingleton::get()->mSum += request.number; }); - reader.set_on_client_stream_end([]() { + reader.set_on_completion_requested([]() { chre_rpc_NumberMessage response; response.number = RpcWorldManagerSingleton::get()->mSum; RpcWorldManagerSingleton::get()->setPermissionForNextMessage( diff --git a/apps/test/chqts/src/general_test/basic_ble_test.cc b/apps/test/chqts/src/general_test/basic_ble_test.cc index 891ad654..4d017220 100644 --- a/apps/test/chqts/src/general_test/basic_ble_test.cc +++ b/apps/test/chqts/src/general_test/basic_ble_test.cc @@ -138,6 +138,11 @@ void BasicBleTest::handleTimerEvent() { } mFlushWasCalled = true; } else { + if (chreBleFlushAsync(&gFlushCookie)) { + sendFatalFailureToHost( + "chreBleFlushAsync should return false if batching is not supported"); + } + if (!chreBleStopScanAsync()) { sendFatalFailureToHost("Failed to stop a BLE scan session"); } diff --git a/apps/test/chqts/src/general_test/send_event_stress_test.cc b/apps/test/chqts/src/general_test/send_event_stress_test.cc index 6bc7e631..41e8dfe1 100644 --- a/apps/test/chqts/src/general_test/send_event_stress_test.cc +++ b/apps/test/chqts/src/general_test/send_event_stress_test.cc @@ -84,8 +84,12 @@ void SendEventStressTest::setUp(uint32_t messageSize, sendFatalFailureToHost("Insufficient events available"); } - // sCompleteCallbacksLeft may be 0 or 1 at this point. We don't care. - // We just know we also expect all the sEventsLeft to have callbacks. + // If kMaxEventsToSend events are sent, we need to reset + // sCompleteCallbacksLeft because we only expect at most sEventsLeft to have + // callbacks. + if (sEventsLeft == kMaxEventsToSend) { + sCompleteCallbacksLeft = 0; + } sCompleteCallbacksLeft += sEventsLeft; sInMethod = false; diff --git a/apps/test/common/chre_api_test/chre_api_test.mk b/apps/test/common/chre_api_test/chre_api_test.mk index 5f8559fb..c9ad8c29 100644 --- a/apps/test/common/chre_api_test/chre_api_test.mk +++ b/apps/test/common/chre_api_test/chre_api_test.mk @@ -25,6 +25,7 @@ COMMON_SRCS += $(NANOAPP_PATH)/src/chre_api_test.cc # Utilities #################################################################### COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/ble.cc +COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/string.cc # Compiler Flags ############################################################### @@ -44,3 +45,4 @@ CHRE_NANOAPP_USES_BLE = true # PW RPC protos ################################################################ PW_RPC_SRCS = $(NANOAPP_PATH)/rpc/chre_api_test.proto +PW_RPC_SRCS += $(ANDROID_BUILD_TOP)/external/protobuf/src/google/protobuf/empty.proto diff --git a/apps/test/common/chre_api_test/inc/chre_api_test_manager.h b/apps/test/common/chre_api_test/inc/chre_api_test_manager.h index 8380e8ae..0e4543b3 100644 --- a/apps/test/common/chre_api_test/inc/chre_api_test_manager.h +++ b/apps/test/common/chre_api_test/inc/chre_api_test_manager.h @@ -38,29 +38,16 @@ class ChreApiTestService final /** * Returns the BLE capabilities. */ - pw::Status ChreBleGetCapabilities(const chre_rpc_Void &request, + pw::Status ChreBleGetCapabilities(const google_protobuf_Empty &request, chre_rpc_Capabilities &response); /** * Returns the BLE filter capabilities. */ - pw::Status ChreBleGetFilterCapabilities(const chre_rpc_Void &request, + pw::Status ChreBleGetFilterCapabilities(const google_protobuf_Empty &request, chre_rpc_Capabilities &response); /** - * Starts a BLE scan. - */ - pw::Status ChreBleStartScanAsync( - const chre_rpc_ChreBleStartScanAsyncInput &request, - chre_rpc_Status &response); - - /** - * Stops a BLE scan. - */ - pw::Status ChreBleStopScanAsync(const chre_rpc_Void &request, - chre_rpc_Status &response); - - /** * Finds the default sensor and returns the handle in the output. */ pw::Status ChreSensorFindDefault( @@ -108,13 +95,6 @@ class ChreApiTestService final chre_rpc_Status &response); /** - * Retrieve the last host endpoint notification. - */ - pw::Status RetrieveLatestDisconnectedHostEndpointEvent( - const chre_rpc_Void &request, - chre_rpc_RetrieveLatestDisconnectedHostEndpointEventOutput &response); - - /** * Gets the host endpoint info for a given host endpoint id. */ pw::Status ChreGetHostEndpointInfo( @@ -132,7 +112,7 @@ class ChreApiTestService final * Stops a BLE scan synchronously. Waits for the CHRE_EVENT_BLE_ASYNC_RESULT * event. */ - void ChreBleStopScanSync(const chre_rpc_Void &request, + void ChreBleStopScanSync(const google_protobuf_Empty &request, ServerWriter<chre_rpc_GeneralSyncMessage> &writer); /** @@ -174,16 +154,6 @@ class ChreApiTestService final private: /** - * Copies a string from source to destination up to the length of the source - * or the max value. Pads with null characters. - * - * @param destination the destination string. - * @param source the source string. - * @param maxChars the maximum number of chars. - */ - void copyString(char *destination, const char *source, size_t maxChars); - - /** * Sets the synchronous timeout timer for the active sync message. * * @return if the operation was successful. @@ -200,17 +170,17 @@ class ChreApiTestService final * false otherwise. */ bool validateInputAndCallChreBleGetCapabilities( - const chre_rpc_Void &request, chre_rpc_Capabilities &response); + const google_protobuf_Empty &request, chre_rpc_Capabilities &response); bool validateInputAndCallChreBleGetFilterCapabilities( - const chre_rpc_Void &request, chre_rpc_Capabilities &response); + const google_protobuf_Empty &request, chre_rpc_Capabilities &response); bool validateInputAndCallChreBleStartScanAsync( const chre_rpc_ChreBleStartScanAsyncInput &request, chre_rpc_Status &response); - bool validateInputAndCallChreBleStopScanAsync(const chre_rpc_Void &request, - chre_rpc_Status &response); + bool validateInputAndCallChreBleStopScanAsync( + const google_protobuf_Empty &request, chre_rpc_Status &response); bool validateInputAndCallChreSensorFindDefault( const chre_rpc_ChreSensorFindDefaultInput &request, @@ -240,14 +210,24 @@ class ChreApiTestService final const chre_rpc_ChreConfigureHostEndpointNotificationsInput &request, chre_rpc_Status &response); - bool validateInputAndRetrieveLatestDisconnectedHostEndpointEvent( - const chre_rpc_Void &request, - chre_rpc_RetrieveLatestDisconnectedHostEndpointEventOutput &response); - bool validateInputAndCallChreGetHostEndpointInfo( const chre_rpc_ChreGetHostEndpointInfoInput &request, chre_rpc_ChreGetHostEndpointInfoOutput &response); + /** + * Validates the BLE scan filters and creates a generic filter in the + * outputScanFilters array. scanFilters and outputScanFilters must be of size + * scanFilterCount or greater. + * + * @param scanFilters the input scan filters. + * @param outputScanFilters the output scan filters. + * @param scanFilterCount the number of scan filters. + * @return whether the validation was successful. + */ + bool validateBleScanFilters(const chre_rpc_ChreBleGenericFilter *scanFilters, + chreBleGenericFilter *outputScanFilters, + uint32_t scanFilterCount); + constexpr static uint32_t kMaxNumEventTypes = 10; // declared in chre_api_test.options @@ -259,12 +239,6 @@ class ChreApiTestService final uint32_t mSyncTimerHandle = CHRE_TIMER_INVALID; uint8_t mRequestType; - /** - * Variables to store disconnected host endpoint notification. - */ - uint32_t mReceivedHostEndpointDisconnectedNum = 0; - chreHostEndpointNotification mLatestHostEndpointNotification; - /* * Variables to control synchronization for sync events calls. * Only one sync event call may be made at a time. diff --git a/apps/test/common/chre_api_test/rpc/chre_api_test.options b/apps/test/common/chre_api_test/rpc/chre_api_test.options index 6ec7e7c8..6c3bc2c8 100644 --- a/apps/test/common/chre_api_test/rpc/chre_api_test.options +++ b/apps/test/common/chre_api_test/rpc/chre_api_test.options @@ -7,3 +7,7 @@ chre.rpc.ChreGetHostEndpointInfoOutput.endpointName max_size:51 # CHRE_MAX_ENDPO chre.rpc.ChreGetHostEndpointInfoOutput.endpointTag max_size:51 # CHRE_MAX_ENDPOINT_TAG_LEN chre.rpc.ChreSensorThreeAxisData.readings max_count:10 chre.rpc.GatherEventsInput.eventTypes max_count:10 +chre.rpc.ChreBleAdvertisingReport.address max_size:6 # CHRE_BLE_ADDRESS_LEN +chre.rpc.ChreBleAdvertisingReport.directAddress max_size:6 # CHRE_BLE_ADDRESS_LEN +chre.rpc.ChreBleAdvertisingReport.data max_size:255 # extended range is [0, 255] +chre.rpc.ChreBleAdvertisementEvent.reports max_count:10 diff --git a/apps/test/common/chre_api_test/rpc/chre_api_test.proto b/apps/test/common/chre_api_test/rpc/chre_api_test.proto index 57b7dbc2..54c716ea 100644 --- a/apps/test/common/chre_api_test/rpc/chre_api_test.proto +++ b/apps/test/common/chre_api_test/rpc/chre_api_test.proto @@ -2,26 +2,17 @@ syntax = "proto3"; package chre.rpc; +import "google/protobuf/empty.proto"; + option java_package = "dev.chre.rpc.proto"; service ChreApiTestService { // Returns the BLE capabilities. - rpc ChreBleGetCapabilities(Void) returns (Capabilities) {} + rpc ChreBleGetCapabilities(google.protobuf.Empty) returns (Capabilities) {} // Returns the BLE filter capabilities. - rpc ChreBleGetFilterCapabilities(Void) returns (Capabilities) {} - - /* Starts a BLE scan asynchronously. This will not wait for the - * event but will return success if the chreBleStartScanAsync - * function returns success. - */ - rpc ChreBleStartScanAsync(ChreBleStartScanAsyncInput) returns (Status) {} - - /* Stops a BLE scan asynchronously. This will not wait for the - * event but will return success if the chreBleStopScanAsync - * function returns success. - */ - rpc ChreBleStopScanAsync(Void) returns (Status) {} + rpc ChreBleGetFilterCapabilities(google.protobuf.Empty) + returns (Capabilities) {} // Finds the default sensor for the given sensor type. rpc ChreSensorFindDefault(ChreSensorFindDefaultInput) @@ -49,12 +40,6 @@ service ChreApiTestService { rpc ChreConfigureHostEndpointNotifications( ChreConfigureHostEndpointNotificationsInput) returns (Status) {} - // TODO(b/274791978): Deprecate this once we can capture event - // Retrieves the latest disconnected host endpoint event and the number of - // disconnect event received. - rpc RetrieveLatestDisconnectedHostEndpointEvent(Void) - returns (RetrieveLatestDisconnectedHostEndpointEventOutput) {} - // Gets the host endpoint info for a given host endpoint id. rpc ChreGetHostEndpointInfo(ChreGetHostEndpointInfoInput) returns (ChreGetHostEndpointInfoOutput) {} @@ -74,7 +59,8 @@ service ChreApiTestService { * function returns success and the event is seen. This will * return the event's success field. */ - rpc ChreBleStopScanSync(Void) returns (stream GeneralSyncMessage) {} + rpc ChreBleStopScanSync(google.protobuf.Empty) + returns (stream GeneralSyncMessage) {} // Returns events that match the eventType filter rpc GatherEvents(GatherEventsInput) returns (stream GeneralEventsMessage) {} @@ -82,9 +68,6 @@ service ChreApiTestService { // General messages -// Empty message (void) -message Void {} - // Contains a capabilities uint32 message Capabilities { uint32 capabilities = 1; @@ -110,7 +93,9 @@ message GeneralSyncMessage { // Contains event filters for gathering events message GatherEventsInput { repeated uint32 eventTypes = 1; - uint32 eventTypeCount = 2; + + // Deprecated: We use the built-in count variable now + uint32 eventTypeCount = 2 [deprecated = true]; uint32 eventCount = 3; uint64 timeoutInNs = 4; } @@ -122,6 +107,8 @@ message GeneralEventsMessage { oneof data { ChreSensorThreeAxisData chreSensorThreeAxisData = 2; ChreSensorSamplingStatusEvent chreSensorSamplingStatusEvent = 3; + ChreHostEndpointNotification chreHostEndpointNotification = 4; + ChreBleAdvertisementEvent chreBleAdvertisementEvent = 5; } } @@ -153,7 +140,7 @@ message ChreSensorDataHeader { uint32 reserved = 5; } -// Individual sample data for a three-axis sensor. +// Individual sample data for a three-axis sensor message ChreSensorThreeAxisSampleData { uint32 timestampDelta = 1; float x = 2; @@ -161,6 +148,30 @@ message ChreSensorThreeAxisSampleData { float z = 4; } +// A BLE advertising event +message ChreBleAdvertisementEvent { + uint32 reserved = 1; + repeated ChreBleAdvertisingReport reports = 2; +} + +// A BLE advertising report +message ChreBleAdvertisingReport { + uint64 timestamp = 1; + uint32 eventTypeAndDataStatus = 2; + uint32 addressType = 3; + bytes address = 4; + uint32 primaryPhy = 5; + uint32 secondaryPhy = 6; + uint32 advertisingSid = 7; + int32 txPower = 8; + uint32 periodicAdvertisingInterval = 9; + int32 rssi = 10; + uint32 directAddressType = 11; + bytes directAddress = 12; + bytes data = 13; + uint32 reserved = 14; +} + // Function specific messages // Input value for ChreSensorFindDefault @@ -181,6 +192,11 @@ message RetrieveLatestDisconnectedHostEndpointEventOutput { uint32 hostEndpointId = 2; } +message ChreHostEndpointNotification { + uint32 hostEndpointId = 1; + uint32 notificationType = 2; +} + // Input value for ChreGetHostEndpointInfo message ChreGetHostEndpointInfoInput { uint32 hostEndpointId = 1; @@ -271,7 +287,9 @@ message ChreBleGenericFilter { // Scan filter for BLE scanning message ChreBleScanFilter { int32 rssiThreshold = 1; - uint32 scanFilterCount = 2; + + // Deprecated: We use the built-in count variable now + uint32 scanFilterCount = 2 [deprecated = true]; repeated ChreBleGenericFilter scanFilters = 3; } diff --git a/apps/test/common/chre_api_test/src/chre_api_test_manager.cc b/apps/test/common/chre_api_test/src/chre_api_test_manager.cc index 3e4a38fb..695255dc 100644 --- a/apps/test/common/chre_api_test/src/chre_api_test_manager.cc +++ b/apps/test/common/chre_api_test/src/chre_api_test_manager.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <algorithm> + #include "chre_api_test_manager.h" #include "chre.h" @@ -27,6 +29,7 @@ constexpr uint64_t kSyncFunctionTimeout = 2 * chre::kOneSecondInNanoseconds; * The following constants are defined in chre_api_test.options. */ constexpr uint32_t kThreeAxisDataReadingsMaxCount = 10; +constexpr uint32_t kChreBleAdvertisementReportMaxCount = 10; /** * Closes the writer and invalidates the writer. @@ -86,7 +89,7 @@ void sendFailureAndFinishCloseWriter( // Start ChreApiTestService RPC generated functions pw::Status ChreApiTestService::ChreBleGetCapabilities( - const chre_rpc_Void &request, chre_rpc_Capabilities &response) { + const google_protobuf_Empty &request, chre_rpc_Capabilities &response) { ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( CHRE_MESSAGE_PERMISSION_NONE); return validateInputAndCallChreBleGetCapabilities(request, response) @@ -95,7 +98,7 @@ pw::Status ChreApiTestService::ChreBleGetCapabilities( } pw::Status ChreApiTestService::ChreBleGetFilterCapabilities( - const chre_rpc_Void &request, chre_rpc_Capabilities &response) { + const google_protobuf_Empty &request, chre_rpc_Capabilities &response) { ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( CHRE_MESSAGE_PERMISSION_NONE); return validateInputAndCallChreBleGetFilterCapabilities(request, response) @@ -103,25 +106,6 @@ pw::Status ChreApiTestService::ChreBleGetFilterCapabilities( : pw::Status::InvalidArgument(); } -pw::Status ChreApiTestService::ChreBleStartScanAsync( - const chre_rpc_ChreBleStartScanAsyncInput &request, - chre_rpc_Status &response) { - ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( - CHRE_MESSAGE_PERMISSION_NONE); - return validateInputAndCallChreBleStartScanAsync(request, response) - ? pw::OkStatus() - : pw::Status::InvalidArgument(); -} - -pw::Status ChreApiTestService::ChreBleStopScanAsync( - const chre_rpc_Void &request, chre_rpc_Status &response) { - ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( - CHRE_MESSAGE_PERMISSION_NONE); - return validateInputAndCallChreBleStopScanAsync(request, response) - ? pw::OkStatus() - : pw::Status::InvalidArgument(); -} - pw::Status ChreApiTestService::ChreSensorFindDefault( const chre_rpc_ChreSensorFindDefaultInput &request, chre_rpc_ChreSensorFindDefaultOutput &response) { @@ -193,17 +177,6 @@ pw::Status ChreApiTestService::ChreConfigureHostEndpointNotifications( : pw::Status::InvalidArgument(); } -pw::Status ChreApiTestService::RetrieveLatestDisconnectedHostEndpointEvent( - const chre_rpc_Void &request, - chre_rpc_RetrieveLatestDisconnectedHostEndpointEventOutput &response) { - ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( - CHRE_MESSAGE_PERMISSION_NONE); - return validateInputAndRetrieveLatestDisconnectedHostEndpointEvent(request, - response) - ? pw::OkStatus() - : pw::Status::InvalidArgument(); -} - pw::Status ChreApiTestService::ChreGetHostEndpointInfo( const chre_rpc_ChreGetHostEndpointInfoInput &request, chre_rpc_ChreGetHostEndpointInfoOutput &response) { @@ -243,7 +216,7 @@ void ChreApiTestService::ChreBleStartScanSync( } void ChreApiTestService::ChreBleStopScanSync( - const chre_rpc_Void &request, + const google_protobuf_Empty &request, ServerWriter<chre_rpc_GeneralSyncMessage> &writer) { if (mWriter.has_value()) { ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( @@ -281,26 +254,18 @@ void ChreApiTestService::GatherEvents( return; } - if (request.eventTypeCount > kMaxNumEventTypes) { - LOGE("GatherEvents: request.eventTypeCount is out of bounds"); - ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( - CHRE_MESSAGE_PERMISSION_NONE); - writer.Finish(); - return; - } - - if (request.eventTypeCount == 0) { - LOGE("GatherEvents: request.eventTypeCount == 0"); + if (request.eventTypes_count == 0) { + LOGE("GatherEvents: request.eventTypes_count == 0"); ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( CHRE_MESSAGE_PERMISSION_NONE); writer.Finish(); return; } - for (uint32_t i = 0; i < request.eventTypeCount; ++i) { + for (uint32_t i = 0; i < request.eventTypes_count; ++i) { if (request.eventTypes[i] < std::numeric_limits<uint16_t>::min() || request.eventTypes[i] > std::numeric_limits<uint16_t>::max()) { - LOGE("GatherEvents: invalid request.eventTypes: i: %" PRIu32, i); + LOGE("GatherEvents: invalid request.eventTypes at index: %" PRIu32, i); ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( CHRE_MESSAGE_PERMISSION_NONE); writer.Finish(); @@ -321,7 +286,7 @@ void ChreApiTestService::GatherEvents( sendFailureAndFinishCloseWriter(mEventWriter); mEventTimerHandle = CHRE_TIMER_INVALID; } else { - mEventTypeCount = request.eventTypeCount; + mEventTypeCount = request.eventTypes_count; mEventExpectedCount = request.eventCount; mEventSentCount = 0; LOGD("GatherEvents: mEventTypeCount: %" PRIu32 @@ -379,7 +344,7 @@ void ChreApiTestService::handleGatheringEvent(uint16_t eventType, message.which_data = chre_rpc_GeneralEventsMessage_chreSensorThreeAxisData_tag; - const struct chreSensorThreeAxisData *data = + const auto *data = static_cast<const struct chreSensorThreeAxisData *>(eventData); message.data.chreSensorThreeAxisData.header.baseTimestamp = data->header.baseTimestamp; @@ -408,7 +373,7 @@ void ChreApiTestService::handleGatheringEvent(uint16_t eventType, break; } case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { - const struct chreSensorSamplingStatusEvent *data = + const auto *data = static_cast<const struct chreSensorSamplingStatusEvent *>(eventData); message.data.chreSensorSamplingStatusEvent.sensorHandle = data->sensorHandle; @@ -424,6 +389,78 @@ void ChreApiTestService::handleGatheringEvent(uint16_t eventType, chre_rpc_GeneralEventsMessage_chreSensorSamplingStatusEvent_tag; break; } + case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: { + const auto *data = + static_cast<const struct chreHostEndpointNotification *>(eventData); + message.data.chreHostEndpointNotification.hostEndpointId = + data->hostEndpointId; + message.data.chreHostEndpointNotification.notificationType = + data->notificationType; + + message.status = true; + message.which_data = + chre_rpc_GeneralEventsMessage_chreHostEndpointNotification_tag; + break; + } + case CHRE_EVENT_BLE_ADVERTISEMENT: { + const auto *data = + static_cast<const struct chreBleAdvertisementEvent *>(eventData); + message.data.chreBleAdvertisementEvent.reserved = data->reserved; + + uint32_t numReports = + MIN(kChreBleAdvertisementReportMaxCount, data->numReports); + message.data.chreBleAdvertisementEvent.reports_count = numReports; + for (uint32_t i = 0; i < numReports; ++i) { + message.data.chreBleAdvertisementEvent.reports[i].timestamp = + data->reports[i].timestamp; + message.data.chreBleAdvertisementEvent.reports[i] + .eventTypeAndDataStatus = data->reports[i].eventTypeAndDataStatus; + message.data.chreBleAdvertisementEvent.reports[i].addressType = + data->reports[i].addressType; + + message.data.chreBleAdvertisementEvent.reports[i].address.size = + CHRE_BLE_ADDRESS_LEN; + std::memcpy( + message.data.chreBleAdvertisementEvent.reports[i].address.bytes, + data->reports[i].address, CHRE_BLE_ADDRESS_LEN); + + message.data.chreBleAdvertisementEvent.reports[i].primaryPhy = + data->reports[i].primaryPhy; + message.data.chreBleAdvertisementEvent.reports[i].secondaryPhy = + data->reports[i].secondaryPhy; + message.data.chreBleAdvertisementEvent.reports[i].advertisingSid = + data->reports[i].advertisingSid; + message.data.chreBleAdvertisementEvent.reports[i].txPower = + data->reports[i].txPower; + message.data.chreBleAdvertisementEvent.reports[i] + .periodicAdvertisingInterval = + data->reports[i].periodicAdvertisingInterval; + message.data.chreBleAdvertisementEvent.reports[i].rssi = + data->reports[i].rssi; + message.data.chreBleAdvertisementEvent.reports[i].directAddressType = + data->reports[i].directAddressType; + + message.data.chreBleAdvertisementEvent.reports[i].directAddress.size = + CHRE_BLE_ADDRESS_LEN; + std::memcpy(message.data.chreBleAdvertisementEvent.reports[i] + .directAddress.bytes, + data->reports[i].directAddress, CHRE_BLE_ADDRESS_LEN); + + message.data.chreBleAdvertisementEvent.reports[i].data.size = + data->reports[i].dataLength; + std::memcpy( + message.data.chreBleAdvertisementEvent.reports[i].data.bytes, + data->reports[i].data, data->reports[i].dataLength); + + message.data.chreBleAdvertisementEvent.reports[i].reserved = + data->reports[i].reserved; + } + + message.status = true; + message.which_data = + chre_rpc_GeneralEventsMessage_chreBleAdvertisementEvent_tag; + break; + } default: { LOGE("GatherEvents: event type: %" PRIu16 " not implemented", eventType); } @@ -461,34 +498,6 @@ void ChreApiTestService::handleTimerEvent(const void *cookie) { } } -void ChreApiTestService::handleHostEndpointNotificationEvent( - const chreHostEndpointNotification *data) { - if (data->notificationType != HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT) { - LOGW("Received non disconnected event"); - return; - } - - ++mReceivedHostEndpointDisconnectedNum; - mLatestHostEndpointNotification = *data; -} - -void ChreApiTestService::copyString(char *destination, const char *source, - size_t maxChars) { - CHRE_ASSERT_NOT_NULL(destination); - CHRE_ASSERT_NOT_NULL(source); - - if (maxChars == 0) { - return; - } - - uint32_t i; - for (i = 0; i < maxChars - 1 && source[i] != '\0'; ++i) { - destination[i] = source[i]; - } - - memset(&destination[i], 0, maxChars - i); -} - bool ChreApiTestService::startSyncTimer() { mSyncTimerHandle = chreTimerSet( kSyncFunctionTimeout, &mSyncTimerHandle /* cookie */, true /* oneShot */); @@ -510,7 +519,7 @@ bool ChreApiTestManager::start() { } void ChreApiTestManager::end() { - // do nothing + mServer.close(); } void ChreApiTestManager::handleEvent(uint32_t senderInstanceId, @@ -530,10 +539,6 @@ void ChreApiTestManager::handleEvent(uint32_t senderInstanceId, case CHRE_EVENT_TIMER: mChreApiTestService.handleTimerEvent(eventData); break; - case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: - mChreApiTestService.handleHostEndpointNotificationEvent( - static_cast<const chreHostEndpointNotification *>(eventData)); - break; default: { // ignore } diff --git a/apps/test/common/chre_api_test/src/chre_api_test_service.cc b/apps/test/common/chre_api_test/src/chre_api_test_service.cc index b045551e..56a3402f 100644 --- a/apps/test/common/chre_api_test/src/chre_api_test_service.cc +++ b/apps/test/common/chre_api_test/src/chre_api_test_service.cc @@ -18,7 +18,9 @@ #include "chre/util/nanoapp/ble.h" #include "chre/util/nanoapp/log.h" +#include "chre/util/nanoapp/string.h" +using ::chre::copyString; using ::chre::createBleGenericFilter; namespace { @@ -26,19 +28,22 @@ namespace { /** * The following constants are defined in chre_api_test.options. */ -constexpr uint32_t kMaxBleScanFilters = 10; -constexpr uint32_t kMaxNameStringSize = 100; +constexpr size_t kMaxNameStringBufferSize = 100; +constexpr size_t kMaxHostEndpointNameBufferSize = 51; +constexpr size_t kMaxHostEndpointTagBufferSize = 51; } // namespace bool ChreApiTestService::validateInputAndCallChreBleGetCapabilities( - const chre_rpc_Void & /* request */, chre_rpc_Capabilities &response) { + const google_protobuf_Empty & /* request */, + chre_rpc_Capabilities &response) { response.capabilities = chreBleGetCapabilities(); LOGD("ChreBleGetCapabilities: capabilities: %" PRIu32, response.capabilities); return true; } bool ChreApiTestService::validateInputAndCallChreBleGetFilterCapabilities( - const chre_rpc_Void & /* request */, chre_rpc_Capabilities &response) { + const google_protobuf_Empty & /* request */, + chre_rpc_Capabilities &response) { response.capabilities = chreBleGetFilterCapabilities(); LOGD("ChreBleGetFilterCapabilities: capabilities: %" PRIu32, response.capabilities); @@ -48,13 +53,15 @@ bool ChreApiTestService::validateInputAndCallChreBleGetFilterCapabilities( bool ChreApiTestService::validateInputAndCallChreBleStartScanAsync( const chre_rpc_ChreBleStartScanAsyncInput &request, chre_rpc_Status &response) { - bool success = false; if (request.mode < _chre_rpc_ChreBleScanMode_MIN || request.mode > _chre_rpc_ChreBleScanMode_MAX || request.mode == chre_rpc_ChreBleScanMode_INVALID) { LOGE("ChreBleStartScanAsync: invalid mode"); - } else if (!request.hasFilter) { - chreBleScanMode mode = static_cast<chreBleScanMode>(request.mode); + return false; + } + + if (!request.hasFilter) { + auto mode = static_cast<chreBleScanMode>(request.mode); response.status = chreBleStartScanAsync(mode, request.reportDelayMs, nullptr); @@ -65,68 +72,47 @@ bool ChreApiTestService::validateInputAndCallChreBleStartScanAsync( : (mode == CHRE_BLE_SCAN_MODE_FOREGROUND ? "foreground" : "aggressive"), request.reportDelayMs, response.status ? "true" : "false"); - success = true; - } else if (request.filter.rssiThreshold < - std::numeric_limits<int8_t>::min() || - request.filter.rssiThreshold > - std::numeric_limits<int8_t>::max()) { + return true; + } + + if (request.filter.rssiThreshold < std::numeric_limits<int8_t>::min() || + request.filter.rssiThreshold > std::numeric_limits<int8_t>::max()) { LOGE("ChreBleStartScanAsync: invalid filter.rssiThreshold"); - } else if (request.filter.scanFilterCount == 0 || - request.filter.scanFilterCount > kMaxBleScanFilters) { - LOGE("ChreBleStartScanAsync: invalid filter.scanFilterCount"); - } else { - chreBleGenericFilter genericFilters[request.filter.scanFilterCount]; - bool validateFiltersSuccess = true; - for (uint32_t i = 0; - validateFiltersSuccess && i < request.filter.scanFilterCount; ++i) { - const chre_rpc_ChreBleGenericFilter &scanFilter = - request.filter.scanFilters[i]; - if (scanFilter.type > std::numeric_limits<uint8_t>::max() || - scanFilter.length > std::numeric_limits<uint8_t>::max()) { - LOGE( - "ChreBleStartScanAsync: invalid request.filter.scanFilters member: " - "type: %" PRIu32 " or length: %" PRIu32, - scanFilter.type, scanFilter.length); - validateFiltersSuccess = false; - } else if (scanFilter.data.size < scanFilter.length || - scanFilter.mask.size < scanFilter.length) { - LOGE( - "ChreBleStartScanAsync: invalid request.filter.scanFilters member: " - "data or mask size"); - validateFiltersSuccess = false; - } else { - genericFilters[i] = createBleGenericFilter( - scanFilter.type, scanFilter.length, scanFilter.data.bytes, - scanFilter.mask.bytes); - } - } + return false; + } - if (validateFiltersSuccess) { - struct chreBleScanFilter filter; - filter.rssiThreshold = request.filter.rssiThreshold; - filter.scanFilterCount = request.filter.scanFilterCount; - filter.scanFilters = genericFilters; - - chreBleScanMode mode = static_cast<chreBleScanMode>(request.mode); - response.status = - chreBleStartScanAsync(mode, request.reportDelayMs, &filter); - - LOGD("ChreBleStartScanAsync: mode: %s, reportDelayMs: %" PRIu32 - ", scanFilterCount: %" PRIu32 ", status: %s", - mode == CHRE_BLE_SCAN_MODE_BACKGROUND - ? "background" - : (mode == CHRE_BLE_SCAN_MODE_FOREGROUND ? "foreground" - : "aggressive"), - request.reportDelayMs, request.filter.scanFilterCount, - response.status ? "true" : "false"); - success = true; - } + if (request.filter.scanFilters_count == 0) { + LOGE("ChreBleStartScanAsync: invalid filter.scanFilters_count"); + return false; + } + + chreBleGenericFilter genericFilters[request.filter.scanFilters_count]; + if (!validateBleScanFilters(request.filter.scanFilters, genericFilters, + request.filter.scanFilters_count)) { + return false; } - return success; + + struct chreBleScanFilter filter; + filter.rssiThreshold = request.filter.rssiThreshold; + filter.scanFilterCount = request.filter.scanFilters_count; + filter.scanFilters = genericFilters; + + auto mode = static_cast<chreBleScanMode>(request.mode); + response.status = chreBleStartScanAsync(mode, request.reportDelayMs, &filter); + + LOGD("ChreBleStartScanAsync: mode: %s, reportDelayMs: %" PRIu32 + ", scanFilterCount: %" PRIu16 ", status: %s", + mode == CHRE_BLE_SCAN_MODE_BACKGROUND + ? "background" + : (mode == CHRE_BLE_SCAN_MODE_FOREGROUND ? "foreground" + : "aggressive"), + request.reportDelayMs, request.filter.scanFilters_count, + response.status ? "true" : "false"); + return true; } bool ChreApiTestService::validateInputAndCallChreBleStopScanAsync( - const chre_rpc_Void & /* request */, chre_rpc_Status &response) { + const google_protobuf_Empty & /* request */, chre_rpc_Status &response) { response.status = chreBleStopScanAsync(); LOGD("ChreBleStopScanAsync: status: %s", response.status ? "true" : "false"); return true; @@ -139,7 +125,7 @@ bool ChreApiTestService::validateInputAndCallChreSensorFindDefault( return false; } - uint8_t sensorType = (uint8_t)request.sensorType; + auto sensorType = static_cast<uint8_t>(request.sensorType); response.foundSensor = chreSensorFindDefault(sensorType, &response.sensorHandle); @@ -157,7 +143,8 @@ bool ChreApiTestService::validateInputAndCallChreGetSensorInfo( response.status = chreGetSensorInfo(request.handle, &sensorInfo); if (response.status) { - copyString(response.sensorName, sensorInfo.sensorName, kMaxNameStringSize); + copyString(response.sensorName, sensorInfo.sensorName, + kMaxNameStringBufferSize); response.sensorType = sensorInfo.sensorType; response.isOnChange = sensorInfo.isOnChange; response.isOneShot = sensorInfo.isOneShot; @@ -210,8 +197,7 @@ bool ChreApiTestService::validateInputAndCallChreGetSensorSamplingStatus( bool ChreApiTestService::validateInputAndCallChreSensorConfigure( const chre_rpc_ChreSensorConfigureInput &request, chre_rpc_Status &response) { - chreSensorConfigureMode mode = - static_cast<chreSensorConfigureMode>(request.mode); + auto mode = static_cast<chreSensorConfigureMode>(request.mode); response.status = chreSensorConfigure(request.sensorHandle, mode, request.interval, request.latency); @@ -222,8 +208,7 @@ bool ChreApiTestService::validateInputAndCallChreSensorConfigure( bool ChreApiTestService::validateInputAndCallChreSensorConfigureModeOnly( const chre_rpc_ChreSensorConfigureModeOnlyInput &request, chre_rpc_Status &response) { - chreSensorConfigureMode mode = - static_cast<chreSensorConfigureMode>(request.mode); + auto mode = static_cast<chreSensorConfigureMode>(request.mode); response.status = chreSensorConfigureModeOnly(request.sensorHandle, mode); LOGD("ChreSensorConfigureModeOnly: status: %s", @@ -239,7 +224,7 @@ bool ChreApiTestService::validateInputAndCallChreAudioGetSource( response.status = chreAudioGetSource(request.handle, &audioSource); if (response.status) { - copyString(response.name, audioSource.name, kMaxNameStringSize); + copyString(response.name, audioSource.name, kMaxNameStringBufferSize); response.sampleRate = audioSource.sampleRate; response.minBufferDuration = audioSource.minBufferDuration; response.maxBufferDuration = audioSource.maxBufferDuration; @@ -273,15 +258,6 @@ bool ChreApiTestService:: return true; } -bool ChreApiTestService:: - validateInputAndRetrieveLatestDisconnectedHostEndpointEvent( - const chre_rpc_Void & /* request */, - chre_rpc_RetrieveLatestDisconnectedHostEndpointEventOutput &response) { - response.disconnectedCount = mReceivedHostEndpointDisconnectedNum; - response.hostEndpointId = mLatestHostEndpointNotification.hostEndpointId; - return true; -} - bool ChreApiTestService::validateInputAndCallChreGetHostEndpointInfo( const chre_rpc_ChreGetHostEndpointInfoInput &request, chre_rpc_ChreGetHostEndpointInfoOutput &response) { @@ -302,15 +278,15 @@ bool ChreApiTestService::validateInputAndCallChreGetHostEndpointInfo( response.isTagValid = hostEndpointInfo.isTagValid; if (hostEndpointInfo.isNameValid) { copyString(response.endpointName, hostEndpointInfo.endpointName, - CHRE_MAX_ENDPOINT_NAME_LEN); + kMaxHostEndpointNameBufferSize); } else { - memset(response.endpointName, 0, CHRE_MAX_ENDPOINT_NAME_LEN); + memset(response.endpointName, 0, kMaxHostEndpointNameBufferSize); } if (hostEndpointInfo.isTagValid) { copyString(response.endpointTag, hostEndpointInfo.endpointTag, - CHRE_MAX_ENDPOINT_TAG_LEN); + kMaxHostEndpointTagBufferSize); } else { - memset(response.endpointTag, 0, CHRE_MAX_ENDPOINT_TAG_LEN); + memset(response.endpointTag, 0, kMaxHostEndpointTagBufferSize); } LOGD("ChreGetHostEndpointInfo: status: true, hostEndpointID: %" PRIu32 @@ -325,3 +301,37 @@ bool ChreApiTestService::validateInputAndCallChreGetHostEndpointInfo( } return true; } + +bool ChreApiTestService::validateBleScanFilters( + const chre_rpc_ChreBleGenericFilter *scanFilters, + chreBleGenericFilter *outputScanFilters, uint32_t scanFilterCount) { + if (scanFilters == nullptr || outputScanFilters == nullptr) { + return false; + } + + for (uint32_t i = 0; i < scanFilterCount; ++i) { + const chre_rpc_ChreBleGenericFilter &scanFilter = scanFilters[i]; + if (scanFilter.type > std::numeric_limits<uint8_t>::max() || + scanFilter.length > std::numeric_limits<uint8_t>::max()) { + LOGE( + "validateBleScanFilters: invalid request.filter.scanFilters member: " + "type: %" PRIu32 " or length: %" PRIu32, + scanFilter.type, scanFilter.length); + return false; + } + + if (scanFilter.data.size < scanFilter.length || + scanFilter.mask.size < scanFilter.length) { + LOGE( + "validateBleScanFilters: invalid request.filter.scanFilters member: " + "data or mask size"); + return false; + } + + outputScanFilters[i] = + createBleGenericFilter(scanFilter.type, scanFilter.length, + scanFilter.data.bytes, scanFilter.mask.bytes); + } + + return true; +} diff --git a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h index ffc2baed..3f47746f 100644 --- a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h +++ b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h @@ -84,22 +84,6 @@ class Manager { bool mChreDataCollectionDone = false; /** - * This is the fraction of the number of results in the greater set of - * scan results between the AP and CHRE that the lesser set can differ by. - * Increasing this value will increase the relative amount that AP and CHRE - * results sizes can differ by. - * - * Ex: AP_results_size = 8 - * CHRE_results_size = 7 - * kMaxDiffNumResultsFraction = 0.25 - * - * CHRE_results_size is valid because it is >= 8 - 8 * 0.25 = 6 - */ - // TODO(b/185026344): Perfect this number. Consider using an abolute - // difference instead of a percentage difference also. - static constexpr float kMaxDiffNumResultsFraction = 0.25f; - - /** * Handle a message from the host. * @param senderInstanceId The instance id of the sender. * @param data The message from the host's data. diff --git a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc index 49af65e3..7351bc20 100644 --- a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc +++ b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc @@ -180,9 +180,11 @@ void Manager::compareAndSendResultToHost() { LOGE("AP and CHRE wifi scan result counts differ, AP = %" PRIu8 ", CHRE = %" PRIu8, mApScanResultsSize, mChreScanResultsSize); - } else { - verifyScanResults(&testResult); + + return; } + + verifyScanResults(&testResult); test_shared::sendMessageToHost( mCrossValidatorState.hostEndpoint, &testResult, chre_test_common_TestResult_fields, diff --git a/apps/test/common/chre_settings_test/inc/chre_settings_test_manager.h b/apps/test/common/chre_settings_test/inc/chre_settings_test_manager.h index 89863dc1..602b60d0 100644 --- a/apps/test/common/chre_settings_test/inc/chre_settings_test_manager.h +++ b/apps/test/common/chre_settings_test/inc/chre_settings_test_manager.h @@ -184,6 +184,10 @@ class Manager { //! The number of scan result received when after getting a wifi async result. uint16_t mReceivedScanResults; + + //! True if we have received a chreAudioSourceStatusEvent with suspended == + //! false. + bool mAudioSamplingEnabled; }; // The settings test manager singleton. diff --git a/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc b/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc index 77b8ac6e..2f55d309 100644 --- a/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc +++ b/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc @@ -44,6 +44,9 @@ constexpr uint32_t kGnssLocationCookie = 0x3456; constexpr uint32_t kGnssMeasurementCookie = 0x4567; constexpr uint32_t kWwanCellInfoCookie = 0x5678; +// The default audio handle. +constexpr uint32_t kAudioHandle = 0; + // Flag to verify if an audio data event was received after a valid sampling // change event (i.e., we only got the data event after a source-enabled-and- // not-suspended event). @@ -182,7 +185,7 @@ bool Manager::isFeatureSupported(Feature feature) { } case Feature::AUDIO: { struct chreAudioSource source; - supported = chreAudioGetSource(0 /* handle */, &source); + supported = chreAudioGetSource(kAudioHandle, &source); break; } case Feature::BLE_SCANNING: { @@ -348,8 +351,8 @@ bool Manager::startTestForFeature(Feature feature) { case Feature::AUDIO: { struct chreAudioSource source; - if ((success = chreAudioGetSource(0 /* handle */, &source))) { - success = chreAudioConfigureSource(0 /* handle */, true /* enable */, + if ((success = chreAudioGetSource(kAudioHandle, &source))) { + success = chreAudioConfigureSource(kAudioHandle, true /* enable */, source.minBufferDuration, source.minBufferDuration); } @@ -547,52 +550,61 @@ void Manager::handleWwanCellInfoResult(const chreWwanCellInfoResult *result) { void Manager::handleAudioSourceStatusEvent( const struct chreAudioSourceStatusEvent *event) { + LOGI("Received sampling status event suspended %d", event->status.suspended); + mAudioSamplingEnabled = !event->status.suspended; + if (!mTestSession.has_value()) { + return; + } + bool success = false; - if (mTestSession.has_value()) { - if (mTestSession->featureState == FeatureState::ENABLED) { - if (event->status.suspended) { - struct chreAudioSource source; - if (chreAudioGetSource(0 /* handle */, &source)) { - const uint64_t duration = - source.minBufferDuration + kOneSecondInNanoseconds; - gAudioDataTimerHandle = chreTimerSet(duration, &kAudioDataTimerCookie, - true /* oneShot */); - - if (gAudioDataTimerHandle == CHRE_TIMER_INVALID) { - LOGE("Failed to set data check timer"); - } else { - success = true; - } - } else { - LOGE("Failed to query audio source"); - } - } else { - // There might be a corner case where CHRE might have queued an audio - // available event just as the microphone disable setting change is - // received that might wrongfully indicate that microphone access - // wasn't disabled when it is dispatched. We add a 2 second timer to - // allow CHRE to send the source status change event to account for - // this, and fail the test if the timer expires without getting said - // event. - LOGW("Source wasn't suspended when Mic Access disabled, waiting 2 sec"); - gAudioStatusTimerHandle = - chreTimerSet(2 * kOneSecondInNanoseconds, &kAudioStatusTimerCookie, - true /* oneShot */); - if (gAudioStatusTimerHandle == CHRE_TIMER_INVALID) { - LOGE("Failed to set audio status check timer"); + if (mTestSession->featureState == FeatureState::ENABLED) { + if (event->status.suspended) { + if (gAudioStatusTimerHandle != CHRE_TIMER_INVALID) { + chreTimerCancel(gAudioStatusTimerHandle); + gAudioStatusTimerHandle = CHRE_TIMER_INVALID; + } + + struct chreAudioSource source; + if (chreAudioGetSource(kAudioHandle, &source)) { + const uint64_t duration = + source.minBufferDuration + kOneSecondInNanoseconds; + gAudioDataTimerHandle = + chreTimerSet(duration, &kAudioDataTimerCookie, true /* oneShot */); + + if (gAudioDataTimerHandle == CHRE_TIMER_INVALID) { + LOGE("Failed to set data check timer"); } else { - // continue the test, fail on timeout. success = true; } + } else { + LOGE("Failed to query audio source"); } } else { - gGotSourceEnabledEvent = true; - success = true; + // There might be a corner case where CHRE might have queued an audio + // available event just as the microphone disable setting change is + // received that might wrongfully indicate that microphone access + // wasn't disabled when it is dispatched. We add a 2 second timer to + // allow CHRE to send the source status change event to account for + // this, and fail the test if the timer expires without getting said + // event. + LOGW("Source wasn't suspended when Mic Access disabled, waiting 2 sec"); + gAudioStatusTimerHandle = + chreTimerSet(2 * kOneSecondInNanoseconds, &kAudioStatusTimerCookie, + true /* oneShot */); + if (gAudioStatusTimerHandle == CHRE_TIMER_INVALID) { + LOGE("Failed to set audio status check timer"); + } else { + // continue the test, fail on timeout. + success = true; + } } + } else { + gGotSourceEnabledEvent = true; + success = true; + } - if (!success) { - sendTestResult(mTestSession->hostEndpointId, success); - } + if (!success) { + sendTestResult(mTestSession->hostEndpointId, success); } } @@ -609,7 +621,7 @@ void Manager::handleAudioDataEvent(const struct chreAudioDataEvent *event) { } else if (gGotSourceEnabledEvent) { success = true; } - chreAudioConfigureSource(0 /* handle */, false /* enable */, + chreAudioConfigureSource(kAudioHandle, false /* enable */, 0 /* minBufferDuration */, 0 /* maxbufferDuration */); sendTestResult(mTestSession->hostEndpointId, success); @@ -619,18 +631,18 @@ void Manager::handleAudioDataEvent(const struct chreAudioDataEvent *event) { void Manager::handleTimeout(const void *eventData) { bool testSuccess = false; auto *cookie = static_cast<const uint32_t *>(eventData); + // Ignore the audio status timer if the suspended status was received. + if (*cookie == kAudioStatusTimerCookie && !mAudioSamplingEnabled) { + gAudioStatusTimerHandle = CHRE_TIMER_INVALID; + return; + } if (*cookie == kAudioDataTimerCookie) { gAudioDataTimerHandle = CHRE_TIMER_INVALID; testSuccess = true; - if (gAudioStatusTimerHandle != CHRE_TIMER_INVALID) { - chreTimerCancel(gAudioStatusTimerHandle); - gAudioStatusTimerHandle = CHRE_TIMER_INVALID; - } } else if (*cookie == kAudioStatusTimerCookie) { - LOGE("Source wasn't suspended when Mic Access was disabled"); gAudioStatusTimerHandle = CHRE_TIMER_INVALID; - testSuccess = false; + LOGE("Source wasn't suspended when Mic Access was disabled"); } else { LOGE("Invalid timer cookie: %" PRIx32, *cookie); } diff --git a/apps/test/common/rpc_service_test/inc/rpc_service_manager.h b/apps/test/common/rpc_service_test/inc/rpc_service_manager.h index f092c868..4807c600 100644 --- a/apps/test/common/rpc_service_test/inc/rpc_service_manager.h +++ b/apps/test/common/rpc_service_test/inc/rpc_service_manager.h @@ -60,6 +60,11 @@ class RpcServiceManager { const void *eventData); /** + * Cleanup on nanoapp end. + */ + void end(); + + /** * Sets the permission for the next server message. * * @params permission Bitmasked CHRE_MESSAGE_PERMISSION_. diff --git a/apps/test/common/rpc_service_test/src/rpc_service_manager.cc b/apps/test/common/rpc_service_test/src/rpc_service_manager.cc index bea1036f..956aad60 100644 --- a/apps/test/common/rpc_service_test/src/rpc_service_manager.cc +++ b/apps/test/common/rpc_service_test/src/rpc_service_manager.cc @@ -57,6 +57,10 @@ void RpcServiceManager::handleEvent(uint32_t senderInstanceId, } } +void RpcServiceManager::end() { + mServer.close(); +} + void RpcServiceManager::setPermissionForNextMessage(uint32_t permission) { mServer.setPermissionForNextMessage(permission); } diff --git a/apps/test/common/rpc_service_test/src/rpc_service_test.cc b/apps/test/common/rpc_service_test/src/rpc_service_test.cc index da709ff3..13812882 100644 --- a/apps/test/common/rpc_service_test/src/rpc_service_test.cc +++ b/apps/test/common/rpc_service_test/src/rpc_service_test.cc @@ -31,6 +31,7 @@ extern "C" bool nanoappStart(void) { } extern "C" void nanoappEnd(void) { + RpcServiceManagerSingleton::get()->end(); RpcServiceManagerSingleton::deinit(); } diff --git a/build/build_template.mk b/build/build_template.mk index 4907577f..18bf30b8 100644 --- a/build/build_template.mk +++ b/build/build_template.mk @@ -51,6 +51,9 @@ define BUILD_TEMPLATE # Target Objects ############################################################### +# Remove duplicates +COMMON_SRCS := $(sort $(COMMON_SRCS)) + # Source files. $(1)_CC_SRCS = $$(filter %.cc, $(COMMON_SRCS) $(8)) $(1)_CPP_SRCS = $$(filter %.cpp, $(COMMON_SRCS) $(8)) @@ -229,18 +232,33 @@ $$($(1)_AR): $$($(1)_CC_OBJS) $$($(1)_CPP_OBJS) $$($(1)_C_OBJS) \ # Token Mapping ################################################################ $$($(1)_TOKEN_MAP): $$($(1)_AR) - @echo " [TOKEN_MAP_GEN] $<" - $(V)mkdir -p $(OUT)/$(1) - $(V)$(TOKEN_MAP_GEN_CMD) $$($(1)_TOKEN_MAP) $$($(1)_AR) - $(V)$(TOKEN_MAP_CSV_GEN_CMD) $$($(1)_TOKEN_MAP_CSV) $$($(1)_AR) + @echo " [TOKEN_MAP_GEN] $$@" + $(V)mkdir -p $$(@D) + $(V)$(TOKEN_MAP_GEN_CMD) $$($(1)_TOKEN_MAP) $$($(1)_AR) 2>&1 + $(V)$(TOKEN_MAP_CSV_GEN_CMD) $$($(1)_TOKEN_MAP_CSV) $$($(1)_AR) 2>&1 + +# Rust ######################################################################### + +ifeq ($(IS_BUILD_REQUIRING_RUST),) +RUST_DEPENDENCIES = +else +RUST_DEPENDENCIES = rust_archive_$(1) +endif + +# Always invoke the cargo build, let cargo decide if updates are needed +.PHONY: rust_archive_$(1) +rust_archive_$(1): + @echo " [Rust Archive] $$@" + $(RUST_FLAGS) cargo +nightly build -Z build-std=core,alloc \ + --$(RUST_OPT_LEVEL) --target $(RUST_TARGET_DIR)/$(RUST_TARGET).json # Link ######################################################################### $$($(1)_SO): $$($(1)_CC_DEPS) \ $$($(1)_CPP_DEPS) $$($(1)_C_DEPS) $$($(1)_S_DEPS) \ $$($(1)_CC_OBJS) $$($(1)_CPP_OBJS) $$($(1)_C_OBJS) \ - $$($(1)_S_OBJS) | $$(OUT)/$(1) $$($(1)_DIRS) - $(V)$(5) $(4) -o $$@ $(11) $$(filter %.o, $$^) $(12) + $$($(1)_S_OBJS) $(RUST_DEPENDENCIES) | $$(OUT)/$(1) $$($(1)_DIRS) + $(5) $(4) -o $$@ $(11) $$(filter %.o, $$^) $(12) $$($(1)_BIN): $$($(1)_CC_DEPS) \ $$($(1)_CPP_DEPS) $$($(1)_C_DEPS) $$($(1)_S_DEPS) \ diff --git a/build/clang.mk b/build/clang.mk index 8481c9dd..0b5a2652 100644 --- a/build/clang.mk +++ b/build/clang.mk @@ -10,5 +10,5 @@ $(error "You should supply an ANDROID_BUILD_TOP environment variable \ endif # Clang toolchain path ######################################################## -CLANG_TOOLCHAIN_PATH=$(ANDROID_BUILD_TOP)/prebuilts/clang/host/linux-x86/clang-r475365b +CLANG_TOOLCHAIN_PATH=$(ANDROID_BUILD_TOP)/prebuilts/clang/host/linux-x86/clang-r498229b IS_CLANG_TOOLCHAIN=true diff --git a/build/common.mk b/build/common.mk index e99c67ac..38a67438 100644 --- a/build/common.mk +++ b/build/common.mk @@ -51,4 +51,7 @@ include $(CHRE_PREFIX)/build/tools_config.mk include $(CHRE_PREFIX)/build/nanopb.mk # TFLM Sources -include $(CHRE_PREFIX)/external/tflm/tflm.mk
\ No newline at end of file +include $(CHRE_PREFIX)/external/tflm/tflm.mk + +# Rust config +include $(CHRE_PREFIX)/build/rust/common_rust_config.mk
\ No newline at end of file diff --git a/build/nanoapp/app.mk b/build/nanoapp/app.mk index 3f827784..fa35eb26 100644 --- a/build/nanoapp/app.mk +++ b/build/nanoapp/app.mk @@ -92,6 +92,11 @@ ifneq ($(CHRE_NANOAPP_USES_WWAN),) COMMON_CFLAGS += -DCHRE_NANOAPP_USES_WWAN endif +ifneq ($(CHRE_NANOAPP_USES_TOKENIZED_LOGGING),) +COMMON_CFLAGS += -DCHRE_TOKENIZED_LOGGING_ENABLED +include $(CHRE_PREFIX)/external/pigweed/pw_tokenizer.mk +endif + # Common Compiler Flags ######################################################## # Add the CHRE API to the include search path. @@ -126,8 +131,9 @@ COMMON_CFLAGS += -DNANOAPP_UNSTABLE_ID="\"$(NANOAPP_UNSTABLE_ID)\"" # Variant-specific Nanoapp Support Source Files ################################ APP_SUPPORT_PATH = $(CHRE_PREFIX)/build/app_support -DSO_SUPPORT_LIB_PATH = $(CHRE_PREFIX)/platform/shared/nanoapp -DSO_SUPPORT_LIB_SRCS = $(DSO_SUPPORT_LIB_PATH)/nanoapp_support_lib_dso.cc +SHARED_NANOAPP_LIB_PATH = $(CHRE_PREFIX)/platform/shared/nanoapp +DSO_SUPPORT_LIB_SRCS = $(SHARED_NANOAPP_LIB_PATH)/nanoapp_support_lib_dso.cc +STACK_CHECK_SRCS = $(SHARED_NANOAPP_LIB_PATH)/nanoapp_stack_check.cc # Required includes for nanoapp_support_lib_dso.cc, but using a special prefix # directory and symlinks to effectively hide them from nanoapps @@ -171,9 +177,6 @@ include $(CHRE_PREFIX)/std_overrides/std_overrides.mk include $(CHRE_PREFIX)/build/defs.mk include $(CHRE_PREFIX)/build/common.mk -# Pigweed module includes -include $(CHRE_PREFIX)/external/pigweed/pw_rpc.mk - # CHRE API version. include $(CHRE_PREFIX)/chre_api/chre_api_version.mk diff --git a/build/nanopb.mk b/build/nanopb.mk index 012d7c69..012d683a 100644 --- a/build/nanopb.mk +++ b/build/nanopb.mk @@ -1,16 +1,20 @@ # -# Nanoapp/CHRE NanoPB Makefile +# Nanoapp/CHRE NanoPB and Pigweed RPC Makefile # -# Include this file in your nanoapp Makefile to produce pb.c and pb.h (or -# $NANOPB_EXTENSION.c and $NANOPB_EXTENSION.h if $NANOPB_EXTENSION is defined) -# for .proto files specified in the NANOPB_SRCS variable. The produced pb.c or -# $NANOPB_EXTENSION.c files are automatically added to the COMMON_SRCS variable +# Include this file in your nanoapp Makefile to generate .c source and .h header +# files. ($NANOPB_EXTENSION.c and $NANOPB_EXTENSION.h if $NANOPB_EXTENSION +# is defined) for .proto files specified in the NANOPB_SRCS and PW_RPC_SRCS +# variables. +# +# The generated source files are automatically added to the COMMON_SRCS variable # for the nanoapp build. # +# The path to the generated header files is similarly added to the COMMON_CFLAGS. +# # The NANOPB_OPTIONS variable can be used to supply an .options file to use when # generating code for all .proto files. Alternatively, if an .options file has -# the same name as a .proto file in NANOPB_SRCS, it'll be automatically picked -# up when generating code **only** for that .proto file. +# the same name as a .proto file, it'll be automatically picked up when generating +# code **only** for that .proto file. # # NANOPB_FLAGS can be used to supply additional command line arguments to the # nanopb compiler. Note that this is global and applies to all protobuf @@ -25,20 +29,34 @@ # Environment Checks ########################################################### +HAS_PROTO_SRC = false + ifneq ($(NANOPB_SRCS),) ifeq ($(NANOPB_PREFIX),) $(error "NANOPB_SRCS is non-empty. You must supply a NANOPB_PREFIX environment \ variable containing a path to the nanopb project. Example: \ export NANOPB_PREFIX=$$HOME/path/to/nanopb/nanopb-c") endif +HAS_PROTO_SRC = true +endif + +ifneq ($(PW_RPC_SRCS),) +ifeq ($(NANOPB_PREFIX),) +$(error "PW_RPC_SRCS is non-empty. You must supply a NANOPB_PREFIX environment \ + variable containing a path to the nanopb project. Example: \ + export NANOPB_PREFIX=$$HOME/path/to/nanopb/nanopb-c") +endif +HAS_PROTO_SRC = true endif +################################################################################ +# Common ####################################################################### +################################################################################ + ifeq ($(PROTOC),) PROTOC=protoc endif -# Generated Source Files ####################################################### - NANOPB_GEN_PATH = $(OUT)/nanopb_gen ifeq ($(NANOPB_EXTENSION),) @@ -51,10 +69,16 @@ NANOPB_GEN_SRCS += $(patsubst %.proto, \ $(NANOPB_GEN_PATH)/%.$(NANOPB_EXTENSION).c, \ $(NANOPB_SRCS)) -ifneq ($(NANOPB_GEN_SRCS),) +# Add Google proto well-known types. See https://protobuf.dev/reference/protobuf/google.protobuf/. +PROTOBUF_DIR = $(ANDROID_BUILD_TOP)/external/protobuf +COMMON_CFLAGS += -I$(NANOPB_GEN_PATH)/$(PROTOBUF_DIR)/src + +################################################################################ +# Common to nanopb & rpc ####################################################### +################################################################################ + +ifeq ($(HAS_PROTO_SRC),true) COMMON_CFLAGS += -I$(NANOPB_PREFIX) -COMMON_CFLAGS += -I$(NANOPB_GEN_PATH) -COMMON_CFLAGS += $(addprefix -I$(NANOPB_GEN_PATH)/, $(NANOPB_INCLUDES)) ifneq ($(NANOPB_INCLUDE_LIBRARY),false) COMMON_SRCS += $(NANOPB_PREFIX)/pb_common.c @@ -62,14 +86,22 @@ COMMON_SRCS += $(NANOPB_PREFIX)/pb_decode.c COMMON_SRCS += $(NANOPB_PREFIX)/pb_encode.c endif +# NanoPB Compiler Flags +ifneq ($(NANOPB_INCLUDE_LIBRARY),false) +COMMON_CFLAGS += -DPB_NO_PACKED_STRUCTS=1 endif -# NanoPB Compiler Flags ######################################################## +NANOPB_PROTOC = $(NANOPB_PREFIX)/generator/protoc-gen-nanopb + +endif # ifeq ($(HAS_PROTO_SRC),true) + +################################################################################ +# nanopb ####################################################################### +################################################################################ ifneq ($(NANOPB_GEN_SRCS),) -ifneq ($(NANOPB_INCLUDE_LIBRARY),false) -COMMON_CFLAGS += -DPB_NO_PACKED_STRUCTS=1 -endif +COMMON_CFLAGS += -I$(NANOPB_GEN_PATH) +COMMON_CFLAGS += $(addprefix -I$(NANOPB_GEN_PATH)/, $(NANOPB_INCLUDES)) endif # NanoPB Generator Setup ####################################################### @@ -78,7 +110,7 @@ NANOPB_GENERATOR_SRCS = $(NANOPB_PREFIX)/generator/proto/nanopb_pb2.py NANOPB_GENERATOR_SRCS += $(NANOPB_PREFIX)/generator/proto/plugin_pb2.py $(NANOPB_GENERATOR_SRCS): - cd $(NANOPB_PREFIX)/generator/proto && make + cd $(NANOPB_PREFIX)/generator/proto && $(MAKE) ifneq ($(NANOPB_OPTIONS),) NANOPB_OPTIONS_FLAG = --options-file=$(NANOPB_OPTIONS) @@ -100,10 +132,12 @@ $(NANOPB_GEN_PATH)/%.$(NANOPB_EXTENSION).c \ $(NANOPB_GENERATOR_SRCS) @echo " [NANOPB] $<" $(V)mkdir -p $(dir $@) - $(V)$(PROTOC) --plugin=protoc-gen-nanopb=$(NANOPB_PROTOC) \ + $(V)PYTHONPATH=$(PYTHONPATH) $(PROTOC) \ + --plugin=protoc-gen-nanopb=$(NANOPB_PROTOC) \ --proto_path=$(abspath $(dir $<)) \ $(NANOPB_FLAGS) \ - --nanopb_out="$(NANOPB_GENERATOR_FLAGS) --options-file=$(basename $<).options:$(dir $@)" \ + --nanopb_out="$(NANOPB_GENERATOR_FLAGS) \ + --options-file=$(basename $<).options:$(dir $@)" \ $(abspath $<) $(NANOPB_GEN_PATH)/%.$(NANOPB_EXTENSION).c \ @@ -112,8 +146,238 @@ $(NANOPB_GEN_PATH)/%.$(NANOPB_EXTENSION).c \ $(NANOPB_GENERATOR_SRCS) @echo " [NANOPB] $<" $(V)mkdir -p $(dir $@) - $(V)$(PROTOC) --plugin=protoc-gen-nanopb=$(NANOPB_PROTOC) \ + $(V)PYTHONPATH=$(PYTHONPATH) $(PROTOC) \ + --plugin=protoc-gen-nanopb=$(NANOPB_PROTOC) \ --proto_path=$(abspath $(dir $<)) \ $(NANOPB_FLAGS) \ --nanopb_out="$(NANOPB_GENERATOR_FLAGS) $(NANOPB_OPTIONS_FLAG):$(dir $@)" \ $(abspath $<) + +################################################################################ +# Specific to pigweed RPC ###################################################### +################################################################################ +ifneq ($(PW_RPC_SRCS),) + +# Location of various Pigweed modules +PIGWEED_DIR = $(ANDROID_BUILD_TOP)/external/pigweed +PROTOBUF_DIR = $(ANDROID_BUILD_TOP)/external/protobuf +CHRE_PREFIX = $(ANDROID_BUILD_TOP)/system/chre +CHRE_UTIL_DIR = $(CHRE_PREFIX)/util +CHRE_API_DIR = $(CHRE_PREFIX)/chre_api +PIGWEED_CHRE_DIR=$(CHRE_PREFIX)/external/pigweed +PIGWEED_CHRE_UTIL_DIR = $(CHRE_UTIL_DIR)/pigweed + +PW_RPC_GEN_PATH = $(OUT)/pw_rpc_gen + +# Create proto used for header generation ###################################### + +PW_RPC_PROTO_GENERATOR = $(PIGWEED_DIR)/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py +PW_RPC_GENERATOR_PROTO = $(PIGWEED_DIR)/pw_rpc/internal/packet.proto +PW_RPC_GENERATOR_COMPILED_PROTO = $(PW_RPC_GEN_PATH)/py/pw_rpc/internal/packet_pb2.py +PW_PROTOBUF_PROTOS = $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/common.proto \ + $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/field_options.proto \ + $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/status.proto + +# Modifies PYTHONPATH so that python can see all of pigweed's modules used by +# their protoc plugins +PW_RPC_GENERATOR_CMD = PYTHONPATH=$$PYTHONPATH:$(PW_RPC_GEN_PATH)/py:$\ + $(PIGWEED_DIR)/pw_status/py:$(PIGWEED_DIR)/pw_protobuf/py:$\ + $(PIGWEED_DIR)/pw_protobuf_compiler/py $(PYTHON) + +$(PW_RPC_GENERATOR_COMPILED_PROTO): $(PW_RPC_GENERATOR_PROTO) + @echo " [PW_RPC] $<" + $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_rpc/internal + $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_protobuf_codegen_protos + $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_protobuf_protos + $(V)cp -R $(PIGWEED_DIR)/pw_rpc/py/pw_rpc $(PW_RPC_GEN_PATH)/py/ + + $(PROTOC) -I$(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos \ + --experimental_allow_proto3_optional \ + --python_out=$(PW_RPC_GEN_PATH)/py/pw_protobuf_protos \ + $(PW_PROTOBUF_PROTOS) + + $(PROTOC) -I$(PIGWEED_DIR)/pw_protobuf/pw_protobuf_codegen_protos \ + --experimental_allow_proto3_optional \ + --python_out=$(PW_RPC_GEN_PATH)/py/pw_protobuf_codegen_protos \ + $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_codegen_protos/codegen_options.proto + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --out-dir=$(PW_RPC_GEN_PATH)/py/pw_rpc/internal \ + --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \ + --language python + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) \ + --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ + --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \ + --language pwpb + +# Generated PW RPC Files ####################################################### + +PW_RPC_GEN_SRCS = $(patsubst %.proto, \ + $(PW_RPC_GEN_PATH)/%.pb.c, \ + $(PW_RPC_SRCS)) + +# Include to-be-generated files +COMMON_CFLAGS += -I$(PW_RPC_GEN_PATH) +COMMON_CFLAGS += -I$(PW_RPC_GEN_PATH)/$(PIGWEED_DIR) + +# Add include paths to reference protos directly +COMMON_CFLAGS += $(addprefix -I$(PW_RPC_GEN_PATH)/, $(abspath $(dir $(PW_RPC_SRCS)))) + +# Add include paths to import protos +ifneq ($(PW_RPC_INCLUDE_DIRS),) +COMMON_CFLAGS += $(addprefix -I$(PW_RPC_GEN_PATH)/, $(abspath $(PW_RPC_INCLUDE_DIRS))) +endif + +# Add Google proto well-known types. See https://protobuf.dev/reference/protobuf/google.protobuf/. +COMMON_CFLAGS += -I$(PW_RPC_GEN_PATH)/$(PROTOBUF_DIR)/src + +COMMON_SRCS += $(PW_RPC_GEN_SRCS) + +# PW RPC library ############################################################### + +# Pigweed RPC include paths +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_assert/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_bytes/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_containers/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_function/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_log/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public_overrides +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/standard_library_public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_protobuf/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_result/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/nanopb/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/pwpb/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/raw/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public_overrides +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_status/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_stream/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_string/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_sync/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_toolchain/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_varint/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/fit/include +COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/stdcompat/include + +# Pigweed RPC sources +COMMON_SRCS += $(PIGWEED_DIR)/pw_assert_log/assert_log.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_containers/intrusive_list.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/decoder.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/encoder.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/stream_decoder.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/call.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/channel.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/channel_list.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/client.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/client_call.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/endpoint.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/packet.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/server.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/server_call.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/service.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/common.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/method.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/server_reader_writer.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/pwpb/server_reader_writer.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_stream/memory_stream.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/stream.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/varint_c.c +COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/varint.cc +# Pigweed configuration +COMMON_CFLAGS += -DPW_RPC_USE_GLOBAL_MUTEX=0 +COMMON_CFLAGS += -DPW_RPC_YIELD_MODE=PW_RPC_YIELD_MODE_BUSY_LOOP + +# Enable closing a client stream. +COMMON_CFLAGS += -DPW_RPC_COMPLETION_REQUEST_CALLBACK + +# Use dynamic channel allocation +COMMON_CFLAGS += -DPW_RPC_DYNAMIC_ALLOCATION +COMMON_CFLAGS += -DPW_RPC_DYNAMIC_CONTAINER\(type\)="chre::DynamicVector<type>" +COMMON_CFLAGS += -DPW_RPC_DYNAMIC_CONTAINER_INCLUDE='"chre/util/dynamic_vector.h"' + +# Add CHRE Pigweed util sources since nanoapps should always use these +COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/chre_channel_output.cc +COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_client.cc +COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_helper.cc +COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_server.cc +COMMON_SRCS += $(CHRE_UTIL_DIR)/nanoapp/callbacks.cc +COMMON_SRCS += $(CHRE_UTIL_DIR)/dynamic_vector_base.cc + +# CHRE Pigweed overrides +COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_log_nanoapp/public_overrides +COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_assert_nanoapp/public_overrides + +# Generate PW RPC headers ###################################################### + +$(PW_RPC_GEN_PATH)/%.pb.c \ + $(PW_RPC_GEN_PATH)/%.pb.h \ + $(PW_RPC_GEN_PATH)/%.rpc.pb.h \ + $(PW_RPC_GEN_PATH)/%.raw_rpc.pb.h: %.proto \ + %.options \ + $(NANOPB_GENERATOR_SRCS) \ + $(PW_RPC_GENERATOR_COMPILED_PROTO) + @echo " [PW_RPC] $<" + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(NANOPB_PROTOC) \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \ + --sources $< + +$(PW_RPC_GEN_PATH)/%.pb.c \ + $(PW_RPC_GEN_PATH)/%.pb.h \ + $(PW_RPC_GEN_PATH)/%.rpc.pb.h \ + $(PW_RPC_GEN_PATH)/%.raw_rpc.pb.h: %.proto \ + $(NANOPB_OPTIONS) \ + $(NANOPB_GENERATOR_SRCS) \ + $(PW_RPC_GENERATOR_COMPILED_PROTO) + @echo " [PW_RPC] $<" + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(NANOPB_PROTOC) \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \ + --sources $< + + $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ + --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \ + --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \ + --sources $< + +endif # ifneq ($(PW_RPC_SRCS),) diff --git a/build/rust/common_rust_config.mk b/build/rust/common_rust_config.mk new file mode 100644 index 00000000..c562445c --- /dev/null +++ b/build/rust/common_rust_config.mk @@ -0,0 +1,4 @@ +RUST_TARGET_DIR = $(CHRE_PRIV_DIR)/build/rust +RUST_OPT_LEVEL = release +# Required for linking of composite Rust + C binaries +RUST_FLAGS = RUSTFLAGS='-C relocation-model=pic'
\ No newline at end of file diff --git a/build/variant/aosp_riscv55e03_tinysys.mk b/build/variant/aosp_riscv55e03_tinysys.mk index 5513f95a..92d8a626 100644 --- a/build/variant/aosp_riscv55e03_tinysys.mk +++ b/build/variant/aosp_riscv55e03_tinysys.mk @@ -34,11 +34,10 @@ TARGET_CFLAGS += -DCHRE_32_BIT_WORD_SIZE # chre platform TARGET_CFLAGS += -DCHRE_FIRST_SUPPORTED_API_VERSION=CHRE_API_VERSION_1_7 -# TODO(b/254121302): Needs to confirm with MTK about the max message size below TARGET_CFLAGS += -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096 TARGET_CFLAGS += -DCHRE_USE_BUFFERED_LOGGING -# TODO(b/256870101): create mutex on heap for now -TARGET_CFLAGS += -DCHRE_CREATE_MUTEX_ON_HEAP +# enable static allocation in freertos +TINYSYS_CFLAGS += -DCFG_STATIC_ALLOCATE # Compiling flags ############################################################## diff --git a/chpp/Android.bp b/chpp/Android.bp index 0560224d..11db9f1d 100644 --- a/chpp/Android.bp +++ b/chpp/Android.bp @@ -52,6 +52,8 @@ cc_defaults { "-D_POSIX_C_SOURCE=199309L", // Required for pthread_setname_np() "-D_GNU_SOURCE", + // Enable assert (i.e. CHPP_ASSERT, ...) + "-UNDEBUG", ], conlyflags: [ "-std=c11", @@ -155,7 +157,10 @@ cc_test_host { "test/app_test.cpp", "test/gnss_test.cpp", "test/transport_test.cpp", - "test/clients_test.cpp", + "test/app_timeout_test.cpp", + "test/app_discovery_test.cpp", + "test/app_req_resp_test.cpp", + "test/app_notification_test.cpp", ], static_libs: [ "chre_chpp_linux", @@ -186,9 +191,10 @@ cc_test_host { name: "chre_chpp_fake_link_sync_tests", defaults: ["chre_chpp_core_without_link"], cflags: [ - // Speed up tests by setting timeouts to 10 ms - "-DCHPP_TRANSPORT_TX_TIMEOUT_NS=10000000", - "-DCHPP_TRANSPORT_RX_TIMEOUT_NS=10000000", + // Speed up tests by setting timeouts to 50 ms. + // Note: the value shouldn't be too low to avoid timeouts on slow test servers. + "-DCHPP_TRANSPORT_TX_TIMEOUT_NS=50000000", + "-DCHPP_TRANSPORT_RX_TIMEOUT_NS=50000000", ], local_include_dirs: [ "include", diff --git a/chpp/RELEASE_NOTES.md b/chpp/RELEASE_NOTES.md index 75d44408..bc1cf4df 100644 --- a/chpp/RELEASE_NOTES.md +++ b/chpp/RELEASE_NOTES.md @@ -263,4 +263,77 @@ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, The handle which used to be returned is now populated in `serviceState`. `service->appContext` is also initialized to the passed `appContext`. -This change makes the signature and behavior consistent with `chreRegisterClient`.
\ No newline at end of file +This change makes the signature and behavior consistent with `chreRegisterClient`. + +### 2023-08 + +Services can now send requests and receive responses from clients. + +The changes to the public API of the different layers are described below. +Check the inline documentation for more information. + +**Breaking changes** + +- `ChppClientState` and `ChppServiceState` have been unified into `ChppEndpointState`. + +#### app.c / app.h + +**Breaking changes** + +- Move all sync primitives to a new `struct ChppSyncResponse`, +- Renamed `ChppClient.rRStateCount` to `ChppClient.outReqCount`. The content and meaning stay the same, +- Split `struct ChppRequestResponseState` to `struct ChppOutgoingRequestState` and `struct ChppIncomingRequestState`. Both the struct have the same layout and usage as the former `struct`. Having different types is only to make code clearer as both clients and services now support both incoming and outgoing requests, +- Renamed `ChppAppState.nextClientRequestTimeoutNs` to `ChppAppState.nextRequestTimeoutNs`. The content and meaning stay the same, +- Renamed `CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE` to `CHPP_REQUEST_TIMEOUT_INFINITE`. + +**Added APIs** + +- Added `chppAllocResponseTypedArray` and `chppAllocResponseFixed` to allocate responses. Those can be used by both clients and services. They call the added `chppAllocResponse`, +- Added `CHPP_MESSAGE_TYPE_SERVICE_REQUEST` and `CHPP_MESSAGE_TYPE_CLIENT_RESPONSE` to the message types (`ChppMessageType`). Used for requests sent by the services and the corresponding responses sent by the client, +- Added `ChppService.responseDispatchFunctionPtr` to handle client responses, +- Added `ChppService.outReqCount` holding the number of commands supported by the service (0 when the service can not send requests), +- Added `ChppClient.requestDispatchFunctionPtr` to handle service requests, +- Added `ChppAppState.registeredServiceStates` to track service states, +- Added `ChppAppState.nextServiceRequestTimeoutNs` to track when the next service sent request will timeout, +- Added `chppTimestampIncomingRequest` to be used by both clients and services, +- Added `chppTimestampOutgoingRequest` to be used by both clients and services, +- Added `chppTimestampIncomingResponse` to be used by both clients and services, +- Added `chppTimestampOutgoingResponse` to be used by both clients and services, +- Added `chppSendTimestampedResponseOrFail` to be used by both clients and services, +- Added `chppSendTimestampedRequestOrFail` to be used by both clients and services, +- Added `chppWaitForResponseWithTimeout` to be used by both clients and services. + +#### clients.c / clients.h + +**Breaking changes** + +- Renamed `ChppClientState.rRStates` to `outReqStates` - the new type is `ChppOutgoingRequestState`. The content and meaning stay the same, +- Nest all sync primitive in `ChppClientState` into a `struct ChppSyncResponse syncResponse`, +- `chppRegisterClient` takes a `ChppOutgoingRequestState` instead of the former `ChppRequestResponseState`, +- `chppClientTimestampRequest` is replaced with `chppTimestampOutgoingRequest` in the app layer. Parameters are different, +- `chppClientTimestampResponse` is replaced with `chppTimestampIncomingResponse` in the app layer. Parameters are different, +- `chppSendTimestampedRequestOrFail` is renamed to `chppClientSendTimestampedRequestOrFail`. It takes a `ChppOutgoingRequestState` instead of the former `ChppRequestResponseState`, +- `chppSendTimestampedRequestAndWait` is renamed to `chppClientSendTimestampedRequestAndWait`. It takes a `ChppOutgoingRequestState` instead of the former `ChppRequestResponseState`, +- `chppSendTimestampedRequestAndWaitTimeout` is renamed to `chppClientSendTimestampedRequestAndWaitTimeout`. It takes a `ChppOutgoingRequestState` instead of the former `ChppRequestResponseState`, +- `chppClientSendOpenRequest` takes a `ChppOutgoingRequestState` instead of the former `ChppRequestResponseState`. + +#### services.c / services.h + +**Breaking changes** + +- Replaced `chppAllocServiceResponseTypedArray` with `chppAllocResponseTypedArray` in the app layer, +- Replaced `chppAllocServiceResponseFixed` with `chppAllocResponseFixed` in the app layer, +- Replaced `chppAllocServiceResponse` with `chppAllocResponse` in the app layer, +- `chppRegisterService` now takes and additional `struct ChppOutgoingRequestState *` to track outgoing requests. `NULL` when the service do not send requests. + +**Added APIs** + +- Added `chppAllocServiceRequestFixed` and `chppAllocServiceRequestTypedArray` to allocate service requests. They call the added `chppAllocServiceRequest`, +- Added `ChppServiceState.outReqStates` to track outgoing requests, +- Added `ChppServiceState.transaction` to track app layer packet number, +- Added `ChppServiceState.syncResponse` for sync responses, +- Added `chppAllocServiceRequest`, +- Added `chppAllocServiceRequestCommand`, +- Added `chppServiceSendTimestampedRequestOrFail`, +- Added `chppServiceSendTimestampedRequestAndWaitTimeout`, +- Added `chppServiceCloseOpenRequests` to close pending requests on reset. diff --git a/chpp/api_parser/.gitignore b/chpp/api_parser/.gitignore deleted file mode 100644 index e431c531..00000000 --- a/chpp/api_parser/.gitignore +++ /dev/null @@ -1 +0,0 @@ -parser_cache diff --git a/chpp/api_parser/chre_api_annotations.json b/chpp/api_parser/chre_api_annotations.json deleted file mode 100644 index c396bb5a..00000000 --- a/chpp/api_parser/chre_api_annotations.json +++ /dev/null @@ -1,297 +0,0 @@ -[ - { - "filename": "chre_api/include/chre_api/chre/wwan.h", - "includes": [ - "chre_api/include/chre_api/chre/common.h" - ], - "output_includes": [ - "chpp/common/common_types.h", - "chre_api/chre/wwan.h" - ], - "struct_info": [ - { - "name": "chreWwanCellInfoResult", - "annotations": [ - { - "field": "version", - "annotation": "fixed_value", - "value": "CHRE_WWAN_CELL_INFO_RESULT_VERSION" - }, - { - "field": "errorCode", - "annotation": "enum", - "enum_type": "chreError" - }, - { - "field": "cookie", - "annotation": "fixed_value", - "value": "0" - }, - { - "field": "cookie", - "annotation": "rewrite_type", - "type_override": "uint32_t" - }, - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - }, - { - "field": "cells", - "annotation": "var_len_array", - "length_field": "cellInfoCount" - } - ] - }, - { - "name": "chreWwanCellInfo", - "annotations": [ - { - "field": "cellInfoType", - "annotation": "enum", - "enum_type": "chreWwanCellInfoType" - }, - { - "field": "CellInfo", - "annotation": "union_variant", - "discriminator": "cellInfoType", - "mapping": [ - ["CHRE_WWAN_CELL_INFO_TYPE_GSM", "gsm"], - ["CHRE_WWAN_CELL_INFO_TYPE_CDMA", "cdma"], - ["CHRE_WWAN_CELL_INFO_TYPE_LTE", "lte"], - ["CHRE_WWAN_CELL_INFO_TYPE_WCDMA", "wcdma"], - ["CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA", "tdscdma"], - ["CHRE_WWAN_CELL_INFO_TYPE_NR", "nr"] - ] - }, - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - }, - { - "name": "chreWwanCellIdentityGsm", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - } - ], - "root_structs": [ - "chreWwanCellInfoResult" - ] -}, -{ - "filename": "chre_api/include/chre_api/chre/wifi.h", - "includes": [ - "chre_api/include/chre_api/chre/common.h" - ], - "output_includes": [ - "chpp/common/common_types.h", - "chre_api/chre/wifi.h" - ], - "struct_info": [ - { - "name": "chreWifiScanEvent", - "annotations": [ - { - "field": "version", - "annotation": "fixed_value", - "value": "CHRE_WIFI_SCAN_EVENT_VERSION" - }, - { - "field": "scannedFreqList", - "annotation": "var_len_array", - "length_field": "scannedFreqListLen" - }, - { - "field": "results", - "annotation": "var_len_array", - "length_field": "resultCount" - } - ] - }, - { - "name": "chreWifiScanResult", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - }, - { - "name": "chreWifiScanParams", - "annotations": [ - { - "field": "frequencyList", - "annotation": "var_len_array", - "length_field": "frequencyListLen" - }, - { - "field": "ssidList", - "annotation": "var_len_array", - "length_field": "ssidListLen" - } - ] - }, - { - "name": "chreWifiRangingEvent", - "annotations": [ - { - "field": "version", - "annotation": "fixed_value", - "value": "CHRE_WIFI_RANGING_EVENT_VERSION" - }, - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - }, - { - "field": "results", - "annotation": "var_len_array", - "length_field": "resultCount" - } - ] - }, - { - "name": "chreWifiRangingResult", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - }, - { - "name": "chreWifiRangingParams", - "annotations": [ - { - "field": "targetList", - "annotation": "var_len_array", - "length_field": "targetListLen" - } - ] - }, - { - "name": "chreWifiRangingTarget", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - }, - { - "name": "chreWifiNanSubscribeConfig", - "annotations": [ - { - "field": "subscribeType", - "annotation": "enum", - "enum_type": "chreWifiNanSubscribeType" - }, - { - "field": "service", - "annotation": "string" - }, - { - "field": "serviceSpecificInfo", - "annotation": "var_len_array", - "length_field": "serviceSpecificInfoSize" - }, - { - "field": "matchFilter", - "annotation": "var_len_array", - "length_field": "matchFilterLength" - } - ] - }, - { - "name": "chreWifiNanDiscoveryEvent", - "annotations": [ - { - "field": "serviceSpecificInfo", - "annotation": "var_len_array", - "length_field": "serviceSpecificInfoSize" - } - ] - } - ], - "root_structs": [ - "chreWifiScanEvent", - "chreWifiScanParams", - "chreWifiRangingEvent", - "chreWifiRangingParams", - "chreWifiNanSubscribeConfig", - "chreWifiNanDiscoveryEvent", - "chreWifiNanSessionLostEvent", - "chreWifiNanSessionTerminatedEvent", - "chreWifiNanRangingParams" - ] -}, -{ - "filename": "chre_api/include/chre_api/chre/gnss.h", - "includes": [ - "chre_api/include/chre_api/chre/common.h" - ], - "output_includes": [ - "chpp/common/common_types.h", - "chre_api/chre/gnss.h" - ], - "struct_info": [ - { - "name": "chreGnssDataEvent", - "annotations": [ - { - "field": "version", - "annotation": "fixed_value", - "value": "CHRE_GNSS_DATA_EVENT_VERSION" - }, - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - }, - { - "field": "measurements", - "annotation": "var_len_array", - "length_field": "measurement_count" - } - ] - }, - { - "name": "chreGnssLocationEvent", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - }, - { - "name": "chreGnssClock", - "annotations": [ - { - "field": "reserved", - "annotation": "fixed_value", - "value": "0" - } - ] - } - ], - "root_structs": [ - "chreGnssDataEvent", - "chreGnssLocationEvent" - ] -}] diff --git a/chpp/api_parser/requirements.txt b/chpp/api_parser/requirements.txt deleted file mode 100644 index 9b9ec0a5..00000000 --- a/chpp/api_parser/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -future==0.18.2 -pyclibrary==0.1.4 @@ -25,6 +25,7 @@ #include "chpp/clients.h" #include "chpp/clients/discovery.h" +#include "chpp/services.h" #ifdef CHPP_CLIENT_ENABLED_LOOPBACK #include "chpp/clients/loopback.h" #endif @@ -40,7 +41,7 @@ #include "chpp/services/loopback.h" #include "chpp/services/nonhandle.h" #include "chpp/services/timesync.h" -#include "chre_api/chre/common.h" +#include "chpp/time.h" /************************************************ * Prototypes @@ -50,31 +51,28 @@ static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, uint8_t *buf, size_t len); static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context, uint8_t *buf, size_t len); -static bool chppProcessPredefinedClientNotification( - struct ChppAppState *context, uint8_t *buf, size_t len); -static bool chppProcessPredefinedServiceNotification( - struct ChppAppState *context, uint8_t *buf, size_t len); static bool chppDatagramLenIsOk(struct ChppAppState *context, - struct ChppAppHeader *rxHeader, size_t len); -ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context, - uint8_t handle, - enum ChppMessageType type); -ChppNotifierFunction *chppGetClientResetNotifierFunction( + const struct ChppAppHeader *rxHeader, + size_t len); +static ChppDispatchFunction *chppGetDispatchFunction( + struct ChppAppState *context, uint8_t handle, enum ChppMessageType type); +#ifdef CHPP_CLIENT_ENABLED_DISCOVERY +static ChppNotifierFunction *chppGetClientResetNotifierFunction( struct ChppAppState *context, uint8_t index); -ChppNotifierFunction *chppGetServiceResetNotifierFunction( +#endif // CHPP_CLIENT_ENABLED_DISCOVERY +static ChppNotifierFunction *chppGetServiceResetNotifierFunction( struct ChppAppState *context, uint8_t index); static inline const struct ChppService *chppServiceOfHandle( struct ChppAppState *appContext, uint8_t handle); static inline const struct ChppClient *chppClientOfHandle( struct ChppAppState *appContext, uint8_t handle); -static inline void *chppServiceContextOfHandle(struct ChppAppState *appContext, - uint8_t handle); -static inline void *chppClientContextOfHandle(struct ChppAppState *appContext, - uint8_t handle); -static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext, - uint8_t handle, - enum ChppMessageType type); +static inline struct ChppEndpointState *chppServiceStateOfHandle( + struct ChppAppState *appContext, uint8_t handle); +static inline struct ChppEndpointState *chppClientStateOfHandle( + struct ChppAppState *appContext, uint8_t handle); +static struct ChppEndpointState *chppClientOrServiceStateOfHandle( + struct ChppAppState *appContext, uint8_t handle, enum ChppMessageType type); static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context, uint8_t *buf, size_t len); @@ -89,7 +87,7 @@ static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, * Processes a client request that is determined to be for a predefined CHPP * service. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. * @@ -97,7 +95,7 @@ static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, */ static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, uint8_t *buf, size_t len) { - struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; + const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; bool handleValid = true; bool dispatchResult = true; @@ -134,7 +132,7 @@ static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, * Processes a service response that is determined to be for a predefined CHPP * client. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. * @@ -142,12 +140,12 @@ static bool chppProcessPredefinedClientRequest(struct ChppAppState *context, */ static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context, uint8_t *buf, size_t len) { + CHPP_DEBUG_NOT_NULL(buf); // Possibly unused if compiling without the clients below enabled UNUSED_VAR(context); - UNUSED_VAR(buf); UNUSED_VAR(len); - struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; + const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; bool handleValid = true; bool dispatchResult = true; @@ -188,55 +186,21 @@ static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context, } /** - * Processes a client notification that is determined to be for a predefined - * CHPP service. - * - * @param context Maintains status for each app layer instance. - * @param buf Input data. Cannot be null. - * @param len Length of input data in bytes. - * - * @return False if handle is invalid. True otherwise. - */ -static bool chppProcessPredefinedClientNotification( - struct ChppAppState *context, uint8_t *buf, size_t len) { - UNUSED_VAR(context); - UNUSED_VAR(len); - UNUSED_VAR(buf); - // No predefined services support these. - return false; -} - -/** - * Processes a service notification that is determined to be for a predefined - * CHPP client. - * - * @param context Maintains status for each app layer instance. - * @param buf Input data. Cannot be null. - * @param len Length of input data in bytes. - * - * @return False if handle is invalid. True otherwise. - */ -static bool chppProcessPredefinedServiceNotification( - struct ChppAppState *context, uint8_t *buf, size_t len) { - UNUSED_VAR(context); - UNUSED_VAR(len); - UNUSED_VAR(buf); - // No predefined clients support these. - return false; -} - -/** * Verifies if the length of a Rx Datagram from the transport layer is - * sufficient for the associated service. + * sufficient for the associated service/client. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param rxHeader The pointer to the datagram RX header. * @param len Length of the datagram in bytes. * * @return true if length is ok. */ static bool chppDatagramLenIsOk(struct ChppAppState *context, - struct ChppAppHeader *rxHeader, size_t len) { + const struct ChppAppHeader *rxHeader, + size_t len) { + CHPP_DEBUG_NOT_NULL(context); + CHPP_DEBUG_NOT_NULL(rxHeader); + size_t minLen = SIZE_MAX; uint8_t handle = rxHeader->handle; @@ -259,6 +223,7 @@ static bool chppDatagramLenIsOk(struct ChppAppState *context, default: // len remains SIZE_MAX CHPP_LOGE("Invalid H#%" PRIu8, handle); + return false; } } else { // Negotiated @@ -267,6 +232,7 @@ static bool chppDatagramLenIsOk(struct ChppAppState *context, switch (messageType) { case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: + case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE: case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { const struct ChppService *service = chppServiceOfHandle(context, handle); @@ -276,6 +242,7 @@ static bool chppDatagramLenIsOk(struct ChppAppState *context, break; } case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: + case CHPP_MESSAGE_TYPE_SERVICE_REQUEST: case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { const struct ChppClient *client = chppClientOfHandle(context, handle); if (client != NULL) { @@ -283,117 +250,128 @@ static bool chppDatagramLenIsOk(struct ChppAppState *context, } break; } - default: { - break; - } - } - - if (minLen == SIZE_MAX) { - CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle); + default: + CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle); + return false; } } - if ((len < minLen) && (minLen != SIZE_MAX)) { + if (len < minLen) { CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len, minLen, handle); + return false; } - return (len >= minLen) && (minLen != SIZE_MAX); + + return true; } /** * Returns the dispatch function of a particular negotiated client/service - * handle and message type. This shall be null if it is unsupported by the - * service. + * handle and message type. + * + * Returns null if it is unsupported by the service. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param handle Handle number for the client/service. * @param type Message type. * * @return Pointer to a function that dispatches incoming datagrams for any * particular client/service. */ -ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context, - uint8_t handle, - enum ChppMessageType type) { +static ChppDispatchFunction *chppGetDispatchFunction( + struct ChppAppState *context, uint8_t handle, enum ChppMessageType type) { + CHPP_DEBUG_NOT_NULL(context); // chppDatagramLenIsOk() has already confirmed that the handle # is valid. // Therefore, no additional checks are necessary for chppClientOfHandle(), - // chppServiceOfHandle(), or chppClientServiceContextOfHandle(). + // chppServiceOfHandle(), or chppClientOrServiceStateOfHandle(). + // Make sure the client is open before it can receive any message: switch (CHPP_APP_GET_MESSAGE_TYPE(type)) { - case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: { - return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr; - } - case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: { - struct ChppClientState *clientState = - (struct ChppClientState *)chppClientServiceContextOfHandle( - context, handle, type); + case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: + case CHPP_MESSAGE_TYPE_SERVICE_REQUEST: + case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { + struct ChppEndpointState *clientState = + chppClientStateOfHandle(context, handle); if (clientState->openState == CHPP_OPEN_STATE_CLOSED) { CHPP_LOGE("RX service response but client closed"); - break; + return NULL; } - return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr; + break; } - case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { + default: + // no check needed on the service side + break; + } + + switch (CHPP_APP_GET_MESSAGE_TYPE(type)) { + case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: + return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr; + case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: + return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr; + case CHPP_MESSAGE_TYPE_SERVICE_REQUEST: + return chppClientOfHandle(context, handle)->requestDispatchFunctionPtr; + case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE: + return chppServiceOfHandle(context, handle)->responseDispatchFunctionPtr; + case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: return chppServiceOfHandle(context, handle) ->notificationDispatchFunctionPtr; - } - case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { - struct ChppClientState *clientState = - (struct ChppClientState *)chppClientServiceContextOfHandle( - context, handle, type); - if (clientState->openState == CHPP_OPEN_STATE_CLOSED) { - CHPP_LOGE("RX service notification but client closed"); - break; - } + case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: return chppClientOfHandle(context, handle) ->notificationDispatchFunctionPtr; - } } return NULL; } +#ifdef CHPP_CLIENT_ENABLED_DISCOVERY /** * Returns the reset notification function pointer of a particular negotiated - * client. The function pointer will be set to null by clients that do not need - * or support a reset notification. + * client. + * + * Returns null for clients that do not need or support a reset notification. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param index Index of the registered client. * * @return Pointer to the reset notification function. */ -ChppNotifierFunction *chppGetClientResetNotifierFunction( +static ChppNotifierFunction *chppGetClientResetNotifierFunction( struct ChppAppState *context, uint8_t index) { + CHPP_DEBUG_NOT_NULL(context); return context->registeredClients[index]->resetNotifierFunctionPtr; } +#endif // CHPP_CLIENT_ENABLED_DISCOVERY /** - * Returns the reset function pointer of a particular registered service. The - * function pointer will be set to null by services that do not need or support - * a reset notification. + * Returns the reset function pointer of a particular registered service. + * + * Returns null for services that do not need or support a reset notification. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param index Index of the registered service. * * @return Pointer to the reset function. */ ChppNotifierFunction *chppGetServiceResetNotifierFunction( struct ChppAppState *context, uint8_t index) { + CHPP_DEBUG_NOT_NULL(context); return context->registeredServices[index]->resetNotifierFunctionPtr; } /** * Returns a pointer to the ChppService struct of the service matched to a - * negotiated handle. Returns null if a service doesn't exist for the handle. + * negotiated handle. * - * @param context Maintains status for each app layer instance. + * Returns null if a service doesn't exist for the handle. + * + * @param context State of the app layer. * @param handle Handle number. * * @return Pointer to the ChppService struct of a particular service handle. */ static inline const struct ChppService *chppServiceOfHandle( struct ChppAppState *context, uint8_t handle) { + CHPP_DEBUG_NOT_NULL(context); uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle); if (serviceIndex < context->registeredServiceCount) { return context->registeredServices[serviceIndex]; @@ -404,15 +382,18 @@ static inline const struct ChppService *chppServiceOfHandle( /** * Returns a pointer to the ChppClient struct of the client matched to a - * negotiated handle. Returns null if a client doesn't exist for the handle. + * negotiated handle. + * + * Returns null if a client doesn't exist for the handle. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param handle Handle number. * * @return Pointer to the ChppClient struct matched to a particular handle. */ static inline const struct ChppClient *chppClientOfHandle( struct ChppAppState *context, uint8_t handle) { + CHPP_DEBUG_NOT_NULL(context); uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle); if (serviceIndex < context->discoveredServiceCount) { uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex]; @@ -425,71 +406,71 @@ static inline const struct ChppClient *chppClientOfHandle( } /** - * Returns a pointer to the service struct of a particular negotiated service - * handle. - * It is up to the caller to ensure the handle number is valid. + * Returns the service state for a given handle. + * + * The caller must pass a valid handle. * - * @param context Maintains status for each app layer instance. + * @param context State of the app layer. * @param handle Handle number for the service. * - * @return Pointer to the context struct of the service. + * @return Pointer to a ChppEndpointState. */ -static inline void *chppServiceContextOfHandle(struct ChppAppState *context, - uint8_t handle) { +static inline struct ChppEndpointState *chppServiceStateOfHandle( + struct ChppAppState *context, uint8_t handle) { + CHPP_DEBUG_NOT_NULL(context); CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) < context->registeredServiceCount); - return context - ->registeredServiceContexts[CHPP_SERVICE_INDEX_OF_HANDLE(handle)]; + + const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle); + return context->registeredServiceStates[serviceIdx]; } /** - * Returns a pointer to the client struct of a particular negotiated client - * handle. - * It is up to the caller to ensure the handle number is valid. + * Returns a pointer to the client state for a given handle. * - * @param context Maintains status for each app layer instance. + * The caller must pass a valid handle. + * + * @param context State of the app layer. * @param handle Handle number for the service. * - * @return Pointer to the ChppService struct of the client. + * @return Pointer to the endpoint state. */ -static inline void *chppClientContextOfHandle(struct ChppAppState *context, - uint8_t handle) { +static inline struct ChppEndpointState *chppClientStateOfHandle( + struct ChppAppState *context, uint8_t handle) { + CHPP_DEBUG_NOT_NULL(context); CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) < context->registeredClientCount); - return context - ->registeredClientContexts[context->clientIndexOfServiceIndex - [CHPP_SERVICE_INDEX_OF_HANDLE(handle)]]; + const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle); + const uint8_t clientIdx = context->clientIndexOfServiceIndex[serviceIdx]; + return context->registeredClientStates[clientIdx]->context; } /** - * Returns a pointer to the client/service struct of a particular negotiated - * client/service handle. - * It is up to the caller to ensure the handle number is valid. + * Returns a pointer to the client or service state for a given handle. * - * @param appContext Maintains status for each app layer instance. + * The caller must pass a valid handle. + * + * @param appContext State of the app layer. * @param handle Handle number for the service. * @param type Message type (indicates if this is for a client or service). * - * @return Pointer to the client/service struct of the service handle. + * @return Pointer to the endpoint state (NULL if wrong type). */ -static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext, - uint8_t handle, - enum ChppMessageType type) { +static struct ChppEndpointState *chppClientOrServiceStateOfHandle( + struct ChppAppState *appContext, uint8_t handle, + enum ChppMessageType type) { switch (CHPP_APP_GET_MESSAGE_TYPE(type)) { case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: - case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { - return chppServiceContextOfHandle(appContext, handle); - break; - } + case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE: + case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: + return chppServiceStateOfHandle(appContext, handle); + case CHPP_MESSAGE_TYPE_SERVICE_REQUEST: case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: - case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { - return chppClientContextOfHandle(appContext, handle); - break; - } - default: { + case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: + return chppClientStateOfHandle(appContext, handle); + default: CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle); return NULL; - } } } @@ -497,38 +478,38 @@ static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext, * Processes a received datagram that is determined to be for a predefined CHPP * service. Responds with an error if unsuccessful. * - * @param context Maintains status for each app layer instance. + * Predefined requests are only sent by the client side. + * Predefined responses are only sent by the service side. + * + * @param context State of the app layer. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context, uint8_t *buf, size_t len) { + CHPP_DEBUG_NOT_NULL(context); + CHPP_DEBUG_NOT_NULL(buf); + struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; - bool success = true; + bool success = false; switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) { case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: { success = chppProcessPredefinedClientRequest(context, buf, len); break; } - case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: { - success = chppProcessPredefinedClientNotification(context, buf, len); - break; - } case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: { success = chppProcessPredefinedServiceResponse(context, buf, len); break; } - case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: { - success = chppProcessPredefinedServiceNotification(context, buf, len); + default: + // Predefined client/services do not use + // - notifications, + // - service requests / client responses break; - } - default: { - success = false; - } } - if (success == false) { + if (!success) { CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE ", ID=%" PRIu8 ")", rxHeader->handle, rxHeader->type, len, rxHeader->transaction); @@ -539,42 +520,54 @@ static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context, /** * Processes a received datagram that is determined to be for a negotiated CHPP - * client or service. Responds with an error if unsuccessful. + * client or service. + * + * The datagram is processed by the dispatch function matching the datagram + * type. @see ChppService and ChppClient. * - * @param context Maintains status for each app layer instance. + * If a request dispatch function returns an error (anything different from + * CHPP_APP_ERROR_NONE) then an error response is automatically sent back to the + * remote endpoint. + * + * @param appContext State of the app layer. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ -static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, +static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *appContext, uint8_t *buf, size_t len) { - struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; + CHPP_DEBUG_NOT_NULL(appContext); + CHPP_DEBUG_NOT_NULL(buf); + + const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type); - void *clientServiceContext = - chppClientServiceContextOfHandle(context, rxHeader->handle, messageType); - if (clientServiceContext == NULL) { + // Could be either the client or the service state depending on the message + // type. + struct ChppEndpointState *endpointState = chppClientOrServiceStateOfHandle( + appContext, rxHeader->handle, messageType); + if (endpointState == NULL) { CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE ", ID=%" PRIu8 ")", rxHeader->handle, rxHeader->type, len, rxHeader->transaction); - chppEnqueueTxErrorDatagram(context->transportContext, + chppEnqueueTxErrorDatagram(appContext->transportContext, CHPP_TRANSPORT_ERROR_APPLAYER); CHPP_DEBUG_ASSERT(false); return; } ChppDispatchFunction *dispatchFunc = - chppGetDispatchFunction(context, rxHeader->handle, messageType); + chppGetDispatchFunction(appContext, rxHeader->handle, messageType); if (dispatchFunc == NULL) { CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE ", ID=%" PRIu8 ")", rxHeader->handle, rxHeader->type, len, rxHeader->transaction); - chppEnqueueTxErrorDatagram(context->transportContext, + chppEnqueueTxErrorDatagram(appContext->transportContext, CHPP_TRANSPORT_ERROR_APPLAYER); return; } // All good. Dispatch datagram and possibly notify a waiting client - enum ChppAppErrorCode error = dispatchFunc(clientServiceContext, buf, len); + enum ChppAppErrorCode error = dispatchFunc(endpointState->context, buf, len); if (error != CHPP_APP_ERROR_NONE) { CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8 @@ -582,34 +575,30 @@ static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context, error, rxHeader->handle, rxHeader->type, rxHeader->transaction, rxHeader->command, len); - // Only client requests require a dispatch failure response. - if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + // Requests require a dispatch failure response. + if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST || + messageType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST) { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(rxHeader, struct ChppAppHeader); - if (response == NULL) { - CHPP_LOG_OOM(); - } else { + chppAllocResponseFixed(rxHeader, struct ChppAppHeader); + if (response != NULL) { response->error = (uint8_t)error; - chppEnqueueTxDatagramOrFail(context->transportContext, response, + chppEnqueueTxDatagramOrFail(appContext->transportContext, response, sizeof(*response)); } } return; } - if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE) { - // Datagram is a service response. Check for synchronous operation and - // notify waiting client if needed. - - struct ChppClientState *clientState = - (struct ChppClientState *)clientServiceContext; - chppMutexLock(&clientState->responseMutex); - clientState->responseReady = true; - CHPP_LOGD( - "Finished dispatching a service response. Notifying a potential " - "synchronous client"); - chppConditionVariableSignal(&clientState->responseCondVar); - chppMutexUnlock(&clientState->responseMutex); + // Datagram is a response. + // Check for synchronous operation and notify waiting endpoint if needed. + if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE || + messageType == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE) { + struct ChppSyncResponse *syncResponse = &endpointState->syncResponse; + chppMutexLock(&syncResponse->mutex); + syncResponse->ready = true; + CHPP_LOGD("Finished dispatching a response -> synchronous notification"); + chppConditionVariableSignal(&syncResponse->condVar); + chppMutexUnlock(&syncResponse->mutex); } } @@ -631,6 +620,7 @@ void chppAppInitWithClientServiceSet( struct ChppTransportState *transportContext, struct ChppClientServiceSet clientServiceSet) { CHPP_NOT_NULL(appContext); + CHPP_DEBUG_NOT_NULL(transportContext); CHPP_LOGD("App Init"); @@ -638,7 +628,8 @@ void chppAppInitWithClientServiceSet( appContext->clientServiceSet = clientServiceSet; appContext->transportContext = transportContext; - appContext->nextRequestTimeoutNs = CHPP_TIME_MAX; + appContext->nextClientRequestTimeoutNs = CHPP_TIME_MAX; + appContext->nextServiceRequestTimeoutNs = CHPP_TIME_MAX; chppPalSystemApiInit(appContext); @@ -670,7 +661,10 @@ void chppAppDeinit(struct ChppAppState *appContext) { void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf, size_t len) { - struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; + CHPP_DEBUG_NOT_NULL(context); + CHPP_DEBUG_NOT_NULL(buf); + + const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (len == 0) { CHPP_DEBUG_ASSERT_LOG(false, "App rx w/ len 0"); @@ -711,6 +705,8 @@ void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf, } void chppAppProcessReset(struct ChppAppState *context) { + CHPP_DEBUG_NOT_NULL(context); + #ifdef CHPP_CLIENT_ENABLED_DISCOVERY if (!context->isDiscoveryComplete) { chppInitiateDiscovery(context); @@ -729,7 +725,8 @@ void chppAppProcessReset(struct ChppAppState *context) { (ResetNotifierFunction != NULL)); if (ResetNotifierFunction != NULL) { - ResetNotifierFunction(context->registeredClientContexts[clientIndex]); + ResetNotifierFunction( + context->registeredClientStates[clientIndex]->context); } } } @@ -745,7 +742,7 @@ void chppAppProcessReset(struct ChppAppState *context) { CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL)); if (ResetNotifierFunction != NULL) { - ResetNotifierFunction(context->registeredServiceContexts[i]); + ResetNotifierFunction(context->registeredServiceStates[i]->context); } } @@ -786,8 +783,11 @@ uint8_t chppAppErrorToChreError(uint8_t chppError) { uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len, const char *responseName) { + CHPP_DEBUG_NOT_NULL(buf); + CHPP_DEBUG_NOT_NULL(responseName); + CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); - struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; + const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (rxHeader->error == CHPP_APP_ERROR_NONE) { CHPP_LOGE("%s resp short len=%" PRIuSIZE, responseName, len); @@ -797,3 +797,422 @@ uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len, CHPP_LOGD("%s resp short len=%" PRIuSIZE, responseName, len); return chppAppErrorToChreError(rxHeader->error); } + +struct ChppAppHeader *chppAllocNotification(uint8_t type, size_t len) { + CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); + CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION || + type == CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION); + + struct ChppAppHeader *notification = chppMalloc(len); + if (notification != NULL) { + notification->type = type; + notification->handle = CHPP_HANDLE_NONE; + notification->transaction = 0; + notification->error = CHPP_APP_ERROR_NONE; + notification->command = CHPP_APP_COMMAND_NONE; + } else { + CHPP_LOG_OOM(); + } + return notification; +} + +struct ChppAppHeader *chppAllocRequest(uint8_t type, + struct ChppEndpointState *endpointState, + size_t len) { + CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); + CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST || + type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST); + CHPP_DEBUG_NOT_NULL(endpointState); + + struct ChppAppHeader *request = chppMalloc(len); + if (request != NULL) { + request->handle = endpointState->handle; + request->type = type; + request->transaction = endpointState->transaction; + request->error = CHPP_APP_ERROR_NONE; + request->command = CHPP_APP_COMMAND_NONE; + + endpointState->transaction++; + } else { + CHPP_LOG_OOM(); + } + return request; +} + +struct ChppAppHeader *chppAllocResponse( + const struct ChppAppHeader *requestHeader, size_t len) { + CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); + CHPP_DEBUG_NOT_NULL(requestHeader); + uint8_t type = requestHeader->type; + CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST || + type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST); + + struct ChppAppHeader *response = chppMalloc(len); + if (response != NULL) { + *response = *requestHeader; + response->type = type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE + : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE; + response->error = CHPP_APP_ERROR_NONE; + } else { + CHPP_LOG_OOM(); + } + return response; +} + +void chppTimestampIncomingRequest(struct ChppIncomingRequestState *inReqState, + const struct ChppAppHeader *requestHeader) { + CHPP_DEBUG_NOT_NULL(inReqState); + CHPP_DEBUG_NOT_NULL(requestHeader); + if (inReqState->responseTimeNs == CHPP_TIME_NONE && + inReqState->requestTimeNs != CHPP_TIME_NONE) { + CHPP_LOGE("RX dupe req t=%" PRIu64, + inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC); + } + inReqState->requestTimeNs = chppGetCurrentTimeNs(); + inReqState->responseTimeNs = CHPP_TIME_NONE; + inReqState->transaction = requestHeader->transaction; +} + +void chppTimestampOutgoingRequest(struct ChppAppState *appState, + struct ChppOutgoingRequestState *outReqState, + const struct ChppAppHeader *requestHeader, + uint64_t timeoutNs) { + CHPP_DEBUG_NOT_NULL(appState); + CHPP_DEBUG_NOT_NULL(outReqState); + CHPP_DEBUG_NOT_NULL(requestHeader); + enum ChppMessageType msgType = requestHeader->type; + enum ChppEndpointType endpointType = + msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? CHPP_ENDPOINT_CLIENT + : CHPP_ENDPOINT_SERVICE; + + CHPP_ASSERT(msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST || + msgType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST); + + if (outReqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) { + CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64, + requestHeader->transaction, outReqState->transaction, + outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC); + + // Clear a possible pending timeout from the previous request + outReqState->responseTimeNs = CHPP_TIME_MAX; + chppRecalculateNextTimeout(appState, endpointType); + } + + outReqState->requestTimeNs = chppGetCurrentTimeNs(); + outReqState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT; + outReqState->transaction = requestHeader->transaction; + + uint64_t *nextRequestTimeoutNs = + getNextRequestTimeoutNs(appState, endpointType); + + if (timeoutNs == CHPP_REQUEST_TIMEOUT_INFINITE) { + outReqState->responseTimeNs = CHPP_TIME_MAX; + + } else { + outReqState->responseTimeNs = timeoutNs + outReqState->requestTimeNs; + + *nextRequestTimeoutNs = + MIN(*nextRequestTimeoutNs, outReqState->responseTimeNs); + } + + CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64 + " (requested=%" PRIu64 "), next timeout=%" PRIu64, + outReqState->transaction, + outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC, + outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC, + timeoutNs / CHPP_NSEC_PER_MSEC, + *nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC); +} + +bool chppTimestampIncomingResponse(struct ChppAppState *appState, + struct ChppOutgoingRequestState *outReqState, + const struct ChppAppHeader *responseHeader) { + CHPP_DEBUG_NOT_NULL(appState); + CHPP_DEBUG_NOT_NULL(outReqState); + CHPP_DEBUG_NOT_NULL(responseHeader); + + uint8_t type = responseHeader->type; + + CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE || + type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE); + + bool success = false; + uint64_t responseTime = chppGetCurrentTimeNs(); + + switch (outReqState->requestState) { + case CHPP_REQUEST_STATE_NONE: { + CHPP_LOGE("Resp with no req t=%" PRIu64, + responseTime / CHPP_NSEC_PER_MSEC); + break; + } + + case CHPP_REQUEST_STATE_RESPONSE_RCV: { + CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64, + responseTime / CHPP_NSEC_PER_MSEC, + outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC); + break; + } + + case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: { + CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64, + responseTime / CHPP_NSEC_PER_MSEC, + outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC); + break; + } + + case CHPP_REQUEST_STATE_REQUEST_SENT: { + if (responseHeader->transaction != outReqState->transaction) { + CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64 + " expected=%" PRIu8, + responseHeader->transaction, + responseTime / CHPP_NSEC_PER_MSEC, outReqState->transaction); + } else { + outReqState->requestState = (responseTime > outReqState->responseTimeNs) + ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT + : CHPP_REQUEST_STATE_RESPONSE_RCV; + success = true; + + CHPP_LOGD( + "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64 + " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)", + outReqState->transaction, + outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC, + responseTime / CHPP_NSEC_PER_MSEC, + outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC, + (responseTime - outReqState->requestTimeNs) / CHPP_NSEC_PER_MSEC, + (responseTime > outReqState->responseTimeNs) ? "yes" : "no"); + } + break; + } + + default: { + CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state"); + } + } + + if (success) { + // When the received request is the next one that was expected + // to timeout we need to recompute the timeout considering the + // other pending requests. + enum ChppEndpointType endpointType = + type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ? CHPP_ENDPOINT_CLIENT + : CHPP_ENDPOINT_SERVICE; + if (outReqState->responseTimeNs == + *getNextRequestTimeoutNs(appState, endpointType)) { + chppRecalculateNextTimeout(appState, endpointType); + } + outReqState->responseTimeNs = responseTime; + } + return success; +} + +uint64_t chppTimestampOutgoingResponse( + struct ChppIncomingRequestState *inReqState) { + CHPP_DEBUG_NOT_NULL(inReqState); + + uint64_t previousResponseTime = inReqState->responseTimeNs; + inReqState->responseTimeNs = chppGetCurrentTimeNs(); + return previousResponseTime; +} + +bool chppSendTimestampedResponseOrFail( + struct ChppAppState *appState, struct ChppIncomingRequestState *inReqState, + void *buf, size_t len) { + CHPP_DEBUG_NOT_NULL(appState); + CHPP_DEBUG_NOT_NULL(inReqState); + CHPP_DEBUG_NOT_NULL(buf); + uint64_t previousResponseTime = chppTimestampOutgoingResponse(inReqState); + + if (inReqState->requestTimeNs == CHPP_TIME_NONE) { + CHPP_LOGE("TX response w/ no req t=%" PRIu64, + inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC); + + } else if (previousResponseTime != CHPP_TIME_NONE) { + CHPP_LOGW("TX additional response t=%" PRIu64 " for req t=%" PRIu64, + inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC, + inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC); + + } else { + CHPP_LOGD("Sending initial response at t=%" PRIu64 + " for request at t=%" PRIu64 " (RTT=%" PRIu64 ")", + inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC, + inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC, + (inReqState->responseTimeNs - inReqState->requestTimeNs) / + CHPP_NSEC_PER_MSEC); + } + + return chppEnqueueTxDatagramOrFail(appState->transportContext, buf, len); +} + +bool chppSendTimestampedRequestOrFail( + struct ChppEndpointState *endpointState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs) { + CHPP_DEBUG_NOT_NULL(outReqState); + CHPP_DEBUG_NOT_NULL(buf); + CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); + + if (timeoutNs < CHPP_TRANSPORT_TX_TIMEOUT_NS) { + // The app layer sits above the transport layer. + // Request timeout (app layer) should be longer than the transport timeout. + CHPP_LOGW("Request timeout (%" PRIu64 + "ns) should be longer than the transport timeout (%" PRIu64 "ns)", + timeoutNs, (uint64_t)CHPP_TRANSPORT_TX_TIMEOUT_NS); + } + + chppTimestampOutgoingRequest(endpointState->appContext, outReqState, buf, + timeoutNs); + endpointState->syncResponse.ready = false; + + bool success = chppEnqueueTxDatagramOrFail( + endpointState->appContext->transportContext, buf, len); + + // Failure to enqueue a TX datagram means that a request was known to be not + // transmitted. We explicitly set requestState to be in the NONE state, so + // that unintended app layer timeouts do not occur. + if (!success) { + outReqState->requestState = CHPP_REQUEST_STATE_NONE; + } + + return success; +} + +bool chppWaitForResponseWithTimeout( + struct ChppSyncResponse *syncResponse, + struct ChppOutgoingRequestState *outReqState, uint64_t timeoutNs) { + CHPP_DEBUG_NOT_NULL(syncResponse); + CHPP_DEBUG_NOT_NULL(outReqState); + + bool result = true; + + chppMutexLock(&syncResponse->mutex); + + while (result && !syncResponse->ready) { + result = chppConditionVariableTimedWait(&syncResponse->condVar, + &syncResponse->mutex, timeoutNs); + } + if (!syncResponse->ready) { + outReqState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT; + CHPP_LOGE("Response timeout after %" PRIu64 " ms", + timeoutNs / CHPP_NSEC_PER_MSEC); + result = false; + } + + chppMutexUnlock(&syncResponse->mutex); + + return result; +} + +struct ChppEndpointState *getRegisteredEndpointState( + struct ChppAppState *appState, uint8_t index, enum ChppEndpointType type) { + CHPP_DEBUG_NOT_NULL(appState); + CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type)); + + return type == CHPP_ENDPOINT_CLIENT + ? appState->registeredClientStates[index] + : appState->registeredServiceStates[index]; +} + +uint16_t getRegisteredEndpointOutReqCount(struct ChppAppState *appState, + uint8_t index, + enum ChppEndpointType type) { + CHPP_DEBUG_NOT_NULL(appState); + CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type)); + + return type == CHPP_ENDPOINT_CLIENT + ? appState->registeredClients[index]->outReqCount + : appState->registeredServices[index]->outReqCount; +} + +uint8_t getRegisteredEndpointCount(struct ChppAppState *appState, + enum ChppEndpointType type) { + return type == CHPP_ENDPOINT_CLIENT ? appState->registeredClientCount + : appState->registeredServiceCount; +} + +void chppRecalculateNextTimeout(struct ChppAppState *appState, + enum ChppEndpointType type) { + CHPP_DEBUG_NOT_NULL(appState); + + uint64_t timeoutNs = CHPP_TIME_MAX; + + const uint8_t endpointCount = getRegisteredEndpointCount(appState, type); + + for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) { + uint16_t reqCount = + getRegisteredEndpointOutReqCount(appState, endpointIdx, type); + struct ChppEndpointState *endpointState = + getRegisteredEndpointState(appState, endpointIdx, type); + struct ChppOutgoingRequestState *reqStates = endpointState->outReqStates; + for (uint16_t cmdIdx = 0; cmdIdx < reqCount; cmdIdx++) { + struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx]; + + if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) { + timeoutNs = MIN(timeoutNs, reqState->responseTimeNs); + } + } + } + + CHPP_LOGD("nextReqTimeout=%" PRIu64, timeoutNs / CHPP_NSEC_PER_MSEC); + + if (type == CHPP_ENDPOINT_CLIENT) { + appState->nextClientRequestTimeoutNs = timeoutNs; + } else { + appState->nextServiceRequestTimeoutNs = timeoutNs; + } +} + +uint64_t *getNextRequestTimeoutNs(struct ChppAppState *appState, + enum ChppEndpointType type) { + return type == CHPP_ENDPOINT_CLIENT ? &appState->nextClientRequestTimeoutNs + : &appState->nextServiceRequestTimeoutNs; +} + +void chppCloseOpenRequests(struct ChppEndpointState *endpointState, + enum ChppEndpointType type, bool clearOnly) { + CHPP_DEBUG_NOT_NULL(endpointState); + + bool recalcNeeded = false; + + struct ChppAppState *appState = endpointState->appContext; + const uint8_t enpointIdx = endpointState->index; + const uint16_t cmdCount = + getRegisteredEndpointOutReqCount(appState, enpointIdx, type); + + for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) { + if (endpointState->outReqStates[cmdIdx].requestState == + CHPP_REQUEST_STATE_REQUEST_SENT) { + recalcNeeded = true; + + CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly); + + if (clearOnly) { + endpointState->outReqStates[cmdIdx].requestState = + CHPP_REQUEST_STATE_RESPONSE_TIMEOUT; + } else { + struct ChppAppHeader *response = + chppMalloc(sizeof(struct ChppAppHeader)); + if (response == NULL) { + CHPP_LOG_OOM(); + } else { + // Simulate receiving a timeout response. + response->handle = endpointState->handle; + response->type = type == CHPP_ENDPOINT_CLIENT + ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE + : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE; + response->transaction = + endpointState->outReqStates[cmdIdx].transaction; + response->error = CHPP_APP_ERROR_TIMEOUT; + response->command = cmdIdx; + + chppAppProcessRxDatagram(appState, (uint8_t *)response, + sizeof(struct ChppAppHeader)); + } + } + } + } + if (recalcNeeded) { + chppRecalculateNextTimeout(appState, type); + } +}
\ No newline at end of file diff --git a/chpp/clients.c b/chpp/clients.c index 198e32b2..4c3df54e 100644 --- a/chpp/clients.c +++ b/chpp/clients.c @@ -50,8 +50,8 @@ * Prototypes ***********************************************/ -static bool chppIsClientApiReady(struct ChppClientState *clientState); -ChppClientDeinitFunction *chppGetClientDeinitFunction( +static bool chppIsClientApiReady(struct ChppEndpointState *clientState); +static ChppClientDeinitFunction *chppGetClientDeinitFunction( struct ChppAppState *context, uint8_t index); /************************************************ @@ -65,9 +65,11 @@ ChppClientDeinitFunction *chppGetClientDeinitFunction( * * @param clientState State of the client sending the client request. * - * @return Indicates whetherthe client is ready. + * @return Indicates whether the client is ready. */ -static bool chppIsClientApiReady(struct ChppClientState *clientState) { +static bool chppIsClientApiReady(struct ChppEndpointState *clientState) { + CHPP_DEBUG_NOT_NULL(clientState); + bool result = false; if (clientState->initialized) { @@ -109,8 +111,10 @@ static bool chppIsClientApiReady(struct ChppClientState *clientState) { * * @return Pointer to the match notification function. */ -ChppClientDeinitFunction *chppGetClientDeinitFunction( +static ChppClientDeinitFunction *chppGetClientDeinitFunction( struct ChppAppState *context, uint8_t index) { + CHPP_DEBUG_NOT_NULL(context); + return context->registeredClients[index]->deinitFunctionPtr; } @@ -120,6 +124,8 @@ ChppClientDeinitFunction *chppGetClientDeinitFunction( void chppRegisterCommonClients(struct ChppAppState *context) { UNUSED_VAR(context); + CHPP_DEBUG_NOT_NULL(context); + CHPP_LOGD("Registering Clients"); #ifdef CHPP_CLIENT_ENABLED_WWAN @@ -143,6 +149,8 @@ void chppRegisterCommonClients(struct ChppAppState *context) { void chppDeregisterCommonClients(struct ChppAppState *context) { UNUSED_VAR(context); + CHPP_DEBUG_NOT_NULL(context); + CHPP_LOGD("Deregistering Clients"); #ifdef CHPP_CLIENT_ENABLED_WWAN @@ -165,10 +173,14 @@ void chppDeregisterCommonClients(struct ChppAppState *context) { } void chppRegisterClient(struct ChppAppState *appContext, void *clientContext, - struct ChppClientState *clientState, - struct ChppRequestResponseState *rRStates, + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqStates, const struct ChppClient *newClient) { CHPP_NOT_NULL(newClient); + CHPP_DEBUG_NOT_NULL(appContext); + CHPP_DEBUG_NOT_NULL(clientContext); + CHPP_DEBUG_NOT_NULL(clientState); + CHPP_DEBUG_NOT_NULL(newClient); if (appContext->registeredClientCount >= CHPP_MAX_REGISTERED_CLIENTS) { CHPP_LOGE("Max clients registered: %" PRIu8, @@ -176,13 +188,12 @@ void chppRegisterClient(struct ChppAppState *appContext, void *clientContext, return; } clientState->appContext = appContext; - clientState->rRStates = rRStates; + clientState->outReqStates = outReqStates; clientState->index = appContext->registeredClientCount; - - appContext->registeredClientContexts[appContext->registeredClientCount] = - clientContext; + clientState->context = clientContext; appContext->registeredClientStates[appContext->registeredClientCount] = clientState; + appContext->registeredClients[appContext->registeredClientCount] = newClient; char uuidText[CHPP_SERVICE_UUID_STRING_LEN]; @@ -199,6 +210,8 @@ void chppRegisterClient(struct ChppAppState *appContext, void *clientContext, void chppInitBasicClients(struct ChppAppState *context) { UNUSED_VAR(context); + CHPP_DEBUG_NOT_NULL(context); + CHPP_LOGD("Initializing basic clients"); #ifdef CHPP_CLIENT_ENABLED_LOOPBACK @@ -216,21 +229,23 @@ void chppInitBasicClients(struct ChppAppState *context) { #endif } -void chppClientInit(struct ChppClientState *clientState, uint8_t handle) { +void chppClientInit(struct ChppEndpointState *clientState, uint8_t handle) { + CHPP_DEBUG_NOT_NULL(clientState); CHPP_ASSERT_LOG(!clientState->initialized, "Client H#%" PRIu8 " already initialized", handle); if (!clientState->everInitialized) { clientState->handle = handle; - chppMutexInit(&clientState->responseMutex); - chppConditionVariableInit(&clientState->responseCondVar); + chppMutexInit(&clientState->syncResponse.mutex); + chppConditionVariableInit(&clientState->syncResponse.condVar); clientState->everInitialized = true; } clientState->initialized = true; } -void chppClientDeinit(struct ChppClientState *clientState) { +void chppClientDeinit(struct ChppEndpointState *clientState) { + CHPP_DEBUG_NOT_NULL(clientState); CHPP_ASSERT_LOG(clientState->initialized, "Client H#%" PRIu8 " already deinitialized", clientState->handle); @@ -240,6 +255,8 @@ void chppClientDeinit(struct ChppClientState *clientState) { void chppDeinitBasicClients(struct ChppAppState *context) { UNUSED_VAR(context); + CHPP_DEBUG_NOT_NULL(context); + CHPP_LOGD("Deinitializing basic clients"); #ifdef CHPP_CLIENT_ENABLED_LOOPBACK @@ -258,6 +275,7 @@ void chppDeinitBasicClients(struct ChppAppState *context) { } void chppDeinitMatchedClients(struct ChppAppState *context) { + CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Deinitializing matched clients"); for (uint8_t i = 0; i < context->discoveredServiceCount; i++) { @@ -272,211 +290,79 @@ void chppDeinitMatchedClients(struct ChppAppState *context) { (clientDeinitFunction != NULL)); if (clientDeinitFunction != NULL) { - clientDeinitFunction(context->registeredClientContexts[clientIndex]); + clientDeinitFunction( + context->registeredClientStates[clientIndex]->context); } } } } struct ChppAppHeader *chppAllocClientRequest( - struct ChppClientState *clientState, size_t len) { - CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); - - struct ChppAppHeader *result = chppMalloc(len); - if (result != NULL) { - result->handle = clientState->handle; - result->type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; - result->transaction = clientState->transaction; - result->error = CHPP_APP_ERROR_NONE; - result->command = CHPP_APP_COMMAND_NONE; - - clientState->transaction++; - } - return result; + struct ChppEndpointState *clientState, size_t len) { + CHPP_DEBUG_NOT_NULL(clientState); + return chppAllocRequest(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, clientState, len); } struct ChppAppHeader *chppAllocClientRequestCommand( - struct ChppClientState *clientState, uint16_t command) { - struct ChppAppHeader *result = + struct ChppEndpointState *clientState, uint16_t command) { + struct ChppAppHeader *request = chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader)); - if (result != NULL) { - result->command = command; + if (request != NULL) { + request->command = command; } - return result; + return request; } -void chppClientTimestampRequest(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - struct ChppAppHeader *requestHeader, - uint64_t timeoutNs) { - if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) { - CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64, - requestHeader->transaction, rRState->transaction, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC); - - // Clear a possible pending timeout from the previous request - rRState->responseTimeNs = CHPP_TIME_MAX; - chppClientRecalculateNextTimeout(clientState->appContext); - } - - rRState->requestTimeNs = chppGetCurrentTimeNs(); - rRState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT; - rRState->transaction = requestHeader->transaction; - - if (timeoutNs == CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE) { - rRState->responseTimeNs = CHPP_TIME_MAX; - - } else { - rRState->responseTimeNs = timeoutNs + rRState->requestTimeNs; - - clientState->appContext->nextRequestTimeoutNs = MIN( - clientState->appContext->nextRequestTimeoutNs, rRState->responseTimeNs); - } - - CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64 - " (requested=%" PRIu64 "), next timeout=%" PRIu64, - rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC, - rRState->responseTimeNs / CHPP_NSEC_PER_MSEC, - timeoutNs / CHPP_NSEC_PER_MSEC, - clientState->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC); -} - -bool chppClientTimestampResponse(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - const struct ChppAppHeader *responseHeader) { - bool success = false; - uint64_t responseTime = chppGetCurrentTimeNs(); - - switch (rRState->requestState) { - case CHPP_REQUEST_STATE_NONE: { - CHPP_LOGE("Resp with no req t=%" PRIu64, - responseTime / CHPP_NSEC_PER_MSEC); - break; - } - - case CHPP_REQUEST_STATE_RESPONSE_RCV: { - CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64, - responseTime / CHPP_NSEC_PER_MSEC, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC); - break; - } - - case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: { - CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64, - responseTime / CHPP_NSEC_PER_MSEC, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC); - break; - } - - case CHPP_REQUEST_STATE_REQUEST_SENT: { - if (responseHeader->transaction != rRState->transaction) { - CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64 - " expected=%" PRIu8, - responseHeader->transaction, - responseTime / CHPP_NSEC_PER_MSEC, rRState->transaction); - } else { - rRState->requestState = (responseTime > rRState->responseTimeNs) - ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT - : CHPP_REQUEST_STATE_RESPONSE_RCV; - success = true; - - CHPP_LOGD( - "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64 - " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)", - rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC, - responseTime / CHPP_NSEC_PER_MSEC, - rRState->responseTimeNs / CHPP_NSEC_PER_MSEC, - (responseTime - rRState->requestTimeNs) / CHPP_NSEC_PER_MSEC, - (responseTime > rRState->responseTimeNs) ? "yes" : "no"); - } - break; - } - - default: { - CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state"); - } - } - - if (success) { - if (rRState->responseTimeNs == - clientState->appContext->nextRequestTimeoutNs) { - // This was the next upcoming timeout - chppClientRecalculateNextTimeout(clientState->appContext); - } - rRState->responseTimeNs = responseTime; - } - return success; -} +bool chppClientSendTimestampedRequestOrFail( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs) { + CHPP_DEBUG_NOT_NULL(clientState); + CHPP_DEBUG_NOT_NULL(outReqState); + CHPP_DEBUG_NOT_NULL(buf); -bool chppSendTimestampedRequestOrFail(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len, - uint64_t timeoutNs) { - CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); if (!chppIsClientApiReady(clientState)) { CHPP_FREE_AND_NULLIFY(buf); return false; } - chppClientTimestampRequest(clientState, rRState, buf, timeoutNs); - clientState->responseReady = false; - - bool success = chppEnqueueTxDatagramOrFail( - clientState->appContext->transportContext, buf, len); - - // Failure to enqueue a TX datagram means that a request was known to be not - // transmitted. We explicitly set requestState to be in the NONE state, so - // that unintended app layer timeouts do not occur. - if (!success) { - rRState->requestState = CHPP_REQUEST_STATE_NONE; - } - - return success; + return chppSendTimestampedRequestOrFail(clientState, outReqState, buf, len, + timeoutNs); } -bool chppSendTimestampedRequestAndWait(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len) { - return chppSendTimestampedRequestAndWaitTimeout( - clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT); +bool chppClientSendTimestampedRequestAndWait( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len) { + return chppClientSendTimestampedRequestAndWaitTimeout( + clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_DEFAULT); } -bool chppSendTimestampedRequestAndWaitTimeout( - struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, void *buf, size_t len, +bool chppClientSendTimestampedRequestAndWaitTimeout( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, uint64_t timeoutNs) { - bool result = chppSendTimestampedRequestOrFail( - clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE); - - if (result) { - chppMutexLock(&clientState->responseMutex); - - while (result && !clientState->responseReady) { - result = chppConditionVariableTimedWait(&clientState->responseCondVar, - &clientState->responseMutex, - timeoutNs); - } - if (!clientState->responseReady) { - rRState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT; - CHPP_LOGE("Response timeout after %" PRIu64 " ms", - timeoutNs / CHPP_NSEC_PER_MSEC); - result = false; - } + bool result = chppClientSendTimestampedRequestOrFail( + clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_INFINITE); - chppMutexUnlock(&clientState->responseMutex); + if (!result) { + return false; } - return result; + return chppWaitForResponseWithTimeout(&clientState->syncResponse, outReqState, + timeoutNs); } -void chppClientPseudoOpen(struct ChppClientState *clientState) { +void chppClientPseudoOpen(struct ChppEndpointState *clientState) { clientState->pseudoOpen = true; } -bool chppClientSendOpenRequest(struct ChppClientState *clientState, - struct ChppRequestResponseState *openRRState, +bool chppClientSendOpenRequest(struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *openReqState, uint16_t openCommand, bool blocking) { + CHPP_NOT_NULL(clientState); + CHPP_NOT_NULL(openReqState); + bool result = false; uint8_t priorState = clientState->openState; @@ -488,7 +374,6 @@ bool chppClientSendOpenRequest(struct ChppClientState *clientState, chppAllocClientRequestCommand(clientState, openCommand); if (request == NULL) { - CHPP_LOG_OOM(); return false; } @@ -496,13 +381,13 @@ bool chppClientSendOpenRequest(struct ChppClientState *clientState, if (blocking) { CHPP_LOGD("Opening service - blocking"); - result = chppSendTimestampedRequestAndWait(clientState, openRRState, - request, sizeof(*request)); + result = chppClientSendTimestampedRequestAndWait(clientState, openReqState, + request, sizeof(*request)); } else { CHPP_LOGD("Opening service - non-blocking"); - result = chppSendTimestampedRequestOrFail( - clientState, openRRState, request, sizeof(*request), - CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE); + result = chppClientSendTimestampedRequestOrFail( + clientState, openReqState, request, sizeof(*request), + CHPP_REQUEST_TIMEOUT_INFINITE); } if (!result) { @@ -517,8 +402,11 @@ bool chppClientSendOpenRequest(struct ChppClientState *clientState, return result; } -void chppClientProcessOpenResponse(struct ChppClientState *clientState, +void chppClientProcessOpenResponse(struct ChppEndpointState *clientState, uint8_t *buf, size_t len) { + CHPP_DEBUG_NOT_NULL(clientState); + CHPP_DEBUG_NOT_NULL(buf); + UNUSED_VAR(len); // Necessary depending on assert macro below // Assert condition already guaranteed by chppAppProcessRxDatagram() but // checking again since this is a public function @@ -534,63 +422,13 @@ void chppClientProcessOpenResponse(struct ChppClientState *clientState, } } -void chppClientRecalculateNextTimeout(struct ChppAppState *context) { - context->nextRequestTimeoutNs = CHPP_TIME_MAX; - - for (uint8_t clientIdx = 0; clientIdx < context->registeredClientCount; - clientIdx++) { - const struct ChppClient *client = context->registeredClients[clientIdx]; - for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) { - const struct ChppClientState *state = - context->registeredClientStates[clientIdx]; - struct ChppRequestResponseState *rRState = &state->rRStates[cmdIdx]; - - if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) { - context->nextRequestTimeoutNs = - MIN(context->nextRequestTimeoutNs, rRState->responseTimeNs); - } - } - } - - CHPP_LOGD("nextReqTimeout=%" PRIu64, - context->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC); -} - -void chppClientCloseOpenRequests(struct ChppClientState *clientState, +void chppClientCloseOpenRequests(struct ChppEndpointState *clientState, const struct ChppClient *client, bool clearOnly) { - bool recalcNeeded = false; - - for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) { - if (clientState->rRStates[cmdIdx].requestState == - CHPP_REQUEST_STATE_REQUEST_SENT) { - recalcNeeded = true; - - CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly); - - if (clearOnly) { - clientState->rRStates[cmdIdx].requestState = - CHPP_REQUEST_STATE_RESPONSE_TIMEOUT; - } else { - struct ChppAppHeader *response = - chppMalloc(sizeof(struct ChppAppHeader)); - if (response == NULL) { - CHPP_LOG_OOM(); - } else { - response->handle = clientState->handle; - response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE; - response->transaction = clientState->rRStates[cmdIdx].transaction; - response->error = CHPP_APP_ERROR_TIMEOUT; - response->command = cmdIdx; - - chppAppProcessRxDatagram(clientState->appContext, (uint8_t *)response, - sizeof(struct ChppAppHeader)); - } - } - } - } - - if (recalcNeeded) { - chppClientRecalculateNextTimeout(clientState->appContext); - } + UNUSED_VAR(client); + chppCloseOpenRequests(clientState, CHPP_ENDPOINT_CLIENT, clearOnly); } + +struct ChppAppHeader *chppAllocClientNotification(size_t len) { + return chppAllocNotification(CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION, len); +}
\ No newline at end of file diff --git a/chpp/clients/discovery.c b/chpp/clients/discovery.c index e6817521..af314e2b 100644 --- a/chpp/clients/discovery.c +++ b/chpp/clients/discovery.c @@ -22,6 +22,7 @@ #include <string.h> #include "chpp/app.h" +#include "chpp/clients.h" #include "chpp/common/discovery.h" #include "chpp/log.h" #include "chpp/macros.h" @@ -160,7 +161,7 @@ static void chppProcessDiscoverAllResponse( // Initialize client if (!client->initFunctionPtr( - appState->registeredClientContexts[clientIndex], + appState->registeredClientStates[clientIndex]->context, CHPP_SERVICE_HANDLE_OF_INDEX(i), service->version)) { CHPP_LOGE("Client v=%" PRIu8 ".%" PRIu8 ".%" PRIu16 " rejected init. Service v=%" PRIu8 ".%" PRIu8 ".%" PRIu16, @@ -199,7 +200,8 @@ static void chppProcessDiscoverAllResponse( (matchNotifierFunction != NULL)); if (matchNotifierFunction != NULL) { - matchNotifierFunction(appState->registeredClientContexts[clientIndex]); + matchNotifierFunction( + appState->registeredClientStates[clientIndex]->context); } } } diff --git a/chpp/clients/gnss.c b/chpp/clients/gnss.c index ca131d7e..f56ce01b 100644 --- a/chpp/clients/gnss.c +++ b/chpp/clients/gnss.c @@ -62,10 +62,11 @@ static void chppGnssClientNotifyMatch(void *clientContext); * (RR) functionality. */ struct ChppGnssClientState { - struct ChppClientState client; // GNSS client state + struct ChppEndpointState client; // CHPP client state const struct chrePalGnssApi *api; // GNSS PAL API - struct ChppRequestResponseState rRState[CHPP_GNSS_CLIENT_REQUEST_MAX + 1]; + struct ChppOutgoingRequestState + outReqStates[CHPP_GNSS_CLIENT_REQUEST_MAX + 1]; uint32_t capabilities; // Cached GetCapabilities result bool requestStateResyncPending; // requestStateResync() is waiting to be @@ -109,8 +110,8 @@ static const struct ChppClient kGnssClientConfig = { // Service notification dispatch function pointer .deinitFunctionPtr = &chppGnssClientDeinit, - // Number of request-response states in the rRStates array. - .rRStateCount = ARRAY_SIZE(gGnssClientContext.rRState), + // Number of request-response states in the outReqStates array. + .outReqCount = ARRAY_SIZE(gGnssClientContext.outReqStates), // Min length is the entire header .minLength = sizeof(struct ChppAppHeader), @@ -181,9 +182,10 @@ static enum ChppAppErrorCode chppDispatchGnssResponse(void *clientContext, if (rxHeader->command > CHPP_GNSS_CLIENT_REQUEST_MAX) { error = CHPP_APP_ERROR_INVALID_COMMAND; - } else if (!chppClientTimestampResponse( - &gnssClientContext->client, - &gnssClientContext->rRState[rxHeader->command], rxHeader)) { + } else if (!chppTimestampIncomingResponse( + gnssClientContext->client.appContext, + &gnssClientContext->outReqStates[rxHeader->command], + rxHeader)) { error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE; } else { @@ -332,7 +334,7 @@ static void chppGnssClientNotifyReset(void *clientContext) { gnssClientContext->client.openState); gnssClientContext->requestStateResyncPending = true; chppClientSendOpenRequest(&gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_OPEN], + &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN], CHPP_GNSS_OPEN, /*blocking=*/false); } @@ -350,7 +352,7 @@ static void chppGnssClientNotifyMatch(void *clientContext) { if (gnssClientContext->client.pseudoOpen) { CHPP_LOGD("Pseudo-open GNSS client opening"); chppClientSendOpenRequest(&gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_OPEN], + &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN], CHPP_GNSS_OPEN, /*blocking=*/false); } @@ -589,8 +591,8 @@ static void chppGnssMeasurementResultNotification( */ static bool chppGnssClientOpen(const struct chrePalSystemApi *systemApi, const struct chrePalGnssCallbacks *callbacks) { - CHPP_DEBUG_ASSERT(systemApi != NULL); - CHPP_DEBUG_ASSERT(callbacks != NULL); + CHPP_DEBUG_NOT_NULL(systemApi); + CHPP_DEBUG_NOT_NULL(callbacks); bool result = false; gSystemApi = systemApi; @@ -604,7 +606,7 @@ static bool chppGnssClientOpen(const struct chrePalSystemApi *systemApi, CHPP_GNSS_DISCOVERY_TIMEOUT_MS)) { result = chppClientSendOpenRequest( &gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_OPEN], CHPP_GNSS_OPEN, + &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN], CHPP_GNSS_OPEN, /*blocking=*/true); } @@ -627,9 +629,9 @@ static void chppGnssClientClose(void) { if (request == NULL) { CHPP_LOG_OOM(); - } else if (chppSendTimestampedRequestAndWait( + } else if (chppClientSendTimestampedRequestAndWait( &gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_CLOSE], request, + &gGnssClientContext.outReqStates[CHPP_GNSS_CLOSE], request, sizeof(*request))) { gGnssClientContext.client.openState = CHPP_OPEN_STATE_CLOSED; gGnssClientContext.capabilities = CHRE_GNSS_CAPABILITIES_NONE; @@ -659,10 +661,10 @@ static uint32_t chppGnssClientGetCapabilities(void) { if (request == NULL) { CHPP_LOG_OOM(); } else { - if (chppSendTimestampedRequestAndWait( + if (chppClientSendTimestampedRequestAndWait( &gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_GET_CAPABILITIES], request, - sizeof(*request))) { + &gGnssClientContext.outReqStates[CHPP_GNSS_GET_CAPABILITIES], + request, sizeof(*request))) { // Success. gGnssClientContext.capabilities is now populated if (gGnssClientContext.capabilitiesValid) { capabilities = gGnssClientContext.capabilities; @@ -703,9 +705,9 @@ static bool chppGnssClientControlLocationSession(bool enable, request->params.minIntervalMs = minIntervalMs; request->params.minTimeToNextFixMs = minTimeToNextFixMs; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_CONTROL_LOCATION_SESSION], + &gGnssClientContext.outReqStates[CHPP_GNSS_CONTROL_LOCATION_SESSION], request, sizeof(*request), CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS); } @@ -749,9 +751,9 @@ static bool chppGnssClientControlMeasurementSession(bool enable, request->params.enable = enable; request->params.minIntervalMs = minIntervalMs; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gGnssClientContext.client, - &gGnssClientContext.rRState[CHPP_GNSS_CONTROL_MEASUREMENT_SESSION], + &gGnssClientContext.outReqStates[CHPP_GNSS_CONTROL_MEASUREMENT_SESSION], request, sizeof(*request), CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS); } @@ -795,11 +797,11 @@ static bool chppGnssClientConfigurePassiveLocationListener(bool enable) { request->header.command = CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER; request->params.enable = enable; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gGnssClientContext.client, &gGnssClientContext - .rRState[CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER], - request, sizeof(*request), CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT); + .outReqStates[CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER], + request, sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT); } return result; @@ -812,8 +814,8 @@ static bool chppGnssClientConfigurePassiveLocationListener(bool enable) { void chppRegisterGnssClient(struct ChppAppState *appContext) { memset(&gGnssClientContext, 0, sizeof(gGnssClientContext)); chppRegisterClient(appContext, (void *)&gGnssClientContext, - &gGnssClientContext.client, gGnssClientContext.rRState, - &kGnssClientConfig); + &gGnssClientContext.client, + gGnssClientContext.outReqStates, &kGnssClientConfig); } void chppDeregisterGnssClient(struct ChppAppState *appContext) { @@ -822,7 +824,7 @@ void chppDeregisterGnssClient(struct ChppAppState *appContext) { UNUSED_VAR(appContext); } -struct ChppClientState *getChppGnssClientState(void) { +struct ChppEndpointState *getChppGnssClientState(void) { return &gGnssClientContext.client; } diff --git a/chpp/clients/loopback.c b/chpp/clients/loopback.c index b8f19e8f..c92ae68a 100644 --- a/chpp/clients/loopback.c +++ b/chpp/clients/loopback.c @@ -42,8 +42,8 @@ * (RR) functionality. */ struct ChppLoopbackClientState { - struct ChppClientState client; // Loopback client state - struct ChppRequestResponseState runLoopbackTest; // Loopback test state + struct ChppEndpointState client; // CHPP client state + struct ChppOutgoingRequestState runLoopbackTest; // Loopback test state struct ChppLoopbackTestResult testResult; // Last test result const uint8_t *loopbackData; // Pointer to loopback data @@ -88,9 +88,9 @@ bool chppDispatchLoopbackServiceResponse(struct ChppAppState *appState, CHPP_NOT_NULL(state); CHPP_NOT_NULL(state->loopbackData); - CHPP_ASSERT( - chppClientTimestampResponse(&state->client, &state->runLoopbackTest, - (const struct ChppAppHeader *)response)); + CHPP_ASSERT(chppTimestampIncomingResponse( + state->client.appContext, &state->runLoopbackTest, + (const struct ChppAppHeader *)response)); struct ChppLoopbackTestResult *result = &state->testResult; @@ -123,10 +123,10 @@ bool chppDispatchLoopbackServiceResponse(struct ChppAppState *appState, result->firstError, result->byteErrors); // Notify waiting (synchronous) client - chppMutexLock(&state->client.responseMutex); - state->client.responseReady = true; - chppConditionVariableSignal(&state->client.responseCondVar); - chppMutexUnlock(&state->client.responseMutex); + chppMutexLock(&state->client.syncResponse.mutex); + state->client.syncResponse.ready = true; + chppConditionVariableSignal(&state->client.syncResponse.condVar); + chppMutexUnlock(&state->client.syncResponse.mutex); return true; } @@ -187,7 +187,7 @@ struct ChppLoopbackTestResult chppRunLoopbackTest(struct ChppAppState *appState, state->loopbackData = buf; memcpy(&loopbackRequest[CHPP_LOOPBACK_HEADER_LEN], buf, len); - if (!chppSendTimestampedRequestAndWaitTimeout( + if (!chppClientSendTimestampedRequestAndWaitTimeout( &state->client, &state->runLoopbackTest, loopbackRequest, result->requestLen, 5 * CHPP_NSEC_PER_SEC)) { result->error = CHPP_APP_ERROR_UNSPECIFIED; diff --git a/chpp/clients/timesync.c b/chpp/clients/timesync.c index de0664b8..3026f216 100644 --- a/chpp/clients/timesync.c +++ b/chpp/clients/timesync.c @@ -40,8 +40,8 @@ * (RR) functionality. */ struct ChppTimesyncClientState { - struct ChppClientState client; // Timesync client state - struct ChppRequestResponseState measureOffset; // Request response state + struct ChppEndpointState client; // CHPP client state + struct ChppOutgoingRequestState measureOffset; // Request response state struct ChppTimesyncResult timesyncResult; // Result of measureOffset }; @@ -104,8 +104,8 @@ bool chppDispatchTimesyncServiceResponse(struct ChppAppState *appState, const struct ChppTimesyncResponse *response = (const struct ChppTimesyncResponse *)buf; - if (chppClientTimestampResponse(&state->client, &state->measureOffset, - &response->header)) { + if (chppTimestampIncomingResponse(state->client.appContext, + &state->measureOffset, &response->header)) { state->timesyncResult.rttNs = state->measureOffset.responseTimeNs - state->measureOffset.requestTimeNs; int64_t offsetNs = @@ -164,9 +164,9 @@ bool chppTimesyncMeasureOffset(struct ChppAppState *appState) { state->timesyncResult.error = CHPP_APP_ERROR_OOM; CHPP_LOG_OOM(); - } else if (!chppSendTimestampedRequestOrFail( + } else if (!chppClientSendTimestampedRequestOrFail( &state->client, &state->measureOffset, request, requestLen, - CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE)) { + CHPP_REQUEST_TIMEOUT_INFINITE)) { state->timesyncResult.error = CHPP_APP_ERROR_UNSPECIFIED; } else { diff --git a/chpp/clients/wifi.c b/chpp/clients/wifi.c index b5446840..9438a845 100644 --- a/chpp/clients/wifi.c +++ b/chpp/clients/wifi.c @@ -75,10 +75,11 @@ static void chppWifiClientNotifyMatch(void *clientContext); * (RR) functionality. */ struct ChppWifiClientState { - struct ChppClientState client; // WiFi client state + struct ChppEndpointState client; // CHPP client state const struct chrePalWifiApi *api; // WiFi PAL API - struct ChppRequestResponseState rRState[CHPP_WIFI_CLIENT_REQUEST_MAX + 1]; + struct ChppOutgoingRequestState + outReqStates[CHPP_WIFI_CLIENT_REQUEST_MAX + 1]; uint32_t capabilities; // Cached GetCapabilities result bool scanMonitorEnabled; // Scan monitoring is enabled @@ -123,8 +124,8 @@ static const struct ChppClient kWifiClientConfig = { // Service notification dispatch function pointer .deinitFunctionPtr = &chppWifiClientDeinit, - // Number of request-response states in the rRStates array. - .rRStateCount = ARRAY_SIZE(gWifiClientContext.rRState), + // Number of request-response states in the outReqStates array. + .outReqCount = ARRAY_SIZE(gWifiClientContext.outReqStates), // Min length is the entire header .minLength = sizeof(struct ChppAppHeader), @@ -201,9 +202,10 @@ static enum ChppAppErrorCode chppDispatchWifiResponse(void *clientContext, if (rxHeader->command > CHPP_WIFI_CLIENT_REQUEST_MAX) { error = CHPP_APP_ERROR_INVALID_COMMAND; - } else if (!chppClientTimestampResponse( - &wifiClientContext->client, - &wifiClientContext->rRState[rxHeader->command], rxHeader)) { + } else if (!chppTimestampIncomingResponse( + wifiClientContext->client.appContext, + &wifiClientContext->outReqStates[rxHeader->command], + rxHeader)) { error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE; } else { @@ -380,7 +382,7 @@ static void chppWifiClientNotifyReset(void *clientContext) { CHPP_LOGI("WiFi client reopening from state=%" PRIu8, wifiClientContext->client.openState); chppClientSendOpenRequest(&wifiClientContext->client, - &wifiClientContext->rRState[CHPP_WIFI_OPEN], + &wifiClientContext->outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/false); } @@ -398,7 +400,7 @@ static void chppWifiClientNotifyMatch(void *clientContext) { if (wifiClientContext->client.pseudoOpen) { CHPP_LOGD("Pseudo-open WiFi client opening"); chppClientSendOpenRequest(&wifiClientContext->client, - &wifiClientContext->rRState[CHPP_WIFI_OPEN], + &wifiClientContext->outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/false); } @@ -832,8 +834,8 @@ static void chppWifiNanSubscriptionCanceledNotification(uint8_t *buf, */ static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi, const struct chrePalWifiCallbacks *callbacks) { - CHPP_DEBUG_ASSERT(systemApi != NULL); - CHPP_DEBUG_ASSERT(callbacks != NULL); + CHPP_DEBUG_NOT_NULL(systemApi); + CHPP_DEBUG_NOT_NULL(callbacks); bool result = false; gSystemApi = systemApi; @@ -847,7 +849,7 @@ static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi, CHPP_WIFI_DISCOVERY_TIMEOUT_MS)) { result = chppClientSendOpenRequest( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, + &gWifiClientContext.outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/true); } @@ -870,9 +872,9 @@ static void chppWifiClientClose(void) { if (request == NULL) { CHPP_LOG_OOM(); - } else if (chppSendTimestampedRequestAndWait( + } else if (chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_CLOSE], request, + &gWifiClientContext.outReqStates[CHPP_WIFI_CLOSE], request, sizeof(*request))) { gWifiClientContext.client.openState = CHPP_OPEN_STATE_CLOSED; gWifiClientContext.capabilities = CHRE_WIFI_CAPABILITIES_NONE; @@ -902,10 +904,10 @@ static uint32_t chppWifiClientGetCapabilities(void) { if (request == NULL) { CHPP_LOG_OOM(); } else { - if (chppSendTimestampedRequestAndWait( + if (chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_GET_CAPABILITIES], request, - sizeof(*request))) { + &gWifiClientContext.outReqStates[CHPP_WIFI_GET_CAPABILITIES], + request, sizeof(*request))) { // Success. gWifiClientContext.capabilities is now populated if (gWifiClientContext.capabilitiesValid) { capabilities = gWifiClientContext.capabilities; @@ -938,12 +940,14 @@ static bool chppWifiClientConfigureScanMonitor(bool enable) { request->header.command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC; request->params.enable = enable; request->params.cookie = - &gWifiClientContext.rRState[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC]; + &gWifiClientContext + .outReqStates[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC]; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC], - request, sizeof(*request), CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT); + &gWifiClientContext + .outReqStates[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC], + request, sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT); } return result; @@ -976,9 +980,9 @@ static bool chppWifiClientRequestScan(const struct chreWifiScanParams *params) { CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS > CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS, "Chpp wifi scan timeout needs to be smaller than CHRE wifi scan " "timeout"); - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_SCAN_ASYNC], request, + &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_SCAN_ASYNC], request, requestLen, CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS); } @@ -1027,10 +1031,10 @@ static bool chppWifiClientRequestRanging( request->header.error = CHPP_APP_ERROR_NONE; request->header.command = CHPP_WIFI_REQUEST_RANGING_ASYNC; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_RANGING_ASYNC], request, - requestLen, CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); + &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_RANGING_ASYNC], + request, requestLen, CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); } return result; @@ -1075,9 +1079,9 @@ static bool chppWifiClientNanSubscribe( request->header.error = CHPP_APP_ERROR_NONE; request->header.command = CHPP_WIFI_REQUEST_NAN_SUB; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_NAN_SUB], request, + &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_SUB], request, requestLen, CHRE_ASYNC_RESULT_TIMEOUT_NS); } return result; @@ -1106,10 +1110,10 @@ static bool chppWifiClientNanSubscribeCancel(uint32_t subscriptionId) { request->header.error = CHPP_APP_ERROR_NONE; request->subscriptionId = subscriptionId; - result = chppSendTimestampedRequestAndWait( + result = chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_NAN_SUB_CANCEL], request, - sizeof(*request)); + &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_SUB_CANCEL], + request, sizeof(*request)); } return result; } @@ -1152,9 +1156,9 @@ static bool chppWifiClientNanRequestNanRanging( request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, - &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC], + &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC], request, requestLen, CHRE_ASYNC_RESULT_TIMEOUT_NS); } return result; @@ -1174,8 +1178,8 @@ static bool chppWifiGetNanCapabilites( void chppRegisterWifiClient(struct ChppAppState *appContext) { memset(&gWifiClientContext, 0, sizeof(gWifiClientContext)); chppRegisterClient(appContext, (void *)&gWifiClientContext, - &gWifiClientContext.client, gWifiClientContext.rRState, - &kWifiClientConfig); + &gWifiClientContext.client, + gWifiClientContext.outReqStates, &kWifiClientConfig); } void chppDeregisterWifiClient(struct ChppAppState *appContext) { @@ -1184,7 +1188,7 @@ void chppDeregisterWifiClient(struct ChppAppState *appContext) { UNUSED_VAR(appContext); } -struct ChppClientState *getChppWifiClientState(void) { +struct ChppEndpointState *getChppWifiClientState(void) { return &gWifiClientContext.client; } diff --git a/chpp/clients/wwan.c b/chpp/clients/wwan.c index 98aea88c..1ac8aa3f 100644 --- a/chpp/clients/wwan.c +++ b/chpp/clients/wwan.c @@ -64,10 +64,11 @@ static void chppWwanClientNotifyMatch(void *clientContext); * (RR) functionality. */ struct ChppWwanClientState { - struct ChppClientState client; // WWAN client state + struct ChppEndpointState client; // CHPP client state const struct chrePalWwanApi *api; // WWAN PAL API - struct ChppRequestResponseState rRState[CHPP_WWAN_CLIENT_REQUEST_MAX + 1]; + struct ChppOutgoingRequestState + outReqStates[CHPP_WWAN_CLIENT_REQUEST_MAX + 1]; uint32_t capabilities; // Cached GetCapabilities result bool capabilitiesValid; // Flag to indicate if the capabilities result @@ -109,8 +110,8 @@ static const struct ChppClient kWwanClientConfig = { // Service notification dispatch function pointer .deinitFunctionPtr = &chppWwanClientDeinit, - // Number of request-response states in the rRStates array. - .rRStateCount = ARRAY_SIZE(gWwanClientContext.rRState), + // Number of request-response states in the outReqStates array. + .outReqCount = ARRAY_SIZE(gWwanClientContext.outReqStates), // Min length is the entire header .minLength = sizeof(struct ChppAppHeader), @@ -163,9 +164,10 @@ static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext, if (rxHeader->command > CHPP_WWAN_CLIENT_REQUEST_MAX) { error = CHPP_APP_ERROR_INVALID_COMMAND; - } else if (!chppClientTimestampResponse( - &wwanClientContext->client, - &wwanClientContext->rRState[rxHeader->command], rxHeader)) { + } else if (!chppTimestampIncomingResponse( + wwanClientContext->client.appContext, + &wwanClientContext->outReqStates[rxHeader->command], + rxHeader)) { error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE; } else { @@ -252,7 +254,7 @@ static void chppWwanClientNotifyReset(void *clientContext) { CHPP_LOGI("WWAN client reopening from state=%" PRIu8, wwanClientContext->client.openState); chppClientSendOpenRequest(&wwanClientContext->client, - &wwanClientContext->rRState[CHPP_WWAN_OPEN], + &wwanClientContext->outReqStates[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN, /*blocking=*/false); } @@ -270,7 +272,7 @@ static void chppWwanClientNotifyMatch(void *clientContext) { if (wwanClientContext->client.pseudoOpen) { CHPP_LOGD("Pseudo-open WWAN client opening"); chppClientSendOpenRequest(&wwanClientContext->client, - &wwanClientContext->rRState[CHPP_WWAN_OPEN], + &wwanClientContext->outReqStates[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN, /*blocking=*/false); } @@ -400,8 +402,8 @@ static void chppWwanGetCellInfoAsyncResult( */ static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi, const struct chrePalWwanCallbacks *callbacks) { - CHPP_DEBUG_ASSERT(systemApi != NULL); - CHPP_DEBUG_ASSERT(callbacks != NULL); + CHPP_DEBUG_NOT_NULL(systemApi); + CHPP_DEBUG_NOT_NULL(callbacks); bool result = false; gSystemApi = systemApi; @@ -416,7 +418,7 @@ static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi, CHPP_WWAN_DISCOVERY_TIMEOUT_MS)) { result = chppClientSendOpenRequest( &gWwanClientContext.client, - &gWwanClientContext.rRState[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN, + &gWwanClientContext.outReqStates[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN, /*blocking=*/true); } @@ -439,9 +441,9 @@ static void chppWwanClientClose(void) { if (request == NULL) { CHPP_LOG_OOM(); - } else if (chppSendTimestampedRequestAndWait( + } else if (chppClientSendTimestampedRequestAndWait( &gWwanClientContext.client, - &gWwanClientContext.rRState[CHPP_WWAN_CLOSE], request, + &gWwanClientContext.outReqStates[CHPP_WWAN_CLOSE], request, sizeof(*request))) { gWwanClientContext.client.openState = CHPP_OPEN_STATE_CLOSED; gWwanClientContext.capabilities = CHRE_WWAN_CAPABILITIES_NONE; @@ -471,10 +473,10 @@ static uint32_t chppWwanClientGetCapabilities(void) { if (request == NULL) { CHPP_LOG_OOM(); } else { - if (chppSendTimestampedRequestAndWait( + if (chppClientSendTimestampedRequestAndWait( &gWwanClientContext.client, - &gWwanClientContext.rRState[CHPP_WWAN_GET_CAPABILITIES], request, - sizeof(*request))) { + &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CAPABILITIES], + request, sizeof(*request))) { // Success. gWwanClientContext.capabilities is now populated if (gWwanClientContext.capabilitiesValid) { capabilities = gWwanClientContext.capabilities; @@ -502,10 +504,10 @@ static bool chppWwanClientGetCellInfoAsync(void) { if (request == NULL) { CHPP_LOG_OOM(); } else { - result = chppSendTimestampedRequestOrFail( + result = chppClientSendTimestampedRequestOrFail( &gWwanClientContext.client, - &gWwanClientContext.rRState[CHPP_WWAN_GET_CELLINFO_ASYNC], request, - sizeof(*request), CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT); + &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CELLINFO_ASYNC], request, + sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT); } return result; @@ -531,8 +533,8 @@ static void chppWwanClientReleaseCellInfoResult( void chppRegisterWwanClient(struct ChppAppState *appContext) { memset(&gWwanClientContext, 0, sizeof(gWwanClientContext)); chppRegisterClient(appContext, (void *)&gWwanClientContext, - &gWwanClientContext.client, gWwanClientContext.rRState, - &kWwanClientConfig); + &gWwanClientContext.client, + gWwanClientContext.outReqStates, &kWwanClientConfig); } void chppDeregisterWwanClient(struct ChppAppState *appContext) { @@ -541,7 +543,7 @@ void chppDeregisterWwanClient(struct ChppAppState *appContext) { UNUSED_VAR(appContext); } -struct ChppClientState *getChppWwanClientState(void) { +struct ChppEndpointState *getChppWwanClientState(void) { return &gWwanClientContext.client; } diff --git a/chpp/include/chpp/app.h b/chpp/include/chpp/app.h index 67bf54da..eda11fa5 100644 --- a/chpp/include/chpp/app.h +++ b/chpp/include/chpp/app.h @@ -24,6 +24,7 @@ #include "chpp/condition_variable.h" #include "chpp/macros.h" #include "chpp/transport.h" +#include "chre_api/chre/common.h" #ifdef __cplusplus extern "C" { @@ -34,6 +35,32 @@ extern "C" { ***********************************************/ /** + * Allocates a variable-length response message of a specific type. + * + * @param requestHeader request header, as per chppAllocResponse(). + * @param type Type of response which includes an arrayed member. + * @param count number of items in the array of arrayField. + * @param arrayField The arrayed member field. + * + * @return Pointer to allocated memory. + */ +#define chppAllocResponseTypedArray(requestHeader, type, count, arrayField) \ + (type *)chppAllocResponse( \ + requestHeader, \ + sizeof(type) + (count)*sizeof_member(type, arrayField[0])) + +/** + * Allocates a response message of a specific type and its corresponding length. + * + * @param requestHeader request header, as per chppAllocResponse(). + * @param type Type of response. + * + * @return Pointer to allocated memory. + */ +#define chppAllocResponseFixed(requestHeader, type) \ + (type *)chppAllocResponse(requestHeader, sizeof(type)) + +/** * Maximum number of services that can be registered by CHPP (not including * predefined services), if not defined by the build system. */ @@ -58,6 +85,25 @@ extern "C" { MAX(CHPP_MAX_REGISTERED_SERVICES, CHPP_MAX_REGISTERED_CLIENTS) #endif +#define CHPP_REQUEST_TIMEOUT_INFINITE CHPP_TIME_MAX + +#if defined(CHPP_REQUEST_TIMEOUT_DEFAULT) && \ + defined(CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT) +// Build systems should prefer to only set CHPP_REQUEST_TIMEOUT_DEFAULT +#error Can not set both CHPP_REQUEST_TIMEOUT_DEFAULT and CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT +#endif + +// For backwards compatibility with vendor build systems +#ifdef CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT +#define CHPP_REQUEST_TIMEOUT_DEFAULT CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT +#undef CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT +#endif + +// If not customized in the build, we default to CHRE expectations +#ifndef CHPP_REQUEST_TIMEOUT_DEFAULT +#define CHPP_REQUEST_TIMEOUT_DEFAULT CHRE_ASYNC_RESULT_TIMEOUT_NS +#endif + /** * Default value for reserved fields. */ @@ -74,6 +120,14 @@ extern "C" { #define CHPP_APP_COMMAND_NONE 0 /** + * Type of endpoint (either client or service) + */ +enum ChppEndpointType { + CHPP_ENDPOINT_CLIENT = 0, + CHPP_ENDPOINT_SERVICE = 1, +}; + +/** * Handle Numbers in ChppAppHeader */ enum ChppHandleNumber { @@ -114,6 +168,13 @@ enum ChppMessageType { //! Notification from service. Client shall not respond. CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION = 3, + + //! Request from service. Needs response from client. + CHPP_MESSAGE_TYPE_SERVICE_REQUEST = 4, + + //! Response from client (with the same Command and Transaction ID as the + //! service request). + CHPP_MESSAGE_TYPE_CLIENT_RESPONSE = 5, }; /** @@ -189,7 +250,10 @@ struct ChppAppHeader { CHPP_PACKED_END /** - * Function type that dispatches incoming datagrams for any client or service + * Function type that dispatches incoming datagrams for any client or service. + * + * The buffer is freed shortly after the function returns. + * User code must make a copy for later processing if needed. */ typedef enum ChppAppErrorCode(ChppDispatchFunction)(void *context, uint8_t *buf, size_t len); @@ -229,6 +293,17 @@ typedef void(ChppNotifierFunction)(void *context); #define CHPP_SERVICE_NAME_MAX_LEN (15 + 1) /** + * Support for sync response. + * + * @see chppClientSendTimestampedRequestAndWaitTimeout. + */ +struct ChppSyncResponse { + struct ChppMutex mutex; + struct ChppConditionVariable condVar; + bool ready; +}; + +/** * CHPP definition of a service descriptor as sent over the wire. */ CHPP_PACKED_START @@ -252,19 +327,31 @@ struct ChppService { //! Service Descriptor as sent over the wire. struct ChppServiceDescriptor descriptor; - //! Pointer to the function that is used to notify the service if CHPP is - //! reset. + //! Notifies the service if CHPP is reset. ChppNotifierFunction *resetNotifierFunctionPtr; - //! Pointer to the function that dispatches incoming client requests for the - //! service. + //! Dispatches incoming client requests. + //! When an error is returned by the dispatch function it is logged and an + //! error response is automatically sent to the remote endpoint. ChppDispatchFunction *requestDispatchFunctionPtr; - //! Pointer to the function that dispatches incoming client notifications for - //! the service. + //! Dispatches incoming client notifications. + //! Errors returned by the dispatch function are logged. ChppDispatchFunction *notificationDispatchFunctionPtr; - //! Minimum valid length of datagrams for the service. + //! Dispatches incoming client responses. + //! Errors returned by the dispatch function are logged. + ChppDispatchFunction *responseDispatchFunctionPtr; + + //! Number of outgoing requests supported by this service. + //! ChppAppHeader.command must be in the range [0, outReqCount - 1] + //! ChppEndpointState.outReqStates must contains that many elements. + uint16_t outReqCount; + + //! Minimum valid length of datagrams for the service: + //! - client requests + //! - client notifications + //! - client responses size_t minLength; }; @@ -287,38 +374,45 @@ struct ChppClient { //! Client descriptor. struct ChppClientDescriptor descriptor; - //! Pointer to the function that is used to notify the client if CHPP is - //! reset. + //! Notifies the client if CHPP is reset. ChppNotifierFunction *resetNotifierFunctionPtr; - //! Pointer to the function that is used to notify the client if CHPP is - //! matched to a service. + //! Notifies the client if CHPP is matched to a service. ChppNotifierFunction *matchNotifierFunctionPtr; - //! Pointer to the function that dispatches incoming service responses for the - //! client. + //! Dispatches incoming service responses. //! Service responses are only dispatched to clients that have been opened or - //! are in the process of being (re)opened. @see ChppOpenState + //! are in the process of being (re)opened. @see ChppOpenState. + //! Errors returned by the dispatch function are logged. ChppDispatchFunction *responseDispatchFunctionPtr; - //! Pointer to the function that dispatches incoming service notifications for - //! the client. + //! Dispatches incoming service notifications. //! Service notifications are only dispatched to clients that have been //! opened. @see ChppOpenState + //! Errors returned by the dispatch function are logged. ChppDispatchFunction *notificationDispatchFunctionPtr; - //! Pointer to the function that initializes the client (after it is matched - //! with a service at discovery) and assigns it its handle number. + //! Dispatches incoming service requests. + //! When an error is returned by the dispatch function it is logged and an + //! error response is automatically sent to the remote endpoint. + ChppDispatchFunction *requestDispatchFunctionPtr; + + //! Initializes the client (after it is matched with a service at discovery) + //! and assigns it its handle number. ChppClientInitFunction *initFunctionPtr; - //! Pointer to the function that deinitializes the client. + //! Deinitializes the client. ChppClientDeinitFunction *deinitFunctionPtr; - //! Number of request-response states in the rRStates array. This is a - //! uint16_t to match the uint16_t command in struct ChppAppHeader. - uint16_t rRStateCount; + //! Number of outgoing requests supported by this client. + //! ChppAppHeader.command must be in the range [0, outReqCount - 1] + //! ChppEndpointState.outReqStates must contains that many elements. + uint16_t outReqCount; - //! Minimum valid length of datagrams for the service. + //! Minimum valid length of datagrams for the service: + //! - service responses + //! - service notifications + //! - service requests size_t minLength; }; @@ -326,31 +420,54 @@ struct ChppClient { * Request status for clients. */ enum ChppRequestState { - CHPP_REQUEST_STATE_NONE = 0, // No request sent ever - CHPP_REQUEST_STATE_REQUEST_SENT = 1, // Sent but no response yet - CHPP_REQUEST_STATE_RESPONSE_RCV = 2, // Sent and response received - CHPP_REQUEST_STATE_RESPONSE_TIMEOUT = 3, // Timeout. Responded as need be + CHPP_REQUEST_STATE_NONE = 0, //!< No request sent ever + CHPP_REQUEST_STATE_REQUEST_SENT = 1, //!< Sent, waiting for a response + CHPP_REQUEST_STATE_RESPONSE_RCV = 2, //!< Sent and response received + CHPP_REQUEST_STATE_RESPONSE_TIMEOUT = 3, //!< Timeout. Responded as need be }; /** - * Maintains the basic state for each request/response functionality of a - * client or service. - * Any number of these may be included in the (context) status variable of a - * client or service (one per every every request/response functionality). + * State of each outgoing request and their response. + * + * There must be as many ChppOutgoingRequestState in the client or service state + * (ChppEndpointState) as the number of commands they support. */ -struct ChppRequestResponseState { +struct ChppOutgoingRequestState { uint64_t requestTimeNs; // Time of the last request - uint64_t - responseTimeNs; // If requestState is CHPP_REQUEST_STATE_REQUEST_SENT, - // indicates the timeout time for the request - // If requestState is CHPP_REQUEST_STATE_RESPONSE_RCV, - // indicates when the response was received - + // When requestState is CHPP_REQUEST_STATE_REQUEST_SENT, + // indicates the timeout time for the request. + // When requestState is CHPP_REQUEST_STATE_RESPONSE_RCV, + // indicates when the response was received. + uint64_t responseTimeNs; uint8_t requestState; // From enum ChppRequestState uint8_t transaction; // Transaction ID for the last request/response }; /** + * State of each incoming request and their response. + * + * There must be as many ChppIncomingRequestState in the client or service state + * as the number of commands supported by the other side (corresponding service + * for a client and corresponding client for a service). + * + * Contrary to ChppOutgoingRequestState those are not part of + * CChppEndpointState. They must be stored to and retrieved from the context + * passed to chppRegisterClient / chppRegisterService. + * + * Note: while ChppIncomingRequestState and ChppOutgoingRequestState have the + * same layout, we want the types to be distinct to be enforced at compile time. + * Using a typedef would make both types equivalent. + * + * @see ChppOutgoingRequestState for field details. + */ +struct ChppIncomingRequestState { + uint64_t requestTimeNs; + uint64_t responseTimeNs; + uint8_t requestState; + uint8_t transaction; +}; + +/** * Enabled clients and services. */ struct ChppClientServiceSet { @@ -366,6 +483,36 @@ struct ChppClientServiceSet { struct ChppLoopbackClientState; struct ChppTimesyncClientState; +/** + * CHPP state of a client or a service. + * + * This is the CHPP internal client/service state. + * Their private state is store in the context field. + */ +struct ChppEndpointState { + struct ChppAppState *appContext; // Pointer to app layer context + + // State for the outgoing requests. + // It must accommodate Chpp{Client,Service}.outReqCount elements. + // It also tracks corresponding incoming responses. + // NULL when outReqCount = 0. + struct ChppOutgoingRequestState *outReqStates; + + void *context; //!< Private state of the endpoint. + + struct ChppSyncResponse syncResponse; + + uint8_t index; //!< Index (in ChppAppState lists). + uint8_t handle; //!< Handle used to match client and service. + uint8_t transaction; //!< Next Transaction ID to be used. + + uint8_t openState; //!< see enum ChppOpenState + + bool pseudoOpen : 1; //!< Client to be opened upon a reset + bool initialized : 1; //!< Client is initialized + bool everInitialized : 1; //!< Client sync primitives initialized +}; + struct ChppAppState { struct ChppTransportState *transportContext; // Pointing to transport context @@ -375,18 +522,19 @@ struct ChppAppState { const struct ChppService *registeredServices[CHPP_MAX_REGISTERED_SERVICES]; - void *registeredServiceContexts[CHPP_MAX_REGISTERED_SERVICES]; + struct ChppEndpointState + *registeredServiceStates[CHPP_MAX_REGISTERED_SERVICES]; uint8_t registeredClientCount; // Number of clients currently registered const struct ChppClient *registeredClients[CHPP_MAX_REGISTERED_CLIENTS]; - const struct ChppClientState - *registeredClientStates[CHPP_MAX_REGISTERED_CLIENTS]; - - void *registeredClientContexts[CHPP_MAX_REGISTERED_CLIENTS]; + struct ChppEndpointState *registeredClientStates[CHPP_MAX_REGISTERED_CLIENTS]; - uint64_t nextRequestTimeoutNs; + // When the first outstanding request sent from the client timeouts. + uint64_t nextClientRequestTimeoutNs; + // When the first outstanding request sent from the service timeouts. + uint64_t nextServiceRequestTimeoutNs; uint8_t clientIndexOfServiceIndex[CHPP_MAX_DISCOVERED_SERVICES]; // Lookup table @@ -509,6 +657,256 @@ uint8_t chppAppErrorToChreError(uint8_t error); uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len, const char *responseName); +/** + * Allocates a notification of a specified length. + * + * This function is internal. Instead use either + * - chppAllocClientNotification + * - or chppAllocServiceNotification + * + * The caller must initialize at least the handle and command fields of the + * ChppAppHeader. + * + * @param type CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION or + * CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION. + * @param len Length of the notification (including header) in bytes. Note + * that the specified length must be at least equal to the length of the + * app layer header. + * + * @return Pointer to allocated memory. + */ +struct ChppAppHeader *chppAllocNotification(uint8_t type, size_t len); + +/** + * Allocates a request message. + * + * This function is internal. Instead use either: + * - chppAllocClientRequest + * - or chppAllocServiceRequest + * + * @param type CHPP_MESSAGE_TYPE_CLIENT_REQUEST or + * CHPP_MESSAGE_TYPE_SERVICE_REQUEST. + * @param endpointState State of the endpoint. + * @param len Length of the response message (including header) in bytes. Note + * that the specified length must be at least equal to the length of the + * app layer header. + * + * @return Pointer to allocated memory. + */ +struct ChppAppHeader *chppAllocRequest(uint8_t type, + struct ChppEndpointState *endpointState, + size_t len); + +/** + * Allocates a response message of a specified length, populating the (app + * layer) response header according to the provided request (app layer) header. + * + * This function can be used to allocate either client or service response. + * + * @param requestHeader request header. + * @param len Length of the response message (including header) in bytes. Note + * that the specified length must be at least equal to the length of the + * app layer header. + * + * @return Pointer to allocated memory. + */ +struct ChppAppHeader *chppAllocResponse( + const struct ChppAppHeader *requestHeader, size_t len); + +/** + * This function shall be called for all incoming requests in order to + * A) Timestamp them, and + * B) Save their Transaction ID + * + * This function prints an error message if a duplicate request is received + * while outstanding request is still pending without a response. + * + * @param inReqState State of the request/response. + * @param requestHeader Request header. + */ +void chppTimestampIncomingRequest(struct ChppIncomingRequestState *inReqState, + const struct ChppAppHeader *requestHeader); + +/** + * This function shall be called for all outgoing requests in order to + * A) Timestamp them, and + * B) Save their Transaction ID + * + * This function prints an error message if a duplicate request is sent + * while outstanding request is still pending without a response. + * + * @param appState App layer state. + * @param outReqState state of the request/response. + * @param requestHeader Client request header. + * @param timeoutNs The timeout. + */ +void chppTimestampOutgoingRequest(struct ChppAppState *appState, + struct ChppOutgoingRequestState *outReqState, + const struct ChppAppHeader *requestHeader, + uint64_t timeoutNs); + +/** + * This function shall be called for incoming responses to a request in + * order to + * A) Verify the correct transaction ID + * B) Timestamp them, and + * C) Mark them as fulfilled + * + * This function prints an error message if a response is received without an + * outstanding request. + * + * @param appState App layer state. + * @param outReqState state of the request/response. + * @param requestHeader Request header. + * + * @return false if there is an error. true otherwise. + */ +bool chppTimestampIncomingResponse(struct ChppAppState *appState, + struct ChppOutgoingRequestState *outReqState, + const struct ChppAppHeader *responseHeader); + +/** + * This function shall be called for the outgoing response to a request in order + * to: + * A) Timestamp them, and + * B) Mark them as fulfilled part of the request/response's + * ChppOutgoingRequestState struct. + * + * For most responses, it is expected that chppSendTimestampedResponseOrFail() + * shall be used to both timestamp and send the response in one shot. + * + * @param inReqState State of the request/response. + * @return The last response time (CHPP_TIME_NONE for the first response). + */ +uint64_t chppTimestampOutgoingResponse( + struct ChppIncomingRequestState *inReqState); + +/** + * Timestamps a response using chppTimestampOutgoingResponse() and enqueues it + * using chppEnqueueTxDatagramOrFail(). + * + * Refer to their respective documentation for details. + * + * This function logs an error message if a response is attempted without an + * outstanding request. + * + * @param appState App layer state. + * @param inReqState State of the request/response. + * @param buf Datagram payload allocated through chppMalloc. Cannot be null. + * @param len Datagram length in bytes. + * + * @return whether the datagram was successfully enqueued. false means that the + * queue was full and the payload discarded. + */ +bool chppSendTimestampedResponseOrFail( + struct ChppAppState *appState, struct ChppIncomingRequestState *inReqState, + void *buf, size_t len); + +/** + * Timestamps and enqueues a request. + * + * This function is internal. User either: + * - chppClientSendTimestampedRequestOrFail + * - or chppServiceSendTimestampedRequestOrFail + * + * Note that the ownership of buf is taken from the caller when this method is + * invoked. + * + * @param endpointState state of the endpoint. + * @param outReqState state of the request/response. + * @param buf Datagram payload allocated through chppMalloc. Cannot be null. + * @param len Datagram length in bytes. + * @param timeoutNs Time in nanoseconds before a timeout response is generated. + * Zero means no timeout response. + * + * @return True informs the sender that the datagram was successfully enqueued. + * False informs the sender that the queue was full and the payload + * discarded. + */ +bool chppSendTimestampedRequestOrFail( + struct ChppEndpointState *endpointState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs); + +/** + * Wait for a response to be received. + * + * @param syncResponse sync primitives. + * @param outReqState state of the request/response. + * @param timeoutNs Time in nanoseconds before a timeout response is generated. + */ +bool chppWaitForResponseWithTimeout( + struct ChppSyncResponse *syncResponse, + struct ChppOutgoingRequestState *outReqState, uint64_t timeoutNs); + +/** + * Returns the state of a registered endpoint. + * + * @param appState State of the app layer. + * @param index Index of the client or service. + * @param type Type of the endpoint to return. + * @return state of the client or service. + */ +struct ChppEndpointState *getRegisteredEndpointState( + struct ChppAppState *appState, uint8_t index, enum ChppEndpointType type); + +/** + * Returns the number of possible outgoing requests. + * + * @param appState State of the app layer. + * @param index Index of the client or service. + * @param type Type of the endpoint to return. + * @return The number of possible outgoing requests. + */ +uint16_t getRegisteredEndpointOutReqCount(struct ChppAppState *appState, + uint8_t index, + enum ChppEndpointType type); + +/** + * Returns the number of registered endpoints of the given type. + * + * @param appState State of the app layer. + * @param type Type of the endpoint to return. + * @return The number of endpoints. + */ +uint8_t getRegisteredEndpointCount(struct ChppAppState *appState, + enum ChppEndpointType type); + +/** + * Recalculates the next upcoming request timeout. + * + * The timeout is updated in the app layer state. + * + * @param appState State of the app layer. + * @param type Type of the endpoint. + */ +void chppRecalculateNextTimeout(struct ChppAppState *appState, + enum ChppEndpointType type); + +/** + * Returns a pointer to the next request timeout for the given endpoint type. + * + * @param appState State of the app layer. + * @param type Type of the endpoint. + * @return Pointer to the timeout in nanoseconds. + */ +uint64_t *getNextRequestTimeoutNs(struct ChppAppState *appState, + enum ChppEndpointType type); + +/** + * Closes any remaining open requests by simulating a timeout. + * + * This function is used when an endpoint is reset. + * + * @param endpointState State of the endpoint. + * @param type The type of the endpoint. + * @param clearOnly If true, indicates that a timeout response shouldn't be + * sent. This must only be set if the requests are being cleared as + * part of the closing. + */ +void chppCloseOpenRequests(struct ChppEndpointState *endpointState, + enum ChppEndpointType type, bool clearOnly); + #ifdef __cplusplus } #endif diff --git a/chpp/include/chpp/clients.h b/chpp/include/chpp/clients.h index f48686f8..fe2dfe72 100644 --- a/chpp/include/chpp/clients.h +++ b/chpp/include/chpp/clients.h @@ -25,7 +25,6 @@ #include "chpp/condition_variable.h" #include "chpp/macros.h" #include "chpp/mutex.h" -#include "chre_api/chre/common.h" #ifdef __cplusplus extern "C" { @@ -36,10 +35,10 @@ extern "C" { ***********************************************/ /** - * Uses chppAllocClientRequest() to allocate a client request message of a - * specific type and its corresponding length. + * Allocates a client request message of a specific type and its corresponding + * length. * - * @param clientState State variable of the client. + * @param clientState State of the client. * @param type Type of response. * * @return Pointer to allocated memory @@ -48,10 +47,9 @@ extern "C" { (type *)chppAllocClientRequest(clientState, sizeof(type)) /** - * Uses chppAllocClientRequest() to allocate a variable-length client request - * message of a specific type. + * Allocates a variable-length client request message of a specific type. * - * @param clientState State variable of the client. + * @param clientState State of the client. * @param type Type of response which includes an arrayed member. * @param count number of items in the array of arrayField. * @param arrayField The arrayed member field. @@ -63,27 +61,27 @@ extern "C" { clientState, sizeof(type) + (count)*sizeof_member(type, arrayField[0])) /** - * Maintains the basic state of a client. - * This is expected to be included once in the (context) status variable of - * each client. + * Allocates a variable-length notification of a specific type. + * + * @param type Type of notification which includes an arrayed member. + * @param count number of items in the array of arrayField. + * @param arrayField The arrayed member field. + * + * @return Pointer to allocated memory + */ +#define chppAllocClientNotificationTypedArray(type, count, arrayField) \ + (type *)chppAllocClientNotification( \ + sizeof(type) + (count)*sizeof_member(type, arrayField[0])) + +/** + * Allocates a notification of a specific type and its corresponding length. + * + * @param type Type of notification. + * + * @return Pointer to allocated memory */ -struct ChppClientState { - struct ChppAppState *appContext; // Pointer to app layer context - struct ChppRequestResponseState - *rRStates; // Pointer to array of request-response states, if any - uint8_t index; // Index of this client - uint8_t handle; // Handle number for this client - uint8_t transaction; // Next Transaction ID to be used - - uint8_t openState; // As defined in enum ChppOpenState - bool pseudoOpen : 1; // Client to be opened upon a reset - bool initialized : 1; // Is initialized - bool everInitialized : 1; // Synchronization primitives initialized - - bool responseReady : 1; // For sync. request/responses - struct ChppMutex responseMutex; - struct ChppConditionVariable responseCondVar; -}; +#define chppAllocClientNotificationFixed(type) \ + (type *)chppAllocClientNotification(sizeof(type)) #ifdef CHPP_CLIENT_ENABLED_CHRE_WWAN #define CHPP_CLIENT_ENABLED_WWAN @@ -105,12 +103,6 @@ struct ChppClientState { #define CHPP_CLIENT_ENABLED #endif -#define CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE CHPP_TIME_MAX - -#ifndef CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT -#define CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT CHRE_ASYNC_RESULT_TIMEOUT_NS -#endif - // Default timeout for discovery completion. #ifndef CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS #define CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS UINT64_C(10000) // 10s @@ -149,19 +141,27 @@ void chppDeregisterCommonClients(struct ChppAppState *context); * When a match succeeds, the client's initialization function (pointer) is * called, assigning them their handle number. * + * outReqStates must point to an array of ChppOutgoingRequestState with + * ChppEndpointState.outReqCount elements. It must be NULL when the client + * does not send requests (ChppEndpointState.outReqCount = 0). + * + * inReqStates must point to an array of ChppIncomingRequestState with + * as many elements as the corresponding service can send. It must be NULL when + * the service does not send requests (ChppEndpointState.outReqCount = 0). + * * Note that the maximum number of clients that can be registered on a platform * can specified as CHPP_MAX_REGISTERED_CLIENTS by the initialization code. * Otherwise, a default value will be used. * * @param appContext State of the app layer. * @param clientContext State of the client instance. - * @param clientState State variable of the client. - * @param rRStates Pointer to array of request-response states, if any. + * @param clientState State of the client. + * @param outReqStates List of outgoing request states. * @param newClient The client to be registered on this platform. */ void chppRegisterClient(struct ChppAppState *appContext, void *clientContext, - struct ChppClientState *clientState, - struct ChppRequestResponseState *rRStates, + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqStates, const struct ChppClient *newClient); /** @@ -175,17 +175,17 @@ void chppInitBasicClients(struct ChppAppState *context); * Initializes a client. This function must be called when a client is matched * with a service during discovery to provides its handle number. * - * @param clientState State variable of the client. + * @param clientState State of the client. * @param handle Handle number for this client. */ -void chppClientInit(struct ChppClientState *clientState, uint8_t handle); +void chppClientInit(struct ChppEndpointState *clientState, uint8_t handle); /** * Deinitializes a client. * - * @param clientState State variable of the client. + * @param clientState State of the client. */ -void chppClientDeinit(struct ChppClientState *clientState); +void chppClientDeinit(struct ChppEndpointState *clientState); /** * Deinitializes basic clients. @@ -202,140 +202,102 @@ void chppDeinitBasicClients(struct ChppAppState *context); void chppDeinitMatchedClients(struct ChppAppState *context); /** - * Allocates a client request message of a specified length, populating the - * (app layer) client request header, including the sequence ID. The - * next-sequence ID stored in the client state variable is subsequently - * incremented. + * Allocates a client request message of a specified length. * - * It is expected that for most use cases, the chppAllocClientRequestFixed() - * or chppAllocClientRequestTypedArray() macros shall be used rather than - * calling this function directly. + * It populates the request header, including the transaction number which is + * then incremented. * - * @param clientState State variable of the client. + * For most use cases, the chppAllocClientRequestFixed() or + * chppAllocClientRequestTypedArray() macros shall be preferred. + * + * @param clientState State of the client. * @param len Length of the response message (including header) in bytes. Note - * that the specified length must be at least equal to the lendth of the app - * layer header. + * that the specified length must be at least equal to the length of the + * app layer header. * * @return Pointer to allocated memory */ struct ChppAppHeader *chppAllocClientRequest( - struct ChppClientState *clientState, size_t len); + struct ChppEndpointState *clientState, size_t len); /** - * Uses chppAllocClientRequest() to allocate a specific client request command - * without any additional payload. + * Allocates a specific client request command without any additional payload. * - * @param clientState State variable of the client. + * @param clientState State of the client. * @param command Type of response. * * @return Pointer to allocated memory */ struct ChppAppHeader *chppAllocClientRequestCommand( - struct ChppClientState *clientState, uint16_t command); + struct ChppEndpointState *clientState, uint16_t command); /** - * This function shall be called for all outgoing client requests in order to - * A) Timestamp them, and - * B) Save their Transaction ID - * as part of the request/response's ChppRequestResponseState struct. - * - * This function prints an error message if a duplicate request is sent - * while outstanding request is still pending without a response. - * - * @param clientState State of the client sending the client request. - * @param transactionId The transaction ID to use when loading the app. - * @param rRState Maintains the basic state for each request/response - * functionality of a client. - * @param requestHeader Client request header. - */ -void chppClientTimestampRequest(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - struct ChppAppHeader *requestHeader, - uint64_t timeoutNs); - -/** - * This function shall be called for incoming responses to a client request in - * order to - * A) Verify the correct transaction ID - * B) Timestamp them, and - * C) Mark them as fulfilled - * D) TODO: check for timeout - * - * This function prints an error message if a response is received without an - * outstanding request. - * - * @param clientState State of the client sending the client request. - * @param rRState Maintains the basic state for each request/response - * functionality of a client. - * @param requestHeader Client request header. - * - * @return false if there is an error. True otherwise. - */ -bool chppClientTimestampResponse(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - const struct ChppAppHeader *responseHeader); - -/** - * Timestamps a client request using chppClientTimestampResponse() and enqueues - * it using chppEnqueueTxDatagramOrFail(). - * - * Refer to their respective documentation for details. + * Timestamps and enqueues a request. * * Note that the ownership of buf is taken from the caller when this method is * invoked. * - * @param clientState State of the client sending the client request. - * @param rRState Maintains the basic state for each request/response - * functionality of a client. + * @param clientState State of the client sending the request. + * @param outReqState State of the request/response * @param buf Datagram payload allocated through chppMalloc. Cannot be null. * @param len Datagram length in bytes. * @param timeoutNs Time in nanoseconds before a timeout response is generated. - * Zero means no timeout response. + * Zero means no timeout response. * * @return True informs the sender that the datagram was successfully enqueued. - * False informs the sender that the queue was full and the payload discarded. + * False informs the sender that the queue was full and the payload + * discarded. */ -bool chppSendTimestampedRequestOrFail(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len, - uint64_t timeoutNs); +bool chppClientSendTimestampedRequestOrFail( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs); /** - * Similar to chppSendTimestampedRequestOrFail() but blocks execution until a - * response is received. Used for synchronous requests. + * Similar to chppClientSendTimestampedRequestOrFail() but blocks execution + * until a response is received. Used for synchronous requests. * * In order to use this function, clientState->responseNotifier must have been * initialized using chppNotifierInit() upon initialization of the client. * - * @param clientState State of the client sending the client request. - * @param rRState Maintains the basic state for each request/response - * functionality of a client. + * @param clientState State of the client sending the request. + * @param outReqState State of the request/response. * @param buf Datagram payload allocated through chppMalloc. Cannot be null. * @param len Datagram length in bytes. * * @return True informs the sender that the datagram was successfully enqueued. - * False informs the sender that the payload was discarded because either the - * queue was full, or the request timed out. + * False informs the sender that the payload was discarded because + * either the queue was full, or the request timed out. */ -bool chppSendTimestampedRequestAndWait(struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len); +bool chppClientSendTimestampedRequestAndWait( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len); /** - * Same as chppSendTimestampedRequestAndWait() but with a specified timeout. + * Same as chppClientSendTimestampedRequestAndWait() but with a specified + * timeout. + * + * @param clientState State of the client sending the request. + * @param outReqState State of the request/response. + * @param buf Datagram payload allocated through chppMalloc. Cannot be null. + * @param len Datagram length in bytes. + * + * @return True informs the sender that the datagram was successfully enqueued. + * False informs the sender that the payload was discarded because + * either the queue was full, or the request timed out. */ -bool chppSendTimestampedRequestAndWaitTimeout( - struct ChppClientState *clientState, - struct ChppRequestResponseState *rRState, void *buf, size_t len, +bool chppClientSendTimestampedRequestAndWaitTimeout( + struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, uint64_t timeoutNs); /** * Marks a closed client as pseudo-open, so that it would be opened upon a * reset. * - * @param clientState State variable of the client. + * @param clientState State of the client. */ -void chppClientPseudoOpen(struct ChppClientState *clientState); +void chppClientPseudoOpen(struct ChppEndpointState *clientState); /** * Sends a client request for the open command in a blocking or non-blocking @@ -343,46 +305,59 @@ void chppClientPseudoOpen(struct ChppClientState *clientState); * A non-blocking open is used to for reopening a service after a reset or for * opening a pseudo-open service. * - * @param clientState State variable of the client. - * @param openRRState Request/response state for the open command. + * @param clientState State of the client. + * @param openReqState State of the request/response for the open command. * @param openCommand Open command to be sent. * @param blocking Indicates a blocking (vs. non-blocking) open request. * * @return Indicates success or failure. */ -bool chppClientSendOpenRequest(struct ChppClientState *clientState, - struct ChppRequestResponseState *openRRState, +bool chppClientSendOpenRequest(struct ChppEndpointState *clientState, + struct ChppOutgoingRequestState *openReqState, uint16_t openCommand, bool blocking); /** * Processes a service response for the open command. * - * @param clientState State variable of the client. + * @param clientState State of the client. */ -void chppClientProcessOpenResponse(struct ChppClientState *clientState, +void chppClientProcessOpenResponse(struct ChppEndpointState *clientState, uint8_t *buf, size_t len); /** - * Recalculates the next upcoming client request timeout time. - * - * @param context State of the app layer. - */ -void chppClientRecalculateNextTimeout(struct ChppAppState *context); + * Closes any remaining open requests by simulating a timeout. -/** - * Closes any remaining open requests for a given client by sending a timeout. * This function is used when a client is reset. * - * @param clientState State variable of the client. - * @param client The client for whech to clear out open requests. + * @param clientState State of the client. + * @param client The client for which to clear out open requests. * @param clearOnly If true, indicates that a timeout response shouldn't be - * sent to the client. This must only be set if the requests are being - * cleared as part of the client closing. + * sent. This must only be set if the requests are being cleared as part + * of the client closing. */ -void chppClientCloseOpenRequests(struct ChppClientState *clientState, +void chppClientCloseOpenRequests(struct ChppEndpointState *clientState, const struct ChppClient *client, bool clearOnly); +/** + * Allocates a client notification of a specified length. + * + * It is expected that for most use cases, the + * chppAllocClientNotificationFixed() or + * chppAllocClientNotificationTypedArray() macros shall be used rather than + * calling this function directly. + * + * The caller must initialize at least the handle and command fields of the + * ChppAppHeader. + * + * @param len Length of the notification (including header) in bytes. Note + * that the specified length must be at least equal to the length of the + * app layer header. + * + * @return Pointer to allocated memory. + */ +struct ChppAppHeader *chppAllocClientNotification(size_t len); + #ifdef __cplusplus } #endif diff --git a/chpp/include/chpp/clients/gnss.h b/chpp/include/chpp/clients/gnss.h index 74f5022d..a26dcbeb 100644 --- a/chpp/include/chpp/clients/gnss.h +++ b/chpp/include/chpp/clients/gnss.h @@ -50,9 +50,9 @@ void chppRegisterGnssClient(struct ChppAppState *appContext); void chppDeregisterGnssClient(struct ChppAppState *appContext); /** - * @return The ChppClientState pointer to the GNSS client. + * @return The ChppEndpointState pointer to the GNSS client. */ -struct ChppClientState *getChppGnssClientState(void); +struct ChppEndpointState *getChppGnssClientState(void); #ifndef CHPP_CLIENT_ENABLED_CHRE_GNSS /** diff --git a/chpp/include/chpp/clients/wifi.h b/chpp/include/chpp/clients/wifi.h index e0384cd0..f750c761 100644 --- a/chpp/include/chpp/clients/wifi.h +++ b/chpp/include/chpp/clients/wifi.h @@ -50,9 +50,9 @@ void chppRegisterWifiClient(struct ChppAppState *appContext); void chppDeregisterWifiClient(struct ChppAppState *appContext); /** - * @return The ChppClientState pointer to the WiFi client. + * @return The ChppEndpointState pointer to the WiFi client. */ -struct ChppClientState *getChppWifiClientState(void); +struct ChppEndpointState *getChppWifiClientState(void); #ifndef CHPP_CLIENT_ENABLED_CHRE_WIFI /** diff --git a/chpp/include/chpp/clients/wwan.h b/chpp/include/chpp/clients/wwan.h index 3b10f2a4..41a680c6 100644 --- a/chpp/include/chpp/clients/wwan.h +++ b/chpp/include/chpp/clients/wwan.h @@ -50,9 +50,9 @@ void chppRegisterWwanClient(struct ChppAppState *appContext); void chppDeregisterWwanClient(struct ChppAppState *appContext); /** - * @return The ChppClientState pointer to the WWAN client. + * @return The ChppEndpointState pointer to the WWAN client. */ -struct ChppClientState *getChppWwanClientState(void); +struct ChppEndpointState *getChppWwanClientState(void); #ifndef CHPP_CLIENT_ENABLED_CHRE_WWAN /** diff --git a/chpp/include/chpp/notifier.h b/chpp/include/chpp/notifier.h index 2624b476..de0e6bd5 100644 --- a/chpp/include/chpp/notifier.h +++ b/chpp/include/chpp/notifier.h @@ -96,6 +96,8 @@ static uint32_t chppNotifierTimedWait(struct ChppNotifier *notifier, * multiple events to be handled simultaneously in chppNotifierTimedWait(). * * @param notifier Points to the ChppNotifier being used. + * @param signal The value where each bit represents a different event. + * As such the value 0 will not notify any event. */ static void chppNotifierSignal(struct ChppNotifier *notifier, uint32_t signal); diff --git a/chpp/include/chpp/services.h b/chpp/include/chpp/services.h index ddd5a93f..a89af3cb 100644 --- a/chpp/include/chpp/services.h +++ b/chpp/include/chpp/services.h @@ -38,71 +38,55 @@ extern "C" { #endif /** - * Uses chppAllocServiceNotification() to allocate a variable-length response - * message of a specific type. + * Allocates a service request message of a specific type and its corresponding + * length. * - * @param type Type of notification which includes an arrayed member. - * @param count number of items in the array of arrayField. - * @param arrayField The arrayed member field. + * @param serviceState State of the service. + * @param type Type of response. * - * @return Pointer to allocated memory + * @return Pointer to allocated memory. */ -#define chppAllocServiceNotificationTypedArray(type, count, arrayField) \ - (type *)chppAllocServiceNotification( \ - sizeof(type) + (count)*sizeof_member(type, arrayField[0])) +#define chppAllocServiceRequestFixed(serviceState, type) \ + (type *)chppAllocServiceRequest(serviceState, sizeof(type)) /** - * Uses chppAllocServiceNotification() to allocate a response message of a - * specific type and its corresponding length. + * Allocate a variable-length service request message of a specific type. * - * @param type Type of notification. + * @param serviceState State of the service. + * @param type Type of response which includes an arrayed member. + * @param count number of items in the array of arrayField. + * @param arrayField The arrayed member field. * - * @return Pointer to allocated memory + * @return Pointer to allocated memory. */ -#define chppAllocServiceNotificationFixed(type) \ - (type *)chppAllocServiceNotification(sizeof(type)) +#define chppAllocServiceRequestTypedArray(serviceState, type, count, \ + arrayField) \ + (type *)chppAllocServiceRequest( \ + serviceState, sizeof(type) + (count)*sizeof_member(type, arrayField[0])) /** - * Uses chppAllocServiceResponse() to allocate a variable-length response - * message of a specific type. + * Allocates a variable-length notification of a specific type. * - * @param requestHeader client request header, as per - * chppAllocServiceResponse(). - * @param type Type of response which includes an arrayed member. + * @param type Type of notification which includes an arrayed member. * @param count number of items in the array of arrayField. * @param arrayField The arrayed member field. * - * @return Pointer to allocated memory + * @return Pointer to allocated memory. */ -#define chppAllocServiceResponseTypedArray(requestHeader, type, count, \ - arrayField) \ - (type *)chppAllocServiceResponse( \ - requestHeader, \ +#define chppAllocServiceNotificationTypedArray(type, count, arrayField) \ + (type *)chppAllocServiceNotification( \ sizeof(type) + (count)*sizeof_member(type, arrayField[0])) /** - * Uses chppAllocServiceResponse() to allocate a response message of a specific - * type and its corresponding length. + * Uses chppAllocServiceNotification() to allocate a response notification of a + * specific type and its corresponding length. * - * @param requestHeader client request header, as per - * chppAllocServiceResponse(). - * @param type Type of response. + * @param type Type of notification. * * @return Pointer to allocated memory */ -#define chppAllocServiceResponseFixed(requestHeader, type) \ - (type *)chppAllocServiceResponse(requestHeader, sizeof(type)) - -/** - * Maintains the basic state of a service. - * This is expected to be included in the state of each service. - */ -struct ChppServiceState { - struct ChppAppState *appContext; // Pointer to app layer context - uint8_t handle; // Handle number for this service - - uint8_t openState; // As defined in enum ChppOpenState -}; +#define chppAllocServiceNotificationFixed(type) \ + (type *)chppAllocServiceNotification(sizeof(type)) /************************************************ * Public functions @@ -132,17 +116,27 @@ void chppDeregisterCommonServices(struct ChppAppState *context); * server (if any), i.e. except those that are registered through * chppRegisterCommonServices(). * + * outReqStates must point to an array of ChppOutgoingRequestState with + * ChppEndpointState.outReqCount elements. It must be NULL when the service + * does not send requests (ChppEndpointState.outReqCount = 0). + * + * inReqStates must point to an array of ChppIncomingRequestState with + * as many elements as the corresponding client can send. It must be NULL when + * the client does not send requests (ChppEndpointState.outReqCount = 0). + * * Note that the maximum number of services that can be registered on a platform * can specified as CHPP_MAX_REGISTERED_SERVICES by the initialization code. * Otherwise, a default value will be used. * * @param appContext State of the app layer. * @param serviceContext State of the service instance. - * @param serviceState State variable of the client. + * @param serviceState State of the client. + * @param outReqStates List of outgoing request states. * @param newService The service to be registered on this platform. */ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, - struct ChppServiceState *serviceState, + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqStates, const struct ChppService *newService); /** @@ -153,83 +147,118 @@ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, * chppAllocServiceNotificationTypedArray() macros shall be used rather than * calling this function directly. * + * The caller must initialize at least the handle and command fields of the + * ChppAppHeader. + * * @param len Length of the notification (including header) in bytes. Note - * that the specified length must be at least equal to the length of the app - * layer header. + * that the specified length must be at least equal to the length of the + * app layer header. * * @return Pointer to allocated memory */ struct ChppAppHeader *chppAllocServiceNotification(size_t len); /** - * Allocates a service response message of a specified length, populating the - * (app layer) service response header according to the provided client request - * (app layer) header. + * Allocates a service request message of a specified length. * - * It is expected that for most use cases, the chppAllocServiceResponseFixed() - * or chppAllocServiceResponseTypedArray() macros shall be used rather than - * calling this function directly. + * It populates the request header, including the transaction number which is + * then incremented. + * + * For most use cases, the chppAllocServiceRequestFixed() or + * chppAllocServiceRequestTypedArray() macros shall be preferred. * - * @param requestHeader Client request header. + * @param serviceState State of the service. * @param len Length of the response message (including header) in bytes. Note - * that the specified length must be at least equal to the length of the app - * layer header. + * that the specified length must be at least equal to the length of the + * app layer header. * * @return Pointer to allocated memory */ -struct ChppAppHeader *chppAllocServiceResponse( - const struct ChppAppHeader *requestHeader, size_t len); +struct ChppAppHeader *chppAllocServiceRequest( + struct ChppEndpointState *serviceState, size_t len); /** - * This function shall be called for all incoming client requests in order to - * A) Timestamp them, and - * B) Save their Transaction ID - * as part of the request/response's ChppRequestResponseState struct. + * Allocates a specific service request command without any additional payload. * - * This function prints an error message if a duplicate request is received - * while outstanding request is still pending without a response. + * @param serviceState State of the service. + * @param command Type of response. * - * @param rRStateState State of the current request/response. - * @param requestHeader Client request header. + * @return Pointer to allocated memory */ -void chppServiceTimestampRequest(struct ChppRequestResponseState *rRState, - struct ChppAppHeader *requestHeader); +struct ChppAppHeader *chppAllocServiceRequestCommand( + struct ChppEndpointState *serviceState, uint16_t command); /** - * This function shall be called for the final service response to a client - * request in order to - * A) Timestamp them, and - * B) Mark them as fulfilled - * part of the request/response's ChppRequestResponseState struct. + * Timestamps and enqueues a request. * - * For most responses, it is expected that chppSendTimestampedResponseOrFail() - * shall be used to both timestamp and send the response in one shot. + * Note that the ownership of buf is taken from the caller when this method is + * invoked. + * + * @param serviceState State of the service sending the request. + * @param outReqState State of the request/response. + * @param buf Datagram payload allocated through chppMalloc. Cannot be null. + * @param len Datagram length in bytes. + * @param timeoutNs Time in nanoseconds before a timeout response is generated. + * Zero means no timeout response. * - * @param rRState State of the current request/response. - * @return The last response time (CHPP_TIME_NONE for the first response). + * @return True informs the sender that the datagram was successfully enqueued. + * False informs the sender that the queue was full and the payload + * discarded. */ -uint64_t chppServiceTimestampResponse(struct ChppRequestResponseState *rRState); +bool chppServiceSendTimestampedRequestOrFail( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs); /** - * Timestamps a service response using chppServiceTimestampResponse() and - * enqueues it using chppEnqueueTxDatagramOrFail(). + * Similar to chppServiceSendTimestampedRequestOrFail() but blocks execution + * until a response is received. Used for synchronous requests. * - * Refer to their respective documentation for details. + * @param serviceState State of the service sending the request. + * @param outReqState State of the request/response. + * @param buf Datagram payload allocated through chppMalloc. Cannot be null. + * @param len Datagram length in bytes. * - * This function logs an error message if a response is attempted without an - * outstanding request. + * @return True informs the sender that the datagram was successfully enqueued. + * False informs the sender that the payload was discarded because + * either the queue was full, or the request timed out. + */ +bool chppServiceSendTimestampedRequestAndWait( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len); + +/** + * Same as chppClientSendTimestampedRequestAndWait() but with a specified + * timeout. * - * @param serviceState State of the service sending the response service. - * @param rRState State of the current request/response. + * @param serviceState State of the service sending the request. + * @param outReqState State of the request/response. * @param buf Datagram payload allocated through chppMalloc. Cannot be null. * @param len Datagram length in bytes. * * @return True informs the sender that the datagram was successfully enqueued. - * False informs the sender that the queue was full and the payload discarded. + * False informs the sender that the payload was discarded because + * either the queue was full, or the request timed out. + */ +bool chppServiceSendTimestampedRequestAndWaitTimeout( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs); + +/** + * Closes any remaining open requests by simulating a timeout. + * + * This function is used when a service is reset. + * + * @param serviceState State of the service. + * @param service The service for which to clear out open requests. + * @param clearOnly If true, indicates that a timeout response shouldn't be + * sent. This must only be set if the requests are being cleared as + * part of the closing. */ -bool chppSendTimestampedResponseOrFail(struct ChppServiceState *serviceState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len); +void chppServiceCloseOpenRequests(struct ChppEndpointState *serviceState, + const struct ChppService *service, + bool clearOnly); #ifdef __cplusplus } diff --git a/chpp/include/chpp/transport.h b/chpp/include/chpp/transport.h index 19c701ce..f25b321c 100644 --- a/chpp/include/chpp/transport.h +++ b/chpp/include/chpp/transport.h @@ -295,7 +295,7 @@ struct ChppTransportConfiguration { uint16_t reserved3; } CHPP_PACKED_ATTR; CHPP_PACKED_END -// LINT.ThenChange(chpp/test/packet_util.cpp) +// LINT.ThenChange(../../../chpp/test/packet_util.cpp) struct ChppRxStatus { //! Current receiving state, as described in ChppRxState. diff --git a/chpp/services.c b/chpp/services.c index 3445524a..0420bfe6 100644 --- a/chpp/services.c +++ b/chpp/services.c @@ -23,7 +23,9 @@ #include "chpp/app.h" #include "chpp/log.h" +#include "chpp/macros.h" #include "chpp/memory.h" +#include "chpp/mutex.h" #ifdef CHPP_SERVICE_ENABLED_GNSS #include "chpp/services/gnss.h" #endif @@ -33,7 +35,6 @@ #ifdef CHPP_SERVICE_ENABLED_WWAN #include "chpp/services/wwan.h" #endif -#include "chpp/time.h" #include "chpp/transport.h" /************************************************ @@ -41,6 +42,7 @@ ***********************************************/ void chppRegisterCommonServices(struct ChppAppState *context) { + CHPP_DEBUG_NOT_NULL(context); UNUSED_VAR(context); #ifdef CHPP_SERVICE_ENABLED_WWAN @@ -63,6 +65,7 @@ void chppRegisterCommonServices(struct ChppAppState *context) { } void chppDeregisterCommonServices(struct ChppAppState *context) { + CHPP_DEBUG_NOT_NULL(context); UNUSED_VAR(context); #ifdef CHPP_SERVICE_ENABLED_WWAN @@ -85,7 +88,8 @@ void chppDeregisterCommonServices(struct ChppAppState *context) { } void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, - struct ChppServiceState *serviceState, + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqStates, const struct ChppService *newService) { CHPP_DEBUG_NOT_NULL(appContext); CHPP_DEBUG_NOT_NULL(serviceContext); @@ -96,6 +100,8 @@ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, serviceState->openState = CHPP_OPEN_STATE_CLOSED; serviceState->appContext = appContext; + serviceState->outReqStates = outReqStates; + serviceState->context = serviceContext; if (numServices >= CHPP_MAX_REGISTERED_SERVICES) { CHPP_LOGE("Max services registered: # %" PRIu8, numServices); @@ -103,12 +109,16 @@ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, return; } + serviceState->index = numServices; serviceState->handle = CHPP_SERVICE_HANDLE_OF_INDEX(numServices); appContext->registeredServices[numServices] = newService; - appContext->registeredServiceContexts[numServices] = serviceContext; + appContext->registeredServiceStates[numServices] = serviceState; appContext->registeredServiceCount++; + chppMutexInit(&serviceState->syncResponse.mutex); + chppConditionVariableInit(&serviceState->syncResponse.condVar); + char uuidText[CHPP_SERVICE_UUID_STRING_LEN]; chppUuidToStr(newService->descriptor.uuid, uuidText); CHPP_LOGD("Registered service # %" PRIu8 @@ -119,79 +129,64 @@ void chppRegisterService(struct ChppAppState *appContext, void *serviceContext, uuidText, newService->descriptor.version.major, newService->descriptor.version.minor, newService->descriptor.version.patch, newService->minLength); - - return; } struct ChppAppHeader *chppAllocServiceNotification(size_t len) { - CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); - - struct ChppAppHeader *result = chppMalloc(len); - if (result) { - result->handle = CHPP_HANDLE_NONE; - result->type = CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION; - result->transaction = 0; - result->error = CHPP_APP_ERROR_NONE; - result->command = CHPP_APP_COMMAND_NONE; - } - return result; + return chppAllocNotification(CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION, len); +} + +struct ChppAppHeader *chppAllocServiceRequest( + struct ChppEndpointState *serviceState, size_t len) { + CHPP_DEBUG_NOT_NULL(serviceState); + return chppAllocRequest(CHPP_MESSAGE_TYPE_SERVICE_REQUEST, serviceState, len); } -struct ChppAppHeader *chppAllocServiceResponse( - const struct ChppAppHeader *requestHeader, size_t len) { - CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); +struct ChppAppHeader *chppAllocServiceRequestCommand( + struct ChppEndpointState *serviceState, uint16_t command) { + struct ChppAppHeader *request = + chppAllocServiceRequest(serviceState, sizeof(struct ChppAppHeader)); - struct ChppAppHeader *result = chppMalloc(len); - if (result) { - *result = *requestHeader; - result->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE; - result->error = CHPP_APP_ERROR_NONE; + if (request != NULL) { + request->command = command; } - return result; + return request; } -void chppServiceTimestampRequest(struct ChppRequestResponseState *rRState, - struct ChppAppHeader *requestHeader) { - if (rRState->responseTimeNs == CHPP_TIME_NONE && - rRState->requestTimeNs != CHPP_TIME_NONE) { - CHPP_LOGE("RX dupe req t=%" PRIu64, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC); - } - rRState->requestTimeNs = chppGetCurrentTimeNs(); - rRState->responseTimeNs = CHPP_TIME_NONE; - rRState->transaction = requestHeader->transaction; +bool chppServiceSendTimestampedRequestOrFail( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs) { + return chppSendTimestampedRequestOrFail(serviceState, outReqState, buf, len, + timeoutNs); } -uint64_t chppServiceTimestampResponse( - struct ChppRequestResponseState *rRState) { - uint64_t previousResponseTime = rRState->responseTimeNs; - rRState->responseTimeNs = chppGetCurrentTimeNs(); - return previousResponseTime; +bool chppServiceSendTimestampedRequestAndWait( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len) { + return chppServiceSendTimestampedRequestAndWaitTimeout( + serviceState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_DEFAULT); } -bool chppSendTimestampedResponseOrFail(struct ChppServiceState *serviceState, - struct ChppRequestResponseState *rRState, - void *buf, size_t len) { - uint64_t previousResponseTime = chppServiceTimestampResponse(rRState); - - if (rRState->requestTimeNs == CHPP_TIME_NONE) { - CHPP_LOGE("TX response w/ no req t=%" PRIu64, - rRState->responseTimeNs / CHPP_NSEC_PER_MSEC); - - } else if (previousResponseTime != CHPP_TIME_NONE) { - CHPP_LOGW("TX additional response t=%" PRIu64 " for req t=%" PRIu64, - rRState->responseTimeNs / CHPP_NSEC_PER_MSEC, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC); - - } else { - CHPP_LOGD("Sending initial response at t=%" PRIu64 - " for request at t=%" PRIu64 " (RTT=%" PRIu64 ")", - rRState->responseTimeNs / CHPP_NSEC_PER_MSEC, - rRState->requestTimeNs / CHPP_NSEC_PER_MSEC, - (rRState->responseTimeNs - rRState->requestTimeNs) / - CHPP_NSEC_PER_MSEC); +bool chppServiceSendTimestampedRequestAndWaitTimeout( + struct ChppEndpointState *serviceState, + struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, + uint64_t timeoutNs) { + CHPP_DEBUG_NOT_NULL(serviceState); + + bool result = chppServiceSendTimestampedRequestOrFail( + serviceState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_INFINITE); + + if (!result) { + return false; } - return chppEnqueueTxDatagramOrFail(serviceState->appContext->transportContext, - buf, len); + return chppWaitForResponseWithTimeout(&serviceState->syncResponse, + outReqState, timeoutNs); } + +void chppServiceCloseOpenRequests(struct ChppEndpointState *serviceState, + const struct ChppService *service, + bool clearOnly) { + UNUSED_VAR(service); + chppCloseOpenRequests(serviceState, CHPP_ENDPOINT_SERVICE, clearOnly); +}
\ No newline at end of file diff --git a/chpp/services/discovery.c b/chpp/services/discovery.c index 4ed54913..2712a648 100644 --- a/chpp/services/discovery.c +++ b/chpp/services/discovery.c @@ -51,9 +51,9 @@ static void chppDiscoveryDiscoverAll( sizeof(struct ChppAppHeader) + context->registeredServiceCount * sizeof(struct ChppServiceDescriptor); - struct ChppDiscoveryResponse *response = chppAllocServiceResponseTypedArray( - requestHeader, struct ChppDiscoveryResponse, - context->registeredServiceCount, services); + struct ChppDiscoveryResponse *response = + chppAllocResponseTypedArray(requestHeader, struct ChppDiscoveryResponse, + context->registeredServiceCount, services); if (response == NULL) { CHPP_LOG_OOM(); diff --git a/chpp/services/gnss.c b/chpp/services/gnss.c index 33f2ca4e..57bdf246 100644 --- a/chpp/services/gnss.c +++ b/chpp/services/gnss.c @@ -72,18 +72,18 @@ static const struct ChppService kGnssServiceConfig = { * (RR) functionality. */ struct ChppGnssServiceState { - struct ChppServiceState service; // GNSS service state + struct ChppEndpointState service; // CHPP service state const struct chrePalGnssApi *api; // GNSS PAL API // Based on chre/pal/gnss.h and chrePalGnssApi - struct ChppRequestResponseState open; // Service init state - struct ChppRequestResponseState close; // Service deinit state - struct ChppRequestResponseState getCapabilities; // Get Capabilities state - struct ChppRequestResponseState + struct ChppIncomingRequestState open; // Service init state + struct ChppIncomingRequestState close; // Service deinit state + struct ChppIncomingRequestState getCapabilities; // Get Capabilities state + struct ChppIncomingRequestState controlLocationSession; // Control Location measurement state - struct ChppRequestResponseState + struct ChppIncomingRequestState controlMeasurementSession; // Control Raw GNSS measurement state - struct ChppRequestResponseState + struct ChppIncomingRequestState configurePassiveLocationListener; // Configure Passive location receiving // state }; @@ -153,51 +153,51 @@ static enum ChppAppErrorCode chppDispatchGnssRequest(void *serviceContext, struct ChppGnssServiceState *gnssServiceContext = (struct ChppGnssServiceState *)serviceContext; - struct ChppRequestResponseState *rRState = NULL; + struct ChppIncomingRequestState *inReqState = NULL; enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; bool dispatched = true; switch (rxHeader->command) { case CHPP_GNSS_OPEN: { - rRState = &gnssServiceContext->open; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->open; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceOpen(gnssServiceContext, rxHeader); break; } case CHPP_GNSS_CLOSE: { - rRState = &gnssServiceContext->close; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->close; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceClose(gnssServiceContext, rxHeader); break; } case CHPP_GNSS_GET_CAPABILITIES: { - rRState = &gnssServiceContext->getCapabilities; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->getCapabilities; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceGetCapabilities(gnssServiceContext, rxHeader); break; } case CHPP_GNSS_CONTROL_LOCATION_SESSION: { - rRState = &gnssServiceContext->controlLocationSession; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->controlLocationSession; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceControlLocationSession(gnssServiceContext, rxHeader, buf, len); break; } case CHPP_GNSS_CONTROL_MEASUREMENT_SESSION: { - rRState = &gnssServiceContext->controlMeasurementSession; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->controlMeasurementSession; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceControlMeasurementSession(gnssServiceContext, rxHeader, buf, len); break; } case CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER: { - rRState = &gnssServiceContext->configurePassiveLocationListener; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &gnssServiceContext->configurePassiveLocationListener; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppGnssServiceConfigurePassiveLocationListener( gnssServiceContext, rxHeader, buf, len); break; @@ -212,8 +212,8 @@ static enum ChppAppErrorCode chppDispatchGnssRequest(void *serviceContext, if (dispatched == true && error != CHPP_APP_ERROR_NONE) { // Request was dispatched but an error was returned. Close out - // chppServiceTimestampRequest() - chppServiceTimestampResponse(rRState); + // chppTimestampIncomingRequest() + chppTimestampOutgoingResponse(inReqState); } return error; @@ -258,14 +258,14 @@ static enum ChppAppErrorCode chppGnssServiceOpen( gnssServiceContext->service.openState = CHPP_OPEN_STATE_OPENED; struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&gnssServiceContext->service, + chppSendTimestampedResponseOrFail(gnssServiceContext->service.appContext, &gnssServiceContext->open, response, responseLen); } @@ -293,14 +293,14 @@ static enum ChppAppErrorCode chppGnssServiceClose( CHPP_LOGD("GNSS service closed"); struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&gnssServiceContext->service, + chppSendTimestampedResponseOrFail(gnssServiceContext->service.appContext, &gnssServiceContext->close, response, responseLen); } @@ -340,9 +340,8 @@ static enum ChppAppErrorCode chppGnssServiceGetCapabilities( struct ChppAppHeader *requestHeader) { enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; - struct ChppGnssGetCapabilitiesResponse *response = - chppAllocServiceResponseFixed(requestHeader, - struct ChppGnssGetCapabilitiesResponse); + struct ChppGnssGetCapabilitiesResponse *response = chppAllocResponseFixed( + requestHeader, struct ChppGnssGetCapabilitiesResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -354,7 +353,7 @@ static enum ChppAppErrorCode chppGnssServiceGetCapabilities( CHPP_LOGD("chppGnssServiceGetCapabilities returning 0x%" PRIx32 ", %" PRIuSIZE " bytes", response->params.capabilities, responseLen); - chppSendTimestampedResponseOrFail(&gnssServiceContext->service, + chppSendTimestampedResponseOrFail(gnssServiceContext->service.appContext, &gnssServiceContext->getCapabilities, response, responseLen); } @@ -472,7 +471,7 @@ static enum ChppAppErrorCode chppGnssServiceConfigurePassiveLocationListener( } else { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -480,7 +479,7 @@ static enum ChppAppErrorCode chppGnssServiceConfigurePassiveLocationListener( error = CHPP_APP_ERROR_OOM; } else { chppSendTimestampedResponseOrFail( - &gnssServiceContext->service, + gnssServiceContext->service.appContext, &gnssServiceContext->configurePassiveLocationListener, response, responseLen); } @@ -527,8 +526,8 @@ static void chppGnssServiceLocationStatusChangeCallback(bool enabled, }; struct ChppGnssControlLocationSessionResponse *response = - chppAllocServiceResponseFixed( - &requestHeader, struct ChppGnssControlLocationSessionResponse); + chppAllocResponseFixed(&requestHeader, + struct ChppGnssControlLocationSessionResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -540,7 +539,7 @@ static void chppGnssServiceLocationStatusChangeCallback(bool enabled, response->errorCode = errorCode; chppSendTimestampedResponseOrFail( - &gGnssServiceContext.service, + gGnssServiceContext.service.appContext, &gGnssServiceContext.controlLocationSession, response, responseLen); } } @@ -599,8 +598,8 @@ static void chppGnssServiceMeasurementStatusChangeCallback(bool enabled, }; struct ChppGnssControlMeasurementSessionResponse *response = - chppAllocServiceResponseFixed( - &requestHeader, struct ChppGnssControlMeasurementSessionResponse); + chppAllocResponseFixed(&requestHeader, + struct ChppGnssControlMeasurementSessionResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -612,7 +611,7 @@ static void chppGnssServiceMeasurementStatusChangeCallback(bool enabled, response->errorCode = errorCode; chppSendTimestampedResponseOrFail( - &gGnssServiceContext.service, + gGnssServiceContext.service.appContext, &gGnssServiceContext.controlMeasurementSession, response, responseLen); } } @@ -670,7 +669,8 @@ void chppRegisterGnssService(struct ChppAppState *appContext) { } else { chppRegisterService(appContext, (void *)&gGnssServiceContext, - &gGnssServiceContext.service, &kGnssServiceConfig); + &gGnssServiceContext.service, NULL /*outReqState*/, + &kGnssServiceConfig); CHPP_DEBUG_ASSERT(gGnssServiceContext.service.handle); } } diff --git a/chpp/services/timesync.c b/chpp/services/timesync.c index c81187ab..b7ad6259 100644 --- a/chpp/services/timesync.c +++ b/chpp/services/timesync.c @@ -39,7 +39,7 @@ static void chppTimesyncGetTime(struct ChppAppState *context, const struct ChppAppHeader *requestHeader) { struct ChppTimesyncResponse *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppTimesyncResponse); + chppAllocResponseFixed(requestHeader, struct ChppTimesyncResponse); size_t responseLen = sizeof(*response); if (response == NULL) { diff --git a/chpp/services/wifi.c b/chpp/services/wifi.c index 966e77f9..090e599d 100644 --- a/chpp/services/wifi.c +++ b/chpp/services/wifi.c @@ -73,22 +73,22 @@ static const struct ChppService kWifiServiceConfig = { * (RR) functionality. */ struct ChppWifiServiceState { - struct ChppServiceState service; // WiFi service state + struct ChppEndpointState service; // CHPP service state const struct chrePalWifiApi *api; // WiFi PAL API // Based on chre/pal/wifi.h and chrePalWifiApi - struct ChppRequestResponseState open; // Service init state - struct ChppRequestResponseState close; // Service deinit state - struct ChppRequestResponseState getCapabilities; // Get Capabilities state - struct ChppRequestResponseState + struct ChppIncomingRequestState open; // Service init state + struct ChppIncomingRequestState close; // Service deinit state + struct ChppIncomingRequestState getCapabilities; // Get Capabilities state + struct ChppIncomingRequestState configureScanMonitorAsync; // Configure scan monitor state - struct ChppRequestResponseState requestScanAsync; // Request scan state - struct ChppRequestResponseState requestRangingAsync; // Request ranging state - struct ChppRequestResponseState + struct ChppIncomingRequestState requestScanAsync; // Request scan state + struct ChppIncomingRequestState requestRangingAsync; // Request ranging state + struct ChppIncomingRequestState requestNanSubscribe; // Request Nan Subscription state - struct ChppRequestResponseState + struct ChppIncomingRequestState requestNanSubscribeCancel; // Request Nan Subscription cancelation state - struct ChppRequestResponseState + struct ChppIncomingRequestState requestNanRangingAsync; // Request NAN ranging state }; @@ -174,75 +174,75 @@ static enum ChppAppErrorCode chppDispatchWifiRequest(void *serviceContext, struct ChppWifiServiceState *wifiServiceContext = (struct ChppWifiServiceState *)serviceContext; - struct ChppRequestResponseState *rRState = NULL; + struct ChppIncomingRequestState *inReqState = NULL; enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; bool dispatched = true; switch (rxHeader->command) { case CHPP_WIFI_OPEN: { - rRState = &wifiServiceContext->open; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->open; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceOpen(wifiServiceContext, rxHeader); break; } case CHPP_WIFI_CLOSE: { - rRState = &wifiServiceContext->close; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->close; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceClose(wifiServiceContext, rxHeader); break; } case CHPP_WIFI_GET_CAPABILITIES: { - rRState = &wifiServiceContext->getCapabilities; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->getCapabilities; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceGetCapabilities(wifiServiceContext, rxHeader); break; } case CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC: { - rRState = &wifiServiceContext->configureScanMonitorAsync; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->configureScanMonitorAsync; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceConfigureScanMonitorAsync(wifiServiceContext, rxHeader, buf, len); break; } case CHPP_WIFI_REQUEST_SCAN_ASYNC: { - rRState = &wifiServiceContext->requestScanAsync; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->requestScanAsync; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceRequestScanAsync(wifiServiceContext, rxHeader, buf, len); break; } case CHPP_WIFI_REQUEST_RANGING_ASYNC: { - rRState = &wifiServiceContext->requestRangingAsync; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->requestRangingAsync; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceRequestRangingAsync(wifiServiceContext, rxHeader, buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB: { - rRState = &wifiServiceContext->requestNanSubscribe; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->requestNanSubscribe; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceRequestNanSubscribe(wifiServiceContext, rxHeader, buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB_CANCEL: { - rRState = &wifiServiceContext->requestNanSubscribeCancel; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->requestNanSubscribeCancel; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceRequestNanSubscribeCancel(wifiServiceContext, rxHeader, buf, len); break; }; case CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC: { - rRState = &wifiServiceContext->requestNanRangingAsync; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wifiServiceContext->requestNanRangingAsync; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWifiServiceRequestNanRanging(wifiServiceContext, rxHeader, buf, len); break; @@ -257,8 +257,8 @@ static enum ChppAppErrorCode chppDispatchWifiRequest(void *serviceContext, if (dispatched == true && error != CHPP_APP_ERROR_NONE) { // Request was dispatched but an error was returned. Close out - // chppServiceTimestampRequest() - chppServiceTimestampResponse(rRState); + // chppTimestampIncomingRequest() + chppTimestampOutgoingResponse(inReqState); } return error; @@ -307,14 +307,14 @@ static enum ChppAppErrorCode chppWifiServiceOpen( wifiServiceContext->service.openState = CHPP_OPEN_STATE_OPENED; struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&wifiServiceContext->service, + chppSendTimestampedResponseOrFail(wifiServiceContext->service.appContext, &wifiServiceContext->open, response, responseLen); } @@ -342,14 +342,14 @@ static enum ChppAppErrorCode chppWifiServiceClose( CHPP_LOGD("WiFi service closed"); struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&wifiServiceContext->service, + chppSendTimestampedResponseOrFail(wifiServiceContext->service.appContext, &wifiServiceContext->close, response, responseLen); } @@ -388,9 +388,8 @@ static enum ChppAppErrorCode chppWifiServiceGetCapabilities( struct ChppAppHeader *requestHeader) { enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; - struct ChppWifiGetCapabilitiesResponse *response = - chppAllocServiceResponseFixed(requestHeader, - struct ChppWifiGetCapabilitiesResponse); + struct ChppWifiGetCapabilitiesResponse *response = chppAllocResponseFixed( + requestHeader, struct ChppWifiGetCapabilitiesResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -402,7 +401,7 @@ static enum ChppAppErrorCode chppWifiServiceGetCapabilities( CHPP_LOGD("chppWifiServiceGetCapabilities returning 0x%" PRIx32 ", %" PRIuSIZE " bytes", response->params.capabilities, responseLen); - chppSendTimestampedResponseOrFail(&wifiServiceContext->service, + chppSendTimestampedResponseOrFail(wifiServiceContext->service.appContext, &wifiServiceContext->getCapabilities, response, responseLen); } @@ -532,7 +531,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestRangingAsync( } else { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -540,7 +539,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestRangingAsync( error = CHPP_APP_ERROR_OOM; } else { chppSendTimestampedResponseOrFail( - &wifiServiceContext->service, + wifiServiceContext->service.appContext, &wifiServiceContext->requestRangingAsync, response, responseLen); } } @@ -575,7 +574,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestNanSubscribe( } else { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -583,7 +582,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestNanSubscribe( error = CHPP_APP_ERROR_OOM; } else { chppSendTimestampedResponseOrFail( - &wifiServiceContext->service, + wifiServiceContext->service.appContext, &wifiServiceContext->requestNanSubscribe, response, responseLen); } } @@ -610,7 +609,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestNanSubscribeCancel( } else { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -618,7 +617,7 @@ static enum ChppAppErrorCode chppWifiServiceRequestNanSubscribeCancel( error = CHPP_APP_ERROR_OOM; } else { chppSendTimestampedResponseOrFail( - &wifiServiceContext->service, + wifiServiceContext->service.appContext, &wifiServiceContext->requestNanSubscribeCancel, response, responseLen); } @@ -647,7 +646,7 @@ static bool chppWifiServiceRequestNanRanging( } else { struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -655,7 +654,7 @@ static bool chppWifiServiceRequestNanRanging( error = CHPP_APP_ERROR_OOM; } else { chppSendTimestampedResponseOrFail( - &wifiServiceContext->service, + wifiServiceContext->service.appContext, &wifiServiceContext->requestNanRangingAsync, response, responseLen); } } @@ -680,8 +679,8 @@ static void chppWifiServiceScanMonitorStatusChangeCallback(bool enabled, }; struct ChppWifiConfigureScanMonitorAsyncResponse *response = - chppAllocServiceResponseFixed( - &requestHeader, struct ChppWifiConfigureScanMonitorAsyncResponse); + chppAllocResponseFixed(&requestHeader, + struct ChppWifiConfigureScanMonitorAsyncResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -693,7 +692,7 @@ static void chppWifiServiceScanMonitorStatusChangeCallback(bool enabled, response->params.errorCode = errorCode; chppSendTimestampedResponseOrFail( - &gWifiServiceContext.service, + gWifiServiceContext.service.appContext, &gWifiServiceContext.configureScanMonitorAsync, response, responseLen); } } @@ -713,7 +712,7 @@ static void chppWifiServiceScanResponseCallback(bool pending, .command = CHPP_WIFI_REQUEST_SCAN_ASYNC, }; - struct ChppWifiRequestScanResponse *response = chppAllocServiceResponseFixed( + struct ChppWifiRequestScanResponse *response = chppAllocResponseFixed( &requestHeader, struct ChppWifiRequestScanResponse); size_t responseLen = sizeof(*response); @@ -725,7 +724,7 @@ static void chppWifiServiceScanResponseCallback(bool pending, response->params.pending = pending; response->params.errorCode = errorCode; - chppSendTimestampedResponseOrFail(&gWifiServiceContext.service, + chppSendTimestampedResponseOrFail(gWifiServiceContext.service.appContext, &gWifiServiceContext.requestScanAsync, response, responseLen); } @@ -1020,7 +1019,8 @@ void chppRegisterWifiService(struct ChppAppState *appContext) { } else { chppRegisterService(appContext, (void *)&gWifiServiceContext, - &gWifiServiceContext.service, &kWifiServiceConfig); + &gWifiServiceContext.service, NULL /*outReqStates*/, + &kWifiServiceConfig); CHPP_DEBUG_ASSERT(gWifiServiceContext.service.handle); } } diff --git a/chpp/services/wwan.c b/chpp/services/wwan.c index bd284039..7f173c82 100644 --- a/chpp/services/wwan.c +++ b/chpp/services/wwan.c @@ -74,13 +74,13 @@ static const struct ChppService kWwanServiceConfig = { * (RR) functionality. */ struct ChppWwanServiceState { - struct ChppServiceState service; // WWAN service state + struct ChppEndpointState service; // CHPP service state const struct chrePalWwanApi *api; // WWAN PAL API - struct ChppRequestResponseState open; // Service init state - struct ChppRequestResponseState close; // Service deinit state - struct ChppRequestResponseState getCapabilities; // Get Capabilities state - struct ChppRequestResponseState getCellInfoAsync; // Get CellInfo Async state + struct ChppIncomingRequestState open; // Service init state + struct ChppIncomingRequestState close; // Service deinit state + struct ChppIncomingRequestState getCapabilities; // Get Capabilities state + struct ChppIncomingRequestState getCellInfoAsync; // Get CellInfo Async state }; // Note: This global definition of gWwanServiceContext supports only one @@ -138,7 +138,7 @@ static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext, struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; struct ChppWwanServiceState *wwanServiceContext = (struct ChppWwanServiceState *)serviceContext; - struct ChppRequestResponseState *rRState = NULL; + struct ChppIncomingRequestState *inReqState = NULL; enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; bool dispatched = true; @@ -146,29 +146,29 @@ static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext, switch (rxHeader->command) { case CHPP_WWAN_OPEN: { - rRState = &wwanServiceContext->open; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wwanServiceContext->open; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWwanServiceOpen(wwanServiceContext, rxHeader); break; } case CHPP_WWAN_CLOSE: { - rRState = &wwanServiceContext->close; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wwanServiceContext->close; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWwanServiceClose(wwanServiceContext, rxHeader); break; } case CHPP_WWAN_GET_CAPABILITIES: { - rRState = &wwanServiceContext->getCapabilities; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wwanServiceContext->getCapabilities; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWwanServiceGetCapabilities(wwanServiceContext, rxHeader); break; } case CHPP_WWAN_GET_CELLINFO_ASYNC: { - rRState = &wwanServiceContext->getCellInfoAsync; - chppServiceTimestampRequest(rRState, rxHeader); + inReqState = &wwanServiceContext->getCellInfoAsync; + chppTimestampIncomingRequest(inReqState, rxHeader); error = chppWwanServiceGetCellInfoAsync(wwanServiceContext, rxHeader); break; } @@ -182,8 +182,8 @@ static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext, if (dispatched == true && error != CHPP_APP_ERROR_NONE) { // Request was dispatched but an error was returned. Close out - // chppServiceTimestampRequest() - chppServiceTimestampResponse(rRState); + // chppTimestampIncomingRequest() + chppTimestampOutgoingResponse(inReqState); } return error; @@ -222,14 +222,14 @@ static enum ChppAppErrorCode chppWwanServiceOpen( wwanServiceContext->service.openState = CHPP_OPEN_STATE_OPENED; struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&wwanServiceContext->service, + chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext, &wwanServiceContext->open, response, responseLen); } @@ -257,14 +257,14 @@ static enum ChppAppErrorCode chppWwanServiceClose( CHPP_LOGD("WWAN service closed"); struct ChppAppHeader *response = - chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); + chppAllocResponseFixed(requestHeader, struct ChppAppHeader); size_t responseLen = sizeof(*response); if (response == NULL) { CHPP_LOG_OOM(); error = CHPP_APP_ERROR_OOM; } else { - chppSendTimestampedResponseOrFail(&wwanServiceContext->service, + chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext, &wwanServiceContext->close, response, responseLen); } @@ -304,9 +304,8 @@ static enum ChppAppErrorCode chppWwanServiceGetCapabilities( struct ChppAppHeader *requestHeader) { enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; - struct ChppWwanGetCapabilitiesResponse *response = - chppAllocServiceResponseFixed(requestHeader, - struct ChppWwanGetCapabilitiesResponse); + struct ChppWwanGetCapabilitiesResponse *response = chppAllocResponseFixed( + requestHeader, struct ChppWwanGetCapabilitiesResponse); size_t responseLen = sizeof(*response); if (response == NULL) { @@ -318,7 +317,7 @@ static enum ChppAppErrorCode chppWwanServiceGetCapabilities( CHPP_LOGD("chppWwanServiceGetCapabilities returning 0x%" PRIx32 ", %" PRIuSIZE " bytes", response->params.capabilities, responseLen); - chppSendTimestampedResponseOrFail(&wwanServiceContext->service, + chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext, &wwanServiceContext->getCapabilities, response, responseLen); } @@ -366,17 +365,17 @@ static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync( static void chppWwanServiceCellInfoResultCallback( struct chreWwanCellInfoResult *result) { // Recover state - struct ChppRequestResponseState *rRState = + struct ChppIncomingRequestState *inReqState = &gWwanServiceContext.getCellInfoAsync; struct ChppWwanServiceState *wwanServiceContext = - container_of(rRState, struct ChppWwanServiceState, getCellInfoAsync); + container_of(inReqState, struct ChppWwanServiceState, getCellInfoAsync); // Craft response per parser script struct ChppWwanCellInfoResultWithHeader *response = NULL; size_t responseLen = 0; if (!chppWwanCellInfoResultFromChre(result, &response, &responseLen)) { CHPP_LOGE("CellInfo conversion failed (OOM?) ID=%" PRIu8, - rRState->transaction); + inReqState->transaction); response = chppMalloc(sizeof(struct ChppAppHeader)); if (response == NULL) { @@ -389,14 +388,14 @@ static void chppWwanServiceCellInfoResultCallback( if (response != NULL) { response->header.handle = wwanServiceContext->service.handle; response->header.type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE; - response->header.transaction = rRState->transaction; + response->header.transaction = inReqState->transaction; response->header.error = (responseLen > sizeof(struct ChppAppHeader)) ? CHPP_APP_ERROR_NONE : CHPP_APP_ERROR_CONVERSION_FAILED; response->header.command = CHPP_WWAN_GET_CELLINFO_ASYNC; - chppSendTimestampedResponseOrFail(&wwanServiceContext->service, rRState, - response, responseLen); + chppSendTimestampedResponseOrFail(wwanServiceContext->service.appContext, + inReqState, response, responseLen); } gWwanServiceContext.api->releaseCellInfoResult(result); @@ -415,7 +414,8 @@ void chppRegisterWwanService(struct ChppAppState *appContext) { } else { chppRegisterService(appContext, (void *)&gWwanServiceContext, - &gWwanServiceContext.service, &kWwanServiceConfig); + &gWwanServiceContext.service, NULL /*outReqStates*/, + &kWwanServiceConfig); CHPP_DEBUG_ASSERT(gWwanServiceContext.service.handle); } } diff --git a/chpp/test/app_discovery_test.cpp b/chpp/test/app_discovery_test.cpp new file mode 100644 index 00000000..51ab800c --- /dev/null +++ b/chpp/test/app_discovery_test.cpp @@ -0,0 +1,332 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <cstring> +#include <thread> + +#include "chpp/app.h" +#include "chpp/clients.h" +#include "chpp/clients/discovery.h" +#include "chpp/macros.h" +#include "chpp/platform/platform_link.h" +#include "chpp/platform/utils.h" +#include "chpp/services.h" +#include "chpp/transport.h" + +namespace chre { + +namespace { + +constexpr uint64_t kResetWaitTimeMs = 5000; +constexpr uint64_t kDiscoveryWaitTimeMs = 5000; + +void *workThread(void *transportState) { + ChppTransportState *state = static_cast<ChppTransportState *>(transportState); + + auto linkContext = + static_cast<struct ChppLinuxLinkState *>(state->linkContext); + + pthread_setname_np(pthread_self(), linkContext->workThreadName); + + chppWorkThreadStart(state); + + return nullptr; +} + +#define TEST_UUID \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x12 \ + } + +constexpr uint16_t kNumCommands = 1; + +struct ClientState { + struct ChppEndpointState chppClientState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; + bool resetNotified; + bool matchNotified; +}; + +void clientNotifyReset(void *clientState); +void clientNotifyMatch(void *clientState); +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion); +void clientDeinit(void *clientState); + +constexpr struct ChppClient kClient = { + .descriptor.uuid = TEST_UUID, + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = &clientNotifyReset, + .matchNotifierFunctionPtr = &clientNotifyMatch, + .responseDispatchFunctionPtr = nullptr, + .notificationDispatchFunctionPtr = nullptr, + .initFunctionPtr = &clientInit, + .deinitFunctionPtr = &clientDeinit, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +void clientNotifyReset(void *clientState) { + auto state = static_cast<struct ClientState *>(clientState); + state->resetNotified = true; +} + +void clientNotifyMatch(void *clientState) { + auto state = static_cast<struct ClientState *>(clientState); + state->matchNotified = true; +} + +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion) { + UNUSED_VAR(serviceVersion); + auto state = static_cast<struct ClientState *>(clientState); + state->chppClientState.openState = CHPP_OPEN_STATE_OPENED; + chppClientInit(&state->chppClientState, handle); + return true; +} + +void clientDeinit(void *clientState) { + auto state = static_cast<struct ClientState *>(clientState); + chppClientDeinit(&state->chppClientState); + state->chppClientState.openState = CHPP_OPEN_STATE_CLOSED; +} + +// Service +struct ServiceState { + struct ChppEndpointState chppServiceState; + struct ChppIncomingRequestState inReqStates[kNumCommands]; + bool resetNotified; +}; + +void serviceNotifyReset(void *serviceState) { + auto state = static_cast<struct ServiceState *>(serviceState); + state->resetNotified = true; +} + +const struct ChppService kService = { + .descriptor.uuid = TEST_UUID, + .descriptor.name = "Test", + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = &serviceNotifyReset, + .requestDispatchFunctionPtr = nullptr, + .notificationDispatchFunctionPtr = nullptr, + .minLength = sizeof(struct ChppAppHeader), +}; + +// Test Clients/Services discovery and matching. +class AppDiscoveryTest : public testing::Test { + protected: + void SetUp() { + chppClearTotalAllocBytes(); + memset(&mClientLinkContext, 0, sizeof(mClientLinkContext)); + memset(&mServiceLinkContext, 0, sizeof(mServiceLinkContext)); + + mServiceLinkContext.linkThreadName = "Host Link"; + mServiceLinkContext.workThreadName = "Host worker"; + mServiceLinkContext.isLinkActive = true; + mServiceLinkContext.remoteLinkState = &mClientLinkContext; + mServiceLinkContext.rxInRemoteEndpointWorker = false; + + mClientLinkContext.linkThreadName = "CHRE Link"; + mClientLinkContext.workThreadName = "CHRE worker"; + mClientLinkContext.isLinkActive = true; + mClientLinkContext.remoteLinkState = &mServiceLinkContext; + mClientLinkContext.rxInRemoteEndpointWorker = false; + + // No default clients/services. + struct ChppClientServiceSet set; + memset(&set, 0, sizeof(set)); + + const struct ChppLinkApi *linkApi = getLinuxLinkApi(); + + // Init client side. + chppTransportInit(&mClientTransportContext, &mClientAppContext, + &mClientLinkContext, linkApi); + chppAppInitWithClientServiceSet(&mClientAppContext, + &mClientTransportContext, set); + + // Init service side. + chppTransportInit(&mServiceTransportContext, &mServiceAppContext, + &mServiceLinkContext, linkApi); + chppAppInitWithClientServiceSet(&mServiceAppContext, + &mServiceTransportContext, set); + } + + void TearDown() { + chppWorkThreadStop(&mClientTransportContext); + chppWorkThreadStop(&mServiceTransportContext); + pthread_join(mClientWorkThread, NULL); + pthread_join(mServiceWorkThread, NULL); + + // Deinit client side. + chppAppDeinit(&mClientAppContext); + chppTransportDeinit(&mClientTransportContext); + + // Deinit service side. + chppAppDeinit(&mServiceAppContext); + chppTransportDeinit(&mServiceTransportContext); + + EXPECT_EQ(chppGetTotalAllocBytes(), 0); + } + + // Client side. + ChppLinuxLinkState mClientLinkContext = {}; + ChppTransportState mClientTransportContext = {}; + ChppAppState mClientAppContext = {}; + pthread_t mClientWorkThread; + ClientState mClientState; + + // Service side + ChppLinuxLinkState mServiceLinkContext = {}; + ChppTransportState mServiceTransportContext = {}; + ChppAppState mServiceAppContext = {}; + pthread_t mServiceWorkThread; + ServiceState mServiceState; +}; + +TEST_F(AppDiscoveryTest, workWhenThereIsNoService) { + // Register the client + memset(&mClientState, 0, sizeof(mClientState)); + chppRegisterClient(&mClientAppContext, &mClientState, + &mClientState.chppClientState, + &mClientState.outReqStates[0], &kClient); + + pthread_create(&mClientWorkThread, NULL, workThread, + &mClientTransportContext); + + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + + // Start the service thread (no service registered). + pthread_create(&mServiceWorkThread, NULL, workThread, + &mServiceTransportContext); + + mClientLinkContext.linkEstablished = true; + mServiceLinkContext.linkEstablished = true; + + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext, + kResetWaitTimeMs)); + + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mServiceAppContext, kDiscoveryWaitTimeMs)); + + EXPECT_FALSE(mClientState.resetNotified); + EXPECT_FALSE(mClientState.matchNotified); + EXPECT_EQ(mClientAppContext.discoveredServiceCount, 0); + EXPECT_EQ(mClientAppContext.matchedClientCount, 0); + EXPECT_EQ(mServiceAppContext.discoveredServiceCount, 0); + EXPECT_EQ(mServiceAppContext.matchedClientCount, 0); +} + +TEST_F(AppDiscoveryTest, servicesShouldBeDiscovered) { + // Start the client thread (no client registered). + pthread_create(&mClientWorkThread, NULL, workThread, + &mClientTransportContext); + + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + + // Register the service + memset(&mServiceState, 0, sizeof(mServiceState)); + chppRegisterService(&mServiceAppContext, &mServiceState, + &mServiceState.chppServiceState, NULL /*outReqStates*/, + &kService); + + pthread_create(&mServiceWorkThread, NULL, workThread, + &mServiceTransportContext); + + mClientLinkContext.linkEstablished = true; + mServiceLinkContext.linkEstablished = true; + + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext, + kResetWaitTimeMs)); + + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mServiceAppContext, kDiscoveryWaitTimeMs)); + + EXPECT_FALSE(mClientState.resetNotified); + EXPECT_TRUE(mServiceState.resetNotified); + EXPECT_FALSE(mClientState.matchNotified); + EXPECT_EQ(mClientAppContext.discoveredServiceCount, 1); + EXPECT_EQ(mClientAppContext.matchedClientCount, 0); + EXPECT_EQ(mServiceAppContext.discoveredServiceCount, 0); + EXPECT_EQ(mServiceAppContext.matchedClientCount, 0); +} + +TEST_F(AppDiscoveryTest, discoveredServiceShouldBeMatchedWithClients) { + // Register the client + memset(&mClientState, 0, sizeof(mClientState)); + chppRegisterClient(&mClientAppContext, &mClientState, + &mClientState.chppClientState, + &mClientState.outReqStates[0], &kClient); + + pthread_create(&mClientWorkThread, NULL, workThread, + &mClientTransportContext); + + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + + // Register the service + memset(&mServiceState, 0, sizeof(mServiceState)); + chppRegisterService(&mServiceAppContext, &mServiceState, + &mServiceState.chppServiceState, NULL /*outReqStates*/, + &kService); + + pthread_create(&mServiceWorkThread, NULL, workThread, + &mServiceTransportContext); + + mClientLinkContext.linkEstablished = true; + mServiceLinkContext.linkEstablished = true; + + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext, + kResetWaitTimeMs)); + + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mServiceAppContext, kDiscoveryWaitTimeMs)); + + EXPECT_FALSE(mClientState.resetNotified); + EXPECT_TRUE(mServiceState.resetNotified); + EXPECT_TRUE(mClientState.matchNotified); + EXPECT_EQ(mClientAppContext.discoveredServiceCount, 1); + EXPECT_EQ(mClientAppContext.matchedClientCount, 1); + EXPECT_TRUE(mClientState.chppClientState.initialized); + EXPECT_EQ(mServiceAppContext.discoveredServiceCount, 0); + EXPECT_EQ(mServiceAppContext.matchedClientCount, 0); +} + +} // namespace + +} // namespace chre
\ No newline at end of file diff --git a/chpp/test/app_notification_test.cpp b/chpp/test/app_notification_test.cpp new file mode 100644 index 00000000..6f1f557d --- /dev/null +++ b/chpp/test/app_notification_test.cpp @@ -0,0 +1,333 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <cstring> +#include <thread> + +#include "chpp/app.h" +#include "chpp/clients.h" +#include "chpp/clients/discovery.h" +#include "chpp/macros.h" +#include "chpp/notifier.h" +#include "chpp/platform/platform_link.h" +#include "chpp/platform/utils.h" +#include "chpp/services.h" +#include "chpp/transport.h" +#include "chre/util/enum.h" +#include "chre/util/time.h" + +namespace chre { + +namespace { + +constexpr uint64_t kResetWaitTimeMs = 5000; +constexpr uint64_t kDiscoveryWaitTimeMs = 5000; + +void *workThread(void *transportState) { + ChppTransportState *state = static_cast<ChppTransportState *>(transportState); + + auto linkContext = + static_cast<struct ChppLinuxLinkState *>(state->linkContext); + + pthread_setname_np(pthread_self(), linkContext->workThreadName); + + chppWorkThreadStart(state); + + return nullptr; +} + +#define TEST_UUID \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x12 \ + } + +enum class Commands : uint16_t { + kServiceNotification, + kClientNotification, +}; + +constexpr uint16_t kNumCommands = 1; + +struct ClientState { + struct ChppEndpointState chppClientState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; + bool serviceNotificationStatus; + struct ChppNotifier notifier; +}; + +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion); +void clientDeinit(void *clientState); +enum ChppAppErrorCode clientDispatchNotification(void *clientState, + uint8_t *buf, size_t len); +constexpr struct ChppClient kClient = { + .descriptor.uuid = TEST_UUID, + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = nullptr, + .matchNotifierFunctionPtr = nullptr, + .responseDispatchFunctionPtr = nullptr, + .notificationDispatchFunctionPtr = &clientDispatchNotification, + .initFunctionPtr = &clientInit, + .deinitFunctionPtr = &clientDeinit, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +// Called when a notification from a service is received. +enum ChppAppErrorCode clientDispatchNotification(void *clientState, + uint8_t *buf, size_t len) { + auto state = static_cast<struct ClientState *>(clientState); + + // The response is composed of the app header only. + if (len != sizeof(ChppAppHeader)) { + return CHPP_APP_ERROR_NONE; + } + + auto notification = reinterpret_cast<struct ChppAppHeader *>(buf); + + switch (notification->command) { + case asBaseType(Commands::kServiceNotification): + state->serviceNotificationStatus = + notification->error == CHPP_APP_ERROR_NONE; + chppNotifierSignal(&state->notifier, 1 /*signal*/); + return CHPP_APP_ERROR_NONE; + + default: + return CHPP_APP_ERROR_NONE; + } +} + +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion) { + UNUSED_VAR(serviceVersion); + auto state = static_cast<struct ClientState *>(clientState); + state->chppClientState.openState = CHPP_OPEN_STATE_OPENED; + chppClientInit(&state->chppClientState, handle); + return true; +} + +void clientDeinit(void *clientState) { + auto state = static_cast<struct ClientState *>(clientState); + chppClientDeinit(&state->chppClientState); + state->chppClientState.openState = CHPP_OPEN_STATE_CLOSED; +} + +// Service +struct ServiceState { + struct ChppEndpointState chppServiceState; + struct ChppIncomingRequestState inReqStates[kNumCommands]; + bool clientNotificationStatus; + struct ChppNotifier notifier; +}; + +// Called when a notification from a client is received. +enum ChppAppErrorCode serviceDispatchNotification(void *serviceState, + uint8_t *buf, size_t len) { + auto state = static_cast<struct ServiceState *>(serviceState); + + // The response is composed of the app header only. + if (len != sizeof(ChppAppHeader)) { + return CHPP_APP_ERROR_NONE; + } + + auto notification = reinterpret_cast<struct ChppAppHeader *>(buf); + + switch (notification->command) { + case asBaseType(Commands::kClientNotification): + state->clientNotificationStatus = + notification->error == CHPP_APP_ERROR_NONE; + chppNotifierSignal(&state->notifier, 1 /*signal*/); + return CHPP_APP_ERROR_NONE; + + default: + return CHPP_APP_ERROR_NONE; + } +} + +const struct ChppService kService = { + .descriptor.uuid = TEST_UUID, + .descriptor.name = "Test", + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = nullptr, + .requestDispatchFunctionPtr = nullptr, + .notificationDispatchFunctionPtr = &serviceDispatchNotification, + .minLength = sizeof(struct ChppAppHeader), +}; + +// Test notifications. +class AppNotificationTest : public testing::Test { + protected: + void SetUp() { + chppClearTotalAllocBytes(); + chppNotifierInit(&mClientState.notifier); + chppNotifierInit(&mServiceState.notifier); + memset(&mClientLinkContext, 0, sizeof(mClientLinkContext)); + memset(&mServiceLinkContext, 0, sizeof(mServiceLinkContext)); + + mServiceLinkContext.linkThreadName = "Host Link"; + mServiceLinkContext.workThreadName = "Host worker"; + mServiceLinkContext.isLinkActive = true; + mServiceLinkContext.remoteLinkState = &mClientLinkContext; + mServiceLinkContext.rxInRemoteEndpointWorker = false; + + mClientLinkContext.linkThreadName = "CHRE Link"; + mClientLinkContext.workThreadName = "CHRE worker"; + mClientLinkContext.isLinkActive = true; + mClientLinkContext.remoteLinkState = &mServiceLinkContext; + mClientLinkContext.rxInRemoteEndpointWorker = false; + + // No default clients/services. + struct ChppClientServiceSet set; + memset(&set, 0, sizeof(set)); + + const struct ChppLinkApi *linkApi = getLinuxLinkApi(); + + // Init client side. + chppTransportInit(&mClientTransportContext, &mClientAppContext, + &mClientLinkContext, linkApi); + chppAppInitWithClientServiceSet(&mClientAppContext, + &mClientTransportContext, set); + + // Init service side. + chppTransportInit(&mServiceTransportContext, &mServiceAppContext, + &mServiceLinkContext, linkApi); + chppAppInitWithClientServiceSet(&mServiceAppContext, + &mServiceTransportContext, set); + + BringUpClient(); + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + BringUpService(); + mClientLinkContext.linkEstablished = true; + mServiceLinkContext.linkEstablished = true; + + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs)); + EXPECT_TRUE(chppWaitForDiscoveryComplete(&mServiceAppContext, + kDiscoveryWaitTimeMs)); + } + + void BringUpClient() { + memset(&mClientState, 0, sizeof(mClientState)); + chppRegisterClient(&mClientAppContext, &mClientState, + &mClientState.chppClientState, + &mClientState.outReqStates[0], &kClient); + + pthread_create(&mClientWorkThread, NULL, workThread, + &mClientTransportContext); + } + + void BringUpService() { + memset(&mServiceState, 0, sizeof(mServiceState)); + chppRegisterService(&mServiceAppContext, &mServiceState, + &mServiceState.chppServiceState, NULL /*outReqStates*/, + &kService); + + pthread_create(&mServiceWorkThread, NULL, workThread, + &mServiceTransportContext); + } + + void TearDown() { + chppNotifierDeinit(&mClientState.notifier); + chppNotifierDeinit(&mServiceState.notifier); + chppWorkThreadStop(&mClientTransportContext); + chppWorkThreadStop(&mServiceTransportContext); + pthread_join(mClientWorkThread, NULL); + pthread_join(mServiceWorkThread, NULL); + + // Deinit client side. + chppAppDeinit(&mClientAppContext); + chppTransportDeinit(&mClientTransportContext); + + // Deinit service side. + chppAppDeinit(&mServiceAppContext); + chppTransportDeinit(&mServiceTransportContext); + + EXPECT_EQ(chppGetTotalAllocBytes(), 0); + } + + // Client side. + ChppLinuxLinkState mClientLinkContext = {}; + ChppTransportState mClientTransportContext = {}; + ChppAppState mClientAppContext = {}; + pthread_t mClientWorkThread; + ClientState mClientState; + + // Service side + ChppLinuxLinkState mServiceLinkContext = {}; + ChppTransportState mServiceTransportContext = {}; + ChppAppState mServiceAppContext = {}; + pthread_t mServiceWorkThread; + ServiceState mServiceState; +}; + +TEST_F(AppNotificationTest, serviceSendANotificationToClient) { + // Send a notification. + constexpr size_t notificationLen = sizeof(struct ChppAppHeader); + + struct ChppAppHeader *notification = + chppAllocServiceNotification(notificationLen); + ASSERT_NE(notification, nullptr); + notification->command = asBaseType(Commands::kServiceNotification); + notification->handle = mServiceState.chppServiceState.handle; + + mClientState.serviceNotificationStatus = false; + + EXPECT_TRUE(chppEnqueueTxDatagramOrFail(&mServiceTransportContext, + notification, notificationLen)); + + chppNotifierWait(&mClientState.notifier); + + EXPECT_TRUE(mClientState.serviceNotificationStatus); +} + +TEST_F(AppNotificationTest, clientSendANotificationToService) { + // Send a notification. + constexpr size_t notificationLen = sizeof(struct ChppAppHeader); + + struct ChppAppHeader *notification = + chppAllocClientNotification(notificationLen); + ASSERT_NE(notification, nullptr); + notification->command = asBaseType(Commands::kClientNotification); + notification->handle = mClientState.chppClientState.handle; + + mServiceState.clientNotificationStatus = false; + + EXPECT_TRUE(chppEnqueueTxDatagramOrFail(&mClientTransportContext, + notification, notificationLen)); + + chppNotifierWait(&mServiceState.notifier); + + EXPECT_TRUE(mServiceState.clientNotificationStatus); +} + +} // namespace + +} // namespace chre
\ No newline at end of file diff --git a/chpp/test/app_req_resp_test.cpp b/chpp/test/app_req_resp_test.cpp new file mode 100644 index 00000000..0bcf0578 --- /dev/null +++ b/chpp/test/app_req_resp_test.cpp @@ -0,0 +1,488 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <cstring> +#include <thread> + +#include "chpp/app.h" +#include "chpp/clients.h" +#include "chpp/clients/discovery.h" +#include "chpp/macros.h" +#include "chpp/notifier.h" +#include "chpp/platform/platform_link.h" +#include "chpp/platform/utils.h" +#include "chpp/services.h" +#include "chpp/transport.h" +#include "chre/util/enum.h" +#include "chre/util/time.h" + +namespace chre { +namespace { + +constexpr uint64_t kResetWaitTimeMs = 5000; +constexpr uint64_t kDiscoveryWaitTimeMs = 5000; + +void *workThread(void *transportState) { + ChppTransportState *state = static_cast<ChppTransportState *>(transportState); + + auto linkContext = + static_cast<struct ChppLinuxLinkState *>(state->linkContext); + + pthread_setname_np(pthread_self(), linkContext->workThreadName); + + chppWorkThreadStart(state); + + return nullptr; +} + +#define TEST_UUID \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x12 \ + } + +enum class Commands : uint16_t { + kOk, + kError, + kTimeout, + // Number of request, must stay last + kNumCommands, +}; + +constexpr uint16_t kNumCommands = asBaseType(Commands::kNumCommands); + +// Common code for the client and the service. + +struct CommonState { + bool okResponseStatus; + bool errorResponseStatus; + bool timeoutResponseStatus; + struct ChppNotifier notifier; +}; + +enum ChppAppErrorCode dispatchResponse( + struct ChppAppState *appState, + struct ChppOutgoingRequestState *outReqStates, struct CommonState *common, + struct ChppAppHeader *response, size_t len) { + // The response is composed of the app header only. + if (len != sizeof(ChppAppHeader)) { + return CHPP_APP_ERROR_NONE; + } + + switch (response->command) { + case asBaseType(Commands::kOk): + // The response for the kOk command should have a CHPP_APP_ERROR_NONE + // error. + common->okResponseStatus = chppTimestampIncomingResponse( + appState, &outReqStates[asBaseType(Commands::kOk)], response); + + common->okResponseStatus &= response->error == CHPP_APP_ERROR_NONE; + return CHPP_APP_ERROR_NONE; + + case asBaseType(Commands::kError): + // The response for the kError command should have a + // CHPP_APP_ERROR_UNSPECIFIED error. + common->errorResponseStatus = chppTimestampIncomingResponse( + appState, &outReqStates[asBaseType(Commands::kError)], response); + + common->errorResponseStatus &= + response->error == CHPP_APP_ERROR_UNSPECIFIED; + return CHPP_APP_ERROR_NONE; + + case asBaseType(Commands::kTimeout): + // The response for the kTimeout command should have a + // CHPP_APP_ERROR_TIMEOUT error. That response is generated by the app + // layer. + common->timeoutResponseStatus = chppTimestampIncomingResponse( + appState, &outReqStates[asBaseType(Commands::kTimeout)], response); + + common->timeoutResponseStatus &= + response->error == CHPP_APP_ERROR_TIMEOUT; + chppNotifierSignal(&common->notifier, 1 /*signal*/); + return CHPP_APP_ERROR_NONE; + + default: + return CHPP_APP_ERROR_NONE; + } +} + +enum ChppAppErrorCode dispatchRequest( + struct ChppAppState *appState, struct ChppIncomingRequestState *inReqStates, + struct ChppAppHeader *request, size_t len) { + // The request is composed of the app header only. + if (len != sizeof(ChppAppHeader)) { + return CHPP_APP_ERROR_NONE; + } + + switch (request->command) { + case asBaseType(Commands::kOk): { + // Return a response for the kOk command. + chppTimestampIncomingRequest(&inReqStates[asBaseType(Commands::kOk)], + request); + + struct ChppAppHeader *response = + chppAllocResponse(request, sizeof(ChppAppHeader)); + + chppSendTimestampedResponseOrFail(appState, + &inReqStates[asBaseType(Commands::kOk)], + response, sizeof(ChppAppHeader)); + return CHPP_APP_ERROR_NONE; + } + case asBaseType(Commands::kError): { + // Return a response with a CHPP_APP_ERROR_UNSPECIFIED error on kError + // command. + return CHPP_APP_ERROR_UNSPECIFIED; + } + + case asBaseType(Commands::kTimeout): { + // Do not send a response on kTimeout for the remote endpoint to timeout. + chppTimestampIncomingRequest(&inReqStates[asBaseType(Commands::kError)], + request); + + return CHPP_APP_ERROR_NONE; + } + + default: + return CHPP_APP_ERROR_NONE; + } +} + +// Client specific code. +struct ClientState { + struct ChppEndpointState chppClientState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; + struct ChppIncomingRequestState inReqStates[kNumCommands]; + struct CommonState common; +}; + +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion); +void clientDeinit(void *clientState); +enum ChppAppErrorCode clientDispatchResponse(void *clientState, uint8_t *buf, + size_t len); +enum ChppAppErrorCode clientDispatchRequest(void *clientState, uint8_t *buf, + size_t len); + +constexpr struct ChppClient kClient = { + .descriptor.uuid = TEST_UUID, + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = nullptr, + .matchNotifierFunctionPtr = nullptr, + .responseDispatchFunctionPtr = &clientDispatchResponse, + .notificationDispatchFunctionPtr = nullptr, + .requestDispatchFunctionPtr = &clientDispatchRequest, + .initFunctionPtr = &clientInit, + .deinitFunctionPtr = &clientDeinit, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +// Called when a response is received from the service. +enum ChppAppErrorCode clientDispatchResponse(void *clientState, uint8_t *buf, + size_t len) { + CHPP_NOT_NULL(clientState); + + auto state = static_cast<struct ClientState *>(clientState); + + return dispatchResponse(state->chppClientState.appContext, + state->outReqStates, &state->common, + reinterpret_cast<struct ChppAppHeader *>(buf), len); +} + +// Called when a request is received from the service. +enum ChppAppErrorCode clientDispatchRequest(void *clientState, uint8_t *buf, + size_t len) { + auto request = reinterpret_cast<struct ChppAppHeader *>(buf); + auto state = static_cast<struct ClientState *>(clientState); + + return dispatchRequest(state->chppClientState.appContext, state->inReqStates, + request, len); +} + +bool clientInit(void *clientState, uint8_t handle, + struct ChppVersion serviceVersion) { + UNUSED_VAR(serviceVersion); + auto state = static_cast<struct ClientState *>(clientState); + state->chppClientState.openState = CHPP_OPEN_STATE_OPENED; + chppClientInit(&state->chppClientState, handle); + return true; +} + +void clientDeinit(void *clientState) { + auto state = static_cast<struct ClientState *>(clientState); + chppClientDeinit(&state->chppClientState); + state->chppClientState.openState = CHPP_OPEN_STATE_CLOSED; +} + +// Service specific code. + +struct ServiceState { + struct ChppEndpointState chppServiceState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; + struct ChppIncomingRequestState inReqStates[kNumCommands]; + struct CommonState common; +}; + +// Called when a request is received from the client. +enum ChppAppErrorCode serviceDispatchRequest(void *serviceState, uint8_t *buf, + size_t len) { + auto request = reinterpret_cast<struct ChppAppHeader *>(buf); + auto state = static_cast<struct ServiceState *>(serviceState); + + return dispatchRequest(state->chppServiceState.appContext, state->inReqStates, + request, len); +} + +// Called when a response is received from the client. +enum ChppAppErrorCode serviceDispatchResponse(void *serviceState, uint8_t *buf, + size_t len) { + CHPP_NOT_NULL(serviceState); + + auto state = static_cast<struct ServiceState *>(serviceState); + + return dispatchResponse(state->chppServiceState.appContext, + state->outReqStates, &state->common, + reinterpret_cast<struct ChppAppHeader *>(buf), len); +} + +const struct ChppService kService = { + .descriptor.uuid = TEST_UUID, + .descriptor.name = "Test", + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .resetNotifierFunctionPtr = nullptr, + .requestDispatchFunctionPtr = &serviceDispatchRequest, + .notificationDispatchFunctionPtr = nullptr, + .responseDispatchFunctionPtr = &serviceDispatchResponse, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +/** + * Test requests and responses. + * + * The test parameter is: + * - CHPP_MESSAGE_TYPE_CLIENT_REQUEST for client side requests + * - CHPP_MESSAGE_TYPE_SERVICE_REQUEST for service side requests + */ +class AppReqRespParamTest : public testing::TestWithParam<ChppMessageType> { + protected: + void SetUp() { + chppClearTotalAllocBytes(); + chppNotifierInit(&mClientState.common.notifier); + chppNotifierInit(&mServiceState.common.notifier); + memset(&mClientLinkState, 0, sizeof(mClientLinkState)); + memset(&mServiceLinkState, 0, sizeof(mServiceLinkState)); + + mServiceLinkState.linkThreadName = "Service Link"; + mServiceLinkState.workThreadName = "Service worker"; + mServiceLinkState.isLinkActive = true; + mServiceLinkState.remoteLinkState = &mClientLinkState; + mServiceLinkState.rxInRemoteEndpointWorker = false; + + mClientLinkState.linkThreadName = "Client Link"; + mClientLinkState.workThreadName = "Client worker"; + mClientLinkState.isLinkActive = true; + mClientLinkState.remoteLinkState = &mServiceLinkState; + mClientLinkState.rxInRemoteEndpointWorker = false; + + // No default clients/services. + struct ChppClientServiceSet set; + memset(&set, 0, sizeof(set)); + + const struct ChppLinkApi *linkApi = getLinuxLinkApi(); + + // Init client side. + chppTransportInit(&mClientTransportState, &mClientAppState, + &mClientLinkState, linkApi); + chppAppInitWithClientServiceSet(&mClientAppState, &mClientTransportState, + set); + + // Init service side. + chppTransportInit(&mServiceTransportState, &mServiceAppState, + &mServiceLinkState, linkApi); + chppAppInitWithClientServiceSet(&mServiceAppState, &mServiceTransportState, + set); + + BringUpClient(); + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + BringUpService(); + mClientLinkState.linkEstablished = true; + mServiceLinkState.linkEstablished = true; + + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportState, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportState, + kResetWaitTimeMs)); + + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppState, kDiscoveryWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mServiceAppState, kDiscoveryWaitTimeMs)); + } + + void BringUpClient() { + memset(&mClientState, 0, sizeof(mClientState)); + chppRegisterClient(&mClientAppState, &mClientState, + &mClientState.chppClientState, + &mClientState.outReqStates[0], &kClient); + + pthread_create(&mClientWorkThread, NULL, workThread, + &mClientTransportState); + } + + void BringUpService() { + memset(&mServiceState, 0, sizeof(mServiceState)); + chppRegisterService(&mServiceAppState, &mServiceState, + &mServiceState.chppServiceState, + &mServiceState.outReqStates[0], &kService); + + pthread_create(&mServiceWorkThread, NULL, workThread, + &mServiceTransportState); + } + + void TearDown() { + chppNotifierDeinit(&mClientState.common.notifier); + chppNotifierDeinit(&mServiceState.common.notifier); + chppWorkThreadStop(&mClientTransportState); + chppWorkThreadStop(&mServiceTransportState); + pthread_join(mClientWorkThread, NULL); + pthread_join(mServiceWorkThread, NULL); + + // Deinit client side. + chppAppDeinit(&mClientAppState); + chppTransportDeinit(&mClientTransportState); + + // Deinit service side. + chppAppDeinit(&mServiceAppState); + chppTransportDeinit(&mServiceTransportState); + + EXPECT_EQ(chppGetTotalAllocBytes(), 0); + } + + struct ChppAppHeader *AllocRequestCommand(Commands command) { + return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? chppAllocClientRequestCommand(&mClientState.chppClientState, + asBaseType(command)) + : chppAllocServiceRequestCommand(&mServiceState.chppServiceState, + asBaseType(command)); + } + + struct CommonState *GetCommonState() { + return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? &mClientState.common + : &mServiceState.common; + } + + bool SendTimestampedRequestAndWait(struct ChppAppHeader *request) { + constexpr size_t len = sizeof(struct ChppAppHeader); + if (request->type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + return chppClientSendTimestampedRequestAndWait( + &mClientState.chppClientState, + &mClientState.outReqStates[request->command], request, len); + } + + return chppServiceSendTimestampedRequestAndWait( + &mServiceState.chppServiceState, + &mServiceState.outReqStates[request->command], request, len); + } + + bool SendTimestampedRequestOrFail(struct ChppAppHeader *request, + uint64_t timeoutNs) { + constexpr size_t len = sizeof(struct ChppAppHeader); + if (request->type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + return chppClientSendTimestampedRequestOrFail( + &mClientState.chppClientState, + &mClientState.outReqStates[request->command], request, len, + timeoutNs); + } + + return chppServiceSendTimestampedRequestOrFail( + &mServiceState.chppServiceState, + &mServiceState.outReqStates[request->command], request, len, timeoutNs); + } + + // Client side. + ChppLinuxLinkState mClientLinkState = {}; + ChppTransportState mClientTransportState = {}; + ChppAppState mClientAppState = {}; + ClientState mClientState; + pthread_t mClientWorkThread; + + // Service side + ChppLinuxLinkState mServiceLinkState = {}; + ChppTransportState mServiceTransportState = {}; + ChppAppState mServiceAppState = {}; + ServiceState mServiceState = {}; + pthread_t mServiceWorkThread; +}; + +TEST_P(AppReqRespParamTest, sendsRequestAndReceiveResponse) { + struct ChppAppHeader *request = AllocRequestCommand(Commands::kOk); + ASSERT_NE(request, nullptr); + + GetCommonState()->okResponseStatus = false; + + EXPECT_TRUE(SendTimestampedRequestAndWait(request)); + + EXPECT_TRUE(GetCommonState()->okResponseStatus); +} + +TEST_P(AppReqRespParamTest, sendsRequestAndReceiveErrorResponse) { + struct ChppAppHeader *request = AllocRequestCommand(Commands::kError); + ASSERT_NE(request, nullptr); + + GetCommonState()->errorResponseStatus = false; + + EXPECT_TRUE(SendTimestampedRequestAndWait(request)); + + EXPECT_TRUE(GetCommonState()->errorResponseStatus); +} + +TEST_P(AppReqRespParamTest, sendsRequestAndReceiveTimeoutResponse) { + struct ChppAppHeader *request = AllocRequestCommand(Commands::kTimeout); + ASSERT_NE(request, nullptr); + + GetCommonState()->timeoutResponseStatus = false; + + EXPECT_TRUE( + SendTimestampedRequestOrFail(request, 10 * kOneMicrosecondInNanoseconds)); + + chppNotifierWait(&GetCommonState()->notifier); + + EXPECT_TRUE(GetCommonState()->timeoutResponseStatus); +} + +INSTANTIATE_TEST_SUITE_P( + AppReqRespTest, AppReqRespParamTest, + testing::Values(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, + CHPP_MESSAGE_TYPE_SERVICE_REQUEST), + [](const testing::TestParamInfo<AppReqRespParamTest::ParamType> &info) { + return info.param == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? "ClientRequests" + : "ServiceRequests"; + }); + +} // namespace +} // namespace chre
\ No newline at end of file diff --git a/chpp/test/app_test.cpp b/chpp/test/app_test.cpp index 84d826c3..fdc2f4bf 100644 --- a/chpp/test/app_test.cpp +++ b/chpp/test/app_test.cpp @@ -142,8 +142,12 @@ TEST_F(ChppAppTest, FragmentedLoopback) { } TEST_F(ChppAppTest, Timesync) { - constexpr uint64_t kMaxRtt = 2 * CHPP_NSEC_PER_MSEC; // in ms - constexpr int64_t kMaxOffset = 1 * CHPP_NSEC_PER_MSEC; // in ms + // Upper bound for the RTT (response received - request sent). + constexpr uint64_t kMaxRttNs = 20 * CHPP_NSEC_PER_MSEC; + // The offset is defined as (Time when the service sent the response) - (Time + // when the client got the response). + // We use half the RTT as the upper bound. + constexpr int64_t kMaxOffsetNs = kMaxRttNs / 2; CHPP_LOGI("Starting timesync test..."); @@ -154,11 +158,11 @@ TEST_F(ChppAppTest, Timesync) { EXPECT_EQ(chppTimesyncGetResult(&mClientAppContext)->error, CHPP_APP_ERROR_NONE); - EXPECT_LT(chppTimesyncGetResult(&mClientAppContext)->rttNs, kMaxRtt); + EXPECT_LT(chppTimesyncGetResult(&mClientAppContext)->rttNs, kMaxRttNs); EXPECT_NE(chppTimesyncGetResult(&mClientAppContext)->rttNs, 0); - EXPECT_LT(chppTimesyncGetResult(&mClientAppContext)->offsetNs, kMaxOffset); - EXPECT_GT(chppTimesyncGetResult(&mClientAppContext)->offsetNs, -kMaxOffset); + EXPECT_LT(chppTimesyncGetResult(&mClientAppContext)->offsetNs, kMaxOffsetNs); + EXPECT_GT(chppTimesyncGetResult(&mClientAppContext)->offsetNs, -kMaxOffsetNs); EXPECT_NE(chppTimesyncGetResult(&mClientAppContext)->offsetNs, 0); } diff --git a/chpp/test/app_test_base.cpp b/chpp/test/app_test_base.cpp index 907b15e4..1a9e3fc9 100644 --- a/chpp/test/app_test_base.cpp +++ b/chpp/test/app_test_base.cpp @@ -97,11 +97,17 @@ void AppTestBase::SetUp() { mClientLinkContext.linkEstablished = true; mServiceLinkContext.linkEstablished = true; - constexpr uint64_t kResetWaitTimeMs = 1500; - chppTransportWaitForResetComplete(&mClientTransportContext, kResetWaitTimeMs); + constexpr uint64_t kResetWaitTimeMs = 5000; + EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext, + kResetWaitTimeMs)); + EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext, + kResetWaitTimeMs)); constexpr uint64_t kDiscoveryWaitTimeMs = 5000; - chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs)); + EXPECT_TRUE( + chppWaitForDiscoveryComplete(&mServiceAppContext, kDiscoveryWaitTimeMs)); } void AppTestBase::TearDown() { diff --git a/chpp/test/app_timeout_test.cpp b/chpp/test/app_timeout_test.cpp new file mode 100644 index 00000000..8f155f22 --- /dev/null +++ b/chpp/test/app_timeout_test.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2021 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 "app_timeout_test.h" + +#include <gtest/gtest.h> +#include <string.h> +#include <cstdint> +#include <thread> + +#include "chpp/app.h" +#include "chpp/clients.h" +#include "chpp/clients/gnss.h" +#include "chpp/clients/wifi.h" +#include "chpp/clients/wwan.h" +#include "chpp/macros.h" +#include "chpp/memory.h" +#include "chpp/platform/platform_link.h" +#include "chpp/platform/utils.h" +#include "chpp/services.h" +#include "chpp/time.h" +#include "chpp/transport.h" +#include "chre/pal/wwan.h" + +namespace chre { +namespace { + +#define TEST_UUID \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x12 \ + } + +// Number of requests supported by the client and the service. +constexpr uint16_t kNumCommands = 3; + +struct ClientState { + struct ChppEndpointState chppClientState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; +}; + +constexpr struct ChppClient kClient = { + .descriptor.uuid = TEST_UUID, + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +struct ServiceState { + struct ChppEndpointState chppServiceState; + struct ChppOutgoingRequestState outReqStates[kNumCommands]; +}; + +const struct ChppService kService = { + .descriptor.uuid = TEST_UUID, + .descriptor.name = "Test", + .descriptor.version.major = 1, + .descriptor.version.minor = 0, + .descriptor.version.patch = 0, + .outReqCount = kNumCommands, + .minLength = sizeof(struct ChppAppHeader), +}; + +void ValidateClientStateAndReqState(struct ChppEndpointState *clientState, + const struct ChppAppHeader *request) { + ASSERT_NE(clientState, nullptr); + const uint8_t clientIdx = clientState->index; + + ASSERT_NE(clientState->appContext, nullptr); + ASSERT_NE(clientState->appContext->registeredClients, nullptr); + ASSERT_NE(clientState->appContext->registeredClients[clientIdx], nullptr); + ASSERT_LT(request->command, + clientState->appContext->registeredClients[clientIdx]->outReqCount); + ASSERT_NE(clientState->appContext->registeredClientStates[clientIdx], + nullptr); + ASSERT_NE( + clientState->appContext->registeredClientStates[clientIdx]->outReqStates, + nullptr); + ASSERT_NE(clientState->appContext->registeredClientStates[clientIdx]->context, + nullptr); +} + +void ValidateServiceStateAndReqState(struct ChppEndpointState *serviceState, + const struct ChppAppHeader *request) { + ASSERT_NE(serviceState, nullptr); + const uint8_t serviceIdx = serviceState->index; + + ASSERT_NE(serviceState->appContext, nullptr); + ASSERT_NE(serviceState->appContext->registeredServices, nullptr); + ASSERT_NE(serviceState->appContext->registeredServices[serviceIdx], nullptr); + ASSERT_LT( + request->command, + serviceState->appContext->registeredServices[serviceIdx]->outReqCount); + ASSERT_NE(serviceState->appContext->registeredServiceStates[serviceIdx], + nullptr); + ASSERT_NE(serviceState->appContext->registeredServiceStates[serviceIdx] + ->outReqStates, + nullptr); + ASSERT_NE( + serviceState->appContext->registeredServiceStates[serviceIdx]->context, + nullptr); +} + +void validateTimeout(uint64_t timeoutTimeNs, uint64_t expectedTimeNs) { + constexpr uint64_t kJitterNs = 10 * CHPP_NSEC_PER_MSEC; + + if (expectedTimeNs == CHPP_TIME_MAX) { + EXPECT_EQ(timeoutTimeNs, expectedTimeNs); + } else { + EXPECT_GE(timeoutTimeNs, expectedTimeNs); + EXPECT_LE(timeoutTimeNs, expectedTimeNs + kJitterNs); + } +} + +void validateTimeoutResponse(const struct ChppAppHeader *request, + const struct ChppAppHeader *response) { + ASSERT_NE(request, nullptr); + ASSERT_NE(response, nullptr); + + EXPECT_EQ(response->handle, request->handle); + + EXPECT_EQ(response->type, request->type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE + : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE); + EXPECT_EQ(response->transaction, request->transaction); + EXPECT_EQ(response->error, CHPP_APP_ERROR_TIMEOUT); + EXPECT_EQ(response->command, request->command); +} + +/** + * Test timeout for client and service side requests. + * + * The test parameter is: + * - CHPP_MESSAGE_TYPE_CLIENT_REQUEST for client side requests + * - CHPP_MESSAGE_TYPE_SERVICE_REQUEST for service side requests + */ +class TimeoutParamTest : public testing::TestWithParam<ChppMessageType> { + protected: + void SetUp() override { + chppClearTotalAllocBytes(); + + memset(&mClientLinkContext, 0, sizeof(mClientLinkContext)); + memset(&mServiceLinkContext, 0, sizeof(mServiceLinkContext)); + + mServiceLinkContext.isLinkActive = true; + mServiceLinkContext.remoteLinkState = &mClientLinkContext; + mServiceLinkContext.rxInRemoteEndpointWorker = false; + + mClientLinkContext.isLinkActive = true; + mClientLinkContext.remoteLinkState = &mServiceLinkContext; + mClientLinkContext.rxInRemoteEndpointWorker = false; + + // No default clients/services. + struct ChppClientServiceSet set; + memset(&set, 0, sizeof(set)); + + const struct ChppLinkApi *linkApi = getLinuxLinkApi(); + + // Init client side. + chppTransportInit(&mClientTransportContext, &mClientAppContext, + &mClientLinkContext, linkApi); + mClientTransportContext.resetState = CHPP_RESET_STATE_NONE; + chppAppInitWithClientServiceSet(&mClientAppContext, + &mClientTransportContext, set); + + // Init service side. + chppTransportInit(&mServiceTransportContext, &mServiceAppContext, + &mServiceLinkContext, linkApi); + mServiceTransportContext.resetState = CHPP_RESET_STATE_NONE; + chppAppInitWithClientServiceSet(&mServiceAppContext, + &mServiceTransportContext, set); + + // Bring up the client + memset(&mClientState, 0, sizeof(mClientState)); + chppRegisterClient(&mClientAppContext, &mClientState, + &mClientState.chppClientState, + &mClientState.outReqStates[0], &kClient); + + // Bring up the service + memset(&mServiceState, 0, sizeof(mServiceState)); + chppRegisterService(&mServiceAppContext, &mServiceState, + &mServiceState.chppServiceState, + &mServiceState.outReqStates[0], &kService); + + mClientLinkContext.linkEstablished = true; + mServiceLinkContext.linkEstablished = true; + + chppClientInit(&mClientState.chppClientState, + CHPP_HANDLE_NEGOTIATED_RANGE_START); + } + + void TearDown() override { + chppAppDeinit(&mClientAppContext); + chppTransportDeinit(&mClientTransportContext); + chppClientDeinit(&mClientState.chppClientState); + + chppAppDeinit(&mServiceAppContext); + chppTransportDeinit(&mServiceTransportContext); + + EXPECT_EQ(chppGetTotalAllocBytes(), 0); + } + + struct ChppAppHeader *AllocRequestCommand(uint16_t command) { + return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? chppAllocClientRequestCommand(&mClientState.chppClientState, + command) + : chppAllocServiceRequestCommand(&mServiceState.chppServiceState, + command); + } + + void TimestampOutgoingRequest(struct ChppAppHeader *request, + uint64_t timeoutNs) { + CHPP_NOT_NULL(request); + + const uint16_t command = request->command; + + if (GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + chppTimestampOutgoingRequest(&mClientAppContext, + &mClientState.outReqStates[command], request, + timeoutNs); + } else { + chppTimestampOutgoingRequest(&mServiceAppContext, + &mServiceState.outReqStates[command], + request, timeoutNs); + } + } + + bool TimestampIncomingResponse(struct ChppAppHeader *response) { + CHPP_NOT_NULL(response); + + const uint16_t command = response->command; + + if (GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + return chppTimestampIncomingResponse( + &mClientAppContext, &mClientState.outReqStates[command], response); + } + return chppTimestampIncomingResponse( + &mServiceAppContext, &mServiceState.outReqStates[command], response); + } + + uint64_t GetNextRequestTimeoutNs(void) { + return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? mClientAppContext.nextClientRequestTimeoutNs + : mServiceAppContext.nextServiceRequestTimeoutNs; + } + + struct ChppAppHeader *GetTimeoutResponse(void) { + return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST + ? chppTransportGetRequestTimeoutResponse( + &mClientTransportContext, CHPP_ENDPOINT_CLIENT) + : chppTransportGetRequestTimeoutResponse( + &mServiceTransportContext, CHPP_ENDPOINT_SERVICE); + } + + void ValidateRequestState(struct ChppAppHeader *request) { + CHPP_NOT_NULL(request); + if (GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) { + ValidateClientStateAndReqState(&mClientState.chppClientState, request); + } else { + ValidateServiceStateAndReqState(&mServiceState.chppServiceState, request); + } + } + + void RegisterAndValidateRequestForTimeout(struct ChppAppHeader *request, + uint64_t kTimeoutNs, + uint64_t expectedTimeNs) { + CHPP_NOT_NULL(request); + ValidateRequestState(request); + TimestampOutgoingRequest(request, kTimeoutNs); + + validateTimeout(GetNextRequestTimeoutNs(), expectedTimeNs); + } + + void RegisterAndValidateResponseForTimeout(struct ChppAppHeader *request, + uint64_t expectedTimeNs) { + CHPP_NOT_NULL(request); + + struct ChppAppHeader *response = + chppAllocResponse(request, sizeof(*request)); + + ValidateRequestState(request); + TimestampIncomingResponse(response); + + validateTimeout(GetNextRequestTimeoutNs(), expectedTimeNs); + + chppFree(response); + } + + // Client side. + ChppLinuxLinkState mClientLinkContext = {}; + ChppTransportState mClientTransportContext = {}; + ChppAppState mClientAppContext = {}; + ClientState mClientState; + + // Service side + ChppLinuxLinkState mServiceLinkContext = {}; + ChppTransportState mServiceTransportContext = {}; + ChppAppState mServiceAppContext = {}; + ServiceState mServiceState; +}; + +// Simulates a request and a response. +// There should be no error as the timeout is infinite. +TEST_P(TimeoutParamTest, RequestResponseTimestampValid) { + struct ChppAppHeader *request = AllocRequestCommand(0 /* command */); + ASSERT_NE(request, nullptr); + TimestampOutgoingRequest(request, CHPP_REQUEST_TIMEOUT_INFINITE); + + struct ChppAppHeader *response = chppAllocResponse(request, sizeof(*request)); + EXPECT_TRUE(TimestampIncomingResponse(response)); + + chppFree(request); + chppFree(response); +} + +// Simulates a single request with 2 responses. +TEST_P(TimeoutParamTest, RequestResponseTimestampDuplicate) { + struct ChppAppHeader *request = AllocRequestCommand(0 /* command */); + ASSERT_NE(request, nullptr); + TimestampOutgoingRequest(request, CHPP_REQUEST_TIMEOUT_INFINITE); + + struct ChppAppHeader *response = chppAllocResponse(request, sizeof(*request)); + + // The first response has no error. + EXPECT_TRUE(TimestampIncomingResponse(response)); + + // The second response errors as one response has already been received. + EXPECT_FALSE(TimestampIncomingResponse(response)); + + chppFree(request); + chppFree(response); +} + +// Simulates a response to a request that has not been timestamped. +TEST_P(TimeoutParamTest, RequestResponseTimestampInvalidId) { + constexpr uint16_t command = 0; + + struct ChppAppHeader *request1 = AllocRequestCommand(command); + ASSERT_NE(request1, nullptr); + TimestampOutgoingRequest(request1, CHPP_REQUEST_TIMEOUT_INFINITE); + + struct ChppAppHeader *request2 = AllocRequestCommand(command); + ASSERT_NE(request2, nullptr); + + // We expect a response for req but get a response for newReq. + // That is an error (the transaction does not match). + struct ChppAppHeader *response = + chppAllocResponse(request2, sizeof(*request1)); + EXPECT_FALSE(TimestampIncomingResponse(response)); + + chppFree(request1); + chppFree(request2); + chppFree(response); +} + +// Make sure the request does not timeout right away. +TEST_P(TimeoutParamTest, RequestTimeoutAddRemoveSingle) { + EXPECT_EQ(GetNextRequestTimeoutNs(), CHPP_TIME_MAX); + + struct ChppAppHeader *request = AllocRequestCommand(1 /* command */); + ASSERT_NE(request, nullptr); + + const uint64_t timeNs = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeoutNs = 1000 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request, kTimeoutNs, + timeNs + kTimeoutNs); + + // Timeout is not expired yet. + EXPECT_EQ(GetTimeoutResponse(), nullptr); + + RegisterAndValidateResponseForTimeout(request, CHPP_TIME_MAX); + + chppFree(request); +} + +TEST_P(TimeoutParamTest, RequestTimeoutAddRemoveMultiple) { + EXPECT_EQ(GetNextRequestTimeoutNs(), CHPP_TIME_MAX); + + struct ChppAppHeader *request1 = AllocRequestCommand(0 /* command */); + struct ChppAppHeader *request2 = AllocRequestCommand(1 /* command */); + struct ChppAppHeader *request3 = AllocRequestCommand(2 /* command */); + ASSERT_NE(request1, nullptr); + ASSERT_NE(request2, nullptr); + ASSERT_NE(request3, nullptr); + + // kTimeout1Ns is the smallest so it will be the first timeout to expire + // for all the requests. + const uint64_t time1Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout1Ns = 2000 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request1, kTimeout1Ns, + time1Ns + kTimeout1Ns); + + const uint64_t time2Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout2Ns = 4000 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request2, kTimeout2Ns, + time1Ns + kTimeout1Ns); + + const uint64_t time3Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout3Ns = 3000 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request3, kTimeout3Ns, + time1Ns + kTimeout1Ns); + + RegisterAndValidateResponseForTimeout(request1, time3Ns + kTimeout3Ns); + + // Timeout is not expired yet. + EXPECT_EQ(GetTimeoutResponse(), nullptr); + + // kTimeout4Ns is now the smallest timeout. + const uint64_t time4Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout4Ns = 1000 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request1, kTimeout4Ns, + time4Ns + kTimeout4Ns); + + RegisterAndValidateResponseForTimeout(request1, time3Ns + kTimeout3Ns); + + RegisterAndValidateResponseForTimeout(request3, time2Ns + kTimeout2Ns); + + RegisterAndValidateResponseForTimeout(request2, CHPP_TIME_MAX); + + EXPECT_EQ(GetTimeoutResponse(), nullptr); + + chppFree(request1); + chppFree(request2); + chppFree(request3); +} + +TEST_P(TimeoutParamTest, DuplicateRequestTimeoutResponse) { + // Sleep padding to make sure we timeout. + constexpr auto kTimeoutPadding = std::chrono::milliseconds(50); + + EXPECT_EQ(GetNextRequestTimeoutNs(), CHPP_TIME_MAX); + + struct ChppAppHeader *request = AllocRequestCommand(1 /* command */); + ASSERT_NE(request, nullptr); + + // Send the first request. + constexpr uint64_t kTimeout1Ns = 20 * CHPP_NSEC_PER_MSEC; + const uint64_t kShouldTimeout1AtNs = chppGetCurrentTimeNs() + kTimeout1Ns; + RegisterAndValidateRequestForTimeout(request, kTimeout1Ns, + kShouldTimeout1AtNs); + + // Override with a new request. + constexpr uint64_t kTimeout2Ns = 400 * CHPP_NSEC_PER_MSEC; + const uint64_t kShouldTimeout2AtNs = chppGetCurrentTimeNs() + kTimeout2Ns; + RegisterAndValidateRequestForTimeout(request, kTimeout2Ns, + kShouldTimeout2AtNs); + + std::this_thread::sleep_for( + std::chrono::nanoseconds(kShouldTimeout1AtNs - chppGetCurrentTimeNs()) + + kTimeoutPadding); + // First request would have timed out but superseded by second request. + EXPECT_GT(GetNextRequestTimeoutNs(), chppGetCurrentTimeNs()); + + std::this_thread::sleep_for( + std::chrono::nanoseconds(kShouldTimeout2AtNs - chppGetCurrentTimeNs()) + + kTimeoutPadding); + // Second request should have timed out - so we get a response. + EXPECT_LT(GetNextRequestTimeoutNs(), chppGetCurrentTimeNs()); + + struct ChppAppHeader *response = GetTimeoutResponse(); + ASSERT_NE(response, nullptr); + validateTimeoutResponse(request, response); + chppFree(response); + + RegisterAndValidateResponseForTimeout(request, CHPP_TIME_MAX); + EXPECT_EQ(GetTimeoutResponse(), nullptr); + + chppFree(request); +} + +TEST_P(TimeoutParamTest, RequestTimeoutResponse) { + EXPECT_EQ(GetNextRequestTimeoutNs(), CHPP_TIME_MAX); + + struct ChppAppHeader *request1 = AllocRequestCommand(1 /* command */); + struct ChppAppHeader *request2 = AllocRequestCommand(2 /* command */); + ASSERT_NE(request1, nullptr); + ASSERT_NE(request2, nullptr); + + const uint64_t time1Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout1Ns = 200 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request1, kTimeout1Ns, + time1Ns + kTimeout1Ns); + + std::this_thread::sleep_for(std::chrono::nanoseconds(kTimeout1Ns)); + ASSERT_LT(GetNextRequestTimeoutNs(), chppGetCurrentTimeNs()); + + // No response in time, we then get a timeout response. + struct ChppAppHeader *response = GetTimeoutResponse(); + validateTimeoutResponse(request1, response); + chppFree(response); + + RegisterAndValidateResponseForTimeout(request1, CHPP_TIME_MAX); + // No other request in timeout. + EXPECT_EQ(GetTimeoutResponse(), nullptr); + + // Simulate a new timeout and make sure we have a timeout response. + const uint64_t time2Ns = chppGetCurrentTimeNs(); + constexpr uint64_t kTimeout2Ns = 200 * CHPP_NSEC_PER_MSEC; + RegisterAndValidateRequestForTimeout(request2, kTimeout2Ns, + time2Ns + kTimeout2Ns); + + std::this_thread::sleep_for(std::chrono::nanoseconds(kTimeout2Ns)); + ASSERT_LT(GetNextRequestTimeoutNs(), chppGetCurrentTimeNs()); + + response = GetTimeoutResponse(); + validateTimeoutResponse(request2, response); + chppFree(response); + + chppFree(request1); + chppFree(request2); +} + +INSTANTIATE_TEST_SUITE_P( + TimeoutTest, TimeoutParamTest, + testing::Values(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, + CHPP_MESSAGE_TYPE_SERVICE_REQUEST), + [](const testing::TestParamInfo<TimeoutParamTest::ParamType> &info) { + return info.param == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? "ClientRequests" + : "ServiceRequests"; + }); + +} // namespace +} // namespace chre
\ No newline at end of file diff --git a/chpp/test/clients_test.h b/chpp/test/app_timeout_test.h index d7caa321..2b41d1ef 100644 --- a/chpp/test/clients_test.h +++ b/chpp/test/app_timeout_test.h @@ -28,8 +28,8 @@ extern "C" { * Functions necessary for unit testing ***********************************************/ -struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse( - struct ChppTransportState *context); +struct ChppAppHeader *chppTransportGetRequestTimeoutResponse( + struct ChppTransportState *context, enum ChppEndpointType type); #ifdef __cplusplus } diff --git a/chpp/test/clients_test.cpp b/chpp/test/clients_test.cpp deleted file mode 100644 index 5f69ec82..00000000 --- a/chpp/test/clients_test.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2021 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 "clients_test.h" - -#include <gtest/gtest.h> - -#include <string.h> -#include <thread> - -#include "chpp/app.h" -#include "chpp/clients.h" -#include "chpp/clients/gnss.h" -#include "chpp/clients/wifi.h" -#include "chpp/clients/wwan.h" -#include "chpp/macros.h" -#include "chpp/memory.h" -#include "chpp/platform/platform_link.h" -#include "chpp/platform/utils.h" -#include "chpp/services.h" -#include "chpp/time.h" -#include "chpp/transport.h" -#include "chre/pal/wwan.h" - -class ClientsTest : public testing::Test { - protected: - void SetUp() override { - chppClearTotalAllocBytes(); - - memset(&mAppContext, 0, sizeof(mAppContext)); - memset(&mTransportContext, 0, sizeof(mTransportContext)); - memset(&mLinkContext, 0, sizeof(mLinkContext)); - mLinkContext.linkEstablished = true; - - chppTransportInit(&mTransportContext, &mAppContext, &mLinkContext, - getLinuxLinkApi()); - chppAppInit(&mAppContext, &mTransportContext); - mClientState = - (struct ChppClientState *)mAppContext.registeredClientContexts[0]; - chppClientInit(mClientState, CHPP_HANDLE_NEGOTIATED_RANGE_START); - - mTransportContext.resetState = CHPP_RESET_STATE_NONE; - } - - void TearDown() override { - chppAppDeinit(&mAppContext); - chppTransportDeinit(&mTransportContext); - - EXPECT_EQ(chppGetTotalAllocBytes(), 0); - } - - struct ChppTransportState mTransportContext; - struct ChppAppState mAppContext; - struct ChppLinuxLinkState mLinkContext; - struct ChppClientState *mClientState; - struct ChppRequestResponseState mRRState; -}; - -void getClientRRStateInputCheck(struct ChppClientState *clientState, - struct ChppAppHeader *header) { - ASSERT_TRUE(clientState != NULL); - uint8_t clientIdx = clientState->index; - - ASSERT_TRUE(clientState->appContext != NULL); - ASSERT_TRUE(clientState->appContext->registeredClients != NULL); - ASSERT_TRUE(clientState->appContext->registeredClients[clientIdx] != NULL); - ASSERT_TRUE( - clientState->appContext->registeredClientStates[clientIdx]->rRStates != - NULL); - ASSERT_LT( - header->command, - clientState->appContext->registeredClients[clientIdx]->rRStateCount); -} - -struct ChppRequestResponseState *getClientRRState( - struct ChppClientState *clientState, struct ChppAppHeader *header) { - getClientRRStateInputCheck(clientState, header); - - uint8_t clientIdx = clientState->index; - return &(clientState->appContext->registeredClientStates[clientIdx] - ->rRStates[header->command]); -} - -void isTimeoutAsExpected(uint64_t timeoutTimeNs, uint64_t expectedTimeNs) { - uint64_t kJitterNs = 10 * CHPP_NSEC_PER_MSEC; - - if (expectedTimeNs == CHPP_TIME_MAX) { - EXPECT_EQ(timeoutTimeNs, expectedTimeNs); - } else { - EXPECT_GE(timeoutTimeNs, expectedTimeNs); - EXPECT_LE(timeoutTimeNs, expectedTimeNs + kJitterNs); - } -} - -void registerAndValidateRequestForTimeout(struct ChppClientState *clientState, - struct ChppAppHeader *header, - uint64_t timeoutNs, - uint64_t expectedTimeNs) { - struct ChppRequestResponseState *rRState = - getClientRRState(clientState, header); - chppClientTimestampRequest(clientState, rRState, header, timeoutNs); - - isTimeoutAsExpected(clientState->appContext->nextRequestTimeoutNs, - expectedTimeNs); -} - -void registerAndValidateResponseForTimeout(struct ChppClientState *clientState, - const struct ChppAppHeader *header, - uint64_t expectedTimeNs) { - ASSERT_FALSE(clientState == NULL); - uint8_t clientIdx = clientState->index; - - ASSERT_FALSE(clientState->appContext == NULL); - ASSERT_FALSE(clientState->appContext->registeredClients == NULL); - ASSERT_FALSE(clientState->appContext->registeredClients[clientIdx] == NULL); - ASSERT_FALSE( - clientState->appContext->registeredClientStates[clientIdx]->rRStates == - NULL); - ASSERT_LT( - header->command, - clientState->appContext->registeredClients[clientIdx]->rRStateCount); - - struct ChppRequestResponseState *rRState = - &(clientState->appContext->registeredClientStates[clientIdx] - ->rRStates[header->command]); - chppClientTimestampResponse(clientState, rRState, header); - - isTimeoutAsExpected(clientState->appContext->nextRequestTimeoutNs, - expectedTimeNs); -} - -void validateTimeoutResponse(const struct ChppAppHeader *request, - const struct ChppAppHeader *response) { - ASSERT_TRUE(request != NULL); - ASSERT_TRUE(response != NULL); - - EXPECT_EQ(response->handle, request->handle); - EXPECT_EQ(response->type, CHPP_MESSAGE_TYPE_SERVICE_RESPONSE); - EXPECT_EQ(response->transaction, request->transaction); - EXPECT_EQ(response->error, CHPP_APP_ERROR_TIMEOUT); - EXPECT_EQ(response->command, request->command); -} - -TEST_F(ClientsTest, RequestResponseTimestampValid) { - struct ChppAppHeader *reqHeader = - chppAllocClientRequestCommand(mClientState, 0 /* command */); - chppClientTimestampRequest(mClientState, &mRRState, reqHeader, - CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE); - - struct ChppAppHeader *respHeader = - chppAllocServiceResponse(reqHeader, sizeof(*reqHeader)); - ASSERT_TRUE(chppClientTimestampResponse(mClientState, &mRRState, respHeader)); - - chppFree(reqHeader); - chppFree(respHeader); -} - -TEST_F(ClientsTest, RequestResponseTimestampDuplicate) { - struct ChppAppHeader *reqHeader = - chppAllocClientRequestCommand(mClientState, 0 /* command */); - chppClientTimestampRequest(mClientState, &mRRState, reqHeader, - CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE); - - struct ChppAppHeader *respHeader = - chppAllocServiceResponse(reqHeader, sizeof(*reqHeader)); - ASSERT_TRUE(chppClientTimestampResponse(mClientState, &mRRState, respHeader)); - ASSERT_FALSE( - chppClientTimestampResponse(mClientState, &mRRState, respHeader)); - - chppFree(reqHeader); - chppFree(respHeader); -} - -TEST_F(ClientsTest, RequestResponseTimestampInvalidId) { - struct ChppAppHeader *reqHeader = - chppAllocClientRequestCommand(mClientState, 0 /* command */); - chppClientTimestampRequest(mClientState, &mRRState, reqHeader, - CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE); - - struct ChppAppHeader *newReqHeader = - chppAllocClientRequestCommand(mClientState, 0 /* command */); - struct ChppAppHeader *respHeader = - chppAllocServiceResponse(newReqHeader, sizeof(*reqHeader)); - ASSERT_FALSE( - chppClientTimestampResponse(mClientState, &mRRState, respHeader)); - - chppFree(reqHeader); - chppFree(newReqHeader); - chppFree(respHeader); -} - -TEST_F(ClientsTest, RequestTimeoutAddRemoveSingle) { - EXPECT_EQ(mAppContext.nextRequestTimeoutNs, CHPP_TIME_MAX); - - struct ChppAppHeader *reqHeader = - chppAllocClientRequestCommand(mClientState, 1 /* command */); - - uint64_t time = chppGetCurrentTimeNs(); - uint64_t timeout = 1000 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader, timeout, - time + timeout); - - EXPECT_TRUE( - chppTransportGetClientRequestTimeoutResponse(&mTransportContext) == NULL); - - registerAndValidateResponseForTimeout(mClientState, reqHeader, CHPP_TIME_MAX); - - chppFree(reqHeader); -} - -TEST_F(ClientsTest, RequestTimeoutAddRemoveMultiple) { - struct ChppAppHeader *reqHeader1 = - chppAllocClientRequestCommand(mClientState, 0 /* command */); - struct ChppAppHeader *reqHeader2 = - chppAllocClientRequestCommand(mClientState, 1 /* command */); - struct ChppAppHeader *reqHeader3 = - chppAllocClientRequestCommand(mClientState, 2 /* command */); - - EXPECT_EQ(mAppContext.nextRequestTimeoutNs, CHPP_TIME_MAX); - - uint64_t time1 = chppGetCurrentTimeNs(); - uint64_t timeout1 = 2000 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader1, timeout1, - time1 + timeout1); - - uint64_t time2 = chppGetCurrentTimeNs(); - uint64_t timeout2 = 4000 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader2, timeout2, - time1 + timeout1); - - uint64_t time3 = chppGetCurrentTimeNs(); - uint64_t timeout3 = 3000 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader3, timeout3, - time1 + timeout1); - - registerAndValidateResponseForTimeout(mClientState, reqHeader1, - time3 + timeout3); - - EXPECT_TRUE( - chppTransportGetClientRequestTimeoutResponse(&mTransportContext) == NULL); - - uint64_t time4 = chppGetCurrentTimeNs(); - uint64_t timeout4 = 1000 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader1, timeout4, - time4 + timeout4); - - registerAndValidateResponseForTimeout(mClientState, reqHeader1, - time3 + timeout3); - - registerAndValidateResponseForTimeout(mClientState, reqHeader3, - time2 + timeout2); - - registerAndValidateResponseForTimeout(mClientState, reqHeader2, - CHPP_TIME_MAX); - - EXPECT_TRUE( - chppTransportGetClientRequestTimeoutResponse(&mTransportContext) == NULL); - - chppFree(reqHeader1); - chppFree(reqHeader2); - chppFree(reqHeader3); -} - -TEST_F(ClientsTest, DuplicateRequestTimeoutResponse) { - EXPECT_EQ(mAppContext.nextRequestTimeoutNs, CHPP_TIME_MAX); - - struct ChppAppHeader *reqHeader = - chppAllocClientRequestCommand(mClientState, 1 /* command */); - - uint64_t time1 = chppGetCurrentTimeNs(); - uint64_t timeout1 = 200 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader, timeout1, - time1 + timeout1); - - std::this_thread::sleep_for(std::chrono::nanoseconds(timeout1 / 2)); - - uint64_t time2 = chppGetCurrentTimeNs(); - uint64_t timeout2 = 200 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader, timeout2, - time2 + timeout2); - - std::this_thread::sleep_for( - std::chrono::nanoseconds(timeout1 + time1 - chppGetCurrentTimeNs())); - // First request would have timed out but superseded by second request - ASSERT_GT(mAppContext.nextRequestTimeoutNs, chppGetCurrentTimeNs()); - - std::this_thread::sleep_for( - std::chrono::nanoseconds(timeout2 + time2 - chppGetCurrentTimeNs())); - // Second request should have timed out - ASSERT_LT(mAppContext.nextRequestTimeoutNs, chppGetCurrentTimeNs()); - - struct ChppAppHeader *response = - chppTransportGetClientRequestTimeoutResponse(&mTransportContext); - validateTimeoutResponse(reqHeader, response); - if (response != NULL) { - chppFree(response); - } - - registerAndValidateResponseForTimeout(mClientState, reqHeader, CHPP_TIME_MAX); - EXPECT_TRUE( - chppTransportGetClientRequestTimeoutResponse(&mTransportContext) == NULL); - - chppFree(reqHeader); -} - -TEST_F(ClientsTest, RequestTimeoutResponse) { - EXPECT_EQ(mAppContext.nextRequestTimeoutNs, CHPP_TIME_MAX); - - struct ChppAppHeader *reqHeader1 = - chppAllocClientRequestCommand(mClientState, 1 /* command */); - struct ChppAppHeader *reqHeader2 = - chppAllocClientRequestCommand(mClientState, 2 /* command */); - - uint64_t time1 = chppGetCurrentTimeNs(); - uint64_t timeout1 = 200 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader1, timeout1, - time1 + timeout1); - - std::this_thread::sleep_for(std::chrono::nanoseconds(timeout1)); - ASSERT_LT(mAppContext.nextRequestTimeoutNs, chppGetCurrentTimeNs()); - - struct ChppAppHeader *response = - chppTransportGetClientRequestTimeoutResponse(&mTransportContext); - validateTimeoutResponse(reqHeader1, response); - if (response != NULL) { - chppFree(response); - } - - registerAndValidateResponseForTimeout(mClientState, reqHeader1, - CHPP_TIME_MAX); - EXPECT_TRUE( - chppTransportGetClientRequestTimeoutResponse(&mTransportContext) == NULL); - - uint64_t time2 = chppGetCurrentTimeNs(); - uint64_t timeout2 = 200 * CHPP_NSEC_PER_MSEC; - registerAndValidateRequestForTimeout(mClientState, reqHeader2, timeout2, - time2 + timeout2); - - std::this_thread::sleep_for(std::chrono::nanoseconds(timeout2)); - ASSERT_LT(mAppContext.nextRequestTimeoutNs, chppGetCurrentTimeNs()); - - response = chppTransportGetClientRequestTimeoutResponse(&mTransportContext); - validateTimeoutResponse(reqHeader2, response); - if (response != NULL) { - chppFree(response); - } - - chppFree(reqHeader1); - chppFree(reqHeader2); -}
\ No newline at end of file diff --git a/chpp/test/fake_link_sync_test.cpp b/chpp/test/fake_link_sync_test.cpp index c4ec2a1a..f926dd6b 100644 --- a/chpp/test/fake_link_sync_test.cpp +++ b/chpp/test/fake_link_sync_test.cpp @@ -16,6 +16,7 @@ #include <gtest/gtest.h> +#include <string.h> #include <cstdint> #include <iostream> #include <thread> @@ -87,6 +88,7 @@ namespace chpp::test { class FakeLinkSyncTests : public testing::Test { protected: void SetUp() override { + memset(&mLinkContext, 0, sizeof(mLinkContext)); chppTransportInit(&mTransportContext, &mAppContext, &mLinkContext, &gLinkApi); chppAppInitWithClientServiceSet(&mAppContext, &mTransportContext, @@ -96,19 +98,24 @@ class FakeLinkSyncTests : public testing::Test { mWorkThread = std::thread(chppWorkThreadStart, &mTransportContext); // Proceed to the initialized state by performing the CHPP 3-way handshake + CHPP_LOGI("Send a RESET packet"); ASSERT_TRUE(mFakeLink->waitForTxPacket()); std::vector<uint8_t> resetPkt = mFakeLink->popTxPacket(); ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket())) << "Full packet: " << asResetPacket(resetPkt); + CHPP_LOGI("Receive a RESET ACK packet"); ChppResetPacket resetAck = generateResetAckPacket(); chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&resetAck), sizeof(resetAck)); + // chppProcessResetAck() results in sending a no error packet. + CHPP_LOGI("Send CHPP_TRANSPORT_ERROR_NONE packet"); ASSERT_TRUE(mFakeLink->waitForTxPacket()); std::vector<uint8_t> ackPkt = mFakeLink->popTxPacket(); ASSERT_TRUE(comparePacket(ackPkt, generateEmptyPacket())) << "Full packet: " << asChpp(ackPkt); + CHPP_LOGI("CHPP handshake complete"); } void TearDown() override { @@ -127,7 +134,7 @@ class FakeLinkSyncTests : public testing::Test { ChppTransportState mTransportContext = {}; ChppAppState mAppContext = {}; - ChppTestLinkState mLinkContext = {}; + ChppTestLinkState mLinkContext; FakeLink *mFakeLink; std::thread mWorkThread; }; @@ -139,6 +146,7 @@ TEST_F(FakeLinkSyncTests, CheckRetryOnTimeout) { std::vector<uint8_t> pkt1 = mFakeLink->popTxPacket(); + // Not calling chppRxDataCb() will result in a timeout. // Ideally, to speed up the test, we'd have a mechanism to trigger // chppNotifierWait() to return immediately, to simulate timeout ASSERT_TRUE(mFakeLink->waitForTxPacket()); diff --git a/chpp/test/transport_test.cpp b/chpp/test/transport_test.cpp index 2ef0bed0..f1defb39 100644 --- a/chpp/test/transport_test.cpp +++ b/chpp/test/transport_test.cpp @@ -83,7 +83,7 @@ class TransportTests : public testing::TestWithParam<int> { mTransportContext.resetState = CHPP_RESET_STATE_NONE; // Make sure CHPP has a correct count of the number of registered services - // on this platform, (in this case, 1,) as registered in the function + // on this platform as registered in the function // chppRegisterCommonServices(). ASSERT_EQ(mAppContext.registeredServiceCount, kServiceCount); } @@ -108,7 +108,7 @@ void WaitForTransport(struct ChppTransportState *transportContext) { // Start sending data out. cycleSendThread(); // Wait for data to be received and processed. - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Should have reset loc and length for next packet / datagram EXPECT_EQ(transportContext->rxStatus.locInDatagram, 0); diff --git a/chpp/transport.c b/chpp/transport.c index 14001cfe..53687c03 100644 --- a/chpp/transport.c +++ b/chpp/transport.c @@ -31,6 +31,7 @@ #include "chpp/macros.h" #include "chpp/memory.h" #include "chpp/platform/platform_link.h" +#include "chpp/services.h" #include "chpp/time.h" /************************************************ @@ -73,6 +74,7 @@ static struct ChppTransportHeader *chppAddHeader( struct ChppTransportState *context); static void chppAddPayload(struct ChppTransportState *context); static void chppAddFooter(struct ChppTransportState *context); +// Can not be static (used in tests). size_t chppDequeueTxDatagram(struct ChppTransportState *context); static void chppClearTxDatagramQueue(struct ChppTransportState *context); static void chppTransportDoWork(struct ChppTransportState *context); @@ -81,35 +83,53 @@ static void chppAppendToPendingTxPacket(struct ChppTransportState *context, static const char *chppGetPacketAttrStr(uint8_t packetCode); static bool chppEnqueueTxDatagram(struct ChppTransportState *context, uint8_t packetCode, void *buf, size_t len); -enum ChppLinkErrorCode chppSendPendingPacket( +static enum ChppLinkErrorCode chppSendPendingPacket( struct ChppTransportState *context); static void chppResetTransportContext(struct ChppTransportState *context); static void chppReset(struct ChppTransportState *context, enum ChppTransportPacketAttributes resetType, enum ChppTransportErrorCode error); -#ifdef CHPP_CLIENT_ENABLED -struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse( - struct ChppTransportState *context); -#endif +struct ChppAppHeader *chppTransportGetRequestTimeoutResponse( + struct ChppTransportState *context, enum ChppEndpointType type); +static const char *chppGetRxStatusLabel(enum ChppRxState state); +static void chppWorkHandleTimeout(struct ChppTransportState *context); /************************************************ * Private Functions ***********************************************/ +/** Returns a string representation of the passed ChppRxState */ +static const char *chppGetRxStatusLabel(enum ChppRxState state) { + switch (state) { + case CHPP_STATE_PREAMBLE: + return "PREAMBLE (0)"; + case CHPP_STATE_HEADER: + return "HEADER (1)"; + case CHPP_STATE_PAYLOAD: + return "PAYLOAD (2)"; + case CHPP_STATE_FOOTER: + return "FOOTER (3)"; + } + + return "invalid"; +} + /** * Called any time the Rx state needs to be changed. Ensures that the location * counter among that state (rxStatus.locInState) is also reset at the same * time. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param newState Next Rx state. */ static void chppSetRxState(struct ChppTransportState *context, enum ChppRxState newState) { - CHPP_LOGD("Changing RX transport state from %" PRIu8 " to %" PRIu8 - " after %" PRIuSIZE " bytes", - context->rxStatus.state, newState, context->rxStatus.locInState); + CHPP_LOGD( + "Changing RX transport state from %s to %s" + " after %" PRIuSIZE " bytes", + chppGetRxStatusLabel(context->rxStatus.state), + chppGetRxStatusLabel(newState), context->rxStatus.locInState); context->rxStatus.locInState = 0; context->rxStatus.state = newState; } @@ -122,7 +142,7 @@ static void chppSetRxState(struct ChppTransportState *context, * Any future backwards-incompatible versions of CHPP Transport will use a * different preamble. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param buf Input data. * @param len Length of input data in bytes. * @@ -169,7 +189,7 @@ static size_t chppConsumePreamble(struct ChppTransportState *context, * stream. * Moves the Rx state to CHPP_STATE_PAYLOAD afterwards. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param buf Input data. * @param len Length of input data in bytes. * @@ -235,7 +255,7 @@ static size_t chppConsumeHeader(struct ChppTransportState *context, * by the header, from the incoming data stream. * Moves the Rx state to CHPP_STATE_FOOTER afterwards. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param buf Input data * @param len Length of input data in bytes * @@ -264,7 +284,7 @@ static size_t chppConsumePayload(struct ChppTransportState *context, * stream. Checks checksum, triggering the correct response (ACK / NACK). * Moves the Rx state to CHPP_STATE_PREAMBLE afterwards. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param buf Input data. * @param len Length of input data in bytes. * @@ -361,7 +381,7 @@ static size_t chppConsumeFooter(struct ChppTransportState *context, * Discards of an incomplete Rx packet during receive (e.g. due to a timeout or * bad checksum). * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppAbortRxPacket(struct ChppTransportState *context) { size_t undoLen = 0; @@ -422,7 +442,7 @@ static void chppAbortRxPacket(struct ChppTransportState *context) { /** * Processes a request that is determined to be for a transport-layer loopback. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ #ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK static void chppProcessTransportLoopbackRequest( @@ -467,7 +487,7 @@ static void chppProcessTransportLoopbackRequest( /** * Processes a response that is determined to be for a transport-layer loopback. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK static void chppProcessTransportLoopbackResponse( @@ -504,7 +524,7 @@ static void chppProcessTransportLoopbackResponse( /** * Method to invoke when the reset sequence is completed. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppSetResetComplete(struct ChppTransportState *context) { context->resetState = CHPP_RESET_STATE_NONE; @@ -516,7 +536,7 @@ static void chppSetResetComplete(struct ChppTransportState *context) { * An incoming reset-ack packet indicates that a reset is complete at the other * end of the CHPP link. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppProcessResetAck(struct ChppTransportState *context) { if (context->resetState == CHPP_RESET_STATE_NONE) { @@ -564,7 +584,7 @@ static void chppProcessResetAck(struct ChppTransportState *context) { /** * Process a received, checksum-validated packet. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppProcessRxPacket(struct ChppTransportState *context) { uint64_t now = chppGetCurrentTimeNs(); @@ -611,7 +631,7 @@ static void chppProcessRxPacket(struct ChppTransportState *context) { * Process the payload of a validated payload-bearing packet and send out the * ACK. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppProcessRxPayload(struct ChppTransportState *context) { context->rxStatus.expectedSeq++; // chppProcessRxPacket() already confirms @@ -657,7 +677,7 @@ static void chppProcessRxPayload(struct ChppTransportState *context) { * layer to inform the transport layer using chppDatagramProcessDoneCb() once it * is done with the buffer so it is freed. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppClearRxDatagram(struct ChppTransportState *context) { context->rxStatus.locInDatagram = 0; @@ -668,7 +688,7 @@ static void chppClearRxDatagram(struct ChppTransportState *context) { /** * Validates the checksum of an incoming packet. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * * @return True if and only if the checksum is correct. */ @@ -696,7 +716,7 @@ static bool chppRxChecksumIsOk(const struct ChppTransportState *context) { * Performs consistency checks on received packet header to determine if it is * obviously corrupt / invalid / duplicate / out-of-order. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * * @return True if and only if header passes checks */ @@ -722,7 +742,7 @@ static enum ChppTransportErrorCode chppRxHeaderCheck( * Registers a received ACK. If an outgoing datagram is fully ACKed, it is * popped from the TX queue. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppRegisterRxAck(struct ChppTransportState *context) { uint8_t rxAckSeq = context->rxHeader.ackSeq; @@ -789,7 +809,7 @@ static void chppRegisterRxAck(struct ChppTransportState *context) { * would only need to send an ACK for the last (correct) packet, hence we only * need a queue length of one here. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param packetCode Error code and packet attributes to be sent. */ static void chppEnqueueTxPacket(struct ChppTransportState *context, @@ -820,7 +840,7 @@ static size_t chppAddPreamble(uint8_t *buf) { /** * Adds the packet header to link tx buffer. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * * @return Pointer to the added packet header. */ @@ -844,7 +864,7 @@ static struct ChppTransportHeader *chppAddHeader( /** * Adds the packet payload to link tx buffer. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppAddPayload(struct ChppTransportState *context) { uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext); @@ -884,7 +904,7 @@ static void chppAddPayload(struct ChppTransportState *context) { /** * Adds a footer (containing the checksum) to a packet. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppAddFooter(struct ChppTransportState *context) { struct ChppTransportFooter footer; @@ -906,7 +926,7 @@ static void chppAddFooter(struct ChppTransportState *context) { * Dequeues the datagram at the front of the datagram tx queue, if any, and * frees the payload. Returns the number of remaining datagrams in the queue. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @return Number of remaining datagrams in queue. */ size_t chppDequeueTxDatagram(struct ChppTransportState *context) { @@ -939,7 +959,7 @@ size_t chppDequeueTxDatagram(struct ChppTransportState *context) { /** * Flushes the Tx datagram queue of any pending packets. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppClearTxDatagramQueue(struct ChppTransportState *context) { while (context->txDatagramQueue.pending > 0) { @@ -959,7 +979,7 @@ static void chppClearTxDatagramQueue(struct ChppTransportState *context) { * Repeat payload: If we haven't received an ACK yet for our previous payload, * i.e. we have registered an explicit or implicit NACK. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppTransportDoWork(struct ChppTransportState *context) { bool havePacketForLinkLayer = false; @@ -1021,10 +1041,11 @@ static void chppTransportDoWork(struct ChppTransportState *context) { } else { CHPP_LOGW( "DoWork nothing to send. hasPackets=%d, linkBusy=%d, pending=%" PRIu8 - ", RX ACK=%" PRIu8 ", TX seq=%" PRIu8 ", RX state=%" PRIu8, + ", RX ACK=%" PRIu8 ", TX seq=%" PRIu8 ", RX state=%s", context->txStatus.hasPacketsToSend, context->txStatus.linkBusy, context->txDatagramQueue.pending, context->rxStatus.receivedAckSeq, - context->txStatus.sentSeq, context->rxStatus.state); + context->txStatus.sentSeq, + chppGetRxStatusLabel(context->rxStatus.state)); } chppMutexUnlock(&context->mutex); @@ -1049,7 +1070,7 @@ static void chppTransportDoWork(struct ChppTransportState *context) { #ifdef CHPP_CLIENT_ENABLED { // create a scope to declare timeoutResponse (C89). struct ChppAppHeader *timeoutResponse = - chppTransportGetClientRequestTimeoutResponse(context); + chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_CLIENT); if (timeoutResponse != NULL) { CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8, @@ -1060,13 +1081,27 @@ static void chppTransportDoWork(struct ChppTransportState *context) { } } #endif // CHPP_CLIENT_ENABLED +#ifdef CHPP_SERVICE_ENABLED + { // create a scope to declare timeoutResponse (C89). + struct ChppAppHeader *timeoutResponse = + chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_SERVICE); + + if (timeoutResponse != NULL) { + CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8, + timeoutResponse->handle, timeoutResponse->command, + timeoutResponse->transaction); + chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse, + sizeof(struct ChppAppHeader)); + } + } +#endif // CHPP_SERVICE_ENABLED } /** * Appends data from a buffer of length len to a link tx buffer, updating its * length. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param buf Input data to be copied from. * @param len Length of input data in bytes. */ @@ -1109,7 +1144,7 @@ static const char *chppGetPacketAttrStr(uint8_t packetCode) { * If enqueueing is unsuccessful, it is up to the caller to decide when or if * to free the payload and/or resend it later. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * @param packetCode Error code and packet attributes to be sent. * @param buf Datagram payload allocated through chppMalloc. Cannot be null. * @param len Datagram length in bytes. @@ -1171,11 +1206,11 @@ static bool chppEnqueueTxDatagram(struct ChppTransportState *context, * Sends the pending outgoing packet over to the link * layer using Send() and updates the last Tx packet time. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. * * @return Result of Send(). */ -enum ChppLinkErrorCode chppSendPendingPacket( +static enum ChppLinkErrorCode chppSendPendingPacket( struct ChppTransportState *context) { enum ChppLinkErrorCode error = context->linkApi->send(context->linkContext, context->linkBufferSize); @@ -1188,7 +1223,7 @@ enum ChppLinkErrorCode chppSendPendingPacket( /** * Resets the transport state, maintaining the link layer parameters. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. */ static void chppResetTransportContext(struct ChppTransportState *context) { memset(&context->rxStatus, 0, sizeof(struct ChppRxStatus)); @@ -1212,7 +1247,7 @@ static void chppResetTransportContext(struct ChppTransportState *context) { * This function retains and restores the platform-specific values of * transportContext.linkContext. * - * @param transportContext Maintains state for each transport layer instance. + * @param transportContext State of the transport layer. * @param resetType Type of reset to send after resetting CHPP (reset vs. * reset-ack), as defined in the ChppTransportPacketAttributes struct. * @param error Provides the error that led to the reset. @@ -1266,68 +1301,74 @@ static void chppReset(struct ChppTransportState *transportContext, } /** - * Checks for a timed out client request and generates a timeout response if a - * client request timeout has occurred. + * Checks for a timed out request and generates a timeout response if a timeout + * has occurred. * - * @param context Maintains state for each transport layer instance. + * @param context State of the transport layer. + * @param type The type of the endpoint. * @return App layer response header if a timeout has occurred. Null otherwise. */ -#ifdef CHPP_CLIENT_ENABLED -struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse( - struct ChppTransportState *context) { +struct ChppAppHeader *chppTransportGetRequestTimeoutResponse( + struct ChppTransportState *context, enum ChppEndpointType type) { + CHPP_DEBUG_NOT_NULL(context); + + struct ChppAppState *appState = context->appContext; struct ChppAppHeader *response = NULL; - bool timeoutClientFound = false; - uint8_t timedOutClient; + bool timeoutEndpointFound = false; + uint8_t timedOutEndpointIdx; uint16_t timedOutCmd; chppMutexLock(&context->mutex); - if (context->appContext->nextRequestTimeoutNs <= chppGetCurrentTimeNs()) { + if (*getNextRequestTimeoutNs(appState, type) <= chppGetCurrentTimeNs()) { // Determine which request has timed out - - uint64_t lowestTimeout = CHPP_TIME_MAX; - for (uint8_t clientIdx = 0; - clientIdx < context->appContext->registeredClientCount; clientIdx++) { - for (uint16_t cmdIdx = 0; - cmdIdx < - context->appContext->registeredClients[clientIdx]->rRStateCount; - cmdIdx++) { - struct ChppRequestResponseState *rRState = - &context->appContext->registeredClientStates[clientIdx] - ->rRStates[cmdIdx]; - - if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT && - rRState->responseTimeNs != CHPP_TIME_NONE && - rRState->responseTimeNs < lowestTimeout) { - lowestTimeout = rRState->responseTimeNs; - timedOutClient = clientIdx; + const uint8_t endpointCount = getRegisteredEndpointCount(appState, type); + uint64_t firstTimeout = CHPP_TIME_MAX; + + for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) { + const uint16_t cmdCount = + getRegisteredEndpointOutReqCount(appState, endpointIdx, type); + const struct ChppEndpointState *endpointState = + getRegisteredEndpointState(appState, endpointIdx, type); + const struct ChppOutgoingRequestState *reqStates = + &endpointState->outReqStates[0]; + for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) { + const struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx]; + + if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT && + reqState->responseTimeNs != CHPP_TIME_NONE && + reqState->responseTimeNs < firstTimeout) { + firstTimeout = reqState->responseTimeNs; + timedOutEndpointIdx = endpointIdx; timedOutCmd = cmdIdx; - timeoutClientFound = true; + timeoutEndpointFound = true; } } } - if (!timeoutClientFound) { - CHPP_LOGE("Timeout at %" PRIu64 " but no client", - context->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC); - chppClientRecalculateNextTimeout(context->appContext); + if (!timeoutEndpointFound) { + CHPP_LOGE("Timeout at %" PRIu64 " but no endpoint", + *getNextRequestTimeoutNs(appState, type) / CHPP_NSEC_PER_MSEC); + chppRecalculateNextTimeout(appState, CHPP_ENDPOINT_CLIENT); } } - if (timeoutClientFound) { - CHPP_LOGE("Client=%" PRIu8 " cmd=%" PRIu16 " timed out", timedOutClient, - timedOutCmd); + if (timeoutEndpointFound) { + CHPP_LOGE("Endpoint=%" PRIu8 " cmd=%" PRIu16 " timed out", + timedOutEndpointIdx, timedOutCmd); response = chppMalloc(sizeof(struct ChppAppHeader)); if (response == NULL) { CHPP_LOG_OOM(); } else { - response->handle = CHPP_SERVICE_HANDLE_OF_INDEX(timedOutClient); - response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE; + const struct ChppEndpointState *endpointState = + getRegisteredEndpointState(appState, timedOutEndpointIdx, type); + response->handle = endpointState->handle; + response->type = type == CHPP_ENDPOINT_CLIENT + ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE + : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE; response->transaction = - context->appContext->registeredClientStates[timedOutClient] - ->rRStates[timedOutCmd] - .transaction; + endpointState->outReqStates[timedOutCmd].transaction; response->error = CHPP_APP_ERROR_TIMEOUT; response->command = timedOutCmd; } @@ -1337,7 +1378,6 @@ struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse( return response; } -#endif /************************************************ * Public Functions @@ -1437,8 +1477,8 @@ bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf, } chppMutexUnlock(&context->mutex); - CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%" PRIu8, len, - context->rxStatus.state); + CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%s", len, + chppGetRxStatusLabel(context->rxStatus.state)); uint64_t now = chppGetCurrentTimeNs(); context->rxStatus.lastDataTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC); context->rxStatus.numTotalDataBytes += len; @@ -1485,10 +1525,9 @@ bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf, void chppRxPacketCompleteCb(struct ChppTransportState *context) { chppMutexLock(&context->mutex); if (context->rxStatus.state != CHPP_STATE_PREAMBLE) { - CHPP_LOGE("RX pkt ended early: state=%" PRIu8 " seq=%" PRIu8 - " len=%" PRIu16, - context->rxStatus.state, context->rxHeader.seq, - context->rxHeader.length); + CHPP_LOGE("RX pkt ended early: state=%s seq=%" PRIu8 " len=%" PRIu16, + chppGetRxStatusLabel(context->rxStatus.state), + context->rxHeader.seq, context->rxHeader.length); chppAbortRxPacket(context); chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_HEADER); // NACK } @@ -1548,7 +1587,12 @@ void chppEnqueueTxErrorDatagram(struct ChppTransportState *context, uint64_t chppTransportGetTimeUntilNextDoWorkNs( struct ChppTransportState *context) { uint64_t currentTime = chppGetCurrentTimeNs(); - uint64_t nextDoWorkTime = context->appContext->nextRequestTimeoutNs; + // This function is called in the context of the transport worker thread. + // As we do not know if the transport is used in the context of a service + // or a client, we use the min of both timeouts. + uint64_t nextDoWorkTime = + MIN(context->appContext->nextClientRequestTimeoutNs, + context->appContext->nextServiceRequestTimeoutNs); if (context->txStatus.hasPacketsToSend || context->resetState == CHPP_RESET_STATE_RESETTING) { @@ -1606,40 +1650,18 @@ bool chppWorkThreadHandleSignal(struct ChppTransportState *context, CHPP_LOGD("CHPP Work Thread terminated"); } else { continueProcessing = true; - if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) { - chppTransportDoWork(context); - } - if (signals == 0) { - // Triggered by timeout - - if (chppGetCurrentTimeNs() - context->txStatus.lastTxTimeNs >= - CHPP_TRANSPORT_TX_TIMEOUT_NS) { - CHPP_LOGE("ACK timeout. Tx t=%" PRIu64, - context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC); + // Triggered by timeout. + chppWorkHandleTimeout(context); + } else { + if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) { chppTransportDoWork(context); } - - if ((context->resetState == CHPP_RESET_STATE_RESETTING) && - (chppGetCurrentTimeNs() - context->resetTimeNs >= - CHPP_TRANSPORT_RESET_TIMEOUT_NS)) { - if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) { - CHPP_LOGE("RESET-ACK timeout; retrying"); - context->resetCount++; - chppReset(context, CHPP_TRANSPORT_ATTR_RESET, - CHPP_TRANSPORT_ERROR_TIMEOUT); - } else { - CHPP_LOGE("RESET-ACK timeout; giving up"); - context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE; - chppClearTxDatagramQueue(context); - } + if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) { + context->linkApi->doWork(context->linkContext, + signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK); } } - - if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) { - context->linkApi->doWork(context->linkContext, - signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK); - } } #ifdef CHPP_ENABLE_WORK_MONITOR @@ -1649,6 +1671,55 @@ bool chppWorkThreadHandleSignal(struct ChppTransportState *context, return continueProcessing; } +/** + * Handle timeouts in the worker thread. + * + * Timeouts occurs when either: + * 1. There are packets to send and last packet send was more than + * CHPP_TRANSPORT_TX_TIMEOUT_NS ago + * 2. We haven't received a response to a request in time + * 3. We haven't received the reset ACK + * + * For 1 and 2, chppTransportDoWork should be called to respectively + * - Transmit the packet + * - Send a timeout response + */ +static void chppWorkHandleTimeout(struct ChppTransportState *context) { + const uint64_t currentTimeNs = chppGetCurrentTimeNs(); + const bool isTxTimeout = currentTimeNs - context->txStatus.lastTxTimeNs >= + CHPP_TRANSPORT_TX_TIMEOUT_NS; + + // Call chppTransportDoWork for both TX and request timeouts. + if (isTxTimeout) { + CHPP_LOGE("ACK timeout. Tx t=%" PRIu64, + context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC); + chppTransportDoWork(context); + } else { + const uint64_t requestTimeoutNs = + MIN(context->appContext->nextClientRequestTimeoutNs, + context->appContext->nextServiceRequestTimeoutNs); + const bool isRequestTimeout = requestTimeoutNs <= currentTimeNs; + if (isRequestTimeout) { + chppTransportDoWork(context); + } + } + + if ((context->resetState == CHPP_RESET_STATE_RESETTING) && + (currentTimeNs - context->resetTimeNs >= + CHPP_TRANSPORT_RESET_TIMEOUT_NS)) { + if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) { + CHPP_LOGE("RESET-ACK timeout; retrying"); + context->resetCount++; + chppReset(context, CHPP_TRANSPORT_ATTR_RESET, + CHPP_TRANSPORT_ERROR_TIMEOUT); + } else { + CHPP_LOGE("RESET-ACK timeout; giving up"); + context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE; + chppClearTxDatagramQueue(context); + } + } +} + void chppWorkThreadStop(struct ChppTransportState *context) { chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EXIT); } diff --git a/chre_api/archive_chre_api.sh b/chre_api/archive_chre_api.sh index b6c36a50..2d23107e 100755 --- a/chre_api/archive_chre_api.sh +++ b/chre_api/archive_chre_api.sh @@ -3,18 +3,28 @@ # Quit if any command produces an error. set -e -# Parse variables -MAJOR_VERSION=$1 -: ${MAJOR_VERSION:? - "You must specify the major version of the API to be archived." - "Usage ./archive_chre_api.sh <major_version> <minor_version>"} +VERSION_FILE=include/chre_api/chre/version.h -MINOR_VERSION=$2 -: ${MINOR_VERSION:? - "You must specify the minor version of the API to be archived." - "Usage ./archive_chre_api.sh <major_version> <minor_version>"} +# Get the latest API version +CURRENT_VERSION=$(grep -E "^\#define CHRE_API_VERSION CHRE_API_VERSION_[0-9]+" $VERSION_FILE) +MAJOR_VERSION=$(echo $CURRENT_VERSION | cut -d "_" -f 6) +MINOR_VERSION=$(echo $CURRENT_VERSION | cut -d "_" -f 7) +ARCHIVE_DIRECTORY=v${MAJOR_VERSION}_${MINOR_VERSION} +mkdir legacy/$ARCHIVE_DIRECTORY +cp -r include/chre_api/* legacy/$ARCHIVE_DIRECTORY -DIRECTORY=v${MAJOR_VERSION}_${MINOR_VERSION} +ARCHIVED_VERSION=$(grep -n "^\#define CHRE_API_VERSION_${MAJOR_VERSION}_${MINOR_VERSION}" $VERSION_FILE) +LINE_NUMBER=$(($(echo $ARCHIVED_VERSION | cut -d ":" -f 1) + 2)) +ARCHIVED_VERSION=$(echo $ARCHIVED_VERSION | cut -d ":" -f 2) -mkdir legacy/$DIRECTORY -cp -r include/chre_api/* legacy/$DIRECTORY +HEX_VERSION=$(echo $(echo $(echo $ARCHIVED_VERSION | cut -d "(" -f 2) | cut -d ")" -f 1) | cut -d "x" -f 2) +HEX_VERSION=$((16#$HEX_VERSION)) +BITSHIFT=$(($MINOR_VERSION << 16)) +HEX_VERSION=$(($HEX_VERSION - $BITSHIFT)) +MINOR_VERSION=$(($MINOR_VERSION + 1)) +BITSHIFT=$(($MINOR_VERSION<< 16)); +HEX_VERSION=$(($HEX_VERSION + $BITSHIFT)) +HEX_VERSION=$(printf "%x" $HEX_VERSION) + +sed -i "${LINE_NUMBER}i#define CHRE_API_VERSION_${MAJOR_VERSION}_${MINOR_VERSION} UINT32_C(0x0${HEX_VERSION})\n" $VERSION_FILE +sed -i "s/${CURRENT_VERSION}/\#define CHRE_API_VERSION CHRE_API_VERSION_${MAJOR_VERSION}_${MINOR_VERSION}/g" $VERSION_FILE diff --git a/chre_api/include/chre_api/chre/audio.h b/chre_api/include/chre_api/chre/audio.h index e8ec9607..085329ec 100644 --- a/chre_api/include/chre_api/chre/audio.h +++ b/chre_api/include/chre_api/chre/audio.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_AUDIO_H_ #define _CHRE_AUDIO_H_ diff --git a/chre_api/include/chre_api/chre/ble.h b/chre_api/include/chre_api/chre/ble.h index 9f049647..701fbc23 100644 --- a/chre_api/include/chre_api/chre/ble.h +++ b/chre_api/include/chre_api/chre/ble.h @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef CHRE_BLE_H_ #define CHRE_BLE_H_ @@ -89,6 +93,11 @@ extern "C" { //! CHRE BLE supports RSSI filters #define CHRE_BLE_FILTER_CAPABILITIES_RSSI UINT32_C(1 << 1) +//! CHRE BLE supports Broadcaster Address filters (Corresponding HCI OCF: +//! 0x0157, Sub-command: 0x02) +//! @since v1.9 +#define CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS UINT32_C(1 << 2) + //! CHRE BLE supports Manufacturer Data filters (Corresponding HCI OCF: 0x0157, //! Sub-command: 0x06) //! @since v1.8 @@ -306,11 +315,6 @@ enum chreBleScanMode { */ enum chreBleAdType { //! Service Data with 16-bit UUID - //! @deprecated as of v1.8 - //! TODO(b/285207430): Remove this enum once CHRE has been updated. - CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 = 0x16, - - //! Service Data with 16-bit UUID //! @since v1.8 CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 was renamed //! CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE to reflect that nanoapps //! compiled against v1.8+ should use OTA format for service data filters. @@ -322,42 +326,34 @@ enum chreBleAdType { }; /** - * Generic scan filters definition based on AD Type, mask, and values. The - * maximum data length is limited to the maximum possible legacy advertisement - * payload data length (29 bytes). - * - * The filter is matched when - * data & dataMask == advData & dataMask - * where advData is the advertisement packet data for the specified AD type. + * Generic filters are used to filter for the presence of AD structures in the + * data field of LE Extended Advertising Report events (ref: BT Core Spec v5.3, + * Vol 3, Part E, Section 11). * * The CHRE generic filter structure represents a generic filter on an AD Type * as defined in the Bluetooth spec Assigned Numbers, Generic Access Profile * (ref: https://www.bluetooth.com/specifications/assigned-numbers/). This - * generic structure is used by the Advertising Packet Content Filter - * (APCF) HCI generic AD type sub-command 0x09 (ref: - * https://source.android.com/devices/bluetooth/hci_requirements#le_apcf_command). + * generic structure is used by the Android HCI Advertising Packet Content + * Filter (APCF) AD Type sub-command 0x09 (ref: + * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-ad_type_sub_cmd). + * + * The filter is matched when an advertisement event contains an AD structure in + * its data field that matches the following criteria: + * AdStructure.type == type + * AdStructure.data & dataMask == data & dataMask + * + * The maximum data length is limited to the maximum possible legacy + * advertisement payload data length (29 bytes). The data and dataMask must be + * in OTA format. For each zero bit of the dataMask, the corresponding + * data bit must also be zero. * * Note that the CHRE implementation may not support every kind of filter that * can be represented by this structure. Use chreBleGetFilterCapabilities() to * discover supported filtering capabilities at runtime. * - * @since v1.8 The data and dataMask must be in OTA format. The nanoapp support - * library will handle converting the data and dataMask values to the correct - * format if a pre v1.8 version of CHRE is running. - * - * NOTE: CHRE versions 1.6 and 1.7 expect the 2-byte UUID prefix in - * CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 to be given as big-endian. This - * was corrected in v1.8 to match the OTA format and Bluetooth specification, - * which uses little-endian. This enum has been removed and replaced with - * CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE to ensure that nanoapps - * compiled against v1.8 and later specify their UUID filter using - * little-endian. Nanoapps compiled against v1.7 or earlier should continue to - * use big-endian, as CHRE must provide cross-version compatibility for all - * possible version combinations. - * * Example 1: To filter on a 16 bit service data UUID of 0xFE2C, the following * settings would be used: - * type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 + * type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE * len = 2 * data = {0x2C, 0xFE} * dataMask = {0xFF, 0xFF} @@ -390,25 +386,43 @@ struct chreBleGenericFilter { }; /** - * CHRE Bluetooth LE scan filters are based on a combination of an RSSI - * threshold and generic scan filters as defined by AD Type, mask, and values. + * Broadcaster address filters are used to filter by the address field of the LE + * Extended Advertising Report event which is defined in the BT Core Spec v5.3, + * Vol 4, Part E, Section 7.7.65.13. * - * CHRE-provided filters are implemented in a best-effort manner, depending on - * HW capabilities of the system and available resources. Therefore, provided - * scan results may be a superset of the specified filters. Nanoapps should try - * to take advantage of CHRE scan filters as much as possible, but must design - * their logic as to not depend on CHRE filtering. + * The CHRE broadcaster address filter structure is modeled after the + * Advertising Packet Content Filter (APCF) HCI broadcaster address sub-command + * 0x02 (ref: + * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-broadcast_address_sub_cmd). * - * The syntax of CHRE scan filter definitions are based on the Android - * Advertising Packet Content Filter (APCF) HCI requirement subtype 0x08 - * ref: - * https://source.android.com/devices/bluetooth/hci_requirements#le_apcf_command-set_filtering_parameters_sub_cmd - * and AD Types as defined in the Bluetooth spec Assigned Numbers, Generic - * Access Profile - * ref: https://www.bluetooth.com/specifications/assigned-numbers/ + * The CHRE broadcaster address filter does not filter by address type at this + * time. If a nanoapp wants to filter for a particular address type, it must + * check the addressType field of the chreBleAdvertisingReport. + * + * NOTE: The broadcasterAddress (6-byte) must be in OTA format. * - * Even though the scan filters are defined in a generic manner, CHRE Bluetooth - * is expected to initially support only a limited set of AD Types. + * The filter is matched when an advertisement even meets the following criteria: + * broadcasterAddress == chreBleAdvertisingReport.address. + * + * Example: To filter on the address (01:02:03:AB:CD:EF), the following + * settings would be used: + * broadcasterAddress = {0xEF, 0xCD, 0xAB, 0x03, 0x02, 0x01} + * + * @since v1.9 + */ +struct chreBleBroadcasterAddressFilter { + //! 6-byte Broadcaster address + uint8_t broadcasterAddress[CHRE_BLE_ADDRESS_LEN]; +}; + +/** + * CHRE Bluetooth LE scan filters. + * + * @see chreBleScanFilterV1_9 for further details. + * + * @deprecated as of v1.9 due to the addition of the + * chreBleBroadcasterAddressFilter. New code should use chreBleScanFilterV1_9 + * instead of this struct. This struct will be removed in a future version. */ struct chreBleScanFilter { //! RSSI threshold filter (Corresponding HCI OCF: 0x0157, Sub: 0x01), where @@ -428,6 +442,60 @@ struct chreBleScanFilter { }; /** + * CHRE Bluetooth LE scan filters are based on a combination of an RSSI + * threshold, generic filters, and broadcaster address filters. + * + * When multiple filters are specified, rssiThreshold is combined with the other + * filters via functional AND, and the other filters are all combined as + * functional OR. In other words, an advertisement matches the filter if: + * rssi >= rssiThreshold + * AND (matchAny(genericFilters) OR matchAny(broadcasterAddressFilters)) + * + * CHRE-provided filters are implemented in a best-effort manner, depending on + * HW capabilities of the system and available resources. Therefore, provided + * scan results may be a superset of the specified filters. Nanoapps should try + * to take advantage of CHRE scan filters as much as possible, but must design + * their logic as to not depend on CHRE filtering. + * + * The syntax of CHRE scan filter definition is modeled after a combination of + * multiple Android HCI Advertising Packet Content Filter (APCF) sub commands + * including the RSSI threshold from the set filtering parameters sub command + * (ref: + * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-set_filtering_parameters_sub_cmd). + * @see chreBleGenericFilter and chreBleBroadcasterAddressFilter for details + * about other APCF sub commands referenced. + * + * @since v1.9 + */ +struct chreBleScanFilterV1_9 { + //! RSSI threshold filter (Corresponding HCI OCF: 0x0157, Sub: 0x01), where + //! advertisements with RSSI values below this threshold may be disregarded. + //! An rssiThreshold value of CHRE_BLE_RSSI_THRESHOLD_NONE indicates no RSSI + //! filtering. + int8_t rssiThreshold; + + //! Number of generic filters provided in the scanFilters array. A + //! genericFilterCount value of 0 indicates no generic filters. + uint8_t genericFilterCount; + + //! Pointer to an array of generic filters. If the array contains more than + //! one entry, advertisements matching any of the entries will be returned + //! (functional OR). This is expected to be null if genericFilterCount is 0. + const struct chreBleGenericFilter *genericFilters; + + //! Number of broadcaster address filters provided in the + //! broadcasterAddressFilters array. A broadcasterAddressFilterCount value + //! of 0 indicates no broadcaster address filters. + uint8_t broadcasterAddressFilterCount; + + //! Pointer to an array of broadcaster address filters. If the array contains + //! more than one entry, advertisements matching any of the entries will be + //! returned (functional OR). This is expected to be null if + //! broadcasterAddressFilterCount is 0. + const struct chreBleBroadcasterAddressFilter *broadcasterAddressFilters; +}; + +/** * CHRE BLE advertising address type is based on the BT Core Spec v5.2, Vol 4, * Part E, Section 7.7.65.13, LE Extended Advertising Report event, * Address_Type. @@ -685,6 +753,19 @@ static inline uint8_t chreBleGetEventTypeAndDataStatus(uint8_t eventType, /** * Start Bluetooth LE (BLE) scanning on CHRE. * + * @see chreBleStartScanAsyncV1_9 for further details. + * + * @deprecated as of v1.9 due to the addition of the chreBleScanFilterV1_9 + * struct and a cookie parameter. New code should use + * chreBleStartScanAsyncV1_9() instead of this function. This function will be + * removed in a future version. + */ +bool chreBleStartScanAsync(enum chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilter *filter); + +/** + * Start Bluetooth LE (BLE) scanning on CHRE. + * * The result of the operation will be delivered asynchronously via the CHRE * event CHRE_EVENT_BLE_ASYNC_RESULT. * @@ -716,8 +797,8 @@ static inline uint8_t chreBleGetEventTypeAndDataStatus(uint8_t eventType, * Legacy-only: false * PHY type: PHY_LE_ALL_SUPPORTED * - * For v1.8 and greater, a CHRE_EVENT_BLE_SCAN_STATUS_CHANGE will be generated - * if the values in chreBleScanStatus changes as a result of this call. + * A CHRE_EVENT_BLE_SCAN_STATUS_CHANGE will be generated if the values in + * chreBleScanStatus changes as a result of this call. * * @param mode Scanning mode selected among enum chreBleScanMode * @param reportDelayMs Maximum requested batching delay in ms. 0 indicates no @@ -727,24 +808,43 @@ static inline uint8_t chreBleGetEventTypeAndDataStatus(uint8_t eventType, * defined by struct chreBleScanFilter. The ownership of filter * and its nested elements remains with the caller, and the caller * may release it as soon as chreBleStartScanAsync() returns. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent as a response to this request. * * @return True to indicate that the request was accepted. False otherwise. * - * @since v1.6 + * @since v1.9 */ -bool chreBleStartScanAsync(enum chreBleScanMode mode, uint32_t reportDelayMs, - const struct chreBleScanFilter *filter); +bool chreBleStartScanAsyncV1_9(enum chreBleScanMode mode, + uint32_t reportDelayMs, + const struct chreBleScanFilterV1_9 *filter, + const void *cookie); + +/** + * Stops a CHRE BLE scan. + * + * @see chreBleStopScanAsyncV1_9 for further details. + * + * @deprecated as of v1.9 due to the addition of the cookie parameter. New code + * should use chreBleStopScanAsyncV1_9() instead of this function. This function + * will be removed in a future version. + */ +bool chreBleStopScanAsync(void); + /** * Stops a CHRE BLE scan. * * The result of the operation will be delivered asynchronously via the CHRE * event CHRE_EVENT_BLE_ASYNC_RESULT. * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent as a response to this request. + * * @return True to indicate that the request was accepted. False otherwise. * - * @since v1.6 + * @since v1.9 */ -bool chreBleStopScanAsync(void); +bool chreBleStopScanAsyncV1_9(const void *cookie); /** * Requests to immediately deliver batched scan results. The nanoapp must diff --git a/chre_api/include/chre_api/chre/common.h b/chre_api/include/chre_api/chre/common.h index 9e3378e0..3b270af5 100644 --- a/chre_api/include/chre_api/chre/common.h +++ b/chre_api/include/chre_api/chre/common.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_COMMON_H_ #define _CHRE_COMMON_H_ @@ -121,7 +124,7 @@ enum chreError { //!< Do not exceed this value when adding new error codes CHRE_ERROR_LAST = UINT8_MAX, }; -// LINT.ThenChange(core/include/chre/core/api_manager_common.h) +// LINT.ThenChange(../../../../core/include/chre/core/api_manager_common.h) /** * Generic data structure to indicate the result of an asynchronous operation. @@ -178,7 +181,8 @@ struct chreAsyncResult { * @since v1.8 */ struct chreBatchCompleteEvent { - //! Indicates the type of event (of type CHRE_EVENT_TYPE_*) that was batched. + //! Indicates the type of event (of type CHRE_EVENT_TYPE_*) that was + //! batched. uint16_t eventType; //! Reserved for future use, set to 0 diff --git a/chre_api/include/chre_api/chre/event.h b/chre_api/include/chre_api/chre/event.h index 04b2d0d8..f91ef062 100644 --- a/chre_api/include/chre_api/chre/event.h +++ b/chre_api/include/chre_api/chre/event.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_EVENT_H_ #define _CHRE_EVENT_H_ @@ -508,19 +511,19 @@ struct chreHostEndpointNotification { */ //! The host endpoint is part of the Android system framework. -#define CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK UINT8_C(0) +#define CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK UINT8_C(0x00) //! The host endpoint is an Android app. -#define CHRE_HOST_ENDPOINT_TYPE_APP UINT8_C(1) +#define CHRE_HOST_ENDPOINT_TYPE_APP UINT8_C(0x01) //! The host endpoint is an Android native program. -#define CHRE_HOST_ENDPOINT_TYPE_NATIVE UINT8_C(2) +#define CHRE_HOST_ENDPOINT_TYPE_NATIVE UINT8_C(0x02) //! Values in the range [CHRE_HOST_ENDPOINT_TYPE_VENDOR_START, //! CHRE_HOST_ENDPOINT_TYPE_VENDOR_END] can be a custom defined host endpoint //! type for platform-specific vendor use. -#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_START UINT8_C(128) -#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_END UINT8_C(255) +#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_START UINT8_C(0x80) +#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_END UINT8_C(0xFF) /** @} */ diff --git a/chre_api/include/chre_api/chre/gnss.h b/chre_api/include/chre_api/chre/gnss.h index 79a8f46b..a326e85b 100644 --- a/chre_api/include/chre_api/chre/gnss.h +++ b/chre_api/include/chre_api/chre/gnss.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_GNSS_H_ #define _CHRE_GNSS_H_ diff --git a/chre_api/include/chre_api/chre/nanoapp.h b/chre_api/include/chre_api/chre/nanoapp.h index da199ee0..3a1c3628 100644 --- a/chre_api/include/chre_api/chre/nanoapp.h +++ b/chre_api/include/chre_api/chre/nanoapp.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_NANOAPP_H_ #define _CHRE_NANOAPP_H_ diff --git a/chre_api/include/chre_api/chre/re.h b/chre_api/include/chre_api/chre/re.h index 20a69b66..1efa5d30 100644 --- a/chre_api/include/chre_api/chre/re.h +++ b/chre_api/include/chre_api/chre/re.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_RE_H_ #define _CHRE_RE_H_ @@ -363,7 +366,7 @@ bool chreTimerCancel(uint32_t timerId); * @return Never. This method does not return, as the CHRE stops nanoapp * execution immediately. */ -void chreAbort(uint32_t abortCode); +void chreAbort(uint32_t abortCode) CHRE_NO_RETURN; /** * Allocate a given number of bytes from the system heap. diff --git a/chre_api/include/chre_api/chre/sensor.h b/chre_api/include/chre_api/chre/sensor.h index 41663741..67d07f8a 100644 --- a/chre_api/include/chre_api/chre/sensor.h +++ b/chre_api/include/chre_api/chre/sensor.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_SENSOR_H_ #define _CHRE_SENSOR_H_ diff --git a/chre_api/include/chre_api/chre/sensor_types.h b/chre_api/include/chre_api/chre/sensor_types.h index 63b495bb..0f55efce 100644 --- a/chre_api/include/chre_api/chre/sensor_types.h +++ b/chre_api/include/chre_api/chre/sensor_types.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_SENSOR_TYPES_H_ #define _CHRE_SENSOR_TYPES_H_ @@ -52,7 +55,7 @@ extern "C" { * * @since v1.2 */ -#define CHRE_SENSOR_TYPE_VENDOR_START UINT8_C(192) +#define CHRE_SENSOR_TYPE_VENDOR_START UINT8_C(0xC0) /** * Accelerometer. @@ -65,7 +68,7 @@ extern "C" { * * @see chreConfigureSensorBiasEvents */ -#define CHRE_SENSOR_TYPE_ACCELEROMETER UINT8_C(1) +#define CHRE_SENSOR_TYPE_ACCELEROMETER UINT8_C(0x01) /** * Instantaneous motion detection. @@ -78,7 +81,7 @@ extern "C" { * to SENSOR_TYPE_MOTION_DETECT, but this triggers instantly upon any * motion, instead of waiting for a period of continuous motion. */ -#define CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT UINT8_C(2) +#define CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT UINT8_C(0x02) /** * Stationary detection. @@ -87,7 +90,7 @@ extern "C" { * * This is a one-shot sensor. */ -#define CHRE_SENSOR_TYPE_STATIONARY_DETECT UINT8_C(3) +#define CHRE_SENSOR_TYPE_STATIONARY_DETECT UINT8_C(0x03) /** * Gyroscope. @@ -100,7 +103,7 @@ extern "C" { * * @see chreConfigureSensorBiasEvents */ -#define CHRE_SENSOR_TYPE_GYROSCOPE UINT8_C(6) +#define CHRE_SENSOR_TYPE_GYROSCOPE UINT8_C(0x06) /** * Uncalibrated gyroscope. @@ -110,7 +113,7 @@ extern "C" { * Note that the UNCALIBRATED_GYROSCOPE_DATA must be factory calibrated data, * but not runtime calibrated. */ -#define CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE UINT8_C(7) +#define CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE UINT8_C(0x07) /** * Magnetometer. @@ -123,7 +126,7 @@ extern "C" { * * @see chreConfigureSensorBiasEvents */ -#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD UINT8_C(8) +#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD UINT8_C(0x08) /** * Uncalibrated magnetometer. @@ -133,14 +136,14 @@ extern "C" { * Note that the UNCALIBRATED_GEOMAGNETIC_FIELD_DATA must be factory calibrated * data, but not runtime calibrated. */ -#define CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD UINT8_C(9) +#define CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD UINT8_C(0x09) /** * Barometric pressure sensor. * * Generates: CHRE_EVENT_SENSOR_PRESSURE_DATA */ -#define CHRE_SENSOR_TYPE_PRESSURE UINT8_C(10) +#define CHRE_SENSOR_TYPE_PRESSURE UINT8_C(0x0A) /** * Ambient light sensor. @@ -149,7 +152,7 @@ extern "C" { * * This is an on-change sensor. */ -#define CHRE_SENSOR_TYPE_LIGHT UINT8_C(12) +#define CHRE_SENSOR_TYPE_LIGHT UINT8_C(0x0C) /** * Proximity detection. @@ -158,7 +161,7 @@ extern "C" { * * This is an on-change sensor. */ -#define CHRE_SENSOR_TYPE_PROXIMITY UINT8_C(13) +#define CHRE_SENSOR_TYPE_PROXIMITY UINT8_C(0x0D) /** * Step detection. @@ -167,7 +170,7 @@ extern "C" { * * @since v1.3 */ -#define CHRE_SENSOR_TYPE_STEP_DETECT UINT8_C(23) +#define CHRE_SENSOR_TYPE_STEP_DETECT UINT8_C(0x17) /** * Step counter. @@ -181,7 +184,7 @@ extern "C" { * * @since v1.5 */ -#define CHRE_SENSOR_TYPE_STEP_COUNTER UINT8_C(24) +#define CHRE_SENSOR_TYPE_STEP_COUNTER UINT8_C(0x18) /** * Hinge angle sensor. @@ -197,7 +200,7 @@ extern "C" { * * @since v1.5 */ -#define CHRE_SENSOR_TYPE_HINGE_ANGLE UINT8_C(36) +#define CHRE_SENSOR_TYPE_HINGE_ANGLE UINT8_C(0x24) /** * Uncalibrated accelerometer. @@ -207,28 +210,28 @@ extern "C" { * Note that the UNCALIBRATED_ACCELEROMETER_DATA must be factory calibrated * data, but not runtime calibrated. */ -#define CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER UINT8_C(55) +#define CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER UINT8_C(0x37) /** * Accelerometer temperature. * * Generates: CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA */ -#define CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE UINT8_C(56) +#define CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE UINT8_C(0x38) /** * Gyroscope temperature. * * Generates: CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA */ -#define CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE UINT8_C(57) +#define CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE UINT8_C(0x39) /** * Magnetometer temperature. * * Generates: CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA */ -#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE UINT8_C(58) +#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE UINT8_C(0x3A) #if CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE >= CHRE_SENSOR_TYPE_VENDOR_START #error Too many sensor types @@ -252,12 +255,12 @@ extern "C" { * @{ */ -#define CHRE_SENSOR_ACCURACY_UNKNOWN UINT8_C(0) -#define CHRE_SENSOR_ACCURACY_UNRELIABLE UINT8_C(1) -#define CHRE_SENSOR_ACCURACY_LOW UINT8_C(2) -#define CHRE_SENSOR_ACCURACY_MEDIUM UINT8_C(3) -#define CHRE_SENSOR_ACCURACY_HIGH UINT8_C(4) -#define CHRE_SENSOR_ACCURACY_VENDOR_START UINT8_C(192) +#define CHRE_SENSOR_ACCURACY_UNKNOWN UINT8_C(0x00) +#define CHRE_SENSOR_ACCURACY_UNRELIABLE UINT8_C(0x01) +#define CHRE_SENSOR_ACCURACY_LOW UINT8_C(0x02) +#define CHRE_SENSOR_ACCURACY_MEDIUM UINT8_C(0x03) +#define CHRE_SENSOR_ACCURACY_HIGH UINT8_C(0x04) +#define CHRE_SENSOR_ACCURACY_VENDOR_START UINT8_C(0xC0) #define CHRE_SENSOR_ACCURACY_VENDOR_END UINT8_MAX /** @} */ diff --git a/chre_api/include/chre_api/chre/toolchain.h b/chre_api/include/chre_api/chre/toolchain.h index c2b87222..05b6fb93 100644 --- a/chre_api/include/chre_api/chre/toolchain.h +++ b/chre_api/include/chre_api/chre/toolchain.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef CHRE_TOOLCHAIN_H_ #define CHRE_TOOLCHAIN_H_ @@ -28,6 +31,9 @@ #define CHRE_DEPRECATED(message) \ __attribute__((deprecated(message))) +// Indicates that the function does not return (i.e. abort). +#define CHRE_NO_RETURN __attribute__((noreturn)) + // Enable printf-style compiler warnings for mismatched format string and args #define CHRE_PRINTF_ATTR(formatPos, argStart) \ __attribute__((format(printf, formatPos, argStart))) diff --git a/chre_api/include/chre_api/chre/user_settings.h b/chre_api/include/chre_api/chre/user_settings.h index c40be90e..a13290d8 100644 --- a/chre_api/include/chre_api/chre/user_settings.h +++ b/chre_api/include/chre_api/chre/user_settings.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_USER_SETTINGS_H_ #define _CHRE_USER_SETTINGS_H_ diff --git a/chre_api/include/chre_api/chre/version.h b/chre_api/include/chre_api/chre/version.h index 6872fdde..30087ed5 100644 --- a/chre_api/include/chre_api/chre/version.h +++ b/chre_api/include/chre_api/chre/version.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_VERSION_H_ #define _CHRE_VERSION_H_ @@ -135,14 +138,28 @@ extern "C" { /** * Value for version 1.8 of the Context Hub Runtime Environment API interface. * - * This version of the CHRE API is shipped with the Android U release. + * This version of the CHRE API is shipped with the Android U release. It adds + * support for filtering by manufacturer data in BLE scans, reading the RSSI + * value of a BLE connection, allowing the nanoapp to check BLE scan status, + * allowing the nanoapp to specify which RPC services it supports, and + * delivering batch complete events for batched BLE scans. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_8 UINT32_C(0x01080000) + +/** + * Value for version 1.9 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API is shipped with a post-launch update to the + * Android U release. It adds the BLE Broadcaster Address filter. * * @note This version of the CHRE API has not been finalized yet, and is * currently considered a preview that is subject to change. * * @see CHRE_API_VERSION */ -#define CHRE_API_VERSION_1_8 UINT32_C(0x01080000) +#define CHRE_API_VERSION_1_9 UINT32_C(0x01090000) /** * Major and Minor Version of this Context Hub Runtime Environment API. @@ -161,7 +178,7 @@ extern "C" { * Note that version numbers can always be numerically compared with * expected results, so 1.0.0 < 1.0.4 < 1.1.0 < 2.0.300 < 3.5.0. */ -#define CHRE_API_VERSION CHRE_API_VERSION_1_8 +#define CHRE_API_VERSION CHRE_API_VERSION_1_9 /** * Utility macro to extract only the API major version of a composite CHRE diff --git a/chre_api/include/chre_api/chre/wifi.h b/chre_api/include/chre_api/chre/wifi.h index b4bda9c0..048e7ae4 100644 --- a/chre_api/include/chre_api/chre/wifi.h +++ b/chre_api/include/chre_api/chre/wifi.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_WIFI_H_ #define _CHRE_WIFI_H_ diff --git a/chre_api/include/chre_api/chre/wwan.h b/chre_api/include/chre_api/chre/wwan.h index 51cf5f9d..ac55f4dd 100644 --- a/chre_api/include/chre_api/chre/wwan.h +++ b/chre_api/include/chre_api/chre/wwan.h @@ -14,6 +14,9 @@ * limitations under the License. */ +// IWYU pragma: private, include "chre_api/chre.h" +// IWYU pragma: friend chre/.*\.h + #ifndef _CHRE_WWAN_H_ #define _CHRE_WWAN_H_ diff --git a/chre_api/legacy/v1_8/chre.h b/chre_api/legacy/v1_8/chre.h new file mode 100644 index 00000000..9b87d08b --- /dev/null +++ b/chre_api/legacy/v1_8/chre.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_H_ +#define _CHRE_H_ + +/** + * @file + * This header file includes all the headers which combine to fully define the + * interface for the Context Hub Runtime Environment (CHRE). This interface is + * of interest to both implementers of CHREs and authors of nanoapps. The API + * documentation attempts to address concerns of both. + * + * See individual header files for API details, and general comments below + * for overall platform information. + */ + +#include <chre/audio.h> +#include <chre/ble.h> +#include <chre/common.h> +#include <chre/event.h> +#include <chre/gnss.h> +#include <chre/nanoapp.h> +#include <chre/re.h> +#include <chre/sensor.h> +#include <chre/toolchain.h> +#include <chre/user_settings.h> +#include <chre/version.h> +#include <chre/wifi.h> +#include <chre/wwan.h> + +/** + * @mainpage + * CHRE is the Context Hub Runtime Environment. CHRE is used in Android to run + * contextual applications, called nanoapps, in a low-power processing domain + * other than the applications processor that runs Android itself. The CHRE + * API, documented herein, is the common interface exposed to nanoapps for any + * compatible CHRE implementation. The CHRE API provides the ability for + * creating nanoapps that are code-compatible across different CHRE + * implementations and underlying platforms. Refer to the following sections for + * a discussion on some important details of CHRE that aren't explicitly exposed + * in the API itself. + * + * @section entry_points Entry points + * + * The following entry points are used to bind a nanoapp to the CHRE system, and + * all three must be implemented by any nanoapp (see chre/nanoapp.h): + * - nanoappStart: initialization + * - nanoappHandleEvent: hook for event-driven processing + * - nanoappEnd: graceful teardown + * + * The CHRE implementation must also ensure that it performs these functions + * prior to invoking nanoappStart, or after nanoappEnd returns: + * - bss section zeroed out (prior to nanoappStart) + * - static variables initialized (prior to nanoappStart) + * - global C++ constructors called (prior to nanoappStart) + * - global C++ destructors called (after nanoappEnd) + * + * @section threading Threading model + * + * A CHRE implementation is free to choose among many different + * threading models, including a single-threaded system or a multi-threaded + * system with preemption. The current platform definition is agnostic to this + * underlying choice. However, the CHRE implementation must ensure that time + * spent executing within a nanoapp does not significantly degrade or otherwise + * interfere with other functions of the system in which CHRE is implemented, + * especially latency-sensitive tasks such as sensor event delivery to the AP. + * In other words, it must ensure that these functions can either occur in + * parallel or preempt a nanoapp's execution. The current version of the API + * does not specify whether the implementation allows for CPU sharing between + * nanoapps on a more granular level than the handling of individual events [1]. + * In any case, event ordering from the perspective of an individual nanoapp + * must be FIFO, but the CHRE implementation may choose to violate total + * ordering of events across all nanoapps to achieve more fair resource sharing, + * but this is not required. + * + * This version of the CHRE API does require that all nanoapps are treated as + * non-reentrant, meaning that only one instance of program flow can be inside + * an individual nanoapp at any given time. That is, any of the functions of + * the nanoapp, including the entry points and all other callbacks, cannot be + * invoked if a previous invocation to the same or any other function in the + * nanoapp has not completed yet. + * + * For example, if a nanoapp is currently in nanoappHandleEvent(), the CHRE is + * not allowed to call nanoappHandleEvent() again, or to call a memory freeing + * callback. Similarly, if a nanoapp is currently in a memory freeing + * callback, the CHRE is not allowed to call nanoappHandleEvent(), or invoke + * another memory freeing callback. + * + * There are two exceptions to this rule: If an invocation of chreSendEvent() + * fails (returns 'false'), it is allowed to immediately invoke the memory + * freeing callback passed into that function. This is a rare case, and one + * where otherwise a CHRE implementation is likely to leak memory. Similarly, + * chreSendMessageToHost() is allowed to invoke the memory freeing callback + * directly, whether it returns 'true' or 'false'. This is because the CHRE + * implementation may copy the message data to its own buffer, and therefore + * wouldn't need the nanoapp-supplied buffer after chreSendMessageToHost() + * returns. + * + * For a nanoapp author, this means no thought needs to be given to + * synchronization issues with global objects, as they will, by definition, + * only be accessed by a single thread at once. + * + * [1]: Note to CHRE implementers: A future version of the CHRE platform may + * require multi-threading with preemption. This is mentioned as a heads up, + * and to allow implementors deciding between implementation approaches to + * make the most informed choice. + * + * @section timing Timing + * + * Nanoapps should expect to be running on a highly constrained system, with + * little memory and little CPU. Any single nanoapp should expect to + * be one of several nanoapps on the system, which also share the CPU with the + * CHRE and possibly other services as well. + * + * Thus, a nanoapp needs to be efficient in its memory and CPU usage. + * Also, as noted in the Threading Model section, a CHRE implementation may + * be single threaded. As a result, all methods invoked in a nanoapp + * (like nanoappStart, nanoappHandleEvent, memory free callbacks, etc.) + * must run "quickly". "Quickly" is difficult to define, as there is a + * diversity of Context Hub hardware. Nanoapp authors are strongly recommended + * to limit their application to consuming no more than 1 second of CPU time + * prior to returning control to the CHRE implementation. A CHRE implementation + * may consider a nanoapp as unresponsive if it spends more time than this to + * process a single event, and take corrective action. + * + * A nanoapp may have the need to occasionally perform a large block of + * calculations that exceeds the 1 second guidance. The recommended approach in + * this case is to split up the large block of calculations into smaller + * batches. In one call into the nanoapp, the nanoapp can perform the first + * batch, and then set a timer or send an event (chreSendEvent()) to itself + * indicating which batch should be done next. This will allow the nanoapp to + * perform the entire calculation over time, without monopolizing system + * resources. + * + * @section floats Floating point support + * + * The C type 'float' is used in this API, and thus a CHRE implementation + * is required to support 'float's. + * + * Support of the C types 'double' and 'long double' is optional for a + * CHRE implementation. Note that if a CHRE decides to support them, unlike + * 'float' support, there is no requirement that this support is particularly + * efficient. So nanoapp authors should be aware this may be inefficient. + * + * If a CHRE implementation chooses not to support 'double' or + * 'long double', then the build toolchain setup provided needs to set + * the preprocessor define CHRE_NO_DOUBLE_SUPPORT. + * + * @section compat CHRE and Nanoapp compatibility + * + * CHRE implementations must make affordances to maintain binary compatibility + * across minor revisions of the API version (e.g. v1.1 to v1.2). This applies + * to both running a nanoapp compiled for a newer version of the API on a CHRE + * implementation built against an older version (backwards compatibility), and + * vice versa (forwards compatibility). API changes that are acceptable in + * minor version changes that may require special measures to ensure binary + * compatibility include: addition of new functions; addition of arguments to + * existing functions when the default value used for nanoapps compiled against + * the old version is well-defined and does not affect existing functionality; + * and addition of fields to existing structures, even when this induces a + * binary layout change (this should be made rare via judicious use of reserved + * fields). API changes that must only occur alongside a major version change + * and are therefore not compatible include: removal of any function, argument, + * field in a data structure, or mandatory functional behavior that a nanoapp + * may depend on; any change in the interpretation of an existing data structure + * field that alters the way it was defined previously (changing the units of a + * field would fall under this, but appropriating a previously reserved field + * for some new functionality would not); and any change in functionality or + * expected behavior that conflicts with the previous definition. + * + * Note that the CHRE API only specifies the software interface between a + * nanoapp and the CHRE system - the binary interface (ABI) between nanoapp and + * CHRE is necessarily implementation-dependent. Therefore, the recommended + * approach to accomplish binary compatibility is to build a Nanoapp Support + * Library (NSL) that is specific to the CHRE implementation into the nanoapp + * binary, and use it to handle ABI details in a way that ensures compatibility. + * In addition, to accomplish forwards compatibility, the CHRE implementation is + * expected to recognize the CHRE API version that a nanoapp is targeting and + * engage compatibility behaviors where necessary. + * + * By definition, major API version changes (e.g. v1.1 to v2.0) break + * compatibility. Therefore, a CHRE implementation must not attempt to load a + * nanoapp that is targeting a newer major API version. + */ + +#endif /* _CHRE_H_ */ + diff --git a/chre_api/legacy/v1_8/chre/audio.h b/chre_api/legacy/v1_8/chre/audio.h new file mode 100644 index 00000000..e8ec9607 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/audio.h @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef _CHRE_AUDIO_H_ +#define _CHRE_AUDIO_H_ + +/** + * @file + * The API for requesting audio in the Context Hub Runtime Environment. + * + * This includes the definition of audio data structures and the ability to + * request audio streams. + */ + +#include <chre/event.h> + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The current compatibility version of the chreAudioDataEvent structure. + */ +#define CHRE_AUDIO_DATA_EVENT_VERSION UINT8_C(1) + +/** + * Produce an event ID in the block of IDs reserved for audio + * @param offset Index into audio event ID block; valid range [0,15] + */ +#define CHRE_AUDIO_EVENT_ID(offset) (CHRE_EVENT_AUDIO_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreAudioSourceStatusEvent + * + * Indicates a change in the format and/or rate of audio data provided to a + * nanoapp. + */ +#define CHRE_EVENT_AUDIO_SAMPLING_CHANGE CHRE_AUDIO_EVENT_ID(0) + +/** + * nanoappHandleEvent argument: struct chreAudioDataEvent + * + * Provides a buffer of audio data to a nanoapp. + */ +#define CHRE_EVENT_AUDIO_DATA CHRE_AUDIO_EVENT_ID(1) + +/** + * The maximum size of the name of an audio source including the + * null-terminator. + */ +#define CHRE_AUDIO_SOURCE_NAME_MAX_SIZE (40) + +/** + * Helper values for sample rates. + * + * @defgroup CHRE_AUDIO_SAMPLE_RATES + * @{ + */ + +//! 16kHz Audio Sample Data +#define CHRE_AUDIO_SAMPLE_RATE_16KHZ (16000) + +/** @} */ + +/** + * Formats for audio that can be provided to a nanoapp. + */ +enum chreAudioDataFormat { + /** + * Unsigned, 8-bit u-Law encoded data as specified by ITU-T G.711. + */ + CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW = 0, + + /** + * Signed, 16-bit linear PCM data. Endianness must be native to the local + * processor. + */ + CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM = 1, +}; + +/** + * A description of an audio source available to a nanoapp. + * + * This provides a description of an audio source with a name and a + * description of the format of the provided audio data. + */ +struct chreAudioSource { + /** + * A human readable name for this audio source. This is a C-style, + * null-terminated string. The length must be less than or equal to + * CHRE_AUDIO_SOURCE_NAME_MAX_SIZE bytes (including the null-terminator) and + * is expected to describe the source of the audio in US English. All + * characters must be printable (i.e.: isprint would return true for all + * characters in the name for the EN-US locale). The typical use of this field + * is for a nanoapp to log the name of the audio source that it is using. + * + * Example: "Camcorder Microphone" + */ + const char *name; + + /** + * The sampling rate in hertz of this mode. This value is rounded to the + * nearest integer. Typical values might include 16000, 44100 and 44800. + * + * If the requested audio source is preempted by another feature of the system + * (e.g. hotword), a gap may occur in received audio data. This is indicated + * to the client by posting a CHRE_EVENT_AUDIO_SAMPLING_CHANGE event. The + * nanoapp will then receive another CHRE_EVENT_AUDIO_SAMPLING_CHANGE event + * once the audio source is available again. + */ + uint32_t sampleRate; + + /** + * The minimum amount of time that this audio source can be buffered, in + * nanoseconds. Audio data is delivered to nanoapps in buffers. This specifies + * the minimum amount of data that can be delivered to a nanoapp without + * losing data. A request for a buffer that is smaller than this will fail. + */ + uint64_t minBufferDuration; + + /** + * The maximum amount of time that this audio source can be buffered, in + * nanoseconds. Audio data is delivered to nanoapps in buffers. This specifies + * the maximum amount of data that can be stored by the system in one event + * without losing data. A request for a buffer that is larger than this will + * fail. + */ + uint64_t maxBufferDuration; + + /** + * The format for data provided to the nanoapp. This will be assigned to one + * of the enum chreAudioDataFormat values. + */ + uint8_t format; +}; + +/** + * The current status of an audio source. + */ +struct chreAudioSourceStatus { + /** + * Set to true if the audio source is currently enabled by this nanoapp. If + * this struct is provided by a CHRE_EVENT_AUDIO_SAMPLING_CHANGE event, it + * must necessarily be set to true because sampling change events are only + * sent for sources which this nanoapp has actively subscribed to. If this + * struct is obtained from the chreAudioGetStatus API, it may be set to true + * or false depending on if audio is currently enabled. + */ + bool enabled; + + /** + * Set to true if the audio source is currently suspended and no audio data + * will be received from this source. + */ + bool suspended; +}; + +/** + * The nanoappHandleEvent argument for CHRE_EVENT_AUDIO_SAMPLING_CHANGE. + */ +struct chreAudioSourceStatusEvent { + /** + * The audio source which has completed a status change. + */ + uint32_t handle; + + /** + * The status of this audio source. + */ + struct chreAudioSourceStatus status; +}; + +/** + * The nanoappHandleEvent argument for CHRE_EVENT_AUDIO_DATA. + * + * One example of the sequence of events for a nanoapp to receive audio data is: + * + * 1. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data is not + * suspended. + * 2. CHRE_EVENT_AUDIO_DATA - One buffer of audio samples. Potentially repeated. + * 3. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data has suspended + * which indicates a gap in the audio. + * 4. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data has resumed + * and that audio data may be delivered + * again if enough samples are buffered. + * 5. CHRE_EVENT_AUDIO_DATA - One buffer of audio samples. Potentially repeated. + * The nanoapp must tolerate a gap in the timestamps. + * + * This process repeats for as long as an active request is made for an audio + * source. A CHRE_EVENT_AUDIO_SAMPLING_CHANGE does not guarantee that the next + * event will be a CHRE_EVENT_AUDIO_DATA event when suspended is set to false. + * It may happen that the audio source is suspended before a complete buffer can + * be captured. This will cause another CHRE_EVENT_AUDIO_SAMPLING_CHANGE event + * to be dispatched with suspended set to true before a buffer is delivered. + * + * Audio events must be delivered to a nanoapp in order. + */ +struct chreAudioDataEvent { + /** + * Indicates the version of the structure, for compatibility purposes. Clients + * do not normally need to worry about this field; the CHRE implementation + * guarantees that the client only receives the structure version it expects. + */ + uint8_t version; + + /** + * Additional bytes reserved for future use; must be set to 0. + */ + uint8_t reserved[3]; + + /** + * The handle for which this audio data originated from. + */ + uint32_t handle; + + /** + * The base timestamp for this buffer of audio data, from the same time base + * as chreGetTime() (in nanoseconds). The audio API does not provide + * timestamps for each audio sample. This timestamp corresponds to the first + * sample of the buffer. Even though the value is expressed in nanoseconds, + * there is an expectation that the sample clock may drift and nanosecond + * level accuracy may not be possible. The goal is to be as accurate as + * possible within reasonable limitations of a given system. + */ + uint64_t timestamp; + + /** + * The sample rate for this buffer of data in hertz, rounded to the nearest + * integer. Fractional sampling rates are not supported. Typical values might + * include 16000, 44100 and 48000. + */ + uint32_t sampleRate; + + /** + * The number of samples provided with this buffer. + */ + uint32_t sampleCount; + + /** + * The format of this audio data. This enumeration and union of pointers below + * form a tagged struct. The consumer of this API must use this enum to + * determine which samples pointer below to dereference. This will be assigned + * to one of the enum chreAudioDataFormat values. + */ + uint8_t format; + + /** + * A union of pointers to various formats of sample data. These correspond to + * the valid chreAudioDataFormat values. + */ + union { + const uint8_t *samplesULaw8; + const int16_t *samplesS16; + }; +}; + +/** + * Retrieves information about an audio source supported by the current CHRE + * implementation. The source returned by the runtime must not change for the + * entire lifecycle of the Nanoapp and hot-pluggable audio sources are not + * supported. + * + * A simple example of iterating all available audio sources is provided here: + * + * struct chreAudioSource audioSource; + * for (uint32_t i = 0; chreAudioGetSource(i, &audioSource); i++) { + * chreLog(CHRE_LOG_INFO, "Found audio source: %s", audioSource.name); + * } + * + * Handles provided to this API must be a stable value for the entire duration + * of a nanoapp. Handles for all audio sources must be zero-indexed and + * contiguous. The following are examples of handles that could be provided to + * this API: + * + * Valid: 0 + * Valid: 0, 1, 2, 3 + * Invalid: 1, 2, 3 + * Invalid: 0, 2 + * + * @param handle The handle for an audio source to obtain details for. The + * range of acceptable handles must be zero-indexed and contiguous. + * @param audioSource A struct to populate with details of the audio source. + * @return true if the query was successful, false if the provided handle is + * invalid or the supplied audioSource is NULL. + * + * @since v1.2 + */ +bool chreAudioGetSource(uint32_t handle, struct chreAudioSource *audioSource); + +/** + * Nanoapps must define CHRE_NANOAPP_USES_AUDIO somewhere in their build + * system (e.g. Makefile) if the nanoapp needs to use the following audio APIs. + * In addition to allowing access to these APIs, defining this macro will also + * ensure CHRE enforces that all host clients this nanoapp talks to have the + * required Android permissions needed to listen to audio data by adding + * metadata to the nanoapp. + */ +#if defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD) + +/** + * Configures delivery of audio data to the current nanoapp. Note that this may + * not fully disable the audio source if it is used by other clients in the + * system but it will halt data delivery to the nanoapp. + * + * The bufferDuration and deliveryInterval parameters as described below are + * used together to determine both how much and how often to deliver data to a + * nanoapp, respectively. A nanoapp will always be provided the requested + * amount of data at the requested interval, even if another nanoapp in CHRE + * requests larger/more frequent buffers or smaller/less frequent buffers. + * These two buffering parameters allow describing the duty cycle of captured + * audio data. If a nanoapp wishes to receive all available audio data, it will + * specify a bufferDuration and deliveryInterval that are equal. A 50% duty + * cycle would be achieved by specifying a deliveryInterval that is double the + * value of the bufferDuration provided. These parameters allow the audio + * subsystem to operate at less than 100% duty cycle and permits use of + * incomplete audio data without periodic reconfiguration of the source. + * + * Two examples are illustrated below: + * + * Target duty cycle: 50% + * bufferDuration: 2 + * deliveryInterval: 4 + * + * Time 0 1 2 3 4 5 6 7 + * Batch A B + * Sample -- -- a1 a2 -- -- b1 b2 + * Duration [ ] [ ] + * Interval [ ] [ ] + * + * + * Target duty cycle: 100% + * bufferDuration: 4 + * deliveryInterval: 4 + * + * Time 0 1 2 3 4 5 6 7 + * Batch A B + * Sample a1 a2 a3 a4 b1 b2 b3 b4 + * Duration [ ] [ ] + * Interval [ ] [ ] + * + * + * This is expected to reduce power overall. + * + * The first audio buffer supplied to the nanoapp may contain data captured + * prior to the request. This could happen if the microphone was already enabled + * and reading into a buffer prior to the nanoapp requesting audio data for + * itself. The nanoapp must tolerate this. + * + * It is important to note that multiple logical audio sources (e.g. different + * sample rate, format, etc.) may map to one physical audio source. It is + * possible for a nanoapp to request audio data from more than one logical + * source at a time. Audio data may be suspended for either the current or other + * requests. The CHRE_EVENT_AUDIO_SAMPLING_CHANGE will be posted to all clients + * if such a change occurs. It is also possible for the request to succeed and + * all audio sources are serviced simultaneously. This is implementation defined + * but at least one audio source must function correctly if it is advertised, + * under normal conditions (e.g. not required for some other system function, + * such as hotword). + * + * @param handle The handle for this audio source. The handle for the desired + * audio source can be determined using chreAudioGetSource(). + * @param enable true if enabling the source, false otherwise. When passed as + * false, the bufferDuration and deliveryInterval parameters are ignored. + * @param bufferDuration The amount of time to capture audio samples from this + * audio source, in nanoseconds per delivery interval. This value must be + * in the range of minBufferDuration/maxBufferDuration for this source or + * the request will fail. The number of samples captured per buffer will be + * derived from the sample rate of the source and the requested duration and + * rounded down to the nearest sample boundary. + * @param deliveryInterval Desired time between each CHRE_EVENT_AUDIO_DATA + * event. This allows specifying the complete duty cycle of a request + * for audio data, in nanoseconds. This value must be greater than or equal + * to bufferDuration or the request will fail due to an invalid + * configuration. + * @return true if the configuration was successful, false if invalid parameters + * were provided (non-existent handle, invalid buffering configuration). + * + * @since v1.2 + * @note Requires audio permission + */ +bool chreAudioConfigureSource(uint32_t handle, bool enable, + uint64_t bufferDuration, + uint64_t deliveryInterval); + +/** + * Gets the current chreAudioSourceStatus struct for a given audio handle. + * + * @param handle The handle for the audio source to query. The provided handle + * is obtained from a chreAudioSource which is requested from the + * chreAudioGetSource API. + * @param status The current status of the supplied audio source. + * @return true if the provided handle is valid and the status was obtained + * successfully, false if the handle was invalid or status is NULL. + * + * @since v1.2 + * @note Requires audio permission + */ +bool chreAudioGetStatus(uint32_t handle, struct chreAudioSourceStatus *status); + +#else /* defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD) */ +#define CHRE_AUDIO_PERM_ERROR_STRING \ + "CHRE_NANOAPP_USES_AUDIO must be defined when building this nanoapp in " \ + "order to refer to " +#define chreAudioConfigureSource(...) \ + CHRE_BUILD_ERROR(CHRE_AUDIO_PERM_ERROR_STRING "chreAudioConfigureSource") +#define chreAudioGetStatus(...) \ + CHRE_BUILD_ERROR(CHRE_AUDIO_PERM_ERROR_STRING "chreAudioGetStatus") +#endif /* defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_AUDIO_H_ */ diff --git a/chre_api/legacy/v1_8/chre/ble.h b/chre_api/legacy/v1_8/chre/ble.h new file mode 100644 index 00000000..9f049647 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/ble.h @@ -0,0 +1,851 @@ +/* + * Copyright (C) 2021 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. + */ +#ifndef CHRE_BLE_H_ +#define CHRE_BLE_H_ + +/** + * @file + * CHRE BLE (Bluetooth Low Energy, Bluetooth LE) API. + * The CHRE BLE API currently supports BLE scanning features. + * + * The features in the CHRE BLE API are a subset and adaptation of Android + * capabilities as described in the Android BLE API and HCI requirements. + * ref: + * https://developer.android.com/guide/topics/connectivity/bluetooth/ble-overview + * ref: https://source.android.com/devices/bluetooth/hci_requirements + */ + +#include <chre/common.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The set of flags returned by chreBleGetCapabilities(). + * + * @defgroup CHRE_BLE_CAPABILITIES + * @{ + */ +//! No BLE APIs are supported +#define CHRE_BLE_CAPABILITIES_NONE UINT32_C(0) + +//! CHRE supports BLE scanning +#define CHRE_BLE_CAPABILITIES_SCAN UINT32_C(1 << 0) + +//! CHRE BLE supports batching of scan results, either through Android-specific +//! HCI (OCF: 0x156), or by the CHRE framework, internally. +//! @since v1.7 Platforms with this capability must also support flushing scan +//! results during a batched scan. +#define CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING UINT32_C(1 << 1) + +//! CHRE BLE scan supports best-effort hardware filtering. If filtering is +//! available, chreBleGetFilterCapabilities() returns a bitmap indicating the +//! specific filtering capabilities that are supported. +//! To differentiate best-effort vs. no filtering, the following requirement +//! must be met for this flag: +//! If only one nanoapp is requesting BLE scans and there are no BLE scans from +//! the AP, only filtered results will be provided to the nanoapp. +#define CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT UINT32_C(1 << 2) + +//! CHRE BLE supports reading the RSSI of a specified LE-ACL connection handle. +#define CHRE_BLE_CAPABILITIES_READ_RSSI UINT32_C(1 << 3) +/** @} */ + +/** + * The set of flags returned by chreBleGetFilterCapabilities(). + * + * The representative bit for each filtering capability is based on the sub-OCF + * of the Android filtering HCI vendor-specific command (LE_APCF_Command, OCF: + * 0x0157) for that particular filtering capability, as found in + * https://source.android.com/devices/bluetooth/hci_requirements + * + * For example, the Service Data filter has a sub-command of 0x7; hence + * the filtering capability is indicated by (1 << 0x7). + * + * @defgroup CHRE_BLE_FILTER_CAPABILITIES + * @{ + */ +//! No CHRE BLE filters are supported +#define CHRE_BLE_FILTER_CAPABILITIES_NONE UINT32_C(0) + +//! CHRE BLE supports RSSI filters +#define CHRE_BLE_FILTER_CAPABILITIES_RSSI UINT32_C(1 << 1) + +//! CHRE BLE supports Manufacturer Data filters (Corresponding HCI OCF: 0x0157, +//! Sub-command: 0x06) +//! @since v1.8 +#define CHRE_BLE_FILTER_CAPABILITIES_MANUFACTURER_DATA UINT32_C(1 << 6) + +//! CHRE BLE supports Service Data filters (Corresponding HCI OCF: 0x0157, +//! Sub-command: 0x07) +#define CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA UINT32_C(1 << 7) +/** @} */ + +/** + * Produce an event ID in the block of IDs reserved for BLE. + * + * Valid input range is [0, 15]. Do not add new events with ID > 15 + * (see chre/event.h) + * + * @param offset Index into BLE event ID block; valid range is [0, 15]. + * + * @defgroup CHRE_BLE_EVENT_ID + * @{ + */ +#define CHRE_BLE_EVENT_ID(offset) (CHRE_EVENT_BLE_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreAsyncResult + * + * Communicates the asynchronous result of a request to the BLE API. The + * requestType field in {@link #chreAsyncResult} is set to a value from enum + * chreBleRequestType. + * + * This is used for results of async config operations which need to + * interop with lower level code (potentially in a different thread) or send an + * HCI command to the FW and wait on the response. + */ +#define CHRE_EVENT_BLE_ASYNC_RESULT CHRE_BLE_EVENT_ID(0) + +/** + * nanoappHandleEvent argument: struct chreBleAdvertisementEvent + * + * Provides results of a BLE scan. + */ +#define CHRE_EVENT_BLE_ADVERTISEMENT CHRE_BLE_EVENT_ID(1) + +/** + * nanoappHandleEvent argument: struct chreAsyncResult + * + * Indicates that a flush request made via chreBleFlushAsync() is complete, and + * all batched advertisements resulting from the flush have been delivered via + * preceding CHRE_EVENT_BLE_ADVERTISEMENT events. + * + * @since v1.7 + */ +#define CHRE_EVENT_BLE_FLUSH_COMPLETE CHRE_BLE_EVENT_ID(2) + +/** + * nanoappHandleEvent argument: struct chreBleReadRssiEvent + * + * Provides the RSSI of an LE ACL connection following a call to + * chreBleReadRssiAsync(). + * + * @since v1.8 + */ +#define CHRE_EVENT_BLE_RSSI_READ CHRE_BLE_EVENT_ID(3) + +/** + * nanoappHandleEvent argument: struct chreBatchCompleteEvent + * + * This event is generated if the platform enabled batching, and when all + * events in a single batch has been delivered (for example, batching + * CHRE_EVENT_BLE_ADVERTISEMENT events if the platform has + * CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING enabled, and a non-zero + * reportDelayMs in chreBleStartScanAsync() was accepted). + * + * If the nanoapp receives a CHRE_EVENT_BLE_SCAN_STATUS_CHANGE with a non-zero + * reportDelayMs and enabled set to true, then this event must be generated. + * + * @since v1.8 + */ +#define CHRE_EVENT_BLE_BATCH_COMPLETE CHRE_BLE_EVENT_ID(4) + +/** + * nanoappHandleEvent argument: struct chreBleScanStatus + * + * This event is generated when the values in chreBleScanStatus changes. + * + * @since v1.8 + */ +#define CHRE_EVENT_BLE_SCAN_STATUS_CHANGE CHRE_BLE_EVENT_ID(5) + +// NOTE: Do not add new events with ID > 15 +/** @} */ + +/** + * Maximum BLE (legacy) advertisement payload data length, in bytes + * This is calculated by subtracting 2 (type + len) from 31 (max payload). + */ +#define CHRE_BLE_DATA_LEN_MAX (29) + +/** + * BLE device address length, in bytes. + */ +#define CHRE_BLE_ADDRESS_LEN (6) + +/** + * RSSI value (int8_t) indicating no RSSI threshold. + */ +#define CHRE_BLE_RSSI_THRESHOLD_NONE (-128) + +/** + * RSSI value (int8_t) indicating no RSSI value available. + */ +#define CHRE_BLE_RSSI_NONE (127) + +/** + * Tx power value (int8_t) indicating no Tx power value available. + */ +#define CHRE_BLE_TX_POWER_NONE (127) + +/** + * Indicates ADI field was not provided in advertisement. + */ +#define CHRE_BLE_ADI_NONE (0xFF) + +/** + * The CHRE BLE advertising event type is based on the BT Core Spec v5.2, + * Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising Report event, + * Event_Type. + * + * Note: helper functions are provided to avoid bugs, e.g. a nanoapp doing + * (eventTypeAndDataStatus == ADV_IND) instead of properly masking off reserved + * and irrelevant bits. + * + * @defgroup CHRE_BLE_EVENT + * @{ + */ +// Extended event types +#define CHRE_BLE_EVENT_MASK_TYPE (0x1f) +#define CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE (1 << 0) +#define CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE (1 << 1) +#define CHRE_BLE_EVENT_TYPE_FLAG_DIRECTED (1 << 2) +#define CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP (1 << 3) +#define CHRE_BLE_EVENT_TYPE_FLAG_LEGACY (1 << 4) + +// Data status +#define CHRE_BLE_EVENT_MASK_DATA_STATUS (0x3 << 5) +#define CHRE_BLE_EVENT_DATA_STATUS_COMPLETE (0x0 << 5) +#define CHRE_BLE_EVENT_DATA_STATUS_MORE_DATA_PENDING (0x1 << 5) +#define CHRE_BLE_EVENT_DATA_STATUS_DATA_TRUNCATED (0x2 << 5) + +// Legacy event types +#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE | \ + CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE) +#define CHRE_BLE_EVENT_TYPE_LEGACY_DIRECT_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE) +#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_SCAN_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE) +#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_NONCONN_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY) +#define CHRE_BLE_EVENT_TYPE_LEGACY_SCAN_RESP_ADV_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP | CHRE_BLE_EVENT_TYPE_LEGACY_ADV_IND) +#define CHRE_BLE_EVENT_TYPE_LEGACY_SCAN_RESP_ADV_SCAN_IND \ + (CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP | CHRE_BLE_EVENT_TYPE_LEGACY_ADV_SCAN_IND) +/** @} */ + +/** + * The maximum amount of time allowed to elapse between the call to + * chreBleFlushAsync() and when CHRE_EVENT_BLE_FLUSH_COMPLETE is delivered to + * the nanoapp on a successful flush. + */ +#define CHRE_BLE_FLUSH_COMPLETE_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC) + +/** + * Indicates a type of request made in this API. Used to populate the resultType + * field of struct chreAsyncResult sent with CHRE_EVENT_BLE_ASYNC_RESULT. + */ +enum chreBleRequestType { + CHRE_BLE_REQUEST_TYPE_START_SCAN = 1, + CHRE_BLE_REQUEST_TYPE_STOP_SCAN = 2, + CHRE_BLE_REQUEST_TYPE_FLUSH = 3, //!< @since v1.7 + CHRE_BLE_REQUEST_TYPE_READ_RSSI = 4, //!< @since v1.8 +}; + +/** + * CHRE BLE scan modes identify functional scan levels without specifying or + * guaranteeing particular scan parameters (e.g. duty cycle, interval, radio + * chain). + * + * The actual scan parameters may be platform dependent and may change without + * notice in real time based on contextual cues, etc. + * + * Scan modes should be selected based on use cases as described. + */ +enum chreBleScanMode { + //! A background scan level for always-running ambient applications. + //! A representative duty cycle may be between 3 - 10 % (tentative, and + //! with no guarantees). + CHRE_BLE_SCAN_MODE_BACKGROUND = 1, + + //! A foreground scan level to be used for short periods. + //! A representative duty cycle may be between 10 - 20 % (tentative, and + //! with no guarantees). + CHRE_BLE_SCAN_MODE_FOREGROUND = 2, + + //! A very high duty cycle scan level to be used for very short durations. + //! A representative duty cycle may be between 50 - 100 % (tentative, and + //! with no guarantees). + CHRE_BLE_SCAN_MODE_AGGRESSIVE = 3, +}; + +/** + * Selected AD Types are available among those defined in the Bluetooth spec. + * Assigned Numbers, Generic Access Profile. + * ref: https://www.bluetooth.com/specifications/assigned-numbers/ + */ +enum chreBleAdType { + //! Service Data with 16-bit UUID + //! @deprecated as of v1.8 + //! TODO(b/285207430): Remove this enum once CHRE has been updated. + CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 = 0x16, + + //! Service Data with 16-bit UUID + //! @since v1.8 CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 was renamed + //! CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE to reflect that nanoapps + //! compiled against v1.8+ should use OTA format for service data filters. + CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE = 0x16, + + //! Manufacturer Specific Data + //! @since v1.8 + CHRE_BLE_AD_TYPE_MANUFACTURER_DATA = 0xff, +}; + +/** + * Generic scan filters definition based on AD Type, mask, and values. The + * maximum data length is limited to the maximum possible legacy advertisement + * payload data length (29 bytes). + * + * The filter is matched when + * data & dataMask == advData & dataMask + * where advData is the advertisement packet data for the specified AD type. + * + * The CHRE generic filter structure represents a generic filter on an AD Type + * as defined in the Bluetooth spec Assigned Numbers, Generic Access Profile + * (ref: https://www.bluetooth.com/specifications/assigned-numbers/). This + * generic structure is used by the Advertising Packet Content Filter + * (APCF) HCI generic AD type sub-command 0x09 (ref: + * https://source.android.com/devices/bluetooth/hci_requirements#le_apcf_command). + * + * Note that the CHRE implementation may not support every kind of filter that + * can be represented by this structure. Use chreBleGetFilterCapabilities() to + * discover supported filtering capabilities at runtime. + * + * @since v1.8 The data and dataMask must be in OTA format. The nanoapp support + * library will handle converting the data and dataMask values to the correct + * format if a pre v1.8 version of CHRE is running. + * + * NOTE: CHRE versions 1.6 and 1.7 expect the 2-byte UUID prefix in + * CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 to be given as big-endian. This + * was corrected in v1.8 to match the OTA format and Bluetooth specification, + * which uses little-endian. This enum has been removed and replaced with + * CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE to ensure that nanoapps + * compiled against v1.8 and later specify their UUID filter using + * little-endian. Nanoapps compiled against v1.7 or earlier should continue to + * use big-endian, as CHRE must provide cross-version compatibility for all + * possible version combinations. + * + * Example 1: To filter on a 16 bit service data UUID of 0xFE2C, the following + * settings would be used: + * type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 + * len = 2 + * data = {0x2C, 0xFE} + * dataMask = {0xFF, 0xFF} + * + * Example 2: To filter for manufacturer data of 0x12, 0x34 from Google (0x00E0), + * the following settings would be used: + * type = CHRE_BLE_AD_TYPE_MANUFACTURER_DATA + * len = 4 + * data = {0xE0, 0x00, 0x12, 0x34} + * dataMask = {0xFF, 0xFF, 0xFF, 0xFF} + * + * Refer to "Supplement to the Bluetooth Core Specification for details (v9, + * Part A, Section 1.4)" for details regarding the manufacturer data format. + */ +struct chreBleGenericFilter { + //! Acceptable values among enum chreBleAdType + uint8_t type; + + /** + * Length of data and dataMask. AD payloads shorter than this length will not + * be matched by the filter. Length must be greater than 0. + */ + uint8_t len; + + //! Used in combination with dataMask to filter an advertisement + uint8_t data[CHRE_BLE_DATA_LEN_MAX]; + + //! Used in combination with data to filter an advertisement + uint8_t dataMask[CHRE_BLE_DATA_LEN_MAX]; +}; + +/** + * CHRE Bluetooth LE scan filters are based on a combination of an RSSI + * threshold and generic scan filters as defined by AD Type, mask, and values. + * + * CHRE-provided filters are implemented in a best-effort manner, depending on + * HW capabilities of the system and available resources. Therefore, provided + * scan results may be a superset of the specified filters. Nanoapps should try + * to take advantage of CHRE scan filters as much as possible, but must design + * their logic as to not depend on CHRE filtering. + * + * The syntax of CHRE scan filter definitions are based on the Android + * Advertising Packet Content Filter (APCF) HCI requirement subtype 0x08 + * ref: + * https://source.android.com/devices/bluetooth/hci_requirements#le_apcf_command-set_filtering_parameters_sub_cmd + * and AD Types as defined in the Bluetooth spec Assigned Numbers, Generic + * Access Profile + * ref: https://www.bluetooth.com/specifications/assigned-numbers/ + * + * Even though the scan filters are defined in a generic manner, CHRE Bluetooth + * is expected to initially support only a limited set of AD Types. + */ +struct chreBleScanFilter { + //! RSSI threshold filter (Corresponding HCI OCF: 0x0157, Sub: 0x01), where + //! advertisements with RSSI values below this threshold may be disregarded. + //! An rssiThreshold value of CHRE_BLE_RSSI_THRESHOLD_NONE indicates no RSSI + //! filtering. + int8_t rssiThreshold; + + //! Number of generic scan filters provided in the scanFilters array. + //! A scanFilterCount value of 0 indicates no generic scan filters. + uint8_t scanFilterCount; + + //! Pointer to an array of scan filters. If the array contains more than one + //! entry, advertisements matching any of the entries will be returned + //! (functional OR). + const struct chreBleGenericFilter *scanFilters; +}; + +/** + * CHRE BLE advertising address type is based on the BT Core Spec v5.2, Vol 4, + * Part E, Section 7.7.65.13, LE Extended Advertising Report event, + * Address_Type. + */ +enum chreBleAddressType { + //! Public device address. + CHRE_BLE_ADDRESS_TYPE_PUBLIC = 0x00, + + //! Random device address. + CHRE_BLE_ADDRESS_TYPE_RANDOM = 0x01, + + //! Public identity address (corresponds to resolved private address). + CHRE_BLE_ADDRESS_TYPE_PUBLIC_IDENTITY = 0x02, + + //! Random (static) Identity Address (corresponds to resolved private + //! address) + CHRE_BLE_ADDRESS_TYPE_RANDOM_IDENTITY = 0x03, + + //! No address provided (anonymous advertisement). + CHRE_BLE_ADDRESS_TYPE_NONE = 0xff, +}; + +/** + * CHRE BLE physical (PHY) channel encoding type, if supported, is based on the + * BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising + * Report event, entries Primary_PHY and Secondary_PHY. + */ +enum chreBlePhyType { + //! No packets on this PHY (only on the secondary channel), or feature not + //! supported. + CHRE_BLE_PHY_NONE = 0x00, + + //! LE 1 MBPS PHY encoding. + CHRE_BLE_PHY_1M = 0x01, + + //! LE 2 MBPS PHY encoding (only on the secondary channel). + CHRE_BLE_PHY_2M = 0x02, + + //! LE long-range coded PHY encoding. + CHRE_BLE_PHY_CODED = 0x03, +}; + +/** + * The CHRE BLE Advertising Report event is based on the BT Core Spec v5.2, + * Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising Report event, with + * the following differences: + * + * 1) A CHRE timestamp field, which can be useful if CHRE is batching results. + * 2) Reordering of the rssi and periodicAdvertisingInterval fields for memory + * alignment (prevent padding). + * 3) Addition of four reserved bytes to reclaim padding. + */ +struct chreBleAdvertisingReport { + //! The base timestamp, in nanoseconds, in the same time base as chreGetTime() + uint64_t timestamp; + + //! @see CHRE_BLE_EVENT + uint8_t eventTypeAndDataStatus; + + //! Advertising address type as defined in enum chreBleAddressType + uint8_t addressType; + + //! Advertising device address + uint8_t address[CHRE_BLE_ADDRESS_LEN]; + + //! Advertiser PHY on primary advertising physical channel, if supported, as + //! defined in enum chreBlePhyType. + uint8_t primaryPhy; + + //! Advertiser PHY on secondary advertising physical channel, if supported, as + //! defined in enum chreBlePhyType. + uint8_t secondaryPhy; + + //! Value of the Advertising SID subfield in the ADI field of the PDU among + //! the range of [0, 0x0f]. + //! CHRE_BLE_ADI_NONE indicates no ADI field was provided. + //! Other values are reserved. + uint8_t advertisingSid; + + //! Transmit (Tx) power in dBm. Typical values are [-127, 20]. + //! CHRE_BLE_TX_POWER_NONE indicates Tx power not available. + int8_t txPower; + + //! Interval of the periodic advertising in 1.25 ms intervals, i.e. + //! time = periodicAdvertisingInterval * 1.25 ms + //! 0 means no periodic advertising. Minimum value is otherwise 6 (7.5 ms). + uint16_t periodicAdvertisingInterval; + + //! RSSI in dBm. Typical values are [-127, 20]. + //! CHRE_BLE_RSSI_NONE indicates RSSI is not available. + int8_t rssi; + + //! Direct address type (i.e. only accept connection requests from a known + //! peer device) as defined in enum chreBleAddressType. + uint8_t directAddressType; + + //! Direct address (i.e. only accept connection requests from a known peer + //! device). + uint8_t directAddress[CHRE_BLE_ADDRESS_LEN]; + + //! Length of data field. Acceptable range is [0, 62] for legacy and + //! [0, 255] for extended advertisements. + uint16_t dataLength; + + //! dataLength bytes of data, or null if dataLength is 0. This represents + //! the ADV_IND payload, optionally concatenated with SCAN_RSP, as indicated + //! by eventTypeAndDataStatus. + const uint8_t *data; + + //! Reserved for future use; set to 0 + uint32_t reserved; +}; + +/** + * A CHRE BLE Advertising Event can contain any number of CHRE BLE Advertising + * Reports (i.e. advertisements). + */ +struct chreBleAdvertisementEvent { + //! Reserved for future use; set to 0 + uint16_t reserved; + + //! Number of advertising reports in this event + uint16_t numReports; + + //! Array of length numReports + const struct chreBleAdvertisingReport *reports; +}; + +/** + * The RSSI read on a particular LE connection handle, based on the parameters + * in BT Core Spec v5.3, Vol 4, Part E, Section 7.5.4, Read RSSI command + */ +struct chreBleReadRssiEvent { + //! Structure which contains the cookie associated with the original request, + //! along with an error code that indicates request success or failure. + struct chreAsyncResult result; + + //! The handle upon which CHRE attempted to read RSSI. + uint16_t connectionHandle; + + //! The RSSI of the last packet received on this connection, if valid + //! (-127 to 20) + int8_t rssi; +}; + +/** + * Describes the current status of the BLE request in the platform. + * + * @since v1.8 + */ +struct chreBleScanStatus { + //! The currently configured report delay in the scan configuration. + //! If enabled is false, this value does not have meaning. + uint32_t reportDelayMs; + + //! True if the BLE scan is currently enabled. This can be set to false + //! if BLE scan was temporarily disabled (e.g. BT subsystem is down, + //! or due to user settings). + bool enabled; + + //! Reserved for future use - set to zero. + uint8_t reserved[3]; +}; + +/** + * Retrieves a set of flags indicating the BLE features supported by the + * current CHRE implementation. The value returned by this function must be + * consistent for the entire duration of the nanoapp's execution. + * + * The client must allow for more flags to be set in this response than it knows + * about, for example if the implementation supports a newer version of the API + * than the client was compiled against. + * + * @return A bitmask with zero or more CHRE_BLE_CAPABILITIES_* flags set. @see + * CHRE_BLE_CAPABILITIES + * + * @since v1.6 + */ +uint32_t chreBleGetCapabilities(void); + +/** + * Retrieves a set of flags indicating the BLE filtering features supported by + * the current CHRE implementation. The value returned by this function must be + * consistent for the entire duration of the nanoapp's execution. + * + * The client must allow for more flags to be set in this response than it knows + * about, for example if the implementation supports a newer version of the API + * than the client was compiled against. + * + * @return A bitmask with zero or more CHRE_BLE_FILTER_CAPABILITIES_* flags set. + * @see CHRE_BLE_FILTER_CAPABILITIES + * + * @since v1.6 + */ +uint32_t chreBleGetFilterCapabilities(void); + +/** + * Helper function to extract event type from eventTypeAndDataStatus as defined + * in the BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended + * Advertising Report event, entry Event_Type. + * + * @see CHRE_BLE_EVENT + * + * @param eventTypeAndDataStatus Combined event type and data status + * + * @return The event type portion of eventTypeAndDataStatus + */ +static inline uint8_t chreBleGetEventType(uint8_t eventTypeAndDataStatus) { + return (eventTypeAndDataStatus & CHRE_BLE_EVENT_MASK_TYPE); +} + +/** + * Helper function to extract data status from eventTypeAndDataStatus as defined + * in the BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended + * Advertising Report event, entry Event_Type. + * + * @see CHRE_BLE_EVENT + * + * @param eventTypeAndDataStatus Combined event type and data status + * + * @return The data status portion of eventTypeAndDataStatus + */ +static inline uint8_t chreBleGetDataStatus(uint8_t eventTypeAndDataStatus) { + return (eventTypeAndDataStatus & CHRE_BLE_EVENT_MASK_DATA_STATUS); +} + +/** + * Helper function to to combine an event type with a data status to create + * eventTypeAndDataStatus as defined in the BT Core Spec v5.2, Vol 4, Part E, + * Section 7.7.65.13, LE Extended Advertising Report event, entry Event_Type. + * + * @see CHRE_BLE_EVENT + * + * @param eventType Event type + * @param dataStatus Data status + * + * @return A combined eventTypeAndDataStatus + */ +static inline uint8_t chreBleGetEventTypeAndDataStatus(uint8_t eventType, + uint8_t dataStatus) { + return ((eventType & CHRE_BLE_EVENT_MASK_TYPE) | + (dataStatus & CHRE_BLE_EVENT_MASK_DATA_STATUS)); +} + +/** + * Nanoapps must define CHRE_NANOAPP_USES_BLE somewhere in their build + * system (e.g. Makefile) if the nanoapp needs to use the following BLE APIs. + * In addition to allowing access to these APIs, defining this macro will also + * ensure CHRE enforces that all host clients this nanoapp talks to have the + * required Android permissions needed to access BLE functionality by adding + * metadata to the nanoapp. + */ +#if defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD) + +/** + * Start Bluetooth LE (BLE) scanning on CHRE. + * + * The result of the operation will be delivered asynchronously via the CHRE + * event CHRE_EVENT_BLE_ASYNC_RESULT. + * + * The scan results will be delivered asynchronously via the CHRE event + * CHRE_EVENT_BLE_ADVERTISEMENT. + * + * If CHRE_USER_SETTING_BLE_AVAILABLE is disabled, CHRE is expected to return an + * async result with error CHRE_ERROR_FUNCTION_DISABLED. If this setting is + * enabled, the Bluetooth subsystem may still be powered down in the scenario + * where the main Bluetooth toggle is disabled, but the Bluetooth scanning + * setting is enabled, and there is no request for BLE to be enabled at the + * Android level. In this scenario, CHRE will return an async result with error + * CHRE_ERROR_FUNCTION_DISABLED. + * + * To ensure that Bluetooth remains powered on in this settings configuration so + * that a nanoapp can scan, the nanoapp's Android host entity should use the + * BluetoothAdapter.enableBLE() API to register this request with the Android + * Bluetooth stack. + * + * If chreBleStartScanAsync() is called while a previous scan has been started, + * the previous scan will be stopped first and replaced with the new scan. + * + * Note that some corresponding Android parameters are missing from the CHRE + * API, where the following default or typical parameters are used: + * Callback type: CALLBACK_TYPE_ALL_MATCHES + * Result type: SCAN_RESULT_TYPE_FULL + * Match mode: MATCH_MODE_AGGRESSIVE + * Number of matches per filter: MATCH_NUM_MAX_ADVERTISEMENT + * Legacy-only: false + * PHY type: PHY_LE_ALL_SUPPORTED + * + * For v1.8 and greater, a CHRE_EVENT_BLE_SCAN_STATUS_CHANGE will be generated + * if the values in chreBleScanStatus changes as a result of this call. + * + * @param mode Scanning mode selected among enum chreBleScanMode + * @param reportDelayMs Maximum requested batching delay in ms. 0 indicates no + * batching. Note that the system may deliver results + * before the maximum specified delay is reached. + * @param filter Pointer to the requested best-effort filter configuration as + * defined by struct chreBleScanFilter. The ownership of filter + * and its nested elements remains with the caller, and the caller + * may release it as soon as chreBleStartScanAsync() returns. + * + * @return True to indicate that the request was accepted. False otherwise. + * + * @since v1.6 + */ +bool chreBleStartScanAsync(enum chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilter *filter); +/** + * Stops a CHRE BLE scan. + * + * The result of the operation will be delivered asynchronously via the CHRE + * event CHRE_EVENT_BLE_ASYNC_RESULT. + * + * @return True to indicate that the request was accepted. False otherwise. + * + * @since v1.6 + */ +bool chreBleStopScanAsync(void); + +/** + * Requests to immediately deliver batched scan results. The nanoapp must + * have an active BLE scan request. If a request is accepted, it will be treated + * as though the reportDelayMs has expired for a batched scan. Upon accepting + * the request, CHRE works to immediately deliver scan results currently kept in + * batching memory, if any, via regular CHRE_EVENT_BLE_ADVERTISEMENT events, + * followed by a CHRE_EVENT_BLE_FLUSH_COMPLETE event. + * + * If the underlying system fails to complete the flush operation within + * CHRE_BLE_FLUSH_COMPLETE_TIMEOUT_NS, CHRE will send a + * CHRE_EVENT_BLE_FLUSH_COMPLETE event with CHRE_ERROR_TIMEOUT. + * + * If multiple flush requests are made prior to flush completion, then the + * requesting nanoapp will receive all batched samples existing at the time of + * the latest flush request. In this case, the number of + * CHRE_EVENT_BLE_FLUSH_COMPLETE events received must equal the number of flush + * requests made. + * + * If chreBleStopScanAsync() is called while a flush operation is in progress, + * it is unspecified whether the flush operation will complete successfully or + * return an error, such as CHRE_ERROR_FUNCTION_DISABLED, but in any case, + * CHRE_EVENT_BLE_FLUSH_COMPLETE must still be delivered. The same applies if + * the Bluetooth user setting is disabled during a flush operation. + * + * If called while running on a CHRE API version below v1.7, this function + * returns false and has no effect. + * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent as a response to this request. + * + * @return True to indicate the request was accepted. False otherwise. + * + * @since v1.7 + */ +bool chreBleFlushAsync(const void *cookie); + +/** + * Requests to read the RSSI of a peer device on the given LE connection + * handle. + * + * If the request is accepted, the response will be delivered in a + * CHRE_EVENT_BLE_RSSI_READ event with the same cookie. + * + * The request may be rejected if resources are not available to service the + * request (such as if too many outstanding requests already exist). If so, the + * client may retry later. + * + * Note that the connectionHandle is valid only while the connection remains + * active. If a peer device disconnects then reconnects, the handle may change. + * BluetoothDevice#getConnectionHandle() can be used from the Android framework + * to get the latest handle upon reconnection. + * + * @param connectionHandle + * @param cookie An opaque value that will be included in the chreAsyncResult + * embedded in the response to this request. + * @return True if the request has been accepted and dispatched to the + * controller. False otherwise. + * + * @since v1.8 + * + */ +bool chreBleReadRssiAsync(uint16_t connectionHandle, const void *cookie); + +/** + * Retrieves the current state of the BLE scan on the platform. + * + * @param status A non-null pointer to where the scan status will be + * populated. + * + * @return True if the status was obtained successfully. + * + * @since v1.8 + */ +bool chreBleGetScanStatus(struct chreBleScanStatus *status); + +/** + * Definitions for handling unsupported CHRE BLE scenarios. + */ +#else // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD) + +#define CHRE_BLE_PERM_ERROR_STRING \ + "CHRE_NANOAPP_USES_BLE must be defined when building this nanoapp in " \ + "order to refer to " + +#define chreBleStartScanAsync(...) \ + CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleStartScanAsync") + +#define chreBleStopScanAsync(...) \ + CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleStopScanAsync") + +#define chreBleFlushAsync(...) \ + CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleFlushAsync") + +#define chreBleReadRssiAsync(...) \ + CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleReadRssiAsync") + +#endif // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD) + +#ifdef __cplusplus +} +#endif + +#endif /* CHRE_BLE_H_ */ diff --git a/chre_api/legacy/v1_8/chre/common.h b/chre_api/legacy/v1_8/chre/common.h new file mode 100644 index 00000000..8e2df597 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/common.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_COMMON_H_ +#define _CHRE_COMMON_H_ + +/** + * @file + * Definitions shared across multiple CHRE header files + */ + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Mask of the 5 most significant bytes in a 64-bit nanoapp or CHRE platform + * identifier, which represents the vendor ID portion of the ID. + */ +#define CHRE_VENDOR_ID_MASK UINT64_C(0xFFFFFFFFFF000000) + +/** + * Vendor ID "Googl". Used in nanoapp IDs and CHRE platform IDs developed and + * released by Google. + */ +#define CHRE_VENDOR_ID_GOOGLE UINT64_C(0x476F6F676C000000) + +/** + * Vendor ID "GoogT". Used for nanoapp IDs associated with testing done by + * Google. + */ +#define CHRE_VENDOR_ID_GOOGLE_TEST UINT64_C(0x476F6F6754000000) + +/** + * Helper macro to mask off all bytes other than the vendor ID (most significant + * 5 bytes) in 64-bit nanoapp and CHRE platform identifiers. + * + * @see chreGetNanoappInfo() + * @see chreGetPlatformId() + */ +#define CHRE_EXTRACT_VENDOR_ID(id) ((id) & CHRE_VENDOR_ID_MASK) + +/** + * Number of nanoseconds in one second, represented as an unsigned 64-bit + * integer + */ +#define CHRE_NSEC_PER_SEC UINT64_C(1000000000) + +/** + * General timeout for asynchronous API requests. Unless specified otherwise, a + * function call that returns data asynchronously via an event, such as + * CHRE_EVENT_ASYNC_GNSS_RESULT, must do so within this amount of time. + */ +#define CHRE_ASYNC_RESULT_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC) + + +/** + * A generic listing of error codes for use in {@link #chreAsyncResult} and + * elsewhere. In general, module-specific error codes may be added to this enum, + * but effort should be made to come up with a generic name that still captures + * the meaning of the error. + */ +// LINT.IfChange +enum chreError { + //! No error occurred + CHRE_ERROR_NONE = 0, + + //! An unspecified failure occurred + CHRE_ERROR = 1, + + //! One or more supplied arguments are invalid + CHRE_ERROR_INVALID_ARGUMENT = 2, + + //! Unable to satisfy request because the system is busy + CHRE_ERROR_BUSY = 3, + + //! Unable to allocate memory + CHRE_ERROR_NO_MEMORY = 4, + + //! The requested feature is not supported + CHRE_ERROR_NOT_SUPPORTED = 5, + + //! A timeout occurred while processing the request + CHRE_ERROR_TIMEOUT = 6, + + //! The relevant capability is disabled, for example due to a user + //! configuration that takes precedence over this request + CHRE_ERROR_FUNCTION_DISABLED = 7, + + //! The request was rejected due to internal rate limiting of the requested + //! functionality - the client may try its request again after waiting an + //! unspecified amount of time + CHRE_ERROR_REJECTED_RATE_LIMIT = 8, + + //! The requested functionality is not currently accessible from the CHRE, + //! because another client, such as the main applications processor, is + //! currently controlling it. + CHRE_ERROR_FUNCTION_RESTRICTED_TO_OTHER_MASTER = 9, + CHRE_ERROR_FUNCTION_RESTRICTED_TO_OTHER_CLIENT = 9, + + //! This request is no longer valid. It may have been replaced by a newer + //! request before taking effect. + CHRE_ERROR_OBSOLETE_REQUEST = 10, + + //!< Do not exceed this value when adding new error codes + CHRE_ERROR_LAST = UINT8_MAX, +}; +// LINT.ThenChange(../../../../core/include/chre/core/api_manager_common.h) + +/** + * Generic data structure to indicate the result of an asynchronous operation. + * + * @note + * The general model followed by CHRE for asynchronous operations is that a + * request function returns a boolean value that indicates whether the request + * was accepted for further processing. The actual result of the operation is + * provided in a subsequent event sent with an event type that is defined in the + * specific API. Typically, a "cookie" parameter is supplied to allow the client + * to tie the response to a specific request, or pass data through, etc. The + * response is expected to be delivered within CHRE_ASYNC_RESULT_TIMEOUT_NS if + * not specified otherwise. + * + * The CHRE implementation must allow for multiple asynchronous requests to be + * outstanding at a given time, under reasonable resource constraints. Further, + * requests must be processed in the same order as supplied by the client of the + * API in order to maintain causality. Using GNSS as an example, if a client + * calls chreGnssLocationSessionStartAsync() and then immediately calls + * chreGnssLocationSessionStopAsync(), the final result must be that the + * location session is stopped. Whether requests always complete in the + * order that they are given is implementation-defined. For example, if a client + * calls chreGnssLocationSessionStart() and then immediately calls + * chreGnssMeasurementSessionStart(), it is possible for the + * CHRE_EVENT_GNSS_RESULT associated with the measurement session to be + * delivered before the one for the location session. + */ +struct chreAsyncResult { + //! Indicates the request associated with this result. The interpretation of + //! values in this field is dependent upon the event type provided when this + //! result was delivered. + uint8_t requestType; + + //! Set to true if the request was successfully processed + bool success; + + //! If the request failed (success is false), this is set to a value from + //! enum chreError (other than CHRE_ERROR_NONE), which may provide + //! additional information about the nature of the failure. + //! @see #chreError + uint8_t errorCode; + + //! Reserved for future use, set to 0 + uint8_t reserved; + + //! Set to the cookie parameter given to the request function tied to this + //! result + const void *cookie; +}; + +/** + * A structure to store an event describing the end of batched events. + * + * @since v1.8 + */ +struct chreBatchCompleteEvent { + //! Indicates the type of event (of type CHRE_EVENT_TYPE_*) that was + //! batched. + uint16_t eventType; + + //! Reserved for future use, set to 0 + uint8_t reserved[2]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_COMMON_H_ */ diff --git a/chre_api/legacy/v1_8/chre/event.h b/chre_api/legacy/v1_8/chre/event.h new file mode 100644 index 00000000..e519c3d3 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/event.h @@ -0,0 +1,948 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_EVENT_H_ +#define _CHRE_EVENT_H_ + +/** + * @file + * Context Hub Runtime Environment API dealing with events and messages. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include <chre/toolchain.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The CHRE implementation is required to provide the following preprocessor + * defines via the build system. + * + * CHRE_MESSAGE_TO_HOST_MAX_SIZE: The maximum size, in bytes, allowed for + * a message sent to chreSendMessageToHostEndpoint(). This must be at least + * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE. + */ + +#ifndef CHRE_MESSAGE_TO_HOST_MAX_SIZE +#error CHRE_MESSAGE_TO_HOST_MAX_SIZE must be defined by the CHRE implementation +#endif + +/** + * The minimum size, in bytes, any CHRE implementation will use for + * CHRE_MESSAGE_TO_HOST_MAX_SIZE is set to 1000 for v1.5+ CHRE implementations, + * and 128 for v1.0-v1.4 implementations (previously kept in + * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, which has been removed). + * + * All CHRE implementations supporting v1.5+ must support the raised limit of + * 1000 bytes, however a nanoapp compiled against v1.5 cannot assume this + * limit if there is a possibility their binary will run on a v1.4 or earlier + * implementation that had a lower limit. To allow for nanoapp compilation in + * these situations, CHRE_MESSAGE_TO_HOST_MAX_SIZE must be set to the minimum + * value the nanoapp may encounter, and CHRE_NANOAPP_SUPPORTS_PRE_V1_5 can be + * defined to skip the compile-time check. + */ +#if (!defined(CHRE_NANOAPP_SUPPORTS_PRE_V1_5) && \ + CHRE_MESSAGE_TO_HOST_MAX_SIZE < 1000) || \ + (defined(CHRE_NANOAPP_SUPPORTS_PRE_V1_5) && \ + CHRE_MESSAGE_TO_HOST_MAX_SIZE < 128) +#error CHRE_MESSAGE_TO_HOST_MAX_SIZE is too small. +#endif + +/** + * The lowest numerical value legal for a user-defined event. + * + * The system reserves all event values from 0 to 0x7FFF, inclusive. + * User events may use any value in the range 0x8000 to 0xFFFF, inclusive. + * + * Note that the same event values might be used by different nanoapps + * for different meanings. This is not a concern, as these values only + * have meaning when paired with the originating nanoapp. + */ +#define CHRE_EVENT_FIRST_USER_VALUE UINT16_C(0x8000) + +/** + * nanoappHandleEvent argument: struct chreMessageFromHostData + * + * The format of the 'message' part of this structure is left undefined, + * and it's up to the nanoapp and host to have an established protocol + * beforehand. + */ +#define CHRE_EVENT_MESSAGE_FROM_HOST UINT16_C(0x0001) + +/** + * nanoappHandleEvent argument: 'cookie' given to chreTimerSet() method. + * + * Indicates that a timer has elapsed, in accordance with how chreTimerSet() was + * invoked. + */ +#define CHRE_EVENT_TIMER UINT16_C(0x0002) + +/** + * nanoappHandleEvent argument: struct chreNanoappInfo + * + * Indicates that a nanoapp has successfully started (its nanoappStart() + * function has been called, and it returned true) and is able to receive events + * sent via chreSendEvent(). Note that this event is not sent for nanoapps that + * were started prior to the current nanoapp - use chreGetNanoappInfo() to + * determine if another nanoapp is already running. + * + * @see chreConfigureNanoappInfoEvents + * @since v1.1 + */ +#define CHRE_EVENT_NANOAPP_STARTED UINT16_C(0x0003) + +/** + * nanoappHandleEvent argument: struct chreNanoappInfo + * + * Indicates that a nanoapp has stopped executing and is no longer able to + * receive events sent via chreSendEvent(). Any events sent prior to receiving + * this event are not guaranteed to have been delivered. + * + * @see chreConfigureNanoappInfoEvents + * @since v1.1 + */ +#define CHRE_EVENT_NANOAPP_STOPPED UINT16_C(0x0004) + +/** + * nanoappHandleEvent argument: NULL + * + * Indicates that CHRE has observed the host wake from low-power sleep state. + * + * @see chreConfigureHostSleepStateEvents + * @since v1.2 + */ +#define CHRE_EVENT_HOST_AWAKE UINT16_C(0x0005) + +/** + * nanoappHandleEvent argument: NULL + * + * Indicates that CHRE has observed the host enter low-power sleep state. + * + * @see chreConfigureHostSleepStateEvents + * @since v1.2 + */ +#define CHRE_EVENT_HOST_ASLEEP UINT16_C(0x0006) + +/** + * nanoappHandleEvent argument: NULL + * + * Indicates that CHRE is collecting debug dumps. Nanoapps can call + * chreDebugDumpLog() to log their debug data while handling this event. + * + * @see chreConfigureDebugDumpEvent + * @see chreDebugDumpLog + * @since v1.4 + */ +#define CHRE_EVENT_DEBUG_DUMP UINT16_C(0x0007) + +/** + * nanoappHandleEvent argument: struct chreHostEndpointNotification + * + * Notifications event regarding a host endpoint. + * + * @see chreConfigureHostEndpointNotifications + * @since v1.6 + */ +#define CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION UINT16_C(0x0008) + +/** + * First possible value for CHRE_EVENT_SENSOR events. + * + * This allows us to separately define our CHRE_EVENT_SENSOR_* events in + * chre/sensor.h, without fear of collision with other event values. + */ +#define CHRE_EVENT_SENSOR_FIRST_EVENT UINT16_C(0x0100) + +/** + * Last possible value for CHRE_EVENT_SENSOR events. + * + * This allows us to separately define our CHRE_EVENT_SENSOR_* events in + * chre/sensor.h, without fear of collision with other event values. + */ +#define CHRE_EVENT_SENSOR_LAST_EVENT UINT16_C(0x02FF) + +/** + * First event in the block reserved for GNSS. These events are defined in + * chre/gnss.h. + */ +#define CHRE_EVENT_GNSS_FIRST_EVENT UINT16_C(0x0300) +#define CHRE_EVENT_GNSS_LAST_EVENT UINT16_C(0x030F) + +/** + * First event in the block reserved for WiFi. These events are defined in + * chre/wifi.h. + */ +#define CHRE_EVENT_WIFI_FIRST_EVENT UINT16_C(0x0310) +#define CHRE_EVENT_WIFI_LAST_EVENT UINT16_C(0x031F) + +/** + * First event in the block reserved for WWAN. These events are defined in + * chre/wwan.h. + */ +#define CHRE_EVENT_WWAN_FIRST_EVENT UINT16_C(0x0320) +#define CHRE_EVENT_WWAN_LAST_EVENT UINT16_C(0x032F) + +/** + * First event in the block reserved for audio. These events are defined in + * chre/audio.h. + */ +#define CHRE_EVENT_AUDIO_FIRST_EVENT UINT16_C(0x0330) +#define CHRE_EVENT_AUDIO_LAST_EVENT UINT16_C(0x033F) + +/** + * First event in the block reserved for settings changed notifications. + * These events are defined in chre/user_settings.h + * + * @since v1.5 + */ +#define CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT UINT16_C(0x340) +#define CHRE_EVENT_SETTING_CHANGED_LAST_EVENT UINT16_C(0x34F) + +/** + * First event in the block reserved for Bluetooth LE. These events are defined + * in chre/ble.h. + */ +#define CHRE_EVENT_BLE_FIRST_EVENT UINT16_C(0x0350) +#define CHRE_EVENT_BLE_LAST_EVENT UINT16_C(0x035F) + +/** + * First in the extended range of values dedicated for internal CHRE + * implementation usage. + * + * This range is semantically the same as the internal event range defined + * below, but has been extended to allow for more implementation-specific events + * to be used. + * + * @since v1.1 + */ +#define CHRE_EVENT_INTERNAL_EXTENDED_FIRST_EVENT UINT16_C(0x7000) + +/** + * First in a range of values dedicated for internal CHRE implementation usage. + * + * If a CHRE wishes to use events internally, any values within this range + * are assured not to be taken by future CHRE API additions. + */ +#define CHRE_EVENT_INTERNAL_FIRST_EVENT UINT16_C(0x7E00) + +/** + * Last in a range of values dedicated for internal CHRE implementation usage. + * + * If a CHRE wishes to use events internally, any values within this range + * are assured not to be taken by future CHRE API additions. + */ +#define CHRE_EVENT_INTERNAL_LAST_EVENT UINT16_C(0x7FFF) + +/** + * A special value for the hostEndpoint argument in + * chreSendMessageToHostEndpoint() that indicates that the message should be + * delivered to all host endpoints. This value will not be used in the + * hostEndpoint field of struct chreMessageFromHostData supplied with + * CHRE_EVENT_MESSAGE_FROM_HOST. + * + * @since v1.1 + */ +#define CHRE_HOST_ENDPOINT_BROADCAST UINT16_C(0xFFFF) + +/** + * A special value for hostEndpoint in struct chreMessageFromHostData that + * indicates that a host endpoint is unknown or otherwise unspecified. This + * value may be received in CHRE_EVENT_MESSAGE_FROM_HOST, but it is not valid to + * provide it to chreSendMessageToHostEndpoint(). + * + * @since v1.1 + */ +#define CHRE_HOST_ENDPOINT_UNSPECIFIED UINT16_C(0xFFFE) + +/** + * Bitmask values that can be given as input to the messagePermissions parameter + * of chreSendMessageWithPermissions(). These values are typically used by + * nanoapps when they used data from the corresponding CHRE APIs to produce the + * message contents being sent and is used to attribute permissions usage on + * the Android side. See chreSendMessageWithPermissions() for more details on + * how these values are used when sending a message. + * + * Values in the range + * [CHRE_MESSAGE_PERMISSION_VENDOR_START, CHRE_MESSAGE_PERMISSION_VENDOR_END] + * are reserved for vendors to use when adding support for permission-gated APIs + * in their implementations. + * + * On the Android side, CHRE permissions are mapped as follows: + * - CHRE_MESSAGE_PERMISSION_AUDIO: android.permission.RECORD_AUDIO + * - CHRE_MESSAGE_PERMISSION_GNSS, CHRE_MESSAGE_PERMISSION_WIFI, and + * CHRE_MESSAGE_PERMISSION_WWAN: android.permission.ACCESS_FINE_LOCATION, and + * android.permissions.ACCESS_BACKGROUND_LOCATION + * + * @since v1.5 + * + * @defgroup CHRE_MESSAGE_PERMISSION + * @{ + */ + +#define CHRE_MESSAGE_PERMISSION_NONE UINT32_C(0) +#define CHRE_MESSAGE_PERMISSION_AUDIO UINT32_C(1) +#define CHRE_MESSAGE_PERMISSION_GNSS (UINT32_C(1) << 1) +#define CHRE_MESSAGE_PERMISSION_WIFI (UINT32_C(1) << 2) +#define CHRE_MESSAGE_PERMISSION_WWAN (UINT32_C(1) << 3) +#define CHRE_MESSAGE_PERMISSION_BLE (UINT32_C(1) << 4) +#define CHRE_MESSAGE_PERMISSION_VENDOR_START (UINT32_C(1) << 24) +#define CHRE_MESSAGE_PERMISSION_VENDOR_END (UINT32_C(1) << 31) + +/** @} */ + +/** + * @see chrePublishRpcServices + * + * @since v1.8 + */ +#define CHRE_MINIMUM_RPC_SERVICE_LIMIT UINT8_C(4) + +/** + * Data provided with CHRE_EVENT_MESSAGE_FROM_HOST. + */ +struct chreMessageFromHostData { + /** + * Message type supplied by the host. + * + * @note In CHRE API v1.0, support for forwarding this field from the host + * was not strictly required, and some implementations did not support it. + * However, its support is mandatory as of v1.1. + */ + union { + /** + * The preferred name to use when referencing this field. + * + * @since v1.1 + */ + uint32_t messageType; + + /** + * @deprecated This is the name for the messageType field used in v1.0. + * Left to allow code to compile against both v1.0 and v1.1 of the API + * definition without needing to use #ifdefs. This will be removed in a + * future API update - use messageType instead. + */ + uint32_t reservedMessageType; + }; + + /** + * The size, in bytes of the following 'message'. + * + * This can be 0. + */ + uint32_t messageSize; + + /** + * The message from the host. + * + * These contents are of a format that the host and nanoapp must have + * established beforehand. + * + * This data is 'messageSize' bytes in length. Note that if 'messageSize' + * is 0, this might be NULL. + */ + const void *message; + + /** + * An identifier for the host-side entity that sent this message. Unless + * this is set to CHRE_HOST_ENDPOINT_UNSPECIFIED, it can be used in + * chreSendMessageToHostEndpoint() to send a directed reply that will only + * be received by the given entity on the host. Endpoint identifiers are + * opaque values assigned at runtime, so they cannot be assumed to always + * describe a specific entity across restarts. + * + * If running on a CHRE API v1.0 implementation, this field will always be + * set to CHRE_HOST_ENDPOINT_UNSPECIFIED. + * + * @since v1.1 + */ + uint16_t hostEndpoint; +}; + +/** + * Provides metadata for a nanoapp in the system. + */ +struct chreNanoappInfo { + /** + * Nanoapp identifier. The convention for populating this value is to set + * the most significant 5 bytes to a value that uniquely identifies the + * vendor, and the lower 3 bytes identify the nanoapp. + */ + uint64_t appId; + + /** + * Nanoapp version. The semantics of this field are defined by the nanoapp, + * however nanoapps are recommended to follow the same scheme used for the + * CHRE version exposed in chreGetVersion(). That is, the most significant + * byte represents the major version, the next byte the minor version, and + * the lower two bytes the patch version. + */ + uint32_t version; + + /** + * The instance ID of this nanoapp, which can be used in chreSendEvent() to + * address an event specifically to this nanoapp. This identifier is + * guaranteed to be unique among all nanoapps in the system. + * + * As of CHRE API v1.6, instance ID is guaranteed to never be greater than + * UINT16_MAX. This allows for the instance ID be packed with other data + * inside a 32-bit integer (useful for RPC routing). + */ + uint32_t instanceId; + + /** + * Reserved for future use. + * Always set to 0. + */ + uint8_t reserved[3]; + + /** + * The number of RPC services exposed by this nanoapp. + * The service details are available in the rpcServices array. + * Must always be set to 0 when running on a CHRE implementation prior to + * v1.8 + * + * @since v1.8 + */ + uint8_t rpcServiceCount; + + /* + * Array of RPC services published by this nanoapp. + * Services are published via chrePublishRpcServices. + * The array contains rpcServiceCount entries. + * + * The pointer is only valid when rpcServiceCount is greater than 0. + * + * @since v1.8 + */ + const struct chreNanoappRpcService *rpcServices; +}; + +/** + * The types of notification events that can be included in struct + * chreHostEndpointNotification. + * + * @defgroup HOST_ENDPOINT_NOTIFICATION_TYPE + * @{ + */ +#define HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT UINT8_C(0) +/** @} */ + +/** + * Data provided in CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION. + */ +struct chreHostEndpointNotification { + /** + * The ID of the host endpoint that this notification is for. + */ + uint16_t hostEndpointId; + + /** + * The type of notification this event represents, which should be + * one of the HOST_ENDPOINT_NOTIFICATION_TYPE_* values. + */ + uint8_t notificationType; + + /** + * Reserved for future use, must be zero. + */ + uint8_t reserved; +}; + +//! The maximum length of a host endpoint's name. +#define CHRE_MAX_ENDPOINT_NAME_LEN (51) + +//! The maximum length of a host endpoint's tag. +#define CHRE_MAX_ENDPOINT_TAG_LEN (51) + +/** + * The type of host endpoint that can be used in the hostEndpointType field + * of chreHostEndpointInfo. + * + * @since v1.6 + * + * @defgroup CHRE_HOST_ENDPOINT_TYPE_ + * @{ + */ + +//! The host endpoint is part of the Android system framework. +#define CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK UINT8_C(0) + +//! The host endpoint is an Android app. +#define CHRE_HOST_ENDPOINT_TYPE_APP UINT8_C(1) + +//! The host endpoint is an Android native program. +#define CHRE_HOST_ENDPOINT_TYPE_NATIVE UINT8_C(2) + +//! Values in the range [CHRE_HOST_ENDPOINT_TYPE_VENDOR_START, +//! CHRE_HOST_ENDPOINT_TYPE_VENDOR_END] can be a custom defined host endpoint +//! type for platform-specific vendor use. +#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_START UINT8_C(128) +#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_END UINT8_C(255) + +/** @} */ + +/** + * Provides metadata for a host endpoint. + * + * @since v1.6 + */ +struct chreHostEndpointInfo { + //! The endpoint ID of this host. + uint16_t hostEndpointId; + + //! The type of host endpoint, which must be set to one of the + //! CHRE_HOST_ENDPOINT_TYPE_* values or a value in the vendor-reserved + //! range. + uint8_t hostEndpointType; + + //! Flag indicating if the packageName/endpointName field is valid. + uint8_t isNameValid : 1; + + //! Flag indicating if the attributionTag/endpointTag field is valid. + uint8_t isTagValid : 1; + + //! A union of null-terminated host name strings. + union { + //! The Android package name associated with this host, valid if the + //! hostEndpointType is CHRE_HOST_ENDPOINT_TYPE_APP or + //! CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK. Refer to the Android documentation + //! for the package attribute in the app manifest. + char packageName[CHRE_MAX_ENDPOINT_NAME_LEN]; + + //! A generic endpoint name that can be used for endpoints that + //! may not have a package name. + char endpointName[CHRE_MAX_ENDPOINT_NAME_LEN]; + }; + + //! A union of null-terminated host tag strings for further identification. + union { + //! The attribution tag associated with this host that is used to audit + //! access to data, which can be valid if the hostEndpointType is + //! CHRE_HOST_ENDPOINT_TYPE_APP. Refer to the Android documentation + //! regarding data audit using attribution tags. + char attributionTag[CHRE_MAX_ENDPOINT_TAG_LEN]; + + //! A generic endpoint tag that can be used for endpoints that + //! may not have an attribution tag. + char endpointTag[CHRE_MAX_ENDPOINT_TAG_LEN]; + }; +}; + +/** + * An RPC service exposed by a nanoapp. + * + * The implementation of the RPC interface is not defined by the HAL, and is written + * at the messaging endpoint layers (Android app and/or CHRE nanoapp). NanoappRpcService + * contains the informational metadata to be consumed by the RPC interface layer. + */ +struct chreNanoappRpcService { + /** + * The unique 64-bit ID of an RPC service exposed by a nanoapp. Note that + * the uniqueness is only required within the nanoapp's domain (i.e. the + * combination of the nanoapp ID and service id must be unique). + */ + uint64_t id; + + /** + * The software version of this service, which follows the sematic + * versioning scheme (see semver.org). It follows the format + * major.minor.patch, where major and minor versions take up one byte + * each, and the patch version takes up the final 2 bytes. + */ + uint32_t version; +}; + +/** + * Callback which frees data associated with an event. + * + * This callback is (optionally) provided to the chreSendEvent() method as + * a means for freeing the event data and performing any other cleanup + * necessary when the event is completed. When this callback is invoked, + * 'eventData' is no longer needed and can be released. + * + * @param eventType The 'eventType' argument from chreSendEvent(). + * @param eventData The 'eventData' argument from chreSendEvent(). + * + * @see chreSendEvent + */ +typedef void (chreEventCompleteFunction)(uint16_t eventType, void *eventData); + +/** + * Callback which frees a message. + * + * This callback is (optionally) provided to the chreSendMessageToHostEndpoint() + * method as a means for freeing the message. When this callback is invoked, + * 'message' is no longer needed and can be released. Note that this in + * no way assures that said message did or did not make it to the host, simply + * that this memory is no longer needed. + * + * @param message The 'message' argument from chreSendMessageToHostEndpoint(). + * @param messageSize The 'messageSize' argument from + * chreSendMessageToHostEndpoint(). + * + * @see chreSendMessageToHostEndpoint + */ +typedef void (chreMessageFreeFunction)(void *message, size_t messageSize); + + +/** + * Enqueue an event to be sent to another nanoapp. + * + * @param eventType This is a user-defined event type, of at least the + * value CHRE_EVENT_FIRST_USER_VALUE. It is illegal to attempt to use any + * of the CHRE_EVENT_* values reserved for the CHRE. + * @param eventData A pointer value that will be understood by the receiving + * app. Note that NULL is perfectly acceptable. It also is not required + * that this be a valid pointer, although if this nanoapp is intended to + * work on arbitrary CHRE implementations, then the size of a + * pointer cannot be assumed to be a certain size. Note that the caller + * no longer owns this memory after the call. + * @param freeCallback A pointer to a callback function. After the lifetime + * of 'eventData' is over (either through successful delivery or the event + * being dropped), this callback will be invoked. This argument is allowed + * to be NULL, in which case no callback will be invoked. + * @param targetInstanceId The ID of the instance we're delivering this event + * to. Note that this is allowed to be our own instance. The instance ID + * of a nanoapp can be retrieved by using chreGetNanoappInfoByInstanceId(). + * @return true if the event was enqueued, false otherwise. Note that even + * if this method returns 'false', the 'freeCallback' will be invoked, + * if non-NULL. Note in the 'false' case, the 'freeCallback' may be + * invoked directly from within chreSendEvent(), so it's necessary + * for nanoapp authors to avoid possible recursion with this. + * + * @see chreEventDataFreeFunction + */ +bool chreSendEvent(uint16_t eventType, void *eventData, + chreEventCompleteFunction *freeCallback, + uint32_t targetInstanceId); + +/** + * Send a message to the host, using the broadcast endpoint + * CHRE_HOST_ENDPOINT_BROADCAST. Refer to chreSendMessageToHostEndpoint() for + * further details. + * + * @see chreSendMessageToHostEndpoint + * + * @deprecated New code should use chreSendMessageToHostEndpoint() instead of + * this function. A future update to the API may cause references to this + * function to produce a compiler warning. + */ +bool chreSendMessageToHost(void *message, uint32_t messageSize, + uint32_t messageType, + chreMessageFreeFunction *freeCallback) + CHRE_DEPRECATED("Use chreSendMessageToHostEndpoint instead"); + +/** + * Send a message to the host, using CHRE_MESSAGE_PERMISSION_NONE for the + * associated message permissions. This method must only be used if no data + * provided by CHRE's audio, GNSS, WiFi, and WWAN APIs was used to produce the + * contents of the message being sent. Refer to chreSendMessageWithPermissions() + * for further details. + * + * @see chreSendMessageWithPermissions + * + * @since v1.1 + */ +bool chreSendMessageToHostEndpoint(void *message, size_t messageSize, + uint32_t messageType, uint16_t hostEndpoint, + chreMessageFreeFunction *freeCallback); + +/** + * Send a message to the host, waking it up if it is currently asleep. + * + * This message is by definition arbitrarily defined. Since we're not + * just a passing a pointer to memory around the system, but need to copy + * this into various buffers to send it to the host, the CHRE + * implementation cannot be asked to support an arbitrarily large message + * size. As a result, we have the CHRE implementation define + * CHRE_MESSAGE_TO_HOST_MAX_SIZE. + * + * CHRE_MESSAGE_TO_HOST_MAX_SIZE is not given a value by the Platform API. The + * Platform API does define CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, and requires + * that CHRE_MESSAGE_TO_HOST_MAX_SIZE is at least that value. + * + * As a result, if your message sizes are all less than + * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, then you have no concerns on any + * CHRE implementation. If your message sizes are larger, you'll need to + * come up with a strategy for splitting your message across several calls + * to this method. As long as that strategy works for + * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, it will work across all CHRE + * implementations (although on some implementations less calls to this + * method may be necessary). + * + * When sending a message to the host, the ContextHub service will enforce + * the host client has been granted Android-level permissions corresponding to + * the ones the nanoapp declares it uses through CHRE_NANOAPP_USES_AUDIO, etc. + * In addition to this, the permissions bitmask provided as input to this method + * results in the Android framework using app-ops to verify and log access upon + * message delivery to an application. This is primarily useful for ensuring + * accurate attribution for messages generated using permission-controlled data. + * The bitmask declared by the nanoapp for this message must be a + * subset of the permissions it declared it would use at build time or the + * message will be rejected. + * + * Nanoapps must use this method if the data they are sending contains or was + * derived from any data sampled through CHRE's audio, GNSS, WiFi, or WWAN APIs. + * Additionally, if vendors add APIs to expose data that would be guarded by a + * permission in Android, vendors must support declaring a message permission + * through this method. + * + * @param message Pointer to a block of memory to send to the host. + * NULL is acceptable only if messageSize is 0. If non-NULL, this + * must be a legitimate pointer (that is, unlike chreSendEvent(), a small + * integral value cannot be cast to a pointer for this). Note that the + * caller no longer owns this memory after the call. + * @param messageSize The size, in bytes, of the given message. If this exceeds + * CHRE_MESSAGE_TO_HOST_MAX_SIZE, the message will be rejected. + * @param messageType Message type sent to the app on the host. + * NOTE: In CHRE API v1.0, support for forwarding this field to the host was + * not strictly required, and some implementations did not support it. + * However, its support is mandatory as of v1.1. + * @param hostEndpoint An identifier for the intended recipient of the message, + * or CHRE_HOST_ENDPOINT_BROADCAST if all registered endpoints on the host + * should receive the message. Endpoint identifiers are assigned on the + * host side, and nanoapps may learn of the host endpoint ID of an intended + * recipient via an initial message sent by the host. This parameter is + * always treated as CHRE_HOST_ENDPOINT_BROADCAST if running on a CHRE API + * v1.0 implementation. CHRE_HOST_ENDPOINT_BROADCAST isn't allowed to be + * specified if anything other than CHRE_MESSAGE_PERMISSION_NONE is given + * as messagePermissions since doing so would potentially attribute + * permissions usage to host clients that don't intend to consume the data. + * @param messagePermissions Bitmasked CHRE_MESSAGE_PERMISSION_ values that will + * be converted to corresponding Android-level permissions and attributed + * the host endpoint upon consumption of the message. + * @param freeCallback A pointer to a callback function. After the lifetime + * of 'message' is over (which does not assure that 'message' made it to + * the host, just that the transport layer no longer needs this memory), + * this callback will be invoked. This argument is allowed + * to be NULL, in which case no callback will be invoked. + * @return true if the message was accepted for transmission, false otherwise. + * Note that even if this method returns 'false', the 'freeCallback' will + * be invoked, if non-NULL. In either case, the 'freeCallback' may be + * invoked directly from within chreSendMessageToHostEndpoint(), so it's + * necessary for nanoapp authors to avoid possible recursion with this. + * + * @see chreMessageFreeFunction + * + * @since v1.5 + */ +bool chreSendMessageWithPermissions(void *message, size_t messageSize, + uint32_t messageType, uint16_t hostEndpoint, + uint32_t messagePermissions, + chreMessageFreeFunction *freeCallback); + +/** + * Queries for information about a nanoapp running in the system. + * + * In the current API, appId is required to be unique, i.e. there cannot be two + * nanoapps running concurrently with the same appId. If this restriction is + * removed in a future API version and multiple instances of the same appId are + * present, this function must always return the first app to start. + * + * @param appId Identifier for the nanoapp that the caller is requesting + * information about. + * @param info Output parameter. If this function returns true, this structure + * will be populated with details of the specified nanoapp. + * @return true if a nanoapp with the given ID is currently running, and the + * supplied info parameter was populated with its information. + * + * @since v1.1 + */ +bool chreGetNanoappInfoByAppId(uint64_t appId, struct chreNanoappInfo *info); + +/** + * Queries for information about a nanoapp running in the system, using the + * runtime unique identifier. This method can be used to get information about + * the sender of an event. + * + * @param instanceId + * @param info Output parameter. If this function returns true, this structure + * will be populated with details of the specified nanoapp. + * @return true if a nanoapp with the given instance ID is currently running, + * and the supplied info parameter was populated with its information. + * + * @since v1.1 + */ +bool chreGetNanoappInfoByInstanceId(uint32_t instanceId, + struct chreNanoappInfo *info); + +/** + * Configures whether this nanoapp will be notified when other nanoapps in the + * system start and stop, via CHRE_EVENT_NANOAPP_STARTED and + * CHRE_EVENT_NANOAPP_STOPPED. These events are disabled by default, and if a + * nanoapp is not interested in interacting with other nanoapps, then it does + * not need to register for them. However, if inter-nanoapp communication is + * desired, nanoapps are recommended to call this function from nanoappStart(). + * + * If running on a CHRE platform that only supports v1.0 of the CHRE API, this + * function has no effect. + * + * @param enable true to enable these events, false to disable + * + * @see CHRE_EVENT_NANOAPP_STARTED + * @see CHRE_EVENT_NANOAPP_STOPPED + * + * @since v1.1 + */ +void chreConfigureNanoappInfoEvents(bool enable); + +/** + * Configures whether this nanoapp will be notified when the host (applications + * processor) transitions between wake and sleep, via CHRE_EVENT_HOST_AWAKE and + * CHRE_EVENT_HOST_ASLEEP. As chreSendMessageToHostEndpoint() wakes the host if + * it is asleep, these events can be used to opportunistically send data to the + * host only when it wakes up for some other reason. Note that this event is + * not instantaneous - there is an inherent delay in CHRE observing power state + * changes of the host processor, which may be significant depending on the + * implementation, especially in the wake to sleep direction. Therefore, + * nanoapps are not guaranteed that messages sent to the host between AWAKE and + * ASLEEP events will not trigger a host wakeup. However, implementations must + * ensure that the nominal wake-up notification latency is strictly less than + * the minimum wake-sleep time of the host processor. Implementations are also + * encouraged to minimize this and related latencies where possible, to avoid + * unnecessary host wake-ups. + * + * These events are only sent on transitions, so the initial state will not be + * sent to the nanoapp as an event - use chreIsHostAwake(). + * + * @param enable true to enable these events, false to disable + * + * @see CHRE_EVENT_HOST_AWAKE + * @see CHRE_EVENT_HOST_ASLEEP + * + * @since v1.2 + */ +void chreConfigureHostSleepStateEvents(bool enable); + +/** + * Retrieves the current sleep/wake state of the host (applications processor). + * Note that, as with the CHRE_EVENT_HOST_AWAKE and CHRE_EVENT_HOST_ASLEEP + * events, there is no guarantee that CHRE's view of the host processor's sleep + * state is instantaneous, and it may also change between querying the state and + * performing a host-waking action like sending a message to the host. + * + * @return true if by CHRE's own estimation the host is currently awake, + * false otherwise + * + * @since v1.2 + */ +bool chreIsHostAwake(void); + +/** + * Configures whether this nanoapp will be notified when CHRE is collecting + * debug dumps, via CHRE_EVENT_DEBUG_DUMP. This event is disabled by default, + * and if a nanoapp is not interested in logging its debug data, then it does + * not need to register for it. + * + * @param enable true to enable receipt of this event, false to disable. + * + * @see CHRE_EVENT_DEBUG_DUMP + * @see chreDebugDumpLog + * + * @since v1.4 + */ +void chreConfigureDebugDumpEvent(bool enable); + +/** + * Configures whether this nanoapp will receive updates regarding a host + * endpoint that is connected with the Context Hub. + * + * If this API succeeds, the nanoapp will receive disconnection notifications, + * via the CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION event with an eventData of type + * chreHostEndpointNotification with its notificationType set to + * HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT, which can be invoked if the host + * has disconnected from the Context Hub either explicitly or implicitly (e.g. + * crashes). Nanoapps can use this notifications to clean up any resources + * associated with this host endpoint. + * + * @param hostEndpointId The host endpoint ID to configure notifications for. + * @param enable true to enable notifications. + * + * @return true on success + * + * @see chreMessageFromHostData + * @see chreHostEndpointNotification + * @see CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION + * + * @since v1.6 + */ +bool chreConfigureHostEndpointNotifications(uint16_t hostEndpointId, + bool enable); + +/** + * Publishes RPC services from this nanoapp. + * + * When this API is invoked, the list of RPC services will be provided to + * host applications interacting with the nanoapp. + * + * This function must be invoked from nanoappStart(), to guarantee stable output + * of the list of RPC services supported by the nanoapp. + * + * Although nanoapps are recommended to only call this API once with all + * services it intends to publish, if it is called multiple times, each + * call will append to the list of published services. + * + * Starting in CHRE API v1.8, the implementation must allow for a nanoapp to + * publish at least CHRE_MINIMUM_RPC_SERVICE_LIMIT services and at most + * UINT8_MAX services. If calling this function would result in exceeding + * the limit, the services must not be published and it must return false. + * + * @param services A non-null pointer to the list of RPC services to publish. + * @param numServices The number of services to publish, i.e. the length of the + * services array. + * + * @return true if the publishing is successful. + * + * @since v1.6 + */ +bool chrePublishRpcServices(struct chreNanoappRpcService *services, + size_t numServices); + +/** + * Retrieves metadata for a given host endpoint ID. + * + * This API will provide metadata regarding an endpoint associated with a + * host endpoint ID. The nanoapp should use this API to determine more + * information about a host endpoint that has sent a message to the nanoapp, + * after receiving a chreMessageFromHostData (which includes the endpoint ID). + * + * If the given host endpoint ID is not associated with a valid host (or if the + * client has disconnected from the Android or CHRE framework, i.e. no longer + * able to send messages to CHRE), this method will return false and info will + * not be populated. + * + * @param hostEndpointId The endpoint ID of the host to get info for. + * @param info The non-null pointer to where the metadata will be stored. + * + * @return true if info has been successfully populated. + * + * @since v1.6 + */ +bool chreGetHostEndpointInfo(uint16_t hostEndpointId, + struct chreHostEndpointInfo *info); + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_EVENT_H_ */ + diff --git a/chre_api/legacy/v1_8/chre/gnss.h b/chre_api/legacy/v1_8/chre/gnss.h new file mode 100644 index 00000000..79a8f46b --- /dev/null +++ b/chre_api/legacy/v1_8/chre/gnss.h @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_GNSS_H_ +#define _CHRE_GNSS_H_ + +/** + * @file + * Global Navigation Satellite System (GNSS) API. + * + * These structures and definitions are based on the Android N GPS HAL. + * Refer to that header file (located at this path as of the time of this + * comment: hardware/libhardware/include/hardware/gps.h) and associated + * documentation for further details and explanations for these fields. + * References in comments like "(ref: GnssAccumulatedDeltaRangeState)" map to + * the relevant element in the GPS HAL where additional information can be + * found. + * + * In general, the parts of this API that are taken from the GPS HAL follow the + * naming conventions established in that interface rather than the CHRE API + * conventions, in order to avoid confusion and enable code re-use where + * applicable. + */ + + +#include <stdbool.h> +#include <stdint.h> + +#include <chre/common.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The set of flags that may be returned by chreGnssGetCapabilities() + * @defgroup CHRE_GNSS_CAPABILITIES + * @{ + */ + +//! A lack of flags indicates that GNSS is not supported in this CHRE +#define CHRE_GNSS_CAPABILITIES_NONE UINT32_C(0) + +//! GNSS position fixes are supported via chreGnssLocationSessionStartAsync() +#define CHRE_GNSS_CAPABILITIES_LOCATION UINT32_C(1 << 0) + +//! GNSS raw measurements are supported via +//! chreGnssMeasurementSessionStartAsync() +#define CHRE_GNSS_CAPABILITIES_MEASUREMENTS UINT32_C(1 << 1) + +//! Location fixes supplied from chreGnssConfigurePassiveLocationListener() +//! are tapped in at the GNSS engine level, so they include additional fixes +//! such as those requested by the AP, and not just those requested by other +//! nanoapps within CHRE (which is the case when this flag is not set) +#define CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER \ + UINT32_C(1 << 2) + +/** @} */ + +/** + * The current version of struct chreGnssDataEvent associated with this API + */ +#define CHRE_GNSS_DATA_EVENT_VERSION UINT8_C(0) + +/** + * The maximum time the CHRE implementation is allowed to elapse before sending + * an event with the result of an asynchronous request, unless specified + * otherwise + */ +#define CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC) + +/** + * Produce an event ID in the block of IDs reserved for GNSS + * @param offset Index into GNSS event ID block; valid range [0,15] + */ +#define CHRE_GNSS_EVENT_ID(offset) (CHRE_EVENT_GNSS_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreAsyncResult + * + * Communicates the asynchronous result of a request to the GNSS API, such as + * starting a location session via chreGnssLocationSessionStartAsync(). The + * requestType field in chreAsyncResult is set to a value from enum + * chreGnssRequestType. + */ +#define CHRE_EVENT_GNSS_ASYNC_RESULT CHRE_GNSS_EVENT_ID(0) + +/** + * nanoappHandleEvent argument: struct chreGnssLocationEvent + * + * Represents a location fix provided by the GNSS subsystem. + */ +#define CHRE_EVENT_GNSS_LOCATION CHRE_GNSS_EVENT_ID(1) + +/** + * nanoappHandleEvent argument: struct chreGnssDataEvent + * + * Represents a set of GNSS measurements with associated clock data. + */ +#define CHRE_EVENT_GNSS_DATA CHRE_GNSS_EVENT_ID(2) + +// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved +// (see chre/event.h) + +// Flags indicating the Accumulated Delta Range's states +// (ref: GnssAccumulatedDeltaRangeState) +#define CHRE_GNSS_ADR_STATE_UNKNOWN UINT16_C(0) +#define CHRE_GNSS_ADR_STATE_VALID UINT16_C(1 << 0) +#define CHRE_GNSS_ADR_STATE_RESET UINT16_C(1 << 1) +#define CHRE_GNSS_ADR_STATE_CYCLE_SLIP UINT16_C(1 << 2) + +// Flags to indicate what fields in chreGnssClock are valid (ref: GnssClockFlags) +#define CHRE_GNSS_CLOCK_HAS_LEAP_SECOND UINT16_C(1 << 0) +#define CHRE_GNSS_CLOCK_HAS_TIME_UNCERTAINTY UINT16_C(1 << 1) +#define CHRE_GNSS_CLOCK_HAS_FULL_BIAS UINT16_C(1 << 2) +#define CHRE_GNSS_CLOCK_HAS_BIAS UINT16_C(1 << 3) +#define CHRE_GNSS_CLOCK_HAS_BIAS_UNCERTAINTY UINT16_C(1 << 4) +#define CHRE_GNSS_CLOCK_HAS_DRIFT UINT16_C(1 << 5) +#define CHRE_GNSS_CLOCK_HAS_DRIFT_UNCERTAINTY UINT16_C(1 << 6) + +// Flags to indicate which values are valid in a GpsLocation +// (ref: GpsLocationFlags) +#define CHRE_GPS_LOCATION_HAS_LAT_LONG UINT16_C(1 << 0) +#define CHRE_GPS_LOCATION_HAS_ALTITUDE UINT16_C(1 << 1) +#define CHRE_GPS_LOCATION_HAS_SPEED UINT16_C(1 << 2) +#define CHRE_GPS_LOCATION_HAS_BEARING UINT16_C(1 << 3) +#define CHRE_GPS_LOCATION_HAS_ACCURACY UINT16_C(1 << 4) + +//! @since v1.3 +#define CHRE_GPS_LOCATION_HAS_ALTITUDE_ACCURACY UINT16_C(1 << 5) +//! @since v1.3 +#define CHRE_GPS_LOCATION_HAS_SPEED_ACCURACY UINT16_C(1 << 6) +//! @since v1.3 +#define CHRE_GPS_LOCATION_HAS_BEARING_ACCURACY UINT16_C(1 << 7) + +/** + * The maximum number of instances of struct chreGnssMeasurement that may be + * included in a single struct chreGnssDataEvent. + * + * The value of this struct was increased from 64 to 128 in CHRE v1.5. For + * nanoapps targeting CHRE v1.4 or lower, the measurement_count will be capped + * at 64. + */ +#define CHRE_GNSS_MAX_MEASUREMENT UINT8_C(128) +#define CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5 UINT8_C(64) + +// Flags indicating the GNSS measurement state (ref: GnssMeasurementState) +#define CHRE_GNSS_MEASUREMENT_STATE_UNKNOWN UINT16_C(0) +#define CHRE_GNSS_MEASUREMENT_STATE_CODE_LOCK UINT16_C(1 << 0) +#define CHRE_GNSS_MEASUREMENT_STATE_BIT_SYNC UINT16_C(1 << 1) +#define CHRE_GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC UINT16_C(1 << 2) +#define CHRE_GNSS_MEASUREMENT_STATE_TOW_DECODED UINT16_C(1 << 3) +#define CHRE_GNSS_MEASUREMENT_STATE_MSEC_AMBIGUOUS UINT16_C(1 << 4) +#define CHRE_GNSS_MEASUREMENT_STATE_SYMBOL_SYNC UINT16_C(1 << 5) +#define CHRE_GNSS_MEASUREMENT_STATE_GLO_STRING_SYNC UINT16_C(1 << 6) +#define CHRE_GNSS_MEASUREMENT_STATE_GLO_TOD_DECODED UINT16_C(1 << 7) +#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_BIT_SYNC UINT16_C(1 << 8) +#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_SUBFRAME_SYNC UINT16_C(1 << 9) +#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1BC_CODE_LOCK UINT16_C(1 << 10) +#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1C_2ND_CODE_LOCK UINT16_C(1 << 11) +#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1B_PAGE_SYNC UINT16_C(1 << 12) +#define CHRE_GNSS_MEASUREMENT_STATE_SBAS_SYNC UINT16_C(1 << 13) + +#define CHRE_GNSS_MEASUREMENT_CARRIER_FREQUENCY_UNKNOWN 0.f + +/** + * Indicates a type of request made in this API. Used to populate the resultType + * field of struct chreAsyncResult sent with CHRE_EVENT_GNSS_ASYNC_RESULT. + */ +enum chreGnssRequestType { + CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START = 1, + CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP = 2, + CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START = 3, + CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP = 4, +}; + +/** + * Constellation type associated with an SV + */ +enum chreGnssConstellationType { + CHRE_GNSS_CONSTELLATION_UNKNOWN = 0, + CHRE_GNSS_CONSTELLATION_GPS = 1, + CHRE_GNSS_CONSTELLATION_SBAS = 2, + CHRE_GNSS_CONSTELLATION_GLONASS = 3, + CHRE_GNSS_CONSTELLATION_QZSS = 4, + CHRE_GNSS_CONSTELLATION_BEIDOU = 5, + CHRE_GNSS_CONSTELLATION_GALILEO = 6, +}; + +/** + * Enumeration of available values for the chreGnssMeasurement multipath indicator + */ +enum chreGnssMultipathIndicator { + //! The indicator is not available or unknown + CHRE_GNSS_MULTIPATH_INDICATOR_UNKNOWN = 0, + //! The measurement is indicated to be affected by multipath + CHRE_GNSS_MULTIPATH_INDICATOR_PRESENT = 1, + //! The measurement is indicated to be not affected by multipath + CHRE_GNSS_MULTIPATH_INDICATOR_NOT_PRESENT = 2, +}; + +/** + * Represents an estimate of the GNSS clock time (see the Android GPS HAL for + * more detailed information) + */ +struct chreGnssClock { + //! The GNSS receiver hardware clock value in nanoseconds, including + //! uncertainty + int64_t time_ns; + + //! The difference between hardware clock inside GNSS receiver and the + //! estimated GNSS time in nanoseconds; contains bias uncertainty + int64_t full_bias_ns; + + //! Sub-nanosecond bias, adds to full_bias_ns + float bias_ns; + + //! The clock's drift in nanoseconds per second + float drift_nsps; + + //! 1-sigma uncertainty associated with the clock's bias in nanoseconds + float bias_uncertainty_ns; + + //! 1-sigma uncertainty associated with the clock's drift in nanoseconds + //! per second + float drift_uncertainty_nsps; + + //! While this number stays the same, timeNs should flow continuously + uint32_t hw_clock_discontinuity_count; + + //! A set of flags indicating the validity of the fields in this data + //! structure (see GNSS_CLOCK_HAS_*) + uint16_t flags; + + //! Reserved for future use; set to 0 + uint8_t reserved[2]; +}; + +/** + * Represents a GNSS measurement; contains raw and computed information (see the + * Android GPS HAL for more detailed information) + */ +struct chreGnssMeasurement { + //! Hardware time offset from time_ns for this measurement, in nanoseconds + int64_t time_offset_ns; + + //! Accumulated delta range since the last channel reset in micro-meters + int64_t accumulated_delta_range_um; + + //! Received GNSS satellite time at the time of measurement, in nanoseconds + int64_t received_sv_time_in_ns; + + //! 1-sigma uncertainty of received GNSS satellite time, in nanoseconds + int64_t received_sv_time_uncertainty_in_ns; + + //! Pseudorange rate at the timestamp in meters per second (uncorrected) + float pseudorange_rate_mps; + + //! 1-sigma uncertainty of pseudorange rate in meters per second + float pseudorange_rate_uncertainty_mps; + + //! 1-sigma uncertainty of the accumulated delta range in meters + float accumulated_delta_range_uncertainty_m; + + //! Carrier-to-noise density in dB-Hz, in the range of [0, 63] + float c_n0_dbhz; + + //! Signal to noise ratio (dB), power above observed noise at correlators + float snr_db; + + //! Satellite sync state flags (GNSS_MEASUREMENT_STATE_*) - sets modulus for + //! received_sv_time_in_ns + uint16_t state; + + //! Set of ADR state flags (GNSS_ADR_STATE_*) + uint16_t accumulated_delta_range_state; + + //! Satellite vehicle ID number + int16_t svid; + + //! Constellation of the given satellite vehicle + //! @see #chreGnssConstellationType + uint8_t constellation; + + //! @see #chreGnssMultipathIndicator + uint8_t multipath_indicator; + + //! Carrier frequency of the signal tracked in Hz. + //! For example, it can be the GPS central frequency for L1 = 1575.45 MHz, + //! or L2 = 1227.60 MHz, L5 = 1176.45 MHz, various GLO channels, etc. + //! + //! Set to CHRE_GNSS_MEASUREMENT_CARRIER_FREQUENCY_UNKNOWN if not reported. + //! + //! For an L1, L5 receiver tracking a satellite on L1 and L5 at the same + //! time, two chreGnssMeasurement structs must be reported for this same + //! satellite, in one of the measurement structs, all the values related to + //! L1 must be filled, and in the other all of the values related to L5 + //! must be filled. + //! @since v1.4 + float carrier_frequency_hz; +}; + +/** + * Data structure sent with events associated with CHRE_EVENT_GNSS_DATA, enabled + * via chreGnssMeasurementSessionStartAsync() + */ +struct chreGnssDataEvent { + //! Indicates the version of the structure, for compatibility purposes. + //! Clients do not normally need to worry about this field; the CHRE + //! implementation guarantees that it only sends the client the structure + //! version it expects. + uint8_t version; + + //! Number of chreGnssMeasurement entries included in this event. Must be in + //! the range [0, CHRE_GNSS_MAX_MEASUREMENT] + uint8_t measurement_count; + + //! Reserved for future use; set to 0 + uint8_t reserved[6]; + + struct chreGnssClock clock; + + //! Pointer to an array containing measurement_count measurements + const struct chreGnssMeasurement *measurements; +}; + +/** + * Data structure sent with events of type CHRE_EVENT_GNSS_LOCATION, enabled via + * chreGnssLocationSessionStartAsync(). This is modeled after GpsLocation in the + * GPS HAL, but does not use the double data type. + */ +struct chreGnssLocationEvent { + //! UTC timestamp for location fix in milliseconds since January 1, 1970 + uint64_t timestamp; + + //! Fixed point latitude, degrees times 10^7 (roughly centimeter resolution) + int32_t latitude_deg_e7; + + //! Fixed point longitude, degrees times 10^7 (roughly centimeter + //! resolution) + int32_t longitude_deg_e7; + + //! Altitude in meters above the WGS 84 reference ellipsoid + float altitude; + + //! Horizontal speed in meters per second + float speed; + + //! Clockwise angle between north and current heading, in degrees; range + //! [0, 360) + float bearing; + + //! Expected horizontal accuracy in meters such that a circle with a radius + //! of length 'accuracy' from the latitude and longitude has a 68% + //! probability of including the true location. + float accuracy; + + //! A set of flags indicating which fields in this structure are valid. + //! If any fields are not available, the flag must not be set and the field + //! must be initialized to 0. + //! @see #GpsLocationFlags + uint16_t flags; + + //! Reserved for future use; set to 0 + //! @since v1.3 + uint8_t reserved[2]; + + //! Expected vertical accuracy in meters such that a range of + //! 2 * altitude_accuracy centered around altitude has a 68% probability of + //! including the true altitude. + //! @since v1.3 + float altitude_accuracy; + + //! Expected speed accuracy in meters per second such that a range of + //! 2 * speed_accuracy centered around speed has a 68% probability of + //! including the true speed. + //! @since v1.3 + float speed_accuracy; + + //! Expected bearing accuracy in degrees such that a range of + //! 2 * bearing_accuracy centered around bearing has a 68% probability of + //! including the true bearing. + //! @since v1.3 + float bearing_accuracy; +}; + + +/** + * Retrieves a set of flags indicating the GNSS features supported by the + * current CHRE implementation. The value returned by this function must be + * consistent for the entire duration of the Nanoapp's execution. + * + * The client must allow for more flags to be set in this response than it knows + * about, for example if the implementation supports a newer version of the API + * than the client was compiled against. + * + * @return A bitmask with zero or more CHRE_GNSS_CAPABILITIES_* flags set + * + * @since v1.1 + */ +uint32_t chreGnssGetCapabilities(void); + +/** + * Nanoapps must define CHRE_NANOAPP_USES_GNSS somewhere in their build + * system (e.g. Makefile) if the nanoapp needs to use the following GNSS APIs. + * In addition to allowing access to these APIs, defining this macro will also + * ensure CHRE enforces that all host clients this nanoapp talks to have the + * required Android permissions needed to listen to GNSS data by adding metadata + * to the nanoapp. + */ +#if defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD) + +/** + * Initiates a GNSS positioning session, or changes the requested interval of an + * existing session. If starting or modifying the session was successful, then + * the GNSS engine will work on determining the device's position. + * + * This result of this request is delivered asynchronously via an event of type + * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult} + * for more details. If the "Location" setting is disabled at the Android level, + * the CHRE implementation is expected to return a result with + * CHRE_ERROR_FUNCTION_DISABLED. + * + * If chreGnssGetCapabilities() returns a value that does not have the + * CHRE_GNSS_CAPABILITIES_LOCATION flag set, then this method will return false. + * + * @param minIntervalMs The desired minimum interval between location fixes + * delivered to the client via CHRE_EVENT_GNSS_LOCATION, in milliseconds. + * The requesting client must allow for fixes to be delivered at shorter + * or longer interval than requested. For example, adverse RF conditions + * may result in fixes arriving at a longer interval, etc. + * @param minTimeToNextFixMs The desired minimum time to the next location fix. + * If this is 0, the GNSS engine should start working on the next fix + * immediately. If greater than 0, the GNSS engine should not spend + * measurable power to produce a location fix until this amount of time + * has elapsed. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires GNSS permission + */ +bool chreGnssLocationSessionStartAsync(uint32_t minIntervalMs, + uint32_t minTimeToNextFixMs, + const void *cookie); + +/** + * Terminates an existing GNSS positioning session. If no positioning session + * is active at the time of this request, it is treated as if an active session + * was successfully ended. + * + * This result of this request is delivered asynchronously via an event of type + * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult} + * for more details. + * + * After CHRE_EVENT_GNSS_ASYNC_RESULT is delivered to the client, no more + * CHRE_EVENT_GNSS_LOCATION events will be delievered until a new location + * session is started. + * + * If chreGnssGetCapabilities() returns a value that does not have the + * CHRE_GNSS_CAPABILITIES_LOCATION flag set, then this method will return false. + * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires GNSS permission + */ +bool chreGnssLocationSessionStopAsync(const void *cookie); + +/** + * Initiates a request to receive raw GNSS measurements. A GNSS measurement + * session can exist independently of location sessions. In other words, a + * Nanoapp is able to receive measurements at its requested interval both with + * and without an active location session. + * + * This result of this request is delivered asynchronously via an event of type + * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult} + * for more details. If the "Location" setting is disabled at the Android level, + * the CHRE implementation is expected to return a result with + * CHRE_ERROR_FUNCTION_DISABLED. + * + * If chreGnssGetCapabilities() returns a value that does not have the + * CHRE_GNSS_CAPABILITIES_MEASUREMENTS flag set, then this method will return + * false. + * + * @param minIntervalMs The desired minimum interval between measurement reports + * delivered via CHRE_EVENT_GNSS_DATA. When requested at 1000ms or + * faster, and GNSS measurements are tracked, device should report + * measurements as fast as requested, and shall report no slower than + * once every 1000ms, on average. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires GNSS permission + */ +bool chreGnssMeasurementSessionStartAsync(uint32_t minIntervalMs, + const void *cookie); + +/** + * Terminates an existing raw GNSS measurement session. If no measurement + * session is active at the time of this request, it is treated as if an active + * session was successfully ended. + * + * This result of this request is delivered asynchronously via an event of type + * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult} + * for more details. + * + * If chreGnssGetCapabilities() returns a value that does not have the + * CHRE_GNSS_CAPABILITIES_MEASUREMENTS flag set, then this method will return + * false. + * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires GNSS permission + */ +bool chreGnssMeasurementSessionStopAsync(const void *cookie); + +/** + * Controls whether this nanoapp will passively receive GNSS-based location + * fixes produced as a result of location sessions initiated by other entities. + * This function allows a nanoapp to opportunistically receive location fixes + * via CHRE_EVENT_GNSS_LOCATION events without imposing additional power cost, + * though with no guarantees as to when or how often those events will arrive. + * There will be no duplication of events if a passive location listener and + * location session are enabled in parallel. + * + * Enabling passive location listening is not required to receive events for an + * active location session started via chreGnssLocationSessionStartAsync(). This + * setting is independent of the active location session, so modifying one does + * not have an effect on the other. + * + * If chreGnssGetCapabilities() returns a value that does not have the + * CHRE_GNSS_CAPABILITIES_LOCATION flag set or the value returned by + * chreGetApiVersion() is less than CHRE_API_VERSION_1_2, then this method will + * return false. + * + * If chreGnssGetCapabilities() includes + * CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER, the passive + * registration is recorded at the GNSS engine level, so events include fixes + * requested by the applications processor and potentially other non-CHRE + * clients. If this flag is not set, then only fixes requested by other nanoapps + * within CHRE are provided. + * + * @param enable true to receive opportunistic location fixes, false to disable + * + * @return true if the configuration was processed successfully, false on error + * or if this feature is not supported + * + * @since v1.2 + * @note Requires GNSS permission + */ +bool chreGnssConfigurePassiveLocationListener(bool enable); + +#else /* defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD) */ +#define CHRE_GNSS_PERM_ERROR_STRING \ + "CHRE_NANOAPP_USES_GNSS must be defined when building this nanoapp in " \ + "order to refer to " +#define chreGnssLocationSessionStartAsync(...) \ + CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \ + "chreGnssLocationSessionStartAsync") +#define chreGnssLocationSessionStopAsync(...) \ + CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \ + "chreGnssLocationSessionStopAsync") +#define chreGnssMeasurementSessionStartAsync(...) \ + CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \ + "chreGnssMeasurementSessionStartAsync") +#define chreGnssMeasurementSessionStopAsync(...) \ + CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \ + "chreGnssMeasurementSessionStopAsync") +#define chreGnssConfigurePassiveLocationListener(...) \ + CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \ + "chreGnssConfigurePassiveLocationListener") +#endif /* defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_GNSS_H_ */ diff --git a/chre_api/legacy/v1_8/chre/nanoapp.h b/chre_api/legacy/v1_8/chre/nanoapp.h new file mode 100644 index 00000000..da199ee0 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/nanoapp.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_NANOAPP_H_ +#define _CHRE_NANOAPP_H_ + +/** + * @file + * Methods in the Context Hub Runtime Environment which must be implemented + * by the nanoapp. + */ + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Method invoked by the CHRE when loading the nanoapp. + * + * Every CHRE method is legal to call from this method. + * + * @return 'true' if the nanoapp successfully started. 'false' if the nanoapp + * failed to properly initialize itself (for example, could not obtain + * sufficient memory from the heap). If this method returns 'false', the + * nanoapp will be unloaded by the CHRE (and nanoappEnd will + * _not_ be invoked in that case). + * @see nanoappEnd + */ +bool nanoappStart(void); + +/** + * Method invoked by the CHRE when there is an event for this nanoapp. + * + * Every CHRE method is legal to call from this method. + * + * @param senderInstanceId The Instance ID for the source of this event. + * Note that this may be CHRE_INSTANCE_ID, indicating that the event + * was generated by the CHRE. + * @param eventType The event type. This might be one of the CHRE_EVENT_* + * types defined in this API. But it might also be a user-defined event. + * @param eventData The associated data, if any, for this specific type of + * event. From the nanoapp's perspective, this eventData's lifetime ends + * when this method returns, and thus any data the nanoapp wishes to + * retain must be copied. Note that interpretation of event data is + * given by the event type, and for some events may not be a valid + * pointer. See documentation of the specific CHRE_EVENT_* types for how to + * interpret this data for those. Note that for user events, you will + * need to establish what this data means. + */ +void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType, + const void *eventData); + +/** + * Method invoked by the CHRE when unloading the nanoapp. + * + * It is not valid to attempt to send events or messages, or to invoke functions + * which will generate events to this app, within the nanoapp implementation of + * this function. That means it is illegal for the nanoapp invoke any of the + * following: + * + * - chreSendEvent() + * - chreSendMessageToHost() + * - chreSensorConfigure() + * - chreSensorConfigureModeOnly() + * - chreTimerSet() + * - etc. + * + * @see nanoappStart + */ +void nanoappEnd(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_NANOAPP_H_ */ diff --git a/chre_api/legacy/v1_8/chre/re.h b/chre_api/legacy/v1_8/chre/re.h new file mode 100644 index 00000000..20a69b66 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/re.h @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_RE_H_ +#define _CHRE_RE_H_ + +/** + * @file + * Some of the core Runtime Environment utilities of the Context Hub + * Runtime Environment. + * + * This includes functions for memory allocation, logging, and timers. + */ + +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include <chre/toolchain.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The instance ID for the CHRE. + * + * This ID is used to identify events generated by the CHRE (as + * opposed to events generated by another nanoapp). + */ +#define CHRE_INSTANCE_ID UINT32_C(0) + +/** + * A timer ID representing an invalid timer. + * + * This valid is returned by chreTimerSet() if a timer cannot be + * started. + */ +#define CHRE_TIMER_INVALID UINT32_C(-1) + + +/** + * The maximum size, in characters including null terminator, guaranteed for + * logging debug data with one call of chreDebugDumpLog() without getting + * truncated. + * + * @see chreDebugDumpLog + * @since v1.4 + */ +#define CHRE_DEBUG_DUMP_MINIMUM_MAX_SIZE 1000 + +/** + * Logging levels used to indicate severity level of logging messages. + * + * CHRE_LOG_ERROR: Something fatal has happened, i.e. something that will have + * user-visible consequences and won't be recoverable without explicitly + * deleting some data, uninstalling applications, wiping the data + * partitions or reflashing the entire phone (or worse). + * CHRE_LOG_WARN: Something that will have user-visible consequences but is + * likely to be recoverable without data loss by performing some explicit + * action, ranging from waiting or restarting an app all the way to + * re-downloading a new version of an application or rebooting the device. + * CHRE_LOG_INFO: Something interesting to most people happened, i.e. when a + * situation is detected that is likely to have widespread impact, though + * isn't necessarily an error. + * CHRE_LOG_DEBUG: Used to further note what is happening on the device that + * could be relevant to investigate and debug unexpected behaviors. You + * should log only what is needed to gather enough information about what + * is going on about your component. + * + * There is currently no API to turn on/off logging by level, but we anticipate + * adding such in future releases. + * + * @see chreLog + */ +enum chreLogLevel { + CHRE_LOG_ERROR, + CHRE_LOG_WARN, + CHRE_LOG_INFO, + CHRE_LOG_DEBUG +}; + + +/** + * Get the application ID. + * + * The application ID is set by the loader of the nanoapp. This is not + * assured to be unique among all nanoapps running in the system. + * + * @return The application ID. + */ +uint64_t chreGetAppId(void); + +/** + * Get the instance ID. + * + * The instance ID is the CHRE handle to this nanoapp. This is assured + * to be unique among all nanoapps running in the system, and to be + * different from the CHRE_INSTANCE_ID. This is the ID used to communicate + * between nanoapps. + * + * @return The instance ID + */ +uint32_t chreGetInstanceId(void); + +/** + * A method for logging information about the system. + * + * The chreLog logging activity alone must not cause host wake-ups. For + * example, logs could be buffered in internal memory when the host is asleep, + * and delivered when appropriate (e.g. the host wakes up). If done this way, + * the internal buffer is recommended to be large enough (at least a few KB), so + * that multiple messages can be buffered. When these logs are sent to the host, + * they are strongly recommended to be made visible under the tag 'CHRE' in + * logcat - a future version of the CHRE API may make this a hard requirement. + * + * A log entry can have a variety of levels (@see LogLevel). This function + * allows a variable number of arguments, in a printf-style format. + * + * A nanoapp needs to be able to rely upon consistent printf format + * recognition across any platform, and thus we establish formats which + * are required to be handled by every CHRE implementation. Some of the + * integral formats may seem obscure, but this API heavily uses types like + * uint32_t and uint16_t. The platform independent macros for those printf + * formats, like PRId32 or PRIx16, end up using some of these "obscure" + * formats on some platforms, and thus are required. + * + * For the initial N release, our emphasis is on correctly getting information + * into the log, and minimizing the requirements for CHRE implementations + * beyond that. We're not as concerned about how the information is visually + * displayed. As a result, there are a number of format sub-specifiers which + * are "OPTIONAL" for the N implementation. "OPTIONAL" in this context means + * that a CHRE implementation is allowed to essentially ignore the specifier, + * but it must understand the specifier enough in order to properly skip it. + * + * For a nanoapp author, an OPTIONAL format means you might not get exactly + * what you want on every CHRE implementation, but you will always get + * something valid. + * + * To be clearer, here's an example with the OPTIONAL 0-padding for integers + * for different hypothetical CHRE implementations. + * Compliant, chose to implement OPTIONAL format: + * chreLog(level, "%04x", 20) ==> "0014" + * Compliant, chose not to implement OPTIONAL format: + * chreLog(level, "%04x", 20) ==> "14" + * Non-compliant, discarded format because the '0' was assumed to be incorrect: + * chreLog(level, "%04x", 20) ==> "" + * + * Note that some of the OPTIONAL specifiers will probably become + * required in future APIs. + * + * We also have NOT_SUPPORTED specifiers. Nanoapp authors should not use any + * NOT_SUPPORTED specifiers, as unexpected things could happen on any given + * CHRE implementation. A CHRE implementation is allowed to support this + * (for example, when using shared code which already supports this), but + * nanoapp authors need to avoid these. + * + * Unless specifically noted as OPTIONAL or NOT_SUPPORTED, format + * (sub-)specifiers listed below are required. + * + * OPTIONAL format sub-specifiers: + * - '-' (left-justify within the given field width) + * - '+' (precede the result with a '+' sign if it is positive) + * - ' ' (precede the result with a blank space if no sign is going to be + * output) + * - '#' (For 'o', 'x' or 'X', precede output with "0", "0x" or "0X", + * respectively. For floating point, unconditionally output a decimal + * point.) + * - '0' (left pad the number with zeroes instead of spaces when <width> + * needs padding) + * - <width> (A number representing the minimum number of characters to be + * output, left-padding with blank spaces if needed to meet the + * minimum) + * - '.'<precision> (A number which has different meaning depending on context.) + * - Integer context: Minimum number of digits to output, padding with + * leading zeros if needed to meet the minimum. + * - 'f' context: Number of digits to output after the decimal + * point (to the right of it). + * - 's' context: Maximum number of characters to output. + * + * Integral format specifiers: + * - 'd' (signed) + * - 'u' (unsigned) + * - 'o' (octal) + * - 'x' (hexadecimal, lower case) + * - 'X' (hexadecimal, upper case) + * + * Integral format sub-specifiers (as prefixes to an above integral format): + * - 'hh' (char) + * - 'h' (short) + * - 'l' (long) + * - 'll' (long long) + * - 'z' (size_t) + * - 't' (ptrdiff_t) + * + * Other format specifiers: + * - 'f' (floating point) + * - 'c' (character) + * - 's' (character string, terminated by '\0') + * - 'p' (pointer) + * - '%' (escaping the percent sign (i.e. "%%" becomes "%")) + * + * NOT_SUPPORTED specifiers: + * - 'n' (output nothing, but fill in a given pointer with the number + * of characters written so far) + * - '*' (indicates that the width/precision value comes from one of the + * arguments to the function) + * - 'e', 'E' (scientific notation output) + * - 'g', 'G' (Shortest floating point representation) + * + * @param level The severity level for this message. + * @param formatStr Either the entirety of the message, or a printf-style + * format string of the format documented above. + * @param ... A variable number of arguments necessary for the given + * 'formatStr' (there may be no additional arguments for some 'formatStr's). + */ +CHRE_PRINTF_ATTR(2, 3) +void chreLog(enum chreLogLevel level, const char *formatStr, ...); + +/** + * Get the system time. + * + * This returns a time in nanoseconds in reference to some arbitrary + * time in the past. This method is only useful for determining timing + * between events on the system, and is not useful for determining + * any sort of absolute time. + * + * This value must always increase (and must never roll over). This + * value has no meaning across CHRE reboots. + * + * @return The system time, in nanoseconds. + */ +uint64_t chreGetTime(void); + +/** + * Retrieves CHRE's current estimated offset between the local CHRE clock + * exposed in chreGetTime(), and the host-side clock exposed in the Android API + * SystemClock.elapsedRealtimeNanos(). This offset is formed as host time minus + * CHRE time, so that it can be added to the value returned by chreGetTime() to + * determine the current estimate of the host time. + * + * A call to this function must not require waking up the host and should return + * quickly. + * + * This function must always return a valid value from the earliest point that + * it can be called by a nanoapp. In other words, it is not valid to return + * some fixed/invalid value while waiting for the initial offset estimate to be + * determined - this initial offset must be ready before nanoapps are started. + * + * @return An estimate of the offset between CHRE's time returned in + * chreGetTime() and the time on the host given in the Android API + * SystemClock.elapsedRealtimeNanos(), accurate to within +/- 10 + * milliseconds, such that adding this offset to chreGetTime() produces the + * estimated current time on the host. This value may change over time to + * account for drift, etc., so multiple calls to this API may produce + * different results. + * + * @since v1.1 + */ +int64_t chreGetEstimatedHostTimeOffset(void); + +/** + * Convenience function to retrieve CHRE's estimate of the current time on the + * host, corresponding to the Android API SystemClock.elapsedRealtimeNanos(). + * + * @return An estimate of the current time on the host, accurate to within + * +/- 10 milliseconds. This estimate is *not* guaranteed to be + * monotonically increasing, and may move backwards as a result of receiving + * new information from the host. + * + * @since v1.1 + */ +static inline uint64_t chreGetEstimatedHostTime(void) { + int64_t offset = chreGetEstimatedHostTimeOffset(); + uint64_t time = chreGetTime(); + + // Just casting time to int64_t and adding the (potentially negative) offset + // should be OK under most conditions, but this way avoids issues if + // time >= 2^63, which is technically allowed since we don't specify a start + // value for chreGetTime(), though one would assume 0 is roughly boot time. + if (offset >= 0) { + time += (uint64_t) offset; + } else { + // Assuming chreGetEstimatedHostTimeOffset() is implemented properly, + // this will never underflow, because offset = hostTime - chreTime, + // and both times are monotonically increasing (e.g. when determining + // the offset, if hostTime is 0 and chreTime is 100 we'll have + // offset = -100, but chreGetTime() will always return >= 100 after that + // point). + time -= (uint64_t) (offset * -1); + } + + return time; +} + +/** + * Set a timer. + * + * When the timer fires, nanoappHandleEvent will be invoked with + * CHRE_EVENT_TIMER and with the given 'cookie'. + * + * A CHRE implementation is required to provide at least 32 + * timers. However, there's no assurance there will be any available + * for any given nanoapp (if it's loaded late, etc). + * + * @param duration Time, in nanoseconds, before the timer fires. + * @param cookie Argument that will be sent to nanoappHandleEvent upon the + * timer firing. This is allowed to be NULL and does not need to be + * a valid pointer (assuming the nanoappHandleEvent code is expecting such). + * @param oneShot If true, the timer will just fire once. If false, the + * timer will continue to refire every 'duration', until this timer is + * canceled (@see chreTimerCancel). + * + * @return The timer ID. If the system is unable to set a timer + * (no more available timers, etc.) then CHRE_TIMER_INVALID will + * be returned. + * + * @see nanoappHandleEvent + */ +uint32_t chreTimerSet(uint64_t duration, const void *cookie, bool oneShot); + +/** + * Cancel a timer. + * + * After this method returns, the CHRE assures there will be no more + * events sent from this timer, and any enqueued events from this timer + * will need to be evicted from the queue by the CHRE. + * + * @param timerId A timer ID obtained by this nanoapp via chreTimerSet(). + * @return true if the timer was cancelled, false otherwise. We may + * fail to cancel the timer if it's a one shot which (just) fired, + * or if the given timer ID is not owned by the calling app. + */ +bool chreTimerCancel(uint32_t timerId); + +/** + * Terminate this nanoapp. + * + * This takes effect immediately. + * + * The CHRE will no longer execute this nanoapp. The CHRE will not invoke + * nanoappEnd(), nor will it call any memory free callbacks in the nanoapp. + * + * The CHRE will unload/evict this nanoapp's code. + * + * @param abortCode A value indicating the reason for aborting. (Note that + * in this version of the API, there is no way for anyone to access this + * code, but future APIs may expose it.) + * @return Never. This method does not return, as the CHRE stops nanoapp + * execution immediately. + */ +void chreAbort(uint32_t abortCode); + +/** + * Allocate a given number of bytes from the system heap. + * + * The nanoapp is required to free this memory via chreHeapFree() prior to + * the nanoapp ending. + * + * While the CHRE implementation is required to free up heap resources of + * a nanoapp when unloading it, future requirements and tests focused on + * nanoapps themselves may check for memory leaks, and will require nanoapps + * to properly manage their heap resources. + * + * @param bytes The number of bytes requested. + * @return A pointer to 'bytes' contiguous bytes of heap memory, or NULL + * if the allocation could not be performed. This pointer must be suitably + * aligned for any kind of variable. + * + * @see chreHeapFree. + */ +void *chreHeapAlloc(uint32_t bytes); + +/** + * Free a heap allocation. + * + * This allocation must be from a value returned from a chreHeapAlloc() call + * made by this nanoapp. In other words, it is illegal to free memory + * allocated by another nanoapp (or the CHRE). + * + * @param ptr 'ptr' is required to be a value returned from chreHeapAlloc(). + * Note that since chreHeapAlloc can return NULL, CHRE + * implementations must safely handle 'ptr' being NULL. + * + * @see chreHeapAlloc. + */ +void chreHeapFree(void *ptr); + +/** + * Logs the nanoapp's debug data into debug dumps. + * + * A debug dump is a string representation of information that can be used to + * diagnose and debug issues. While chreLog() is useful for logging events as + * they happen, the debug dump is a complementary function typically used to + * output a snapshot of a nanoapp's state, history, vital statistics, etc. The + * CHRE framework is required to pass this information to the debug method in + * the Context Hub HAL, where it can be captured in Android bugreports, etc. + * + * This function must only be called while handling CHRE_DEBUG_DUMP_EVENT, + * otherwise it will have no effect. A nanoapp can call this function multiple + * times while handling the event. If the resulting formatted string from a + * single call to this function is longer than CHRE_DEBUG_DUMP_MINIMUM_MAX_SIZE + * characters, it may get truncated. + * + * @param formatStr A printf-style format string of the format documented in + * chreLog(). + * @param ... A variable number of arguments necessary for the given 'formatStr' + * (there may be no additional arguments for some 'formatStr's). + * + * @see chreConfigureDebugDumpEvent + * @see chreLog + * + * @since v1.4 + */ +CHRE_PRINTF_ATTR(1, 2) +void chreDebugDumpLog(const char *formatStr, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_RE_H_ */ + diff --git a/chre_api/legacy/v1_8/chre/sensor.h b/chre_api/legacy/v1_8/chre/sensor.h new file mode 100644 index 00000000..41663741 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/sensor.h @@ -0,0 +1,1119 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_SENSOR_H_ +#define _CHRE_SENSOR_H_ + +/** + * @file + * API dealing with sensor interaction in the Context Hub Runtime + * Environment. + * + * This includes the definition of our sensor types and the ability to + * configure them for receiving events. + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <chre/common.h> +#include <chre/event.h> +#include <chre/sensor_types.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Base value for all of the data events for sensors. + * + * The value for a data event FOO is + * CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_FOO + * + * This allows for easy mapping, and also explains why there are gaps + * in our values since we don't have all possible sensor types assigned. + */ +#define CHRE_EVENT_SENSOR_DATA_EVENT_BASE CHRE_EVENT_SENSOR_FIRST_EVENT + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in SI units (m/s^2) and measure the acceleration applied to + * the device. + */ +#define CHRE_EVENT_SENSOR_ACCELEROMETER_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_ACCELEROMETER) + +/** + * nanoappHandleEvent argument: struct chreSensorOccurrenceData + * + * Since this is a one-shot sensor, after this event is delivered to the + * nanoapp, the sensor automatically goes into DONE mode. Sensors of this + * type must be configured with a ONE_SHOT mode. + */ +#define CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT) + +/** + * nanoappHandleEvent argument: struct chreSensorOccurrenceData + * + * Since this is a one-shot sensor, after this event is delivered to the + * nanoapp, the sensor automatically goes into DONE mode. Sensors of this + * type must be configured with a ONE_SHOT mode. + */ +#define CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STATIONARY_DETECT) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in radians/second and measure the rate of rotation + * around the X, Y and Z axis. + */ +#define CHRE_EVENT_SENSOR_GYROSCOPE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GYROSCOPE) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in micro-Tesla (uT) and measure the geomagnetic + * field in the X, Y and Z axis. + */ +#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The data can be interpreted using the 'pressure' field within 'readings'. + * This value is in hectopascals (hPa). + */ +#define CHRE_EVENT_SENSOR_PRESSURE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_PRESSURE) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The data can be interpreted using the 'light' field within 'readings'. + * This value is in SI lux units. + */ +#define CHRE_EVENT_SENSOR_LIGHT_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_LIGHT) + +/** + * nanoappHandleEvent argument: struct chreSensorByteData + * + * The data is interpreted from the following fields in 'readings': + * o 'isNear': If set to 1, we are nearby (on the order of centimeters); + * if set to 0, we are far. The meaning of near/far in this field must be + * consistent with the Android definition. + * o 'invalid': If set to 1, this is not a valid reading of this data. + * As of CHRE API v1.2, this field is deprecated and must always be set to + * 0. If an invalid reading is generated by the sensor hardware, it must + * be dropped and not delivered to any nanoapp. + * + * In prior versions of the CHRE API, there can be an invalid event generated + * upon configuring this sensor. Thus, the 'invalid' field must be checked on + * the first event before interpreting 'isNear'. + */ +#define CHRE_EVENT_SENSOR_PROXIMITY_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_PROXIMITY) + +/** + * nanoappHandleEvent argument: struct chreSensorOccurrenceData + * + * This data is generated every time a step is taken by the user. + * + * This is backed by the same algorithm that feeds Android's + * SENSOR_TYPE_STEP_DETECTOR, and therefore sacrifices some accuracy to target + * an update latency of under 2 seconds. + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_STEP_DETECT_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STEP_DETECT) + +/** + * nanoappHandleEvent argument: struct chreSensorUint64Data + * + * The value of the data is the cumulative number of steps taken by the user + * since the last reboot while the sensor is active. This data is generated + * every time a step is taken by the user. + * + * This is backed by the same algorithm that feeds Android's + * SENSOR_TYPE_STEP_COUNTER, and therefore targets high accuracy with under + * 10 seconds of update latency. + * + * @since v1.5 + */ +#define CHRE_EVENT_SENSOR_STEP_COUNTER_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STEP_COUNTER) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The value of the data is the measured hinge angle between 0 and 360 degrees + * inclusive. + * + * This is backed by the same algorithm that feeds Android's + * SENSOR_TYPE_HINGE_ANGLE. + * + * @since v1.5 + */ +#define CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_HINGE_ANGLE) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in SI units (m/s^2) and measure the acceleration applied to + * the device. + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in radians/second and measure the rate of rotation + * around the X, Y and Z axis. + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x', 'y', and 'z' fields within + * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z). + * + * All values are in micro-Tesla (uT) and measure the geomagnetic + * field in the X, Y and Z axis. + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The data can be interpreted using the 'temperature' field within 'readings'. + * This value is in degrees Celsius. + */ +#define CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The data can be interpreted using the 'temperature' field within 'readings'. + * This value is in degrees Celsius. + */ +#define CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE) + +/** + * nanoappHandleEvent argument: struct chreSensorFloatData + * + * The data can be interpreted using the 'temperature' field within 'readings'. + * This value is in degrees Celsius. + */ +#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA \ + (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE) + +/** + * First value for sensor events which are not data from the sensor. + * + * Unlike the data event values, these other event values don't have any + * mapping to sensor types. + */ +#define CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE \ + (CHRE_EVENT_SENSOR_FIRST_EVENT + 0x0100) + +/** + * nanoappHandleEvent argument: struct chreSensorSamplingStatusEvent + * + * Indicates that the interval and/or the latency which this sensor is + * sampling at has changed. + */ +#define CHRE_EVENT_SENSOR_SAMPLING_CHANGE \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 0) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias' + * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias; + * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated + * data to generate calibrated data. + * + * All values are in radians/second and measure the rate of rotation + * around the X, Y and Z axis. + * + * If bias delivery is supported, this event is generated by default when + * chreSensorConfigure is called to enable for the sensor of type + * CHRE_SENSOR_TYPE_GYROSCOPE, or if bias delivery is explicitly enabled + * through chreSensorConfigureBiasEvents() for the sensor. + */ +#define CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 1) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias' + * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias; + * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated + * data to generate calibrated data. + * + * All values are in micro-Tesla (uT) and measure the geomagnetic + * field in the X, Y and Z axis. + * + * If bias delivery is supported, this event is generated by default when + * chreSensorConfigure is called to enable for the sensor of type + * CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD, or if bias delivery is explicitly enabled + * through chreSensorConfigureBiasEvents() for the sensor. + */ +#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 2) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias' + * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias; + * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated + * data to generate calibrated data. + * + * All values are in SI units (m/s^2) and measure the acceleration applied to + * the device. + * + * If bias delivery is supported, this event is generated by default when + * chreSensorConfigure is called to enable for the sensor of type + * CHRE_SENSOR_TYPE_ACCELEROMETER, or if bias delivery is explicitly enabled + * through chreSensorConfigureBiasEvents() for the sensor. + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 3) + +/** + * nanoappHandleEvent argument: struct chreSensorFlushCompleteEvent + * + * An event indicating that a flush request made by chreSensorFlushAsync has + * completed. + * + * @see chreSensorFlushAsync + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_FLUSH_COMPLETE \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 4) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data of this event is the same as that of + * CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO, except the sensorHandle field of + * chreSensorDataHeader contains the handle of the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE. + * + * This event is only generated if the bias reporting is explicitly enabled + * for a nanoapp through chreSensorConfigureBiasEvents() for the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE. + * + * @see CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 5) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data of this event is the same as that of + * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO, except the sensorHandle field + * of chreSensorDataHeader contains the handle of the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD. + * + * This event is only generated if the bias reporting is explicitly enabled + * for a nanoapp through chreSensorConfigureBiasEvents() for the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD. + * + * @see CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 6) + +/** + * nanoappHandleEvent argument: struct chreSensorThreeAxisData + * + * The data of this event is the same as that of + * CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO, except the sensorHandle field + * of chreSensorDataHeader contains the handle of the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER. + * + * This event is only generated if the bias reporting is explicitly enabled + * for a nanoapp through chreSensorConfigureBiasEvents for the sensor of type + * CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER. + * + * @see CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO + * + * @since v1.3 + */ +#define CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_BIAS_INFO \ + (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 7) + +#if CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_BIAS_INFO > \ + CHRE_EVENT_SENSOR_LAST_EVENT +#error Too many sensor events. +#endif + +/** + * Value indicating we want the smallest possible latency for a sensor. + * + * This literally translates to 0 nanoseconds for the chreSensorConfigure() + * argument. While we won't get exactly 0 nanoseconds, the CHRE will + * queue up this event As Soon As Possible. + */ +#define CHRE_SENSOR_LATENCY_ASAP UINT64_C(0) + +/** + * Special value indicating non-importance, or non-applicability of the sampling + * interval. + * + * @see chreSensorConfigure + * @see chreSensorSamplingStatus + */ +#define CHRE_SENSOR_INTERVAL_DEFAULT UINT64_C(-1) + +/** + * Special value indicating non-importance of the latency. + * + * @see chreSensorConfigure + * @see chreSensorSamplingStatus + */ +#define CHRE_SENSOR_LATENCY_DEFAULT UINT64_C(-1) + +/** + * A sensor index value indicating that it is the default sensor. + * + * @see chreSensorFind + */ +#define CHRE_SENSOR_INDEX_DEFAULT UINT8_C(0) + +/** + * Special value indicating non-importance of the batch interval. + * + * @see chreSensorConfigureWithBatchInterval + */ +#define CHRE_SENSOR_BATCH_INTERVAL_DEFAULT UINT64_C(-1) + +// This is used to define elements of enum chreSensorConfigureMode. +#define CHRE_SENSOR_CONFIGURE_RAW_POWER_ON (1 << 0) + +// This is used to define elements of enum chreSensorConfigureMode. +#define CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS (1 << 1) + +// This is used to define elements of enum chreSensorConfigureMode. +#define CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT (2 << 1) + +/** + * The maximum amount of time allowed to elapse between the call to + * chreSensorFlushAsync() and when CHRE_EVENT_SENSOR_FLUSH_COMPLETE is delivered + * to the nanoapp on a successful flush. + */ +#define CHRE_SENSOR_FLUSH_COMPLETE_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC) + +/** + * Modes we can configure a sensor to use. + * + * Our mode will affect not only how/if we receive events, but + * also whether or not the sensor will be powered on our behalf. + * + * @see chreSensorConfigure + */ +enum chreSensorConfigureMode { + /** + * Get events from the sensor. + * + * Power: Turn on if not already on. + * Reporting: Continuous. Send each new event as it comes (subject to + * batching and latency). + */ + CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS = + (CHRE_SENSOR_CONFIGURE_RAW_POWER_ON | + CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS), + + /** + * Get a single event from the sensor and then become DONE. + * + * Once the event is sent, the sensor automatically + * changes to CHRE_SENSOR_CONFIGURE_MODE_DONE mode. + * + * Power: Turn on if not already on. + * Reporting: One shot. Send the next event and then be DONE. + */ + CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT = + (CHRE_SENSOR_CONFIGURE_RAW_POWER_ON | + CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT), + + /** + * Get events from a sensor that are generated for any client in the system. + * + * This is considered passive because the sensor will not be powered on for + * the sake of our nanoapp. If and only if another client in the system has + * requested this sensor power on will we get events. + * + * This can be useful for something which is interested in seeing data, but + * not interested enough to be responsible for powering on the sensor. + * + * Power: Do not power the sensor on our behalf. + * Reporting: Continuous. Send each event as it comes. + */ + CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS = + CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS, + + /** + * Get a single event from a sensor that is generated for any client in the + * system. + * + * See CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS for more details on + * what the "passive" means. + * + * Power: Do not power the sensor on our behalf. + * Reporting: One shot. Send only the next event and then be DONE. + */ + CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT = + CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT, + + /** + * Indicate we are done using this sensor and no longer interested in it. + * + * See chreSensorConfigure for more details on expressing interest or + * lack of interest in a sensor. + * + * Power: Do not power the sensor on our behalf. + * Reporting: None. + */ + CHRE_SENSOR_CONFIGURE_MODE_DONE = 0, +}; + +/** + * A structure containing information about a Sensor. + * + * See documentation of individual fields below. + */ +struct chreSensorInfo { + /** + * The name of the sensor. + * + * A text name, useful for logging/debugging, describing the Sensor. This + * is not assured to be unique (i.e. there could be multiple sensors with + * the name "Temperature"). + * + * CHRE implementations may not set this as NULL. An empty + * string, while discouraged, is legal. + */ + const char *sensorName; + + /** + * One of the CHRE_SENSOR_TYPE_* defines above. + */ + uint8_t sensorType; + + /** + * Flag indicating if this sensor is on-change. + * + * An on-change sensor only generates events when underlying state + * changes. This has the same meaning as on-change does in the Android + * Sensors HAL. See sensors.h for much more details. + * + * A value of 1 indicates this is on-change. 0 indicates this is not + * on-change. + */ + uint8_t isOnChange : 1; + + /** + * Flag indicating if this sensor is one-shot. + * + * A one-shot sensor only triggers a single event, and then automatically + * disables itself. + * + * A value of 1 indicates this is one-shot. 0 indicates this is not + * on-change. + */ + uint8_t isOneShot : 1; + + /** + * Flag indicating if this sensor supports reporting bias info events. + * + * This field will be set to 0 when running on CHRE API versions prior to + * v1.3, but must be ignored (i.e. does not mean bias info event is not + * supported). + * + * @see chreSensorConfigureBiasEvents + * + * @since v1.3 + */ + uint8_t reportsBiasEvents : 1; + + /** + * Flag indicating if this sensor supports passive mode requests. + * + * This field will be set to 0 when running on CHRE API versions prior to + * v1.4, and must be ignored (i.e. does not mean passive mode requests are + * not supported). + * + * @see chreSensorConfigure + * + * @since v1.4 + */ + uint8_t supportsPassiveMode : 1; + + uint8_t unusedFlags : 4; + + /** + * The minimum sampling interval supported by this sensor, in nanoseconds. + * + * Requests to chreSensorConfigure with a lower interval than this will + * fail. If the sampling interval is not applicable to this sensor, this + * will be set to CHRE_SENSOR_INTERVAL_DEFAULT. + * + * This field will be set to 0 when running on CHRE API versions prior to + * v1.1, indicating that the minimum interval is not known. + * + * @since v1.1 + */ + uint64_t minInterval; + + /** + * Uniquely identifies the sensor for a given type. A value of 0 indicates + * that this is the "default" sensor, which is returned by + * chreSensorFindDefault(). + * + * The sensor index of a given type must be stable across boots (i.e. must + * not change), and a different sensor of the same type must have different + * sensor index values, and the set of sensorIndex values for a given sensor + * type must be continuguous. + * + * @since v1.5 + */ + uint8_t sensorIndex; +}; + +/** + * The status of a sensor's sampling configuration. + */ +struct chreSensorSamplingStatus { + /** + * The interval, in nanoseconds, at which sensor data is being sampled at. + * This should be used by nanoapps to determine the rate at which samples + * will be generated and not to indicate what the sensor is truly sampling + * at since resampling may occur to limit incoming data. + * + * If this is CHRE_SENSOR_INTERVAL_DEFAULT, then a sampling interval + * isn't meaningful for this sensor. + * + * Note that if 'enabled' is false, this value is not meaningful. + */ + uint64_t interval; + + /** + * The latency, in nanoseconds, at which the sensor is now reporting. + * + * If this is CHRE_SENSOR_LATENCY_DEFAULT, then a latency + * isn't meaningful for this sensor. + * + * The effective batch interval can be derived from this value by + * adding the current sampling interval. + * + * Note that if 'enabled' is false, this value is not meaningful. + */ + uint64_t latency; + + /** + * True if the sensor is actively powered and sampling; false otherwise. + */ + bool enabled; +}; + +/** + * The nanoappHandleEvent argument for CHRE_EVENT_SENSOR_SAMPLING_CHANGE. + * + * Note that only at least one of 'interval' or 'latency' must be + * different than it was prior to this event. Thus, one of these + * fields may be (but doesn't need to be) the same as before. + */ +struct chreSensorSamplingStatusEvent { + /** + * The handle of the sensor which has experienced a change in sampling. + */ + uint32_t sensorHandle; + + /** + * The new sampling status. + * + * At least one of the field in this struct will be different from + * the previous sampling status event. + */ + struct chreSensorSamplingStatus status; +}; + +/** + * The nanoappHandleEvent argument for CHRE_EVENT_SENSOR_FLUSH_COMPLETE. + * + * @see chreSensorFlushAsync + * + * @since v1.3 + */ +struct chreSensorFlushCompleteEvent { + /** + * The handle of the sensor which a flush was completed. + */ + uint32_t sensorHandle; + + /** + * Populated with a value from enum {@link #chreError}, indicating whether + * the flush failed, and if so, provides the cause of the failure. + */ + uint8_t errorCode; + + /** + * Reserved for future use. Set to 0. + */ + uint8_t reserved[3]; + + /** + * Set to the cookie parameter given to chreSensorFlushAsync. + */ + const void *cookie; +}; + +/** + * Find the default sensor for a given sensor type. + * + * @param sensorType One of the CHRE_SENSOR_TYPE_* constants. + * @param handle If a sensor is found, then the memory will be filled with + * the value for the sensor's handle. This argument must be non-NULL. + * @return true if a sensor was found, false otherwise. + */ +bool chreSensorFindDefault(uint8_t sensorType, uint32_t *handle); + +/** + * Finds a sensor of a given index and sensor type. + * + * For CHRE implementations that support multiple sensors of the same sensor + * type, this method can be used to get the non-default sensor(s). The default + * sensor, as defined in the chreSensorFindDefault(), will be returned if + * a sensor index of zero is specified. + * + * A simple example of iterating all available sensors of a given type is + * provided here: + * + * uint32_t handle; + * for (uint8_t i = 0; chreSensorFind(sensorType, i, &handle); i++) { + * chreLog(CHRE_LOG_INFO, + * "Found sensor index %" PRIu8 ", which has handle %" PRIu32, + * i, handle); + * } + * + * If this method is invoked for CHRE versions prior to v1.5, invocations with + * sensorIndex value of 0 will be equivalent to using chreSensorFindDefault, and + * if sensorIndex is non-zero will return false. + * + * In cases where multiple sensors are supported in both the Android sensors + * framework and CHRE, the sensorName of the chreSensorInfo struct for a given + * sensor instance must match exactly with that of the + * android.hardware.Sensor#getName() return value. This can be used to match a + * sensor instance between the Android and CHRE sensors APIs. + * + * @param sensorType One of the CHRE_SENSOR_TYPE_* constants. + * @param sensorIndex The index of the desired sensor. + * @param handle If a sensor is found, then the memory will be filled with + * the value for the sensor's handle. This argument must be non-NULL. + * @return true if a sensor was found, false otherwise. + * + * @since v1.5 + */ +bool chreSensorFind(uint8_t sensorType, uint8_t sensorIndex, uint32_t *handle); + +/** + * Get the chreSensorInfo struct for a given sensor. + * + * @param sensorHandle The sensor handle, as obtained from + * chreSensorFindDefault() or passed to nanoappHandleEvent(). + * @param info If the sensor is valid, then this memory will be filled with + * the SensorInfo contents for this sensor. This argument must be + * non-NULL. + * @return true if the senor handle is valid and 'info' was filled in; + * false otherwise. + */ +bool chreGetSensorInfo(uint32_t sensorHandle, struct chreSensorInfo *info); + +/** + * Get the chreSensorSamplingStatus struct for a given sensor. + * + * Note that this may be different from what was requested in + * chreSensorConfigure(), for multiple reasons. It's possible that the sensor + * does not exactly support the interval requested in chreSensorConfigure(), so + * a faster one was chosen. + * + * It's also possible that there is another user of this sensor who has + * requested a faster interval and/or lower latency. This latter scenario + * should be noted, because it means the sensor rate can change due to no + * interaction from this nanoapp. Note that the + * CHRE_EVENT_SENSOR_SAMPLING_CHANGE event will trigger in this case, so it's + * not necessary to poll for such a change. + * + * This function must return a valid status if the provided sensor is being + * actively sampled by a nanoapp and a CHRE_EVENT_SENSOR_SAMPLING_CHANGE has + * been delivered indicating their request has taken effect. It is not required + * to return a valid status if no nanoapp is actively sampling the sensor. + * + * @param sensorHandle The sensor handle, as obtained from + * chreSensorFindDefault() or passed to nanoappHandleEvent(). + * @param status If the sensor is actively enabled by a nanoapp, then this + * memory must be filled with the sampling status contents for this sensor. + * This argument must be non-NULL. + * @return true if the sensor handle is valid and 'status' was filled in; + * false otherwise. + */ +bool chreGetSensorSamplingStatus(uint32_t sensorHandle, + struct chreSensorSamplingStatus *status); + +/** + * Configures a given sensor at a specific interval and latency and mode. + * + * If this sensor's chreSensorInfo has isOneShot set to 1, + * then the mode must be one of the ONE_SHOT modes, or this method will fail. + * + * The CHRE wants to power as few sensors as possible, in keeping with its + * low power design. As such, it only turns on sensors when there are clients + * actively interested in that sensor data, and turns off sensors as soon as + * there are no clients interested in them. Calling this method generally + * indicates an interest, and using CHRE_SENSOR_CONFIGURE_MODE_DONE shows + * when we are no longer interested. + * + * Thus, each initial Configure of a sensor (per nanoapp) needs to eventually + * have a DONE call made, either directly or on its behalf. Subsequent calls + * to a Configure method within the same nanoapp, when there has been no DONE + * in between, still only require a single DONE call. + * + * For example, the following is valid usage: + * <code> + * chreSensorConfigure(myHandle, mode, interval0, latency0); + * [...] + * chreSensorConfigure(myHandle, mode, interval1, latency0); + * [...] + * chreSensorConfigure(myHandle, mode, interval1, latency1); + * [...] + * chreSensorConfigureModeOnly(myHandle, CHRE_SENSOR_CONFIGURE_MODE_DONE); + * </code> + * + * The first call to Configure is the one which creates the requirement + * to eventually call with DONE. The subsequent calls are just changing the + * interval/latency. They have not changed the fact that this nanoapp is + * still interested in output from the sensor 'myHandle'. Thus, only one + * single call for DONE is needed. + * + * There is a special case. One-shot sensors, sensors which + * just trigger a single event and never trigger again, implicitly go into + * DONE mode after that single event triggers. Thus, the + * following are legitimate usages: + * <code> + * chreSensorConfigure(myHandle, MODE_ONE_SHOT, interval, latency); + * [...] + * [myHandle triggers an event] + * [no need to configure to DONE]. + * </code> + * + * And: + * <code> + * chreSensorConfigure(myHandle, MODE_ONE_SHOT, interval, latency); + * [...] + * chreSensorConfigureModeOnly(myHandle, MODE_DONE); + * [we cancelled myHandle before it ever triggered an event] + * </code> + * + * Note that while PASSIVE modes, by definition, don't express an interest in + * powering the sensor, DONE is still necessary to silence the event reporting. + * Starting with CHRE API v1.4, for sensors that do not support passive mode, a + * request with mode set to CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS or + * CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT will be rejected. CHRE API + * versions 1.3 and older implicitly assume that passive mode is supported + * across all sensors, however this is not necessarily the case. Clients can + * call chreSensorInfo to identify whether a sensor supports passive mode. + * + * When a calibrated sensor (e.g. CHRE_SENSOR_TYPE_ACCELEROMETER) is + * successfully enabled through this method and if bias delivery is supported, + * by default CHRE will start delivering bias events for the sensor + * (e.g. CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO) to the nanoapp. If the + * nanoapp does not wish to receive these events, they can be disabled through + * chreSensorConfigureBiasEvents after enabling the sensor. + * + * @param sensorHandle The handle to the sensor, as obtained from + * chreSensorFindDefault(). + * @param mode The mode to use. See descriptions within the + * chreSensorConfigureMode enum. + * @param interval The interval, in nanoseconds, at which we want events from + * the sensor. On success, the sensor will be set to 'interval', or a value + * less than 'interval'. There is a special value + * CHRE_SENSOR_INTERVAL_DEFAULT, in which we don't express a preference for + * the interval, and allow the sensor to choose what it wants. Note that + * due to batching, we may receive events less frequently than + * 'interval'. + * @param latency The maximum latency, in nanoseconds, allowed before the + * CHRE begins delivery of an event. This will control how many events + * can be queued by the sensor before requiring a delivery event. + * Latency is defined as the "timestamp when event is queued by the CHRE" + * minus "timestamp of oldest unsent data reading". + * There is a special value CHRE_SENSOR_LATENCY_DEFAULT, in which we don't + * express a preference for the latency, and allow the sensor to choose what + * it wants. + * Note that there is no assurance of how long it will take an event to + * get through a CHRE's queueing system, and thus there is no ability to + * request a minimum time from the occurrence of a phenomenon to when the + * nanoapp receives the information. The current CHRE API has no + * real-time elements, although future versions may introduce some to + * help with this issue. + * @return true if the configuration succeeded, false otherwise. + * + * @see chreSensorConfigureMode + * @see chreSensorFindDefault + * @see chreSensorInfo + * @see chreSensorConfigureBiasEvents + */ +bool chreSensorConfigure(uint32_t sensorHandle, + enum chreSensorConfigureMode mode, + uint64_t interval, uint64_t latency); + +/** + * Short cut for chreSensorConfigure where we only want to configure the mode + * and do not care about interval/latency. + * + * @see chreSensorConfigure + */ +static inline bool chreSensorConfigureModeOnly( + uint32_t sensorHandle, enum chreSensorConfigureMode mode) { + return chreSensorConfigure(sensorHandle, + mode, + CHRE_SENSOR_INTERVAL_DEFAULT, + CHRE_SENSOR_LATENCY_DEFAULT); +} + +/** + * Convenience function that wraps chreSensorConfigure but enables batching to + * be controlled by specifying the desired maximum batch interval rather + * than maximum sample latency. Users may find the batch interval to be a more + * intuitive method of expressing the desired batching behavior. + * + * Batch interval is different from latency as the batch interval time is + * counted starting when the prior event containing a batch of sensor samples is + * delivered, while latency starts counting when the first sample is deferred to + * start collecting a batch. In other words, latency ignores the time between + * the last sample in a batch to the first sample of the next batch, while it's + * included in the batch interval, as illustrated below. + * + * Time 0 1 2 3 4 5 6 7 8 + * Batch A B C + * Sample a1 a2 a3 b1 b2 b3 c1 c2 c3 + * Latency [ ] [ ] [ ] + * BatchInt | | | + * + * In the diagram, the effective sample interval is 1 time unit, latency is 2 + * time units, and batch interval is 3 time units. + * + * @param sensorHandle See chreSensorConfigure#sensorHandle + * @param mode See chreSensorConfigure#mode + * @param sampleInterval See chreSensorConfigure#interval, but note that + * CHRE_SENSOR_INTERVAL_DEFAULT is not a supported input to this method. + * @param batchInterval The desired maximum interval, in nanoseconds, between + * CHRE enqueuing each batch of sensor samples. + * @return Same as chreSensorConfigure + * + * @see chreSensorConfigure + * + * @since v1.1 + */ +static inline bool chreSensorConfigureWithBatchInterval( + uint32_t sensorHandle, enum chreSensorConfigureMode mode, + uint64_t sampleInterval, uint64_t batchInterval) { + bool result = false; + + if (sampleInterval != CHRE_SENSOR_INTERVAL_DEFAULT) { + uint64_t latency; + if (batchInterval == CHRE_SENSOR_BATCH_INTERVAL_DEFAULT) { + latency = CHRE_SENSOR_LATENCY_DEFAULT; + } else if (batchInterval > sampleInterval) { + latency = batchInterval - sampleInterval; + } else { + latency = CHRE_SENSOR_LATENCY_ASAP; + } + result = chreSensorConfigure(sensorHandle, mode, sampleInterval, + latency); + } + + return result; +} + +/** + * Configures the reception of bias events for a specific sensor. + * + * If bias event delivery is supported for a sensor, the sensor's chreSensorInfo + * has reportsBiasEvents set to 1. If supported, it must be supported for both + * calibrated and uncalibrated versions of the sensor. If supported, CHRE must + * provide bias events to the nanoapp by default when chreSensorConfigure is + * called to enable the calibrated version of the sensor (for backwards + * compatibility reasons, as this is the defined behavior for CHRE API v1.0). + * When configuring uncalibrated sensors, nanoapps must explicitly configure an + * enable request through this method to receive bias events. If bias event + * delivery is not supported for the sensor, this method will return false and + * no bias events will be generated. + * + * To enable bias event delivery (enable=true), the nanoapp must be registered + * to the sensor through chreSensorConfigure, and bias events will only be + * generated when the sensor is powered on. To disable the bias event delivery, + * this method can be invoked with enable=false. + * + * If an enable configuration is successful, the calling nanoapp will receive + * bias info events, e.g. CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO, when the + * bias status changes (or first becomes available). Calibrated data + * (e.g. CHRE_SENSOR_TYPE_ACCELEROMETER) is generated by subracting bias from + * uncalibrated data (e.g. CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER). + * Calibrated sensor events are generated by applying the most recent bias + * available (i.e. timestamp of calibrated data are greater than or equal to the + * timestamp of the bias data that has been applied to it). The configuration of + * bias event delivery persists until the sensor is unregistered by the nanoapp + * through chreSensorConfigure or modified through this method. + * + * To get an initial bias before new bias events, the nanoapp should get the + * bias synchronously after this method is invoked, e.g.: + * + * if (chreSensorConfigure(handle, ...)) { + * chreSensorConfigureBiasEvents(handle, true); + * chreSensorGetThreeAxisBias(handle, &bias); + * } + * + * Note that chreSensorGetThreeAxisBias() should be called after + * chreSensorConfigureBiasEvents() to ensure that no bias events are lost. + * + * If called while running on a CHRE API version below v1.3, this function + * returns false and has no effect. The default behavior regarding bias events + * is unchanged, meaning that the implementation may still send bias events + * when a calibrated sensor is registered (if supported), and will not send bias + * events when an uncalibrated sensor is registered. + * + * @param sensorHandle The handle to the sensor, as obtained from + * chreSensorFindDefault(). + * @param enable true to receive bias events, false otherwise + * + * @return true if the configuration succeeded, false otherwise + * + * @since v1.3 + */ +bool chreSensorConfigureBiasEvents(uint32_t sensorHandle, bool enable); + +/** + * Synchronously provides the most recent bias info available for a sensor. The + * bias will only be provided for a sensor that supports bias event delivery + * using the chreSensorThreeAxisData type. If the bias is not yet available + * (but is supported), this method will store data with a bias of 0 and the + * accuracy field in chreSensorDataHeader set to CHRE_SENSOR_ACCURACY_UNKNOWN. + * + * If called while running on a CHRE API version below v1.3, this function + * returns false. + * + * @param sensorHandle The handle to the sensor, as obtained from + * chreSensorFindDefault(). + * @param bias A pointer to where the bias will be stored. + * + * @return true if the bias was successfully stored, false if sensorHandle was + * invalid or the sensor does not support three axis bias delivery + * + * @since v1.3 + * + * @see chreSensorConfigureBiasEvents + */ +bool chreSensorGetThreeAxisBias(uint32_t sensorHandle, + struct chreSensorThreeAxisData *bias); + +/** + * Makes a request to flush all samples stored for batching. The nanoapp must be + * registered to the sensor through chreSensorConfigure, and the sensor must be + * powered on. If the request is accepted, all batched samples of the sensor + * are sent to nanoapps registered to the sensor. During a flush, it is treated + * as though the latency as given in chreSensorConfigure has expired. When all + * batched samples have been flushed (or the flush fails), the nanoapp will + * receive a unicast CHRE_EVENT_SENSOR_FLUSH_COMPLETE event. The time to deliver + * this event must not exceed CHRE_SENSOR_FLUSH_COMPLETE_TIMEOUT_NS after this + * method is invoked. If there are no samples in the batch buffer (either in + * hardware FIFO or software), then this method will return true and a + * CHRE_EVENT_SENSOR_FLUSH_COMPLETE event is delivered immediately. + * + * If a flush request is invalid (e.g. the sensor refers to a one-shot sensor, + * or the sensor was not enabled), and this API will return false and no + * CHRE_EVENT_SENSOR_FLUSH_COMPLETE event will be delivered. + * + * If multiple flush requests are made for a sensor prior to flush completion, + * then the requesting nanoapp will receive all batched samples existing at the + * time of the latest flush request. In this case, the number of + * CHRE_EVENT_SENSOR_FLUSH_COMPLETE events received must equal the number of + * flush requests made. + * + * If a sensor request is disabled after a flush request is made through this + * method but before the flush operation is completed, the nanoapp will receive + * a CHRE_EVENT_SENSOR_FLUSH_COMPLETE with the error code + * CHRE_ERROR_FUNCTION_DISABLED for any pending flush requests. + * + * Starting with CHRE API v1.3, implementations must support this capability + * across all exposed sensor types. + * + * @param sensorHandle The handle to the sensor, as obtained from + * chreSensorFindDefault(). + * @param cookie An opaque value that will be included in the + * chreSensorFlushCompleteEvent sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.3 + */ +bool chreSensorFlushAsync(uint32_t sensorHandle, const void *cookie); + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_SENSOR_H_ */ diff --git a/chre_api/legacy/v1_8/chre/sensor_types.h b/chre_api/legacy/v1_8/chre/sensor_types.h new file mode 100644 index 00000000..63b495bb --- /dev/null +++ b/chre_api/legacy/v1_8/chre/sensor_types.h @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef _CHRE_SENSOR_TYPES_H_ +#define _CHRE_SENSOR_TYPES_H_ + +/** + * @file + * Standalone definition of sensor types, and the data structures of the sample + * events they emit. + */ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * The CHRE_SENSOR_TYPE_* defines are the sensor types supported. + * + * Unless otherwise noted, each of these sensor types is based off of a + * corresponding sensor type in the Android API's sensors.h interface. + * For a given CHRE_SENSOR_TYPE_FOO, it corresponds to the SENSOR_TYPE_FOO in + * hardware/libhardware/include/hardware/sensors.h of the Android code base. + * + * Unless otherwise noted below, a CHRE_SENSOR_TYPE_FOO should be assumed + * to work the same as the Android SENSOR_TYPE_FOO, as documented in the + * sensors.h documentation and as detailed within the Android Compatibility + * Definition Document. + * + * Note that every sensor will generate CHRE_EVENT_SENSOR_SAMPLING_CHANGE + * events, so it is not listed with each individual sensor. + */ + +/** + * Start value for all of the vendor-defined private sensors. + * + * @since v1.2 + */ +#define CHRE_SENSOR_TYPE_VENDOR_START UINT8_C(192) + +/** + * Accelerometer. + * + * Generates: CHRE_EVENT_SENSOR_ACCELEROMETER_DATA and + * optionally CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO + * + * Note that the ACCELEROMETER_DATA is always the fully calibrated data, + * including factory calibration and runtime calibration if available. + * + * @see chreConfigureSensorBiasEvents + */ +#define CHRE_SENSOR_TYPE_ACCELEROMETER UINT8_C(1) + +/** + * Instantaneous motion detection. + * + * Generates: CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA + * + * This is a one-shot sensor. + * + * This does not have a direct analogy within sensors.h. This is similar + * to SENSOR_TYPE_MOTION_DETECT, but this triggers instantly upon any + * motion, instead of waiting for a period of continuous motion. + */ +#define CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT UINT8_C(2) + +/** + * Stationary detection. + * + * Generates: CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA + * + * This is a one-shot sensor. + */ +#define CHRE_SENSOR_TYPE_STATIONARY_DETECT UINT8_C(3) + +/** + * Gyroscope. + * + * Generates: CHRE_EVENT_SENSOR_GYROSCOPE_DATA and + * optionally CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO + * + * Note that the GYROSCOPE_DATA is always the fully calibrated data, including + * factory calibration and runtime calibration if available. + * + * @see chreConfigureSensorBiasEvents + */ +#define CHRE_SENSOR_TYPE_GYROSCOPE UINT8_C(6) + +/** + * Uncalibrated gyroscope. + * + * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA + * + * Note that the UNCALIBRATED_GYROSCOPE_DATA must be factory calibrated data, + * but not runtime calibrated. + */ +#define CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE UINT8_C(7) + +/** + * Magnetometer. + * + * Generates: CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA and + * optionally CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO + * + * Note that the GEOMAGNETIC_FIELD_DATA is always the fully calibrated data, + * including factory calibration and runtime calibration if available. + * + * @see chreConfigureSensorBiasEvents + */ +#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD UINT8_C(8) + +/** + * Uncalibrated magnetometer. + * + * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA + * + * Note that the UNCALIBRATED_GEOMAGNETIC_FIELD_DATA must be factory calibrated + * data, but not runtime calibrated. + */ +#define CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD UINT8_C(9) + +/** + * Barometric pressure sensor. + * + * Generates: CHRE_EVENT_SENSOR_PRESSURE_DATA + */ +#define CHRE_SENSOR_TYPE_PRESSURE UINT8_C(10) + +/** + * Ambient light sensor. + * + * Generates: CHRE_EVENT_SENSOR_LIGHT_DATA + * + * This is an on-change sensor. + */ +#define CHRE_SENSOR_TYPE_LIGHT UINT8_C(12) + +/** + * Proximity detection. + * + * Generates: CHRE_EVENT_SENSOR_PROXIMITY_DATA + * + * This is an on-change sensor. + */ +#define CHRE_SENSOR_TYPE_PROXIMITY UINT8_C(13) + +/** + * Step detection. + * + * Generates: CHRE_EVENT_SENSOR_STEP_DETECT_DATA + * + * @since v1.3 + */ +#define CHRE_SENSOR_TYPE_STEP_DETECT UINT8_C(23) + +/** + * Step counter. + * + * Generates: CHRE_EVENT_SENSOR_STEP_COUNTER_DATA + * + * This is an on-change sensor. Note that the data returned by this sensor must + * match the value that can be obtained via the Android sensors framework at the + * same point in time. This means, if CHRE reboots from the rest of the system, + * the counter must not reset to 0. + * + * @since v1.5 + */ +#define CHRE_SENSOR_TYPE_STEP_COUNTER UINT8_C(24) + +/** + * Hinge angle sensor. + * + * Generates: CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA + * + * This is an on-change sensor. + * + * A sensor of this type measures the angle, in degrees, between two + * integral parts of the device. Movement of a hinge measured by this sensor + * type is expected to alter the ways in which the user may interact with + * the device, for example by unfolding or revealing a display. + * + * @since v1.5 + */ +#define CHRE_SENSOR_TYPE_HINGE_ANGLE UINT8_C(36) + +/** + * Uncalibrated accelerometer. + * + * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA + * + * Note that the UNCALIBRATED_ACCELEROMETER_DATA must be factory calibrated + * data, but not runtime calibrated. + */ +#define CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER UINT8_C(55) + +/** + * Accelerometer temperature. + * + * Generates: CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA + */ +#define CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE UINT8_C(56) + +/** + * Gyroscope temperature. + * + * Generates: CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA + */ +#define CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE UINT8_C(57) + +/** + * Magnetometer temperature. + * + * Generates: CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA + */ +#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE UINT8_C(58) + +#if CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE >= CHRE_SENSOR_TYPE_VENDOR_START +#error Too many sensor types +#endif + +/** + * Values that can be stored in the accuracy field of chreSensorDataHeader. + * If CHRE_SENSOR_ACCURACY_UNKNOWN is returned, then the driver did not provide + * accuracy information with the data. Values in the range + * [CHRE_SENSOR_ACCURACY_VENDOR_START, CHRE_SENSOR_ACCURACY_VENDOR_END] are + * reserved for vendor-specific values for vendor sensor types, and are not used + * by CHRE for standard sensor types. + * + * Otherwise, the values have the same meaning as defined in the Android + * Sensors definition: + * https://developer.android.com/reference/android/hardware/SensorManager + * + * @since v1.3 + * + * @defgroup CHRE_SENSOR_ACCURACY + * @{ + */ + +#define CHRE_SENSOR_ACCURACY_UNKNOWN UINT8_C(0) +#define CHRE_SENSOR_ACCURACY_UNRELIABLE UINT8_C(1) +#define CHRE_SENSOR_ACCURACY_LOW UINT8_C(2) +#define CHRE_SENSOR_ACCURACY_MEDIUM UINT8_C(3) +#define CHRE_SENSOR_ACCURACY_HIGH UINT8_C(4) +#define CHRE_SENSOR_ACCURACY_VENDOR_START UINT8_C(192) +#define CHRE_SENSOR_ACCURACY_VENDOR_END UINT8_MAX + +/** @} */ + +/** + * Header used in every structure containing batchable data from a sensor. + * + * The typical structure for sensor data looks like: + * + * struct chreSensorTypeData { + * struct chreSensorDataHeader header; + * struct chreSensorTypeSampleData { + * uint32_t timestampDelta; + * union { + * <type> value; + * <type> interpretation0; + * <type> interpretation1; + * }; + * } readings[1]; + * }; + * + * Despite 'readings' being declared as an array of 1 element, + * an instance of the struct will actually have 'readings' as + * an array of header.readingCount elements (which may be 1). + * The 'timestampDelta' is in relation to the previous 'readings' (or + * the baseTimestamp for readings[0]. So, + * Timestamp for readings[0] == header.baseTimestamp + + * readings[0].timestampDelta. + * Timestamp for readings[1] == timestamp for readings[0] + + * readings[1].timestampDelta. + * And thus, in order to determine the timestamp for readings[N], it's + * necessary to process through all of the N-1 readings. The advantage, + * though, is that our entire readings can span an arbitrary length of time, + * just as long as any two consecutive readings differ by no more than + * 4.295 seconds (timestampDelta, like all time in the CHRE, is in + * nanoseconds). + * + * If a sensor has batched readings where two consecutive readings differ by + * more than 4.295 seconds, the CHRE will split them across multiple + * instances of the struct, and send multiple events. + * + * The value from the sensor is typically expressed in a union, + * allowing a generic access to the data ('value'), along with + * differently named access giving a more natural interpretation + * of the data for the specific sensor types which use this + * structure. This allows, for example, barometer code to + * reference readings[N].pressure, and an ambient light sensor + * to reference readings[N].light, while both use the same + * structure. + */ +struct chreSensorDataHeader { + /** + * The base timestamp, in nanoseconds; must be in the same time base as + * chreGetTime(). + */ + uint64_t baseTimestamp; + + /** + * The handle of the sensor producing this event. + */ + uint32_t sensorHandle; + + /** + * The number elements in the 'readings' array. + * + * This must be at least 1. + */ + uint16_t readingCount; + + /** + * The accuracy of the sensor data. + * + * @ref CHRE_SENSOR_ACCURACY + * + * @since v1.3 + */ + uint8_t accuracy; + + /** + * Reserved bytes. + * + * This must be 0. + */ + uint8_t reserved; +}; + +/** + * Data for a sensor which reports on three axes. + * + * This is used by CHRE_EVENT_SENSOR_ACCELEROMETER_DATA, + * CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO, + * CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA, + * CHRE_EVENT_SENSOR_GYROSCOPE_DATA, + * CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO, + * CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA, + * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA, + * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO, and + * CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA. + */ +struct chreSensorThreeAxisData { + /** + * @see chreSensorDataHeader + */ + struct chreSensorDataHeader header; + struct chreSensorThreeAxisSampleData { + /** + * @see chreSensorDataHeader + */ + uint32_t timestampDelta; + union { + float values[3]; + float v[3]; + struct { + float x; + float y; + float z; + }; + float bias[3]; + struct { + float x_bias; + float y_bias; + float z_bias; + }; + }; + } readings[1]; +}; + +/** + * Data from a sensor where we only care about a event occurring. + * + * This is a bit unusual in that our readings have no data in addition + * to the timestamp. But since we only care about the occurrence, we + * don't need to know anything else. + * + * Used by: CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA, + * CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA, and + * CHRE_EVENT_SENSOR_STEP_DETECT_DATA. + */ +struct chreSensorOccurrenceData { + struct chreSensorDataHeader header; + struct chreSensorOccurrenceSampleData { + uint32_t timestampDelta; + // This space intentionally left blank. + // Only the timestamp is meaningful here, there + // is no additional data. + } readings[1]; +}; + +/** + * This is used by CHRE_EVENT_SENSOR_LIGHT_DATA, + * CHRE_EVENT_SENSOR_PRESSURE_DATA, + * CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA, + * CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA, + * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA, and + * CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA. + */ +struct chreSensorFloatData { + struct chreSensorDataHeader header; + struct chreSensorFloatSampleData { + uint32_t timestampDelta; + union { + float value; + float light; //!< Unit: lux + float pressure; //!< Unit: hectopascals (hPa) + float temperature; //!< Unit: degrees Celsius + float angle; //!< Unit: angular degrees + }; + } readings[1]; +}; + +/** + * CHRE_EVENT_SENSOR_PROXIMITY_DATA. + */ +struct chreSensorByteData { + struct chreSensorDataHeader header; + struct chreSensorByteSampleData { + uint32_t timestampDelta; + union { + uint8_t value; + struct { + uint8_t isNear : 1; + //! @deprecated As of v1.2, this field is deprecated and must + //! always be set to 0 + uint8_t invalid : 1; + uint8_t padding0 : 6; + }; + }; + } readings[1]; +}; + +/** + * Data for a sensor which reports a single uint64 value. + * + * This is used by CHRE_EVENT_SENSOR_STEP_COUNTER_DATA. + */ +struct chreSensorUint64Data { + struct chreSensorDataHeader header; + struct chreSensorUint64SampleData { + uint32_t timestampDelta; + uint64_t value; + } readings[1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_SENSOR_TYPES_H_ */ diff --git a/chre_api/legacy/v1_8/chre/toolchain.h b/chre_api/legacy/v1_8/chre/toolchain.h new file mode 100644 index 00000000..c2b87222 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/toolchain.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef CHRE_TOOLCHAIN_H_ +#define CHRE_TOOLCHAIN_H_ + +/** + * @file + * Compiler/build toolchain-specific macros used by the CHRE API + */ + +#if defined(__GNUC__) || defined(__clang__) +// For GCC and clang + +#define CHRE_DEPRECATED(message) \ + __attribute__((deprecated(message))) + +// Enable printf-style compiler warnings for mismatched format string and args +#define CHRE_PRINTF_ATTR(formatPos, argStart) \ + __attribute__((format(printf, formatPos, argStart))) + +#define CHRE_BUILD_ERROR(message) CHRE_DO_PRAGMA(GCC error message) +#define CHRE_DO_PRAGMA(message) _Pragma(#message) + +#elif defined(__ICCARM__) || defined(__CC_ARM) +// For IAR ARM and Keil MDK-ARM compilers + +#define CHRE_PRINTF_ATTR(formatPos, argStart) + +#elif defined(_MSC_VER) +// For Microsoft Visual Studio + +#define CHRE_PRINTF_ATTR(formatPos, argStart) + +#else // if !defined(__GNUC__) && !defined(__clang__) + +#error Need to add support for new compiler + +#endif + +// For platforms that don't support error pragmas, utilize the best method of +// showing an error depending on the platform support. +#ifndef CHRE_BUILD_ERROR +#ifdef __cplusplus // C++17 or greater assumed +#define CHRE_BUILD_ERROR(message) static_assert(0, message) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define CHRE_BUILD_ERROR(message) _Static_assert(0, message) +#else +#define CHRE_BUILD_ERROR(message) char buildError[-1] = message +#endif +#endif + +#endif // CHRE_TOOLCHAIN_H_ diff --git a/chre_api/legacy/v1_8/chre/user_settings.h b/chre_api/legacy/v1_8/chre/user_settings.h new file mode 100644 index 00000000..c40be90e --- /dev/null +++ b/chre_api/legacy/v1_8/chre/user_settings.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef _CHRE_USER_SETTINGS_H_ +#define _CHRE_USER_SETTINGS_H_ + +/** + * @file + * The API for requesting notifications on changes in the settings of the + * active user. If the device is set up with one or more secondary users + * (see https://source.android.com/devices/tech/admin/multi-user), the user + * settings in CHRE reflect that of the currently active user. + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <chre/event.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The user settings that nanoapps can request notifications for on a status + * change. + * + * NOTE: The WIFI available setting indicates the overall availability + * of WIFI related functionality. For example, if wifi is disabled for + * connectivity but enabled for location, the WIFI available setting is + * enabled. + * + * NOTE: The BLE available setting is the logical OR of the main Bluetooth + * setting and the Bluetooth scanning setting found under Location settings. + * Note that this indicates whether the user is allowing Bluetooth to be used, + * however the system may still fully power down the BLE chip in some scenarios + * if no request for it exists on the Android host side. See the + * chreBleStartScanAsync() API documentation for more information. + * + * @defgroup CHRE_USER_SETTINGS + * @{ + */ +#define CHRE_USER_SETTING_LOCATION UINT8_C(0) +#define CHRE_USER_SETTING_WIFI_AVAILABLE UINT8_C(1) +#define CHRE_USER_SETTING_AIRPLANE_MODE UINT8_C(2) +#define CHRE_USER_SETTING_MICROPHONE UINT8_C(3) +#define CHRE_USER_SETTING_BLE_AVAILABLE UINT8_C(4) + +/** @} */ + +/** + * Produce an event ID in the block of IDs reserved for settings notifications. + * + * @param offset Index into the event ID block, valid in the range [0,15] + */ +#define CHRE_SETTING_EVENT_ID(offset) (CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreUserSettingChangedEvent + * + * Notify nanoapps of a change in the associated setting. Nanoapps must first + * register (via chreUserSettingConfigureEvents) for events before they are + * sent out. + */ +#define CHRE_EVENT_SETTING_CHANGED_LOCATION CHRE_SETTING_EVENT_ID(0) +#define CHRE_EVENT_SETTING_CHANGED_WIFI_AVAILABLE CHRE_SETTING_EVENT_ID(1) +#define CHRE_EVENT_SETTING_CHANGED_AIRPLANE_MODE CHRE_SETTING_EVENT_ID(2) +#define CHRE_EVENT_SETTING_CHANGED_MICROPHONE CHRE_SETTING_EVENT_ID(3) +#define CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE CHRE_SETTING_EVENT_ID(4) + +#if CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE > CHRE_EVENT_SETTING_CHANGED_LAST_EVENT +#error Too many setting changed events. +#endif + +/** + * Indicates the current state of a setting. + * The setting state is 'unknown' only in the following scenarios: + * - CHRE hasn't received the initial state yet on a restart. + * - The nanoapp is running on CHRE v1.4 or older + * - Nanoapp provided in invalid setting ID to chreUserSettingGetStatus. + */ +enum chreUserSettingState { + CHRE_USER_SETTING_STATE_UNKNOWN = -1, + CHRE_USER_SETTING_STATE_DISABLED = 0, + CHRE_USER_SETTING_STATE_ENABLED = 1 +}; + +/** + * The nanoappHandleEvent argument for CHRE settings changed notifications. + */ +struct chreUserSettingChangedEvent { + //! Indicates the setting whose state has changed. + uint8_t setting; + + //! A value that corresponds to a member in enum chreUserSettingState, + // indicating the latest value of the setting. + int8_t settingState; +}; + +/** + * Get the current state of a given setting. + * + * @param setting The setting to get the current status of. + * + * @return The current state of the requested setting. The state is returned + * as an int8_t to be consistent with the associated event data, but is + * guaranteed to be a valid enum chreUserSettingState member. + * + * @since v1.5 + */ +int8_t chreUserSettingGetState(uint8_t setting); + +/** + * Register or deregister for a notification on a status change for a given + * setting. Note that registration does not produce an event with the initial + * (or current) state, though nanoapps can use chreUserSettingGetState() for + * this purpose. + * + * @param setting The setting on whose change a notification is desired. + * @param enable The nanoapp is registered to receive notifications on a + * change in the user settings if this parameter is true, otherwise the + * nanoapp receives no further notifications for this setting. + * + * @since v1.5 + */ +void chreUserSettingConfigureEvents(uint8_t setting, bool enable); + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_USER_SETTINGS_H_ */ diff --git a/chre_api/legacy/v1_8/chre/version.h b/chre_api/legacy/v1_8/chre/version.h new file mode 100644 index 00000000..6872fdde --- /dev/null +++ b/chre_api/legacy/v1_8/chre/version.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_VERSION_H_ +#define _CHRE_VERSION_H_ + +/** + * @file + * Definitions and methods for the versioning of the Context Hub Runtime + * Environment. + * + * The CHRE API versioning pertains to all header files in the CHRE API. + */ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Value for version 0.1 of the Context Hub Runtime Environment API interface. + * + * This is a legacy version of the CHRE API. Version 1.0 is considered the first + * official CHRE API version. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_0_1 UINT32_C(0x00010000) + +/** + * Value for version 1.0 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android Nougat release. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_0 UINT32_C(0x01000000) + +/** + * Value for version 1.1 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android O release. It adds + * initial support for new GNSS, WiFi, and WWAN modules. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_1 UINT32_C(0x01010000) + +/** + * Value for version 1.2 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android P release. It adds + * initial support for the new audio module. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_2 UINT32_C(0x01020000) + +/** + * Value for version 1.3 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android Q release. It adds + * support for GNSS location altitude/speed/bearing accuracy. It also adds step + * detect as a standard CHRE sensor and supports bias event delivery and sensor + * data flushing. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_3 UINT32_C(0x01030000) + +/** + * Value for version 1.4 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android R release. It adds + * support for collecting debug dump information from nanoapps, receiving L5 + * GNSS measurements, determining if a sensor supports passive requests, + * receiving 5G cell info, and deprecates chreSendMessageToHost. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_4 UINT32_C(0x01040000) + +/** + * Value for version 1.5 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API shipped with the Android S release. It adds + * support for multiple sensors of the same type, permissions for sensitive CHRE + * APIs / data usage, ability to receive user settings updates, step counter and + * hinge angle sensors, improved WiFi scan preferences to support power + * optimization, new WiFi security types, increased the lower bound for the + * maximum CHRE to host message size, and increased GNSS measurements in + * chreGnssDataEvent. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_5 UINT32_C(0x01050000) + +/** + * Value for version 1.6 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API is shipped with the Android T release. It adds + * support for BLE scanning, subscribing to the WiFi NAN discovery engine, + * subscribing to host endpoint notifications, requesting metadata for a host + * endpoint ID, nanoapps publishing RPC services they support, and limits the + * nanoapp instance ID size to INT16_MAX. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_6 UINT32_C(0x01060000) + +/** + * Value for version 1.7 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API is shipped with a post-launch update to the + * Android T release. It adds the BLE flush API. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_7 UINT32_C(0x01070000) + +/** + * Value for version 1.8 of the Context Hub Runtime Environment API interface. + * + * This version of the CHRE API is shipped with the Android U release. + * + * @note This version of the CHRE API has not been finalized yet, and is + * currently considered a preview that is subject to change. + * + * @see CHRE_API_VERSION + */ +#define CHRE_API_VERSION_1_8 UINT32_C(0x01080000) + +/** + * Major and Minor Version of this Context Hub Runtime Environment API. + * + * The major version changes when there is an incompatible API change. + * + * The minor version changes when there is an addition in functionality + * in a backwards-compatible manner. + * + * We define the version number as an unsigned 32-bit value. The most + * significant byte is the Major Version. The second-most significant byte + * is the Minor Version. The two least significant bytes are the Patch + * Version. The Patch Version is not defined by this header API, but + * is provided by a specific CHRE implementation (see chreGetVersion()). + * + * Note that version numbers can always be numerically compared with + * expected results, so 1.0.0 < 1.0.4 < 1.1.0 < 2.0.300 < 3.5.0. + */ +#define CHRE_API_VERSION CHRE_API_VERSION_1_8 + +/** + * Utility macro to extract only the API major version of a composite CHRE + * version. + * + * @param version A uint32_t version, e.g. the value returned by + * chreGetApiVersion() + * + * @return The API major version in the least significant byte, e.g. 0x01 + */ +#define CHRE_EXTRACT_MAJOR_VERSION(version) \ + (uint32_t)(((version) & UINT32_C(0xFF000000)) >> 24) + +/** + * Utility macro to extract only the API minor version of a composite CHRE + * version. + * + * @param version A uint32_t version, e.g. the CHRE_API_VERSION constant + * + * @return The API minor version in the least significant byte, e.g. 0x01 + */ +#define CHRE_EXTRACT_MINOR_VERSION(version) \ + (uint32_t)(((version) & UINT32_C(0x00FF0000)) >> 16) + +/** + * Utility macro to extract only the API minor version of a composite CHRE + * version. + * + * @param version A complete uint32_t version, e.g. the value returned by + * chreGetVersion() + * + * @return The implementation patch version in the least significant two bytes, + * e.g. 0x0123, with all other bytes set to 0 + */ +#define CHRE_EXTRACT_PATCH_VERSION(version) (uint32_t)((version) & UINT32_C(0xFFFF)) + +/** + * Get the API version the CHRE implementation was compiled against. + * + * This is not necessarily the CHRE_API_VERSION in the header the nanoapp was + * built against, and indeed may not have even appeared in the context_hub_os.h + * header which this nanoapp was built against. + * + * By definition, this will have the two least significant bytes set to 0, + * and only contain the major and minor version number. + * + * @return The API version. + */ +uint32_t chreGetApiVersion(void); + +/** + * Get the version of this CHRE implementation. + * + * By definition, ((chreGetApiVersion() & UINT32_C(0xFFFF0000)) == + * (chreGetVersion() & UINT32_C(0xFFFF0000))). + * + * The Patch Version, in the lower two bytes, only have meaning in context + * of this specific platform ID. It is increased by the platform every time + * a backwards-compatible bug fix is released. + * + * @return The version. + * + * @see chreGetPlatformId() + */ +uint32_t chreGetVersion(void); + +/** + * Get the Platform ID of this CHRE. + * + * The most significant five bytes are the vendor ID as set out by the + * NANOAPP_VENDOR convention in the original context hub HAL header file + * (context_hub.h), also used by nanoapp IDs. + * + * The least significant three bytes are set by the vendor, but must be + * unique for each different CHRE implementation/hardware that the vendor + * supplies. + * + * The idea is that in the case of known bugs in the field, a new nanoapp could + * be shipped with a workaround that would use this value, and chreGetVersion(), + * to have code that can conditionally work around the bug on a buggy version. + * Thus, we require this uniqueness to allow such a setup to work. + * + * @return The platform ID. + * + * @see CHRE_EXTRACT_VENDOR_ID + */ +uint64_t chreGetPlatformId(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_VERSION_H_ */ diff --git a/chre_api/legacy/v1_8/chre/wifi.h b/chre_api/legacy/v1_8/chre/wifi.h new file mode 100644 index 00000000..b4bda9c0 --- /dev/null +++ b/chre_api/legacy/v1_8/chre/wifi.h @@ -0,0 +1,1313 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_WIFI_H_ +#define _CHRE_WIFI_H_ + +/** + * @file + * WiFi (IEEE 802.11) API, currently covering scanning features useful for + * determining location and offloading certain connectivity scans. + * + * In this file, specification references use the following shorthand: + * + * Shorthand | Full specification name + * ---------- | ------------------------ + * "802.11" | IEEE Std 802.11-2007 + * "HT" | IEEE Std 802.11n-2009 + * "VHT" | IEEE Std 802.11ac-2013 + * "WiFi 6" | IEEE Std 802.11ax draft + * "NAN" | Wi-Fi Neighbor Awareness Networking (NAN) Technical + * Specification (v3.2) + * + * In the current version of CHRE API, the 6GHz band introduced in WiFi 6 is + * not supported. A scan request from CHRE should not result in scanning 6GHz + * channels. In particular, if a 6GHz channel is specified in scanning or + * ranging request parameter, CHRE should return an error code of + * CHRE_ERROR_NOT_SUPPORTED. Additionally, CHRE implementations must not include + * observations of access points on 6GHz channels in scan results, especially + * those produced due to scan monitoring. + */ + +#include "common.h" +#include <chre/common.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The set of flags returned by chreWifiGetCapabilities(). + * @defgroup CHRE_WIFI_CAPABILITIES + * @{ + */ + +//! No WiFi APIs are supported +#define CHRE_WIFI_CAPABILITIES_NONE UINT32_C(0) + +//! Listening to scan results is supported, as enabled via +//! chreWifiConfigureScanMonitorAsync() +#define CHRE_WIFI_CAPABILITIES_SCAN_MONITORING UINT32_C(1 << 0) + +//! Requesting WiFi scans on-demand is supported via chreWifiRequestScanAsync() +#define CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN UINT32_C(1 << 1) + +//! Specifying the radio chain preference in on-demand scan requests, and +//! reporting it in scan events is supported +//! @since v1.2 +#define CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF UINT32_C(1 << 2) + +//! Requesting RTT ranging is supported via chreWifiRequestRangingAsync() +//! @since v1.2 +#define CHRE_WIFI_CAPABILITIES_RTT_RANGING UINT32_C(1 << 3) + +//! Specifies if WiFi NAN service subscription is supported. If a platform +//! supports subscriptions, then it must also support RTT ranging for NAN +//! services via chreWifiNanRequestRangingAsync() +//! @since v1.6 +#define CHRE_WIFI_CAPABILITIES_NAN_SUB UINT32_C(1 << 4) + +/** @} */ + +/** + * Produce an event ID in the block of IDs reserved for WiFi + * @param offset Index into WiFi event ID block; valid range [0,15] + */ +#define CHRE_WIFI_EVENT_ID(offset) (CHRE_EVENT_WIFI_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreAsyncResult + * + * Communicates the asynchronous result of a request to the WiFi API. The + * requestType field in {@link #chreAsyncResult} is set to a value from enum + * chreWifiRequestType. + */ +#define CHRE_EVENT_WIFI_ASYNC_RESULT CHRE_WIFI_EVENT_ID(0) + +/** + * nanoappHandleEvent argument: struct chreWifiScanEvent + * + * Provides results of a WiFi scan. + */ +#define CHRE_EVENT_WIFI_SCAN_RESULT CHRE_WIFI_EVENT_ID(1) + +/** + * nanoappHandleEvent argument: struct chreWifiRangingEvent + * + * Provides results of an RTT ranging request. + */ +#define CHRE_EVENT_WIFI_RANGING_RESULT CHRE_WIFI_EVENT_ID(2) + +/** + * nanoappHandleEvent argument: struct chreWifiNanIdentifierEvent + * + * Lets the client know if the NAN engine was able to successfully assign + * an identifier to the subscribe call. The 'cookie' field in the event + * argument struct can be used to track which subscribe request this identifier + * maps to. + */ +#define CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT CHRE_WIFI_EVENT_ID(3) + +/** + * nanoappHandleEvent argument: struct chreWifiNanDiscoveryEvent + * + * Event that is sent whenever a NAN service matches the criteria specified + * in a subscription request. + */ +#define CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT CHRE_WIFI_EVENT_ID(4) + +/** + * nanoappHandleEvent argument: struct chreWifiNanSessionLostEvent + * + * Informs the client that a discovered service is no longer available or + * visible. + * The ID of the service on the client that was communicating with the extinct + * service is indicated by the event argument. + */ +#define CHRE_EVENT_WIFI_NAN_SESSION_LOST CHRE_WIFI_EVENT_ID(5) + +/** + * nanoappHandleEvent argument: struct chreWifiNanSessionTerminatedEvent + * + * Signals the end of a NAN subscription session. The termination can be due to + * the user turning the WiFi off, or other platform reasons like not being able + * to support NAN concurrency with the host. The terminated event will have a + * reason code appropriately populated to denote why the event was sent. + */ +#define CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED CHRE_WIFI_EVENT_ID(6) + +// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved +// (see chre/event.h) + +/** + * The maximum amount of time that is allowed to elapse between a call to + * chreWifiRequestScanAsync() that returns true, and the associated + * CHRE_EVENT_WIFI_ASYNC_RESULT used to indicate whether the scan completed + * successfully or not. + */ +#define CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS (30 * CHRE_NSEC_PER_SEC) + +/** + * The maximum amount of time that is allowed to elapse between a call to + * chreWifiRequestRangingAsync() that returns true, and the associated + * CHRE_EVENT_WIFI_RANGING_RESULT used to indicate whether the ranging operation + * completed successfully or not. + */ +#define CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS (30 * CHRE_NSEC_PER_SEC) + +/** + * The current compatibility version of the chreWifiScanEvent structure, + * including nested structures. + */ +#define CHRE_WIFI_SCAN_EVENT_VERSION UINT8_C(1) + +/** + * The current compatibility version of the chreWifiRangingEvent structure, + * including nested structures. + */ +#define CHRE_WIFI_RANGING_EVENT_VERSION UINT8_C(0) + +/** + * Maximum number of frequencies that can be explicitly specified when + * requesting a scan + * @see #chreWifiScanParams + */ +#define CHRE_WIFI_FREQUENCY_LIST_MAX_LEN (20) + +/** + * Maximum number of SSIDs that can be explicitly specified when requesting a + * scan + * @see #chreWifiScanParams + */ +#define CHRE_WIFI_SSID_LIST_MAX_LEN (20) + +/** + * The maximum number of devices that can be specified in a single RTT ranging + * request. + * @see #chreWifiRangingParams + */ +#define CHRE_WIFI_RANGING_LIST_MAX_LEN (10) + +/** + * The maximum number of octets in an SSID (see 802.11 7.3.2.1) + */ +#define CHRE_WIFI_SSID_MAX_LEN (32) + +/** + * The number of octets in a BSSID (see 802.11 7.1.3.3.3) + */ +#define CHRE_WIFI_BSSID_LEN (6) + +/** + * Set of flags which can either indicate a frequency band. Specified as a bit + * mask to allow for combinations in future API versions. + * @defgroup CHRE_WIFI_BAND_MASK + * @{ + */ + +#define CHRE_WIFI_BAND_MASK_2_4_GHZ UINT8_C(1 << 0) //!< 2.4 GHz +#define CHRE_WIFI_BAND_MASK_5_GHZ UINT8_C(1 << 1) //!< 5 GHz + +/** @} */ + +/** + * Characteristics of a scanned device given in struct chreWifiScanResult.flags + * @defgroup CHRE_WIFI_SCAN_RESULT_FLAGS + * @{ + */ + +#define CHRE_WIFI_SCAN_RESULT_FLAGS_NONE UINT8_C(0) + +//! Element ID 61 (HT Operation) is present (see HT 7.3.2) +#define CHRE_WIFI_SCAN_RESULT_FLAGS_HT_OPS_PRESENT UINT8_C(1 << 0) + +//! Element ID 192 (VHT Operation) is present (see VHT 8.4.2) +#define CHRE_WIFI_SCAN_RESULT_FLAGS_VHT_OPS_PRESENT UINT8_C(1 << 1) + +//! Element ID 127 (Extended Capabilities) is present, and bit 70 (Fine Timing +//! Measurement Responder) is set to 1 (see IEEE Std 802.11-2016 9.4.2.27) +#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER UINT8_C(1 << 2) + +//! Retained for backwards compatibility +//! @see CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER +#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_80211MC_RTT_RESPONDER \ + CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER + +//! HT Operation element indicates that a secondary channel is present +//! (see HT 7.3.2.57) +#define CHRE_WIFI_SCAN_RESULT_FLAGS_HAS_SECONDARY_CHANNEL_OFFSET UINT8_C(1 << 3) + +//! HT Operation element indicates that the secondary channel is below the +//! primary channel (see HT 7.3.2.57) +#define CHRE_WIFI_SCAN_RESULT_FLAGS_SECONDARY_CHANNEL_OFFSET_IS_BELOW \ + UINT8_C(1 << 4) + +/** @} */ + +/** + * Identifies the authentication methods supported by an AP. Note that not every + * combination of flags may be possible. Based on WIFI_PNO_AUTH_CODE_* from + * hardware/libhardware_legacy/include/hardware_legacy/gscan.h in Android. + * @defgroup CHRE_WIFI_SECURITY_MODE_FLAGS + * @{ + */ + +#define CHRE_WIFI_SECURITY_MODE_UNKONWN UINT8_C(0) + +#define CHRE_WIFI_SECURITY_MODE_OPEN UINT8_C(1 << 0) //!< No auth/security +#define CHRE_WIFI_SECURITY_MODE_WEP UINT8_C(1 << 1) +#define CHRE_WIFI_SECURITY_MODE_PSK UINT8_C(1 << 2) //!< WPA-PSK or WPA2-PSK +#define CHRE_WIFI_SECURITY_MODE_EAP UINT8_C(1 << 3) //!< WPA-EAP or WPA2-EAP + +//! @since v1.5 +#define CHRE_WIFI_SECURITY_MODE_SAE UINT8_C(1 << 4) + +//! @since v1.5 +#define CHRE_WIFI_SECURITY_MODE_EAP_SUITE_B UINT8_C(1 << 5) + +//! @since v1.5 +#define CHRE_WIFI_SECURITY_MODE_OWE UINT8_C(1 << 6) + +/** @} */ + +/** + * Identifies which radio chain was used to discover an AP. The underlying + * hardware does not necessarily support more than one radio chain. + * @defgroup CHRE_WIFI_RADIO_CHAIN_FLAGS + * @{ + */ + +#define CHRE_WIFI_RADIO_CHAIN_UNKNOWN UINT8_C(0) +#define CHRE_WIFI_RADIO_CHAIN_0 UINT8_C(1 << 0) +#define CHRE_WIFI_RADIO_CHAIN_1 UINT8_C(1 << 1) + +/** @} */ + +//! Special value indicating that an LCI uncertainty fields is not provided +//! Ref: RFC 6225 +#define CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN UINT8_C(0) + +/** + * Defines the flags that may be returned in + * {@link #chreWifiRangingResult.flags}. Undefined bits are reserved for future + * use and must be ignored by nanoapps. + * @defgroup CHRE_WIFI_RTT_RESULT_FLAGS + * @{ + */ + +//! If set, the nested chreWifiLci structure is populated; otherwise it is +//! invalid and must be ignored +#define CHRE_WIFI_RTT_RESULT_HAS_LCI UINT8_C(1 << 0) + +/** @} */ + +/** + * Identifies a WiFi frequency band + */ +enum chreWifiBand { + CHRE_WIFI_BAND_2_4_GHZ = CHRE_WIFI_BAND_MASK_2_4_GHZ, + CHRE_WIFI_BAND_5_GHZ = CHRE_WIFI_BAND_MASK_5_GHZ, +}; + +/** + * Indicates the BSS operating channel width determined from the VHT and/or HT + * Operation elements. Refer to VHT 8.4.2.161 and HT 7.3.2.57. + */ +enum chreWifiChannelWidth { + CHRE_WIFI_CHANNEL_WIDTH_20_MHZ = 0, + CHRE_WIFI_CHANNEL_WIDTH_40_MHZ = 1, + CHRE_WIFI_CHANNEL_WIDTH_80_MHZ = 2, + CHRE_WIFI_CHANNEL_WIDTH_160_MHZ = 3, + CHRE_WIFI_CHANNEL_WIDTH_80_PLUS_80_MHZ = 4, +}; + +/** + * Indicates the type of scan requested or performed + */ +enum chreWifiScanType { + //! Perform a purely active scan using probe requests. Do not scan channels + //! restricted to use via Dynamic Frequency Selection (DFS) only. + CHRE_WIFI_SCAN_TYPE_ACTIVE = 0, + + //! Perform an active scan on unrestricted channels, and also perform a + //! passive scan on channels that are restricted to use via Dynamic + //! Frequency Selection (DFS), e.g. the U-NII bands 5250-5350MHz and + //! 5470-5725MHz in the USA as mandated by FCC regulation. + CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS = 1, + + //! Perform a passive scan, only listening for beacons. + CHRE_WIFI_SCAN_TYPE_PASSIVE = 2, + + //! Client has no preference for a particular scan type. + //! Only valid in a {@link #chreWifiScanParams}. + //! + //! On a v1.4 or earlier platform, this will fall back to + //! CHRE_WIFI_SCAN_TYPE_ACTIVE if {@link #chreWifiScanParams.channelSet} is + //! set to CHRE_WIFI_CHANNEL_SET_NON_DFS, and to + //! CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS otherwise. + //! + //! If CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF is supported, a v1.5 or + //! later platform shall perform a type of scan optimized for {@link + //! #chreWifiScanParams.radioChainPref}. + //! + //! Clients are strongly encouraged to set this value in {@link + //! #chreWifiScanParams.scanType} and instead express their preferences + //! through {@link #chreWifiRadioChainPref} and {@link #chreWifiChannelSet} + //! so the platform can best optimize power and performance. + //! + //! @since v1.5 + CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE = 3, +}; + +/** + * Indicates whether RTT ranging with a specific device succeeded + */ +enum chreWifiRangingStatus { + //! Ranging completed successfully + CHRE_WIFI_RANGING_STATUS_SUCCESS = 0, + + //! Ranging failed due to an unspecified error + CHRE_WIFI_RANGING_STATUS_ERROR = 1, +}; + +/** + * Possible values for {@link #chreWifiLci.altitudeType}. Ref: RFC 6225 2.4 + */ +enum chreWifiLciAltitudeType { + CHRE_WIFI_LCI_ALTITUDE_TYPE_UNKNOWN = 0, + CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS = 1, + CHRE_WIFI_LCI_ALTITUDE_TYPE_FLOORS = 2, +}; + +/** + * Indicates a type of request made in this API. Used to populate the resultType + * field of struct chreAsyncResult sent with CHRE_EVENT_WIFI_ASYNC_RESULT. + */ +enum chreWifiRequestType { + CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR = 1, + CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN = 2, + CHRE_WIFI_REQUEST_TYPE_RANGING = 3, + CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE = 4, +}; + +/** + * Allows a nanoapp to express its preference for how multiple available + * radio chains should be used when performing an on-demand scan. This is only a + * preference from the nanoapp and is not guaranteed to be honored by the WiFi + * firmware. + */ +enum chreWifiRadioChainPref { + //! No preference for radio chain usage + CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT = 0, + + //! In a scan result, indicates that the radio chain preference used for the + //! scan is not known + CHRE_WIFI_RADIO_CHAIN_PREF_UNKNOWN = CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT, + + //! Prefer to use available radio chains in a way that minimizes time to + //! complete the scan + CHRE_WIFI_RADIO_CHAIN_PREF_LOW_LATENCY = 1, + + //! Prefer to use available radio chains in a way that minimizes total power + //! consumed for the scan + CHRE_WIFI_RADIO_CHAIN_PREF_LOW_POWER = 2, + + //! Prefer to use available radio chains in a way that maximizes accuracy of + //! the scan result, e.g. RSSI measurements + CHRE_WIFI_RADIO_CHAIN_PREF_HIGH_ACCURACY = 3, +}; + +/** + * WiFi NAN subscription type. + */ +enum chreWifiNanSubscribeType { + //! In the active mode, explicit transmission of a subscribe message is + //! requested, and publish messages are processed. + CHRE_WIFI_NAN_SUBSCRIBE_TYPE_ACTIVE = 0, + + //! In the passive mode, no transmission of a subscribe message is + //! requested, but received publish messages are checked for matches. + CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE = 1, +}; + +/** + * Indicates the reason for a subscribe session termination. + */ +enum chreWifiNanTerminatedReason { + CHRE_WIFI_NAN_TERMINATED_BY_USER_REQUEST = 0, + CHRE_WIFI_NAN_TERMINATED_BY_TIMEOUT = 1, + CHRE_WIFI_NAN_TERMINATED_BY_FAILURE = 2, +}; + +/** + * SSID with an explicit length field, used when an array of SSIDs is supplied. + */ +struct chreWifiSsidListItem { + //! Number of valid bytes in ssid. Valid range [0, CHRE_WIFI_SSID_MAX_LEN] + uint8_t ssidLen; + + //! Service Set Identifier (SSID) + uint8_t ssid[CHRE_WIFI_SSID_MAX_LEN]; +}; + +/** + * Indicates the set of channels to be scanned. + * + * @since v1.5 + */ +enum chreWifiChannelSet { + //! The set of channels that allows active scan using probe request. + CHRE_WIFI_CHANNEL_SET_NON_DFS = 0, + + //! The set of all channels supported. + CHRE_WIFI_CHANNEL_SET_ALL = 1, +}; + +/** + * Data structure passed to chreWifiRequestScanAsync + */ +struct chreWifiScanParams { + //! Set to a value from @ref enum chreWifiScanType + uint8_t scanType; + + //! Indicates whether the client is willing to tolerate receiving cached + //! results of a previous scan, and if so, the maximum age of the scan that + //! the client will accept. "Age" in this case is defined as the elapsed + //! time between when the most recent scan was completed and the request is + //! received, in milliseconds. If set to 0, no cached results may be + //! provided, and all scan results must come from a "fresh" WiFi scan, i.e. + //! one that completes strictly after this request is received. If more than + //! one scan is cached and meets this age threshold, only the newest scan is + //! provided. + uint32_t maxScanAgeMs; + + //! If set to 0, scan all frequencies. Otherwise, this indicates the number + //! of frequencies to scan, as specified in the frequencyList array. Valid + //! range [0, CHRE_WIFI_FREQUENCY_LIST_MAX_LEN]. + uint16_t frequencyListLen; + + //! Pointer to an array of frequencies to scan, given as channel center + //! frequencies in MHz. This field may be NULL if frequencyListLen is 0. + const uint32_t *frequencyList; + + //! If set to 0, do not restrict scan to any SSIDs. Otherwise, this + //! indicates the number of SSIDs in the ssidList array to be used for + //! directed probe requests. Not applicable and ignore when scanType is + //! CHRE_WIFI_SCAN_TYPE_PASSIVE. + uint8_t ssidListLen; + + //! Pointer to an array of SSIDs to use for directed probe requests. May be + //! NULL if ssidListLen is 0. + const struct chreWifiSsidListItem *ssidList; + + //! Set to a value from enum chreWifiRadioChainPref to specify the desired + //! trade-off between power consumption, accuracy, etc. If + //! chreWifiGetCapabilities() does not have the applicable bit set, this + //! parameter is ignored. + //! @since v1.2 + uint8_t radioChainPref; + + //! Set to a value from enum chreWifiChannelSet to specify the set of + //! channels to be scanned. This field is considered by the platform only + //! if scanType is CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE and frequencyListLen + //! is equal to zero. + //! + //! @since v1.5 + uint8_t channelSet; +}; + +/** + * Provides information about a single access point (AP) detected in a scan. + */ +struct chreWifiScanResult { + //! Number of milliseconds prior to referenceTime in the enclosing + //! chreWifiScanEvent struct when the probe response or beacon frame that + //! was used to populate this structure was received. + uint32_t ageMs; + + //! Capability Information field sent by the AP (see 802.11 7.3.1.4). This + //! field must reflect native byte order and bit ordering, such that + //! (capabilityInfo & 1) gives the bit for the ESS subfield. + uint16_t capabilityInfo; + + //! Number of valid bytes in ssid. Valid range [0, CHRE_WIFI_SSID_MAX_LEN] + uint8_t ssidLen; + + //! Service Set Identifier (SSID), a series of 0 to 32 octets identifying + //! the access point. Note that this is commonly a human-readable ASCII + //! string, but this is not the required encoding per the standard. + uint8_t ssid[CHRE_WIFI_SSID_MAX_LEN]; + + //! Basic Service Set Identifier (BSSID), represented in big-endian byte + //! order, such that the first octet of the OUI is accessed in byte index 0. + uint8_t bssid[CHRE_WIFI_BSSID_LEN]; + + //! A set of flags from CHRE_WIFI_SCAN_RESULT_FLAGS_* + uint8_t flags; + + //! RSSI (Received Signal Strength Indicator), in dBm. Typically negative. + //! If multiple radio chains were used to scan this AP, this is a "best + //! available" measure that may be a composite of measurements taken across + //! the radio chains. + int8_t rssi; + + //! Operating band, set to a value from enum chreWifiBand + uint8_t band; + + /** + * Indicates the center frequency of the primary 20MHz channel, given in + * MHz. This value is derived from the channel number via the formula: + * + * primaryChannel (MHz) = CSF + 5 * primaryChannelNumber + * + * Where CSF is the channel starting frequency (in MHz) given by the + * operating class/band (i.e. 2407 or 5000), and primaryChannelNumber is the + * channel number in the range [1, 200]. + * + * Refer to VHT 22.3.14. + */ + uint32_t primaryChannel; + + /** + * If the channel width is 20 MHz, this field is not relevant and set to 0. + * If the channel width is 40, 80, or 160 MHz, then this denotes the channel + * center frequency (in MHz). If the channel is 80+80 MHz, then this denotes + * the center frequency of segment 0, which contains the primary channel. + * This value is derived from the frequency index using the same formula as + * for primaryChannel. + * + * Refer to VHT 8.4.2.161, and VHT 22.3.14. + * + * @see #primaryChannel + */ + uint32_t centerFreqPrimary; + + /** + * If the channel width is 80+80MHz, then this denotes the center frequency + * of segment 1, which does not contain the primary channel. Otherwise, this + * field is not relevant and set to 0. + * + * @see #centerFreqPrimary + */ + uint32_t centerFreqSecondary; + + //! @see #chreWifiChannelWidth + uint8_t channelWidth; + + //! Flags from CHRE_WIFI_SECURITY_MODE_* indicating supported authentication + //! and associated security modes + //! @see CHRE_WIFI_SECURITY_MODE_FLAGS + uint8_t securityMode; + + //! Identifies the radio chain(s) used to discover this AP + //! @see CHRE_WIFI_RADIO_CHAIN_FLAGS + //! @since v1.2 + uint8_t radioChain; + + //! If the CHRE_WIFI_RADIO_CHAIN_0 bit is set in radioChain, gives the RSSI + //! measured on radio chain 0 in dBm; otherwise invalid and set to 0. This + //! field, along with its relative rssiChain1, can be used to determine RSSI + //! measurements from each radio chain when multiple chains were used to + //! discover this AP. + //! @see #radioChain + //! @since v1.2 + int8_t rssiChain0; + int8_t rssiChain1; //!< @see #rssiChain0 + + //! Reserved; set to 0 + uint8_t reserved[7]; +}; + +/** + * Data structure sent with events of type CHRE_EVENT_WIFI_SCAN_RESULT. + */ +struct chreWifiScanEvent { + //! Indicates the version of the structure, for compatibility purposes. + //! Clients do not normally need to worry about this field; the CHRE + //! implementation guarantees that the client only receives the structure + //! version it expects. + uint8_t version; + + //! The number of entries in the results array in this event. The CHRE + //! implementation may split scan results across multiple events for memory + //! concerns, etc. + uint8_t resultCount; + + //! The total number of results returned by the scan. Allows an event + //! consumer to identify when it has received all events associated with a + //! scan. + uint8_t resultTotal; + + //! Sequence number for this event within the series of events comprising a + //! complete scan result. Scan events are delivered strictly in order, i.e. + //! this is monotonically increasing for the results of a single scan. Valid + //! range [0, <number of events for scan> - 1]. The number of events for a + //! scan is typically given by + //! ceil(resultTotal / <max results per event supported by platform>). + uint8_t eventIndex; + + //! A value from enum chreWifiScanType indicating the type of scan performed + uint8_t scanType; + + //! If a directed scan was performed to a limited set of SSIDs, then this + //! identifies the number of unique SSIDs included in the probe requests. + //! Otherwise, this is set to 0, indicating that the scan was not limited by + //! SSID. Note that if this is non-zero, the list of SSIDs used is not + //! included in the scan event. + uint8_t ssidSetSize; + + //! If 0, indicates that all frequencies applicable for the scanType were + //! scanned. Otherwise, indicates the number of frequencies scanned, as + //! specified in scannedFreqList. + uint16_t scannedFreqListLen; + + //! Timestamp when the scan was completed, from the same time base as + //! chreGetTime() (in nanoseconds) + uint64_t referenceTime; + + //! Pointer to an array containing scannedFreqListLen values comprising the + //! set of frequencies that were scanned. Frequencies are specified as + //! channel center frequencies in MHz. May be NULL if scannedFreqListLen is + //! 0. + const uint32_t *scannedFreqList; + + //! Pointer to an array containing resultCount entries. May be NULL if + //! resultCount is 0. + const struct chreWifiScanResult *results; + + //! Set to a value from enum chreWifiRadioChainPref indicating the radio + //! chain preference used for the scan. If the applicable bit is not set in + //! chreWifiGetCapabilities(), this will always be set to + //! CHRE_WIFI_RADIO_CHAIN_PREF_UNKNOWN. + //! @since v1.2 + uint8_t radioChainPref; +}; + +/** + * Identifies a device to perform RTT ranging against. These values are normally + * populated based on the contents of a scan result. + * @see #chreWifiScanResult + * @see chreWifiRangingTargetFromScanResult() + */ +struct chreWifiRangingTarget { + //! Device MAC address, specified in the same byte order as + //! {@link #chreWifiScanResult.bssid} + uint8_t macAddress[CHRE_WIFI_BSSID_LEN]; + + //! Center frequency of the primary 20MHz channel, in MHz + //! @see #chreWifiScanResult.primaryChannel + uint32_t primaryChannel; + + //! Channel center frequency, in MHz, or 0 if not relevant + //! @see #chreWifiScanResult.centerFreqPrimary + uint32_t centerFreqPrimary; + + //! Channel center frequency of segment 1 if channel width is 80+80MHz, + //! otherwise 0 + //! @see #chreWifiScanResult.centerFreqSecondary + uint32_t centerFreqSecondary; + + //! @see #chreWifiChannelWidth + uint8_t channelWidth; + + //! Reserved for future use and ignored by CHRE + uint8_t reserved[3]; +}; + +/** + * Parameters for an RTT ("Fine Timing Measurement" in terms of 802.11-2016) + * ranging request, supplied to chreWifiRequestRangingAsync(). + */ +struct chreWifiRangingParams { + //! Number of devices to perform ranging against and the length of + //! targetList, in range [1, CHRE_WIFI_RANGING_LIST_MAX_LEN]. + uint8_t targetListLen; + + //! Array of macAddressListLen MAC addresses (e.g. BSSIDs) with which to + //! attempt RTT ranging. + const struct chreWifiRangingTarget *targetList; +}; + +/** + * Provides the result of RTT ranging with a single device. + */ +struct chreWifiRangingResult { + //! Time when the ranging operation on this device was performed, in the + //! same time base as chreGetTime() (in nanoseconds) + uint64_t timestamp; + + //! MAC address of the device for which ranging was requested + uint8_t macAddress[CHRE_WIFI_BSSID_LEN]; + + //! Gives the result of ranging to this device. If not set to + //! CHRE_WIFI_RANGING_STATUS_SUCCESS, the ranging attempt to this device + //! failed, and other fields in this structure may be invalid. + //! @see #chreWifiRangingStatus + uint8_t status; + + //! The mean RSSI measured during the RTT burst, in dBm. Typically negative. + //! If status is not CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0. + int8_t rssi; + + //! Estimated distance to the device with the given BSSID, in millimeters. + //! Generally the mean of multiple measurements performed in a single burst. + //! If status is not CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0. + uint32_t distance; + + //! Standard deviation of estimated distance across multiple measurements + //! performed in a single RTT burst, in millimeters. If status is not + //! CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0. + uint32_t distanceStdDev; + + //! Location Configuration Information (LCI) information optionally returned + //! during the ranging procedure. Only valid if {@link #flags} has the + //! CHRE_WIFI_RTT_RESULT_HAS_LCI bit set. Refer to IEEE 802.11-2016 + //! 9.4.2.22.10, 11.24.6.7, and RFC 6225 (July 2011) for more information. + //! Coordinates are to be interpreted according to the WGS84 datum. + struct chreWifiLci { + //! Latitude in degrees as 2's complement fixed-point with 25 fractional + //! bits, i.e. degrees * 2^25. Ref: RFC 6225 2.3 + int64_t latitude; + + //! Longitude, same format as {@link #latitude} + int64_t longitude; + + //! Altitude represented as a 2's complement fixed-point value with 8 + //! fractional bits. Interpretation depends on {@link #altitudeType}. If + //! UNKNOWN, this field must be ignored. If *METERS, distance relative + //! to the zero point in the vertical datum. If *FLOORS, a floor value + //! relative to the ground floor, potentially fractional, e.g. to + //! indicate mezzanine levels. Ref: RFC 6225 2.4 + int32_t altitude; + + //! Maximum extent of latitude uncertainty in degrees, decoded via this + //! formula: 2 ^ (8 - x) where "x" is the encoded value passed in this + //! field. Unknown if set to CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN. + //! Ref: RFC 6225 2.3.2 + uint8_t latitudeUncertainty; + + //! @see #latitudeUncertainty + uint8_t longitudeUncertainty; + + //! Defines how to interpret altitude, set to a value from enum + //! chreWifiLciAltitudeType + uint8_t altitudeType; + + //! Uncertainty in altitude, decoded via this formula: 2 ^ (21 - x) + //! where "x" is the encoded value passed in this field. Unknown if set + //! to CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN. Only applies when altitudeType + //! is CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS. Ref: RFC 6225 2.4.5 + uint8_t altitudeUncertainty; + } lci; + + //! Refer to CHRE_WIFI_RTT_RESULT_FLAGS + uint8_t flags; + + //! Reserved; set to 0 + uint8_t reserved[7]; +}; + +/** + * Data structure sent with events of type CHRE_EVENT_WIFI_RANGING_RESULT. + */ +struct chreWifiRangingEvent { + //! Indicates the version of the structure, for compatibility purposes. + //! Clients do not normally need to worry about this field; the CHRE + //! implementation guarantees that the client only receives the structure + //! version it expects. + uint8_t version; + + //! The number of ranging results included in the results array; matches the + //! number of MAC addresses specified in the request + uint8_t resultCount; + + //! Reserved; set to 0 + uint8_t reserved[2]; + + //! Pointer to an array containing resultCount entries + const struct chreWifiRangingResult *results; +}; + +/** + * Indicates the WiFi NAN capabilities of the device. Must contain non-zero + * values if WiFi NAN is supported. + */ +struct chreWifiNanCapabilities { + //! Maximum length of the match filter arrays (applies to both tx and rx + //! match filters). + uint32_t maxMatchFilterLength; + + //! Maximum length of the service specific information byte array. + uint32_t maxServiceSpecificInfoLength; + + //! Maximum length of the service name. Includes the NULL terminator. + uint8_t maxServiceNameLength; + + //! Reserved for future use. + uint8_t reserved[3]; +}; + +/** + * Data structure sent with events of type + * CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT + */ +struct chreWifiNanIdentifierEvent { + //! A unique ID assigned by the NAN engine for the subscribe request + //! associated with the cookie encapsulated in the async result below. The + //! ID is set to 0 if there was a request failure in which case the async + //! result below contains the appropriate error code indicating the failure + //! reason. + uint32_t id; + + //! Structure which contains the cookie associated with the publish/ + //! subscribe request, along with an error code that indicates request + //! success or failure. + struct chreAsyncResult result; +}; + +/** + * Indicates the desired configuration for a WiFi NAN ranging request. + */ +struct chreWifiNanRangingParams { + //! MAC address of the NAN device for which range is to be determined. + uint8_t macAddress[CHRE_WIFI_BSSID_LEN]; +}; + +/** + * Configuration parameters specific to the Subscribe Function (Spec 4.1.1.1) + */ +struct chreWifiNanSubscribeConfig { + //! Indicates the subscribe type, set to a value from @ref + //! chreWifiNanSubscribeType. + uint8_t subscribeType; + + //! UTF-8 name string that identifies the service/application. Must be NULL + //! terminated. Note that the string length cannot be greater than the + //! maximum length specified by @ref chreWifiNanCapabilities. No + //! restriction is placed on the string case, since the service name + //! matching is expected to be case insensitive. + const char *service; + + //! An array of bytes (and the associated array length) of service-specific + //! information. Note that the array length must be less than the + //! maxServiceSpecificInfoLength parameter obtained from the NAN + //! capabilities (@see struct chreWifiNanCapabilities). + const uint8_t *serviceSpecificInfo; + uint32_t serviceSpecificInfoSize; + + //! Ordered sequence of {length | value} pairs that specify match criteria + //! beyond the service name. 'length' uses 1 byte, and its value indicates + //! the number of bytes of the match criteria that follow. The length of + //! the match filter array should not exceed the maximum match filter + //! length obtained from @ref chreWifiNanGetCapabilities. When a service + //! publish message discovery frame containing the Service ID being + //! subscribed to is received, the matching is done as follows: + //! Each {length | value} pair in the kth position (1 <= k <= #length-value + //! pairs) is compared against the kth {length | value} pair in the + //! matching filter field of the publish message. + //! - For a kth position {length | value} pair in the rx match filter with + //! a length of 0, a match is declared regardless of the tx match filter + //! contents. + //! - For a kth position {length | value} pair in the rx match with a non- + //! zero length, there must be an exact match with the kth position pair + //! in the match filter field of the received service descriptor for a + //! match to be found. + //! Please refer to Appendix H of the NAN spec for examples on matching. + //! The match filter length should not exceed the maxMatchFilterLength + //! obtained from @ref chreWifiNanCapabilities. + const uint8_t *matchFilter; + uint32_t matchFilterLength; +}; + +/** + * Data structure sent with events of type + * CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT. + */ +struct chreWifiNanDiscoveryEvent { + //! Identifier of the subscribe function instance that requested a + //! discovery. + uint32_t subscribeId; + + //! Identifier of the publisher on the remote NAN device. + uint32_t publishId; + + //! NAN interface address of the publisher + uint8_t publisherAddress[CHRE_WIFI_BSSID_LEN]; + + //! An array of bytes (and the associated array length) of service-specific + //! information. Note that the array length must be less than the + //! maxServiceSpecificInfoLength parameter obtained from the NAN + //! capabilities (@see struct chreWifiNanCapabilities). + const uint8_t *serviceSpecificInfo; + uint32_t serviceSpecificInfoSize; +}; + +/** + * Data structure sent with events of type CHRE_EVENT_WIFI_NAN_SESSION_LOST. + */ +struct chreWifiNanSessionLostEvent { + //! The original ID (returned by the NAN discovery engine) of the subscriber + //! instance. + uint32_t id; + + //! The ID of the previously discovered publisher on a peer NAN device that + //! is no longer connected. + uint32_t peerId; +}; + +/** + * Data structure sent with events of type + * CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED. + */ +struct chreWifiNanSessionTerminatedEvent { + //! The original ID (returned by the NAN discovery engine) of the subscriber + //! instance that was terminated. + uint32_t id; + + //! A value that maps to one of the termination reasons in @ref enum + //! chreWifiNanTerminatedReason. + uint8_t reason; + + //! Reserved for future use. + uint8_t reserved[3]; +}; + +/** + * Retrieves a set of flags indicating the WiFi features supported by the + * current CHRE implementation. The value returned by this function must be + * consistent for the entire duration of the Nanoapp's execution. + * + * The client must allow for more flags to be set in this response than it knows + * about, for example if the implementation supports a newer version of the API + * than the client was compiled against. + * + * @return A bitmask with zero or more CHRE_WIFI_CAPABILITIES_* flags set + * + * @since v1.1 + */ +uint32_t chreWifiGetCapabilities(void); + +/** + * Retrieves device-specific WiFi NAN capabilities, and populates them in + * the @ref chreWifiNanCapabilities structure. + * + * @param capabilities Structure into which the WiFi NAN capabilities of + * the device are populated into. Must not be NULL. + * @return true if WiFi NAN is supported, false otherwise. + * + * @since v1.6 + */ +bool chreWifiNanGetCapabilities(struct chreWifiNanCapabilities *capabilities); + +/** + * Nanoapps must define CHRE_NANOAPP_USES_WIFI somewhere in their build + * system (e.g. Makefile) if the nanoapp needs to use the following WiFi APIs. + * In addition to allowing access to these APIs, defining this macro will also + * ensure CHRE enforces that all host clients this nanoapp talks to have the + * required Android permissions needed to listen to WiFi data by adding metadata + * to the nanoapp. + */ +#if defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD) + +/** + * Manages a client's request to receive the results of WiFi scans performed for + * other purposes, for example scans done to maintain connectivity and scans + * requested by other clients. The presence of this request has no effect on the + * frequency or configuration of the WiFi scans performed - it is purely a + * registration by the client to receive the results of scans that would + * otherwise occur normally. This should include all available scan results, + * including those that are not normally sent to the applications processor, + * such as Preferred Network Offload (PNO) scans. Scan results provided because + * of this registration must not contain cached results - they are always + * expected to contain the fresh results from a recent scan. + * + * An active scan monitor subscription must persist across temporary conditions + * under which no WiFi scans will be performed, for example if WiFi is + * completely disabled via user-controlled settings, or if the WiFi system + * restarts independently of CHRE. Likewise, a request to enable a scan monitor + * subscription must succeed under normal conditions, even in circumstances + * where no WiFi scans will be performed. In these cases, the scan monitor + * implementation must produce scan results once the temporary condition is + * cleared, for example after WiFi is enabled by the user. + * + * These scan results are delivered to the Nanoapp's handle event callback using + * CHRE_EVENT_WIFI_SCAN_RESULT. + * + * An active scan monitor subscription is not necessary to receive the results + * of an on-demand scan request sent via chreWifiRequestScanAsync(), and it does + * not result in duplicate delivery of scan results generated from + * chreWifiRequestScanAsync(). + * + * If no monitor subscription is active at the time of a request with + * enable=false, it is treated as if an active subscription was successfully + * ended. + * + * The result of this request is delivered asynchronously via an event of type + * CHRE_EVENT_WIFI_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult} + * for more details. + * + * @param enable Set to true to enable monitoring scan results, false to + * disable + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires WiFi permission + */ +bool chreWifiConfigureScanMonitorAsync(bool enable, const void *cookie); + +/** + * Sends an on-demand request for WiFi scan results. This may trigger a new + * scan, or be entirely serviced from cache, depending on the maxScanAgeMs + * parameter. + * + * This resulting status of this request is delivered asynchronously via an + * event of type CHRE_EVENT_WIFI_ASYNC_RESULT. The result must be delivered + * within CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS of the this request. Refer to the + * note in {@link #chreAsyncResult} for more details. + * + * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that + * the scan results are ready to be delivered in a subsequent event (or events, + * which arrive consecutively without any other scan results in between) + * of type CHRE_EVENT_WIFI_SCAN_RESULT. + * + * WiFi scanning must be disabled if both "WiFi scanning" and "WiFi" settings + * are disabled at the Android level. In this case, the CHRE implementation is + * expected to return a result with CHRE_ERROR_FUNCTION_DISABLED. + * + * It is not valid for a client to request a new scan while a result is pending + * based on a previous scan request from the same client. In this situation, the + * CHRE implementation is expected to return a result with CHRE_ERROR_BUSY. + * However, if a scan is currently pending or in progress due to a request from + * another client, whether within the CHRE or otherwise, the implementation must + * not fail the request for this reason. If the pending scan satisfies the + * client's request parameters, then the implementation should use its results + * to satisfy the request rather than scheduling a new scan. + * + * @param params A set of parameters for the scan request. Must not be NULL. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires WiFi permission + */ +bool chreWifiRequestScanAsync(const struct chreWifiScanParams *params, + const void *cookie); + +/** + * Convenience function which calls chreWifiRequestScanAsync() with a default + * set of scan parameters. + * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires WiFi permission + */ +static inline bool chreWifiRequestScanAsyncDefault(const void *cookie) { + static const struct chreWifiScanParams params = { + /*.scanType=*/ CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE, + /*.maxScanAgeMs=*/ 5000, // 5 seconds + /*.frequencyListLen=*/ 0, + /*.frequencyList=*/ NULL, + /*.ssidListLen=*/ 0, + /*.ssidList=*/ NULL, + /*.radioChainPref=*/ CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT, + /*.channelSet=*/ CHRE_WIFI_CHANNEL_SET_NON_DFS + }; + return chreWifiRequestScanAsync(¶ms, cookie); +} + +/** + * Issues a request to initiate distance measurements using round-trip time + * (RTT), aka Fine Timing Measurement (FTM), to one or more devices identified + * by MAC address. Within CHRE, MACs are typically the BSSIDs of scanned APs + * that have the CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER flag set. + * + * This resulting status of this request is delivered asynchronously via an + * event of type CHRE_EVENT_WIFI_ASYNC_RESULT. The result must be delivered + * within CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS of the this request. Refer to the + * note in {@link #chreAsyncResult} for more details. + * + * WiFi RTT ranging must be disabled if any of the following is true: + * - Both "WiFi" and "WiFi Scanning" settings are disabled at the Android level. + * - The "Location" setting is disabled at the Android level. + * In this case, the CHRE implementation is expected to return a result with + * CHRE_ERROR_FUNCTION_DISABLED. + * + * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that + * the results of ranging will be delivered in a subsequent event of type + * CHRE_EVENT_WIFI_RANGING_RESULT. Note that the CHRE_EVENT_WIFI_ASYNC_RESULT + * gives an overall status - for example, it is used to indicate failure if the + * entire ranging request was rejected because WiFi is disabled. However, it is + * valid for this event to indicate success, but RTT ranging to fail for all + * requested devices - for example, they may be out of range. Therefore, it is + * also necessary to check the status field in {@link #chreWifiRangingResult}. + * + * @param params Structure containing the parameters of the scan request, + * including the list of devices to attempt ranging. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.2 + * @note Requires WiFi permission + */ +bool chreWifiRequestRangingAsync(const struct chreWifiRangingParams *params, + const void *cookie); + +/** + * Helper function to populate an instance of struct chreWifiRangingTarget with + * the contents of a scan result provided in struct chreWifiScanResult. + * Populates other parameters that are not directly derived from the scan result + * with default values. + * + * @param scanResult The scan result to parse as input + * @param rangingTarget The RTT ranging target to populate as output + * + * @note Requires WiFi permission + */ +static inline void chreWifiRangingTargetFromScanResult( + const struct chreWifiScanResult *scanResult, + struct chreWifiRangingTarget *rangingTarget) { + memcpy(rangingTarget->macAddress, scanResult->bssid, + sizeof(rangingTarget->macAddress)); + rangingTarget->primaryChannel = scanResult->primaryChannel; + rangingTarget->centerFreqPrimary = scanResult->centerFreqPrimary; + rangingTarget->centerFreqSecondary = scanResult->centerFreqSecondary; + rangingTarget->channelWidth = scanResult->channelWidth; + + // Note that this is not strictly necessary (CHRE can see which API version + // the nanoapp was built against, so it knows to ignore these fields), but + // we do it here to keep things nice and tidy + memset(rangingTarget->reserved, 0, sizeof(rangingTarget->reserved)); +} + +/** + * Subscribe to a NAN service. + * + * Sends a subscription request to the NAN discovery engine with the + * specified configration parameters. If successful, a unique non-zero + * subscription ID associated with this instance of the subscription + * request is assigned by the NAN discovery engine. The subscription request + * is active until explicitly canceled, or if the connection was interrupted. + * + * Note that CHRE forwards any discovery events that it receives to the + * subscribe function instance, and does no duplicate filtering. If + * multiple events of the same discovery are undesirable, it is up to the + * platform NAN discovery engine implementation to implement redundancy + * detection mechanisms. + * + * If WiFi is turned off by the user at the Android level, an existing + * subscribe session is canceled, and a CHRE_EVENT_WIFI_ASYNC_RESULT event is + * event is sent to the subscriber. Nanoapps are expected to register for user + * settings notifications (@see chreUserSettingConfigureEvents), and + * re-establish a subscribe session on a WiFi re-enabled settings changed + * notification. + * + * @param config Service subscription configuration + * @param cookie A value that the nanoapp uses to track this particular + * subscription request. + * @return true if NAN is enabled and a subscription request was successfully + * made to the NAN engine. The actual result of the service discovery + * is sent via a CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT event. + * + * @since v1.6 + * @note Requires WiFi permission + */ +bool chreWifiNanSubscribe(struct chreWifiNanSubscribeConfig *config, + const void *cookie); + +/** + * Cancel a subscribe function instance. + * + * @param subscriptionId The ID that was originally assigned to this instance + * of the subscribe function. + * @return true if NAN is enabled, the subscribe ID was found and the instance + * successfully canceled. + * + * @since v1.6 + * @note Requires WiFi permission + */ +bool chreWifiNanSubscribeCancel(uint32_t subscriptionID); + +/** + * Request RTT ranging from a peer NAN device. + * + * Nanoapps can use this API to explicitly request measurement reports from + * the peer device. Note that both end points have to support ranging for a + * successful request. The MAC address of the peer NAN device for which ranging + * is desired may be obtained either from a NAN service discovery or from an + * out-of-band source (HAL service, BLE, etc.). + * + * If WiFi is turned off by the user at the Android level, an existing + * ranging session is canceled, and a CHRE_EVENT_WIFI_ASYNC_RESULT event is + * sent to the subscriber. Nanoapps are expected to register for user settings + * notifications (@see chreUserSettingConfigureEvents), and perform another + * ranging request on a WiFi re-enabled settings changed notification. + * + * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that + * the results of ranging will be delivered in a subsequent event of type + * CHRE_EVENT_WIFI_RANGING_RESULT. + * + * @param params Structure containing the parameters of the ranging request, + * including the MAC address of the peer NAN device to attempt ranging. + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * @return true if the request was accepted for processing, false otherwise. + */ +bool chreWifiNanRequestRangingAsync(const struct chreWifiNanRangingParams *params, + const void *cookie); + +#else /* defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD) */ +#define CHRE_WIFI_PERM_ERROR_STRING \ + "CHRE_NANOAPP_USES_WIFI must be defined when building this nanoapp in " \ + "order to refer to " +#define chreWifiConfigureScanMonitorAsync(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \ + "chreWifiConfigureScanMonitorAsync") +#define chreWifiRequestScanAsync(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \ + "chreWifiRequestScanAsync") +#define chreWifiRequestScanAsyncDefault(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \ + "chreWifiRequestScanAsyncDefault") +#define chreWifiRequestRangingAsync(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiRequestRangingAsync") +#define chreWifiRangingTargetFromScanResult(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \ + "chreWifiRangingTargetFromScanResult") +#define chreWifiNanSubscribe(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanSubscribe") +#define chreWifiNanSubscribeCancel(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanSubscribeCancel") +#define chreWifiNanRequestRangingAsync(...) \ + CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanRequestRangingAsync") +#endif /* defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_WIFI_H_ */ diff --git a/chre_api/legacy/v1_8/chre/wwan.h b/chre_api/legacy/v1_8/chre/wwan.h new file mode 100644 index 00000000..51cf5f9d --- /dev/null +++ b/chre_api/legacy/v1_8/chre/wwan.h @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _CHRE_WWAN_H_ +#define _CHRE_WWAN_H_ + +/** + * @file + * Wireless Wide Area Network (WWAN, i.e. mobile/cellular network) API relevant + * for querying cell tower identity and associated information that can be + * useful in determining location. + * + * Based on Android N RIL definitions (located at this path as of the time of + * this comment: hardware/ril/include/telephony/ril.h), version 12. Updated + * based on Android radio HAL definition (hardware/interfaces/radio) for more + * recent Android builds. Refer to those files and associated documentation for + * further details. + * + * In general, the parts of this API that are taken from the RIL follow the + * field naming conventions established in that interface rather than the CHRE + * API conventions, in order to avoid confusion and enable code re-use where + * applicable. Note that structure names include the chreWwan* prefix rather + * than RIL_*, but field names are the same. If necessary to enable code + * sharing, it is recommended to create typedefs that map from the CHRE + * structures to the associated RIL type names, for example "typedef struct + * chreWwanCellIdentityGsm RIL_CellIdentityGsm_v12", etc. + */ + +#include <chre/common.h> + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The set of flags returned by chreWwanGetCapabilities(). + * @defgroup CHRE_WWAN_CAPABILITIES + * @{ + */ + +//! No WWAN APIs are supported +#define CHRE_WWAN_CAPABILITIES_NONE UINT32_C(0) + +//! Current cell information can be queried via chreWwanGetCellInfoAsync() +#define CHRE_WWAN_GET_CELL_INFO UINT32_C(1 << 0) + +/** @} */ + +/** + * Produce an event ID in the block of IDs reserved for WWAN + * @param offset Index into WWAN event ID block; valid range [0,15] + */ +#define CHRE_WWAN_EVENT_ID(offset) (CHRE_EVENT_WWAN_FIRST_EVENT + (offset)) + +/** + * nanoappHandleEvent argument: struct chreWwanCellInfoResult + * + * Provides the result of an asynchronous request for cell info sent via + * chreWwanGetCellInfoAsync(). + */ +#define CHRE_EVENT_WWAN_CELL_INFO_RESULT CHRE_WWAN_EVENT_ID(0) + +// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved +// (see chre/event.h) + +/** + * The current version of struct chreWwanCellInfoResult associated with this + * API definition. + */ +#define CHRE_WWAN_CELL_INFO_RESULT_VERSION UINT8_C(1) + +//! Reference: RIL_CellIdentityGsm_v12 +struct chreWwanCellIdentityGsm { + //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown + int32_t mcc; + + //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown + int32_t mnc; + + //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown + int32_t lac; + + //! 16-bit GSM Cell Identity described in TS 27.007, 0..65535, + //! INT32_MAX if unknown + int32_t cid; + + //! 16-bit GSM Absolute RF channel number, INT32_MAX if unknown + int32_t arfcn; + + //! 6-bit Base Station Identity Code, UINT8_MAX if unknown + uint8_t bsic; + + //! Reserved for future use; must be set to 0 + uint8_t reserved[3]; +}; + +//! Reference: RIL_CellIdentityWcdma_v12 +struct chreWwanCellIdentityWcdma { + //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown + int32_t mcc; + + //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown + int32_t mnc; + + //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown + int32_t lac; + + //! 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, + //! INT32_MAX if unknown + int32_t cid; + + //! 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, + //! INT32_MAX if unknown + int32_t psc; + + //! 16-bit UMTS Absolute RF Channel Number, INT32_MAX if unknown + int32_t uarfcn; +}; + +//! Reference: RIL_CellIdentityCdma +struct chreWwanCellIdentityCdma { + //! Network Id 0..65535, INT32_MAX if unknown + int32_t networkId; + + //! CDMA System Id 0..32767, INT32_MAX if unknown + int32_t systemId; + + //! Base Station Id 0..65535, INT32_MAX if unknown + int32_t basestationId; + + //! Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. + //! It is represented in units of 0.25 seconds and ranges from -2592000 + //! to 2592000, both values inclusive (corresponding to a range of -180 + //! to +180 degrees). INT32_MAX if unknown + int32_t longitude; + + //! Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. + //! It is represented in units of 0.25 seconds and ranges from -1296000 + //! to 1296000, both values inclusive (corresponding to a range of -90 + //! to +90 degrees). INT32_MAX if unknown + int32_t latitude; +}; + +//! Reference: RIL_CellIdentityLte_v12 +struct chreWwanCellIdentityLte { + //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown + int32_t mcc; + + //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown + int32_t mnc; + + //! 28-bit Cell Identity described in TS ???, INT32_MAX if unknown + int32_t ci; + + //! physical cell id 0..503, INT32_MAX if unknown + int32_t pci; + + //! 16-bit tracking area code, INT32_MAX if unknown + int32_t tac; + + //! 18-bit LTE Absolute RF Channel Number, INT32_MAX if unknown + int32_t earfcn; +}; + +//! Reference: RIL_CellIdentityTdscdma +struct chreWwanCellIdentityTdscdma { + //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown + int32_t mcc; + + //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown + int32_t mnc; + + //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown + int32_t lac; + + //! 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, + //! INT32_MAX if unknown + int32_t cid; + + //! 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT32_MAX if + //! unknown + int32_t cpid; +}; + +//! Reference: android.hardware.radio@1.4 CellIdentityNr +//! @since v1.4 +struct chreWwanCellIdentityNr { + //! 3-digit Mobile Country Code, in range [0, 999]. This value must be valid + //! for registered or camped cells. INT32_MAX means invalid/unreported. + int32_t mcc; + + //! 2 or 3-digit Mobile Network Code, in range [0, 999]. This value must be + //! valid for registered or camped cells. INT32_MAX means + //! invalid/unreported. + int32_t mnc; + + //! NR Cell Identity in range [0, 68719476735] (36 bits), which + //! unambiguously identifies a cell within a public land mobile network + //! (PLMN). This value must be valid for registered or camped cells. + //! Reference: TS 38.413 section 9.3.1.7. + //! + //! Note: for backward compatibility reasons, the nominally int64_t nci is + //! split into two uint32_t values, with nci0 being the least significant 4 + //! bytes. If chreWwanUnpackNrNci returns INT64_MAX, it means nci is + //! invalid/unreported. + //! + //! Users are recommended to use the helper accessor chreWwanUnpackNrNci to + //! access the nci field. + //! + //! @see chreWwanUnpackNrNci + uint32_t nci0; + uint32_t nci1; + + //! Physical cell id in range [0, 1007]. This value must be valid. + //! Reference: TS 38.331 section 6.3.2. + int32_t pci; + + //! 24-bit tracking area code in range [0, 16777215]. INT32_MAX means + //! invalid/unreported. + //! Reference: TS 38.413 section 9.3.3.10 and TS 29.571 section 5.4.2. + int32_t tac; + + //! NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. This + //! value must be valid. + //! Reference: TS 38.101-1 section 5.4.2.1 and TS 38.101-2 section 5.4.2.1. + int32_t nrarfcn; +}; + +//! Reference: RIL_GSM_SignalStrength_v12 +struct chreWwanSignalStrengthGsm { + //! Valid values are (0-31, 99) as defined in TS 27.007 8.5 + //! INT32_MAX means invalid/unreported. + int32_t signalStrength; + + //! bit error rate (0-7, 99) as defined in TS 27.007 8.5 + //! INT32_MAX means invalid/unreported. + int32_t bitErrorRate; + + //! Timing Advance in bit periods. 1 bit period = 48.13 us. + //! INT32_MAX means invalid/unreported. + int32_t timingAdvance; +}; + +//! Reference: RIL_SignalStrengthWcdma +struct chreWwanSignalStrengthWcdma { + //! Valid values are (0-31, 99) as defined in TS 27.007 8.5 + //! INT32_MAX means invalid/unreported. + int32_t signalStrength; + + //! bit error rate (0-7, 99) as defined in TS 27.007 8.5 + //! INT32_MAX means invalid/unreported. + int32_t bitErrorRate; +}; + +//! Reference: RIL_CDMA_SignalStrength +struct chreWwanSignalStrengthCdma { + //! Valid values are positive integers. This value is the actual RSSI value + //! multiplied by -1. Example: If the actual RSSI is -75, then this + //! response value will be 75. + //! INT32_MAX means invalid/unreported. + int32_t dbm; + + //! Valid values are positive integers. This value is the actual Ec/Io + //! multiplied by -10. Example: If the actual Ec/Io is -12.5 dB, then this + //! response value will be 125. + //! INT32_MAX means invalid/unreported. + int32_t ecio; +}; + +//! Reference: RIL_EVDO_SignalStrength +struct chreWwanSignalStrengthEvdo { + //! Valid values are positive integers. This value is the actual RSSI value + //! multiplied by -1. Example: If the actual RSSI is -75, then this + //! response value will be 75. + //! INT32_MAX means invalid/unreported. + int32_t dbm; + + //! Valid values are positive integers. This value is the actual Ec/Io + //! multiplied by -10. Example: If the actual Ec/Io is -12.5 dB, then this + //! response value will be 125. + //! INT32_MAX means invalid/unreported. + int32_t ecio; + + //! Valid values are 0-8. 8 is the highest signal to noise ratio. + //! INT32_MAX means invalid/unreported. + int32_t signalNoiseRatio; +}; + +//! Reference: RIL_LTE_SignalStrength_v8 +struct chreWwanSignalStrengthLte { + //! Valid values are (0-31, 99) as defined in TS 27.007 8.5 + int32_t signalStrength; + + //! The current Reference Signal Receive Power in dBm multiplied by -1. + //! Range: 44 to 140 dBm + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP TS 36.133 9.1.4 + int32_t rsrp; + + //! The current Reference Signal Receive Quality in dB multiplied by -1. + //! Range: 3 to 20 dB. + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP TS 36.133 9.1.7 + int32_t rsrq; + + //! The current reference signal signal-to-noise ratio in 0.1 dB units. + //! Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB). + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP TS 36.101 8.1.1 + int32_t rssnr; + + //! The current Channel Quality Indicator. + //! Range: 0 to 15. + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP TS 36.101 9.2, 9.3, A.4 + int32_t cqi; + + //! timing advance in micro seconds for a one way trip from cell to device. + //! Approximate distance can be calculated using 300m/us * timingAdvance. + //! Range: 0 to 0x7FFFFFFE + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP 36.321 section 6.1.3.5 + //! also: http://www.cellular-planningoptimization.com/2010/02/timing-advance-with-calculation.html + int32_t timingAdvance; +}; + +//! Reference: RIL_TD_SCDMA_SignalStrength +struct chreWwanSignalStrengthTdscdma { + //! The Received Signal Code Power in dBm multiplied by -1. + //! Range : 25 to 120 + //! INT32_MAX means invalid/unreported. + //! Reference: 3GPP TS 25.123, section 9.1.1.1 + int32_t rscp; +}; + +//! Reference: android.hardware.radio@1.4 NrSignalStrength +//! @since v1.4 +struct chreWwanSignalStrengthNr { + //! SS (second synchronization) reference signal received power in dBm + //! multiplied by -1. + //! Range [44, 140], INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.1 and TS 38.133 section 10.1.6. + int32_t ssRsrp; + + //! SS reference signal received quality in 0.5 dB units. + //! Range [-86, 41] with -86 = -43.0 dB and 41 = 20.5 dB. + //! INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.3 and TS 38.133 section 10.1.11.1. + int32_t ssRsrq; + + //! SS signal-to-noise and interference ratio in 0.5 dB units. + //! Range [-46, 81] with -46 = -23.0 dB and 81 = 40.5 dB. + //! INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.5 and TS 38.133 section 10.1.16.1. + int32_t ssSinr; + + //! CSI reference signal received power in dBm multiplied by -1. + //! Range [44, 140], INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.2 and TS 38.133 section 10.1.6. + int32_t csiRsrp; + + //! CSI reference signal received quality in 0.5 dB units. + //! Range [-86, 41] with -86 = -43.0 dB and 41 = 20.5 dB. + //! INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.4 and TS 38.133 section 10.1.11.1. + int32_t csiRsrq; + + //! CSI signal-to-noise and interference ratio in 0.5 dB units. + //! Range [-46, 81] with -46 = -23.0 dB and 81 = 40.5 dB. + //! INT32_MAX means invalid/unreported. + //! Reference: TS 38.215 section 5.1.6 and TS 38.133 section 10.1.16.1. + int32_t csiSinr; +}; + +//! Reference: RIL_CellInfoGsm_v12 +struct chreWwanCellInfoGsm { + struct chreWwanCellIdentityGsm cellIdentityGsm; + struct chreWwanSignalStrengthGsm signalStrengthGsm; +}; + +//! Reference: RIL_CellInfoWcdma_v12 +struct chreWwanCellInfoWcdma { + struct chreWwanCellIdentityWcdma cellIdentityWcdma; + struct chreWwanSignalStrengthWcdma signalStrengthWcdma; +}; + +//! Reference: RIL_CellInfoCdma +struct chreWwanCellInfoCdma { + struct chreWwanCellIdentityCdma cellIdentityCdma; + struct chreWwanSignalStrengthCdma signalStrengthCdma; + struct chreWwanSignalStrengthEvdo signalStrengthEvdo; +}; + +//! Reference: RIL_CellInfoLte_v12 +struct chreWwanCellInfoLte { + struct chreWwanCellIdentityLte cellIdentityLte; + struct chreWwanSignalStrengthLte signalStrengthLte; +}; + +//! Reference: RIL_CellInfoTdscdma +struct chreWwanCellInfoTdscdma { + struct chreWwanCellIdentityTdscdma cellIdentityTdscdma; + struct chreWwanSignalStrengthTdscdma signalStrengthTdscdma; +}; + +//! Reference: android.hardware.radio@1.4 CellInfoNr +//! @since v1.4 +struct chreWwanCellInfoNr { + struct chreWwanCellIdentityNr cellIdentityNr; + struct chreWwanSignalStrengthNr signalStrengthNr; +}; + +//! Reference: RIL_CellInfoType +//! All other values are reserved and should be ignored by nanoapps. +enum chreWwanCellInfoType { + CHRE_WWAN_CELL_INFO_TYPE_GSM = 1, + CHRE_WWAN_CELL_INFO_TYPE_CDMA = 2, + CHRE_WWAN_CELL_INFO_TYPE_LTE = 3, + CHRE_WWAN_CELL_INFO_TYPE_WCDMA = 4, + CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA = 5, + CHRE_WWAN_CELL_INFO_TYPE_NR = 6, //! @since v1.4 +}; + +//! Reference: RIL_TimeStampType +enum chreWwanCellTimeStampType { + CHRE_WWAN_CELL_TIMESTAMP_TYPE_UNKNOWN = 0, + CHRE_WWAN_CELL_TIMESTAMP_TYPE_ANTENNA = 1, + CHRE_WWAN_CELL_TIMESTAMP_TYPE_MODEM = 2, + CHRE_WWAN_CELL_TIMESTAMP_TYPE_OEM_RIL = 3, + CHRE_WWAN_CELL_TIMESTAMP_TYPE_JAVA_RIL = 4, +}; + +//! Reference: RIL_CellInfo_v12 +struct chreWwanCellInfo { + //! Timestamp in nanoseconds; must be in the same time base as chreGetTime() + uint64_t timeStamp; + + //! A value from enum {@link #CellInfoType} indicating the radio access + //! technology of the cell, and which field in union CellInfo can be used + //! to retrieve additional information + uint8_t cellInfoType; + + //! A value from enum {@link #CellTimeStampType} that identifies the source + //! of the value in timeStamp. This is typically set to + //! CHRE_WWAN_CELL_TIMESTAMP_TYPE_OEM_RIL, and indicates the time given by + //! chreGetTime() that an intermediate module received the data from the + //! modem and forwarded it to the requesting CHRE client. + uint8_t timeStampType; + + //! !0 if this cell is registered, 0 if not registered + uint8_t registered; + + //! Reserved for future use; must be set to 0 + uint8_t reserved; + + //! The value in cellInfoType indicates which field in this union is valid + union chreWwanCellInfoPerRat { + struct chreWwanCellInfoGsm gsm; + struct chreWwanCellInfoCdma cdma; + struct chreWwanCellInfoLte lte; + struct chreWwanCellInfoWcdma wcdma; + struct chreWwanCellInfoTdscdma tdscdma; + struct chreWwanCellInfoNr nr; //! @since v1.4 + } CellInfo; +}; + +/** + * Data structure provided with events of type CHRE_EVENT_WWAN_CELL_INFO_RESULT. + */ +struct chreWwanCellInfoResult { + //! Indicates the version of the structure, for compatibility purposes. + //! Clients do not normally need to worry about this field; the CHRE + //! implementation guarantees that the client only receives the structure + //! version it expects. + uint8_t version; + + //! Populated with a value from enum {@link #chreError}, indicating whether + //! the request failed, and if so, provides the cause of the failure + uint8_t errorCode; + + //! The number of valid entries in cells[] + uint8_t cellInfoCount; + + //! Reserved for future use; must be set to 0 + uint8_t reserved; + + //! Set to the cookie parameter given to chreWwanGetCellInfoAsync() + const void *cookie; + + //! Pointer to an array of cellInfoCount elements containing information + //! about serving and neighbor cells + const struct chreWwanCellInfo *cells; +}; + + +/** + * Retrieves a set of flags indicating the WWAN features supported by the + * current CHRE implementation. The value returned by this function must be + * consistent for the entire duration of the Nanoapp's execution. + * + * The client must allow for more flags to be set in this response than it knows + * about, for example if the implementation supports a newer version of the API + * than the client was compiled against. + * + * @return A bitmask with zero or more CHRE_WWAN_CAPABILITIES_* flags set + * + * @since v1.1 + */ +uint32_t chreWwanGetCapabilities(void); + +/** + * Nanoapps must define CHRE_NANOAPP_USES_WWAN somewhere in their build + * system (e.g. Makefile) if the nanoapp needs to use the following WWAN APIs. + * In addition to allowing access to these APIs, defining this macro will also + * ensure CHRE enforces that all host clients this nanoapp talks to have the + * required Android permissions needed to listen to WWAN data by adding metadata + * to the nanoapp. + */ +#if defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD) + +/** + * Query information about the current serving cell and its neighbors. This does + * not perform a network scan, but should return state from the current network + * registration data stored in the cellular modem. This is effectively the same + * as a request for RIL_REQUEST_GET_CELL_INFO_LIST in the RIL. + * + * The requested cellular information is returned asynchronously via + * CHRE_EVENT_WWAN_CELL_INFO_RESULT. The implementation must send this event, + * either with successful data or an error status, within + * CHRE_ASYNC_RESULT_TIMEOUT_NS. + * + * If the airplane mode setting is enabled at the Android level, the CHRE + * implementation is expected to return a successful asynchronous result with an + * empty cell info list. + * + * @param cookie An opaque value that will be included in the chreAsyncResult + * sent in relation to this request. + * + * @return true if the request was accepted for processing, false otherwise + * + * @since v1.1 + * @note Requires WWAN permission + */ +bool chreWwanGetCellInfoAsync(const void *cookie); + +/** + * Helper accessor for nci in the chreWwanCellIdentityNr struct. + * + * @return nci or INT64_MAX if invalid/unreported. + * + * @see chreWwanCellIdentityNr + * + * @since v1.4 + * @note Requires WWAN permission + */ +static inline int64_t chreWwanUnpackNrNci( + const struct chreWwanCellIdentityNr *nrCellId) { + return (int64_t) (((uint64_t) nrCellId->nci1 << 32) | nrCellId->nci0); +} + +#else /* defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD) */ +#define CHRE_WWAN_PERM_ERROR_STRING \ + "CHRE_NANOAPP_USES_WWAN must be defined when building this nanoapp in " \ + "order to refer to " +#define chreWwanGetCellInfoAsync(...) \ + CHRE_BUILD_ERROR(CHRE_WWAN_PERM_ERROR_STRING "chreWwanGetCellInfoAsync") +#define chreWwanUnpackNrNci(...) \ + CHRE_BUILD_ERROR(CHRE_WWAN_PERM_ERROR_STRING "chreWwanUnpackNrNci") +#endif /* defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CHRE_WWAN_H_ */ diff --git a/chre_daemon.rc b/chre_daemon.rc index 120da10d..58bf3d98 100644 --- a/chre_daemon.rc +++ b/chre_daemon.rc @@ -14,6 +14,9 @@ # limitations under the License. # +on post-fs-data + mkdir /data/vendor/chre 0770 context_hub context_hub + service vendor.chre /vendor/bin/chre class late_start user context_hub diff --git a/chre_flags.aconfig b/chre_flags.aconfig new file mode 100644 index 00000000..70c6586e --- /dev/null +++ b/chre_flags.aconfig @@ -0,0 +1,36 @@ +package: "android.chre.flags" + +flag { + name: "flag_log_nanoapp_load_metrics" + namespace: "context_hub" + description: "This flag controls nanoapp load failure logging in the HAL and the addition of MetricsReporter" + bug: "298459533" +} + +flag { + name: "metrics_reporter_in_the_daemon" + namespace: "context_hub" + description: "This flag controls the addition of MetricsReporter into the CHRE daemon" + bug: "298459533" +} + +flag { + name: "wait_for_preloaded_nanoapp_start" + namespace: "context_hub" + description: "This flag controls the waiting-for-nanoapp-start behavior in the CHRE daemon" + bug: "298459533" +} + +flag { + name: "remove_ap_wakeup_metric_report_limit" + namespace: "context_hub" + description: "This flag controls removing a count limit on reporting the AP wakeup metric" + bug: "298459533" +} + +flag { + name: "context_hub_callback_uuid_enabled" + namespace: "context_hub" + description: "Call IContextHubCallback.getUuid() to retrieve the UUID when this flag is on" + bug: "247124878" +} diff --git a/core/ble_request.cc b/core/ble_request.cc index 7924349a..f097c0c1 100644 --- a/core/ble_request.cc +++ b/core/ble_request.cc @@ -32,31 +32,49 @@ bool filtersMatch(const chreBleGenericFilter &filter, (memcmp(filter.dataMask, otherFilter.dataMask, filter.len) == 0); } +bool broadcasterFiltersMatch( + const chreBleBroadcasterAddressFilter &filter, + const chreBleBroadcasterAddressFilter &otherFilter) { + return (memcmp(filter.broadcasterAddress, otherFilter.broadcasterAddress, + sizeof(filter.broadcasterAddress)) == 0); +} + } // namespace -BleRequest::BleRequest() : BleRequest(0, false) {} +BleRequest::BleRequest() + : BleRequest(0 /* instanceId */, false /* enable */, nullptr /* cookie */) { +} -BleRequest::BleRequest(uint16_t instanceId, bool enable) +BleRequest::BleRequest(uint16_t instanceId, bool enable, const void *cookie) : BleRequest(instanceId, enable, CHRE_BLE_SCAN_MODE_BACKGROUND, - 0 /* reportDelayMs */, nullptr /* filter */) {} + 0 /* reportDelayMs */, nullptr /* filter */, cookie) {} BleRequest::BleRequest(uint16_t instanceId, bool enable, chreBleScanMode mode, - uint32_t reportDelayMs, const chreBleScanFilter *filter) + uint32_t reportDelayMs, + const chreBleScanFilterV1_9 *filter, const void *cookie) : mReportDelayMs(reportDelayMs), mInstanceId(instanceId), mMode(mode), mEnabled(enable), mRssiThreshold(CHRE_BLE_RSSI_THRESHOLD_NONE), - mStatus(RequestStatus::PENDING_REQ) { + mStatus(RequestStatus::PENDING_REQ), + mCookie(cookie) { if (filter != nullptr) { mRssiThreshold = filter->rssiThreshold; - if (filter->scanFilterCount > 0) { - if (!mFilters.resize(filter->scanFilterCount)) { + if (filter->genericFilterCount > 0) { + if (!mGenericFilters.resize(filter->genericFilterCount)) { FATAL_ERROR("Unable to reserve filter count"); } - for (size_t i = 0; i < filter->scanFilterCount; i++) { - mFilters[i] = filter->scanFilters[i]; + memcpy(mGenericFilters.data(), filter->genericFilters, + sizeof(chreBleGenericFilter) * filter->genericFilterCount); + } + if (filter->broadcasterAddressFilterCount > 0) { + if (!mBroadcasterFilters.resize(filter->broadcasterAddressFilterCount)) { + FATAL_ERROR("Unable to reserve broadcaster address filter count"); } + memcpy(mBroadcasterFilters.data(), filter->broadcasterAddressFilters, + sizeof(chreBleBroadcasterAddressFilter) * + filter->broadcasterAddressFilterCount); } } } @@ -70,9 +88,11 @@ BleRequest &BleRequest::operator=(BleRequest &&other) { mMode = other.mMode; mReportDelayMs = other.mReportDelayMs; mRssiThreshold = other.mRssiThreshold; - mFilters = std::move(other.mFilters); + mGenericFilters = std::move(other.mGenericFilters); + mBroadcasterFilters = std::move(other.mBroadcasterFilters); mEnabled = other.mEnabled; mStatus = other.mStatus; + mCookie = other.mCookie; return *this; } @@ -103,10 +123,11 @@ bool BleRequest::mergeWith(const BleRequest &request) { attributesChanged = true; } } - const DynamicVector<chreBleGenericFilter> &otherFilters = request.mFilters; + const DynamicVector<chreBleGenericFilter> &otherFilters = + request.mGenericFilters; for (const chreBleGenericFilter &otherFilter : otherFilters) { bool addFilter = true; - for (const chreBleGenericFilter &filter : mFilters) { + for (const chreBleGenericFilter &filter : mGenericFilters) { if (filtersMatch(filter, otherFilter)) { addFilter = false; break; @@ -114,7 +135,25 @@ bool BleRequest::mergeWith(const BleRequest &request) { } if (addFilter) { attributesChanged = true; - if (!mFilters.push_back(otherFilter)) { + if (!mGenericFilters.push_back(otherFilter)) { + FATAL_ERROR("Unable to merge filters"); + } + } + } + const DynamicVector<chreBleBroadcasterAddressFilter> + &otherBroadcasterFilters = request.mBroadcasterFilters; + for (const chreBleBroadcasterAddressFilter &otherFilter : + otherBroadcasterFilters) { + bool addFilter = true; + for (const chreBleBroadcasterAddressFilter &filter : mBroadcasterFilters) { + if (broadcasterFiltersMatch(filter, otherFilter)) { + addFilter = false; + break; + } + } + if (addFilter) { + attributesChanged = true; + if (!mBroadcasterFilters.push_back(otherFilter)) { FATAL_ERROR("Unable to merge filters"); } } @@ -123,14 +162,26 @@ bool BleRequest::mergeWith(const BleRequest &request) { } bool BleRequest::isEquivalentTo(const BleRequest &request) { - const DynamicVector<chreBleGenericFilter> &otherFilters = request.mFilters; - bool isEquivalent = (mEnabled && request.mEnabled && mMode == request.mMode && - mReportDelayMs == request.mReportDelayMs && - mRssiThreshold == request.mRssiThreshold && - mFilters.size() == otherFilters.size()); + const DynamicVector<chreBleGenericFilter> &otherFilters = + request.mGenericFilters; + const DynamicVector<chreBleBroadcasterAddressFilter> + &otherBroadcasterFilters = request.mBroadcasterFilters; + bool isEquivalent = + (mEnabled && request.mEnabled && mMode == request.mMode && + mReportDelayMs == request.mReportDelayMs && + mRssiThreshold == request.mRssiThreshold && + mGenericFilters.size() == otherFilters.size() && + mBroadcasterFilters.size() == otherBroadcasterFilters.size()); if (isEquivalent) { for (size_t i = 0; i < otherFilters.size(); i++) { - if (!filtersMatch(mFilters[i], otherFilters[i])) { + if (!filtersMatch(mGenericFilters[i], otherFilters[i])) { + isEquivalent = false; + break; + } + } + for (size_t i = 0; i < otherBroadcasterFilters.size(); i++) { + if (!broadcasterFiltersMatch(mBroadcasterFilters[i], + otherBroadcasterFilters[i])) { isEquivalent = false; break; } @@ -165,18 +216,29 @@ void BleRequest::setRequestStatus(RequestStatus status) { const DynamicVector<chreBleGenericFilter> &BleRequest::getGenericFilters() const { - return mFilters; + return mGenericFilters; } -chreBleScanFilter BleRequest::getScanFilter() const { - return chreBleScanFilter{ - mRssiThreshold, static_cast<uint8_t>(mFilters.size()), mFilters.data()}; +const DynamicVector<chreBleBroadcasterAddressFilter> & +BleRequest::getBroadcasterFilters() const { + return mBroadcasterFilters; +} + +chreBleScanFilterV1_9 BleRequest::getScanFilter() const { + return chreBleScanFilterV1_9{ + mRssiThreshold, static_cast<uint8_t>(mGenericFilters.size()), + mGenericFilters.data(), static_cast<uint8_t>(mBroadcasterFilters.size()), + mBroadcasterFilters.data()}; } bool BleRequest::isEnabled() const { return mEnabled; } +const void *BleRequest::getCookie() const { + return mCookie; +} + void BleRequest::logStateToBuffer(DebugDumpWrapper &debugDump, bool isPlatformRequest) const { if (!isPlatformRequest) { @@ -189,8 +251,8 @@ void BleRequest::logStateToBuffer(DebugDumpWrapper &debugDump, " mode=%" PRIu8 " reportDelayMs=%" PRIu32 " rssiThreshold=%" PRId8, static_cast<uint8_t>(mMode), mReportDelayMs, mRssiThreshold); if (isPlatformRequest) { - debugDump.print(" filters=["); - for (const chreBleGenericFilter &filter : mFilters) { + debugDump.print(" genericFilters=["); + for (const chreBleGenericFilter &filter : mGenericFilters) { debugDump.print("(type=%" PRIx8, filter.type); if (filter.len > 0) { debugDump.print(" data=%s dataMask=%s len=%" PRIu8 "), ", @@ -200,9 +262,21 @@ void BleRequest::logStateToBuffer(DebugDumpWrapper &debugDump, } } debugDump.print("]\n"); + debugDump.print(" broadcasterAddressFilters=["); + for (const chreBleBroadcasterAddressFilter &filter : + mBroadcasterFilters) { + debugDump.print( + "(address=%02X:%02X:%02X:%02X:%02X:%02X), ", + filter.broadcasterAddress[5], filter.broadcasterAddress[4], + filter.broadcasterAddress[3], filter.broadcasterAddress[2], + filter.broadcasterAddress[1], filter.broadcasterAddress[0]); + } + debugDump.print("]\n"); } else { - debugDump.print(" filterCount=%" PRIu8 "\n", - static_cast<uint8_t>(mFilters.size())); + debugDump.print(" genericFilterCount=%" PRIu8 + " broadcasterFilterCount=%" PRIu8 "\n", + static_cast<uint8_t>(mGenericFilters.size()), + static_cast<uint8_t>(mBroadcasterFilters.size())); } } } diff --git a/core/ble_request_manager.cc b/core/ble_request_manager.cc index 469b357a..0b1237f6 100644 --- a/core/ble_request_manager.cc +++ b/core/ble_request_manager.cc @@ -19,7 +19,9 @@ #include "chre/core/event_loop_manager.h" #include "chre/platform/fatal_error.h" #include "chre/platform/log.h" +#include "chre/util/fixed_size_vector.h" #include "chre/util/nested_data_ptr.h" +#include "chre/util/system/ble_util.h" #include "chre/util/system/event_callbacks.h" namespace chre { @@ -46,19 +48,20 @@ void BleRequestManager::handleExistingRequest(uint16_t instanceId, foundRequest->getRequestStatus() != RequestStatus::APPLIED) { handleAsyncResult(instanceId, foundRequest->isEnabled(), false /* success */, CHRE_ERROR_OBSOLETE_REQUEST, - true /* forceUnregister */); + foundRequest->getCookie(), true /* forceUnregister */); } } bool BleRequestManager::compliesWithBleSetting(uint16_t instanceId, bool enabled, bool hasExistingRequest, - size_t requestIndex) { + size_t requestIndex, + const void *cookie) { bool success = true; if (enabled && !bleSettingEnabled()) { success = false; handleAsyncResult(instanceId, enabled, false /* success */, - CHRE_ERROR_FUNCTION_DISABLED); + CHRE_ERROR_FUNCTION_DISABLED, cookie); if (hasExistingRequest) { bool requestChanged = false; mRequests.removeRequest(requestIndex, &requestChanged); @@ -85,18 +88,18 @@ bool BleRequestManager::updateRequests(BleRequest &&request, return success; } -bool BleRequestManager::startScanAsync(Nanoapp *nanoapp, chreBleScanMode mode, - uint32_t reportDelayMs, - const struct chreBleScanFilter *filter) { +bool BleRequestManager::startScanAsync( + Nanoapp *nanoapp, chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilterV1_9 *filter, const void *cookie) { CHRE_ASSERT(nanoapp); - BleRequest request(nanoapp->getInstanceId(), true, mode, reportDelayMs, - filter); + BleRequest request(nanoapp->getInstanceId(), true /* enable */, mode, + reportDelayMs, filter, cookie); return configure(std::move(request)); } -bool BleRequestManager::stopScanAsync(Nanoapp *nanoapp) { +bool BleRequestManager::stopScanAsync(Nanoapp *nanoapp, const void *cookie) { CHRE_ASSERT(nanoapp); - BleRequest request(nanoapp->getInstanceId(), false /* enable */); + BleRequest request(nanoapp->getInstanceId(), false /* enable */, cookie); return configure(std::move(request)); } @@ -112,7 +115,8 @@ uint32_t BleRequestManager::disableActiveScan(const Nanoapp *nanoapp) { return 0; } - BleRequest request(nanoapp->getInstanceId(), false /* enable */); + BleRequest request(nanoapp->getInstanceId(), false /* enable */, + nullptr /* cookie */); configure(std::move(request)); return 1; } @@ -141,6 +145,32 @@ bool BleRequestManager::readRssiAsync(Nanoapp *nanoapp, } #endif +bool BleRequestManager::flushAsync(Nanoapp *nanoapp, const void *cookie) { + CHRE_ASSERT(nanoapp); + + bool supportsFlush = + getCapabilities() & CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING; + if (!supportsFlush) { + return false; + } + + bool success = false; + const BleRequest *foundRequest = + mRequests.findRequest(nanoapp->getInstanceId(), nullptr); + if (foundRequest == nullptr) { + LOGE("Nanoapp with instance ID: %" PRIu16 + " does not have an existing BLE request and cannot flush", + nanoapp->getInstanceId()); + } else if (mFlushRequestQueue.full()) { + LOG_OOM(); + } else { + mFlushRequestQueue.emplace(nanoapp->getInstanceId(), cookie); + success = processFlushRequests(); + } + + return success; +} + void BleRequestManager::addBleRequestLog(uint32_t instanceId, bool enabled, size_t requestIndex, bool compliesWithBleSetting) { @@ -165,8 +195,9 @@ bool BleRequestManager::configure(BleRequest &&request) { uint16_t instanceId = request.getInstanceId(); uint8_t enabled = request.isEnabled(); handleExistingRequest(instanceId, &hasExistingRequest, &requestIndex); - bool compliant = compliesWithBleSetting(instanceId, enabled, - hasExistingRequest, requestIndex); + bool compliant = + compliesWithBleSetting(instanceId, enabled, hasExistingRequest, + requestIndex, request.getCookie()); if (compliant) { success = updateRequests(std::move(request), hasExistingRequest, &requestChanged, &requestIndex); @@ -174,7 +205,7 @@ bool BleRequestManager::configure(BleRequest &&request) { if (!mPlatformRequestInProgress) { if (!requestChanged) { handleAsyncResult(instanceId, enabled, true /* success */, - CHRE_ERROR_NONE); + CHRE_ERROR_NONE, request.getCookie()); if (requestIndex < mRequests.getRequests().size()) { mRequests.getMutableRequests()[requestIndex].setRequestStatus( RequestStatus::APPLIED); @@ -202,17 +233,20 @@ bool BleRequestManager::controlPlatform() { bool success = false; const BleRequest &maxRequest = mRequests.getCurrentMaximalRequest(); bool enable = bleSettingEnabled() && maxRequest.isEnabled(); + if (enable) { - chreBleScanFilter filter = maxRequest.getScanFilter(); + chreBleScanFilterV1_9 filter = maxRequest.getScanFilter(); success = mPlatformBle.startScanAsync( maxRequest.getMode(), maxRequest.getReportDelayMs(), &filter); - mPendingPlatformRequest = - BleRequest(0, enable, maxRequest.getMode(), - maxRequest.getReportDelayMs(), &filter); + mPendingPlatformRequest = BleRequest( + 0 /* instanceId */, enable, maxRequest.getMode(), + maxRequest.getReportDelayMs(), &filter, nullptr /* cookie */); } else { success = mPlatformBle.stopScanAsync(); - mPendingPlatformRequest = BleRequest(0, enable); + mPendingPlatformRequest = + BleRequest(0 /* instanceId */, enable, nullptr /* cookie */); } + if (success) { for (BleRequest &req : mRequests.getMutableRequests()) { if (req.getRequestStatus() == RequestStatus::PENDING_REQ) { @@ -240,17 +274,21 @@ void BleRequestManager::freeAdvertisingEventCallback(uint16_t /* eventType */, void BleRequestManager::handleAdvertisementEvent( struct chreBleAdvertisementEvent *event) { + for (uint16_t i = 0; i < event->numReports; i++) { + populateLegacyAdvertisingReportFields( + const_cast<chreBleAdvertisingReport &>(event->reports[i])); + } EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_BLE_ADVERTISEMENT, event, freeAdvertisingEventCallback); } void BleRequestManager::handlePlatformChange(bool enable, uint8_t errorCode) { auto callback = [](uint16_t /*type*/, void *data, void *extraData) { - bool enable = NestedDataPtr<bool>(data); - uint8_t errorCode = NestedDataPtr<uint8_t>(extraData); + bool enableCb = NestedDataPtr<bool>(data); + uint8_t errorCodeCb = NestedDataPtr<uint8_t>(extraData); EventLoopManagerSingleton::get() ->getBleRequestManager() - .handlePlatformChangeSync(enable, errorCode); + .handlePlatformChangeSync(enableCb, errorCodeCb); }; EventLoopManagerSingleton::get()->deferCallback( @@ -272,7 +310,7 @@ void BleRequestManager::handlePlatformChangeSync(bool enable, for (BleRequest &req : mRequests.getMutableRequests()) { if (req.getRequestStatus() == RequestStatus::PENDING_RESP) { handleAsyncResult(req.getInstanceId(), req.isEnabled(), success, - errorCode); + errorCode, req.getCookie()); if (success) { req.setRequestStatus(RequestStatus::APPLIED); } @@ -288,56 +326,46 @@ void BleRequestManager::handlePlatformChangeSync(bool enable, mActivePlatformRequest = std::move(mPendingPlatformRequest); } - dispatchPendingRequests(); - - // Only clear mResyncPending if the request succeeded or after all pending - // requests are dispatched and a resync request can be issued with only the - // requests that were previously applied. - if (mResyncPending) { - if (success) { - mResyncPending = false; - } else if (!success && !mPlatformRequestInProgress) { - mResyncPending = false; - updatePlatformRequest(true /* forceUpdate */); - } + if (mRequests.hasRequests(RequestStatus::PENDING_REQ)) { + dispatchPendingRequests(); + } else if (!success && mResyncPending) { + updatePlatformRequest(true /* forceUpdate */); } - // Finish dispatching pending requests before processing the setting change - // request to ensure nanoapps receive CHRE_ERROR_FUNCTION_DISABLED responses. - // If both a resync and a setting change are pending, prioritize the resync. - // If the resync successfully completes, the PAL will be in the correct state - // and updatePlatformRequest will not begin a new request. - if (mSettingChangePending && !mPlatformRequestInProgress) { + + if (!mPlatformRequestInProgress && mSettingChangePending) { updatePlatformRequest(); - mSettingChangePending = false; } + + mResyncPending = false; + mSettingChangePending = false; } void BleRequestManager::dispatchPendingRequests() { - if (mRequests.hasRequests(RequestStatus::PENDING_REQ)) { - uint8_t errorCode = CHRE_ERROR_NONE; - if (!bleSettingEnabled() && mRequests.isMaximalRequestEnabled()) { - errorCode = CHRE_ERROR_FUNCTION_DISABLED; - } else if (!controlPlatform()) { - errorCode = CHRE_ERROR; - } - if (errorCode != CHRE_ERROR_NONE) { - for (const BleRequest &req : mRequests.getRequests()) { - if (req.getRequestStatus() == RequestStatus::PENDING_REQ) { - handleAsyncResult(req.getInstanceId(), req.isEnabled(), - false /* success */, errorCode); - } + uint8_t errorCode = CHRE_ERROR_NONE; + if (!bleSettingEnabled() && mRequests.isMaximalRequestEnabled()) { + errorCode = CHRE_ERROR_FUNCTION_DISABLED; + } else if (!controlPlatform()) { + errorCode = CHRE_ERROR; + } + if (errorCode != CHRE_ERROR_NONE) { + for (const BleRequest &req : mRequests.getRequests()) { + if (req.getRequestStatus() == RequestStatus::PENDING_REQ) { + handleAsyncResult(req.getInstanceId(), req.isEnabled(), + false /* success */, errorCode, req.getCookie()); } - mRequests.removeRequests(RequestStatus::PENDING_REQ); } + mRequests.removeRequests(RequestStatus::PENDING_REQ); } } void BleRequestManager::handleAsyncResult(uint16_t instanceId, bool enabled, bool success, uint8_t errorCode, + const void *cookie, bool forceUnregister) { uint8_t requestType = enabled ? CHRE_BLE_REQUEST_TYPE_START_SCAN : CHRE_BLE_REQUEST_TYPE_STOP_SCAN; - postAsyncResultEventFatal(instanceId, requestType, success, errorCode); + postAsyncResultEventFatal(instanceId, requestType, success, errorCode, + cookie); handleNanoappEventRegistration(instanceId, enabled, success, forceUnregister); } @@ -464,6 +492,21 @@ uint8_t BleRequestManager::readRssi(uint16_t connectionHandle) { } #endif +void BleRequestManager::handleFlushComplete(uint8_t errorCode) { + if (mFlushRequestTimerHandle != CHRE_TIMER_INVALID) { + EventLoopManagerSingleton::get()->cancelDelayedCallback( + mFlushRequestTimerHandle); + mFlushRequestTimerHandle = CHRE_TIMER_INVALID; + } + + handleFlushCompleteInternal(errorCode); +} + +void BleRequestManager::handleFlushCompleteTimeout() { + mFlushRequestTimerHandle = CHRE_TIMER_INVALID; + handleFlushCompleteInternal(CHRE_ERROR_TIMEOUT); +} + bool BleRequestManager::getScanStatus(struct chreBleScanStatus * /* status */) { // TODO(b/266820139): Implement this return false; @@ -496,6 +539,106 @@ void BleRequestManager::updatePlatformRequest(bool forceUpdate) { } } +void BleRequestManager::handleFlushCompleteInternal(uint8_t errorCode) { + auto callback = [](uint16_t /* type */, void *data, void * /* extraData */) { + uint8_t cbErrorCode = NestedDataPtr<uint8_t>(data); + EventLoopManagerSingleton::get() + ->getBleRequestManager() + .handleFlushCompleteSync(cbErrorCode); + }; + + if (!EventLoopManagerSingleton::get()->deferCallback( + SystemCallbackType::BleFlushComplete, + NestedDataPtr<uint8_t>(errorCode), callback)) { + FATAL_ERROR("Unable to defer flush complete callback"); + } +} + +void BleRequestManager::handleFlushCompleteSync(uint8_t errorCode) { + if (mFlushRequestQueue.empty() || !mFlushRequestQueue.front().isActive) { + LOGE( + "handleFlushCompleteSync was called, but there is no active flush " + "request"); + return; + } + + FlushRequest &flushRequest = mFlushRequestQueue.front(); + sendFlushCompleteEventOrDie(flushRequest, errorCode); + mFlushRequestQueue.pop(); + + processFlushRequests(); +} + +uint8_t BleRequestManager::doFlushRequest() { + CHRE_ASSERT(!mFlushRequestQueue.empty()); + + FlushRequest &flushRequest = mFlushRequestQueue.front(); + if (flushRequest.isActive) { + return CHRE_ERROR_NONE; + } + + Nanoseconds now = SystemTime::getMonotonicTime(); + uint8_t errorCode = CHRE_ERROR_NONE; + if (now >= flushRequest.deadlineTimestamp) { + LOGE("BLE flush request for nanoapp with instance ID: %" PRIu16 + " failed: deadline exceeded", + flushRequest.nanoappInstanceId); + errorCode = CHRE_ERROR_TIMEOUT; + } else { + auto timeoutCallback = [](uint16_t /* type */, void * /* data */, + void * /* extraData */) { + EventLoopManagerSingleton::get() + ->getBleRequestManager() + .handleFlushCompleteTimeout(); + }; + mFlushRequestTimerHandle = + EventLoopManagerSingleton::get()->setDelayedCallback( + SystemCallbackType::BleFlushTimeout, nullptr, timeoutCallback, + flushRequest.deadlineTimestamp - now); + + if (!mPlatformBle.flushAsync()) { + LOGE("Could not request flush from BLE platform"); + errorCode = CHRE_ERROR; + EventLoopManagerSingleton::get()->cancelDelayedCallback( + mFlushRequestTimerHandle); + mFlushRequestTimerHandle = CHRE_TIMER_INVALID; + } else { + flushRequest.isActive = true; + } + } + return errorCode; +} + +void BleRequestManager::sendFlushCompleteEventOrDie( + const FlushRequest &flushRequest, uint8_t errorCode) { + chreAsyncResult *event = memoryAlloc<chreAsyncResult>(); + if (event == nullptr) { + FATAL_ERROR("Unable to allocate chreAsyncResult"); + } + + event->requestType = CHRE_BLE_REQUEST_TYPE_FLUSH; + event->success = errorCode == CHRE_ERROR_NONE; + event->errorCode = errorCode; + event->reserved = 0; + event->cookie = flushRequest.cookie; + EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( + CHRE_EVENT_BLE_FLUSH_COMPLETE, event, freeEventDataCallback, + flushRequest.nanoappInstanceId); +} + +bool BleRequestManager::processFlushRequests() { + while (!mFlushRequestQueue.empty()) { + uint8_t errorCode = doFlushRequest(); + if (errorCode == CHRE_ERROR_NONE) { + return true; + } + + sendFlushCompleteEventOrDie(mFlushRequestQueue.front(), errorCode); + mFlushRequestQueue.pop(); + } + return false; +} + // TODO(b/290860901): require data & ~mask == 0 bool BleRequestManager::validateParams(const BleRequest &request) { bool valid = true; @@ -517,7 +660,8 @@ bool BleRequestManager::validateParams(const BleRequest &request) { void BleRequestManager::postAsyncResultEventFatal(uint16_t instanceId, uint8_t requestType, bool success, - uint8_t errorCode) { + uint8_t errorCode, + const void *cookie) { chreAsyncResult *event = memoryAlloc<chreAsyncResult>(); if (event == nullptr) { FATAL_ERROR("Failed to alloc BLE async result"); @@ -525,6 +669,7 @@ void BleRequestManager::postAsyncResultEventFatal(uint16_t instanceId, event->requestType = requestType; event->success = success; event->errorCode = errorCode; + event->cookie = cookie; event->reserved = 0; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( @@ -566,10 +711,11 @@ void BleRequestManager::logStateToBuffer(DebugDumpWrapper &debugDump) const { log.timestamp.toRawNanoseconds(), log.instanceId, log.enable ? "enable" : "disable\n"); if (log.enable && log.compliesWithBleSetting) { - debugDump.print(" mode=%" PRIu8 " reportDelayMs=%" PRIu32 - " rssiThreshold=%" PRId8 " scanCount=%" PRIu8 "\n", - static_cast<uint8_t>(log.mode), log.reportDelayMs, - log.rssiThreshold, log.scanFilterCount); + debugDump.print( + " mode=%" PRIu8 " reportDelayMs=%" PRIu32 " rssiThreshold=%" PRId8 + " scanCount=%" PRIu8 " broadcasterAddressCount=%" PRIu8 "\n", + static_cast<uint8_t>(log.mode), log.reportDelayMs, log.rssiThreshold, + log.scanFilterCount, log.broadcasterFilterCount); } else if (log.enable) { debugDump.print(" request did not comply with BLE setting\n"); } diff --git a/core/ble_request_multiplexer.cc b/core/ble_request_multiplexer.cc index eb32b33a..13a9ec1b 100644 --- a/core/ble_request_multiplexer.cc +++ b/core/ble_request_multiplexer.cc @@ -26,9 +26,11 @@ DynamicVector<BleRequest> &BleRequestMultiplexer::getMutableRequests() { const BleRequest *BleRequestMultiplexer::findRequest(uint16_t instanceId, size_t *index) { - for (size_t i = 0; i < mRequests.size(); i++) { + for (size_t i = 0; i < mRequests.size(); ++i) { if (mRequests[i].getInstanceId() == instanceId) { - *index = i; + if (index != nullptr) { + *index = i; + } return &mRequests[i]; } } diff --git a/core/event_loop.cc b/core/event_loop.cc index 56354c91..493d457b 100644 --- a/core/event_loop.cc +++ b/core/event_loop.cc @@ -192,10 +192,6 @@ bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) { } else if (!mNanoapps.prepareForPush()) { LOG_OOM(); } else { - nanoapp->setInstanceId(eventLoopManager->getNextInstanceId()); - LOGD("Instance ID %" PRIu16 " assigned to app ID 0x%016" PRIx64, - nanoapp->getInstanceId(), nanoapp->getAppId()); - Nanoapp *newNanoapp = nanoapp.get(); { LockGuard<Mutex> lock(mNanoappsLock); @@ -208,16 +204,10 @@ bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) { success = newNanoapp->start(); mCurrentApp = nullptr; if (!success) { - // TODO: to be fully safe, need to purge/flush any events and messages - // sent by the nanoapp here (but don't call nanoappEnd). For now, we just - // destroy the Nanoapp instance. LOGE("Nanoapp %" PRIu16 " failed to start", newNanoapp->getInstanceId()); - - // Note that this lock protects against concurrent read and modification - // of mNanoapps, but we are assured that no new nanoapps were added since - // we pushed the new nanoapp - LockGuard<Mutex> lock(mNanoappsLock); - mNanoapps.pop_back(); + unloadNanoapp(newNanoapp->getInstanceId(), + /*allowSystemNanoappUnload=*/true, + /*nanoappStarted=*/false); } else { notifyAppStatusChange(CHRE_EVENT_NANOAPP_STARTED, *newNanoapp); } @@ -227,7 +217,8 @@ bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) { } bool EventLoop::unloadNanoapp(uint16_t instanceId, - bool allowSystemNanoappUnload) { + bool allowSystemNanoappUnload, + bool nanoappStarted) { bool unloaded = false; for (size_t i = 0; i < mNanoapps.size(); i++) { @@ -252,13 +243,17 @@ bool EventLoop::unloadNanoapp(uint16_t instanceId, flushInboundEventQueue(); // Post the unload event now (so we can reference the Nanoapp instance - // directly), but nanoapps won't get it until after the unload completes - notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp); + // directly), but nanoapps won't get it until after the unload + // completes. No need to notify status change if nanoapps failed to + // start. + if (nanoappStarted) { + notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp); + } // Finally, we are at a point where there should not be any pending // events or messages sent by the app that could potentially reference // the nanoapp's memory, so we are safe to unload it - unloadNanoappAtIndex(i); + unloadNanoappAtIndex(i, nanoappStarted); mStoppingNanoapp = nullptr; LOGD("Unloaded nanoapp with instanceId %" PRIu16, instanceId); @@ -303,7 +298,7 @@ void EventLoop::postEventOrDie(uint16_t eventType, void *eventData, if (mRunning) { if (hasNoSpaceForHighPriorityEvent() || !allocateAndPostEvent(eventType, eventData, freeCallback, - false /*isLowPriority*/, kSystemInstanceId, + /* isLowPriority= */ false, kSystemInstanceId, targetInstanceId, targetGroupMask)) { FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType); } @@ -343,20 +338,14 @@ bool EventLoop::postLowPriorityEventOrFree( bool eventPosted = false; if (mRunning) { -#ifdef CHRE_STATIC_EVENT_LOOP - if (mEventPool.getFreeBlockCount() > kMinReservedHighPriorityEventCount) -#else - if (mEventPool.getFreeSpaceCount() > kMinReservedHighPriorityEventCount) -#endif - { - eventPosted = allocateAndPostEvent( - eventType, eventData, freeCallback, true /*isLowPriority*/, - senderInstanceId, targetInstanceId, targetGroupMask); - if (!eventPosted) { - LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu16, - eventType, targetInstanceId); - ++mNumDroppedLowPriEvents; - } + eventPosted = + allocateAndPostEvent(eventType, eventData, freeCallback, + /* isLowPriority= */ true, senderInstanceId, + targetInstanceId, targetGroupMask); + if (!eventPosted) { + LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu16, + eventType, targetInstanceId); + ++mNumDroppedLowPriEvents; } } @@ -421,12 +410,25 @@ void EventLoop::logStateToBuffer(DebugDumpWrapper &debugDump) const { uint64_t durationMins = kIntervalWakeupBucket.toRawNanoseconds() / kOneMinuteInNanoseconds; debugDump.print(" Nanoapp host wakeup tracking: cycled %" PRIu64 - "mins ago, bucketDuration=%" PRIu64 "mins\n", + " mins ago, bucketDuration=%" PRIu64 "mins\n", timeSinceMins, durationMins); debugDump.print("\nNanoapps:\n"); - for (const UniquePtr<Nanoapp> &app : mNanoapps) { - app->logStateToBuffer(debugDump); + + if (mNanoapps.size()) { + for (const UniquePtr<Nanoapp> &app : mNanoapps) { + app->logStateToBuffer(debugDump); + } + + mNanoapps[0]->logMemAndComputeHeader(debugDump); + for (const UniquePtr<Nanoapp> &app : mNanoapps) { + app->logMemAndComputeEntry(debugDump); + } + + mNanoapps[0]->logMessageHistoryHeader(debugDump); + for (const UniquePtr<Nanoapp> &app : mNanoapps) { + app->logMessageHistoryEntry(debugDump); + } } } @@ -534,7 +536,7 @@ void EventLoop::notifyAppStatusChange(uint16_t eventType, } } -void EventLoop::unloadNanoappAtIndex(size_t index) { +void EventLoop::unloadNanoappAtIndex(size_t index, bool nanoappStarted) { const UniquePtr<Nanoapp> &nanoapp = mNanoapps[index]; // Lock here to prevent the nanoapp instance from being accessed between the @@ -543,7 +545,12 @@ void EventLoop::unloadNanoappAtIndex(size_t index) { // Let the app know it's going away mCurrentApp = nanoapp.get(); - nanoapp->end(); + + // nanoappEnd() is not invoked for nanoapps that return false in + // nanoappStart(), per CHRE API + if (nanoappStarted) { + nanoapp->end(); + } // Cleanup resources. #ifdef CHRE_WIFI_SUPPORT_ENABLED @@ -594,21 +601,19 @@ void EventLoop::unloadNanoappAtIndex(size_t index) { nanoapp.get()); logDanglingResources("heap blocks", numFreedBlocks); - mCurrentApp = nullptr; - // Destroy the Nanoapp instance mNanoapps.erase(index); + + mCurrentApp = nullptr; } void EventLoop::handleNanoappWakeupBuckets() { Nanoseconds now = SystemTime::getMonotonicTime(); Nanoseconds duration = now - mTimeLastWakeupBucketCycled; if (duration > kIntervalWakeupBucket) { - size_t numBuckets = static_cast<size_t>( - duration.toRawNanoseconds() / kIntervalWakeupBucket.toRawNanoseconds()); mTimeLastWakeupBucketCycled = now; for (auto &nanoapp : mNanoapps) { - nanoapp->cycleWakeupBuckets(numBuckets); + nanoapp->cycleWakeupBuckets(now); } } } diff --git a/core/event_loop_manager.cc b/core/event_loop_manager.cc index 51167711..4baf5d63 100644 --- a/core/event_loop_manager.cc +++ b/core/event_loop_manager.cc @@ -16,6 +16,7 @@ #include "chre/core/event_loop_manager.h" +#include "chre/platform/atomic.h" #include "chre/platform/fatal_error.h" #include "chre/platform/memory.h" #include "chre/util/lock_guard.h" @@ -31,18 +32,19 @@ Nanoapp *EventLoopManager::validateChreApiCall(const char *functionName) { } uint16_t EventLoopManager::getNextInstanceId() { - ++mLastInstanceId; + // Get the next available instance ID and mask off the upper 16 bit. + uint16_t instanceId = + static_cast<uint16_t>(mNextInstanceId.fetch_increment() & 0x0000FFFF); - // ~4 billion instance IDs should be enough for anyone... if we need to + // 65536 instance IDs should be enough for normal use cases. If we need to // support wraparound for stress testing load/unload, then we can set a flag // when wraparound occurs and use EventLoop::findNanoappByInstanceId to ensure // we avoid conflicts - if (mLastInstanceId == kBroadcastInstanceId || - mLastInstanceId == kSystemInstanceId) { + if (instanceId == kBroadcastInstanceId || instanceId == kSystemInstanceId) { FATAL_ERROR("Exhausted instance IDs!"); } - return mLastInstanceId; + return instanceId; } void EventLoopManager::lateInit() { diff --git a/core/host_comms_manager.cc b/core/host_comms_manager.cc index 81afa58e..102813fa 100644 --- a/core/host_comms_manager.cc +++ b/core/host_comms_manager.cc @@ -20,6 +20,7 @@ #include "chre/core/event_loop_manager.h" #include "chre/core/host_comms_manager.h" #include "chre/platform/assert.h" +#include "chre/platform/context.h" #include "chre/platform/host_link.h" #include "chre/util/macros.h" @@ -67,13 +68,17 @@ bool HostCommsManager::sendMessageToHostFromNanoapp( success = HostLink::sendMessage(msgToHost); if (!success) { mMessagePool.deallocate(msgToHost); - } else if (wokeHost) { - // If message successfully sent and host was suspended before sending - EventLoopManagerSingleton::get() - ->getEventLoop() - .handleNanoappWakeupBuckets(); - mIsNanoappBlamedForWakeup = true; - nanoapp->blameHostWakeup(); + } else { + if (wokeHost) { + // If message successfully sent and host was suspended before sending + EventLoopManagerSingleton::get() + ->getEventLoop() + .handleNanoappWakeupBuckets(); + mIsNanoappBlamedForWakeup = true; + nanoapp->blameHostWakeup(); + } + // Record the nanoapp having sent a message to the host + nanoapp->blameHostMessageSent(); } } } @@ -196,6 +201,11 @@ void HostCommsManager::onMessageToHostComplete(const MessageToHost *message) { // EventLoop context. if (msgToHost->toHostData.nanoappFreeFunction == nullptr) { mMessagePool.deallocate(msgToHost); + } else if (inEventLoopThread()) { + // If we're already within the event pool context, it is safe to call the + // free callback synchronously. + EventLoopManagerSingleton::get()->getHostCommsManager().freeMessageToHost( + msgToHost); } else { auto freeMsgCallback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { diff --git a/core/include/chre/core/ble_request.h b/core/include/chre/core/ble_request.h index 66667fb3..e442e795 100644 --- a/core/include/chre/core/ble_request.h +++ b/core/include/chre/core/ble_request.h @@ -40,10 +40,11 @@ class BleRequest : public NonCopyable { public: BleRequest(); - BleRequest(uint16_t instanceId, bool enable); + BleRequest(uint16_t instanceId, bool enable, const void *cookie); BleRequest(uint16_t instanceId, bool enable, chreBleScanMode mode, - uint32_t reportDelayMs, const chreBleScanFilter *filter); + uint32_t reportDelayMs, const chreBleScanFilterV1_9 *filter, + const void *cookie); BleRequest(BleRequest &&other); @@ -103,10 +104,21 @@ class BleRequest : public NonCopyable { const DynamicVector<chreBleGenericFilter> &getGenericFilters() const; /** - * @return chreBleScanFilter that is valid only as long as the internal + * @return Broadcaster address filters of this request. + */ + const DynamicVector<chreBleBroadcasterAddressFilter> &getBroadcasterFilters() + const; + + /** + * @return The cookie this request. + */ + const void *getCookie() const; + + /** + * @return chreBleScanFilterV1_9 that is valid only as long as the internal * contents of this class are not modified */ - chreBleScanFilter getScanFilter() const; + chreBleScanFilterV1_9 getScanFilter() const; /** * @return true if nanoapp intends to enable a request. @@ -136,7 +148,7 @@ class BleRequest : public NonCopyable { chreBleScanMode mMode; // Whether a nanoapp intends to enable this request. If set to false, the - // following members are invalid: mMode, mReportDelayMs, mFilter. + // following members are invalid: mMode, mReportDelayMs, mGenericFilters. bool mEnabled; // RSSI threshold filter. @@ -148,7 +160,13 @@ class BleRequest : public NonCopyable { RequestStatus mStatus; // Generic scan filters. - DynamicVector<chreBleGenericFilter> mFilters; + DynamicVector<chreBleGenericFilter> mGenericFilters; + + // Broadcaster address filters. + DynamicVector<chreBleBroadcasterAddressFilter> mBroadcasterFilters; + + // Cookie value included in this request, supplied by the nanoapp. + const void *mCookie; }; } // namespace chre diff --git a/core/include/chre/core/ble_request_manager.h b/core/include/chre/core/ble_request_manager.h index 747b5a72..16d280fc 100644 --- a/core/include/chre/core/ble_request_manager.h +++ b/core/include/chre/core/ble_request_manager.h @@ -21,7 +21,9 @@ #include "chre/core/ble_request_multiplexer.h" #include "chre/core/nanoapp.h" #include "chre/core/settings.h" +#include "chre/core/timer_pool.h" #include "chre/platform/platform_ble.h" +#include "chre/platform/system_time.h" #include "chre/util/array_queue.h" #include "chre/util/non_copyable.h" #include "chre/util/system/debug_dump.h" @@ -61,24 +63,29 @@ class BleRequestManager : public NonCopyable { * batching. Note that the system may deliver results * before the maximum specified delay is reached. * @param filter Pointer to the requested best-effort filter configuration as - * defined by struct chreBleScanFilter. The ownership of filter - * and its nested elements remains with the caller, and the - * caller may release it as soon as chreBleStartScanAsync() - * returns. + * defined by struct chreBleScanFilterV1_9. The ownership of + * filter and its nested elements remains with the caller, and + * the caller may release it as soon as + * chreBleStartScanAsyncV1_9() returns. + * @param cookie The cookie to be provided to the nanoapp. This is + * round-tripped from the nanoapp to provide context. * @return true if scan was successfully enabled. */ bool startScanAsync(Nanoapp *nanoapp, chreBleScanMode mode, uint32_t reportDelayMs, - const struct chreBleScanFilter *filter); + const struct chreBleScanFilterV1_9 *filter, + const void *cookie); /** * End a BLE scan asynchronously. The result is delivered through a * CHRE_EVENT_BLE_ASYNC_RESULT event. * * @param nanoapp The nanoapp stopping the request. + * @param cookie A cookie that is round-tripped back to the nanoapp to + * provide a context when making the request. * @return whether the scan was successfully ended. */ - bool stopScanAsync(Nanoapp *nanoapp); + bool stopScanAsync(Nanoapp *nanoapp, const void *cookie); #ifdef CHRE_BLE_READ_RSSI_SUPPORT_ENABLED /** @@ -111,6 +118,18 @@ class BleRequestManager : public NonCopyable { #endif /** + * Initiates a flush operation where all batched advertisement events will be + * immediately processed and delivered. The nanoapp must have an existing + * active BLE scan. + * + * @param nanoapp the nanoapp requesting the flush operation. + * @param cookie the cookie value stored with the request. + * @return true if the request has been accepted and dispatched to the + * controller. false otherwise. + */ + bool flushAsync(Nanoapp *nanoapp, const void *cookie); + + /** * Disables active scan for a nanoapp (no-op if no active scan). * * @param nanoapp A non-null pointer to the nanoapp. @@ -173,6 +192,17 @@ class BleRequestManager : public NonCopyable { #endif /** + * Handler for the flush complete operation. Called when a flush operation is + * complete. Processes in an asynchronous manner. + * + * @param errorCode the error code from the flush operation. + */ + void handleFlushComplete(uint8_t errorCode); + + //! Timeout handler for the flush operation. Called on a timeout. + void handleFlushCompleteTimeout(); + + /** * Retrieves the current scan status. * * @param status A non-null pointer to where the scan status will be @@ -201,6 +231,23 @@ class BleRequestManager : public NonCopyable { void logStateToBuffer(DebugDumpWrapper &debugDump) const; private: + //! An internal structure to store incoming sensor flush requests + struct FlushRequest { + FlushRequest(uint16_t id, const void *cookiePtr) + : nanoappInstanceId(id), cookie(cookiePtr) {} + + //! The timestamp at which this request should complete. + Nanoseconds deadlineTimestamp = + SystemTime::getMonotonicTime() + + Nanoseconds(CHRE_BLE_FLUSH_COMPLETE_TIMEOUT_NS); + //! The ID of the nanoapp that requested the flush. + uint16_t nanoappInstanceId; + //! The opaque pointer provided in flushAsync(). + const void *cookie; + //! True if this flush request is active and is pending completion. + bool isActive = false; + }; + // Multiplexer used to keep track of BLE requests from nanoapps. BleRequestMultiplexer mRequests; @@ -222,6 +269,13 @@ class BleRequestManager : public NonCopyable { // True if a setting change request is pending to be processed. bool mSettingChangePending; + //! A queue of flush requests made by nanoapps. + static constexpr size_t kMaxFlushRequests = 16; + ArrayQueue<FlushRequest, kMaxFlushRequests> mFlushRequestQueue; + + //! The timer handle for the flush operation. Used to track a flush timeout. + TimerHandle mFlushRequestTimerHandle = CHRE_TIMER_INVALID; + #ifdef CHRE_BLE_READ_RSSI_SUPPORT_ENABLED // A pending request from a nanoapp struct BleReadRssiRequest { @@ -238,17 +292,19 @@ class BleRequestManager : public NonCopyable { // Struct to hold ble request data for logging struct BleRequestLog { - BleRequestLog(Nanoseconds timestamp, uint32_t instanceId, bool enable, - bool compliesWithBleSetting) - : timestamp(timestamp), - instanceId(instanceId), - enable(enable), - compliesWithBleSetting(compliesWithBleSetting) {} + BleRequestLog(Nanoseconds timestamp_, uint32_t instanceId_, bool enable_, + bool compliesWithBleSetting_) + : timestamp(timestamp_), + instanceId(instanceId_), + enable(enable_), + compliesWithBleSetting(compliesWithBleSetting_) {} void populateRequestData(const BleRequest &req) { mode = req.getMode(); reportDelayMs = req.getReportDelayMs(); rssiThreshold = req.getRssiThreshold(); scanFilterCount = static_cast<uint8_t>(req.getGenericFilters().size()); + broadcasterFilterCount = + static_cast<uint8_t>(req.getBroadcasterFilters().size()); } Nanoseconds timestamp; uint32_t instanceId; @@ -258,6 +314,7 @@ class BleRequestManager : public NonCopyable { uint32_t reportDelayMs; int8_t rssiThreshold; uint8_t scanFilterCount; + uint8_t broadcasterFilterCount; }; // List of most recent ble request logs @@ -301,11 +358,13 @@ class BleRequestManager : public NonCopyable { * to the nanoapp instance id of the new request. * @param requestIndex If hasExistingRequest is true, requestIndex * corresponds to the index of that request. + * @param cookie The cookie to be provided to the nanoapp. * @return true if the request does not attempt to enable the platform while * the BLE setting is disabled. */ bool compliesWithBleSetting(uint16_t instanceId, bool enabled, - bool hasExistingRequest, size_t requestIndex); + bool hasExistingRequest, size_t requestIndex, + const void *cookie); /** * Add a log to list of BLE request logs possibly pushing out the oldest log. @@ -375,10 +434,13 @@ class BleRequestManager : public NonCopyable { * @param success Whether the request was processed by the PAL successfully * @param errorCode Error code resulting from the request * @param forceUnregister Whether the nanoapp should be force unregistered + * @param cookie The cookie to be provided to the nanoapp. This is + * round-tripped from the nanoapp to provide context. * from BLE broadcast events. */ void handleAsyncResult(uint16_t instanceId, bool enabled, bool success, - uint8_t errorCode, bool forceUnregister = false); + uint8_t errorCode, const void *cookie, + bool forceUnregister = false); /** * Invoked as a result of a requestStateResync() callback from the BLE PAL. @@ -396,6 +458,54 @@ class BleRequestManager : public NonCopyable { void updatePlatformRequest(bool forceUpdate = false); /** + * Helper function for flush complete handling in all cases - normal and + * timeout. This function defers a call to handleFlushCompleteSync. + * + * @param errorCode the error code for the flush operation. + */ + void handleFlushCompleteInternal(uint8_t errorCode); + + /** + * Synchronously processed a flush complete operation. Starts a new flush + * operation if there is one in the queue. Properly sends the flush complete + * event. + * + * @param errorCode the error code for the flush operation. + */ + void handleFlushCompleteSync(uint8_t errorCode); + + /** + * Sends the flush request to the controller if there is a non-active flush + * request in the flush request queue. Sets the timer callback to handle + * timeouts. + * + * @return the error code, chreError enum (CHRE_ERROR_NONE for success). + */ + uint8_t doFlushRequest(); + + /** + * Sends the flush complete event or aborts CHRE. + * + * @param flushRequest the current active flush request. + * @param errorCode the error code, chreError enum. + */ + void sendFlushCompleteEventOrDie(const FlushRequest &flushRequest, + uint8_t errorCode); + + /** + * Processes flush requests in the flush request queue in order. Calls + * doFlushRequest on the request. If an error is detected, it sends the flush + * complete event with the error. This function continues to process requests + * until one flush request is successfully made. Once this happens, the + * request manager waits for a timeout or for a callback from the BLE + * platform. + * + * @return true if there was one flush request that was successfully + * initiated, false otherwise. + */ + bool processFlushRequests(); + + /** * Validates the parameters given to ensure that they can be issued to the * PAL. * @@ -410,10 +520,11 @@ class BleRequestManager : public NonCopyable { * @param requestType The type of BLE request the nanoapp issued. * @param success true if the operation was successful. * @param errorCode the error code as a result of this operation. + * @param cookie The cookie to be provided to the nanoapp. */ static void postAsyncResultEventFatal(uint16_t instanceId, uint8_t requestType, bool success, - uint8_t errorCode); + uint8_t errorCode, const void *cookie); /** * @return True if the given advertisement type is valid diff --git a/core/include/chre/core/ble_request_multiplexer.h b/core/include/chre/core/ble_request_multiplexer.h index 46357852..e25747c9 100644 --- a/core/include/chre/core/ble_request_multiplexer.h +++ b/core/include/chre/core/ble_request_multiplexer.h @@ -43,12 +43,12 @@ class BleRequestMultiplexer : public RequestMultiplexer<BleRequest> { /** * Searches through the list of BLE requests for a request owned by the - * given nanoapp. The provided non-null index pointer is populated with the - * index of the request if it is found. + * given nanoapp. The provided index pointer is populated with the + * index of the request if it is found and the pointer is not null. * * @param instanceId The instance ID of the nanoapp whose request is being * searched for. - * @param index A non-null pointer to an index that is populated if a + * @param index A pointer to an index that is populated if a * request for this nanoapp is found. * @return A pointer to a BleRequest that is owned by the provided * nanoapp if one is found otherwise nullptr. diff --git a/core/include/chre/core/event_loop.h b/core/include/chre/core/event_loop.h index 34034109..6a64956f 100644 --- a/core/include/chre/core/event_loop.h +++ b/core/include/chre/core/event_loop.h @@ -57,13 +57,6 @@ #define CHRE_MAX_EVENT_BLOCKS 4 #endif -#ifndef CHRE_UNSCHEDULED_EVENT_PER_BLOCK -#define CHRE_UNSCHEDULED_EVENT_PER_BLOCK 24 -#endif - -#ifndef CHRE_MAX_UNSCHEDULED_EVENT_BLOCKS -#define CHRE_MAX_UNSCHEDULED_EVENT_BLOCKS 4 -#endif #endif namespace chre { @@ -78,7 +71,7 @@ class EventLoop : public NonCopyable { EventLoop() : #ifndef CHRE_STATIC_EVENT_LOOP - mEvents(kMaxUnscheduleEventBlocks), + mEvents(kMaxEventBlock), #endif mTimeLastWakeupBucketCycled(SystemTime::getMonotonicTime()), mRunning(true) { @@ -159,10 +152,12 @@ class EventLoop : public NonCopyable { * @param instanceId The nanoapp's unique instance identifier * @param allowSystemNanoappUnload If false, this function will reject * attempts to unload a system nanoapp + * @param nanoappStarted Indicates whether the nanoapp successfully started * * @return true if the nanoapp with the given instance ID was found & unloaded */ - bool unloadNanoapp(uint16_t instanceId, bool allowSystemNanoappUnload); + bool unloadNanoapp(uint16_t instanceId, bool allowSystemNanoappUnload, + bool nanoappStarted = true); /** * Executes the loop that blocks on the event queue and delivers received @@ -356,10 +351,6 @@ class EventLoop : public NonCopyable { //! The maximum number of events that can be active in the system. static constexpr size_t kMaxEventCount = CHRE_MAX_EVENT_COUNT; - //! The minimum number of events to reserve in the event pool for high - //! priority events. - static constexpr size_t kMinReservedHighPriorityEventCount = 16; - //! The maximum number of events that are awaiting to be scheduled. These //! events are in a queue to be distributed to apps. static constexpr size_t kMaxUnscheduledEventCount = @@ -382,28 +373,15 @@ class EventLoop : public NonCopyable { static constexpr size_t kMaxEventCount = CHRE_EVENT_PER_BLOCK * CHRE_MAX_EVENT_BLOCKS; - //! The minimum number of events to reserve in the event pool for high - //! priority events. - static constexpr size_t kMinReservedHighPriorityEventCount = 16; - - //! The maximum number of events per block that are awaiting to be scheduled. - //! These events are in a queue to be distributed to apps. - static constexpr size_t kMaxUnscheduledEventPerBlock = - CHRE_UNSCHEDULED_EVENT_PER_BLOCK; - - //! The maximum number of event blocks that mEvents can hold. - static constexpr size_t kMaxUnscheduleEventBlocks = - CHRE_MAX_UNSCHEDULED_EVENT_BLOCKS; - //! The memory pool to allocate incoming events from. SynchronizedExpandableMemoryPool<Event, kEventPerBlock, kMaxEventBlock> mEventPool; //! The blocking queue of incoming events from the system that have not been //! distributed out to apps yet. - BlockingSegmentedQueue<Event *, kMaxUnscheduledEventPerBlock> mEvents; + BlockingSegmentedQueue<Event *, kEventPerBlock> mEvents; #endif - //! The time interval of nanoapp wakeup buckets, adjust in conjuction with + //! The time interval of nanoapp wakeup buckets, adjust in conjunction with //! Nanoapp::kMaxSizeWakeupBuckets. static constexpr Nanoseconds kIntervalWakeupBucket = Nanoseconds(180 * kOneMinuteInNanoseconds); @@ -551,8 +529,12 @@ class EventLoop : public NonCopyable { * be any pending events in this nanoapp's queue, and there must not be any * outstanding events sent by this nanoapp, as they may reference the * nanoapp's own memory (even if there is no free callback). + * + * @param index Index of the nanoapp in the list of nanoapps managed by event + * loop. + * @param nanoappStarted Indicates whether the nanoapp successfully started */ - void unloadNanoappAtIndex(size_t index); + void unloadNanoappAtIndex(size_t index, bool nanoappStarted = true); /** * Logs dangling resources when a nanoapp is unloaded. diff --git a/core/include/chre/core/event_loop_common.h b/core/include/chre/core/event_loop_common.h index e487cbce..c6f4946e 100644 --- a/core/include/chre/core/event_loop_common.h +++ b/core/include/chre/core/event_loop_common.h @@ -70,6 +70,9 @@ enum class SystemCallbackType : uint16_t { BleRequestResyncEvent, RequestTimeoutEvent, BleReadRssiEvent, + BleFlushComplete, + BleFlushTimeout, + PulseResponse, }; //! Deferred/delayed callbacks use the event subsystem but are invariably sent diff --git a/core/include/chre/core/event_loop_manager.h b/core/include/chre/core/event_loop_manager.h index f6e7d749..b5f52b3c 100644 --- a/core/include/chre/core/event_loop_manager.h +++ b/core/include/chre/core/event_loop_manager.h @@ -24,6 +24,7 @@ #include "chre/core/host_endpoint_manager.h" #include "chre/core/settings.h" #include "chre/core/system_health_monitor.h" +#include "chre/platform/atomic.h" #include "chre/platform/memory_manager.h" #include "chre/platform/mutex.h" #include "chre/util/always_false.h" @@ -349,8 +350,8 @@ class EventLoopManager : public NonCopyable { void lateInit(); private: - //! The instance ID that was previously generated by getNextInstanceId() - uint16_t mLastInstanceId = kSystemInstanceId; + //! The instance ID generated by getNextInstanceId(). + AtomicUint32 mNextInstanceId{kSystemInstanceId + 1}; #ifdef CHRE_AUDIO_SUPPORT_ENABLED //! The audio request manager handles requests for all nanoapps and manages diff --git a/core/include/chre/core/gnss_manager.h b/core/include/chre/core/gnss_manager.h index 7b88d31a..d4885ff3 100644 --- a/core/include/chre/core/gnss_manager.h +++ b/core/include/chre/core/gnss_manager.h @@ -413,11 +413,11 @@ class GnssManager : public NonCopyable { GnssSession &getLocationSession() { return mLocationSession; - }; + } GnssSession &getMeasurementSession() { return mMeasurementSession; - }; + } /** * Invoked when the host notifies CHRE of a settings change. diff --git a/core/include/chre/core/host_endpoint_manager.h b/core/include/chre/core/host_endpoint_manager.h index 40781bbc..8fd7e099 100644 --- a/core/include/chre/core/host_endpoint_manager.h +++ b/core/include/chre/core/host_endpoint_manager.h @@ -90,6 +90,6 @@ class HostEndpointManager : public NonCopyable { auto getHostNotificationCallback(); }; -}; // namespace chre +} // namespace chre #endif // CHRE_CORE_HOST_ENDPOINT_MANAGER_H_ diff --git a/core/include/chre/core/nanoapp.h b/core/include/chre/core/nanoapp.h index ae72327e..9fb9a986 100644 --- a/core/include/chre/core/nanoapp.h +++ b/core/include/chre/core/nanoapp.h @@ -25,6 +25,7 @@ #include "chre/core/event_ref_queue.h" #include "chre/platform/heap_block_header.h" #include "chre/platform/platform_nanoapp.h" +#include "chre/platform/system_time.h" #include "chre/util/dynamic_vector.h" #include "chre/util/fixed_size_vector.h" #include "chre/util/system/debug_dump.h" @@ -56,6 +57,10 @@ class Nanoapp : public PlatformNanoapp { Nanoapp(); + // The nanoapp instance ID should only come from the event loop manager. This + // constructor should never be called except for use in unit tests. + Nanoapp(uint16_t instanceId); + /** * Calls the start function of the nanoapp. For dynamically loaded nanoapps, * this must also result in calling through to any of the nanoapp's static @@ -76,14 +81,6 @@ class Nanoapp : public PlatformNanoapp { } /** - * Assigns an instance ID to this Nanoapp. This must be called prior to - * starting this Nanoapp. - */ - void setInstanceId(uint16_t instanceId) { - mInstanceId = instanceId; - } - - /** * @return The current total number of bytes the nanoapp has allocated. */ size_t getTotalAllocatedBytes() const { @@ -188,14 +185,23 @@ class Nanoapp : public PlatformNanoapp { */ void blameHostWakeup(); + /** + * Log info about a single message sent to the host that this nanoapp + * triggered by storing the count of messages in mNumMessagesSentSinceBoot. + */ + void blameHostMessageSent(); + /* * If buckets not full, then just pushes a 0 to back of buckets. If full, then * shifts down all buckets from back to front and sets back to 0, losing the * latest bucket value that was in front. * - * @param numBuckets the number of buckets to cycle into to mWakeupBuckets + * With nanoapps tracking their cycling time, there is no reason to ever + * cycle more than one bucket at a time. Doing more wastes valuable data + * + * @param timestamp the current time when this bucket was created */ - void cycleWakeupBuckets(size_t numBuckets); + void cycleWakeupBuckets(Nanoseconds timestamp); /** * Prints state in a string buffer. Must only be called from the context of @@ -206,6 +212,39 @@ class Nanoapp : public PlatformNanoapp { void logStateToBuffer(DebugDumpWrapper &debugDump) const; /** + * Prints header for memory allocation and event processing time stats table + * in a string buffer. Must only be called from the context of the main CHRE + * thread. + * + * @param debugDump The object that is printed into for debug dump logs. + */ + void logMemAndComputeHeader(DebugDumpWrapper &debugDump) const; + + /** + * Prints memory allocation and event processing time stats in a string + * buffer. Must only be called from the context of the main CHRE thread. + * + * @param debugDump The object that is printed into for debug dump logs. + */ + void logMemAndComputeEntry(DebugDumpWrapper &debugDump) const; + + /** + * Prints header for wakeup and host message stats table in a string buffer. + * Must only be called from the context of the main CHRE thread. + * + * @param debugDump The object that is printed into for debug dump logs. + */ + void logMessageHistoryHeader(DebugDumpWrapper &debugDump) const; + + /** + * Prints wakeup and host message stats in a string buffer. Must only be + * called from the context of the main CHRE thread. + * + * @param debugDump The object that is printed into for debug dump logs. + */ + void logMessageHistoryEntry(DebugDumpWrapper &debugDump) const; + + /** * @return true if the nanoapp is permitted to use the provided permission. */ bool permitPermissionUse(uint32_t permission) const; @@ -269,6 +308,12 @@ class Nanoapp : public PlatformNanoapp { //! The total number of wakeup counts for a nanoapp. uint32_t mNumWakeupsSinceBoot = 0; + //! The total number of messages sent to host by this nanoapp. + uint32_t mNumMessagesSentSinceBoot = 0; + + //! The total time in ms spend processing events by this nanoapp. + uint64_t mEventProcessTimeSinceBoot = 0; + /** * Head of the singly linked list of heap block headers. * @@ -284,13 +329,28 @@ class Nanoapp : public PlatformNanoapp { //! The peak total number of bytes allocated by the nanoapp. size_t mPeakAllocatedBytes = 0; + //! Container for "bucketed" stats associated with wakeup logging + struct BucketedStats { + BucketedStats(uint16_t wakeupCount_, uint16_t hostMessageCount_, + uint64_t eventProcessTime_, uint64_t creationTimestamp_) + : wakeupCount(wakeupCount_), + hostMessageCount(hostMessageCount_), + eventProcessTime(eventProcessTime_), + creationTimestamp(creationTimestamp_) {} + + uint16_t wakeupCount = 0; + uint16_t hostMessageCount = 0; + uint64_t eventProcessTime = 0; + uint64_t creationTimestamp = 0; + }; + //! The number of buckets for wakeup logging, adjust along with - //! EventLoop::kIntervalWakupBucketInMins. - static constexpr size_t kMaxSizeWakeupBuckets = 4; + //! EventLoop::kIntervalWakeupBucket. + static constexpr size_t kMaxSizeWakeupBuckets = 5; //! A fixed size buffer of buckets that keeps track of the number of host //! wakeups over time intervals. - FixedSizeVector<uint16_t, kMaxSizeWakeupBuckets> mWakeupBuckets; + FixedSizeVector<BucketedStats, kMaxSizeWakeupBuckets> mWakeupBuckets; //! Collects process time in nanoseconds of each event StatsContainer<uint64_t> mEventProcessTime; diff --git a/core/include/chre/core/request_multiplexer_impl.h b/core/include/chre/core/request_multiplexer_impl.h index 63b742fd..a5dfb342 100644 --- a/core/include/chre/core/request_multiplexer_impl.h +++ b/core/include/chre/core/request_multiplexer_impl.h @@ -27,7 +27,7 @@ bool RequestMultiplexer<RequestType>::addRequest(const RequestType &request, size_t *index, bool *maximalRequestChanged) { CHRE_ASSERT_NOT_NULL(index); - CHRE_ASSERT(maximalRequestChanged); + CHRE_ASSERT_NOT_NULL(maximalRequestChanged); bool requestStored = mRequests.push_back(request); if (requestStored) { @@ -43,7 +43,7 @@ bool RequestMultiplexer<RequestType>::addRequest(RequestType &&request, size_t *index, bool *maximalRequestChanged) { CHRE_ASSERT_NOT_NULL(index); - CHRE_ASSERT(maximalRequestChanged); + CHRE_ASSERT_NOT_NULL(maximalRequestChanged); bool requestStored = mRequests.push_back(std::move(request)); if (requestStored) { @@ -57,7 +57,7 @@ bool RequestMultiplexer<RequestType>::addRequest(RequestType &&request, template <typename RequestType> void RequestMultiplexer<RequestType>::updateRequest( size_t index, const RequestType &request, bool *maximalRequestChanged) { - CHRE_ASSERT(maximalRequestChanged); + CHRE_ASSERT_NOT_NULL(maximalRequestChanged); CHRE_ASSERT(index < mRequests.size()); if (index < mRequests.size()) { @@ -114,6 +114,8 @@ const RequestType &RequestMultiplexer<RequestType>::getCurrentMaximalRequest() template <typename RequestType> void RequestMultiplexer<RequestType>::updateMaximalRequest( bool *maximalRequestChanged) { + CHRE_ASSERT_NOT_NULL(maximalRequestChanged); + RequestType maximalRequest; for (size_t i = 0; i < mRequests.size(); i++) { maximalRequest.mergeWith(mRequests[i]); diff --git a/core/include/chre/core/system_health_monitor.h b/core/include/chre/core/system_health_monitor.h index 2b203a4b..ea4af24f 100644 --- a/core/include/chre/core/system_health_monitor.h +++ b/core/include/chre/core/system_health_monitor.h @@ -17,6 +17,8 @@ #ifndef CHRE_CORE_SYSTEM_HEALTH_MONITOR_H_ #define CHRE_CORE_SYSTEM_HEALTH_MONITOR_H_ +#include <cstdint> + #include "chre/platform/assert.h" #include "chre/platform/log.h" #include "chre/util/enum.h" diff --git a/core/include/chre/core/timer_pool.h b/core/include/chre/core/timer_pool.h index fc447853..d6dd2046 100644 --- a/core/include/chre/core/timer_pool.h +++ b/core/include/chre/core/timer_pool.h @@ -149,10 +149,10 @@ class TimerPool : public NonCopyable { uint16_t instanceId; /** - * Provides a greater than comparison of TimerRequests. + * Returns whether the current request expires after the passed one. * - * @param request The other request to compare against. - * @return Returns true if this request is greater than the provided + * @param request The other request. + * @return Returns whether this request expires after the provided * request. */ bool operator>(const TimerRequest &request) const; diff --git a/core/log.cc b/core/log.cc index c8ac7660..f95b8885 100644 --- a/core/log.cc +++ b/core/log.cc @@ -16,6 +16,7 @@ #ifdef CHRE_TOKENIZED_LOGGING_ENABLED #include "chre/platform/log.h" +#include "pw_log_tokenized/config.h" #include "pw_tokenizer/encode_args.h" #include "pw_tokenizer/tokenize.h" @@ -26,7 +27,8 @@ void EncodeTokenizedMessage(uint32_t level, pw_tokenizer_Token token, pw_tokenizer_ArgTypes types, ...) { va_list args; va_start(args, types); - pw::tokenizer::EncodedMessage encodedMessage(token, types, args); + pw::tokenizer::EncodedMessage<pw::log_tokenized::kEncodingBufferSizeBytes> + encodedMessage(token, types, args); va_end(args); chrePlatformEncodedLogToBuffer(static_cast<chreLogLevel>(level), diff --git a/core/nanoapp.cc b/core/nanoapp.cc index 789f2da0..faf00daa 100644 --- a/core/nanoapp.cc +++ b/core/nanoapp.cc @@ -36,13 +36,18 @@ namespace chre { constexpr size_t Nanoapp::kMaxSizeWakeupBuckets; -Nanoapp::Nanoapp() { +Nanoapp::Nanoapp() + : Nanoapp(EventLoopManagerSingleton::get()->getNextInstanceId()) {} + +Nanoapp::Nanoapp(uint16_t instanceId) { // Push first bucket onto wakeup bucket queue - cycleWakeupBuckets(1); + cycleWakeupBuckets(SystemTime::getMonotonicTime()); + mInstanceId = instanceId; } bool Nanoapp::start() { - traceRegisterNanoapp(getInstanceId(), getAppName()); + // TODO(b/294116163): update trace with nanoapp instance id and nanoapp name + CHRE_TRACE_INSTANT("Nanoapp start"); mIsInNanoappStart = true; bool success = PlatformNanoapp::start(); mIsInNanoappStart = false; @@ -134,37 +139,49 @@ void Nanoapp::configureUserSettingEvent(uint8_t setting, bool enable) { void Nanoapp::processEvent(Event *event) { Nanoseconds eventStartTime = SystemTime::getMonotonicTime(); - traceNanoappHandleEventStart(getInstanceId(), event->eventType); + // TODO(b/294116163): update trace with event type and nanoapp name so it can + // be differentiated from other events + CHRE_TRACE_START("Handle event", "nanoapp", getInstanceId()); if (event->eventType == CHRE_EVENT_GNSS_DATA) { handleGnssMeasurementDataEvent(event); } else { handleEvent(event->senderInstanceId, event->eventType, event->eventData); } - traceNanoappHandleEventEnd(getInstanceId()); + // TODO(b/294116163): update trace with nanoapp name + CHRE_TRACE_END("Handle event", "nanoapp", getInstanceId()); Nanoseconds eventProcessTime = SystemTime::getMonotonicTime() - eventStartTime; + uint64_t eventTimeMs = Milliseconds(eventProcessTime).getMilliseconds(); if (Milliseconds(eventProcessTime) >= Milliseconds(100)) { LOGE("Nanoapp 0x%" PRIx64 " took %" PRIu64 - " ms to process event type %" PRIu16, - getAppId(), Milliseconds(eventProcessTime).getMilliseconds(), - event->eventType); + " ms to process event type 0x%" PRIx16, + getAppId(), eventTimeMs, event->eventType); } - mEventProcessTime.addValue(Milliseconds(eventProcessTime).getMilliseconds()); + mEventProcessTime.addValue(eventTimeMs); + mEventProcessTimeSinceBoot += eventTimeMs; + mWakeupBuckets.back().eventProcessTime += eventTimeMs; } void Nanoapp::blameHostWakeup() { - if (mWakeupBuckets.back() < UINT16_MAX) ++mWakeupBuckets.back(); + if (mWakeupBuckets.back().wakeupCount < UINT16_MAX) { + ++mWakeupBuckets.back().wakeupCount; + } if (mNumWakeupsSinceBoot < UINT32_MAX) ++mNumWakeupsSinceBoot; } -void Nanoapp::cycleWakeupBuckets(size_t numBuckets) { - numBuckets = std::min(numBuckets, kMaxSizeWakeupBuckets); - for (size_t i = 0; i < numBuckets; ++i) { - if (mWakeupBuckets.full()) { - mWakeupBuckets.erase(0); - } - mWakeupBuckets.push_back(0); +void Nanoapp::blameHostMessageSent() { + if (mWakeupBuckets.back().hostMessageCount < UINT16_MAX) { + ++mWakeupBuckets.back().hostMessageCount; + } + if (mNumMessagesSentSinceBoot < UINT32_MAX) ++mNumMessagesSentSinceBoot; +} + +void Nanoapp::cycleWakeupBuckets(Nanoseconds timestamp) { + if (mWakeupBuckets.full()) { + mWakeupBuckets.erase(0); } + mWakeupBuckets.push_back( + BucketedStats(0, 0, 0, timestamp.toRawNanoseconds())); } void Nanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const { @@ -172,27 +189,134 @@ void Nanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const { getAppId()); PlatformNanoapp::logStateToBuffer(debugDump); debugDump.print(" v%" PRIu32 ".%" PRIu32 ".%" PRIu32 " tgtAPI=%" PRIu32 - ".%" PRIu32 " curAlloc=%zu peakAlloc=%zu", + ".%" PRIu32 "\n", CHRE_EXTRACT_MAJOR_VERSION(getAppVersion()), CHRE_EXTRACT_MINOR_VERSION(getAppVersion()), CHRE_EXTRACT_PATCH_VERSION(getAppVersion()), CHRE_EXTRACT_MAJOR_VERSION(getTargetApiVersion()), - CHRE_EXTRACT_MINOR_VERSION(getTargetApiVersion()), - getTotalAllocatedBytes(), getPeakAllocatedBytes()); - debugDump.print(" hostWakeups=[ cur->"); - // Get buckets latest -> earliest except last one - for (size_t i = mWakeupBuckets.size() - 1; i > 0; --i) { - debugDump.print("%" PRIu16 ", ", mWakeupBuckets[i]); - } - // Earliest bucket gets no comma - debugDump.print("%" PRIu16 " ]", mWakeupBuckets.front()); - - // Print total wakeups since boot - debugDump.print(" totWakeups=%" PRIu32 " ", mNumWakeupsSinceBoot); - - // Print mean and max event process time - debugDump.print("eventProcessTimeMs: mean=%" PRIu64 ", max=%" PRIu64 "\n", - mEventProcessTime.getMean(), mEventProcessTime.getMax()); + CHRE_EXTRACT_MINOR_VERSION(getTargetApiVersion())); +} + +void Nanoapp::logMemAndComputeHeader(DebugDumpWrapper &debugDump) const { + // Print table header + // Nanoapp column sized to accommodate largest known name + debugDump.print("\n%10sNanoapp%9s| Mem Alloc (Bytes) |%7sEvent Time (Ms)\n", + "", "", ""); + debugDump.print("%26s| Current | Max | Mean | Max | Total\n", + ""); +} + +void Nanoapp::logMemAndComputeEntry(DebugDumpWrapper &debugDump) const { + debugDump.print("%*s |", 25, getAppName()); + debugDump.print(" %*zu |", 7, getTotalAllocatedBytes()); + debugDump.print(" %*zu |", 7, getPeakAllocatedBytes()); + debugDump.print(" %*" PRIu64 " |", 7, mEventProcessTime.getMean()); + debugDump.print(" %*" PRIu64 " |", 7, mEventProcessTime.getMax()); + debugDump.print(" %*" PRIu64 "\n", 7, mEventProcessTimeSinceBoot); +} + +void Nanoapp::logMessageHistoryHeader(DebugDumpWrapper &debugDump) const { + // Print time ranges for buckets + Nanoseconds now = SystemTime::getMonotonicTime(); + uint64_t currentTimeMins = 0; + uint64_t nextTimeMins = 0; + uint64_t nanosecondsSince = 0; + char bucketLabel = 'A'; + + char bucketTags[kMaxSizeWakeupBuckets][4]; + for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) { + bucketTags[i][0] = '['; + bucketTags[i][1] = bucketLabel++; + bucketTags[i][2] = ']'; + bucketTags[i][3] = '\0'; + } + + debugDump.print( + "\nHistogram stat buckets cover the following time ranges:\n"); + + for (int32_t i = kMaxSizeWakeupBuckets - 1; + i > static_cast<int32_t>(mWakeupBuckets.size() - 1); --i) { + debugDump.print(" Bucket%s: N/A (unused)\n", bucketTags[i]); + } + + for (int32_t i = static_cast<int32_t>(mWakeupBuckets.size() - 1); i >= 0; + --i) { + size_t idx = static_cast<size_t>(i); + nanosecondsSince = + now.toRawNanoseconds() - mWakeupBuckets[idx].creationTimestamp; + currentTimeMins = (nanosecondsSince / kOneMinuteInNanoseconds); + + debugDump.print(" Bucket%s:", bucketTags[idx]); + debugDump.print(" %*" PRIu64 "", 3, nextTimeMins); + debugDump.print(" - %*" PRIu64 " mins ago\n", 3, currentTimeMins); + nextTimeMins = currentTimeMins; + } + + int wuHistColWidth = 2 + (4 * kMaxSizeWakeupBuckets); + int messageHistColWidth = 2 + (4 * kMaxSizeWakeupBuckets); + int eventHistColWidth = 2 + (7 * kMaxSizeWakeupBuckets); + + // Print table header + debugDump.print("\n%*s|", 26, " Nanoapp "); + debugDump.print("%*s|", 11, " Total w/u "); + debugDump.print("%*s|", wuHistColWidth, " Wakeup Histogram "); + debugDump.print("%*s|", 12, " Total Msgs "); + debugDump.print("%*s|", messageHistColWidth, " Message Histogram "); + debugDump.print("%*s|", 12, " Event Time "); + debugDump.print("%*s", eventHistColWidth, " Event Time Histogram (ms) "); + + debugDump.print("\n%26s|%11s|", "", ""); + for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) { + debugDump.print(" %*s", 3, bucketTags[i]); + } + debugDump.print(" |%*s|", 12, ""); + for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) { + debugDump.print(" %*s", 3, bucketTags[i]); + } + debugDump.print(" |%*s|", 12, ""); + for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) { + debugDump.print(" %*s", 7, bucketTags[i]); + } + debugDump.print("\n"); +} + +void Nanoapp::logMessageHistoryEntry(DebugDumpWrapper &debugDump) const { + debugDump.print("%*s |", 25, getAppName()); + + // Print wakeupCount and histogram + debugDump.print(" %*" PRIu32 " | ", 9, mNumWakeupsSinceBoot); + for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) { + if (i >= mWakeupBuckets.size()) { + debugDump.print(" --,"); + } else { + debugDump.print(" %*" PRIu16 ",", 2, mWakeupBuckets[i].wakeupCount); + } + } + debugDump.print(" %*" PRIu16 " |", 2, mWakeupBuckets.front().wakeupCount); + + // Print hostMessage count and histogram + debugDump.print(" %*" PRIu32 " | ", 10, mNumMessagesSentSinceBoot); + for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) { + if (i >= mWakeupBuckets.size()) { + debugDump.print(" --,"); + } else { + debugDump.print(" %*" PRIu16 ",", 2, mWakeupBuckets[i].hostMessageCount); + } + } + debugDump.print(" %*" PRIu16 " |", 2, + mWakeupBuckets.front().hostMessageCount); + + // Print eventProcessingTime count and histogram + debugDump.print(" %*" PRIu64 " | ", 10, mEventProcessTimeSinceBoot); + for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) { + if (i >= mWakeupBuckets.size()) { + debugDump.print(" --,"); + } else { + debugDump.print(" %*" PRIu64 ",", 6, mWakeupBuckets[i].eventProcessTime); + } + } + debugDump.print(" %*" PRIu64 "\n", 6, + mWakeupBuckets.front().eventProcessTime); } bool Nanoapp::permitPermissionUse(uint32_t permission) const { diff --git a/core/telemetry_manager.cc b/core/telemetry_manager.cc index 1cb47f07..6772cb5a 100644 --- a/core/telemetry_manager.cc +++ b/core/telemetry_manager.cc @@ -24,7 +24,7 @@ #include "chre/util/macros.h" #include "chre/util/nested_data_ptr.h" #include "chre/util/time.h" -#include "chre_metrics.nanopb.h" +#include "core/chre_metrics.nanopb.h" namespace chre { @@ -42,11 +42,15 @@ namespace { _android_chre_metrics_ChrePalType:: \ android_chre_metrics_ChrePalType_CHRE_PAL_TYPE_##x +// These IDs must be kept in sync with +// hardware/google/pixel/pixelstats/pixelatoms.proto. +constexpr uint32_t kEventQueueSnapshotReportedId = 105035; +constexpr uint32_t kPalOpenedFailedId = 105032; + void sendMetricToHost(uint32_t atomId, const pb_field_t fields[], const void *data) { size_t size; - if (!pb_get_encoded_size(&size, CHREATOMS_GET(ChrePalOpenFailed_fields), - data)) { + if (!pb_get_encoded_size(&size, fields, data)) { LOGE("Failed to get message size"); } else { pb_byte_t *bytes = static_cast<pb_byte_t *>(memoryAlloc(size)); @@ -54,13 +58,12 @@ void sendMetricToHost(uint32_t atomId, const pb_field_t fields[], LOG_OOM(); } else { pb_ostream_t stream = pb_ostream_from_buffer(bytes, size); - if (!pb_encode(&stream, CHREATOMS_GET(ChrePalOpenFailed_fields), data)) { + if (!pb_encode(&stream, fields, data)) { LOGE("Failed to metric error %s", PB_GET_ERROR(&stream)); } else { HostCommsManager &manager = EventLoopManagerSingleton::get()->getHostCommsManager(); - if (!manager.sendMetricLog(CHREATOMS_GET(Atom_chre_pal_open_failed_tag), - bytes, size)) { + if (!manager.sendMetricLog(atomId, bytes, size)) { LOGE("Failed to send metric message"); } } @@ -78,8 +81,8 @@ void sendPalOpenFailedMetric(_android_chre_metrics_ChrePalType pal) { result.type = _android_chre_metrics_ChrePalOpenFailed_Type:: android_chre_metrics_ChrePalOpenFailed_Type_INITIAL_OPEN; - sendMetricToHost(CHREATOMS_GET(Atom_chre_pal_open_failed_tag), - CHREATOMS_GET(ChrePalOpenFailed_fields), &result); + sendMetricToHost(kPalOpenedFailedId, CHREATOMS_GET(ChrePalOpenFailed_fields), + &result); } void sendEventLoopStats(uint32_t maxQueueSize, uint32_t meanQueueSize, @@ -97,7 +100,7 @@ void sendEventLoopStats(uint32_t maxQueueSize, uint32_t meanQueueSize, result.has_num_dropped_events = true; result.num_dropped_events = numDroppedEvents; - sendMetricToHost(CHREATOMS_GET(Atom_chre_event_queue_snapshot_reported_tag), + sendMetricToHost(kEventQueueSnapshotReportedId, CHREATOMS_GET(ChreEventQueueSnapshotReported_fields), &result); } @@ -158,7 +161,7 @@ void TelemetryManager::collectSystemMetrics() { } void TelemetryManager::scheduleMetricTimer() { - constexpr Seconds kDelay = Seconds(60 * 60 * 24); // 24 hours + constexpr Seconds kDelay = Seconds(kOneDayInSeconds); auto callback = [](uint16_t /* eventType */, void * /* data */, void * /* extraData */) { EventLoopManagerSingleton::get() diff --git a/core/tests/ble_request_test.cc b/core/tests/ble_request_test.cc index bc0dbc5c..e8ed98e3 100644 --- a/core/tests/ble_request_test.cc +++ b/core/tests/ble_request_test.cc @@ -30,12 +30,12 @@ TEST(BleRequest, DefaultMinimalRequest) { } TEST(BleRequest, AggressiveModeIsHigherThanBackground) { - BleRequest backgroundMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 0 /* reportDelayMs */, nullptr /* filter */); - BleRequest aggressiveMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_AGGRESSIVE, - 0 /* reportDelayMs */, nullptr /* filter */); + BleRequest backgroundMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); + BleRequest aggressiveMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_AGGRESSIVE, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); BleRequest mergedRequest; EXPECT_TRUE(mergedRequest.mergeWith(aggressiveMode)); @@ -48,14 +48,15 @@ TEST(BleRequest, AggressiveModeIsHigherThanBackground) { } TEST(BleRequest, MergeWithReplacesParametersOfDisabledRequest) { - chreBleScanFilter filter; + chreBleScanFilterV1_9 filter; filter.rssiThreshold = -5; - filter.scanFilterCount = 1; + filter.genericFilterCount = 1; auto scanFilters = std::make_unique<chreBleGenericFilter>(); scanFilters->type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE; scanFilters->len = 2; - filter.scanFilters = scanFilters.get(); - BleRequest enabled(0, true, CHRE_BLE_SCAN_MODE_AGGRESSIVE, 20, &filter); + filter.genericFilters = scanFilters.get(); + BleRequest enabled(0, true, CHRE_BLE_SCAN_MODE_AGGRESSIVE, 20, &filter, + nullptr /* cookie */); BleRequest mergedRequest; EXPECT_FALSE(mergedRequest.isEnabled()); @@ -71,72 +72,84 @@ TEST(BleRequest, MergeWithReplacesParametersOfDisabledRequest) { } TEST(BleRequest, IsEquivalentToBasic) { - BleRequest backgroundMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 0 /* reportDelayMs */, nullptr /* filter */); + BleRequest backgroundMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); EXPECT_TRUE(backgroundMode.isEquivalentTo(backgroundMode)); } TEST(BleRequest, IsNotEquivalentToBasic) { - BleRequest backgroundMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 0 /* reportDelayMs */, nullptr /* filter */); - BleRequest aggressiveMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_AGGRESSIVE, - 0 /* reportDelayMs */, nullptr /* filter */); + BleRequest backgroundMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); + BleRequest aggressiveMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_AGGRESSIVE, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); EXPECT_FALSE(backgroundMode.isEquivalentTo(aggressiveMode)); } +TEST(BleRequest, IsNotEquivalentWithCookie) { + constexpr uint32_t kCookieOne = 123; + constexpr uint32_t kCookieTwo = 234; + BleRequest requestOne(0 /* instanceId */, true /* enable */, + CHRE_BLE_SCAN_MODE_BACKGROUND, 0 /* reportDelayMs */, + nullptr /* filter */, &kCookieOne); + BleRequest requestTwo(0 /* instanceId */, true /* enable */, + CHRE_BLE_SCAN_MODE_BACKGROUND, 0 /* reportDelayMs */, + nullptr /* filter */, &kCookieTwo); + EXPECT_TRUE(requestOne.isEquivalentTo(requestTwo)); +} + TEST(BleRequest, IsEquivalentToAdvanced) { - chreBleScanFilter filter; + chreBleScanFilterV1_9 filter; filter.rssiThreshold = -5; - filter.scanFilterCount = 1; + filter.genericFilterCount = 1; auto scanFilters = std::make_unique<chreBleGenericFilter>(); scanFilters->type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE; scanFilters->len = 4; - filter.scanFilters = scanFilters.get(); + filter.genericFilters = scanFilters.get(); - BleRequest backgroundMode(100 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 100 /* reportDelayMs */, &filter); + BleRequest backgroundMode( + 100 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 100 /* reportDelayMs */, &filter, nullptr /* cookie */); EXPECT_TRUE(backgroundMode.isEquivalentTo(backgroundMode)); } TEST(BleRequest, IsNotEquivalentToAdvanced) { - chreBleScanFilter filter; + chreBleScanFilterV1_9 filter; filter.rssiThreshold = -5; - filter.scanFilterCount = 1; + filter.genericFilterCount = 1; auto scanFilters = std::make_unique<chreBleGenericFilter>(); scanFilters->type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE; scanFilters->len = 4; - filter.scanFilters = scanFilters.get(); + filter.genericFilters = scanFilters.get(); - BleRequest backgroundMode(100 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 100 /* reportDelayMs */, &filter /* filter */); - BleRequest aggressiveMode(0 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_AGGRESSIVE, - 0 /* reportDelayMs */, nullptr /* filter */); + BleRequest backgroundMode( + 100 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 100 /* reportDelayMs */, &filter /* filter */, nullptr /* cookie */); + BleRequest aggressiveMode( + 0 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_AGGRESSIVE, + 0 /* reportDelayMs */, nullptr /* filter */, nullptr /* cookie */); EXPECT_FALSE(backgroundMode.isEquivalentTo(aggressiveMode)); } TEST(BleRequest, GetScanFilter) { - chreBleScanFilter filter; + chreBleScanFilterV1_9 filter; filter.rssiThreshold = -5; - filter.scanFilterCount = 1; + filter.genericFilterCount = 1; auto scanFilters = std::make_unique<chreBleGenericFilter>(); scanFilters->type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE; scanFilters->len = 4; - filter.scanFilters = scanFilters.get(); + filter.genericFilters = scanFilters.get(); - BleRequest backgroundMode(100 /* instanceId */, true /* enable */, - CHRE_BLE_SCAN_MODE_BACKGROUND, - 100 /* reportDelayMs */, &filter /* filter */); + BleRequest backgroundMode( + 100 /* instanceId */, true /* enable */, CHRE_BLE_SCAN_MODE_BACKGROUND, + 100 /* reportDelayMs */, &filter /* filter */, nullptr /* cookie */); - chreBleScanFilter retFilter = backgroundMode.getScanFilter(); + chreBleScanFilterV1_9 retFilter = backgroundMode.getScanFilter(); EXPECT_EQ(filter.rssiThreshold, retFilter.rssiThreshold); - EXPECT_EQ(filter.scanFilterCount, retFilter.scanFilterCount); - EXPECT_EQ(0, memcmp(scanFilters.get(), retFilter.scanFilters, + EXPECT_EQ(filter.genericFilterCount, retFilter.genericFilterCount); + EXPECT_EQ(0, memcmp(scanFilters.get(), retFilter.genericFilters, sizeof(chreBleGenericFilter))); } diff --git a/core/tests/memory_manager_test.cc b/core/tests/memory_manager_test.cc index 69e07a08..b7b151ce 100644 --- a/core/tests/memory_manager_test.cc +++ b/core/tests/memory_manager_test.cc @@ -16,10 +16,12 @@ #include "gtest/gtest.h" +#include "chre/core/event.h" #include "chre/platform/log.h" #include "chre/platform/memory.h" #include "chre/platform/memory_manager.h" +using chre::kInvalidInstanceId; using chre::MemoryManager; using chre::Nanoapp; @@ -36,7 +38,7 @@ TEST(MemoryManager, DefaultTotalMemoryAllocatedIsZero) { TEST(MemoryManager, BasicAllocationFree) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); void *ptr = manager.nanoappAlloc(&app, 1u); EXPECT_NE(ptr, nullptr); EXPECT_EQ(manager.getTotalAllocatedBytes(), 1u); @@ -48,7 +50,7 @@ TEST(MemoryManager, BasicAllocationFree) { TEST(MemoryManager, NullPointerFree) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); manager.nanoappFree(&app, nullptr); EXPECT_EQ(manager.getTotalAllocatedBytes(), 0u); EXPECT_EQ(manager.getAllocationCount(), 0u); @@ -56,7 +58,7 @@ TEST(MemoryManager, NullPointerFree) { TEST(MemoryManager, ZeroAllocationFails) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); void *ptr = manager.nanoappAlloc(&app, 0u); EXPECT_EQ(ptr, nullptr); EXPECT_EQ(manager.getTotalAllocatedBytes(), 0u); @@ -65,7 +67,7 @@ TEST(MemoryManager, ZeroAllocationFails) { TEST(MemoryManager, HugeAllocationFails) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); void *ptr = manager.nanoappAlloc(&app, manager.getMaxAllocationBytes() + 1); EXPECT_EQ(ptr, nullptr); EXPECT_EQ(manager.getTotalAllocatedBytes(), 0u); @@ -73,7 +75,7 @@ TEST(MemoryManager, HugeAllocationFails) { TEST(MemoryManager, ManyAllocationsTest) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); size_t maxCount = manager.getMaxAllocationCount(); node *head = static_cast<node *>(manager.nanoappAlloc(&app, sizeof(node))); node *curr = nullptr, *prev = head; @@ -99,7 +101,7 @@ TEST(MemoryManager, ManyAllocationsTest) { TEST(MemoryManager, NegativeAllocationFails) { MemoryManager manager; - Nanoapp app; + Nanoapp app(kInvalidInstanceId); void *ptr = manager.nanoappAlloc(&app, -1u); EXPECT_EQ(ptr, nullptr); EXPECT_EQ(manager.getTotalAllocatedBytes(), 0u); diff --git a/core/timer_pool.cc b/core/timer_pool.cc index 2db3a296..521300c3 100644 --- a/core/timer_pool.cc +++ b/core/timer_pool.cc @@ -81,16 +81,22 @@ TimerHandle TimerPool::setTimer(uint16_t instanceId, Nanoseconds duration, timerRequest.callbackType = callbackType; timerRequest.isOneShot = isOneShot; - bool newTimerExpiresEarliest = - (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest); bool success = insertTimerRequestLocked(timerRequest); if (success) { - if (newTimerExpiresEarliest) { - mSystemTimer.set(handleSystemTimerCallback, this, duration); - } else if (mTimerRequests.size() == 1) { + if (mTimerRequests.size() == 1) { // If this timer request was the first, schedule it. handleExpiredTimersAndScheduleNextLocked(); + } else { + // If there was already a timer pending before this, and we just inserted + // to the top of the queue, just update the system timer. This is slightly + // more efficient than calling into + // handleExpiredTimersAndScheduleNextLocked(). + bool newRequestExpiresFirst = + timerRequest.timerHandle == mTimerRequests.top().timerHandle; + if (newRequestExpiresFirst) { + mSystemTimer.set(handleSystemTimerCallback, this, duration); + } } } @@ -132,7 +138,7 @@ TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandleLocked( } bool TimerPool::TimerRequest::operator>(const TimerRequest &request) const { - return (expirationTime > request.expirationTime); + return expirationTime > request.expirationTime; } TimerHandle TimerPool::generateTimerHandleLocked() { @@ -264,7 +270,7 @@ bool TimerPool::handleExpiredTimersAndScheduleNextLocked() { // insert operation (thereby invalidating it). TimerRequest cyclicTimerRequest = currentTimerRequest; cyclicTimerRequest.expirationTime = - currentTime + currentTimerRequest.duration; + currentTimerRequest.expirationTime + currentTimerRequest.duration; popTimerRequestLocked(); CHRE_ASSERT(insertTimerRequestLocked(cyclicTimerRequest)); } else { diff --git a/doc/porting_guide.md b/doc/porting_guide.md index 1c960b74..7fa43455 100644 --- a/doc/porting_guide.md +++ b/doc/porting_guide.md @@ -51,7 +51,7 @@ Platform-specific code can be further subdivided into: `platform_sensor_base.h` respectively, or required macros * **Fully platform-specific headers**: these typically appear at - `platform/<platform_name>/include/chre/platform/<platform_name/<file_name>.h` + `platform/<platform_name>/include/chre/platform/<platform_name>/<file_name>.h` and may only be included by other platform-specific code ## Open Sourcing diff --git a/external/pigweed/pw_rpc.mk b/external/pigweed/pw_rpc.mk deleted file mode 100644 index 35fb25f5..00000000 --- a/external/pigweed/pw_rpc.mk +++ /dev/null @@ -1,241 +0,0 @@ -# -# Makefile for Pigweed's RPC module -# -# NOTE: In order to use this, you *must* have the following: -# - Installed mypy-protobuf and protoc -# - nanopb-c git repo checked out -# - -ifneq ($(PW_RPC_SRCS),) - -# Environment Checks ########################################################### - -# Location of various Pigweed modules -PIGWEED_DIR = $(ANDROID_BUILD_TOP)/external/pigweed -CHRE_PREFIX = $(ANDROID_BUILD_TOP)/system/chre -CHRE_UTIL_DIR = $(CHRE_PREFIX)/util -CHRE_API_DIR = $(CHRE_PREFIX)/chre_api -PIGWEED_CHRE_DIR=$(CHRE_PREFIX)/external/pigweed -PIGWEED_CHRE_UTIL_DIR = $(CHRE_UTIL_DIR)/pigweed - -ifeq ($(NANOPB_PREFIX),) -$(error "PW_RPC_SRCS is non-empty. You must supply a NANOPB_PREFIX environment \ - variable containing a path to the nanopb project. Example: \ - export NANOPB_PREFIX=$$HOME/path/to/nanopb/nanopb-c") -endif - -ifeq ($(PROTOC),) -PROTOC=protoc -endif - -PW_RPC_GEN_PATH = $(OUT)/pw_rpc_gen - -# Create proto used for header generation ###################################### - -PW_RPC_PROTO_GENERATOR = $(PIGWEED_DIR)/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py -PW_RPC_GENERATOR_PROTO = $(PIGWEED_DIR)/pw_rpc/internal/packet.proto -PW_RPC_GENERATOR_COMPILED_PROTO = $(PW_RPC_GEN_PATH)/py/pw_rpc/internal/packet_pb2.py -PW_PROTOBUF_PROTOS = $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/common.proto \ - $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/field_options.proto \ - $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos/status.proto - -# Modifies PYTHONPATH so that python can see all of pigweed's modules used by -# their protoc plugins -PW_RPC_GENERATOR_CMD = PYTHONPATH=$$PYTHONPATH:$(PW_RPC_GEN_PATH)/py:$\ - $(PIGWEED_DIR)/pw_status/py:$(PIGWEED_DIR)/pw_protobuf/py:$\ - $(PIGWEED_DIR)/pw_protobuf_compiler/py $(PYTHON) - -$(PW_RPC_GENERATOR_COMPILED_PROTO): $(PW_RPC_GENERATOR_PROTO) - @echo " [PW_RPC] $<" - $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_rpc/internal - $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_protobuf_codegen_protos - $(V)mkdir -p $(PW_RPC_GEN_PATH)/py/pw_protobuf_protos - $(V)cp -R $(PIGWEED_DIR)/pw_rpc/py/pw_rpc $(PW_RPC_GEN_PATH)/py/ - - $(PROTOC) -I$(PIGWEED_DIR)/pw_protobuf/pw_protobuf_protos \ - --experimental_allow_proto3_optional \ - --python_out=$(PW_RPC_GEN_PATH)/py/pw_protobuf_protos \ - $(PW_PROTOBUF_PROTOS) - - $(PROTOC) -I$(PIGWEED_DIR)/pw_protobuf/pw_protobuf_codegen_protos \ - --experimental_allow_proto3_optional \ - --python_out=$(PW_RPC_GEN_PATH)/py/pw_protobuf_codegen_protos \ - $(PIGWEED_DIR)/pw_protobuf/pw_protobuf_codegen_protos/codegen_options.proto - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) --out-dir=$(PW_RPC_GEN_PATH)/py/pw_rpc/internal \ - --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \ - --language python - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) \ - --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ - --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \ - --language pwpb - -# Generated PW RPC Files ####################################################### - -PW_RPC_GEN_SRCS = $(patsubst %.proto, \ - $(PW_RPC_GEN_PATH)/%.pb.c, \ - $(PW_RPC_SRCS)) - -# Include to-be-generated files -COMMON_CFLAGS += -I$(PW_RPC_GEN_PATH) -COMMON_CFLAGS += -I$(PW_RPC_GEN_PATH)/$(PIGWEED_DIR) -COMMON_CFLAGS += $(addprefix -I$(PW_RPC_GEN_PATH)/, $(abspath $(dir $(PW_RPC_SRCS)))) - -COMMON_SRCS += $(PW_RPC_GEN_SRCS) - -# PW RPC library ############################################################### - -# Pigweed RPC include paths -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_assert/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_bytes/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_containers/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_function/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_log/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public_overrides -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/standard_library_public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_protobuf/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_result/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/nanopb/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/pwpb/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_rpc/raw/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public_overrides -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_status/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_stream/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_string/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_sync/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_toolchain/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_varint/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/fit/include -COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/stdcompat/include - -# Pigweed RPC sources -COMMON_SRCS += $(PIGWEED_DIR)/pw_assert_log/assert_log.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_containers/intrusive_list.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/decoder.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/encoder.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/stream_decoder.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/call.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/channel.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/channel_list.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/client.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/client_call.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/endpoint.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/packet.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/server.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/server_call.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/service.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/common.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/method.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/nanopb/server_reader_writer.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_rpc/pwpb/server_reader_writer.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_stream/memory_stream.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/stream.cc -COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/varint.cc - -# NanoPB header includes -COMMON_CFLAGS += -I$(NANOPB_PREFIX) - -COMMON_CFLAGS += -DPW_RPC_USE_GLOBAL_MUTEX=0 -COMMON_CFLAGS += -DPW_RPC_YIELD_MODE=PW_RPC_YIELD_MODE_BUSY_LOOP - -# Enable closing a client stream. -COMMON_CFLAGS += -DPW_RPC_CLIENT_STREAM_END_CALLBACK - - -# Use dynamic channel allocation -COMMON_CFLAGS += -DPW_RPC_DYNAMIC_ALLOCATION -COMMON_CFLAGS += -DPW_RPC_DYNAMIC_CONTAINER\(type\)="chre::DynamicVector<type>" -COMMON_CFLAGS += -DPW_RPC_DYNAMIC_CONTAINER_INCLUDE='"chre/util/dynamic_vector.h"' - -# NanoPB sources -COMMON_SRCS += $(NANOPB_PREFIX)/pb_common.c -COMMON_SRCS += $(NANOPB_PREFIX)/pb_decode.c -COMMON_SRCS += $(NANOPB_PREFIX)/pb_encode.c - -COMMON_CFLAGS += -DPB_NO_PACKED_STRUCTS=1 - -# Add CHRE Pigweed util sources since nanoapps should always use these -COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/chre_channel_output.cc -COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_client.cc -COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_helper.cc -COMMON_SRCS += $(PIGWEED_CHRE_UTIL_DIR)/rpc_server.cc -COMMON_SRCS += $(CHRE_UTIL_DIR)/nanoapp/callbacks.cc -COMMON_SRCS += $(CHRE_UTIL_DIR)/dynamic_vector_base.cc - -# CHRE Pigweed overrides -COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_log_nanoapp/public_overrides -COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_assert_nanoapp/public_overrides - -# Generate PW RPC headers ###################################################### - -$(PW_RPC_GEN_PATH)/%.pb.c \ - $(PW_RPC_GEN_PATH)/%.pb.h \ - $(PW_RPC_GEN_PATH)/%.rpc.pb.h \ - $(PW_RPC_GEN_PATH)/%.raw_rpc.pb.h: %.proto \ - %.options \ - $(NANOPB_GENERATOR_SRCS) \ - $(PW_RPC_GENERATOR_COMPILED_PROTO) - @echo " [PW_RPC] $<" - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(NANOPB_PROTOC) \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \ - --sources $< - -$(PW_RPC_GEN_PATH)/%.pb.c \ - $(PW_RPC_GEN_PATH)/%.pb.h \ - $(PW_RPC_GEN_PATH)/%.rpc.pb.h \ - $(PW_RPC_GEN_PATH)/%.raw_rpc.pb.h: %.proto \ - $(NANOPB_OPTIONS) \ - $(NANOPB_GENERATOR_SRCS) \ - $(PW_RPC_GENERATOR_COMPILED_PROTO) - @echo " [PW_RPC] $<" - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(NANOPB_PROTOC) \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \ - --sources $< - - $(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \ - --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \ - --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \ - --sources $< -endif
\ No newline at end of file diff --git a/external/pigweed/pw_tokenizer.mk b/external/pigweed/pw_tokenizer.mk index 15b5c5a4..e214e6ba 100644 --- a/external/pigweed/pw_tokenizer.mk +++ b/external/pigweed/pw_tokenizer.mk @@ -24,13 +24,17 @@ TOKEN_MAP_CSV_GEN_CMD = $(PYTHON) $(PIGWEED_SCRIPTS_DIR)/database.py create \ COMMON_SRCS += $(PIGWEED_DIR)/pw_tokenizer/encode_args.cc COMMON_SRCS += $(PIGWEED_DIR)/pw_tokenizer/tokenize.cc COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/varint.cc +COMMON_SRCS += $(PIGWEED_DIR)/pw_varint/varint_c.c # Pigweed include paths COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_containers/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_log_tokenized/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_log/public COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/standard_library_public COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public_overrides COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_tokenizer/public -COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_varint/public/ +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_varint/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/stdcompat/include
\ No newline at end of file diff --git a/external/pigweed/pw_trace.mk b/external/pigweed/pw_trace.mk new file mode 100644 index 00000000..1a8328bb --- /dev/null +++ b/external/pigweed/pw_trace.mk @@ -0,0 +1,17 @@ +# +# Makefile for Pigweed's trace module +# + +# Environment Checks +ifeq ($(ANDROID_BUILD_TOP),) +$(error "You should supply an ANDROID_BUILD_TOP environment variable \ + containing a path to the Android source tree. This is typically \ + provided by initializing the Android build environment.") +endif + +# Location of Pigweed +PIGWEED_DIR = $(ANDROID_BUILD_TOP)/external/pigweed + +# Pigweed include paths +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_trace/public +COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public
\ No newline at end of file diff --git a/host/common/chre_aidl_hal_client.cc b/host/common/chre_aidl_hal_client.cc index eb2f425e..d4a60eab 100644 --- a/host/common/chre_aidl_hal_client.cc +++ b/host/common/chre_aidl_hal_client.cc @@ -30,26 +30,30 @@ #include <regex> #include <stdexcept> #include <string> +#include <unordered_set> #include <vector> #include "chre_api/chre/version.h" #include "chre_host/file_stream.h" +#include "chre_host/hal_client.h" #include "chre_host/napp_header.h" -using aidl::android::hardware::contexthub::AsyncEventType; -using aidl::android::hardware::contexthub::BnContextHubCallback; -using aidl::android::hardware::contexthub::ContextHubInfo; -using aidl::android::hardware::contexthub::ContextHubMessage; -using aidl::android::hardware::contexthub::HostEndpointInfo; -using aidl::android::hardware::contexthub::IContextHub; -using aidl::android::hardware::contexthub::NanoappBinary; -using aidl::android::hardware::contexthub::NanoappInfo; -using aidl::android::hardware::contexthub::NanSessionRequest; -using aidl::android::hardware::contexthub::Setting; -using android::chre::NanoAppBinaryHeader; -using android::chre::readFileContents; -using android::internal::ToString; -using ndk::ScopedAStatus; +using ::aidl::android::hardware::contexthub::AsyncEventType; +using ::aidl::android::hardware::contexthub::BnContextHubCallback; +using ::aidl::android::hardware::contexthub::ContextHubInfo; +using ::aidl::android::hardware::contexthub::ContextHubMessage; +using ::aidl::android::hardware::contexthub::HostEndpointInfo; +using ::aidl::android::hardware::contexthub::IContextHub; +using ::aidl::android::hardware::contexthub::MessageDeliveryStatus; +using ::aidl::android::hardware::contexthub::NanoappBinary; +using ::aidl::android::hardware::contexthub::NanoappInfo; +using ::aidl::android::hardware::contexthub::NanSessionRequest; +using ::aidl::android::hardware::contexthub::Setting; +using ::android::chre::HalClient; +using ::android::chre::NanoAppBinaryHeader; +using ::android::chre::readFileContents; +using ::android::internal::ToString; +using ::ndk::ScopedAStatus; namespace { // A default id 0 is used for every command requiring a context hub id. When @@ -58,7 +62,17 @@ namespace { constexpr uint32_t kContextHubId = 0; constexpr int32_t kLoadTransactionId = 1; constexpr int32_t kUnloadTransactionId = 2; + +// Though IContextHub.aidl says loading operation is capped at 30s to finish, +// multiclient HAL can terminate a load/unload transaction after 5s to avoid +// blocking other load/unload transactions. constexpr auto kTimeOutThresholdInSec = std::chrono::seconds(5); + +// 34a3a27e-9b83-4098-b564-e83b0c28d4bb +constexpr std::array<uint8_t, 16> kUuid = {0x34, 0xa3, 0xa2, 0x7e, 0x9b, 0x83, + 0x40, 0x98, 0xb5, 0x64, 0xe8, 0x3b, + 0x0c, 0x28, 0xd4, 0xbb}; + // Locations should be searched in the sequence defined below: const char *kPredefinedNanoappPaths[] = { "/vendor/etc/chre/", @@ -66,41 +80,8 @@ const char *kPredefinedNanoappPaths[] = { "/vendor/dsp/sdsp/", "/vendor/lib/rfsa/adsp/", }; -// Please keep kUsage in alphabetical order -constexpr char kUsage[] = R"( -Usage: chre_aidl_hal_client COMMAND [ARGS] -COMMAND ARGS...: - connect - connect to HAL, register the callback and keep - the session alive while user can execute other - commands. Use `exit` to quit the session. - connectEndpoint <HEX_HOST_ENDPOINT_ID> - - associate an endpoint with the current client - and notify HAL. - disableSetting <SETTING> - disable a setting identified by a number defined - in android/hardware/contexthub/Setting.aidl. - disableTestMode - disable test mode. - disconnectEndpoint <HEX_HOST_ENDPOINT_ID> - - remove an endpoint with the current client and - notify HAL. - enableSetting <SETTING> - enable a setting identified by a number defined - in android/hardware/contexthub/Setting.aidl. - enableTestMode - enable test mode. - getContextHubs - get all the context hubs. - list <PATH_OF_NANOAPPS> - list all the nanoapps' header info in the path. - load <APP_NAME> - load the nanoapp specified by the name. - If an absolute path like /path/to/awesome.so, - which is optional, is not provided then default - locations are searched. - query - show all loaded nanoapps (system apps excluded) - sendMessage <HEX_HOST_ENDPOINT_ID> <HEX_NANOAPP_ID | APP_NAME> <HEX_PAYLOAD> - - send a payload to a nanoapp. - unload <HEX_NANOAPP_ID | APP_NAME> - - unload the nanoapp specified by either the - nanoapp id in hex format or the app name. - If an absolute path like /path/to/awesome.so, - which is optional, is not provided then default - locations are searched. -)"; + +const std::string kClientName{"ChreAidlHalClient"}; inline void throwError(const std::string &message) { throw std::system_error{std::error_code(), message}; @@ -176,29 +157,30 @@ class ContextHubCallback : public BnContextHubCallback { << "\n\trpcServices: " << ToString(app.rpcServices) << "\n}" << std::endl; } - setPromiseAndRefresh(); + resetPromise(); return ScopedAStatus::ok(); } ScopedAStatus handleContextHubMessage( const ContextHubMessage &message, const std::vector<std::string> & /*msgContentPerms*/) override { - std::cout << "Received a message with type " << message.messageType - << " size " << message.messageBody.size() << " from nanoapp 0x" - << std::hex << message.nanoappId - << " sent to the host endpoint 0x" - << static_cast<int>(message.hostEndPoint) << std::endl; - std::cout << "message: 0x"; + std::cout << "Received a message!" << std::endl + << " From: 0x" << std::hex << message.nanoappId << std::endl + << " To: 0x" << static_cast<int>(message.hostEndPoint) + << std::endl + << " Body: (type " << message.messageType << " size " + << message.messageBody.size() << ") 0x"; for (const uint8_t &data : message.messageBody) { - std::cout << std::hex << static_cast<uint32_t>(data); + std::cout << std::hex << static_cast<uint16_t>(data); } - std::cout << std::endl; - setPromiseAndRefresh(); + std::cout << std::endl << std::endl; + resetPromise(); return ScopedAStatus::ok(); } - ScopedAStatus handleContextHubAsyncEvent(AsyncEventType /*event*/) override { - setPromiseAndRefresh(); + ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override { + std::cout << "Received async event " << toString(event) << std::endl; + resetPromise(); return ScopedAStatus::ok(); } @@ -207,27 +189,66 @@ class ContextHubCallback : public BnContextHubCallback { bool success) override { std::cout << parseTransactionId(transactionId) << " transaction is " << (success ? "successful" : "failed") << std::endl; - setPromiseAndRefresh(); + resetPromise(); return ScopedAStatus::ok(); } ScopedAStatus handleNanSessionRequest( const NanSessionRequest & /* request */) override { + resetPromise(); return ScopedAStatus::ok(); } - std::promise<void> promise; + ScopedAStatus handleMessageDeliveryStatus( + char16_t /* hostEndPointId */, + const MessageDeliveryStatus & /* messageDeliveryStatus */) override { + resetPromise(); + return ScopedAStatus::ok(); + } + + ScopedAStatus getUuid(std::array<uint8_t, 16> *out_uuid) override { + *out_uuid = kUuid; + return ScopedAStatus::ok(); + } + + ScopedAStatus getName(std::string *out_name) override { + *out_name = kClientName; + return ScopedAStatus::ok(); + } - private: - void setPromiseAndRefresh() { + void resetPromise() { promise.set_value(); promise = std::promise<void>{}; } + + // TODO(b/247124878): + // This promise is shared among all the HAL callbacks to simplify the + // implementation. This is based on the assumption that every command should + // get a response before timeout and the first callback triggered is for the + // response. + // + // In very rare cases, however, the assumption doesn't hold: + // - multiple callbacks are triggered by a command and come back out of order + // - one command is timed out and the user typed in another command then the + // first callback for the first command is triggered + // Once we have a chance we should consider refactor this design to let each + // callback use their specific promises. + std::promise<void> promise; }; std::shared_ptr<IContextHub> gContextHub = nullptr; std::shared_ptr<ContextHubCallback> gCallback = nullptr; +void registerHostCallback() { + if (gCallback != nullptr) { + gCallback.reset(); + } + gCallback = ContextHubCallback::make<ContextHubCallback>(); + if (!gContextHub->registerCallback(kContextHubId, gCallback).isOk()) { + throwError("Failed to register the callback"); + } +} + /** Initializes gContextHub and register gCallback. */ std::shared_ptr<IContextHub> getContextHub() { if (gContextHub == nullptr) { @@ -238,11 +259,9 @@ std::shared_ptr<IContextHub> getContextHub() { throwError("Could not find Context Hub HAL"); } gContextHub = IContextHub::fromBinder(binder); - gCallback = ContextHubCallback::make<ContextHubCallback>(); - - if (!gContextHub->registerCallback(kContextHubId, gCallback).isOk()) { - throwError("Failed to register the callback"); - } + } + if (gCallback == nullptr) { + registerHostCallback(); } return gContextHub; } @@ -304,6 +323,7 @@ void readNanoappHeaders(std::map<std::string, NanoAppBinaryHeader> &nanoapps, void verifyStatus(const std::string &operation, const ScopedAStatus &status) { if (!status.isOk()) { + gCallback->resetPromise(); throwError(operation + " fails with abnormal status " + ToString(status.getMessage()) + " error code " + ToString(status.getServiceSpecificError())); @@ -317,6 +337,7 @@ void verifyStatusAndSignal(const std::string &operation, std::future_status future_status = future_signal.wait_for(kTimeOutThresholdInSec); if (future_status != std::future_status::ready) { + gCallback->resetPromise(); throwError(operation + " doesn't finish within " + ToString(kTimeOutThresholdInSec.count()) + " seconds"); } @@ -359,7 +380,6 @@ std::unique_ptr<NanoAppBinaryHeader> findHeaderAndNormalizePath( continue; } pathAndName = predefinedPath + appName + ".so"; - std::cout << "Found the nanoapp header for " << pathAndName << std::endl; return result; } throwError("Unable to find the nanoapp header for " + pathAndName); @@ -435,19 +455,23 @@ void queryNanoapps() { gCallback->promise.get_future()); } -void onEndpointConnected(const std::string &hexEndpointId) { - auto contextHub = getContextHub(); +HostEndpointInfo createHostEndpointInfo(const std::string &hexEndpointId) { uint16_t hostEndpointId = verifyAndConvertEndpointHexId(hexEndpointId); - HostEndpointInfo info = { + return { .hostEndpointId = hostEndpointId, .type = HostEndpointInfo::Type::NATIVE, .packageName = "chre_aidl_hal_client", .attributionTag{}, }; +} + +void onEndpointConnected(const std::string &hexEndpointId) { + auto contextHub = getContextHub(); + HostEndpointInfo info = createHostEndpointInfo(hexEndpointId); // connect the endpoint to HAL verifyStatus(/* operation= */ "connect endpoint", contextHub->onHostEndpointConnected(info)); - std::cout << "onHostEndpointConnected() is called. " << std::endl; + std::cout << "Connected." << std::endl; } void onEndpointDisconnected(const std::string &hexEndpointId) { @@ -456,13 +480,12 @@ void onEndpointDisconnected(const std::string &hexEndpointId) { // disconnect the endpoint from HAL verifyStatus(/* operation= */ "disconnect endpoint", contextHub->onHostEndpointDisconnected(hostEndpointId)); - std::cout << "onHostEndpointDisconnected() is called. " << std::endl; + std::cout << "Disconnected." << std::endl; } -/** Sends a hexPayload from hexHostEndpointId to appIdOrName. */ -void sendMessageToNanoapp(const std::string &hexHostEndpointId, - std::string &appIdOrName, - const std::string &hexPayload) { +ContextHubMessage createContextHubMessage(const std::string &hexHostEndpointId, + std::string &appIdOrName, + const std::string &hexPayload) { if (!isValidHexNumber(hexPayload)) { throwError("Invalid hex payload."); } @@ -479,13 +502,20 @@ void sendMessageToNanoapp(const std::string &hexHostEndpointId, contextHubMessage.messageBody.push_back( std::stoi(hexPayload.substr(i, 2), /* idx= */ nullptr, /* base= */ 16)); } + return contextHubMessage; +} + +/** Sends a hexPayload from hexHostEndpointId to appIdOrName. */ +void sendMessageToNanoapp(const std::string &hexHostEndpointId, + std::string &appIdOrName, + const std::string &hexPayload) { + ContextHubMessage contextHubMessage = + createContextHubMessage(hexHostEndpointId, appIdOrName, hexPayload); // send the message auto contextHub = getContextHub(); - onEndpointConnected(hexHostEndpointId); auto status = contextHub->sendMessageToHub(kContextHubId, contextHubMessage); verifyStatusAndSignal(/* operation= */ "sending a message to " + appIdOrName, status, gCallback->promise.get_future()); - onEndpointDisconnected(hexHostEndpointId); } void changeSetting(const std::string &setting, bool enabled) { @@ -504,14 +534,23 @@ void changeSetting(const std::string &setting, bool enabled) { void enableTestModeOnContextHub() { auto status = getContextHub()->setTestMode(true); - verifyStatusAndSignal(/* operation= */ "enabling test mode", status, - gCallback->promise.get_future()); + verifyStatus(/* operation= */ "enabling test mode", status); + std::cout << "Test mode is enabled" << std::endl; } void disableTestModeOnContextHub() { auto status = getContextHub()->setTestMode(false); - verifyStatusAndSignal(/* operation= */ "disabling test mode", status, - gCallback->promise.get_future()); + verifyStatus(/* operation= */ "disabling test mode", status); + std::cout << "Test mode is disabled" << std::endl; +} + +void getAllPreloadedNanoappIds() { + std::vector<int64_t> appIds{}; + verifyStatus("get preloaded nanoapp ids", + getContextHub()->getPreloadedNanoappIds(kContextHubId, &appIds)); + for (const auto &appId : appIds) { + std::cout << "0x" << std::hex << appId << std::endl; + } } // Please keep Command in alphabetical order @@ -524,9 +563,11 @@ enum Command { enableSetting, enableTestMode, getContextHubs, + getPreloadedNanoappIds, list, load, query, + registerCallback, sendMessage, unload, unsupported @@ -534,34 +575,137 @@ enum Command { struct CommandInfo { Command cmd; - u_int8_t numofArgs; // including cmd; + u_int8_t numOfArgs; // including cmd; + std::string argsFormat; + std::string usage; }; -Command parseCommand(const std::vector<std::string> &cmdLine) { - std::map<std::string, CommandInfo> commandMap{ - {"connect", {connect, 1}}, - {"connectEndpoint", {connectEndpoint, 2}}, - {"disableSetting", {disableSetting, 2}}, - {"disableTestMode", {disableTestMode, 1}}, - {"disconnectEndpoint", {disconnectEndpoint, 2}}, - {"enableSetting", {enableSetting, 2}}, - {"enableTestMode", {enableTestMode, 1}}, - {"getContextHubs", {getContextHubs, 1}}, - {"list", {list, 2}}, - {"load", {load, 2}}, - {"query", {query, 1}}, - {"sendMessage", {sendMessage, 4}}, - {"unload", {unload, 2}}, - }; - if (cmdLine.empty() || commandMap.find(cmdLine[0]) == commandMap.end()) { +const std::map<std::string, CommandInfo> kAllCommands{ + {"connect", + {.cmd = connect, + .numOfArgs = 1, + .argsFormat = "", + .usage = "connect to HAL using hal_client library and keep the session " + "alive while user can execute other commands. Use 'exit' to " + "quit the session."}}, + {"connectEndpoint", + {.cmd = connectEndpoint, + .numOfArgs = 2, + .argsFormat = "<HEX_ENDPOINT_ID>", + .usage = + "associate an endpoint with the current client and notify HAL."}}, + {"disableSetting", + {.cmd = disableSetting, + .numOfArgs = 2, + .argsFormat = "<SETTING>", + .usage = "disable a setting identified by a number defined in " + "android/hardware/contexthub/Setting.aidl."}}, + {"disableTestMode", + {.cmd = disableTestMode, + .numOfArgs = 1, + .argsFormat = "", + .usage = "disable test mode."}}, + {"disconnectEndpoint", + {.cmd = disconnectEndpoint, + .numOfArgs = 2, + .argsFormat = "<HEX_ENDPOINT_ID>", + .usage = "remove an endpoint with the current client and notify HAL."}}, + {"enableSetting", + {.cmd = enableSetting, + .numOfArgs = 2, + .argsFormat = "<SETTING>", + .usage = "enable a setting identified by a number defined in " + "android/hardware/contexthub/Setting.aidl."}}, + {"enableTestMode", + {.cmd = enableTestMode, + .numOfArgs = 1, + .argsFormat = "", + .usage = "enable test mode."}}, + {"getContextHubs", + {.cmd = getContextHubs, + .numOfArgs = 1, + .argsFormat = "", + .usage = "get all the context hubs."}}, + {"getPreloadedNanoappIds", + {.cmd = getPreloadedNanoappIds, + .numOfArgs = 1, + .argsFormat = "", + .usage = "get a list of ids for the preloaded nanoapps."}}, + {"list", + {.cmd = list, + .numOfArgs = 2, + .argsFormat = "</PATH/TO/NANOAPPS>", + .usage = "list all the nanoapps' header info in the path."}}, + {"load", + {.cmd = load, + .numOfArgs = 2, + .argsFormat = "<APP_NAME | /PATH/TO/APP_NAME>", + .usage = "load the nanoapp specified by the name. If an absolute path is " + "not provided the default locations are searched."}}, + {"query", + {.cmd = query, + .numOfArgs = 1, + .argsFormat = "", + .usage = "show all loaded nanoapps (system apps excluded)."}}, + {"registerCallback", + {.cmd = registerCallback, + .numOfArgs = 1, + .argsFormat = "", + .usage = "register a callback for the current client."}}, + {"sendMessage", + {.cmd = sendMessage, + .numOfArgs = 4, + .argsFormat = "<HEX_ENDPOINT_ID> <HEX_NANOAPP_ID | APP_NAME | " + "/PATH/TO/APP_NAME> <HEX_PAYLOAD>", + .usage = "send a payload to a nanoapp. If an absolute path is not " + "provided the default locations are searched."}}, + {"unload", + {.cmd = unload, + .numOfArgs = 2, + .argsFormat = "<HEX_NANOAPP_ID | APP_NAME | /PATH/TO/APP_NAME>", + .usage = "unload the nanoapp specified by either the nanoapp id or the " + "app name. If an absolute path is not provided the default " + "locations are searched."}}, +}; + +void fillSupportedCommandMap( + const std::unordered_set<std::string> &supportedCommands, + std::map<std::string, CommandInfo> &supportedCommandMap) { + std::copy_if(kAllCommands.begin(), kAllCommands.end(), + std::inserter(supportedCommandMap, supportedCommandMap.begin()), + [&](auto const &kv_pair) { + return supportedCommands.find(kv_pair.first) != + supportedCommands.end(); + }); +} + +void printUsage(const std::map<std::string, CommandInfo> &supportedCommands) { + constexpr uint32_t kCommandLength = 40; + std::cout << std::left << "Usage: COMMAND [ARGUMENTS]" << std::endl; + for (auto const &kv_pair : supportedCommands) { + std::string cmdLine = kv_pair.first + " " + kv_pair.second.argsFormat; + std::cout << std::setw(kCommandLength) << cmdLine; + if (cmdLine.size() > kCommandLength) { + std::cout << std::endl << std::string(kCommandLength, ' '); + } + std::cout << " - " + kv_pair.second.usage << std::endl; + } + std::cout << std::endl; +} + +Command parseCommand( + const std::vector<std::string> &cmdLine, + const std::map<std::string, CommandInfo> &supportedCommandMap) { + if (cmdLine.empty() || + supportedCommandMap.find(cmdLine[0]) == supportedCommandMap.end()) { return unsupported; } - auto cmdInfo = commandMap.at(cmdLine[0]); - return cmdLine.size() == cmdInfo.numofArgs ? cmdInfo.cmd : unsupported; + auto cmdInfo = supportedCommandMap.at(cmdLine[0]); + return cmdLine.size() == cmdInfo.numOfArgs ? cmdInfo.cmd : unsupported; } void executeCommand(std::vector<std::string> cmdLine) { - switch (parseCommand(cmdLine)) { + switch (parseCommand(cmdLine, kAllCommands)) { case connectEndpoint: { onEndpointConnected(cmdLine[1]); break; @@ -590,6 +734,10 @@ void executeCommand(std::vector<std::string> cmdLine) { getAllContextHubs(); break; } + case getPreloadedNanoappIds: { + getAllPreloadedNanoappIds(); + break; + } case list: { std::map<std::string, NanoAppBinaryHeader> nanoapps{}; readNanoappHeaders(nanoapps, cmdLine[1]); @@ -607,6 +755,10 @@ void executeCommand(std::vector<std::string> cmdLine) { queryNanoapps(); break; } + case registerCallback: { + registerHostCallback(); + break; + } case sendMessage: { sendMessageToNanoapp(cmdLine[1], cmdLine[2], cmdLine[3]); break; @@ -616,7 +768,7 @@ void executeCommand(std::vector<std::string> cmdLine) { break; } default: - std::cout << kUsage; + printUsage(kAllCommands); } } @@ -642,22 +794,63 @@ std::vector<std::string> getCommandLine() { } void connectToHal() { - auto hub = getContextHub(); - std::cout << "Connected to context hub." << std::endl; + if (gCallback == nullptr) { + gCallback = ContextHubCallback::make<ContextHubCallback>(); + } + std::unique_ptr<HalClient> halClient = HalClient::create(gCallback); + if (halClient == nullptr) { + LOGE("Failed to init the connection to HAL."); + return; + } + std::unordered_set<std::string> supportedCommands = { + "connectEndpoint", "disconnectEndpoint", "query", "sendMessage"}; + std::map<std::string, CommandInfo> supportedCommandMap{}; + fillSupportedCommandMap(supportedCommands, supportedCommandMap); + while (true) { auto cmdLine = getCommandLine(); if (cmdLine.empty()) { continue; } - if (cmdLine.size() == 1 && cmdLine[0] == "connect") { - std::cout << "Already in a live session." << std::endl; - continue; - } if (cmdLine.size() == 1 && cmdLine[0] == "exit") { break; } try { - executeCommand(cmdLine); + switch (parseCommand(cmdLine, supportedCommandMap)) { + case connectEndpoint: { + HostEndpointInfo info = + createHostEndpointInfo(/* hexEndpointId= */ cmdLine[1]); + verifyStatus(/* operation= */ "connect endpoint", + halClient->connectEndpoint(info)); + break; + } + + case query: { + verifyStatusAndSignal(/* operation= */ "querying nanoapps", + halClient->queryNanoapps(), + gCallback->promise.get_future()); + break; + } + + case disconnectEndpoint: { + uint16_t hostEndpointId = + verifyAndConvertEndpointHexId(/* number= */ cmdLine[1]); + verifyStatus(/* operation= */ "disconnect endpoint", + halClient->disconnectEndpoint(hostEndpointId)); + break; + } + case sendMessage: { + ContextHubMessage message = createContextHubMessage( + /* hexHostEndpointId= */ cmdLine[1], + /* appIdOrName= */ cmdLine[2], /* hexPayload= */ cmdLine[3]); + verifyStatusAndSignal( + /* operation= */ "sending a message to " + cmdLine[2], + halClient->sendMessage(message), gCallback->promise.get_future()); + break; + } + default: + printUsage(supportedCommandMap); + } } catch (std::system_error &e) { std::cerr << e.what() << std::endl; } @@ -673,15 +866,15 @@ int main(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { cmdLine.emplace_back(argv[i]); } - if (cmdLine.size() == 1 && cmdLine[0] == "connect") { - connectToHal(); - return 0; - } try { + if (cmdLine.size() == 1 && cmdLine[0] == "connect") { + connectToHal(); + return 0; + } executeCommand(cmdLine); } catch (std::system_error &e) { std::cerr << e.what() << std::endl; return -1; } return 0; -}
\ No newline at end of file +} diff --git a/host/common/daemon_base.cc b/host/common/daemon_base.cc index 48737f01..b7681cbb 100644 --- a/host/common/daemon_base.cc +++ b/host/common/daemon_base.cc @@ -14,6 +14,9 @@ * limitations under the License. */ +// TODO(b/298459533): metrics_reporter_in_the_daemon ramp up -> remove old +// code + #include <signal.h> #include <cstdlib> #include <fstream> @@ -25,12 +28,9 @@ #include "chre_host/napp_header.h" #ifdef CHRE_DAEMON_METRIC_ENABLED +#include <android_chre_flags.h> #include <chre_atoms_log.h> #include <system/chre/core/chre_metrics.pb.h> - -using ::aidl::android::frameworks::stats::IStats; -using ::aidl::android::frameworks::stats::VendorAtom; -using ::aidl::android::frameworks::stats::VendorAtomValue; #endif // CHRE_DAEMON_METRIC_ENABLED // Aliased for consistency with the way these symbols are referenced in @@ -40,6 +40,17 @@ namespace fbs = ::chre::fbs; namespace android { namespace chre { +#ifdef CHRE_DAEMON_METRIC_ENABLED +using ::aidl::android::frameworks::stats::IStats; +using ::aidl::android::frameworks::stats::VendorAtom; +using ::aidl::android::frameworks::stats::VendorAtomValue; + +using ::android::chre::Atoms::CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED; +using ::android::chre::Atoms::CHRE_PAL_OPEN_FAILED; +using ::android::chre::Atoms::ChrePalOpenFailed; +using ::android::chre::flags::metrics_reporter_in_the_daemon; +#endif // CHRE_DAEMON_METRIC_ENABLED + namespace { void signalHandler(void *ctx) { @@ -153,46 +164,66 @@ void ChreDaemonBase::handleMetricLog(const ::chre::fbs::MetricLogT *metricMsg) { const std::vector<int8_t> &encodedMetric = metricMsg->encoded_metric; switch (metricMsg->id) { - case Atoms::CHRE_PAL_OPEN_FAILED: { + case CHRE_PAL_OPEN_FAILED: { metrics::ChrePalOpenFailed metric; if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) { LOGE("Failed to parse metric data"); } else { - std::vector<VendorAtomValue> values(2); - values[0].set<VendorAtomValue::intValue>(metric.pal()); - values[1].set<VendorAtomValue::intValue>(metric.type()); - const VendorAtom atom{ - .atomId = Atoms::CHRE_PAL_OPEN_FAILED, - .values{std::move(values)}, - }; - reportMetric(atom); + if (metrics_reporter_in_the_daemon()) { + ChrePalOpenFailed::ChrePalType pal = + static_cast<ChrePalOpenFailed::ChrePalType>(metric.pal()); + ChrePalOpenFailed::Type type = + static_cast<ChrePalOpenFailed::Type>(metric.type()); + if (!mMetricsReporter.logPalOpenFailed(pal, type)) { + LOGE("Could not log the PAL open failed metric"); + } + } else { + std::vector<VendorAtomValue> values(2); + values[0].set<VendorAtomValue::intValue>(metric.pal()); + values[1].set<VendorAtomValue::intValue>(metric.type()); + const VendorAtom atom{ + .atomId = Atoms::CHRE_PAL_OPEN_FAILED, + .values{std::move(values)}, + }; + reportMetric(atom); + } } break; } - case Atoms::CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED: { + case CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED: { metrics::ChreEventQueueSnapshotReported metric; if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) { LOGE("Failed to parse metric data"); } else { - std::vector<VendorAtomValue> values(6); - values[0].set<VendorAtomValue::intValue>( - metric.snapshot_chre_get_time_ms()); - values[1].set<VendorAtomValue::intValue>(metric.max_event_queue_size()); - values[2].set<VendorAtomValue::intValue>( - metric.mean_event_queue_size()); - values[3].set<VendorAtomValue::intValue>(metric.num_dropped_events()); - // Last two values are not currently populated and will be implemented - // later. To avoid confusion of the interpretation, we use UINT32_MAX - // as a placeholder value. - values[4].set<VendorAtomValue::intValue>( - UINT32_MAX); // max_queue_delay_us - values[5].set<VendorAtomValue::intValue>( - UINT32_MAX); // mean_queue_delay_us - const VendorAtom atom{ - .atomId = Atoms::CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED, - .values{std::move(values)}, - }; - reportMetric(atom); + if (metrics_reporter_in_the_daemon()) { + if (!mMetricsReporter.logEventQueueSnapshotReported( + metric.snapshot_chre_get_time_ms(), + metric.max_event_queue_size(), metric.mean_event_queue_size(), + metric.num_dropped_events())) { + LOGE("Could not log the event queue snapshot metric"); + } + } else { + std::vector<VendorAtomValue> values(6); + values[0].set<VendorAtomValue::intValue>( + metric.snapshot_chre_get_time_ms()); + values[1].set<VendorAtomValue::intValue>( + metric.max_event_queue_size()); + values[2].set<VendorAtomValue::intValue>( + metric.mean_event_queue_size()); + values[3].set<VendorAtomValue::intValue>(metric.num_dropped_events()); + // Last two values are not currently populated and will be implemented + // later. To avoid confusion of the interpretation, we use UINT32_MAX + // as a placeholder value. + values[4].set<VendorAtomValue::intValue>( + UINT32_MAX); // max_queue_delay_us + values[5].set<VendorAtomValue::intValue>( + UINT32_MAX); // mean_queue_delay_us + const VendorAtom atom{ + .atomId = Atoms::CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED, + .values{std::move(values)}, + }; + reportMetric(atom); + } } break; } diff --git a/host/common/fbs_daemon_base.cc b/host/common/fbs_daemon_base.cc index 94f07708..b41926f5 100644 --- a/host/common/fbs_daemon_base.cc +++ b/host/common/fbs_daemon_base.cc @@ -14,6 +14,9 @@ * limitations under the License. */ +// TODO(b/298459533): metrics_reporter_in_the_daemon ramp up -> remove old +// code + #include <cstdlib> #include <fstream> @@ -26,11 +29,8 @@ #ifdef CHRE_DAEMON_METRIC_ENABLED #include <aidl/android/frameworks/stats/IStats.h> #include <android/binder_manager.h> +#include <android_chre_flags.h> #include <chre_atoms_log.h> - -using ::aidl::android::frameworks::stats::IStats; -using ::aidl::android::frameworks::stats::VendorAtom; -using ::aidl::android::frameworks::stats::VendorAtomValue; #endif // CHRE_DAEMON_METRIC_ENABLED // Aliased for consistency with the way these symbols are referenced in @@ -40,6 +40,14 @@ namespace fbs = ::chre::fbs; namespace android { namespace chre { +#ifdef CHRE_DAEMON_METRIC_ENABLED +using ::aidl::android::frameworks::stats::IStats; +using ::aidl::android::frameworks::stats::VendorAtom; +using ::aidl::android::frameworks::stats::VendorAtomValue; +using ::android::chre::Atoms::ChreHalNanoappLoadFailed; +using ::android::chre::flags::metrics_reporter_in_the_daemon; +#endif // CHRE_DAEMON_METRIC_ENABLED + bool FbsDaemonBase::sendNanoappLoad(uint64_t appId, uint32_t appVersion, uint32_t appTargetApiVersion, const std::string &appBinaryName, @@ -145,6 +153,9 @@ void FbsDaemonBase::onMessageReceived(const unsigned char *messageBuffer, fbs::UnPackMessageContainer(messageBuffer); handleNanConfigurationRequest( container->message.AsNanConfigurationRequest()); + } else if (messageType == fbs::ChreMessage::NanoappInstanceIdInfo) { + // TODO(b/242760291): Use this info to map nanoapp log detokenizers with + // instance ID in log message parser. } else if (hostClientId == kHostClientIdDaemon) { handleDaemonMessage(messageBuffer); } else if (hostClientId == ::chre::kHostClientIdUnspecified) { @@ -176,18 +187,27 @@ void FbsDaemonBase::handleDaemonMessage(const uint8_t *message) { mPreloadedNanoappPendingTransactions.front().transactionId); #ifdef CHRE_DAEMON_METRIC_ENABLED - std::vector<VendorAtomValue> values(3); - values[0].set<VendorAtomValue::longValue>( - mPreloadedNanoappPendingTransactions.front().nanoappId); - values[1].set<VendorAtomValue::intValue>( - Atoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED); - values[2].set<VendorAtomValue::intValue>( - Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC); - const VendorAtom atom{ - .atomId = Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED, - .values{std::move(values)}, - }; - reportMetric(atom); + if (metrics_reporter_in_the_daemon()) { + if (!mMetricsReporter.logNanoappLoadFailed( + mPreloadedNanoappPendingTransactions.front().nanoappId, + ChreHalNanoappLoadFailed::TYPE_PRELOADED, + ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC)) { + LOGE("Could not log the nanoapp load failed metric"); + } + } else { + std::vector<VendorAtomValue> values(3); + values[0].set<VendorAtomValue::longValue>( + mPreloadedNanoappPendingTransactions.front().nanoappId); + values[1].set<VendorAtomValue::intValue>( + Atoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED); + values[2].set<VendorAtomValue::intValue>( + Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC); + const VendorAtom atom{ + .atomId = Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED, + .values{std::move(values)}, + }; + reportMetric(atom); + } #endif // CHRE_DAEMON_METRIC_ENABLED } mPreloadedNanoappPendingTransactions.pop(); diff --git a/host/common/fragmented_load_transaction.cc b/host/common/fragmented_load_transaction.cc index c3c26d33..42c910d8 100644 --- a/host/common/fragmented_load_transaction.cc +++ b/host/common/fragmented_load_transaction.cc @@ -49,9 +49,8 @@ inline std::vector<uint8_t> getSubVector(const std::vector<uint8_t> &source, FragmentedLoadTransaction::FragmentedLoadTransaction( uint32_t transactionId, uint64_t appId, uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion, - const std::vector<uint8_t> &appBinary, size_t fragmentSize) { - mTransactionId = transactionId; - + const std::vector<uint8_t> &appBinary, size_t fragmentSize) + : mTransactionId(transactionId), mNanoappId(appId) { // Start with fragmentId at 1 since 0 is used to indicate // legacy behavior at CHRE size_t fragmentId = 1; @@ -64,7 +63,7 @@ FragmentedLoadTransaction::FragmentedLoadTransaction( getSubVector(appBinary, byteIndex, fragmentSize)); } else { mFragmentRequests.emplace_back( - fragmentId++, transactionId, + fragmentId++, transactionId, appId, getSubVector(appBinary, byteIndex, fragmentSize)); } diff --git a/host/common/hal_client.cc b/host/common/hal_client.cc new file mode 100644 index 00000000..6a1c0bfa --- /dev/null +++ b/host/common/hal_client.cc @@ -0,0 +1,180 @@ +/* + * 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. + */ + +#ifndef LOG_TAG +#define LOG_TAG "CHRE.HAL.CLIENT" +#endif + +#include "chre_host/hal_client.h" + +#include <utils/SystemClock.h> + +#include <cinttypes> +#include <thread> + +namespace android::chre { + +using ::aidl::android::hardware::contexthub::IContextHub; +using ::aidl::android::hardware::contexthub::IContextHubCallback; +using ::ndk::ScopedAStatus; + +std::unique_ptr<HalClient> HalClient::create( + const std::shared_ptr<IContextHubCallback> &callback, + int32_t contextHubId) { + if (callback == nullptr) { + LOGE("Callback function must not be null."); + return nullptr; + } + auto halClient = + std::unique_ptr<HalClient>(new HalClient(callback, contextHubId)); + if (halClient->initConnection() != HalError::SUCCESS) { + return nullptr; + } + return halClient; +} + +HalError HalClient::initConnection() { + std::lock_guard<std::shared_mutex> lockGuard{mConnectionLock}; + + if (mContextHub != nullptr) { + LOGW("Context hub is already connected."); + return HalError::SUCCESS; + } + + // Wait to connect to the service. Note that we don't do local retries because + // we're relying on the internal retries in AServiceManager_waitForService(). + // If HAL service has just restarted, it can take a few seconds to connect. + ndk::SpAIBinder binder{ + AServiceManager_waitForService(kAidlServiceName.c_str())}; + if (binder.get() == nullptr) { + return HalError::BINDER_CONNECTION_FAILED; + } + + // Link the death recipient to handle the binder disconnection event. + if (AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), this) != + STATUS_OK) { + LOGE("Failed to link the binder death recipient"); + return HalError::LINK_DEATH_RECIPIENT_FAILED; + } + + // Retrieve a handle of context hub service. + mContextHub = IContextHub::fromBinder(binder); + if (mContextHub == nullptr) { + LOGE("Got null context hub from the binder connection"); + return HalError::NULL_CONTEXT_HUB_FROM_BINDER; + } + + // Register an IContextHubCallback. + ScopedAStatus status = + mContextHub->registerCallback(kDefaultContextHubId, mCallback); + if (!status.isOk()) { + LOGE("Unable to register callback: %s", status.getDescription().c_str()); + return HalError::CALLBACK_REGISTRATION_FAILED; + } + LOGI("Successfully connected to HAL."); + return HalError::SUCCESS; +} + +void HalClient::onHalDisconnected(void *cookie) { + LOGW("CHRE HAL is disconnected. Reconnecting..."); + int64_t startTime = ::android::elapsedRealtime(); + auto *halClient = static_cast<HalClient *>(cookie); + { + std::lock_guard<std::shared_mutex> lock(halClient->mConnectionLock); + halClient->mContextHub = nullptr; + } + + HalError result = halClient->initConnection(); + uint64_t duration = ::android::elapsedRealtime() - startTime; + if (result != HalError::SUCCESS) { + LOGE("Failed to fully reconnect to HAL after %" PRIu64 + "ms, HalErrorCode: %" PRIi32, + duration, result); + return; + } + tryReconnectEndpoints(halClient); + LOGI("Reconnected to HAL after %" PRIu64 "ms", duration); +} + +ScopedAStatus HalClient::connectEndpoint( + const HostEndpointInfo &hostEndpointInfo) { + HostEndpointId endpointId = hostEndpointInfo.hostEndpointId; + if (isEndpointConnected(endpointId)) { + // Connecting the endpoint again even though it is already connected to let + // HAL and/or CHRE be the single place to control the behavior. + LOGW("Endpoint id %" PRIu16 " is already connected.", endpointId); + } + ScopedAStatus result = callIfConnected( + [&]() { return mContextHub->onHostEndpointConnected(hostEndpointInfo); }); + if (result.isOk()) { + insertConnectedEndpoint(hostEndpointInfo); + } else { + LOGE("Failed to connect the endpoint id %" PRIu16, + hostEndpointInfo.hostEndpointId); + } + return result; +} + +ScopedAStatus HalClient::disconnectEndpoint(HostEndpointId hostEndpointId) { + if (!isEndpointConnected(hostEndpointId)) { + // Disconnecting the endpoint again even though it is already disconnected + // to let HAL and/or CHRE be the single place to control the behavior. + LOGW("Endpoint id %" PRIu16 " is already disconnected.", hostEndpointId); + } + ScopedAStatus result = callIfConnected([&]() { + return mContextHub->onHostEndpointDisconnected(hostEndpointId); + }); + if (result.isOk()) { + removeConnectedEndpoint(hostEndpointId); + } else { + LOGE("Failed to disconnect the endpoint id %" PRIu16, hostEndpointId); + } + return result; +} + +ScopedAStatus HalClient::sendMessage(const ContextHubMessage &message) { + uint16_t hostEndpointId = message.hostEndPoint; + if (!isEndpointConnected(hostEndpointId)) { + // For now this is still allowed but in the future + // HalError::UNEXPECTED_ENDPOINT_STATE will be returned. + LOGW("Endpoint id %" PRIu16 + " is unknown or disconnected. Message sending will be skipped in the " + "future."); + } + return callIfConnected( + [&]() { return mContextHub->sendMessageToHub(mContextHubId, message); }); +} + +void HalClient::tryReconnectEndpoints(HalClient *halClient) { + std::lock_guard<std::shared_mutex> lock(halClient->mStateLock); + for (const auto &[endpointId, endpointInfo] : + halClient->mConnectedEndpoints) { + if (!halClient + ->callIfConnected([&]() { + return halClient->mContextHub->onHostEndpointConnected( + endpointInfo); + }) + .isOk()) { + LOGE("Failed to set up the connected state for endpoint %" PRIu16 + " after HAL restarts.", + endpointId); + halClient->mConnectedEndpoints.erase(endpointId); + } else { + LOGI("Reconnected endpoint %" PRIu16 " to CHRE HAL.", endpointId); + } + } +} +} // namespace android::chre
\ No newline at end of file diff --git a/host/common/host_protocol_host.cc b/host/common/host_protocol_host.cc index 24932061..a24ec94e 100644 --- a/host/common/host_protocol_host.cc +++ b/host/common/host_protocol_host.cc @@ -250,5 +250,10 @@ void HostProtocolHost::encodeNanconfigurationUpdate( finalize(builder, fbs::ChreMessage::NanConfigurationUpdate, message.Union()); } +void HostProtocolHost::encodePulseRequest(FlatBufferBuilder &builder) { + auto message = fbs::CreatePulseRequest(builder); + finalize(builder, fbs::ChreMessage::PulseRequest, message.Union()); +} + } // namespace chre } // namespace android diff --git a/host/common/include/chre_host/daemon_base.h b/host/common/include/chre_host/daemon_base.h index c0c6e730..453a8665 100644 --- a/host/common/include/chre_host/daemon_base.h +++ b/host/common/include/chre_host/daemon_base.h @@ -25,10 +25,14 @@ * implement. */ +// TODO(b/298459533): metrics_reporter_in_the_daemon ramp up -> remove old +// code + #include <atomic> #include <csignal> #include <cstdint> #include <map> +#include <mutex> #include <queue> #include <string> #include <thread> @@ -40,6 +44,8 @@ #ifdef CHRE_DAEMON_METRIC_ENABLED #include <aidl/android/frameworks/stats/IStats.h> #include <android/binder_manager.h> + +#include "chre_host/metrics_reporter.h" #endif // CHRE_DAEMON_METRIC_ENABLED namespace android { @@ -230,16 +236,15 @@ class ChreDaemonBase { #ifdef CHRE_LOG_ATOM_EXTENSION_ENABLED /** * Handles additional metrics that aren't logged by the common CHRE code. - * */ virtual void handleVendorMetricLog( const ::chre::fbs::MetricLogT *metric_msg) = 0; #endif // CHRE_LOG_ATOM_EXTENSION_ENABLED /** - * Create and report CHRE vendor atom and send it to stats_client + * Create and report CHRE vendor atom and send it to stats_client. * - * @param atom the vendor atom to be reported + * @param atom the vendor atom to be reported. */ void reportMetric(const aidl::android::frameworks::stats::VendorAtom &atom); #endif // CHRE_DAEMON_METRIC_ENABLED @@ -263,6 +268,10 @@ class ChreDaemonBase { //! Server used to communicate with daemon clients SocketServer mServer; +#ifdef CHRE_DAEMON_METRIC_ENABLED + android::chre::MetricsReporter mMetricsReporter; +#endif // CHRE_DAEMON_METRIC_ENABLED + private: LogMessageParser mLogger; diff --git a/host/common/include/chre_host/fragmented_load_transaction.h b/host/common/include/chre_host/fragmented_load_transaction.h index f0b9ff45..1d60b3e4 100644 --- a/host/common/include/chre_host/fragmented_load_transaction.h +++ b/host/common/include/chre_host/fragmented_load_transaction.h @@ -50,8 +50,8 @@ struct FragmentedLoadRequest { std::vector<uint8_t> binary; FragmentedLoadRequest(size_t fragmentId, uint32_t transactionId, - const std::vector<uint8_t> &binary) - : FragmentedLoadRequest(fragmentId, transactionId, 0, 0, 0, 0, 0, + uint64_t appId, const std::vector<uint8_t> &binary) + : FragmentedLoadRequest(fragmentId, transactionId, appId, 0, 0, 0, 0, binary) {} FragmentedLoadRequest(size_t fragmentId, uint32_t transactionId, @@ -115,10 +115,15 @@ class FragmentedLoadTransaction { return mTransactionId; } + uint64_t getNanoappId() const { + return mNanoappId; + } + private: std::vector<FragmentedLoadRequest> mFragmentRequests; size_t mCurrentRequestIndex = 0; uint32_t mTransactionId; + uint64_t mNanoappId; static constexpr size_t kDefaultFragmentSize = CHRE_HOST_DEFAULT_FRAGMENT_SIZE; diff --git a/host/common/include/chre_host/generated/host_messages_generated.h b/host/common/include/chre_host/generated/host_messages_generated.h index 9b80d2ec..262bb3ff 100644 --- a/host/common/include/chre_host/generated/host_messages_generated.h +++ b/host/common/include/chre_host/generated/host_messages_generated.h @@ -45,6 +45,10 @@ struct LoadNanoappResponse; struct LoadNanoappResponseBuilder; struct LoadNanoappResponseT; +struct NanoappInstanceIdInfo; +struct NanoappInstanceIdInfoBuilder; +struct NanoappInstanceIdInfoT; + struct UnloadNanoappRequest; struct UnloadNanoappRequestBuilder; struct UnloadNanoappRequestT; @@ -129,6 +133,14 @@ struct DebugConfiguration; struct DebugConfigurationBuilder; struct DebugConfigurationT; +struct PulseRequest; +struct PulseRequestBuilder; +struct PulseRequestT; + +struct PulseResponse; +struct PulseResponseBuilder; +struct PulseResponseT; + struct HostAddress; struct MessageContainer; @@ -340,11 +352,14 @@ enum class ChreMessage : uint8_t { NanConfigurationRequest = 26, NanConfigurationUpdate = 27, DebugConfiguration = 28, + PulseRequest = 29, + PulseResponse = 30, + NanoappInstanceIdInfo = 31, MIN = NONE, - MAX = DebugConfiguration + MAX = NanoappInstanceIdInfo }; -inline const ChreMessage (&EnumValuesChreMessage())[29] { +inline const ChreMessage (&EnumValuesChreMessage())[32] { static const ChreMessage values[] = { ChreMessage::NONE, ChreMessage::NanoappMessage, @@ -374,13 +389,16 @@ inline const ChreMessage (&EnumValuesChreMessage())[29] { ChreMessage::BatchedMetricLog, ChreMessage::NanConfigurationRequest, ChreMessage::NanConfigurationUpdate, - ChreMessage::DebugConfiguration + ChreMessage::DebugConfiguration, + ChreMessage::PulseRequest, + ChreMessage::PulseResponse, + ChreMessage::NanoappInstanceIdInfo }; return values; } inline const char * const *EnumNamesChreMessage() { - static const char * const names[30] = { + static const char * const names[33] = { "NONE", "NanoappMessage", "HubInfoRequest", @@ -410,13 +428,16 @@ inline const char * const *EnumNamesChreMessage() { "NanConfigurationRequest", "NanConfigurationUpdate", "DebugConfiguration", + "PulseRequest", + "PulseResponse", + "NanoappInstanceIdInfo", nullptr }; return names; } inline const char *EnumNameChreMessage(ChreMessage e) { - if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::DebugConfiguration)) return ""; + if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::NanoappInstanceIdInfo)) return ""; const size_t index = static_cast<size_t>(e); return EnumNamesChreMessage()[index]; } @@ -537,6 +558,18 @@ template<> struct ChreMessageTraits<chre::fbs::DebugConfiguration> { static const ChreMessage enum_value = ChreMessage::DebugConfiguration; }; +template<> struct ChreMessageTraits<chre::fbs::PulseRequest> { + static const ChreMessage enum_value = ChreMessage::PulseRequest; +}; + +template<> struct ChreMessageTraits<chre::fbs::PulseResponse> { + static const ChreMessage enum_value = ChreMessage::PulseResponse; +}; + +template<> struct ChreMessageTraits<chre::fbs::NanoappInstanceIdInfo> { + static const ChreMessage enum_value = ChreMessage::NanoappInstanceIdInfo; +}; + struct ChreMessageUnion { ChreMessage type; void *value; @@ -793,6 +826,30 @@ struct ChreMessageUnion { return type == ChreMessage::DebugConfiguration ? reinterpret_cast<const chre::fbs::DebugConfigurationT *>(value) : nullptr; } + chre::fbs::PulseRequestT *AsPulseRequest() { + return type == ChreMessage::PulseRequest ? + reinterpret_cast<chre::fbs::PulseRequestT *>(value) : nullptr; + } + const chre::fbs::PulseRequestT *AsPulseRequest() const { + return type == ChreMessage::PulseRequest ? + reinterpret_cast<const chre::fbs::PulseRequestT *>(value) : nullptr; + } + chre::fbs::PulseResponseT *AsPulseResponse() { + return type == ChreMessage::PulseResponse ? + reinterpret_cast<chre::fbs::PulseResponseT *>(value) : nullptr; + } + const chre::fbs::PulseResponseT *AsPulseResponse() const { + return type == ChreMessage::PulseResponse ? + reinterpret_cast<const chre::fbs::PulseResponseT *>(value) : nullptr; + } + chre::fbs::NanoappInstanceIdInfoT *AsNanoappInstanceIdInfo() { + return type == ChreMessage::NanoappInstanceIdInfo ? + reinterpret_cast<chre::fbs::NanoappInstanceIdInfoT *>(value) : nullptr; + } + const chre::fbs::NanoappInstanceIdInfoT *AsNanoappInstanceIdInfo() const { + return type == ChreMessage::NanoappInstanceIdInfo ? + reinterpret_cast<const chre::fbs::NanoappInstanceIdInfoT *>(value) : nullptr; + } }; bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type); @@ -2026,6 +2083,80 @@ inline flatbuffers::Offset<LoadNanoappResponse> CreateLoadNanoappResponse( flatbuffers::Offset<LoadNanoappResponse> CreateLoadNanoappResponse(flatbuffers::FlatBufferBuilder &_fbb, const LoadNanoappResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct NanoappInstanceIdInfoT : public flatbuffers::NativeTable { + typedef NanoappInstanceIdInfo TableType; + uint32_t instance_id; + uint64_t app_id; + NanoappInstanceIdInfoT() + : instance_id(0), + app_id(0) { + } +}; + +struct NanoappInstanceIdInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef NanoappInstanceIdInfoT NativeTableType; + typedef NanoappInstanceIdInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_INSTANCE_ID = 4, + VT_APP_ID = 6 + }; + uint32_t instance_id() const { + return GetField<uint32_t>(VT_INSTANCE_ID, 0); + } + bool mutate_instance_id(uint32_t _instance_id) { + return SetField<uint32_t>(VT_INSTANCE_ID, _instance_id, 0); + } + uint64_t app_id() const { + return GetField<uint64_t>(VT_APP_ID, 0); + } + bool mutate_app_id(uint64_t _app_id) { + return SetField<uint64_t>(VT_APP_ID, _app_id, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<uint32_t>(verifier, VT_INSTANCE_ID) && + VerifyField<uint64_t>(verifier, VT_APP_ID) && + verifier.EndTable(); + } + NanoappInstanceIdInfoT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NanoappInstanceIdInfoT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset<NanoappInstanceIdInfo> Pack(flatbuffers::FlatBufferBuilder &_fbb, const NanoappInstanceIdInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NanoappInstanceIdInfoBuilder { + typedef NanoappInstanceIdInfo Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_instance_id(uint32_t instance_id) { + fbb_.AddElement<uint32_t>(NanoappInstanceIdInfo::VT_INSTANCE_ID, instance_id, 0); + } + void add_app_id(uint64_t app_id) { + fbb_.AddElement<uint64_t>(NanoappInstanceIdInfo::VT_APP_ID, app_id, 0); + } + explicit NanoappInstanceIdInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + NanoappInstanceIdInfoBuilder &operator=(const NanoappInstanceIdInfoBuilder &); + flatbuffers::Offset<NanoappInstanceIdInfo> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<NanoappInstanceIdInfo>(end); + return o; + } +}; + +inline flatbuffers::Offset<NanoappInstanceIdInfo> CreateNanoappInstanceIdInfo( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t instance_id = 0, + uint64_t app_id = 0) { + NanoappInstanceIdInfoBuilder builder_(_fbb); + builder_.add_app_id(app_id); + builder_.add_instance_id(instance_id); + return builder_.Finish(); +} + +flatbuffers::Offset<NanoappInstanceIdInfo> CreateNanoappInstanceIdInfo(flatbuffers::FlatBufferBuilder &_fbb, const NanoappInstanceIdInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct UnloadNanoappRequestT : public flatbuffers::NativeTable { typedef UnloadNanoappRequest TableType; uint32_t transaction_id; @@ -2760,7 +2891,8 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// uint8_t - Log metadata, encoded as follows: /// [EI(Upper nibble) | Level(Lower nibble)] /// * Log Type - /// (0 = No encoding, 1 = Tokenized log, 2 = BT snoop log) + /// (0 = No encoding, 1 = Tokenized log, + /// 2 = BT snoop log, 3 = Nanoapp Tokenized log) /// * LogBuffer log level (1 = error, 2 = warn, /// 3 = info, 4 = debug, /// 5 = verbose) @@ -2772,7 +2904,7 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// terminated string (eg: pass to string manipulation functions, get its /// size via strlen(), etc.). /// - /// * Encoded logs: The first byte of the log buffer indicates the size of + /// * Tokenized logs: The first byte of the log buffer indicates the size of /// the actual encoded data to follow. For example, if a tokenized log of /// size 24 bytes were to be represented, a buffer of size 25 bytes would /// be needed to encode this as: [Size(1B) | Data(24B)]. A decoder would @@ -2786,6 +2918,15 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// size 24 bytes were to be represented, a buffer of size 26 bytes would /// be needed to encode this as: [Direction(1B) | Size(1B) | Data(24B)]. /// + /// * Tokenized nanoapp logs: This log type is specifically for nanoapps with + /// tokenized logs enabled. Similar to tokenized logs, the first byte is the + /// size of the tokenized log data at the end. The next two bytes is the instance + /// ID of the nanoapp which sends this tokenized log message. This instance ID + /// will be used to map to the corresponding detokenizer in the log message parser. + /// For example, if a nanoapp tokenized log of size 24 bytes were to be sent, + /// a buffer of size 27 bytes would be needed to encode this as: + /// [Size(1B) | InstanceId (2B) | Data(24B)]. + /// /// This pattern repeats until the end of the buffer for multiple log /// messages. The last byte will always be a null-terminator. There are no /// padding bytes between these fields. Treat this like a packed struct and be @@ -3479,6 +3620,90 @@ inline flatbuffers::Offset<DebugConfiguration> CreateDebugConfiguration( flatbuffers::Offset<DebugConfiguration> CreateDebugConfiguration(flatbuffers::FlatBufferBuilder &_fbb, const DebugConfigurationT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct PulseRequestT : public flatbuffers::NativeTable { + typedef PulseRequest TableType; + PulseRequestT() { + } +}; + +struct PulseRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PulseRequestT NativeTableType; + typedef PulseRequestBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + PulseRequestT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PulseRequestT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset<PulseRequest> Pack(flatbuffers::FlatBufferBuilder &_fbb, const PulseRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PulseRequestBuilder { + typedef PulseRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PulseRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PulseRequestBuilder &operator=(const PulseRequestBuilder &); + flatbuffers::Offset<PulseRequest> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<PulseRequest>(end); + return o; + } +}; + +inline flatbuffers::Offset<PulseRequest> CreatePulseRequest( + flatbuffers::FlatBufferBuilder &_fbb) { + PulseRequestBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset<PulseRequest> CreatePulseRequest(flatbuffers::FlatBufferBuilder &_fbb, const PulseRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct PulseResponseT : public flatbuffers::NativeTable { + typedef PulseResponse TableType; + PulseResponseT() { + } +}; + +struct PulseResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PulseResponseT NativeTableType; + typedef PulseResponseBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + PulseResponseT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PulseResponseT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset<PulseResponse> Pack(flatbuffers::FlatBufferBuilder &_fbb, const PulseResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PulseResponseBuilder { + typedef PulseResponse Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PulseResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PulseResponseBuilder &operator=(const PulseResponseBuilder &); + flatbuffers::Offset<PulseResponse> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<PulseResponse>(end); + return o; + } +}; + +inline flatbuffers::Offset<PulseResponse> CreatePulseResponse( + flatbuffers::FlatBufferBuilder &_fbb) { + PulseResponseBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset<PulseResponse> CreatePulseResponse(flatbuffers::FlatBufferBuilder &_fbb, const PulseResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct MessageContainerT : public flatbuffers::NativeTable { typedef MessageContainer TableType; chre::fbs::ChreMessageUnion message; @@ -3589,6 +3814,15 @@ struct MessageContainer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const chre::fbs::DebugConfiguration *message_as_DebugConfiguration() const { return message_type() == chre::fbs::ChreMessage::DebugConfiguration ? static_cast<const chre::fbs::DebugConfiguration *>(message()) : nullptr; } + const chre::fbs::PulseRequest *message_as_PulseRequest() const { + return message_type() == chre::fbs::ChreMessage::PulseRequest ? static_cast<const chre::fbs::PulseRequest *>(message()) : nullptr; + } + const chre::fbs::PulseResponse *message_as_PulseResponse() const { + return message_type() == chre::fbs::ChreMessage::PulseResponse ? static_cast<const chre::fbs::PulseResponse *>(message()) : nullptr; + } + const chre::fbs::NanoappInstanceIdInfo *message_as_NanoappInstanceIdInfo() const { + return message_type() == chre::fbs::ChreMessage::NanoappInstanceIdInfo ? static_cast<const chre::fbs::NanoappInstanceIdInfo *>(message()) : nullptr; + } void *mutable_message() { return GetPointer<void *>(VT_MESSAGE); } @@ -3729,6 +3963,18 @@ template<> inline const chre::fbs::DebugConfiguration *MessageContainer::message return message_as_DebugConfiguration(); } +template<> inline const chre::fbs::PulseRequest *MessageContainer::message_as<chre::fbs::PulseRequest>() const { + return message_as_PulseRequest(); +} + +template<> inline const chre::fbs::PulseResponse *MessageContainer::message_as<chre::fbs::PulseResponse>() const { + return message_as_PulseResponse(); +} + +template<> inline const chre::fbs::NanoappInstanceIdInfo *MessageContainer::message_as<chre::fbs::NanoappInstanceIdInfo>() const { + return message_as_NanoappInstanceIdInfo(); +} + struct MessageContainerBuilder { typedef MessageContainer Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -4100,6 +4346,35 @@ inline flatbuffers::Offset<LoadNanoappResponse> CreateLoadNanoappResponse(flatbu _fragment_id); } +inline NanoappInstanceIdInfoT *NanoappInstanceIdInfo::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + std::unique_ptr<chre::fbs::NanoappInstanceIdInfoT> _o = std::unique_ptr<chre::fbs::NanoappInstanceIdInfoT>(new NanoappInstanceIdInfoT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void NanoappInstanceIdInfo::UnPackTo(NanoappInstanceIdInfoT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = instance_id(); _o->instance_id = _e; } + { auto _e = app_id(); _o->app_id = _e; } +} + +inline flatbuffers::Offset<NanoappInstanceIdInfo> NanoappInstanceIdInfo::Pack(flatbuffers::FlatBufferBuilder &_fbb, const NanoappInstanceIdInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateNanoappInstanceIdInfo(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset<NanoappInstanceIdInfo> CreateNanoappInstanceIdInfo(flatbuffers::FlatBufferBuilder &_fbb, const NanoappInstanceIdInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const NanoappInstanceIdInfoT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _instance_id = _o->instance_id; + auto _app_id = _o->app_id; + return chre::fbs::CreateNanoappInstanceIdInfo( + _fbb, + _instance_id, + _app_id); +} + inline UnloadNanoappRequestT *UnloadNanoappRequest::UnPack(const flatbuffers::resolver_function_t *_resolver) const { std::unique_ptr<chre::fbs::UnloadNanoappRequestT> _o = std::unique_ptr<chre::fbs::UnloadNanoappRequestT>(new UnloadNanoappRequestT()); UnPackTo(_o.get(), _resolver); @@ -4661,6 +4936,52 @@ inline flatbuffers::Offset<DebugConfiguration> CreateDebugConfiguration(flatbuff _health_monitor_failure_crash); } +inline PulseRequestT *PulseRequest::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + std::unique_ptr<chre::fbs::PulseRequestT> _o = std::unique_ptr<chre::fbs::PulseRequestT>(new PulseRequestT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PulseRequest::UnPackTo(PulseRequestT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset<PulseRequest> PulseRequest::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PulseRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreatePulseRequest(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset<PulseRequest> CreatePulseRequest(flatbuffers::FlatBufferBuilder &_fbb, const PulseRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const PulseRequestT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + return chre::fbs::CreatePulseRequest( + _fbb); +} + +inline PulseResponseT *PulseResponse::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + std::unique_ptr<chre::fbs::PulseResponseT> _o = std::unique_ptr<chre::fbs::PulseResponseT>(new PulseResponseT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PulseResponse::UnPackTo(PulseResponseT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset<PulseResponse> PulseResponse::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PulseResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreatePulseResponse(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset<PulseResponse> CreatePulseResponse(flatbuffers::FlatBufferBuilder &_fbb, const PulseResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const PulseResponseT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + return chre::fbs::CreatePulseResponse( + _fbb); +} + inline MessageContainerT *MessageContainer::UnPack(const flatbuffers::resolver_function_t *_resolver) const { std::unique_ptr<chre::fbs::MessageContainerT> _o = std::unique_ptr<chre::fbs::MessageContainerT>(new MessageContainerT()); UnPackTo(_o.get(), _resolver); @@ -4810,6 +5131,18 @@ inline bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, auto ptr = reinterpret_cast<const chre::fbs::DebugConfiguration *>(obj); return verifier.VerifyTable(ptr); } + case ChreMessage::PulseRequest: { + auto ptr = reinterpret_cast<const chre::fbs::PulseRequest *>(obj); + return verifier.VerifyTable(ptr); + } + case ChreMessage::PulseResponse: { + auto ptr = reinterpret_cast<const chre::fbs::PulseResponse *>(obj); + return verifier.VerifyTable(ptr); + } + case ChreMessage::NanoappInstanceIdInfo: { + auto ptr = reinterpret_cast<const chre::fbs::NanoappInstanceIdInfo *>(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } @@ -4940,6 +5273,18 @@ inline void *ChreMessageUnion::UnPack(const void *obj, ChreMessage type, const f auto ptr = reinterpret_cast<const chre::fbs::DebugConfiguration *>(obj); return ptr->UnPack(resolver); } + case ChreMessage::PulseRequest: { + auto ptr = reinterpret_cast<const chre::fbs::PulseRequest *>(obj); + return ptr->UnPack(resolver); + } + case ChreMessage::PulseResponse: { + auto ptr = reinterpret_cast<const chre::fbs::PulseResponse *>(obj); + return ptr->UnPack(resolver); + } + case ChreMessage::NanoappInstanceIdInfo: { + auto ptr = reinterpret_cast<const chre::fbs::NanoappInstanceIdInfo *>(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -5058,6 +5403,18 @@ inline flatbuffers::Offset<void> ChreMessageUnion::Pack(flatbuffers::FlatBufferB auto ptr = reinterpret_cast<const chre::fbs::DebugConfigurationT *>(value); return CreateDebugConfiguration(_fbb, ptr, _rehasher).Union(); } + case ChreMessage::PulseRequest: { + auto ptr = reinterpret_cast<const chre::fbs::PulseRequestT *>(value); + return CreatePulseRequest(_fbb, ptr, _rehasher).Union(); + } + case ChreMessage::PulseResponse: { + auto ptr = reinterpret_cast<const chre::fbs::PulseResponseT *>(value); + return CreatePulseResponse(_fbb, ptr, _rehasher).Union(); + } + case ChreMessage::NanoappInstanceIdInfo: { + auto ptr = reinterpret_cast<const chre::fbs::NanoappInstanceIdInfoT *>(value); + return CreateNanoappInstanceIdInfo(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -5176,6 +5533,18 @@ inline ChreMessageUnion::ChreMessageUnion(const ChreMessageUnion &u) : type(u.ty value = new chre::fbs::DebugConfigurationT(*reinterpret_cast<chre::fbs::DebugConfigurationT *>(u.value)); break; } + case ChreMessage::PulseRequest: { + value = new chre::fbs::PulseRequestT(*reinterpret_cast<chre::fbs::PulseRequestT *>(u.value)); + break; + } + case ChreMessage::PulseResponse: { + value = new chre::fbs::PulseResponseT(*reinterpret_cast<chre::fbs::PulseResponseT *>(u.value)); + break; + } + case ChreMessage::NanoappInstanceIdInfo: { + value = new chre::fbs::NanoappInstanceIdInfoT(*reinterpret_cast<chre::fbs::NanoappInstanceIdInfoT *>(u.value)); + break; + } default: break; } @@ -5323,6 +5692,21 @@ inline void ChreMessageUnion::Reset() { delete ptr; break; } + case ChreMessage::PulseRequest: { + auto ptr = reinterpret_cast<chre::fbs::PulseRequestT *>(value); + delete ptr; + break; + } + case ChreMessage::PulseResponse: { + auto ptr = reinterpret_cast<chre::fbs::PulseResponseT *>(value); + delete ptr; + break; + } + case ChreMessage::NanoappInstanceIdInfo: { + auto ptr = reinterpret_cast<chre::fbs::NanoappInstanceIdInfoT *>(value); + delete ptr; + break; + } default: break; } value = nullptr; diff --git a/host/common/include/chre_host/hal_client.h b/host/common/include/chre_host/hal_client.h new file mode 100644 index 00000000..bc6db3bb --- /dev/null +++ b/host/common/include/chre_host/hal_client.h @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#ifndef CHRE_HOST_HAL_CLIENT_H_ +#define CHRE_HOST_HAL_CLIENT_H_ + +#include <cinttypes> +#include <memory> +#include <shared_mutex> +#include <unordered_map> +#include <vector> + +#include <aidl/android/hardware/contexthub/BnContextHubCallback.h> +#include <aidl/android/hardware/contexthub/ContextHubMessage.h> +#include <aidl/android/hardware/contexthub/HostEndpointInfo.h> +#include <aidl/android/hardware/contexthub/IContextHub.h> +#include <aidl/android/hardware/contexthub/IContextHubCallback.h> +#include <aidl/android/hardware/contexthub/NanoappBinary.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "chre_host/log.h" +#include "host/hal_generic/common/hal_error.h" + +namespace android::chre { + +using ::aidl::android::hardware::contexthub::AsyncEventType; +using ::aidl::android::hardware::contexthub::BnContextHubCallback; +using ::aidl::android::hardware::contexthub::ContextHubInfo; +using ::aidl::android::hardware::contexthub::ContextHubMessage; +using ::aidl::android::hardware::contexthub::HostEndpointInfo; +using ::aidl::android::hardware::contexthub::IContextHub; +using ::aidl::android::hardware::contexthub::IContextHubCallback; +using ::aidl::android::hardware::contexthub::IContextHubDefault; +using ::aidl::android::hardware::contexthub::MessageDeliveryStatus; +using ::aidl::android::hardware::contexthub::NanoappBinary; +using ::aidl::android::hardware::contexthub::NanoappInfo; +using ::aidl::android::hardware::contexthub::NanSessionRequest; +using ::aidl::android::hardware::contexthub::Setting; +using ::ndk::ScopedAStatus; + +/** + * A class exposing CHRE HAL APIs to clients and taking care of binder + * (re)connection. + * + * <p>This class also maintains a set of connected host endpoints, using which + * it is enforced that a message can only be sent to/from an endpoint id that is + * already connected to HAL. + * + * <p>When the binder connection to HAL is disconnected HalClient will have a + * death recipient re-establish the connection and reconnect the previously + * connected endpoints. In a rare case that CHRE also restarts at the same time, + * a client should rely on IContextHubCallback.handleContextHubAsyncEvent() to + * handle the RESTARTED event which is a signal that CHRE is up running. + * + * TODO(b/297912356): The name of this class is the same as an internal struct + * used by HalClientManager. Consider rename the latter one to avoid confusion + * + */ +class HalClient { + public: + static constexpr int32_t kDefaultContextHubId = 0; + + /** + * Create a HalClient unique pointer used to communicate with CHRE HAL. + * + * @param callback a non-null callback. + * @param contextHubId context hub id that only 0 is supported at this moment. + * + * @return null pointer if the creation fails. + */ + static std::unique_ptr<HalClient> create( + const std::shared_ptr<IContextHubCallback> &callback, + int32_t contextHubId = kDefaultContextHubId); + + ScopedAStatus queryNanoapps() { + return callIfConnected( + [&]() { return mContextHub->queryNanoapps(mContextHubId); }); + } + + ScopedAStatus sendMessage(const ContextHubMessage &message); + + ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo); + + ScopedAStatus disconnectEndpoint(char16_t hostEndpointId); + + protected: + class HalClientCallback : public BnContextHubCallback { + public: + explicit HalClientCallback( + const std::shared_ptr<IContextHubCallback> &callback, + HalClient *halClient) + : mCallback(callback), mHalClient(halClient) {} + + ScopedAStatus handleNanoappInfo( + const std::vector<NanoappInfo> &appInfo) override { + return mCallback->handleNanoappInfo(appInfo); + } + + ScopedAStatus handleContextHubMessage( + const ContextHubMessage &msg, + const std::vector<std::string> &msgContentPerms) override { + return mCallback->handleContextHubMessage(msg, msgContentPerms); + } + + ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override { + if (event == AsyncEventType::RESTARTED) { + LOGW("CHRE has restarted. Reconnecting endpoints."); + tryReconnectEndpoints(mHalClient); + } + return mCallback->handleContextHubAsyncEvent(event); + } + + ScopedAStatus handleTransactionResult(int32_t transactionId, + bool success) override { + return mCallback->handleTransactionResult(transactionId, success); + } + + ScopedAStatus handleNanSessionRequest( + const NanSessionRequest &request) override { + return mCallback->handleNanSessionRequest(request); + } + + ScopedAStatus handleMessageDeliveryStatus( + char16_t hostEndPointId, + const MessageDeliveryStatus &messageDeliveryStatus) override { + return mCallback->handleMessageDeliveryStatus(hostEndPointId, + messageDeliveryStatus); + } + + ScopedAStatus getUuid(std::array<uint8_t, 16> *outUuid) override { + return mCallback->getUuid(outUuid); + } + + ScopedAStatus getName(std::string *outName) override { + return mCallback->getName(outName); + } + + private: + std::shared_ptr<IContextHubCallback> mCallback; + HalClient *mHalClient; + }; + + explicit HalClient(const std::shared_ptr<IContextHubCallback> &callback, + int32_t contextHubId = kDefaultContextHubId) + : mContextHubId(contextHubId) { + mCallback = ndk::SharedRefBase::make<HalClientCallback>(callback, this); + ABinderProcess_startThreadPool(); + mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( + AIBinder_DeathRecipient_new(onHalDisconnected)); + } + + /** + * Initializes the connection to CHRE HAL. + */ + HalError initConnection(); + + using HostEndpointId = char16_t; + + const std::string kAidlServiceName = + std::string() + IContextHub::descriptor + "/default"; + + /** The callback for a disconnected HAL binder connection. */ + static void onHalDisconnected(void *cookie); + + /** Reconnect previously connected endpoints after CHRE or HAL restarts. */ + static void tryReconnectEndpoints(HalClient *halClient); + + ScopedAStatus callIfConnected(const std::function<ScopedAStatus()> &func) { + std::shared_lock<std::shared_mutex> sharedLock(mConnectionLock); + if (mContextHub == nullptr) { + return fromHalError(HalError::BINDER_DISCONNECTED); + } + return func(); + } + + bool isEndpointConnected(HostEndpointId hostEndpointId) { + std::shared_lock<std::shared_mutex> lock(mStateLock); + return mConnectedEndpoints.find(hostEndpointId) != + mConnectedEndpoints.end(); + } + + void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) { + std::lock_guard<std::shared_mutex> lock(mStateLock); + mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo; + } + + void removeConnectedEndpoint(HostEndpointId hostEndpointId) { + std::lock_guard<std::shared_mutex> lock(mStateLock); + mConnectedEndpoints.erase(hostEndpointId); + } + + void clearConnectedEndpoints() { + std::lock_guard<std::shared_mutex> lock(mStateLock); + mConnectedEndpoints.clear(); + } + + static ScopedAStatus fromHalError(HalError errorCode) { + return errorCode == HalError::SUCCESS + ? ScopedAStatus::ok() + : ScopedAStatus::fromServiceSpecificError( + static_cast<int32_t>(errorCode)); + } + + // Multi-contextHub is not supported at this moment. + int32_t mContextHubId; + + // The lock guarding mConnectedEndpoints. + std::shared_mutex mStateLock; + std::unordered_map<HostEndpointId, HostEndpointInfo> mConnectedEndpoints{}; + + // The lock guarding mContextHub. + std::shared_mutex mConnectionLock; + std::shared_ptr<IContextHub> mContextHub; + + // Handler of the binder disconnection event with HAL. + ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; + + std::shared_ptr<HalClientCallback> mCallback; +}; + +} // namespace android::chre +#endif // CHRE_HOST_HAL_CLIENT_H_
\ No newline at end of file diff --git a/host/common/include/chre_host/host_protocol_host.h b/host/common/include/chre_host/host_protocol_host.h index 29c1f8be..35f1cedf 100644 --- a/host/common/include/chre_host/host_protocol_host.h +++ b/host/common/include/chre_host/host_protocol_host.h @@ -99,6 +99,14 @@ class HostProtocolHost : public ::chre::HostProtocolCommon { IChreMessageHandlers &handlers); /** + * Encodes a message requesting pulse from CHRE + * + * @param builder A newly constructed FlatBufferBuilder that will be used to + * construct the message + */ + static void encodePulseRequest(flatbuffers::FlatBufferBuilder &builder); + + /** * Encodes a message requesting hub information from CHRE * * @param builder A newly constructed FlatBufferBuilder that will be used to diff --git a/host/common/include/chre_host/log.h b/host/common/include/chre_host/log.h index a247e87d..937d368b 100644 --- a/host/common/include/chre_host/log.h +++ b/host/common/include/chre_host/log.h @@ -23,25 +23,35 @@ #include <log/log.h> +namespace android::chre { + /** - * Logs a message to both logcat and stdout. Don't use this directly; prefer one - * of LOGE, LOGW, etc. to populate the level. Use LOG_PRI directly rather than - * ALOG to avoid misinterpreting LOG_* macros that may be incorrectly evaluated. + * Logs a message to both logcat and stdout/stderr. Don't use this directly; + * prefer one of LOGE, LOGW, etc. * - * @param level log level to pass to ALOG (LOG_ERROR, LOG_WARN, etc.) - * @param stream output stream to print to (e.g. stdout) + * @param level android log level, e.g., ANDROID_LOG_ERROR + * @param stream output stream to print to, e.g., stdout * @param format printf-style format string + * @param func the function name included in the log, e.g., __func__ + * @param line line number included in the log */ -#define CHRE_LOG(level, stream, format, ...) \ - do { \ - LOG_PRI(ANDROID_##level, LOG_TAG, format, ##__VA_ARGS__); \ - fprintf(stream, "%s:%d: " format "\n", __func__, __LINE__, ##__VA_ARGS__); \ - } while (0) +void outputHostLog(int priority, FILE *stream, const char *format, + const char *func, unsigned int line, ...); + +} // namespace android::chre -#define LOGE(format, ...) CHRE_LOG(LOG_ERROR, stderr, format, ##__VA_ARGS__) -#define LOGW(format, ...) CHRE_LOG(LOG_WARN, stdout, format, ##__VA_ARGS__) -#define LOGI(format, ...) CHRE_LOG(LOG_INFO, stdout, format, ##__VA_ARGS__) -#define LOGD(format, ...) CHRE_LOG(LOG_DEBUG, stdout, format, ##__VA_ARGS__) +#define LOGE(format, ...) \ + ::android::chre::outputHostLog(ANDROID_LOG_ERROR, stderr, format, __func__, \ + __LINE__, ##__VA_ARGS__) +#define LOGW(format, ...) \ + ::android::chre::outputHostLog(ANDROID_LOG_WARN, stdout, format, __func__, \ + __LINE__, ##__VA_ARGS__) +#define LOGI(format, ...) \ + ::android::chre::outputHostLog(ANDROID_LOG_INFO, stdout, format, __func__, \ + __LINE__, ##__VA_ARGS__) +#define LOGD(format, ...) \ + ::android::chre::outputHostLog(ANDROID_LOG_DEBUG, stdout, format, __func__, \ + __LINE__, ##__VA_ARGS__) #if LOG_NDEBUG __attribute__((format(printf, 1, 2))) inline void chreLogNull( @@ -49,7 +59,9 @@ __attribute__((format(printf, 1, 2))) inline void chreLogNull( #define LOGV(format, ...) chreLogNull(format, ##__VA_ARGS__) #else -#define LOGV(format, ...) CHRE_LOG(LOG_VERBOSE, stdout, format, ##__VA_ARGS__) +#define LOGV(format, ...) \ + ::android::chre::outputHostLog(ANDROID_LOG_VERBOSE, stdout, format, \ + __func__, __LINE__, ##__VA_ARGS__) #endif /** diff --git a/host/common/include/chre_host/metrics_reporter.h b/host/common/include/chre_host/metrics_reporter.h new file mode 100644 index 00000000..ea1ddfb5 --- /dev/null +++ b/host/common/include/chre_host/metrics_reporter.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#ifndef CHRE_HOST_METRICS_REPORTER_H_ +#define CHRE_HOST_METRICS_REPORTER_H_ + +#include <aidl/android/frameworks/stats/IStats.h> +#include <chre_atoms_log.h> +#include <mutex> + +namespace android::chre { + +class MetricsReporter { + public: + MetricsReporter() = default; + ~MetricsReporter() = default; + + MetricsReporter(const MetricsReporter &) = delete; + MetricsReporter &operator=(const MetricsReporter &) = delete; + + /** + * Creates and reports CHRE vendor atom and send it to stats_client. + * + * @param atom the vendor atom to be reported + * @return true if the metric was successfully reported, false otherwise. + */ + bool reportMetric(const aidl::android::frameworks::stats::VendorAtom &atom); + + /** + * Reports an AP Wakeup caused by a nanoapp. + * + * @return whether the operation was successful. + */ + bool logApWakeupOccurred(uint64_t nanoappId); + + /** + * Reports a nanoapp load failed metric. + * + * @return whether the operation was successful. + */ + bool logNanoappLoadFailed( + uint64_t nanoappId, + android::chre::Atoms::ChreHalNanoappLoadFailed::Type type, + android::chre::Atoms::ChreHalNanoappLoadFailed::Reason reason); + + /** + * Reports a PAL open failed metric. + * + * @return whether the operation was successful. + */ + bool logPalOpenFailed( + android::chre::Atoms::ChrePalOpenFailed::ChrePalType pal, + android::chre::Atoms::ChrePalOpenFailed::Type type); + + /** + * Reports a event queue snapshot reported metric. + * + * @return whether the operation was successful. + */ + bool logEventQueueSnapshotReported(int32_t snapshotChreGetTimeMs, + int32_t max_event_queue_size, + int32_t mean_event_queue_size, + int32_t num_dropped_events); + + /** + * Called when the binder dies for the stats service. + */ + void onBinderDied(); + + private: + /** + * Connects to the stats service or return nullptr if it cannot connect. + * This also adds a binder death handler to the service that will call + * onBinderDied on this. + * + * @return the stats service + */ + std::shared_ptr<aidl::android::frameworks::stats::IStats> getStatsService(); + + std::mutex mStatsServiceMutex; + std::shared_ptr<aidl::android::frameworks::stats::IStats> mStatsService = + nullptr; +}; + +} // namespace android::chre + +#endif // CHRE_HOST_METRICS_REPORTER_H_
\ No newline at end of file diff --git a/host/common/include/chre_host/pigweed/hal_channel_output.h b/host/common/include/chre_host/pigweed/hal_channel_output.h new file mode 100644 index 00000000..f190297b --- /dev/null +++ b/host/common/include/chre_host/pigweed/hal_channel_output.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef CHRE_PIGWEED_HAL_CHANNEL_OUTPUT_H_ +#define CHRE_PIGWEED_HAL_CHANNEL_OUTPUT_H_ + +#include <cstdint> + +#include "chre/util/non_copyable.h" +#include "chre_host/socket_client.h" +#include "pw_rpc/channel.h" +#include "pw_span/span.h" + +namespace android::chre { + +/** + * RPC Channel Output for native vendor processes. + */ +class HalChannelOutput : public ::chre::NonCopyable, + public pw::rpc::ChannelOutput { + public: + HalChannelOutput(android::chre::SocketClient &client, uint32_t hostEndpointId, + uint64_t serverNanoappId, size_t maxMessageLen) + : pw::rpc::ChannelOutput("CHRE"), + mServerNanoappId(serverNanoappId), + mHostEndpointId(hostEndpointId), + mMaxMessageLen(maxMessageLen), + mSocketClient(client) {} + + size_t MaximumTransmissionUnit() override; + + pw::Status Send(pw::span<const std::byte> buffer) override; + + private: + // Padding used to encode nanoapp messages. + static constexpr size_t kFlatBufferPadding = 80; + + const uint64_t mServerNanoappId; + const uint32_t mHostEndpointId; + const size_t mMaxMessageLen; + SocketClient &mSocketClient; +}; + +} // namespace android::chre + +#endif // CHRE_PIGWEED_HAL_CHANNEL_OUTPUT_H_
\ No newline at end of file diff --git a/host/common/include/chre_host/pigweed/hal_rpc_client.h b/host/common/include/chre_host/pigweed/hal_rpc_client.h new file mode 100644 index 00000000..c8cdee60 --- /dev/null +++ b/host/common/include/chre_host/pigweed/hal_rpc_client.h @@ -0,0 +1,202 @@ +/* + * 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. + */ + +#ifndef CHRE_PIGWEED_HAL_RPC_CLIENT_H_ +#define CHRE_PIGWEED_HAL_RPC_CLIENT_H_ + +#include <chrono> +#include <condition_variable> +#include <cstdint> +#include <memory> +#include <mutex> +#include <optional> +#include <string_view> + +#include <android-base/thread_annotations.h> + +#include "chre/util/pigweed/rpc_common.h" +#include "chre_host/pigweed/hal_channel_output.h" + +#include "chre/util/macros.h" +#include "chre/util/non_copyable.h" +#include "chre_host/host_protocol_host.h" +#include "chre_host/log.h" +#include "chre_host/pigweed/hal_channel_output.h" +#include "chre_host/socket_client.h" +#include "pw_rpc/client.h" + +namespace android::chre { + +/** + * RPC client helper to use with native vendor processes. + */ +class HalRpcClient : public ::chre::NonCopyable { + public: + /** + * Creates a RPC client helper. + * + * This method connects to the socket blocks until the initialization is + * complete. + * + # @param appName Name of the app. + * @param client A SocketClient that must not be already connected. + * @param socketCallbacks The callbacks to call on SocketClient events. + * @param hostEndpointId The host endpoint ID for the app. + * @param serverNanoappId The ID of the nanoapp providing the service. + * @return A pointer to a HalRpcClient. nullptr on error. + */ + static std::unique_ptr<HalRpcClient> createClient( + std::string_view appName, SocketClient &client, + sp<SocketClient::ICallbacks> socketCallbacks, uint16_t hostEndpointId, + uint64_t serverNanoappId); + + ~HalRpcClient() { + close(); + } + + /** + * Closes the RPC client and de-allocate resources. + * + * Note: This method is called from the destructor. However it can also be + * called explicitly. + */ + void close(); + + /** + * Returns a service client. + * + * NOTE: The template parameter must be set to the Pigweed client type, + * i.e. pw::rpc::pw_rpc::nanopb::<ServiceName>::Client + + * @return The service client. It has no value on errors. + */ + template <typename T> + std::optional<T> get(); + + /** + * Returns whether the server nanoapp supports the service. + * + * Also returns false when the nanoapp is not loaded. + * + * @return whether the service is published by the server. + */ + bool hasService(uint64_t id, uint32_t version); + + private: + /** Timeout for the requests to the daemon */ + static constexpr auto kRequestTimeout = std::chrono::milliseconds(2000); + + class Callbacks : public SocketClient::ICallbacks, + public IChreMessageHandlers { + public: + Callbacks(HalRpcClient *client, + sp<SocketClient::ICallbacks> socketCallbacks) + : mClient(client), mSocketCallbacks(socketCallbacks) {} + + /** Socket callbacks. */ + void onMessageReceived(const void *data, size_t length) override; + void onConnected() override; + void onConnectionAborted() override; + void onDisconnected() override; + + /** Message handlers. */ + void handleNanoappMessage( + const ::chre::fbs::NanoappMessageT &message) override; + void handleHubInfoResponse( + const ::chre::fbs::HubInfoResponseT &response) override; + void handleNanoappListResponse( + const ::chre::fbs::NanoappListResponseT &response) override; + + private: + HalRpcClient *mClient; + sp<SocketClient::ICallbacks> mSocketCallbacks; + }; + + HalRpcClient(std::string_view appName, SocketClient &client, + uint16_t hostEndpointId, uint64_t serverNanoappId) + : mServerNanoappId(serverNanoappId), + mHostEndpointId(hostEndpointId), + mAppName(appName), + mSocketClient(client) {} + + /** + * Initializes the RPC client helper. + * + * @param socketCallbacks The callbacks to call on SocketClient events. + * @return Whether the initialization was successful. + */ + bool init(sp<SocketClient::ICallbacks> socketCallbacks); + + /** @return the Pigweed RPC channel ID */ + uint32_t GetChannelId() { + return ::chre::kChannelIdHostClient | mHostEndpointId; + } + + /** + * Notifies CHRE that the host endpoint has connected. + * + * Needed as the RPC Server helper will retrieve the host endpoint metadata + * when servicing a request. + */ + bool notifyEndpointConnected(); + + /** Notifies CHRE that the host endpoint has disconnected. */ + bool notifyEndpointDisconnected(); + + /** Query CHRE to retrieve the maximum message length. */ + bool retrieveMaxMessageLen(); + + /** Query CHRE to retrieve the services published by the server nanoapp. */ + bool retrieveServices(); + + const uint64_t mServerNanoappId; + const uint16_t mHostEndpointId; + const std::string mAppName; + SocketClient &mSocketClient; + std::unique_ptr<HalChannelOutput> mChannelOutput; + pw::rpc::Client mRpcClient; + bool mIsChannelOpened = false; + + /** Request Hub Info. */ + std::mutex mHubInfoMutex; + size_t mMaxMessageLen GUARDED_BY(mHubInfoMutex); + std::condition_variable mHubInfoCond; + + /** Request Nanoapps. */ + std::mutex mNanoappMutex; + std::vector<::chre::fbs::NanoappRpcServiceT> mServices + GUARDED_BY(mNanoappMutex); + std::condition_variable mNanoappCond; +}; + +template <typename T> +std::optional<T> HalRpcClient::get() { + if (mChannelOutput == nullptr) { + LOGE("No channel output"); + return {}; + } + + if (!mIsChannelOpened) { + mRpcClient.OpenChannel(GetChannelId(), *mChannelOutput); + mIsChannelOpened = true; + } + + return T(mRpcClient, GetChannelId()); +} + +} // namespace android::chre + +#endif // CHRE_PIGWEED_HAL_RPC_CLIENT_H_
\ No newline at end of file diff --git a/host/common/include/chre_host/preloaded_nanoapp_loader.h b/host/common/include/chre_host/preloaded_nanoapp_loader.h index a605ae74..3a3a2fc0 100644 --- a/host/common/include/chre_host/preloaded_nanoapp_loader.h +++ b/host/common/include/chre_host/preloaded_nanoapp_loader.h @@ -18,10 +18,12 @@ #define CHRE_HOST_PRELOADED_NANOAPP_LOADER_H_ #include <android/binder_to_string.h> +#include <atomic> #include <cstdint> #include <mutex> #include <optional> #include <string> +#include <unordered_set> #include <utility> #include "chre_connection.h" @@ -46,7 +48,7 @@ class PreloadedNanoappLoader { public: explicit PreloadedNanoappLoader(ChreConnection *connection, std::string configPath) - : mConnection(connection), mConfigPath(std::move(configPath)){}; + : mConnection(connection), mConfigPath(std::move(configPath)) {} ~PreloadedNanoappLoader() = default; /** @@ -60,8 +62,14 @@ class PreloadedNanoappLoader { * ]} * * The napp_header and so files will both be used. + * + * @param selectedNanoappIds only nanoapp ids in this set will be loaded if it + * is set. Otherwise the default value means every preloaded nanoapp will be + * loaded. */ - void loadPreloadedNanoapps(); + bool loadPreloadedNanoapps( + const std::optional<const std::unordered_set<uint64_t>> + &selectedNanoappIds = std::nullopt); /** Callback function to handle the response from CHRE. */ bool onLoadNanoappResponse(const ::chre::fbs::LoadNanoappResponseT &response, @@ -72,35 +80,28 @@ class PreloadedNanoappLoader { /** Returns true if the loading is ongoing. */ [[nodiscard]] bool isPreloadOngoing() const { return mIsPreloadingOngoing; - }; + } private: + /** Tracks the transaction state of the ongoing nanoapp loading */ + struct Transaction { + uint32_t transactionId; + size_t fragmentId; + }; + /** Timeout value of waiting for the response of a fragmented load */ static constexpr auto kTimeoutInMs = std::chrono::milliseconds(2000); /** - * Loads a preloaded nanoapp given a filename. - * - * This function allows the transaction to complete before the nanoapp starts - * so the server can start serving requests as soon as possible. - * - * @param directory The directory to load the nanoapp from. - * @param name The filename of the nanoapp to load. - * @param transactionId The transaction ID to use when loading the app. - */ - void loadPreloadedNanoapp(const std::string &directory, - const std::string &name, uint32_t transactionId); - - /** * Loads a preloaded nanoapp. * - * @param header The nanoapp header binary blob. - * @param nanoapp The nanoapp binary. + * @param appHeader The nanoapp header binary blob. + * @param nanoappFileName The nanoapp binary file name. * @param transactionId The transaction ID identifying this load transaction. * @return true if successful, false otherwise. */ - bool loadNanoapp(const std::vector<uint8_t> &header, - const std::vector<uint8_t> &nanoapp, uint32_t transactionId); + bool loadNanoapp(const NanoAppBinaryHeader *appHeader, + const std::string &nanoappFileName, uint32_t transactionId); /** * Chunks the nanoapp binary into fragments and load each fragment @@ -123,11 +124,6 @@ class PreloadedNanoappLoader { [[nodiscard]] bool verifyFragmentLoadResponse( const ::chre::fbs::LoadNanoappResponseT &response) const; - /** Tracks the transaction state of the ongoing nanoapp loading */ - struct Transaction { - uint32_t transactionId; - size_t fragmentId; - }; Transaction mPreloadedNanoappPendingTransaction{0, 0}; /** The value of this promise carries the result in the load response. */ @@ -136,7 +132,7 @@ class PreloadedNanoappLoader { /** The mutex used to guard states change for preloading. */ std::mutex mPreloadedNanoappsMutex; - bool mIsPreloadingOngoing = false; + std::atomic_bool mIsPreloadingOngoing = false; ChreConnection *mConnection; std::string mConfigPath; diff --git a/host/common/include/chre_host/socket_server.h b/host/common/include/chre_host/socket_server.h index 6a19ecbe..6e5b8404 100644 --- a/host/common/include/chre_host/socket_server.h +++ b/host/common/include/chre_host/socket_server.h @@ -28,8 +28,7 @@ #include <android-base/macros.h> #include <cutils/sockets.h> -namespace android { -namespace chre { +namespace android::chre { class SocketServer { public: @@ -81,7 +80,7 @@ class SocketServer { */ bool sendToClientById(const void *data, size_t length, uint16_t clientId); - void shutdownServer() { + static void shutdownServer() { sSignalReceived = true; } @@ -93,8 +92,18 @@ class SocketServer { static_cast<int>(kMaxActiveClients); static constexpr size_t kMaxPacketSize = 1024 * 1024; + // This is the same value as defined in + // host/hal_generic/common/hal_client_id.h. It is redefined here to avoid + // adding dependency path at multiple places for such a temporary change, + // which will be removed after migrating generic HAL to multiclient HAL. + static constexpr uint16_t kMaxHalClientId = 0x1ff; + int mSockFd = INVALID_SOCKET; - uint16_t mNextClientId = 1; + // Socket client id and Hal client id are using the same field in the fbs + // message. To keep their id range disjoint enables message routing for both + // at the same time. There are 0xffff - 0x01ff = 0xfe00 (65024) socket + // client ids to use, which should be more than enough. + uint16_t mNextClientId = kMaxHalClientId + 1; // TODO: std::vector-ify this struct pollfd mPollFds[1 + kMaxActiveClients] = {}; @@ -116,16 +125,19 @@ class SocketServer { ClientMessageCallback mClientMessageCallback; void acceptClientConnection(); + void disconnectClient(int clientSocket); + void handleClientData(int clientSocket); + bool sendToClientSocket(const void *data, size_t length, int clientSocket, uint16_t clientId); + void serviceSocket(); static std::atomic<bool> sSignalReceived; }; -} // namespace chre -} // namespace android +} // namespace android::chre #endif // CHRE_HOST_SOCKET_SERVER_H_ diff --git a/host/common/include/chre_host/time_syncer.h b/host/common/include/chre_host/time_syncer.h index 9518ea25..f73e8ecb 100644 --- a/host/common/include/chre_host/time_syncer.h +++ b/host/common/include/chre_host/time_syncer.h @@ -26,11 +26,9 @@ namespace android::chre { using hardware::contexthub::common::implementation::ChreConnection; -/** The class synchronizes time between the Context hub and Android. */ -class TimeSyncer { +/** The functions synchronizing time between the Context hub and Android. */ +class TimeSyncer final { public: - explicit TimeSyncer(ChreConnection *connection) : mConnection(connection) {} - /** * Sends time sync message to Context hub and retries numRetries times until * success. @@ -40,7 +38,9 @@ class TimeSyncer { * * @return true if success, false otherwise. */ - bool sendTimeSyncWithRetry(size_t numOfRetries, useconds_t retryDelayUs); + static bool sendTimeSyncWithRetry(ChreConnection *connection, + size_t numOfRetries, + useconds_t retryDelayUs); /** * Sends a time sync message to Context hub for once. @@ -50,10 +50,10 @@ class TimeSyncer { * * @return true if success, false otherwise. */ - bool sendTimeSync(); + static bool sendTimeSync(ChreConnection *connection); private: - ChreConnection *mConnection; + TimeSyncer() = default; }; } // namespace android::chre diff --git a/host/common/log.cc b/host/common/log.cc new file mode 100644 index 00000000..8f3249ff --- /dev/null +++ b/host/common/log.cc @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#include <cstdio> + +#include "chre_host/log.h" + +namespace android::chre { + +void outputHostLog(int priority, FILE *stream, const char *format, + const char *func, unsigned int line, ...) { + va_list args; + va_start(args, line); + LOG_PRI_VA(priority, LOG_TAG, format, args); + va_end(args); + va_start(args, line); + fprintf(stream, "%s:%d: ", func, line); + vfprintf(stream, format, args); + fprintf(stream, "\n"); + fflush(stream); // flush the buffer to print out the log immediately + va_end(args); +} + +} // namespace android::chre
\ No newline at end of file diff --git a/host/common/metrics_reporter.cc b/host/common/metrics_reporter.cc new file mode 100644 index 00000000..490c80ac --- /dev/null +++ b/host/common/metrics_reporter.cc @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#include "chre_host/metrics_reporter.h" + +#include <android/binder_manager.h> +#include <chre_atoms_log.h> +#include <chre_host/log.h> +#include <limits> +#include <mutex> + +namespace android::chre { + +using ::aidl::android::frameworks::stats::IStats; +using ::aidl::android::frameworks::stats::VendorAtom; +using ::aidl::android::frameworks::stats::VendorAtomValue; +using ::android::chre::Atoms::CHRE_AP_WAKE_UP_OCCURRED; +using ::android::chre::Atoms::CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED; +using ::android::chre::Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED; +using ::android::chre::Atoms::CHRE_PAL_OPEN_FAILED; +using ::android::chre::Atoms::ChreHalNanoappLoadFailed; +using ::android::chre::Atoms::ChrePalOpenFailed; + +std::shared_ptr<IStats> MetricsReporter::getStatsService() { + const std::string statsServiceName = + std::string(IStats::descriptor).append("/default"); + if (!AServiceManager_isDeclared(statsServiceName.c_str())) { + LOGE("Stats service is not declared."); + return nullptr; + } + + ndk::SpAIBinder statsServiceBinder = + ndk::SpAIBinder(AServiceManager_waitForService(statsServiceName.c_str())); + if (statsServiceBinder.get() == nullptr) { + LOGE("Failed to get the IStats service binder"); + return nullptr; + } + + binder_status_t status = AIBinder_linkToDeath( + statsServiceBinder.get(), AIBinder_DeathRecipient_new([](void *cookie) { + MetricsReporter *metricsReporter = + static_cast<MetricsReporter *>(cookie); + metricsReporter->onBinderDied(); + }), + this); + if (status != STATUS_OK) { + LOGE("Failed to link to death the stats service binder"); + return nullptr; + } + + std::shared_ptr<IStats> statsService = IStats::fromBinder(statsServiceBinder); + if (statsService == nullptr) { + LOGE("Failed to get IStats service"); + return nullptr; + } + return statsService; +} + +bool MetricsReporter::reportMetric(const VendorAtom &atom) { + ndk::ScopedAStatus ret; + { + std::lock_guard<std::mutex> lock(mStatsServiceMutex); + if (mStatsService == nullptr) { + mStatsService = getStatsService(); + if (mStatsService == nullptr) { + return false; + } + } + + ret = mStatsService->reportVendorAtom(atom); + } + + if (!ret.isOk()) { + LOGE("Failed to report vendor atom"); + } + return ret.isOk(); +} + +bool MetricsReporter::logApWakeupOccurred(uint64_t nanoappId) { + std::vector<VendorAtomValue> values(1); + values[0].set<VendorAtomValue::longValue>(nanoappId); + + const VendorAtom atom{ + .atomId = CHRE_AP_WAKE_UP_OCCURRED, + .values{std::move(values)}, + }; + + return reportMetric(atom); +} + +bool MetricsReporter::logNanoappLoadFailed( + uint64_t nanoappId, ChreHalNanoappLoadFailed::Type type, + ChreHalNanoappLoadFailed::Reason reason) { + std::vector<VendorAtomValue> values(3); + values[0].set<VendorAtomValue::longValue>(nanoappId); + values[1].set<VendorAtomValue::intValue>(type); + values[2].set<VendorAtomValue::intValue>(reason); + + const VendorAtom atom{ + .atomId = CHRE_HAL_NANOAPP_LOAD_FAILED, + .values{std::move(values)}, + }; + + return reportMetric(atom); +} + +bool MetricsReporter::logPalOpenFailed(ChrePalOpenFailed::ChrePalType pal, + ChrePalOpenFailed::Type type) { + std::vector<VendorAtomValue> values(2); + values[0].set<VendorAtomValue::intValue>(pal); + values[1].set<VendorAtomValue::intValue>(type); + + const VendorAtom atom{ + .atomId = CHRE_PAL_OPEN_FAILED, + .values{std::move(values)}, + }; + + return reportMetric(atom); +} + +bool MetricsReporter::logEventQueueSnapshotReported( + int32_t snapshotChreGetTimeMs, int32_t maxEventQueueSize, + int32_t meanEventQueueSize, int32_t numDroppedEvents) { + std::vector<VendorAtomValue> values(6); + values[0].set<VendorAtomValue::intValue>(snapshotChreGetTimeMs); + values[1].set<VendorAtomValue::intValue>(maxEventQueueSize); + values[2].set<VendorAtomValue::intValue>(meanEventQueueSize); + values[3].set<VendorAtomValue::intValue>(numDroppedEvents); + + // TODO(b/298459533): Implement these two values + // Last two values are not currently populated and will be implemented + // later. To avoid confusion of the interpretation, we use UINT32_MAX + // as a placeholder value. + values[4].set<VendorAtomValue::longValue>( + std::numeric_limits<int64_t>::max()); + values[5].set<VendorAtomValue::longValue>( + std::numeric_limits<int64_t>::max()); + + const VendorAtom atom{ + .atomId = CHRE_EVENT_QUEUE_SNAPSHOT_REPORTED, + .values{std::move(values)}, + }; + + return reportMetric(atom); +} + +void MetricsReporter::onBinderDied() { + LOGI("MetricsReporter: stats service died - reconnecting"); + + std::lock_guard<std::mutex> lock(mStatsServiceMutex); + mStatsService.reset(); + mStatsService = getStatsService(); +} + +} // namespace android::chre diff --git a/host/common/pigweed/hal_channel_output.cc b/host/common/pigweed/hal_channel_output.cc new file mode 100644 index 00000000..f61d719e --- /dev/null +++ b/host/common/pigweed/hal_channel_output.cc @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#include "chre_host/pigweed/hal_channel_output.h" + +#include <cstdint> + +#include "chre/event.h" +#include "chre/util/pigweed/rpc_common.h" +#include "chre_host/host_protocol_host.h" +#include "chre_host/log.h" +#include "chre_host/socket_client.h" +#include "pw_rpc/channel.h" +#include "pw_span/span.h" + +namespace android::chre { + +using ::flatbuffers::FlatBufferBuilder; + +size_t HalChannelOutput::MaximumTransmissionUnit() { + return mMaxMessageLen > kFlatBufferPadding + ? mMaxMessageLen - kFlatBufferPadding + : 0; +} + +pw::Status HalChannelOutput::Send(pw::span<const std::byte> buffer) { + if (buffer.size() > 0) { + FlatBufferBuilder builder(buffer.size() + kFlatBufferPadding); + + HostProtocolHost::encodeNanoappMessage( + builder, mServerNanoappId, CHRE_MESSAGE_TYPE_RPC, mHostEndpointId, + buffer.data(), buffer.size()); + + if (!mSocketClient.sendMessage(builder.GetBufferPointer(), + builder.GetSize())) { + LOGE("Failed to send message"); + return PW_STATUS_UNKNOWN; + } + } + + return PW_STATUS_OK; +} + +} // namespace android::chre
\ No newline at end of file diff --git a/host/common/pigweed/hal_rpc_client.cc b/host/common/pigweed/hal_rpc_client.cc new file mode 100644 index 00000000..d9a91468 --- /dev/null +++ b/host/common/pigweed/hal_rpc_client.cc @@ -0,0 +1,228 @@ +/* + * 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. + */ + +#include "chre_host/pigweed/hal_rpc_client.h" + +#include <cstdint> +#include <memory> + +#include "chre/event.h" +#include "chre/util/pigweed/rpc_common.h" +#include "chre_host/host_protocol_host.h" +#include "chre_host/log.h" +#include "chre_host/pigweed/hal_channel_output.h" + +namespace android::chre { + +using ::chre::fbs::HubInfoResponseT; +using ::chre::fbs::NanoappListEntryT; +using ::chre::fbs::NanoappListResponseT; +using ::chre::fbs::NanoappMessageT; +using ::chre::fbs::NanoappRpcServiceT; +using ::flatbuffers::FlatBufferBuilder; + +std::unique_ptr<HalRpcClient> HalRpcClient::createClient( + std::string_view appName, SocketClient &client, + sp<SocketClient::ICallbacks> socketCallbacks, uint16_t hostEndpointId, + uint64_t serverNanoappId) { + auto rpcClient = std::unique_ptr<HalRpcClient>( + new HalRpcClient(appName, client, hostEndpointId, serverNanoappId)); + + if (!rpcClient->init(socketCallbacks)) { + return nullptr; + } + + return rpcClient; +} + +bool HalRpcClient::hasService(uint64_t id, uint32_t version) { + std::lock_guard lock(mNanoappMutex); + for (const NanoappRpcServiceT &service : mServices) { + if (service.id == id && service.version == version) { + return true; + } + } + + return false; +} + +void HalRpcClient::close() { + if (mIsChannelOpened) { + mRpcClient.CloseChannel(GetChannelId()); + mIsChannelOpened = false; + } + if (mSocketClient.isConnected()) { + notifyEndpointDisconnected(); + mSocketClient.disconnect(); + } +} + +// Private methods + +bool HalRpcClient::init(sp<SocketClient::ICallbacks> socketCallbacks) { + if (mSocketClient.isConnected()) { + LOGE("Already connected to socket"); + return false; + } + + auto callbacks = sp<Callbacks>::make(this, socketCallbacks); + + if (!mSocketClient.connect("chre", callbacks)) { + LOGE("Couldn't connect to socket"); + return false; + } + + bool success = true; + + if (!notifyEndpointConnected()) { + LOGE("Failed to notify connection"); + success = false; + } else if (!retrieveMaxMessageLen()) { + LOGE("Failed to retrieve the max message length"); + success = false; + } else if (!retrieveServices()) { + LOGE("Failed to retrieve the services"); + success = false; + } + + if (!success) { + mSocketClient.disconnect(); + return false; + } + + { + std::lock_guard lock(mHubInfoMutex); + mChannelOutput = std::make_unique<HalChannelOutput>( + mSocketClient, mHostEndpointId, mServerNanoappId, mMaxMessageLen); + } + + return true; +} + +bool HalRpcClient::notifyEndpointConnected() { + FlatBufferBuilder builder(64); + HostProtocolHost::encodeHostEndpointConnected( + builder, mHostEndpointId, CHRE_HOST_ENDPOINT_TYPE_NATIVE, mAppName, + /* attributionTag= */ ""); + return mSocketClient.sendMessage(builder.GetBufferPointer(), + builder.GetSize()); +} + +bool HalRpcClient::notifyEndpointDisconnected() { + FlatBufferBuilder builder(64); + HostProtocolHost::encodeHostEndpointDisconnected(builder, mHostEndpointId); + return mSocketClient.sendMessage(builder.GetBufferPointer(), + builder.GetSize()); +} + +bool HalRpcClient::retrieveMaxMessageLen() { + FlatBufferBuilder builder(64); + HostProtocolHost::encodeHubInfoRequest(builder); + if (!mSocketClient.sendMessage(builder.GetBufferPointer(), + builder.GetSize())) { + return false; + } + + std::unique_lock lock(mHubInfoMutex); + std::cv_status status = mHubInfoCond.wait_for(lock, kRequestTimeout); + + return status != std::cv_status::timeout; +} + +bool HalRpcClient::retrieveServices() { + FlatBufferBuilder builder(64); + HostProtocolHost::encodeNanoappListRequest(builder); + + if (!mSocketClient.sendMessage(builder.GetBufferPointer(), + builder.GetSize())) { + return false; + } + + std::unique_lock lock(mNanoappMutex); + std::cv_status status = mNanoappCond.wait_for(lock, kRequestTimeout); + + return status != std::cv_status::timeout; +} + +// Socket callbacks. + +void HalRpcClient::Callbacks::onMessageReceived(const void *data, + size_t length) { + if (!android::chre::HostProtocolHost::decodeMessageFromChre(data, length, + *this)) { + LOGE("Failed to decode message"); + } + mSocketCallbacks->onMessageReceived(data, length); +} + +void HalRpcClient::Callbacks::onConnected() { + mSocketCallbacks->onConnected(); +} + +void HalRpcClient::Callbacks::onConnectionAborted() { + mSocketCallbacks->onConnectionAborted(); +} + +void HalRpcClient::Callbacks::onDisconnected() { + // Close connections on CHRE reset. + mClient->close(); + mSocketCallbacks->onDisconnected(); +} + +// Message handlers. + +void HalRpcClient::Callbacks::handleNanoappMessage( + const NanoappMessageT &message) { + if (message.message_type == CHRE_MESSAGE_TYPE_RPC) { + pw::span packet(reinterpret_cast<const std::byte *>(message.message.data()), + message.message.size()); + + if (message.app_id == mClient->mServerNanoappId) { + pw::Status status = mClient->mRpcClient.ProcessPacket(packet); + if (status != pw::OkStatus()) { + LOGE("Failed to process the packet"); + } + } + } +} + +void HalRpcClient::Callbacks::handleHubInfoResponse( + const HubInfoResponseT &response) { + { + std::lock_guard lock(mClient->mHubInfoMutex); + mClient->mMaxMessageLen = response.max_msg_len; + } + mClient->mHubInfoCond.notify_all(); +} + +void HalRpcClient::Callbacks::handleNanoappListResponse( + const NanoappListResponseT &response) { + for (const std::unique_ptr<NanoappListEntryT> &nanoapp : response.nanoapps) { + if (nanoapp->app_id == mClient->mServerNanoappId) { + std::lock_guard lock(mClient->mNanoappMutex); + mClient->mServices.clear(); + mClient->mServices.reserve(nanoapp->rpc_services.size()); + for (const std::unique_ptr<NanoappRpcServiceT> &service : + nanoapp->rpc_services) { + mClient->mServices.push_back(*service); + } + } + } + + mClient->mNanoappCond.notify_all(); +} + +} // namespace android::chre
\ No newline at end of file diff --git a/host/common/preloaded_nanoapp_loader.cc b/host/common/preloaded_nanoapp_loader.cc index b6fb892b..6739d5d2 100644 --- a/host/common/preloaded_nanoapp_loader.cc +++ b/host/common/preloaded_nanoapp_loader.cc @@ -28,82 +28,108 @@ namespace android::chre { using android::chre::readFileContents; using android::hardware::contexthub::common::implementation::kHalId; +namespace { + +bool getNanoappHeaderFromFile(const char *headerFileName, + std::vector<uint8_t> &headerBuffer) { + if (!readFileContents(headerFileName, headerBuffer)) { + LOGE("Failed to read header file for nanoapp %s", headerFileName); + return false; + } + if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) { + LOGE("Nanoapp binary's header size is incorrect"); + return false; + } + return true; +} + +inline bool shouldSkipNanoapp( + std::optional<const std::unordered_set<uint64_t>> selectedNanoappIds, + uint64_t appId) { + return selectedNanoappIds.has_value() && + selectedNanoappIds->find(appId) == selectedNanoappIds->end(); +} +} // namespace + void PreloadedNanoappLoader::getPreloadedNanoappIds( std::vector<uint64_t> &out_preloadedNanoappIds) { std::vector<std::string> nanoappNames; std::string directory; out_preloadedNanoappIds.clear(); - bool success = - getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoappNames); - if (!success) { + if (!getPreloadedNanoappsFromConfigFile(mConfigPath, directory, + nanoappNames)) { LOGE("Failed to parse preloaded nanoapps config file"); } for (const std::string &nanoappName : nanoappNames) { - std::string headerFile = directory + "/" + nanoappName + ".napp_header"; + std::string headerFileName = directory + "/" + nanoappName + ".napp_header"; std::vector<uint8_t> headerBuffer; - if (!readFileContents(headerFile.c_str(), headerBuffer)) { - LOGE("Cannot read header file: %s", headerFile.c_str()); - continue; - } - if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) { - LOGE("Header size mismatch"); + if (!getNanoappHeaderFromFile(headerFileName.c_str(), headerBuffer)) { + LOGE("Failed to parse the nanoapp header for %s", headerFileName.c_str()); continue; } - const auto *appHeader = + auto header = reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data()); - out_preloadedNanoappIds.emplace_back(appHeader->appId); + out_preloadedNanoappIds.emplace_back(header->appId); } } -void PreloadedNanoappLoader::loadPreloadedNanoapps() { +bool PreloadedNanoappLoader::loadPreloadedNanoapps( + const std::optional<const std::unordered_set<uint64_t>> + &selectedNanoappIds) { std::string directory; std::vector<std::string> nanoapps; - - bool success = - getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoapps); - if (!success) { + if (!getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoapps)) { LOGE("Failed to load any preloaded nanoapp"); - } else { - mIsPreloadingOngoing = true; - for (uint32_t i = 0; i < nanoapps.size(); ++i) { - loadPreloadedNanoapp(directory, nanoapps[i], i); - } - mIsPreloadingOngoing = false; + return false; } -} - -void PreloadedNanoappLoader::loadPreloadedNanoapp(const std::string &directory, - const std::string &name, - uint32_t transactionId) { - std::vector<uint8_t> headerBuffer; - std::vector<uint8_t> nanoappBuffer; - - std::string headerFilename = directory + "/" + name + ".napp_header"; - std::string nanoappFilename = directory + "/" + name + ".so"; - - if (!readFileContents(headerFilename.c_str(), headerBuffer) || - !readFileContents(nanoappFilename.c_str(), nanoappBuffer) || - !loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) { - LOGE("Failed to load nanoapp: '%s'", name.c_str()); + if (mIsPreloadingOngoing.exchange(true)) { + LOGE("Preloading is ongoing. A new request shouldn't happen."); + return false; + } + bool success = true; + for (uint32_t i = 0; i < nanoapps.size(); ++i) { + std::string headerFilename = directory + "/" + nanoapps[i] + ".napp_header"; + std::string nanoappFilename = directory + "/" + nanoapps[i] + ".so"; + // parse the header + std::vector<uint8_t> headerBuffer; + if (!getNanoappHeaderFromFile(headerFilename.c_str(), headerBuffer)) { + LOGE("Failed to parse the nanoapp header for %s", + nanoappFilename.c_str()); + success = false; + continue; + } + const auto header = + reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data()); + // check if the app should be skipped + if (shouldSkipNanoapp(selectedNanoappIds, header->appId)) { + LOGI("Loading of %s is skipped.", headerFilename.c_str()); + continue; + } + // load the binary + if (!loadNanoapp(header, nanoappFilename, i)) { + success = false; + } } + mIsPreloadingOngoing.store(false); + return success; } -bool PreloadedNanoappLoader::loadNanoapp(const std::vector<uint8_t> &header, - const std::vector<uint8_t> &nanoapp, +bool PreloadedNanoappLoader::loadNanoapp(const NanoAppBinaryHeader *appHeader, + const std::string &nanoappFileName, uint32_t transactionId) { - if (header.size() != sizeof(NanoAppBinaryHeader)) { - LOGE("Nanoapp binary's header size is incorrect"); + // parse the binary + std::vector<uint8_t> nanoappBuffer; + if (!readFileContents(nanoappFileName.c_str(), nanoappBuffer)) { + LOGE("Unable to read %s.", nanoappFileName.c_str()); return false; } - const auto *appHeader = - reinterpret_cast<const NanoAppBinaryHeader *>(header.data()); - // Build the target API version from major and minor. uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) | (appHeader->targetChreApiMinorVersion << 16); return sendFragmentedLoadAndWaitForEachResponse( appHeader->appId, appHeader->appVersion, appHeader->flags, - targetApiVersion, nanoapp.data(), nanoapp.size(), transactionId); + targetApiVersion, nanoappBuffer.data(), nanoappBuffer.size(), + transactionId); } bool PreloadedNanoappLoader::sendFragmentedLoadAndWaitForEachResponse( diff --git a/host/common/socket_client.cc b/host/common/socket_client.cc index a8c18d9c..cf172073 100644 --- a/host/common/socket_client.cc +++ b/host/common/socket_client.cc @@ -177,6 +177,10 @@ void SocketClient::receiveThread() { ssize_t bytesReceived = recv(mSockFd, buffer, sizeof(buffer), 0); if (bytesReceived < 0) { LOG_ERROR("Exiting RX thread", errno); + if (!mGracefulShutdown) { + LOGI("Force onDisconnected"); + mCallbacks->onDisconnected(); + } break; } else if (bytesReceived == 0) { if (!mGracefulShutdown) { diff --git a/host/common/socket_server.cc b/host/common/socket_server.cc index e5403a23..1c2681a6 100644 --- a/host/common/socket_server.cc +++ b/host/common/socket_server.cc @@ -19,6 +19,7 @@ #include <poll.h> #include <cassert> +#include <cerrno> #include <cinttypes> #include <csignal> #include <cstdlib> @@ -172,6 +173,9 @@ void SocketServer::handleClientData(int clientSocket) { if (packetSize < 0) { LOGE("Couldn't get packet from client %" PRIu16 ": %s", clientId, strerror(errno)); + if (ENOTCONN == errno) { + disconnectClient(clientSocket); + } } else if (packetSize == 0) { LOGI("Client %" PRIu16 " disconnected", clientId); disconnectClient(clientSocket); diff --git a/host/common/test/chre_test_client.cc b/host/common/test/chre_test_client.cc index 57da705d..397c66c0 100644 --- a/host/common/test/chre_test_client.cc +++ b/host/common/test/chre_test_client.cc @@ -14,26 +14,29 @@ * limitations under the License. */ -#include "chre/util/nanoapp/app_id.h" -#include "chre/util/system/napp_header_utils.h" -#include "chre_host/file_stream.h" -#include "chre_host/host_protocol_host.h" -#include "chre_host/log.h" -#include "chre_host/napp_header.h" -#include "chre_host/socket_client.h" - #include <inttypes.h> #include <sys/socket.h> #include <sys/types.h> +#include <chrono> +#include <condition_variable> #include <fstream> #include <future> +#include <mutex> #include <sstream> #include <thread> #include <cutils/sockets.h> #include <utils/StrongPointer.h> +#include "chre/util/nanoapp/app_id.h" +#include "chre/util/system/napp_header_utils.h" +#include "chre_host/file_stream.h" +#include "chre_host/host_protocol_host.h" +#include "chre_host/log.h" +#include "chre_host/napp_header.h" +#include "chre_host/socket_client.h" + /** * @file * A test utility that connects to the CHRE daemon that runs on the apps @@ -47,6 +50,7 @@ */ using android::sp; +using android::chre::FragmentedLoadRequest; using android::chre::FragmentedLoadTransaction; using android::chre::getStringFromByteVector; using android::chre::HostProtocolHost; @@ -62,15 +66,34 @@ namespace fbs = ::chre::fbs; namespace { -//! The host endpoint we use when sending; set to CHRE_HOST_ENDPOINT_UNSPECIFIED -//! Other clients below the HAL may use a value above 0x8000 to enable unicast -//! messaging (currently requires internal coordination to avoid conflict; -//! in the future these should be assigned by the daemon). -constexpr uint16_t kHostEndpoint = 0xfffe; +//! The host endpoint we use when sending; Clients may use a value above +//! 0x8000 to enable unicast messaging (currently requires internal coordination +//! to avoid conflict). +constexpr uint16_t kHostEndpoint = 0x8002; constexpr uint32_t kDefaultAppVersion = 1; constexpr uint32_t kDefaultApiVersion = 0x01000000; +// Timeout for loading a nanoapp fragment. +static constexpr auto kFragmentTimeout = std::chrono::milliseconds(2000); + +enum class LoadingStatus { + kLoading, + kSuccess, + kError, +}; + +// State of a nanoapp fragment. +struct FragmentStatus { + size_t id; + LoadingStatus loadStatus; +}; + +// State of the current nanoapp fragment. +std::mutex gFragmentMutex; +std::condition_variable gFragmentCondVar; +FragmentStatus gFragmentStatus; + class SocketCallbacks : public SocketClient::ICallbacks, public IChreMessageHandlers { public: @@ -127,8 +150,21 @@ class SocketCallbacks : public SocketClient::ICallbacks, void handleLoadNanoappResponse( const fbs::LoadNanoappResponseT &response) override { - LOGI("Got load nanoapp response, transaction ID 0x%" PRIx32 " result %d", - response.transaction_id, response.success); + LOGI("Got load nanoapp response, transaction ID 0x%" PRIx32 + " fragment %" PRIx32 " result %d", + response.transaction_id, response.fragment_id, response.success); + + { + std::lock_guard lock(gFragmentMutex); + if (response.fragment_id != gFragmentStatus.id) { + gFragmentStatus.loadStatus = LoadingStatus::kError; + } else { + gFragmentStatus.loadStatus = + response.success ? LoadingStatus::kSuccess : LoadingStatus::kError; + } + } + + gFragmentCondVar.notify_all(); } void handleUnloadNanoappResponse( @@ -187,20 +223,48 @@ void sendMessageToNanoapp(SocketClient &client) { void sendNanoappLoad(SocketClient &client, uint64_t appId, uint32_t appVersion, uint32_t apiVersion, uint32_t appFlags, const std::vector<uint8_t> &binary) { - // Perform loading with 1 fragment for simplicity - FlatBufferBuilder builder(binary.size() + 128); FragmentedLoadTransaction transaction = FragmentedLoadTransaction( - 1 /* transactionId */, appId, appVersion, appFlags, apiVersion, binary, - binary.size() /* fragmentSize */); - HostProtocolHost::encodeFragmentedLoadNanoappRequest( - builder, transaction.getNextRequest()); - - LOGI("Sending load nanoapp request (%" PRIu32 - " bytes total w/%zu bytes of " - "payload)", - builder.GetSize(), binary.size()); - if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { - LOGE("Failed to send message"); + 1 /* transactionId */, appId, appVersion, appFlags, apiVersion, binary); + + bool success = true; + while (!transaction.isComplete()) { + const FragmentedLoadRequest &request = transaction.getNextRequest(); + LOGI("Loading nanoapp fragment %zu", request.fragmentId); + + FlatBufferBuilder builder(request.binary.size() + 128); + HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, request); + + std::unique_lock lock(gFragmentMutex); + gFragmentStatus = {.id = request.fragmentId, + .loadStatus = LoadingStatus::kLoading}; + lock.unlock(); + + if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { + LOGE("Failed to send fragment"); + success = false; + break; + } + + lock.lock(); + std::cv_status status = gFragmentCondVar.wait_for(lock, kFragmentTimeout); + + if (status == std::cv_status::timeout) { + success = false; + LOGE("Timeout loading the fragment"); + break; + } + + if (gFragmentStatus.loadStatus != LoadingStatus::kSuccess) { + LOGE("Error loading the fragment"); + success = false; + break; + } + } + + if (success) { + LOGI("Nanoapp loaded successfully"); + } else { + LOGE("Error loading the nanoapp"); } } diff --git a/host/common/test/chre_test_rpc.cc b/host/common/test/chre_test_rpc.cc new file mode 100644 index 00000000..64ad98c7 --- /dev/null +++ b/host/common/test/chre_test_rpc.cc @@ -0,0 +1,148 @@ +/* + * 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. + */ + +#include <inttypes.h> +#include <utils/StrongPointer.h> +#include <chrono> +#include <cstdint> +#include <future> +#include <thread> + +#include "chre/util/nanoapp/app_id.h" +#include "chre_host/host_protocol_host.h" +#include "chre_host/log.h" +#include "chre_host/pigweed/hal_rpc_client.h" +#include "chre_host/socket_client.h" +#include "rpc_world.pb.h" +#include "rpc_world.rpc.pb.h" + +/** + * @file + * Test RPC by calling a service provided by the rpc_world nanoapp. + * + * Usage: + * 1. Compile and push the rpc_world nanoapp to the device. + * + * 2. Load the nanoapp: + * adb shell chre_test_client load_with_header \ + * /vendor/etc/chre/rpc_world.napp_header \ + * /vendor/etc/chre/rpc_world.so + * + * 3. Build this test and push it to the device: + * m chre_test_rpc + * adb push \ + * out/target/product/<product>/vendor/bin/chre_test_rpc \ + * /vendor/bin/chre_test_rpc + * + * 4. Launch the test: + * adb shell chre_test_rpc + */ + +namespace { + +using ::android::sp; +using ::android::chre::HalRpcClient; +using ::android::chre::HostProtocolHost; +using ::android::chre::IChreMessageHandlers; +using ::android::chre::SocketClient; +using ::flatbuffers::FlatBufferBuilder; + +// Aliased for consistency with the way these symbols are referenced in +// CHRE-side code +namespace fbs = ::chre::fbs; + +constexpr uint16_t kHostEndpoint = 0x8006; + +constexpr uint32_t kRequestNumber = 10; +std::promise<uint32_t> gResponsePromise; + +class SocketCallbacks : public SocketClient::ICallbacks, + public IChreMessageHandlers { + public: + void onMessageReceived(const void *data, size_t length) override { + if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) { + LOGE("Failed to decode message"); + } + } + + void handleNanoappListResponse( + const fbs::NanoappListResponseT &response) override { + LOGI("Got nanoapp list response with %zu apps", response.nanoapps.size()); + } +}; + +} // namespace + +void incrementResponse(const chre_rpc_NumberMessage &response, + pw::Status status) { + if (status.ok()) { + gResponsePromise.set_value(response.number); + } else { + LOGE("Increment failed with status %d", static_cast<int>(status.code())); + } +} + +int main(int argc, char *argv[]) { + UNUSED_VAR(argc); + UNUSED_VAR(argv); + + SocketClient socketClient; + + auto callbacks = sp<SocketCallbacks>::make(); + + std::unique_ptr<HalRpcClient> rpcClient = + HalRpcClient::createClient("chre_test_rpc", socketClient, callbacks, + kHostEndpoint, chre::kRpcWorldAppId); + + if (rpcClient == nullptr) { + LOGE("Failed to create the RPC client"); + return -1; + } + + if (!rpcClient->hasService(/* id= */ 0xca8f7150a3f05847, + /* version= */ 0x01020034)) { + LOGE("RpcWorld service not found"); + return -1; + } + + auto client = + rpcClient->get<chre::rpc::pw_rpc::nanopb::RpcWorldService::Client>(); + + chre_rpc_NumberMessage incrementRequest; + incrementRequest.number = kRequestNumber; + + pw::rpc::NanopbUnaryReceiver<chre_rpc_NumberMessage> call = + client->Increment(incrementRequest, incrementResponse); + + if (!call.active()) { + LOGE("Failed to call the service"); + return -1; + } + + std::future<uint32_t> response = gResponsePromise.get_future(); + + if (response.wait_for(std::chrono::seconds(2)) != std::future_status::ready) { + LOGE("No response received from RPC"); + } else { + const uint32_t value = response.get(); + LOGI("The RPC service says %" PRIu32 " + 1 = %" PRIu32, kRequestNumber, + value); + } + + rpcClient->close(); + + return 0; +} diff --git a/host/common/test/power_test/chre_power_test_client.cc b/host/common/test/power_test/chre_power_test_client.cc index 344944dc..1251e998 100644 --- a/host/common/test/power_test/chre_power_test_client.cc +++ b/host/common/test/power_test/chre_power_test_client.cc @@ -145,7 +145,10 @@ namespace ptest = ::chre::power_test; namespace { -constexpr uint16_t kHostEndpoint = 0xfffd; +//! The host endpoint we use when sending; Clients may use a value above +//! 0x8000 to enable unicast messaging (currently requires internal coordination +//! to avoid conflict). +constexpr uint16_t kHostEndpoint = 0x8003; constexpr uint64_t kPowerTestAppId = 0x012345678900000f; constexpr uint64_t kPowerTestTcmAppId = 0x0123456789000010; diff --git a/host/common/time_syncer.cc b/host/common/time_syncer.cc index e6d27929..41028b99 100644 --- a/host/common/time_syncer.cc +++ b/host/common/time_syncer.cc @@ -20,33 +20,33 @@ namespace android::chre { // TODO(b/247124878): Can we add a static assert to make sure these functions -// are not called when mConnection->isTimeSyncNeeded() returns false? -bool TimeSyncer::sendTimeSync() { - if (!mConnection->isTimeSyncNeeded()) { +// are not called when connection->isTimeSyncNeeded() returns false? +bool TimeSyncer::sendTimeSync(ChreConnection *connection) { + if (!connection->isTimeSyncNeeded()) { LOGW("Platform doesn't require time sync. Ignore the request."); return true; } int64_t timeOffsetUs = 0; - if (!mConnection->getTimeOffset(&timeOffsetUs)) { + if (!connection->getTimeOffset(&timeOffsetUs)) { LOGE("Failed to get time offset."); return false; } flatbuffers::FlatBufferBuilder builder(64); // clientId doesn't matter for time sync request so the default id is used. HostProtocolHost::encodeTimeSyncMessage(builder, timeOffsetUs); - return mConnection->sendMessage(builder.GetBufferPointer(), - builder.GetSize()); + return connection->sendMessage(builder.GetBufferPointer(), builder.GetSize()); } -bool TimeSyncer::sendTimeSyncWithRetry(size_t numOfRetries, +bool TimeSyncer::sendTimeSyncWithRetry(ChreConnection *connection, + size_t numOfRetries, useconds_t retryDelayUs) { - if (!mConnection->isTimeSyncNeeded()) { + if (!connection->isTimeSyncNeeded()) { LOGW("Platform doesn't require time sync. Ignore the request."); return true; } bool success = false; while (!success && (numOfRetries-- > 0)) { - success = sendTimeSync(); + success = sendTimeSync(connection); if (!success) { usleep(retryDelayUs); } diff --git a/host/hal_generic/Android.bp b/host/hal_generic/Android.bp index caef5284..7b85a82a 100644 --- a/host/hal_generic/Android.bp +++ b/host/hal_generic/Android.bp @@ -27,22 +27,31 @@ package { cc_binary { name: "android.hardware.contexthub-service.generic", - defaults: ["hidl_defaults"], + defaults: ["chre_aidl_hal_generic_defaults"], vendor: true, relative_install_path: "hw", + srcs: [":hal_aidl_generic_srcs", "aidl/service.cc"], + init_rc: ["aidl/android.hardware.contexthub-service.generic.rc"], + vintf_fragments: ["aidl/android.hardware.contexthub-service.generic.xml"], + visibility: ["//visibility:public"], +} + +filegroup { + name: "hal_aidl_generic_srcs", srcs: [ "aidl/generic_context_hub_aidl.cc", - "aidl/service.cc", "common/hal_chre_socket_connection.cc", "common/permissions_util.cc", ], +} + +cc_defaults { + name: "chre_aidl_hal_generic_defaults", + vendor: true, include_dirs: [ + "system/chre/host/hal_generic/common/", "system/chre/util/include", ], - local_include_dirs: [ - "common/", - ], - init_rc: ["aidl/android.hardware.contexthub-service.generic.rc"], cflags: [ "-Wall", "-Werror", @@ -50,6 +59,9 @@ cc_binary { "-DCHRE_HAL_SOCKET_METRICS_ENABLED", "-DCHRE_IS_HOST_BUILD", ], + header_libs: [ + "chre_api", + ], shared_libs: [ "android.frameworks.stats-V1-ndk", "libcutils", @@ -59,17 +71,24 @@ cc_binary { "libutils", "libbase", "libbinder_ndk", - "android.hardware.contexthub-V2-ndk", + "android.hardware.contexthub-V3-ndk", "chremetrics-cpp", "chre_atoms_log", - ], - header_libs: [ - "chre_api", + "chre_metrics_reporter", + "server_configurable_flags", ], static_libs: [ "chre_client", "chre_config_util", "event_logger", + "chre_flags_c_lib", + ], +} + +cc_library_headers { + name: "chre_aidl_hal_generic", + vendor: true, + export_include_dirs: [ + "aidl", ], - vintf_fragments: ["aidl/android.hardware.contexthub-service.generic.xml"], } diff --git a/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml b/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml index 930f6721..2f8ddc8e 100644 --- a/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml +++ b/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml @@ -1,7 +1,7 @@ <manifest version="1.0" type="device"> <hal format="aidl"> <name>android.hardware.contexthub</name> - <version>2</version> + <version>3</version> <interface> <name>IContextHub</name> <instance>default</instance> diff --git a/host/hal_generic/aidl/generic_context_hub_aidl.cc b/host/hal_generic/aidl/generic_context_hub_aidl.cc index dae0d6f4..16c5c3c7 100644 --- a/host/hal_generic/aidl/generic_context_hub_aidl.cc +++ b/host/hal_generic/aidl/generic_context_hub_aidl.cc @@ -51,6 +51,7 @@ constexpr uint32_t kDefaultHubId = 0; constexpr char kPreloadedNanoappsConfigPath[] = "/vendor/etc/chre/preloaded_nanoapps.json"; constexpr std::chrono::duration kTestModeTimeout = std::chrono::seconds(10); +constexpr uint16_t kMaxValidHostEndPointId = 0x7fff; /* * The starting transaction ID for internal transactions. We choose @@ -124,6 +125,9 @@ ScopedAStatus ContextHub::getContextHubs( hub.supportedPermissions = kSupportedPermissions; + // TODO(b/312417087): Implement reliable message support + hub.supportsReliableMessages = false; + out_contextHubInfos->push_back(hub); } @@ -296,6 +300,13 @@ ScopedAStatus ContextHub::setTestMode(bool enable) { return enable ? enableTestMode() : disableTestMode(); } +ScopedAStatus ContextHub::sendMessageDeliveryStatusToHub( + int32_t /* contextHubId */, + const MessageDeliveryStatus & /* messageDeliveryStatus */) { + // TODO(b/312417087): Implement reliable message support + return ndk::ScopedAStatus::ok(); +} + ScopedAStatus ContextHub::onHostEndpointConnected( const HostEndpointInfo &in_info) { std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex); @@ -345,6 +356,11 @@ ScopedAStatus ContextHub::onNanSessionStateChanged( void ContextHub::onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) { std::lock_guard<std::mutex> lock(mCallbackMutex); if (mCallback != nullptr) { + if (message.host_endpoint > kMaxValidHostEndPointId && + message.host_endpoint != CHRE_HOST_ENDPOINT_BROADCAST) { + return; + } + mEventLogger.logMessageFromNanoapp(message); ContextHubMessage outMessage; outMessage.nanoappId = message.app_id; diff --git a/host/hal_generic/aidl/generic_context_hub_aidl.h b/host/hal_generic/aidl/generic_context_hub_aidl.h index 9c12c16c..024cb1e3 100644 --- a/host/hal_generic/aidl/generic_context_hub_aidl.h +++ b/host/hal_generic/aidl/generic_context_hub_aidl.h @@ -79,6 +79,9 @@ class ContextHub : public BnContextHub, ::ndk::ScopedAStatus sendMessageToHub( int32_t contextHubId, const ContextHubMessage &message) override; ::ndk::ScopedAStatus setTestMode(bool enable) override; + ::ndk::ScopedAStatus sendMessageDeliveryStatusToHub( + int32_t contextHubId, + const MessageDeliveryStatus &messageDeliveryStatus) override; ::ndk::ScopedAStatus onHostEndpointConnected( const HostEndpointInfo &in_info) override; ::ndk::ScopedAStatus onHostEndpointDisconnected( diff --git a/host/hal_generic/common/hal_chre_socket_connection.cc b/host/hal_generic/common/hal_chre_socket_connection.cc index b113e44f..432a36ef 100644 --- a/host/hal_generic/common/hal_chre_socket_connection.cc +++ b/host/hal_generic/common/hal_chre_socket_connection.cc @@ -14,6 +14,9 @@ * limitations under the License. */ +// TODO(b/298459533): remove_ap_wakeup_metric_report_limit ramp up -> remove old +// code + #define LOG_TAG "ContextHubHal" #define LOG_NDEBUG 1 @@ -22,8 +25,13 @@ #include <log/log.h> #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED +// TODO(b/298459533): Remove these when the flag_log_nanoapp_load_metrics flag +// is cleaned up #include <aidl/android/frameworks/stats/IStats.h> #include <android/binder_manager.h> +#include <android_chre_flags.h> +// TODO(b/298459533): Remove end + #include <chre_atoms_log.h> #include <utils/SystemClock.h> #endif // CHRE_HAL_SOCKET_METRICS_ENABLED @@ -34,15 +42,25 @@ namespace contexthub { namespace common { namespace implementation { -using chre::FragmentedLoadRequest; -using chre::FragmentedLoadTransaction; -using chre::HostProtocolHost; -using flatbuffers::FlatBufferBuilder; +using ::android::chre::FragmentedLoadRequest; +using ::android::chre::FragmentedLoadTransaction; +using ::android::chre::HostProtocolHost; +using ::flatbuffers::FlatBufferBuilder; #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED +// TODO(b/298459533): Remove these when the flag_log_nanoapp_load_metrics flag +// is cleaned up using ::aidl::android::frameworks::stats::IStats; using ::aidl::android::frameworks::stats::VendorAtom; using ::aidl::android::frameworks::stats::VendorAtomValue; +using ::android::chre::Atoms::CHRE_AP_WAKE_UP_OCCURRED; +using ::android::chre::Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED; +using ::android::chre::flags::flag_log_nanoapp_load_metrics; +using ::android::chre::flags::remove_ap_wakeup_metric_report_limit; +// TODO(b/298459533): Remove end + +using ::android::chre::MetricsReporter; +using ::android::chre::Atoms::ChreHalNanoappLoadFailed; #endif // CHRE_HAL_SOCKET_METRICS_ENABLED HalChreSocketConnection::HalChreSocketConnection( @@ -182,7 +200,9 @@ HalChreSocketConnection::SocketCallbacks::SocketCallbacks( HalChreSocketConnection &parent, IChreSocketCallback *callback) : mParent(parent), mCallback(callback) { #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED - mLastClearedTimestamp = elapsedRealtime(); + if (!remove_ap_wakeup_metric_report_limit()) { + mLastClearedTimestamp = elapsedRealtime(); + } #endif // CHRE_HAL_SOCKET_METRICS_ENABLED } @@ -217,25 +237,35 @@ void HalChreSocketConnection::SocketCallbacks::handleNanoappMessage( // check and update the 24hour timer std::lock_guard<std::mutex> lock(mNanoappWokeApCountMutex); long nanoappId = message.app_id; - long timeElapsed = elapsedRealtime() - mLastClearedTimestamp; - if (timeElapsed > kOneDayinMillis) { - mNanoappWokeUpCount = 0; - mLastClearedTimestamp = elapsedRealtime(); + + if (!remove_ap_wakeup_metric_report_limit()) { + long timeElapsed = elapsedRealtime() - mLastClearedTimestamp; + if (timeElapsed > kOneDayinMillis) { + mNanoappWokeUpCount = 0; + mLastClearedTimestamp = elapsedRealtime(); + } + + mNanoappWokeUpCount++; } - // update and report the AP woke up metric - mNanoappWokeUpCount++; - if (mNanoappWokeUpCount < kMaxDailyReportedApWakeUp) { - // create and report the vendor atom - std::vector<VendorAtomValue> values(1); - values[0].set<VendorAtomValue::longValue>(nanoappId); + if (remove_ap_wakeup_metric_report_limit() || + mNanoappWokeUpCount < kMaxDailyReportedApWakeUp) { + if (flag_log_nanoapp_load_metrics()) { + if (!mParent.mMetricsReporter.logApWakeupOccurred(nanoappId)) { + ALOGE("Could not log AP Wakeup metric"); + } + } else { + // create and report the vendor atom + std::vector<VendorAtomValue> values(1); + values[0].set<VendorAtomValue::longValue>(nanoappId); - const VendorAtom atom{ - .atomId = chre::Atoms::CHRE_AP_WAKE_UP_OCCURRED, - .values{std::move(values)}, - }; + const VendorAtom atom{ + .atomId = CHRE_AP_WAKE_UP_OCCURRED, + .values{std::move(values)}, + }; - mParent.reportMetric(atom); + mParent.reportMetric(atom); + } } } #endif // CHRE_HAL_SOCKET_METRICS_ENABLED @@ -293,6 +323,19 @@ void HalChreSocketConnection::SocketCallbacks::handleLoadNanoappResponse( } } else { success = response.success; + +#ifdef CHRE_HAL_SOCKET_METRICS_ENABLED + if (!success) { + if (flag_log_nanoapp_load_metrics()) { + if (!mParent.mMetricsReporter.logNanoappLoadFailed( + transaction.getNanoappId(), + ChreHalNanoappLoadFailed::TYPE_DYNAMIC, + ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC)) { + ALOGE("Could not log the nanoapp load failed metric"); + } + } + } +#endif // CHRE_HAL_SOCKET_METRICS_ENABLED } if (!continueLoadRequest) { @@ -347,19 +390,27 @@ bool HalChreSocketConnection::sendFragmentedLoadNanoAppRequest( request.fragmentId); #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED - // create and report the vendor atom - std::vector<VendorAtomValue> values(3); - values[0].set<VendorAtomValue::longValue>(request.appId); - values[1].set<VendorAtomValue::intValue>( - chre::Atoms::ChreHalNanoappLoadFailed::TYPE_DYNAMIC); - values[2].set<VendorAtomValue::intValue>( - chre::Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC); - - const VendorAtom atom{ - .atomId = chre::Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED, - .values{std::move(values)}, - }; - reportMetric(atom); + if (flag_log_nanoapp_load_metrics()) { + if (!mMetricsReporter.logNanoappLoadFailed( + request.appId, ChreHalNanoappLoadFailed::TYPE_DYNAMIC, + ChreHalNanoappLoadFailed::REASON_CONNECTION_ERROR)) { + ALOGE("Could not log the nanoapp load failed metric"); + } + } else { + // create and report the vendor atom + std::vector<VendorAtomValue> values(3); + values[0].set<VendorAtomValue::longValue>(request.appId); + values[1].set<VendorAtomValue::intValue>( + ChreHalNanoappLoadFailed::TYPE_DYNAMIC); + values[2].set<VendorAtomValue::intValue>( + ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC); + + const VendorAtom atom{ + .atomId = CHRE_HAL_NANOAPP_LOAD_FAILED, + .values{std::move(values)}, + }; + reportMetric(atom); + } #endif // CHRE_HAL_SOCKET_METRICS_ENABLED } else { @@ -371,6 +422,8 @@ bool HalChreSocketConnection::sendFragmentedLoadNanoAppRequest( } #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED +// TODO(b/298459533): Remove this the flag_log_nanoapp_load_metrics flag is +// cleaned up void HalChreSocketConnection::reportMetric(const VendorAtom atom) { const std::string statsServiceName = std::string(IStats::descriptor).append("/default"); @@ -391,6 +444,7 @@ void HalChreSocketConnection::reportMetric(const VendorAtom atom) { ALOGE("Failed to report vendor atom"); } } +// TODO(b/298459533): Remove end #endif // CHRE_HAL_SOCKET_METRICS_ENABLED } // namespace implementation diff --git a/host/hal_generic/common/hal_chre_socket_connection.h b/host/hal_generic/common/hal_chre_socket_connection.h index b3bb294c..417638c6 100644 --- a/host/hal_generic/common/hal_chre_socket_connection.h +++ b/host/hal_generic/common/hal_chre_socket_connection.h @@ -26,7 +26,9 @@ #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED #include <aidl/android/frameworks/stats/IStats.h> -#endif // CHRE_HAL_SOCKET_METRICS_ENABLED + +#include "chre_host/metrics_reporter.h" +#endif // CHRE_HAL_SOCKET_METRICS_ENABLED namespace android { namespace hardware { @@ -182,6 +184,10 @@ class HalChreSocketConnection { std::optional<chre::FragmentedLoadTransaction> mPendingLoadTransaction; std::mutex mPendingLoadTransactionMutex; +#ifdef CHRE_HAL_SOCKET_METRICS_ENABLED + android::chre::MetricsReporter mMetricsReporter; +#endif // CHRE_HAL_SOCKET_METRICS_ENABLED + /** * Checks to see if a load response matches the currently pending * fragmented load transaction. mPendingLoadTransactionMutex must @@ -206,14 +212,17 @@ class HalChreSocketConnection { bool sendFragmentedLoadNanoAppRequest( chre::FragmentedLoadTransaction &transaction); +#ifdef CHRE_HAL_SOCKET_METRICS_ENABLED + // TODO(b/298459533): Remove this when the flag_log_nanoapp_load_metrics flag + // is cleaned up /** * Create and report CHRE vendor atom and send it to stats_client * * @param atom the vendor atom to be reported */ -#ifdef CHRE_HAL_SOCKET_METRICS_ENABLED void reportMetric(const aidl::android::frameworks::stats::VendorAtom atom); #endif // CHRE_HAL_SOCKET_METRICS_ENABLED + // TODO(b/298459533): Remove end }; } // namespace implementation diff --git a/host/hal_generic/common/hal_client_id.h b/host/hal_generic/common/hal_client_id.h index fb6e2a5b..778a59d0 100644 --- a/host/hal_generic/common/hal_client_id.h +++ b/host/hal_generic/common/hal_client_id.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_ID_H_ #define ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_ID_H_ +#include <cstdint> #include <limits> namespace android::hardware::contexthub::common::implementation { @@ -29,9 +30,6 @@ constexpr HalClientId kMaxHalClientId = 0x1ff; /** Max number of HAL clients supported. */ constexpr uint16_t kMaxNumOfHalClients = kMaxHalClientId - 1; -/** The default HAL client id indicating the id is not assigned. */ -constexpr HalClientId kDefaultHalClientId = 0; - /** * The HAL client id indicating the message is actually sent to the HAL itself. */ diff --git a/host/hal_generic/common/hal_client_manager.cc b/host/hal_generic/common/hal_client_manager.cc index 39ce4c9b..4658c837 100644 --- a/host/hal_generic/common/hal_client_manager.cc +++ b/host/hal_generic/common/hal_client_manager.cc @@ -14,197 +14,246 @@ * limitations under the License. */ #include "hal_client_manager.h" + +#include <fstream> + #include <aidl/android/hardware/contexthub/AsyncEventType.h> #include <android-base/strings.h> +#include <android_chre_flags.h> #include <json/json.h> #include <utils/SystemClock.h> -#include <fstream> namespace android::hardware::contexthub::common::implementation { -using aidl::android::hardware::contexthub::AsyncEventType; -using aidl::android::hardware::contexthub::ContextHubMessage; -using aidl::android::hardware::contexthub::HostEndpointInfo; -using aidl::android::hardware::contexthub::IContextHubCallback; +using ::aidl::android::hardware::contexthub::AsyncEventType; +using ::aidl::android::hardware::contexthub::ContextHubMessage; +using ::aidl::android::hardware::contexthub::HostEndpointInfo; +using ::aidl::android::hardware::contexthub::IContextHubCallback; +using ::android::chre::flags::context_hub_callback_uuid_enabled; + +using HalClient = HalClientManager::HalClient; namespace { -bool getClientMappingsFromFile(const char *filePath, Json::Value &mappings) { +bool getClientMappingsFromFile(const std::string &filePath, + Json::Value &mappings) { std::fstream file(filePath); Json::CharReaderBuilder builder; return file.good() && Json::parseFromStream(builder, file, &mappings, /* errs= */ nullptr); } + +std::string getUuid(const std::shared_ptr<IContextHubCallback> &callback) { + std::array<uint8_t, 16> uuidBytes{}; + callback->getUuid(&uuidBytes); + std::ostringstream oStringStream; + char buffer[3]{}; + for (const uint8_t &byte : uuidBytes) { + snprintf(buffer, sizeof(buffer), "%02x", static_cast<int>(byte)); + oStringStream << buffer; + } + return oStringStream.str(); +} + +std::string getName(const std::shared_ptr<IContextHubCallback> &callback) { + std::string name; + callback->getName(&name); + return name; +} + +bool isCallbackV3Enabled(const std::shared_ptr<IContextHubCallback> &callback) { + int32_t callbackVersion; + callback->getInterfaceVersion(&callbackVersion); + return callbackVersion >= 3 && context_hub_callback_uuid_enabled(); +} + } // namespace -std::optional<HalClientId> HalClientManager::createClientIdLocked( - const std::string &processName) { - if (mPIdsToClientIds.size() > kMaxNumOfHalClients || - mNextClientId > kMaxHalClientId) { - LOGE("Too many HAL clients registered which should never happen."); - return std::nullopt; +HalClient *HalClientManager::getClientByField( + const std::function<bool(const HalClient &client)> &fieldMatcher) { + for (HalClient &client : mClients) { + if (fieldMatcher(client)) { + return &client; + } + } + return nullptr; +} + +HalClient *HalClientManager::getClientByClientIdLocked(HalClientId clientId) { + return getClientByField([&clientId](const HalClient &client) { + return client.clientId == clientId; + }); +} + +HalClient *HalClientManager::getClientByUuidLocked(const std::string &uuid) { + return getClientByField( + [&uuid](const HalClient &client) { return client.uuid == uuid; }); +} + +HalClient *HalClientManager::getClientByProcessIdLocked(pid_t pid) { + return getClientByField( + [&pid](const HalClient &client) { return client.pid == pid; }); +} + +bool HalClientManager::updateNextClientIdLocked() { + std::unordered_set<HalClientId> usedClientIds{}; + for (const HalClient &client : mClients) { + usedClientIds.insert(client.clientId); + } + for (int i = 0; i < kMaxNumOfHalClients; i++) { + mNextClientId = (mNextClientId + 1) % kMaxHalClientId; + if (mNextClientId != ::chre::kHostClientIdUnspecified && + mReservedClientIds.find(mNextClientId) == mReservedClientIds.end() && + usedClientIds.find(mNextClientId) == usedClientIds.end()) { + // Found a client id that is not reserved nor used. + return true; + } + } + LOGE("Unable to find the next available client id"); + mNextClientId = ::chre::kHostClientIdUnspecified; + return false; +} + +bool HalClientManager::createClientLocked( + const std::string &uuid, pid_t pid, + const std::shared_ptr<IContextHubCallback> &callback, + void *deathRecipientCookie) { + if (mClients.size() > kMaxNumOfHalClients || + mNextClientId == ::chre::kHostClientIdUnspecified) { + LOGE("Too many HAL clients (%zu) registered which should never happen.", + mClients.size()); + return false; } - if (mProcessNamesToClientIds.find(processName) != - mProcessNamesToClientIds.end()) { - return mProcessNamesToClientIds[processName]; + std::string name{"undefined"}; + if (isCallbackV3Enabled(callback)) { + name = getName(callback); } + mClients.emplace_back(uuid, name, mNextClientId, pid, callback, + deathRecipientCookie); // Update the json list with the new mapping - mProcessNamesToClientIds.emplace(processName, mNextClientId); Json::Value mappings; - for (const auto &[name, clientId] : mProcessNamesToClientIds) { + for (const auto &client : mClients) { Json::Value mapping; - mapping[kJsonProcessName] = name; - mapping[kJsonClientId] = clientId; + mapping[kJsonUuid] = client.uuid; + mapping[kJsonName] = client.name; + mapping[kJsonClientId] = client.clientId; mappings.append(mapping); } // write to the file; Create the file if it doesn't exist Json::StreamWriterBuilder factory; std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter()); - std::ofstream fileStream(kClientMappingFilePath); + std::ofstream fileStream(mClientMappingFilePath); writer->write(mappings, &fileStream); fileStream << std::endl; - return {mNextClientId++}; + updateNextClientIdLocked(); + return true; } -HalClientId HalClientManager::getClientId() { - pid_t pid = AIBinder_getCallingPid(); +HalClientId HalClientManager::getClientId(pid_t pid) { const std::lock_guard<std::mutex> lock(mLock); - if (isKnownPIdLocked(pid)) { - return mPIdsToClientIds[pid]; + const HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { + LOGE("Failed to find the client id for pid %d", pid); + return ::chre::kHostClientIdUnspecified; } - LOGE("Failed to find the client id for pid %d", pid); - return kDefaultHalClientId; + return client->clientId; } std::shared_ptr<IContextHubCallback> HalClientManager::getCallback( HalClientId clientId) { const std::lock_guard<std::mutex> lock(mLock); - if (isAllocatedClientIdLocked(clientId)) { - return mClientIdsToClientInfo.at(clientId).callback; + const HalClient *client = getClientByClientIdLocked(clientId); + if (client == nullptr) { + LOGE("Failed to find the callback for the client id %" PRIu16, clientId); + return nullptr; } - LOGE("Failed to find the callback for the client id %" PRIu16, clientId); - return nullptr; + return client->callback; } bool HalClientManager::registerCallback( - const std::shared_ptr<IContextHubCallback> &callback, - const ndk::ScopedAIBinder_DeathRecipient &deathRecipient, + pid_t pid, const std::shared_ptr<IContextHubCallback> &callback, void *deathRecipientCookie) { - pid_t pid = AIBinder_getCallingPid(); const std::lock_guard<std::mutex> lock(mLock); - if (AIBinder_linkToDeath(callback->asBinder().get(), deathRecipient.get(), - deathRecipientCookie) != STATUS_OK) { - LOGE("Failed to link client binder to death recipient."); - return false; - } - if (isKnownPIdLocked(pid)) { + HalClient *client = getClientByProcessIdLocked(pid); + if (client != nullptr) { LOGW("The pid %d has already registered. Overriding its callback.", pid); - return overrideCallbackLocked(pid, callback, deathRecipient, - deathRecipientCookie); - } - std::string processName = getProcessName(pid); - std::optional<HalClientId> clientIdOptional = - createClientIdLocked(processName); - if (clientIdOptional == std::nullopt) { - LOGE("Failed to generate a valid client id for process %s", - processName.c_str()); - return false; - } - HalClientId clientId = clientIdOptional.value(); - if (mClientIdsToClientInfo.find(clientId) != mClientIdsToClientInfo.end()) { - LOGE("Process %s already has a connection to HAL.", processName.c_str()); - return false; + if (!mDeadClientUnlinker(client->callback, client->deathRecipientCookie)) { + LOGE("Unable to unlink the old callback for pid %d", pid); + return false; + } + client->callback.reset(); + client->callback = callback; + client->deathRecipientCookie = deathRecipientCookie; + return true; } - mPIdsToClientIds[pid] = clientId; - mClientIdsToClientInfo.emplace(clientId, - HalClientInfo(callback, deathRecipientCookie)); - if (mFrameworkServiceClientId == kDefaultHalClientId && - processName == kSystemServerName) { - mFrameworkServiceClientId = clientId; + + std::string uuid; + if (isCallbackV3Enabled(callback)) { + uuid = getUuid(callback); + } else { + uuid = getUuidLocked(); } - return true; -} -bool HalClientManager::overrideCallbackLocked( - pid_t pid, const std::shared_ptr<IContextHubCallback> &callback, - const ndk::ScopedAIBinder_DeathRecipient &deathRecipient, - void *deathRecipientCookie) { - LOGI("Overriding the callback for pid %d", pid); - HalClientInfo &clientInfo = - mClientIdsToClientInfo.at(mPIdsToClientIds.at(pid)); - if (AIBinder_unlinkToDeath(clientInfo.callback->asBinder().get(), - deathRecipient.get(), - clientInfo.deathRecipientCookie) != STATUS_OK) { - LOGE("Unable to unlink the old callback for pid %d", pid); - return false; + client = getClientByUuidLocked(uuid); + if (client != nullptr) { + if (client->pid != HalClient::PID_UNSET) { + // A client is trying to connect to HAL from a different process. But the + // previous connection is still active because otherwise the pid will be + // cleared in handleClientDeath(). + LOGE("Client (uuid=%s) already has a connection to HAL.", uuid.c_str()); + return false; + } + // For a known client the previous assigned clientId will be reused. + client->reset(/* processId= */ pid, + /* contextHubCallback= */ callback, + /* cookie= */ deathRecipientCookie); + return true; } - clientInfo.callback.reset(); - clientInfo.callback = callback; - clientInfo.deathRecipientCookie = deathRecipientCookie; - return true; + return createClientLocked(uuid, pid, callback, deathRecipientCookie); } -void HalClientManager::handleClientDeath( - pid_t pid, const ndk::ScopedAIBinder_DeathRecipient &deathRecipient) { +void HalClientManager::handleClientDeath(pid_t pid) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE("Failed to locate the dead pid %d", pid); return; } - HalClientId clientId = mPIdsToClientIds[pid]; - mPIdsToClientIds.erase(mPIdsToClientIds.find(pid)); - if (!isAllocatedClientIdLocked(clientId)) { - LOGE("Failed to locate the dead client id %" PRIu16, clientId); - return; - } - for (const auto &[procName, id] : mProcessNamesToClientIds) { - if (id == clientId && procName == kSystemServerName) { - LOGE("System server is disconnected"); - mIsFirstClient = true; - } - } - - HalClientInfo &clientInfo = mClientIdsToClientInfo.at(clientId); - if (AIBinder_unlinkToDeath(clientInfo.callback->asBinder().get(), - deathRecipient.get(), - clientInfo.deathRecipientCookie) != STATUS_OK) { + if (!mDeadClientUnlinker(client->callback, client->deathRecipientCookie)) { LOGE("Unable to unlink the old callback for pid %d in death handler", pid); } - clientInfo.callback.reset(); + client->reset(/* processId= */ HalClient::PID_UNSET, + /* contextHubCallback= */ nullptr, /* cookie= */ nullptr); + if (mPendingLoadTransaction.has_value() && - mPendingLoadTransaction->clientId == clientId) { + mPendingLoadTransaction->clientId == client->clientId) { mPendingLoadTransaction.reset(); } if (mPendingUnloadTransaction.has_value() && - mPendingUnloadTransaction->clientId == clientId) { + mPendingUnloadTransaction->clientId == client->clientId) { mPendingLoadTransaction.reset(); } - mClientIdsToClientInfo.erase(clientId); - if (mFrameworkServiceClientId == clientId) { - mFrameworkServiceClientId = kDefaultHalClientId; - } LOGI("Process %" PRIu32 " is disconnected from HAL.", pid); } bool HalClientManager::registerPendingLoadTransaction( - std::unique_ptr<chre::FragmentedLoadTransaction> transaction) { + pid_t pid, std::unique_ptr<chre::FragmentedLoadTransaction> transaction) { if (transaction->isComplete()) { LOGW("No need to register a completed load transaction."); return false; } - pid_t pid = AIBinder_getCallingPid(); const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + const HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE("Unknown HAL client when registering its pending load transaction."); return false; } - auto clientId = mPIdsToClientIds[pid]; - if (!isNewTransactionAllowedLocked(clientId)) { + if (!isNewTransactionAllowedLocked(client->clientId)) { return false; } mPendingLoadTransaction.emplace( - clientId, /* registeredTimeMs= */ android::elapsedRealtime(), + client->clientId, /* registeredTimeMs= */ android::elapsedRealtime(), /* currentFragmentId= */ 0, std::move(transaction)); return true; } @@ -228,19 +277,18 @@ HalClientManager::getNextFragmentedLoadRequest() { } bool HalClientManager::registerPendingUnloadTransaction( - uint32_t transactionId) { - pid_t pid = AIBinder_getCallingPid(); + pid_t pid, uint32_t transactionId) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + const HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE("Unknown HAL client when registering its pending unload transaction."); return false; } - auto clientId = mPIdsToClientIds[pid]; - if (!isNewTransactionAllowedLocked(clientId)) { + if (!isNewTransactionAllowedLocked(client->clientId)) { return false; } mPendingUnloadTransaction.emplace( - clientId, transactionId, + client->clientId, transactionId, /* registeredTimeMs= */ android::elapsedRealtime()); return true; } @@ -293,79 +341,94 @@ bool HalClientManager::isNewTransactionAllowedLocked(HalClientId clientId) { return true; } -bool HalClientManager::registerEndpointId(const HostEndpointId &endpointId) { - pid_t pid = AIBinder_getCallingPid(); +bool HalClientManager::registerEndpointId(pid_t pid, + const HostEndpointId &endpointId) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE( "Unknown HAL client (pid %d). Register the callback before registering " "an endpoint.", pid); return false; } - HalClientId clientId = mPIdsToClientIds[pid]; - if (!isValidEndpointId(clientId, endpointId)) { + if (!isValidEndpointId(client, endpointId)) { LOGE("Endpoint id %" PRIu16 " from process %d is out of range.", endpointId, pid); return false; } - if (mClientIdsToClientInfo[clientId].endpointIds.find(endpointId) != - mClientIdsToClientInfo[clientId].endpointIds.end()) { + if (client->endpointIds.find(endpointId) != client->endpointIds.end()) { LOGW("The endpoint %" PRIu16 " is already connected.", endpointId); return false; } - mClientIdsToClientInfo[clientId].endpointIds.insert(endpointId); + client->endpointIds.insert(endpointId); LOGI("Endpoint id %" PRIu16 " is connected to client %" PRIu16, endpointId, - clientId); + client->clientId); return true; } -bool HalClientManager::removeEndpointId(const HostEndpointId &endpointId) { - pid_t pid = AIBinder_getCallingPid(); +bool HalClientManager::removeEndpointId(pid_t pid, + const HostEndpointId &endpointId) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE( "Unknown HAL client (pid %d). A callback should have been registered " "before removing an endpoint.", pid); return false; } - HalClientId clientId = mPIdsToClientIds[pid]; - if (!isValidEndpointId(clientId, endpointId)) { + if (!isValidEndpointId(client, endpointId)) { LOGE("Endpoint id %" PRIu16 " from process %d is out of range.", endpointId, pid); return false; } - if (mClientIdsToClientInfo[clientId].endpointIds.find(endpointId) == - mClientIdsToClientInfo[clientId].endpointIds.end()) { + if (client->endpointIds.find(endpointId) == client->endpointIds.end()) { LOGW("The endpoint %" PRIu16 " is not connected.", endpointId); return false; } - mClientIdsToClientInfo[clientId].endpointIds.erase(endpointId); + client->endpointIds.erase(endpointId); LOGI("Endpoint id %" PRIu16 " is removed from client %" PRIu16, endpointId, - clientId); + client->clientId); return true; } std::shared_ptr<IContextHubCallback> HalClientManager::getCallbackForEndpoint( - const HostEndpointId &endpointId) { + const HostEndpointId mutatedEndpointId) { const std::lock_guard<std::mutex> lock(mLock); - HalClientId clientId = getClientIdFromEndpointId(endpointId); - if (!isAllocatedClientIdLocked(clientId)) { + HalClient *client; + if (mutatedEndpointId & kVendorEndpointIdBitMask) { + HalClientId clientId = + mutatedEndpointId >> kNumOfBitsForEndpointId & kMaxHalClientId; + client = getClientByClientIdLocked(clientId); + } else { + client = getClientByUuidLocked(kSystemServerUuid); + } + + HostEndpointId originalEndpointId = + convertToOriginalEndpointId(mutatedEndpointId); + if (client == nullptr) { LOGE("Unknown endpoint id %" PRIu16 ". Please register the callback first.", - endpointId); + originalEndpointId); return nullptr; } - return mClientIdsToClientInfo[clientId].callback; + if (client->endpointIds.find(originalEndpointId) == + client->endpointIds.end()) { + LOGW( + "Received a message from CHRE for an unknown or disconnected endpoint " + "id %" PRIu16, + originalEndpointId); + } + return client->callback; } void HalClientManager::sendMessageForAllCallbacks( const ContextHubMessage &message, const std::vector<std::string> &messageParams) { const std::lock_guard<std::mutex> lock(mLock); - for (const auto &[_, clientInfo] : mClientIdsToClientInfo) { - if (clientInfo.callback != nullptr) { - clientInfo.callback->handleContextHubMessage(message, messageParams); + for (const auto &client : mClients) { + if (client.callback != nullptr) { + client.callback->handleContextHubMessage(message, messageParams); } } } @@ -373,30 +436,26 @@ void HalClientManager::sendMessageForAllCallbacks( const std::unordered_set<HostEndpointId> *HalClientManager::getAllConnectedEndpoints(pid_t pid) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + const HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE("Unknown HAL client with pid %d", pid); return nullptr; } - HalClientId clientId = mPIdsToClientIds[pid]; - if (mClientIdsToClientInfo.find(clientId) == mClientIdsToClientInfo.end()) { - LOGE("Can't find any information for client id %" PRIu16, clientId); - return nullptr; - } - return &mClientIdsToClientInfo[clientId].endpointIds; + return &(client->endpointIds); } bool HalClientManager::mutateEndpointIdFromHostIfNeeded( - const pid_t &pid, HostEndpointId &endpointId) { + pid_t pid, HostEndpointId &endpointId) { const std::lock_guard<std::mutex> lock(mLock); - if (!isKnownPIdLocked(pid)) { + const HalClient *client = getClientByProcessIdLocked(pid); + if (client == nullptr) { LOGE("Unknown HAL client with pid %d", pid); return false; } // no need to mutate client id for framework service - if (mPIdsToClientIds[pid] != mFrameworkServiceClientId) { - HalClientId clientId = mPIdsToClientIds[pid]; + if (client->uuid != kSystemServerUuid) { endpointId = kVendorEndpointIdBitMask | - clientId << kNumOfBitsForEndpointId | endpointId; + client->clientId << kNumOfBitsForEndpointId | endpointId; } return true; } @@ -409,31 +468,35 @@ HostEndpointId HalClientManager::convertToOriginalEndpointId( return endpointId; } -HalClientManager::HalClientManager() { +HalClientManager::HalClientManager( + DeadClientUnlinker deadClientUnlinker, + const std::string &clientIdMappingFilePath, + const std::unordered_set<HalClientId> &reservedClientIds) { + mDeadClientUnlinker = std::move(deadClientUnlinker); + mClientMappingFilePath = clientIdMappingFilePath; + mReservedClientIds = reservedClientIds; // Parses the file to construct a mapping from process names to client ids. Json::Value mappings; - if (!getClientMappingsFromFile(kClientMappingFilePath, mappings)) { + if (!getClientMappingsFromFile(mClientMappingFilePath, mappings)) { // TODO(b/247124878): When the device was firstly booted up the file doesn't // exist which is expected. Consider to create a default file to avoid // confusions. - LOGW("Unable to find and read %s.", kClientMappingFilePath); - return; - } - for (int i = 0; i < mappings.size(); i++) { - Json::Value mapping = mappings[i]; - if (!mapping.isMember(kJsonClientId) || - !mapping.isMember(kJsonProcessName)) { - LOGE("Unable to find expected key name for the entry %d", i); - continue; - } - std::string processName = mapping[kJsonProcessName].asString(); - auto clientId = static_cast<HalClientId>(mapping[kJsonClientId].asUInt()); - mProcessNamesToClientIds[processName] = clientId; - // mNextClientId should always hold the next available client id - if (mNextClientId <= clientId) { - mNextClientId = clientId + 1; + LOGW("Unable to find and read %s.", mClientMappingFilePath.c_str()); + } else { + for (int i = 0; i < mappings.size(); i++) { + Json::Value mapping = mappings[i]; + if (!mapping.isMember(kJsonClientId) || !mapping.isMember(kJsonUuid) || + !mapping.isMember(kJsonName)) { + LOGE("Unable to find expected key name for the entry %d", i); + continue; + } + std::string uuid = mapping[kJsonUuid].asString(); + std::string name = mapping[kJsonName].asString(); + auto clientId = static_cast<HalClientId>(mapping[kJsonClientId].asUInt()); + mClients.emplace_back(uuid, name, clientId); } } + updateNextClientIdLocked(); } bool HalClientManager::isPendingLoadTransactionMatchedLocked( @@ -490,15 +553,14 @@ void HalClientManager::handleChreRestart() { const std::lock_guard<std::mutex> lock(mLock); mPendingLoadTransaction.reset(); mPendingUnloadTransaction.reset(); - for (auto &[_, clientInfo] : mClientIdsToClientInfo) { - clientInfo.endpointIds.clear(); + for (HalClient &client : mClients) { + client.endpointIds.clear(); } } // Incurs callbacks without holding the lock to avoid deadlocks. - for (auto &[_, clientInfo] : mClientIdsToClientInfo) { - if (clientInfo.callback != nullptr) { - clientInfo.callback->handleContextHubAsyncEvent( - AsyncEventType::RESTARTED); + for (const HalClient &client : mClients) { + if (client.callback != nullptr) { + client.callback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED); } } } diff --git a/host/hal_generic/common/hal_client_manager.h b/host/hal_generic/common/hal_client_manager.h index 66ca8594..80f20896 100644 --- a/host/hal_generic/common/hal_client_manager.h +++ b/host/hal_generic/common/hal_client_manager.h @@ -16,17 +16,21 @@ #ifndef ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_ #define ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_ -#include <aidl/android/hardware/contexthub/ContextHubMessage.h> -#include <aidl/android/hardware/contexthub/IContextHub.h> -#include <aidl/android/hardware/contexthub/IContextHubCallback.h> -#include <chre_host/fragmented_load_transaction.h> -#include <chre_host/preloaded_nanoapp_loader.h> +#include "chre/platform/shared/host_protocol_common.h" +#include "chre_host/fragmented_load_transaction.h" +#include "chre_host/log.h" +#include "chre_host/preloaded_nanoapp_loader.h" +#include "hal_client_id.h" + #include <sys/types.h> #include <cstddef> #include <unordered_map> #include <unordered_set> -#include "chre_host/log.h" -#include "hal_client_id.h" +#include <utility> + +#include <aidl/android/hardware/contexthub/ContextHubMessage.h> +#include <aidl/android/hardware/contexthub/IContextHub.h> +#include <aidl/android/hardware/contexthub/IContextHubCallback.h> using aidl::android::hardware::contexthub::ContextHubMessage; using aidl::android::hardware::contexthub::HostEndpointInfo; @@ -42,16 +46,21 @@ namespace android::hardware::contexthub::common::implementation { * A HAL client is defined as a user calling the IContextHub API. The main * purpose of this class are: * - to assign a unique HalClientId identifying each client; - * - to maintain a mapping between client ids and HalClientInfos; - * - to maintain a mapping between client ids and their endpoint ids. + * - to maintain a mapping between a HAL client and its states defined in + * HalClient; + * - to track the ongoing load/unload transactions * - * There are two types of ids HalClientManager will track, host endpoint id and - * client id. A host endpoint id, which is defined at - * hardware/interfaces/contexthub/aidl/android/hardware/contexthub/ContextHubMessage.aidl, - * identifies a host app that communicates with a HAL client. A client id - * identifies a HAL client, which is the layer beneath the host apps, such as - * ContextHubService. Multiple apps with different host endpoint IDs can have - * the same client ID. + * There are 3 types of ids HalClientManager will track: client uuid, HAL client + * id and host endpoint id. + * - A uuid uniquely identifies a client when it registers its callback. + * After a callback is registered, a HAL client id is created and will be + * used to identify the client in the following API calls from/to it + * - A client id identifies a HAL client, which is the layer beneath the host + * apps, such as ContextHubService. Multiple apps with different host + * endpoint IDs can have the same client ID. + * - A host endpoint id, which is defined at + * hardware/interfaces/contexthub/aidl/android/hardware/contexthub/ContextHubMessage.aidl, + * identifies a host app that communicates with a HAL client. * * For a host endpoint connected to ContextHubService, its endpoint id is kept *in the form below during the communication with CHRE. @@ -77,7 +86,57 @@ namespace android::hardware::contexthub::common::implementation { */ class HalClientManager { public: - HalClientManager(); + struct HalClient { + static constexpr pid_t PID_UNSET = 0; + + explicit HalClient(const std::string &uuid, const std::string &name, + const HalClientId clientId) + : HalClient(uuid, name, clientId, /* pid= */ PID_UNSET, + /* callback= */ nullptr, + /* deathRecipientCookie= */ nullptr) {} + + explicit HalClient(std::string uuid, std::string name, + const HalClientId clientId, pid_t pid, + const std::shared_ptr<IContextHubCallback> &callback, + void *deathRecipientCookie) + : uuid{std::move(uuid)}, + name{std::move(name)}, + clientId{clientId}, + pid{pid}, + callback{callback}, + deathRecipientCookie{deathRecipientCookie} {} + + /** Resets the client's fields except uuid and clientId. */ + void reset(pid_t processId, + const std::shared_ptr<IContextHubCallback> &contextHubCallback, + void *cookie) { + pid = processId; + callback = contextHubCallback; + deathRecipientCookie = cookie; + endpointIds.clear(); + } + + const std::string uuid; + std::string name; + const HalClientId clientId; + pid_t pid{}; + std::shared_ptr<IContextHubCallback> callback{}; + // cookie is used by the death recipient's linked callback + void *deathRecipientCookie{}; + std::unordered_set<HostEndpointId> endpointIds{}; + }; + + // The endpoint id is from a vendor client if the highest bit is set to 1. + static constexpr HostEndpointId kVendorEndpointIdBitMask = 0x8000; + static constexpr uint8_t kNumOfBitsForEndpointId = 6; + + using DeadClientUnlinker = std::function<bool( + const std::shared_ptr<IContextHubCallback> &callback, void *cookie)>; + + explicit HalClientManager( + DeadClientUnlinker deadClientUnlinker, + const std::string &clientIdMappingFilePath, + const std::unordered_set<HalClientId> &reservedClientIds = {}); virtual ~HalClientManager() = default; /** Disable copy constructor and copy assignment to avoid duplicates. */ @@ -87,14 +146,16 @@ class HalClientManager { /** * Gets the client id allocated to the current HAL client. * - * The current HAL client is identified by its process id, which is retrieved - * by calling AIBinder_getCallingPid(). If the process doesn't have any client - * id assigned, HalClientManager will create one mapped to its process id. + * The current HAL client is identified by its process id. If the process + * doesn't have any client id assigned, HalClientManager will create one + * mapped to its process id. + * + * @param pid process id of the current client * - * @return client id assigned to the calling process, or kDefaultHalClientId - * if the process id is not found. + * @return client id assigned to the calling process, or + * ::chre::kHostClientIdUnspecified if the process id is not found. */ - HalClientId getClientId(); + HalClientId getClientId(pid_t pid); /** * Gets the callback for the current HAL client identified by the clientId. @@ -109,16 +170,15 @@ class HalClientManager { * client id. @p deathRecipient and @p deathRecipientCookie are used to unlink * the previous registered callback for the same client, if any. * + * @param pid process id of the current client * @param callback a function incurred to handle the client death event. - * @param deathRecipient a handle on the death notification. * @param deathRecipientCookie the data used by the callback. * * @return true if success, otherwise false. */ - bool registerCallback( - const std::shared_ptr<IContextHubCallback> &callback, - const ndk::ScopedAIBinder_DeathRecipient &deathRecipient, - void *deathRecipientCookie); + bool registerCallback(pid_t pid, + const std::shared_ptr<IContextHubCallback> &callback, + void *deathRecipientCookie); /** * Registers a FragmentedLoadTransaction for the current HAL client. @@ -126,10 +186,13 @@ class HalClientManager { * At this moment only one active transaction, either load or unload, is * supported. * + * @param pid process id of the current client + * @param transaction the transaction being registered + * * @return true if success, otherwise false. */ bool registerPendingLoadTransaction( - std::unique_ptr<FragmentedLoadTransaction> transaction); + pid_t pid, std::unique_ptr<chre::FragmentedLoadTransaction> transaction); /** * Returns true if the load transaction matches the arguments provided. @@ -166,9 +229,12 @@ class HalClientManager { * At this moment only one active transaction, either load or unload, is * supported. * + * @param pid process id of the current client + * @param transaction the transaction being registered + * * @return true if success, otherwise false. */ - bool registerPendingUnloadTransaction(uint32_t transactionId); + bool registerPendingUnloadTransaction(pid_t pid, uint32_t transactionId); /** * Clears the pending unload transaction. @@ -188,31 +254,39 @@ class HalClientManager { /** * Registers an endpoint id when it is connected to HAL. * + * @param pid process id of the current HAL client + * @param endpointId the endpointId being registered + * * @return true if success, otherwise false. */ - bool registerEndpointId(const HostEndpointId &endpointId); + bool registerEndpointId(pid_t pid, const HostEndpointId &endpointId); /** * Removes an endpoint id when it is disconnected to HAL. * + * @param pid process id of the current HAL client + * @param endpointId the endpointId being registered + * * @return true if success, otherwise false. */ - bool removeEndpointId(const HostEndpointId &endpointId); + bool removeEndpointId(pid_t pid, const HostEndpointId &endpointId); /** * Mutates the endpoint id if the hal client is not the framework service. * + * @param pid process id of the current HAL client + * @param endpointId the endpointId being registered + * * @return true if success, otherwise false. */ - bool mutateEndpointIdFromHostIfNeeded(const pid_t &pid, - HostEndpointId &endpointId); + bool mutateEndpointIdFromHostIfNeeded(pid_t pid, HostEndpointId &endpointId); /** Returns the original endpoint id sent by the host client. */ static HostEndpointId convertToOriginalEndpointId( const HostEndpointId &endpointId); /** - * Gets all the connected endpoints for the client identified by the pid. + * Gets all the connected endpoints for the client identified by the @p pid. * * @return the pointer to the endpoint id set if the client is identifiable, * otherwise nullptr. @@ -225,45 +299,28 @@ class HalClientManager { const std::vector<std::string> &messageParams); std::shared_ptr<IContextHubCallback> getCallbackForEndpoint( - const HostEndpointId &endpointId); + HostEndpointId mutatedEndpointId); /** * Handles the client death event. * * @param pid of the client that loses the binder connection to the HAL. - * @param deathRecipient to be unlinked with the client's callback */ - void handleClientDeath( - pid_t pid, const ndk::ScopedAIBinder_DeathRecipient &deathRecipient); + void handleClientDeath(pid_t pid); /** Handles CHRE restart event. */ void handleChreRestart(); protected: - static constexpr char kSystemServerName[] = "system_server"; - static constexpr char kClientMappingFilePath[] = - "/data/vendor/chre/chre_hal_clients.json"; + static constexpr char kSystemServerUuid[] = + "9a17008d6bf1445a90116d21bd985b6c"; + static constexpr char kVendorClientUuid[] = "vendor-client"; static constexpr char kJsonClientId[] = "ClientId"; - static constexpr char kJsonProcessName[] = "ProcessName"; + static constexpr char kJsonUuid[] = "uuid"; + static constexpr char kJsonName[] = "name"; static constexpr int64_t kTransactionTimeoutThresholdMs = 5000; // 5 seconds - static constexpr uint8_t kNumOfBitsForEndpointId = 6; static constexpr HostEndpointId kMaxVendorEndpointId = (1 << kNumOfBitsForEndpointId) - 1; - // The endpoint id is from a vendor client if the highest bit is set to 1. - static constexpr HostEndpointId kVendorEndpointIdBitMask = 0x8000; - - struct HalClientInfo { - explicit HalClientInfo(const std::shared_ptr<IContextHubCallback> &callback, - void *cookie) { - this->callback = callback; - this->deathRecipientCookie = cookie; - } - HalClientInfo() = default; - std::shared_ptr<IContextHubCallback> callback; - // cookie is used by the death recipient's linked callback - void *deathRecipientCookie{}; - std::unordered_set<HostEndpointId> endpointIds{}; - }; struct PendingTransaction { PendingTransaction(HalClientId clientId, uint32_t transactionId, @@ -310,10 +367,17 @@ class HalClientManager { * * mLock must be held when this function is called. * - * @param processName the process name of the client */ - virtual std::optional<HalClientId> createClientIdLocked( - const std::string &processName); + bool createClientLocked(const std::string &uuid, pid_t pid, + const std::shared_ptr<IContextHubCallback> &callback, + void *deathRecipientCookie); + + /** + * Update @p mNextClientId to be the next available one. + * + * @return true if success, otherwise false. + */ + bool updateNextClientIdLocked(); /** * Returns true if @p clientId and @p transactionId match the @@ -355,84 +419,48 @@ class HalClientManager { */ bool isNewTransactionAllowedLocked(HalClientId clientId); - /** - * Returns true if the clientId is being used. - * - * mLock must be held when this function is called. - */ - inline bool isAllocatedClientIdLocked(HalClientId clientId) { - return mClientIdsToClientInfo.find(clientId) != - mClientIdsToClientInfo.end(); + /** Returns true if the endpoint id is within the accepted range. */ + [[nodiscard]] static inline bool isValidEndpointId( + const HalClient *client, const HostEndpointId &endpointId) { + return client->uuid == kSystemServerUuid || + endpointId <= kMaxVendorEndpointId; } - /** - * Returns true if the pid is being used to identify a client. - * - * mLock must be held when this function is called. - */ - inline bool isKnownPIdLocked(pid_t pid) { - return mPIdsToClientIds.find(pid) != mPIdsToClientIds.end(); + // TODO(b/290375569): isSystemServerConnectedLocked() and getUuidLocked() are + // temporary solutions to get a pseudo-uuid. Remove these two functions when + // flag context_hub_callback_uuid_enabled is ramped up. + inline bool isSystemServerConnectedLocked() { + HalClient *client = getClientByUuidLocked(kSystemServerUuid); + return client != nullptr && client->pid != 0; } - - /** Returns true if the endpoint id is within the accepted range. */ - [[nodiscard]] inline bool isValidEndpointId( - const HalClientId &clientId, const HostEndpointId &endpointId) const { - if (clientId != mFrameworkServiceClientId) { - return endpointId <= kMaxVendorEndpointId; - } - return true; + inline std::string getUuidLocked() { + return isSystemServerConnectedLocked() ? kVendorClientUuid + : kSystemServerUuid; } - /** - * Overrides the old callback registered with the client. - * - * @return true if success, otherwise false - */ - bool overrideCallbackLocked( - pid_t pid, const std::shared_ptr<IContextHubCallback> &callback, - const ndk::ScopedAIBinder_DeathRecipient &deathRecipient, - void *deathRecipientCookie); + HalClient *getClientByField( + const std::function<bool(const HalClient &client)> &fieldMatcher); - /** - * Extracts the client id from the endpoint id. - * - * @param endpointId the endpoint id received from CHRE, before any conversion - */ - [[nodiscard]] inline HalClientId getClientIdFromEndpointId( - const HostEndpointId &endpointId) const { - if (endpointId & kVendorEndpointIdBitMask) { - return endpointId >> kNumOfBitsForEndpointId & kMaxHalClientId; - } - return mFrameworkServiceClientId; - } + HalClient *getClientByClientIdLocked(HalClientId clientId); - std::string getProcessName(pid_t /*pid*/) { - // TODO(b/274597758): this is a temporary solution that should be updated - // after b/274597758 is resolved. - if (mIsFirstClient) { - mIsFirstClient = false; - return kSystemServerName; - } - return "the_vendor_client"; - } + HalClient *getClientByUuidLocked(const std::string &uuid); + + HalClient *getClientByProcessIdLocked(pid_t pid); - bool mIsFirstClient = true; + DeadClientUnlinker mDeadClientUnlinker{}; + + std::string mClientMappingFilePath; // next available client id - HalClientId mNextClientId = kDefaultHalClientId + 1; - // framework service client id - HalClientId mFrameworkServiceClientId = kDefaultHalClientId; + HalClientId mNextClientId = ::chre::kHostClientIdUnspecified; + + // reserved client ids that will not be used + std::unordered_set<HalClientId> mReservedClientIds; // The lock guarding the access to clients' states and pending transactions std::mutex mLock; - // Map from process name to client id which stays consistent with the file - // stored at kClientMappingFilePath - std::unordered_map<std::string, HalClientId> mProcessNamesToClientIds{}; - // Map from pids to client ids - std::unordered_map<pid_t, HalClientId> mPIdsToClientIds{}; - // Map from client ids to ClientInfos - std::unordered_map<HalClientId, HalClientInfo> mClientIdsToClientInfo{}; + std::vector<HalClient> mClients; // States tracking pending transactions std::optional<PendingLoadTransaction> mPendingLoadTransaction = std::nullopt; diff --git a/host/hal_generic/common/hal_error.h b/host/hal_generic/common/hal_error.h new file mode 100644 index 00000000..8dc437a9 --- /dev/null +++ b/host/hal_generic/common/hal_error.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef CHRE_HOST_HAL_ERROR_H_ +#define CHRE_HOST_HAL_ERROR_H_ + +#include <cinttypes> + +namespace android::chre { + +enum class HalError { + SUCCESS = 0, + + // Hal service errors + OPERATION_FAILED = -1, + INVALID_RESULT = -2, + INVALID_ARGUMENT = -3, + + // Hal client errors + BINDER_CONNECTION_FAILED = -100, + BINDER_DISCONNECTED = -101, + NULL_CONTEXT_HUB_FROM_BINDER = -102, + LINK_DEATH_RECIPIENT_FAILED = -103, + CALLBACK_REGISTRATION_FAILED = -104, + UNEXPECTED_ENDPOINT_STATE = -105, +}; + +} // namespace android::chre +#endif // CHRE_HOST_HAL_ERROR_H_
\ No newline at end of file diff --git a/host/hal_generic/common/multi_client_context_hub_base.cc b/host/hal_generic/common/multi_client_context_hub_base.cc index 950c09fb..0c257c36 100644 --- a/host/hal_generic/common/multi_client_context_hub_base.cc +++ b/host/hal_generic/common/multi_client_context_hub_base.cc @@ -15,14 +15,15 @@ */ #include "multi_client_context_hub_base.h" + #include <chre/platform/shared/host_protocol_common.h> #include <chre_host/generated/host_messages_generated.h> #include <chre_host/log.h> #include "chre/event.h" #include "chre_host/config_util.h" -#include "chre_host/file_stream.h" #include "chre_host/fragmented_load_transaction.h" #include "chre_host/host_protocol_host.h" +#include "hal_error.h" #include "permissions_util.h" namespace android::hardware::contexthub::common::implementation { @@ -37,12 +38,8 @@ constexpr uint32_t kDefaultHubId = 0; // timeout for calling getContextHubs(), which is synchronous constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5); - -enum class HalErrorCode : int32_t { - OPERATION_FAILED = -1, - INVALID_RESULT = -2, - INVALID_ARGUMENT = -3, -}; +// timeout for enable/disable test mode, which is synchronous +constexpr std::chrono::duration ktestModeTimeOut = std::chrono::seconds(5); bool isValidContextHubId(uint32_t hubId) { if (hubId != kDefaultHubId) { @@ -89,16 +86,34 @@ inline constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) { } // functions that help to generate ScopedAStatus from different values. -inline ScopedAStatus fromServiceError(HalErrorCode errorCode) { +inline ScopedAStatus fromServiceError(HalError errorCode) { return ScopedAStatus::fromServiceSpecificError( static_cast<int32_t>(errorCode)); } inline ScopedAStatus fromResult(bool result) { return result ? ScopedAStatus::ok() - : fromServiceError(HalErrorCode::OPERATION_FAILED); + : fromServiceError(HalError::OPERATION_FAILED); } } // anonymous namespace +MultiClientContextHubBase::MultiClientContextHubBase() { + mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( + AIBinder_DeathRecipient_new(onClientDied)); + AIBinder_DeathRecipient_setOnUnlinked( + mDeathRecipient.get(), /*onUnlinked= */ [](void *cookie) { + LOGI("Callback is unlinked. Releasing the death recipient cookie."); + delete static_cast<HalDeathRecipientCookie *>(cookie); + }); + mDeadClientUnlinker = + [&deathRecipient = mDeathRecipient]( + const std::shared_ptr<IContextHubCallback> &callback, + void *deathRecipientCookie) { + return AIBinder_unlinkToDeath(callback->asBinder().get(), + deathRecipient.get(), + deathRecipientCookie) == STATUS_OK; + }; +} + ScopedAStatus MultiClientContextHubBase::getContextHubs( std::vector<ContextHubInfo> *contextHubInfos) { std::unique_lock<std::mutex> lock(mHubInfoMutex); @@ -108,7 +123,7 @@ ScopedAStatus MultiClientContextHubBase::getContextHubs( HostProtocolHost::encodeHubInfoRequest(builder); if (!mConnection->sendMessage(builder)) { LOGE("Failed to send a message to CHRE to get context hub info."); - return fromServiceError(HalErrorCode::OPERATION_FAILED); + return fromServiceError(HalError::OPERATION_FAILED); } mHubInfoCondition.wait_for(lock, kHubInfoQueryTimeout, [this]() { return mContextHubInfo != nullptr; }); @@ -119,7 +134,7 @@ ScopedAStatus MultiClientContextHubBase::getContextHubs( } LOGE("Unable to get a valid context hub info for PID %d", AIBinder_getCallingPid()); - return fromServiceError(HalErrorCode::INVALID_RESULT); + return fromServiceError(HalError::INVALID_RESULT); } ScopedAStatus MultiClientContextHubBase::loadNanoapp( @@ -135,20 +150,25 @@ ScopedAStatus MultiClientContextHubBase::loadNanoapp( transactionId, appBinary.nanoappId, appBinary.nanoappVersion, appBinary.flags, targetApiVersion, appBinary.customBinary, mConnection->getLoadFragmentSizeBytes()); + pid_t pid = AIBinder_getCallingPid(); if (!mHalClientManager->registerPendingLoadTransaction( - std::move(transaction))) { + pid, std::move(transaction))) { return fromResult(false); } - auto clientId = mHalClientManager->getClientId(); + auto clientId = mHalClientManager->getClientId(pid); auto request = mHalClientManager->getNextFragmentedLoadRequest(); if (request.has_value() && sendFragmentedLoadRequest(clientId, request.value())) { + mEventLogger.logNanoappLoad(appBinary, /* success= */ true); return ScopedAStatus::ok(); } LOGE("Failed to send the first load request for nanoapp 0x%" PRIx64, appBinary.nanoappId); mHalClientManager->resetPendingLoadTransaction(); + // TODO(b/284481035): The result should be logged after the async response is + // received. + mEventLogger.logNanoappLoad(appBinary, /* success= */ false); return fromResult(false); } @@ -168,10 +188,12 @@ ScopedAStatus MultiClientContextHubBase::unloadNanoapp(int32_t contextHubId, if (!isValidContextHubId(contextHubId)) { return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - if (!mHalClientManager->registerPendingUnloadTransaction(transactionId)) { + pid_t pid = AIBinder_getCallingPid(); + if (!mHalClientManager->registerPendingUnloadTransaction(pid, + transactionId)) { return fromResult(false); } - HalClientId clientId = mHalClientManager->getClientId(); + HalClientId clientId = mHalClientManager->getClientId(pid); flatbuffers::FlatBufferBuilder builder(64); HostProtocolHost::encodeUnloadNanoappRequest( builder, transactionId, appId, /* allowSystemNanoappUnload= */ false); @@ -182,6 +204,9 @@ ScopedAStatus MultiClientContextHubBase::unloadNanoapp(int32_t contextHubId, if (!result) { mHalClientManager->resetPendingUnloadTransaction(clientId, transactionId); } + // TODO(b/284481035): The result should be logged after the async response is + // received. + mEventLogger.logNanoappUnload(appId, result); return fromResult(result); } @@ -255,9 +280,9 @@ ScopedAStatus MultiClientContextHubBase::queryNanoapps(int32_t contextHubId) { } flatbuffers::FlatBufferBuilder builder(64); HostProtocolHost::encodeNanoappListRequest(builder); - HostProtocolHost::mutateHostClientId(builder.GetBufferPointer(), - builder.GetSize(), - mHalClientManager->getClientId()); + HostProtocolHost::mutateHostClientId( + builder.GetBufferPointer(), builder.GetSize(), + mHalClientManager->getClientId(AIBinder_getCallingPid())); return fromResult(mConnection->sendMessage(builder)); } @@ -291,14 +316,20 @@ ScopedAStatus MultiClientContextHubBase::registerCallback( LOGE("Callback of context hub HAL must not be null"); return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - // If everything is successful cookie will be released by the callback of - // binder unlinking (callback overridden). - auto *cookie = new HalDeathRecipientCookie(this, AIBinder_getCallingPid()); - if (!mHalClientManager->registerCallback(callback, mDeathRecipient, cookie)) { - LOGE("Unable to register the callback"); + pid_t pid = AIBinder_getCallingPid(); + auto *cookie = new HalDeathRecipientCookie(this, pid); + if (AIBinder_linkToDeath(callback->asBinder().get(), mDeathRecipient.get(), + cookie) != STATUS_OK) { + LOGE("Failed to link a client binder (pid=%d) to the death recipient", pid); delete cookie; return fromResult(false); } + // If AIBinder_linkToDeath is successful the cookie will be released by the + // callback of binder unlinking (callback overridden). + if (!mHalClientManager->registerCallback(pid, callback, cookie)) { + LOGE("Unable to register a client (pid=%d) callback", pid); + return fromResult(false); + } return ScopedAStatus::ok(); } @@ -316,7 +347,9 @@ ScopedAStatus MultiClientContextHubBase::sendMessageToHub( HostProtocolHost::encodeNanoappMessage( builder, message.nanoappId, message.messageType, hostEndpointId, message.messageBody.data(), message.messageBody.size()); - return fromResult(mConnection->sendMessage(builder)); + bool success = mConnection->sendMessage(builder); + mEventLogger.logMessageToNanoapp(message, success); + return fromResult(success); } ScopedAStatus MultiClientContextHubBase::onHostEndpointConnected( @@ -334,14 +367,14 @@ ScopedAStatus MultiClientContextHubBase::onHostEndpointConnected( break; default: LOGE("Unsupported host endpoint type %" PRIu32, info.type); - return fromServiceError(HalErrorCode::INVALID_ARGUMENT); + return fromServiceError(HalError::INVALID_ARGUMENT); } uint16_t endpointId = info.hostEndpointId; - if (!mHalClientManager->registerEndpointId(info.hostEndpointId) || - !mHalClientManager->mutateEndpointIdFromHostIfNeeded( - AIBinder_getCallingPid(), endpointId)) { - return fromServiceError(HalErrorCode::INVALID_ARGUMENT); + pid_t pid = AIBinder_getCallingPid(); + if (!mHalClientManager->registerEndpointId(pid, info.hostEndpointId) || + !mHalClientManager->mutateEndpointIdFromHostIfNeeded(pid, endpointId)) { + return fromServiceError(HalError::INVALID_ARGUMENT); } flatbuffers::FlatBufferBuilder builder(64); HostProtocolHost::encodeHostEndpointConnected( @@ -353,10 +386,11 @@ ScopedAStatus MultiClientContextHubBase::onHostEndpointConnected( ScopedAStatus MultiClientContextHubBase::onHostEndpointDisconnected( char16_t in_hostEndpointId) { HostEndpointId hostEndpointId = in_hostEndpointId; + pid_t pid = AIBinder_getCallingPid(); bool isSuccessful = false; - if (mHalClientManager->removeEndpointId(hostEndpointId) && - mHalClientManager->mutateEndpointIdFromHostIfNeeded( - AIBinder_getCallingPid(), hostEndpointId)) { + if (mHalClientManager->removeEndpointId(pid, hostEndpointId) && + mHalClientManager->mutateEndpointIdFromHostIfNeeded(pid, + hostEndpointId)) { flatbuffers::FlatBufferBuilder builder(64); HostProtocolHost::encodeHostEndpointDisconnected(builder, hostEndpointId); isSuccessful = mConnection->sendMessage(builder); @@ -373,9 +407,65 @@ ScopedAStatus MultiClientContextHubBase::onNanSessionStateChanged( return ndk::ScopedAStatus::ok(); } -ScopedAStatus MultiClientContextHubBase::setTestMode(bool /*enable*/) { - // To be implemented. - return ScopedAStatus::ok(); +ScopedAStatus MultiClientContextHubBase::setTestMode(bool enable) { + return fromResult(enable ? enableTestMode() : disableTestMode()); +} + +ScopedAStatus MultiClientContextHubBase::sendMessageDeliveryStatusToHub( + int32_t /* contextHubId */, + const MessageDeliveryStatus & /* messageDeliveryStatus */) { + // TODO(b/312417087): Implement reliable message support - transaction status + return ndk::ScopedAStatus::ok(); +} + +bool MultiClientContextHubBase::enableTestMode() { + std::unique_lock<std::mutex> lock(mTestModeMutex); + if (mIsTestModeEnabled) { + return true; + } + mTestModeNanoapps.reset(); + if (!queryNanoapps(kDefaultHubId).isOk()) { + LOGE("Failed to get a list of loaded nanoapps."); + mTestModeNanoapps.emplace(); + return false; + } + mEnableTestModeCv.wait_for(lock, ktestModeTimeOut, + [&]() { return mTestModeNanoapps.has_value(); }); + for (const auto &appId : *mTestModeNanoapps) { + if (!unloadNanoapp(kDefaultHubId, appId, mTestModeTransactionId).isOk()) { + LOGE("Failed to unload nanoapp 0x%" PRIx64 " to enable the test mode.", + appId); + return false; + } + mTestModeSyncUnloadResult.reset(); + mEnableTestModeCv.wait_for(lock, ktestModeTimeOut, [&]() { + return mTestModeSyncUnloadResult.has_value(); + }); + if (!*mTestModeSyncUnloadResult) { + LOGE("Failed to unload nanoapp 0x%" PRIx64 " to enable the test mode.", + appId); + return false; + } + } + mIsTestModeEnabled = true; + return true; +} + +bool MultiClientContextHubBase::disableTestMode() { + std::unique_lock<std::mutex> lock(mTestModeMutex); + if (!mIsTestModeEnabled) { + return true; + } + if (mTestModeNanoapps.has_value() && !mTestModeNanoapps->empty()) { + if (!mPreloadedNanoappLoader->loadPreloadedNanoapps(*mTestModeNanoapps)) { + LOGE("Failed to reload the nanoapps to disable the test mode."); + return false; + } + } + mTestModeNanoapps.emplace(); + mTestModeTransactionId = static_cast<int32_t>(kDefaultTestModeTransactionId); + mIsTestModeEnabled = false; + return true; } void MultiClientContextHubBase::handleMessageFromChre( @@ -402,6 +492,14 @@ void MultiClientContextHubBase::handleMessageFromChre( onNanoappLoadResponse(*message.AsLoadNanoappResponse(), clientId); break; } + case fbs::ChreMessage::TimeSyncRequest: { + if (mConnection->isTimeSyncNeeded()) { + TimeSyncer::sendTimeSync(mConnection.get()); + } else { + LOGW("Received an unexpected time sync request from CHRE."); + } + break; + } case fbs::ChreMessage::UnloadNanoappResponse: { onNanoappUnloadResponse(*message.AsUnloadNanoappResponse(), clientId); break; @@ -410,6 +508,14 @@ void MultiClientContextHubBase::handleMessageFromChre( onNanoappMessage(*message.AsNanoappMessage()); break; } + case fbs::ChreMessage::DebugDumpData: { + onDebugDumpData(*message.AsDebugDumpData()); + break; + } + case fbs::ChreMessage::DebugDumpResponse: { + onDebugDumpComplete(*message.AsDebugDumpResponse()); + break; + } default: LOGW("Got unexpected message type %" PRIu8, static_cast<uint8_t>(message.type)); @@ -432,9 +538,32 @@ void MultiClientContextHubBase::handleHubInfoResponse( mContextHubInfo->chreApiMinorVersion = extractChreApiMinorVersion(version); mContextHubInfo->chrePatchVersion = extractChrePatchVersion(version); mContextHubInfo->supportedPermissions = kSupportedPermissions; + + // TODO(b/312417087): Implement reliable message support + mContextHubInfo->supportsReliableMessages = false; mHubInfoCondition.notify_all(); } +void MultiClientContextHubBase::onDebugDumpData( + const ::chre::fbs::DebugDumpDataT &data) { + auto str = std::string(reinterpret_cast<const char *>(data.debug_str.data()), + data.debug_str.size()); + debugDumpAppend(str); +} + +void MultiClientContextHubBase::onDebugDumpComplete( + const ::chre::fbs::DebugDumpResponseT &response) { + if (!response.success) { + LOGE("Dumping debug information fails"); + } + if (checkDebugFd()) { + const std::string &dump = mEventLogger.dump(); + writeToDebugFile(dump.c_str()); + writeToDebugFile("\n-- End of CHRE/ASH debug info --\n"); + } + debugDumpComplete(); +} + void MultiClientContextHubBase::onNanoappListResponse( const fbs::NanoappListResponseT &response, HalClientId clientId) { std::shared_ptr<IContextHubCallback> callback = @@ -463,6 +592,17 @@ void MultiClientContextHubBase::onNanoappListResponse( appInfo.rpcServices = rpcServices; appInfoList.push_back(appInfo); } + { + std::unique_lock<std::mutex> lock(mTestModeMutex); + if (!mTestModeNanoapps.has_value()) { + mTestModeNanoapps.emplace(); + for (const auto &appInfo : appInfoList) { + mTestModeNanoapps->insert(appInfo.nanoappId); + } + mEnableTestModeCv.notify_all(); + } + } + callback->handleNanoappInfo(appInfoList); } @@ -517,6 +657,14 @@ void MultiClientContextHubBase::onNanoappUnloadResponse( const fbs::UnloadNanoappResponseT &response, HalClientId clientId) { if (mHalClientManager->resetPendingUnloadTransaction( clientId, response.transaction_id)) { + { + std::unique_lock<std::mutex> lock(mTestModeMutex); + if (response.transaction_id == mTestModeTransactionId) { + mTestModeSyncUnloadResult.emplace(response.success); + mEnableTestModeCv.notify_all(); + return; + } + } if (auto callback = mHalClientManager->getCallback(clientId); callback != nullptr) { callback->handleTransactionResult(response.transaction_id, @@ -527,6 +675,7 @@ void MultiClientContextHubBase::onNanoappUnloadResponse( void MultiClientContextHubBase::onNanoappMessage( const ::chre::fbs::NanoappMessageT &message) { + mEventLogger.logMessageFromNanoapp(message); ContextHubMessage outMessage; outMessage.nanoappId = message.app_id; outMessage.hostEndPoint = message.host_endpoint; @@ -567,11 +716,32 @@ void MultiClientContextHubBase::handleClientDeath(pid_t clientPid) { mConnection->sendMessage(builder); } } - mHalClientManager->handleClientDeath(clientPid, mDeathRecipient); + mHalClientManager->handleClientDeath(clientPid); } void MultiClientContextHubBase::onChreRestarted() { mIsWifiAvailable.reset(); + mEventLogger.logContextHubRestart(); mHalClientManager->handleChreRestart(); } + +binder_status_t MultiClientContextHubBase::dump(int fd, + const char ** /* args */, + uint32_t /* numArgs */) { + // debugDumpStart waits for the dump to finish before returning. + debugDumpStart(fd); + return STATUS_OK; +} + +bool MultiClientContextHubBase::requestDebugDump() { + flatbuffers::FlatBufferBuilder builder; + HostProtocolHost::encodeDebugDumpRequest(builder); + return mConnection->sendMessage(builder); +} + +void MultiClientContextHubBase::writeToDebugFile(const char *str) { + if (!::android::base::WriteStringToFd(std::string(str), getDebugFd())) { + LOGW("Failed to write %zu bytes to debug dump fd", strlen(str)); + } +} } // namespace android::hardware::contexthub::common::implementation diff --git a/host/hal_generic/common/multi_client_context_hub_base.h b/host/hal_generic/common/multi_client_context_hub_base.h index 8fa00e54..7d92e71e 100644 --- a/host/hal_generic/common/multi_client_context_hub_base.h +++ b/host/hal_generic/common/multi_client_context_hub_base.h @@ -17,16 +17,15 @@ #ifndef ANDROID_HARDWARE_CONTEXTHUB_COMMON_MULTICLIENTS_HAL_BASE_H_ #define ANDROID_HARDWARE_CONTEXTHUB_COMMON_MULTICLIENTS_HAL_BASE_H_ -#ifndef LOG_TAG -#define LOG_TAG "CHRE.HAL" -#endif - #include <aidl/android/hardware/contexthub/BnContextHub.h> #include <chre_host/generated/host_messages_generated.h> #include "chre_connection_callback.h" #include "chre_host/napp_header.h" #include "chre_host/preloaded_nanoapp_loader.h" +#include "chre_host/time_syncer.h" +#include "debug_dump_helper.h" +#include "event_logger.h" #include "hal_client_id.h" #include "hal_client_manager.h" @@ -37,29 +36,22 @@ using namespace android::chre; using ::ndk::ScopedAStatus; /** - * The base class of multiclients HAL. + * The base class of multiclient HAL. * * A subclass should initiate mConnection, mHalClientManager and * mPreloadedNanoappLoader in its constructor. - * - * TODO(b/247124878): A few things are pending: - * - Some APIs of IContextHub are not implemented yet; - * - onHostEndpointConnected/Disconnected now returns an error if the endpoint - * id is illegal or already connected/disconnected. The doc of - * IContextHub.aidl should be updated accordingly. - * - registerCallback() can fail if mHalClientManager sees an error during - * registration. The doc of IContextHub.aidl should be updated accordingly. - * - Involve EventLogger to log API calls; - * - extends DebugDumpHelper to ease debugging */ class MultiClientContextHubBase : public BnContextHub, public ::android::hardware::contexthub::common::implementation:: - ChreConnectionCallback { + ChreConnectionCallback, + public ::android::hardware::contexthub::DebugDumpHelper { public: /** The entry point of death recipient for a disconnected client. */ static void onClientDied(void *cookie); + MultiClientContextHubBase(); + // functions implementing IContextHub ScopedAStatus getContextHubs( std::vector<ContextHubInfo> *contextHubInfos) override; @@ -86,12 +78,20 @@ class MultiClientContextHubBase ScopedAStatus onNanSessionStateChanged( const NanSessionStateUpdate &in_update) override; ScopedAStatus setTestMode(bool enable) override; + ScopedAStatus sendMessageDeliveryStatusToHub( + int32_t contextHubId, + const MessageDeliveryStatus &messageDeliveryStatus) override; // The callback function implementing ChreConnectionCallback void handleMessageFromChre(const unsigned char *messageBuffer, size_t messageLen) override; void onChreRestarted() override; + // The functions for dumping debug information + binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; + bool requestDebugDump() override; + void writeToDebugFile(const char *str) override; + protected: // The data needed by the death client to clear states of a client. struct HalDeathRecipientCookie { @@ -102,7 +102,15 @@ class MultiClientContextHubBase this->clientPid = pid; } }; - MultiClientContextHubBase() = default; + + static constexpr uint32_t kDefaultTestModeTransactionId = 0x80000000; + + void tryTimeSync(size_t numOfRetries, useconds_t retryDelayUs) { + if (mConnection->isTimeSyncNeeded()) { + TimeSyncer::sendTimeSyncWithRetry(mConnection.get(), numOfRetries, + retryDelayUs); + } + } bool sendFragmentedLoadRequest(HalClientId clientId, FragmentedLoadRequest &fragmentedLoadRequest); @@ -117,14 +125,21 @@ class MultiClientContextHubBase const ::chre::fbs::UnloadNanoappResponseT &response, HalClientId clientId); void onNanoappMessage(const ::chre::fbs::NanoappMessageT &message); - + void onDebugDumpData(const ::chre::fbs::DebugDumpDataT &data); + void onDebugDumpComplete( + const ::chre::fbs::DebugDumpResponseT & /* response */); void handleClientDeath(pid_t pid); + bool enableTestMode(); + bool disableTestMode(); + inline bool isSettingEnabled(Setting setting) { return mSettingEnabled.find(setting) != mSettingEnabled.end() && mSettingEnabled[setting]; } + HalClientManager::DeadClientUnlinker mDeadClientUnlinker; + // HAL is the unique owner of the communication channel to CHRE. std::unique_ptr<ChreConnection> mConnection{}; @@ -151,6 +166,18 @@ class MultiClientContextHubBase // A mutex to synchronize access to the list of preloaded nanoapp IDs. std::mutex mPreloadedNanoappIdsMutex; std::optional<std::vector<uint64_t>> mPreloadedNanoappIds{}; + + // test mode settings + std::mutex mTestModeMutex; + std::condition_variable mEnableTestModeCv; + bool mIsTestModeEnabled = false; + std::optional<bool> mTestModeSyncUnloadResult = std::nullopt; + std::optional<std::unordered_set<uint64_t>> mTestModeNanoapps = + std::unordered_set<uint64_t>{}; + int32_t mTestModeTransactionId = + static_cast<int32_t>(kDefaultTestModeTransactionId); + + EventLogger mEventLogger; }; } // namespace android::hardware::contexthub::common::implementation #endif // ANDROID_HARDWARE_CONTEXTHUB_COMMON_MULTICLIENTS_HAL_BASE_H_ diff --git a/host/test/hal_generic/common/hal_client_manager_test.cc b/host/test/hal_generic/common/hal_client_manager_test.cc new file mode 100644 index 00000000..b8e7a5f9 --- /dev/null +++ b/host/test/hal_generic/common/hal_client_manager_test.cc @@ -0,0 +1,552 @@ +/* + * 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. + */ + +#include <array> +#include <chrono> +#include <cstdlib> +#include <fstream> +#include <thread> + +#include <json/json.h> + +#include <aidl/android/hardware/contexthub/BnContextHubCallback.h> +#include <aidl/android/hardware/contexthub/IContextHub.h> +#include <aidl/android/hardware/contexthub/NanoappBinary.h> +#include "chre/platform/shared/host_protocol_common.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "hal_client_manager.h" + +namespace android::hardware::contexthub::common::implementation { + +namespace { +using aidl::android::hardware::contexthub::AsyncEventType; +using aidl::android::hardware::contexthub::BnContextHubCallback; +using aidl::android::hardware::contexthub::ContextHubMessage; +using aidl::android::hardware::contexthub::MessageDeliveryStatus; +using aidl::android::hardware::contexthub::NanoappInfo; +using aidl::android::hardware::contexthub::NanSessionRequest; + +using ndk::ScopedAStatus; + +using ::testing::_; +using ::testing::ByMove; +using ::testing::IsEmpty; +using ::testing::Return; +using ::testing::SizeIs; +using ::testing::UnorderedElementsAre; + +using HalClient = HalClientManager::HalClient; + +constexpr pid_t kSystemServerPid = 1000; +// The uuid assigned to ContextHubService +const std::string kSystemServerUuid = "9a17008d6bf1445a90116d21bd985b6c"; + +constexpr pid_t kVendorPid = 1001; +const std::string kVendorUuid = "6e406b36cf4f4c0d8183db3708f45d8f"; + +const std::string kClientIdMappingFilePath = "./chre_hal_clients.json"; +const std::string kClientName = "HalClientManagerTest"; + +class ContextHubCallbackForTest : public BnContextHubCallback { + public: + explicit ContextHubCallbackForTest(const std::string &uuid) { + assert(uuid.length() == 32); // 2 digits for one bytes x 16 bytes + for (int i = 0; i < 16; i++) { + mUuid[i] = strtol(uuid.substr(i * 2, 2).c_str(), /* end_ptr= */ nullptr, + /* base= */ 16); + } + ON_CALL(*this, handleContextHubAsyncEvent(_)) + .WillByDefault(Return(ByMove(ScopedAStatus::ok()))); + } + ScopedAStatus handleNanoappInfo( + const std::vector<NanoappInfo> & /*appInfo*/) override { + return ScopedAStatus::ok(); + } + + ScopedAStatus handleContextHubMessage( + const ContextHubMessage & /*message*/, + const std::vector<std::string> & /*msgContentPerms*/) override { + return ScopedAStatus::ok(); + } + + MOCK_METHOD(ScopedAStatus, handleContextHubAsyncEvent, (AsyncEventType event), + (override)); + + // Called after loading/unloading a nanoapp. + ScopedAStatus handleTransactionResult(int32_t /*transactionId*/, + bool /*success*/) override { + return ScopedAStatus::ok(); + } + + ScopedAStatus handleNanSessionRequest( + const NanSessionRequest & /* request */) override { + return ScopedAStatus::ok(); + } + + ScopedAStatus handleMessageDeliveryStatus( + char16_t /* hostEndPointId */, + const MessageDeliveryStatus & /* messageDeliveryStatus */) override { + return ScopedAStatus::ok(); + } + + ScopedAStatus getUuid(std::array<uint8_t, 16> *out_uuid) override { + *out_uuid = mUuid; + return ScopedAStatus::ok(); + } + + ScopedAStatus getName(std::string *out_name) override { + *out_name = kClientName; + return ScopedAStatus::ok(); + } + + private: + const std::string kClientName = "HalClientManagerUnitTest"; + std::array<uint8_t, 16> mUuid{}; +}; + +class HalClientManagerForTest : public HalClientManager { + public: + HalClientManagerForTest( + DeadClientUnlinker deadClientUnlinker, + const std::string &clientIdMappingFilePath, + const std::unordered_set<HalClientId> &reservedClientIds = {}) + : HalClientManager(std::move(deadClientUnlinker), clientIdMappingFilePath, + reservedClientIds) {} + + const std::vector<HalClient> getClients() { + return mClients; + } + + bool createClientForTest(const std::string &uuid, pid_t pid) { + // No need to hold the lock during a unit test which is single-threaded + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + return createClientLocked(uuid, pid, callback, + /* deathRecipientCookie= */ nullptr); + } + + HalClientId getNextClientId() { + return mNextClientId; + } + + static int64_t getTransactionTimeoutSeconds() { + return kTransactionTimeoutThresholdMs / 1000; + } + + static const char *getClientIdTag() { + return kJsonClientId; + } + + static const char *getUuidTag() { + return kJsonUuid; + } + + static const char *getNameTag() { + return kJsonName; + } +}; + +class HalClientManagerTest : public ::testing::Test { + protected: + void SetUp() override { + // Clears out the mapping file content + std::ofstream file(kClientIdMappingFilePath); + ASSERT_TRUE(file.good()); + } + void TearDown() override {} +}; + +auto mockDeadClientUnlinker = + [](const std::shared_ptr<IContextHubCallback> & /*callback*/, + void * /*deathRecipientCookie*/) { return true; }; + +std::unique_ptr<FragmentedLoadTransaction> createLoadTransaction( + uint32_t transactionId) { + uint64_t appId = 0x476f6f676cabcdef; + uint32_t appVersion = 2; + uint32_t appFlags = 3; + uint32_t targetApiVersion = 4; + std::vector<uint8_t> binary = {0xf0, 0xf1}; + return std::make_unique<FragmentedLoadTransaction>( + transactionId, appId, appVersion, appFlags, targetApiVersion, binary, + /* fragmentSize= */ 2048); +} + +TEST_F(HalClientManagerTest, ClientIdMappingFile) { + HalClientId systemClientId = 100; + { + // Write systemClientId into the mapping file + Json::Value mappings; + Json::Value mapping; + mapping[HalClientManagerForTest::getClientIdTag()] = systemClientId; + mapping[HalClientManagerForTest::getUuidTag()] = kSystemServerUuid; + mapping[HalClientManagerForTest::getNameTag()] = kClientName; + mappings.append(mapping); + Json::StreamWriterBuilder factory; + std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter()); + std::ofstream fileStream(kClientIdMappingFilePath); + writer->write(mappings, &fileStream); + fileStream << std::endl; + } + + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + EXPECT_TRUE( + halClientManager->registerCallback(kSystemServerPid, callback, + /* deathRecipientCookie= */ nullptr)); + + std::vector<HalClient> clients = halClientManager->getClients(); + const HalClient &client = clients.front(); + EXPECT_THAT(clients, SizeIs(1)); + EXPECT_THAT(client.endpointIds, IsEmpty()); + EXPECT_EQ(client.callback, callback); + EXPECT_EQ(client.uuid, kSystemServerUuid); + EXPECT_EQ(client.pid, kSystemServerPid); + // The client id allocated should be the one specified in the mapping file + EXPECT_EQ(client.clientId, systemClientId); +} + +TEST_F(HalClientManagerTest, CallbackRegistryBasic) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + + EXPECT_TRUE( + halClientManager->registerCallback(kSystemServerPid, callback, + /* deathRecipientCookie= */ nullptr)); + + std::vector<HalClient> clients = halClientManager->getClients(); + const HalClient &client = clients.front(); + + EXPECT_THAT(clients, SizeIs(1)); + EXPECT_THAT(client.endpointIds, IsEmpty()); + EXPECT_EQ(client.callback, callback); + EXPECT_EQ(client.uuid, kSystemServerUuid); + EXPECT_EQ(client.pid, kSystemServerPid); + EXPECT_NE(client.clientId, ::chre::kHostClientIdUnspecified); +} + +TEST_F(HalClientManagerTest, CallbackRegistryTwiceFromSameClient) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callbackA = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + std::shared_ptr<ContextHubCallbackForTest> callbackB = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + + EXPECT_TRUE( + halClientManager->registerCallback(kSystemServerPid, callbackA, + /* deathRecipientCookie= */ nullptr)); + EXPECT_THAT(halClientManager->getClients(), SizeIs(1)); + EXPECT_EQ(halClientManager->getClients().front().callback, callbackA); + // Same client can override its callback + EXPECT_TRUE( + halClientManager->registerCallback(kSystemServerPid, callbackB, + /* deathRecipientCookie= */ nullptr)); + EXPECT_THAT(halClientManager->getClients(), SizeIs(1)); + EXPECT_EQ(halClientManager->getClients().front().callback, callbackB); +} + +TEST_F(HalClientManagerTest, CallbackRetrievalByEndpoint) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> systemCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + std::shared_ptr<ContextHubCallbackForTest> vendorCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid); + uint16_t vendorEndpointId = 1; + uint16_t systemServerEndpointId = 1; + + // Register the callbacks and endpoint ids + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); + EXPECT_TRUE(halClientManager->registerEndpointId(kSystemServerPid, + systemServerEndpointId)); + EXPECT_TRUE(halClientManager->registerCallback( + kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); + EXPECT_TRUE( + halClientManager->registerEndpointId(kVendorPid, vendorEndpointId)); + + // Though endpoint ids have the same value, they should be mutated before + // getting sent to CHRE and mapped to different callbacks + EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( + kVendorPid, vendorEndpointId)); + EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( + kSystemServerPid, systemServerEndpointId)); + EXPECT_EQ(halClientManager->getCallbackForEndpoint(vendorEndpointId), + vendorCallback); + EXPECT_EQ(halClientManager->getCallbackForEndpoint(systemServerEndpointId), + systemCallback); +} + +TEST_F(HalClientManagerTest, ClientCreation) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + int uuid = 1; + int pid = 1; + for (int i = 0; i < kMaxNumOfHalClients; i++, uuid++, pid++) { + EXPECT_TRUE( + halClientManager->createClientForTest(std::to_string(uuid), pid)); + } + // if max number of clients are reached no more client can be created + EXPECT_FALSE( + halClientManager->createClientForTest(std::to_string(uuid), pid)); + // mNextClientId is reset to ::chre::kHostClientIdUnspecified when new client + // is not accepted + EXPECT_EQ(halClientManager->getNextClientId(), + ::chre::kHostClientIdUnspecified); +} + +TEST_F(HalClientManagerTest, ClientCreationWithReservedClientId) { + std::unordered_set<HalClientId> reservedClientIds{ + ::chre::kHostClientIdUnspecified + 1, 64}; + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath, reservedClientIds); + int uuid = 1; + int pid = 1; + for (int i = 0; i < kMaxNumOfHalClients - reservedClientIds.size(); + i++, uuid++, pid++) { + EXPECT_TRUE( + halClientManager->createClientForTest(std::to_string(uuid), pid)); + } + // if max number of clients are reached no more client can be created + EXPECT_FALSE( + halClientManager->createClientForTest(std::to_string(uuid), pid)); + // mNextClientId is reset to ::chre::kHostClientIdUnspecified when new client + // is not accepted + EXPECT_EQ(halClientManager->getNextClientId(), + ::chre::kHostClientIdUnspecified); + // Verify that every reserved client id is not used: + for (const HalClient &client : halClientManager->getClients()) { + EXPECT_EQ(reservedClientIds.find(client.clientId), reservedClientIds.end()); + } +} + +TEST_F(HalClientManagerTest, TransactionRegistryAndOverridden) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); + + EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( + kSystemServerPid, createLoadTransaction(/* transactionId= */ 1))); + + // Immediate transaction overridden is not allowed as each transaction is + // given a certain amount of time to finish + EXPECT_FALSE(halClientManager->registerPendingLoadTransaction( + kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); + + // Wait until the transaction is timed out to override it + std::this_thread::sleep_for(std::chrono::seconds( + HalClientManagerForTest::getTransactionTimeoutSeconds())); + EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( + kSystemServerPid, createLoadTransaction(/* transactionId= */ 3))); +} + +TEST_F(HalClientManagerTest, TransactionRegistryLoadAndUnload) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); + + EXPECT_TRUE(halClientManager->registerPendingUnloadTransaction( + kSystemServerPid, /* transactionId= */ 1)); + + // Load and unload transaction can't coexist because unloading a nanoapp that + // is being loaded can cause problems. + EXPECT_FALSE(halClientManager->registerPendingLoadTransaction( + kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); + + // Clears out the pending unload transaction to register a new one. + halClientManager->resetPendingUnloadTransaction( + halClientManager->getClientId(kSystemServerPid), /* transactionId= */ 1); + EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( + kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); +} + +TEST_F(HalClientManagerTest, EndpointRegistry) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> systemCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + std::shared_ptr<ContextHubCallbackForTest> vendorCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid); + + halClientManager->registerCallback(kSystemServerPid, systemCallback, + /* deathRecipientCookie= */ nullptr); + halClientManager->registerCallback(kVendorPid, vendorCallback, + /* deathRecipientCookie= */ nullptr); + + std::vector<HalClient> clients = halClientManager->getClients(); + EXPECT_THAT(clients, SizeIs(2)); + // only system server can register endpoint ids > 63. + + EXPECT_TRUE(halClientManager->registerEndpointId(kSystemServerPid, + /* endpointId= */ 64)); + EXPECT_TRUE(halClientManager->registerEndpointId(kVendorPid, + /*endpointId= */ 63)); + EXPECT_FALSE(halClientManager->registerEndpointId(kVendorPid, + /* endpointId= */ 64)); +} + +TEST_F(HalClientManagerTest, EndpointIdMutationForVendorClient) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> vendorCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid); + std::shared_ptr<ContextHubCallbackForTest> systemCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + const uint16_t originalEndpointId = 10; // 0b1010; + uint16_t mutatedEndpointId = originalEndpointId; + + // Register the system callback + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); + // Register the vendor callback + EXPECT_TRUE(halClientManager->registerCallback( + kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); + + // Mutate endpoint id from host to CHRE + EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( + kVendorPid, mutatedEndpointId)); + HalClientId clientId = halClientManager->getClientId(kVendorPid); + EXPECT_EQ(mutatedEndpointId, 0x8000 | clientId << 6 | originalEndpointId); + + // Mutate endpoint id from CHRE to Host + EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), + originalEndpointId); +} + +TEST_F(HalClientManagerTest, EndpointIdMutationForSystemServer) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + const uint16_t originalEndpointId = 100; + uint16_t mutatedEndpointId = originalEndpointId; + + // Register the callback + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); + + // Endpoint id from the system server shouldn't be mutated + EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( + kSystemServerPid, mutatedEndpointId)); + EXPECT_EQ(mutatedEndpointId, originalEndpointId); + EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), + originalEndpointId); +} + +TEST_F(HalClientManagerTest, EndpointIdUnknownFromChre) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> vendorCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid); + std::shared_ptr<ContextHubCallbackForTest> systemCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + const HostEndpointId originalEndpointId = 0x10; // unregistered endpoint id + HostEndpointId mutatedEndpointId = originalEndpointId; + + // Register the callback + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); + EXPECT_TRUE(halClientManager->registerCallback( + kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); + + // As long as a client's callback is registered, hal_client_manager won't + // block message exchanged from/to the client even if the endpoint id is + // not registered. The enforcement of endpoint id registration is done on the + // client side (contextHubService, library, etc.). + EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( + kVendorPid, mutatedEndpointId)); + EXPECT_NE(mutatedEndpointId, originalEndpointId); + EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), + originalEndpointId); + EXPECT_EQ(halClientManager->getCallbackForEndpoint(mutatedEndpointId), + vendorCallback); +} + +TEST_F(HalClientManagerTest, handleDeathClient) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> callback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + halClientManager->registerCallback(kSystemServerPid, callback, + /* deathRecipientCookie= */ nullptr); + halClientManager->registerEndpointId(kSystemServerPid, /* endpointId= */ 10); + + halClientManager->handleClientDeath(kSystemServerPid); + + const std::vector<HalClient> &clients = halClientManager->getClients(); + EXPECT_THAT(clients, SizeIs(1)); + const HalClient &client = clients.front(); + EXPECT_EQ(client.callback, nullptr); + EXPECT_EQ(client.pid, HalClient::PID_UNSET); + EXPECT_EQ(client.uuid, kSystemServerUuid); + EXPECT_NE(client.clientId, ::chre::kHostClientIdUnspecified); + EXPECT_THAT(client.endpointIds, IsEmpty()); +} + +TEST_F(HalClientManagerTest, handleChreRestartForConnectedClientsOnly) { + auto halClientManager = std::make_unique<HalClientManagerForTest>( + mockDeadClientUnlinker, kClientIdMappingFilePath); + std::shared_ptr<ContextHubCallbackForTest> vendorCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid); + std::shared_ptr<ContextHubCallbackForTest> systemCallback = + ContextHubCallbackForTest::make<ContextHubCallbackForTest>( + kSystemServerUuid); + // Register the system callback + EXPECT_TRUE(halClientManager->registerCallback( + kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); + // Register the vendor callback + EXPECT_TRUE(halClientManager->registerCallback( + kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); + + // Only connected clients' handleContextHubAsyncEvent should be called. + EXPECT_CALL(*systemCallback, + handleContextHubAsyncEvent(AsyncEventType::RESTARTED)); + EXPECT_CALL(*vendorCallback, + handleContextHubAsyncEvent(AsyncEventType::RESTARTED)) + .Times(0); + + // Disconnect the vendor client and handle CHRE restart for the system server + halClientManager->handleClientDeath(kVendorPid); + halClientManager->handleChreRestart(); +} + +} // namespace +} // namespace android::hardware::contexthub::common::implementation diff --git a/host/test/hal_generic/common/hal_client_test.cc b/host/test/hal_generic/common/hal_client_test.cc new file mode 100644 index 00000000..eac5bd84 --- /dev/null +++ b/host/test/hal_generic/common/hal_client_test.cc @@ -0,0 +1,212 @@ +/* + * 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. + */ +#include "chre_host/hal_client.h" +#include "host/hal_generic/common/hal_error.h" + +#include <unordered_set> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include <aidl/android/hardware/contexthub/IContextHub.h> + +namespace android::chre { + +namespace { +using ::aidl::android::hardware::contexthub::ContextHubMessage; +using ::aidl::android::hardware::contexthub::HostEndpointInfo; +using ::aidl::android::hardware::contexthub::IContextHub; +using ::aidl::android::hardware::contexthub::IContextHubCallbackDefault; +using ::aidl::android::hardware::contexthub::IContextHubDefault; + +using ::ndk::ScopedAStatus; + +using ::testing::_; +using ::testing::ByMove; +using ::testing::Field; +using ::testing::IsEmpty; +using ::testing::Return; +using ::testing::UnorderedElementsAre; + +using HostEndpointId = char16_t; +constexpr HostEndpointId kEndpointId = 0x10; + +class HalClientForTest : public HalClient { + public: + HalClientForTest(const std::shared_ptr<IContextHub> &contextHub, + const std::vector<HostEndpointId> &connectedEndpoints, + const std::shared_ptr<IContextHubCallback> &callback = + ndk::SharedRefBase::make<IContextHubCallbackDefault>()) + : HalClient(callback) { + mContextHub = contextHub; + for (const HostEndpointId &endpointId : connectedEndpoints) { + mConnectedEndpoints[endpointId] = {.hostEndpointId = endpointId}; + } + } + + std::unordered_set<HostEndpointId> getConnectedEndpointIds() { + std::unordered_set<HostEndpointId> result{}; + for (const auto &[endpointId, unusedEndpointInfo] : mConnectedEndpoints) { + result.insert(endpointId); + } + return result; + } + + HalClientCallback *getClientCallback() { + return mCallback.get(); + } +}; + +class MockContextHub : public IContextHubDefault { + public: + MOCK_METHOD(ScopedAStatus, onHostEndpointConnected, + (const HostEndpointInfo &info), (override)); + MOCK_METHOD(ScopedAStatus, onHostEndpointDisconnected, + (HostEndpointId endpointId), (override)); + MOCK_METHOD(ScopedAStatus, queryNanoapps, (int32_t icontextHubId), + (override)); + MOCK_METHOD(ScopedAStatus, sendMessageToHub, + (int32_t contextHubId, const ContextHubMessage &message), + (override)); +}; + +} // namespace + +TEST(HalClientTest, EndpointConnectionBasic) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + const HostEndpointInfo kInfo = { + .hostEndpointId = kEndpointId, + .type = HostEndpointInfo::Type::NATIVE, + .packageName = "HalClientTest", + .attributionTag{}, + }; + + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{}); + EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty()); + + EXPECT_CALL(*mockContextHub, + onHostEndpointConnected( + Field(&HostEndpointInfo::hostEndpointId, kEndpointId))) + .WillOnce(Return(ScopedAStatus::ok())); + + halClient->connectEndpoint(kInfo); + EXPECT_THAT(halClient->getConnectedEndpointIds(), + UnorderedElementsAre(kEndpointId)); +} + +TEST(HalClientTest, EndpointConnectionMultipleRequests) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + const HostEndpointInfo kInfo = { + .hostEndpointId = kEndpointId, + .type = HostEndpointInfo::Type::NATIVE, + .packageName = "HalClientTest", + .attributionTag{}, + }; + + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{}); + EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty()); + + // multiple requests are tolerated + EXPECT_CALL(*mockContextHub, + onHostEndpointConnected( + Field(&HostEndpointInfo::hostEndpointId, kEndpointId))) + .WillOnce(Return(ScopedAStatus::ok())) + .WillOnce(Return(ScopedAStatus::ok())); + + halClient->connectEndpoint(kInfo); + halClient->connectEndpoint(kInfo); + + EXPECT_THAT(halClient->getConnectedEndpointIds(), + UnorderedElementsAre(kEndpointId)); +} + +TEST(HalClientTest, EndpointDisconnectionBasic) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{kEndpointId}); + + EXPECT_THAT(halClient->getConnectedEndpointIds(), + UnorderedElementsAre(kEndpointId)); + + EXPECT_CALL(*mockContextHub, onHostEndpointDisconnected(kEndpointId)) + .WillOnce(Return(ScopedAStatus::ok())); + halClient->disconnectEndpoint(kEndpointId); + + EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty()); +} + +TEST(HalClientTest, EndpointDisconnectionMultipleRequest) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{kEndpointId}); + EXPECT_THAT(halClient->getConnectedEndpointIds(), + UnorderedElementsAre(kEndpointId)); + + EXPECT_CALL(*mockContextHub, onHostEndpointDisconnected(kEndpointId)) + .WillOnce(Return(ScopedAStatus::ok())) + .WillOnce(Return(ScopedAStatus::ok())); + + halClient->disconnectEndpoint(kEndpointId); + halClient->disconnectEndpoint(kEndpointId); + + EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty()); +} + +TEST(HalClientTest, SendMessageBasic) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + const ContextHubMessage contextHubMessage = { + .nanoappId = 0xbeef, + .hostEndPoint = kEndpointId, + .messageBody = {}, + .permissions = {}, + }; + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{kEndpointId}); + + EXPECT_CALL(*mockContextHub, sendMessageToHub(_, _)) + .WillOnce(Return(ScopedAStatus::ok())); + halClient->sendMessage(contextHubMessage); +} + +TEST(HalClientTest, QueryNanoapp) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, std::vector<HostEndpointId>{}); + + EXPECT_CALL(*mockContextHub, queryNanoapps(HalClient::kDefaultContextHubId)); + halClient->queryNanoapps(); +} + +TEST(HalClientTest, HandleChreRestart) { + auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>(); + + auto halClient = std::make_unique<HalClientForTest>( + mockContextHub, + std::vector<HostEndpointId>{kEndpointId, kEndpointId + 1}); + + EXPECT_CALL(*mockContextHub, onHostEndpointConnected( + Field(&HostEndpointInfo::hostEndpointId, _))) + .WillOnce(Return(ScopedAStatus::ok())) + .WillOnce(Return(ScopedAStatus::ok())); + + halClient->getClientCallback()->handleContextHubAsyncEvent( + AsyncEventType::RESTARTED); + EXPECT_THAT(halClient->getConnectedEndpointIds(), + UnorderedElementsAre(kEndpointId, kEndpointId + 1)); +} +} // namespace android::chre diff --git a/host/tinysys/hal/Android.bp b/host/tinysys/hal/Android.bp index 0550da16..5dde4af0 100644 --- a/host/tinysys/hal/Android.bp +++ b/host/tinysys/hal/Android.bp @@ -33,7 +33,7 @@ cc_binary { "tinysys_chre_connection.cc", "tinysys_context_hub.cc", ":st_hal_lpma_handler", - ":contexthub_generic_aidl_hal_core", + ":contexthub_hal_core", ], include_dirs: [ "system/chre/util/include/", @@ -53,7 +53,7 @@ cc_binary { ], shared_libs: [ "android.media.soundtrigger.types-V1-ndk", - "android.hardware.contexthub-V2-ndk", + "android.hardware.contexthub-V3-ndk", "android.hardware.soundtrigger3-V1-ndk", "libcutils", "liblog", @@ -62,6 +62,7 @@ cc_binary { "libbinder_ndk", "libpower", "libjsoncpp", + "server_configurable_flags", ], header_libs: [ "chre_api", @@ -70,6 +71,7 @@ cc_binary { ], static_libs: [ "chre_client", + "chre_flags_c_lib", "event_logger", "pw_varint", "pw_detokenizer", diff --git a/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml b/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml index 54d4592e..db2e2d7c 100644 --- a/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml +++ b/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml @@ -1,7 +1,7 @@ <manifest version="1.0" type="device"> <hal format="aidl"> <name>android.hardware.contexthub</name> - <version>2</version> + <version>3</version> <fqname>IContextHub/default</fqname> </hal> </manifest> diff --git a/host/tinysys/hal/tinysys_chre_connection.cc b/host/tinysys/hal/tinysys_chre_connection.cc index 3be581ce..c560d7ba 100644 --- a/host/tinysys/hal/tinysys_chre_connection.cc +++ b/host/tinysys/hal/tinysys_chre_connection.cc @@ -21,6 +21,7 @@ #include <hardware_legacy/power.h> #include <sys/ioctl.h> +#include <utils/SystemClock.h> #include <cerrno> #include <thread> @@ -131,10 +132,13 @@ bool TinysysChreConnection::init() { chreNextState); } if (chreCurrentState == SCP_CHRE_STOP && chreNextState == SCP_CHRE_START) { - // TODO(b/277128368): We should have an explicit indication from CHRE for - // restart recovery. - LOGW("SCP restarted. Give it 5s for recovery before notifying clients"); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + int64_t startTime = ::android::elapsedRealtime(); + // Though usually CHRE is recovered within 1s after SCP is up, in a corner + // case it can go beyond 5s. Wait for 10s to cover more extreme cases. + chreConnection->waitChreBackOnline( + /* timeoutMs= */ std::chrono::milliseconds(10000)); + LOGW("SCP restarted! CHRE recover time: %" PRIu64 "ms.", + ::android::elapsedRealtime() - startTime); chreConnection->getCallback()->onChreRestarted(); } chreCurrentState = chreNextState; @@ -206,6 +210,10 @@ void TinysysChreConnection::handleMessageFromChre( chreConnection->getLpmaHandler()->enable(/* enabled= */ false); break; } + case fbs::ChreMessage::PulseResponse: { + chreConnection->notifyChreBackOnline(); + break; + } case fbs::ChreMessage::MetricLog: case fbs::ChreMessage::NanConfigurationRequest: case fbs::ChreMessage::TimeSyncRequest: diff --git a/host/tinysys/hal/tinysys_chre_connection.h b/host/tinysys/hal/tinysys_chre_connection.h index e42b0c49..32974e0e 100644 --- a/host/tinysys/hal/tinysys_chre_connection.h +++ b/host/tinysys/hal/tinysys_chre_connection.h @@ -20,6 +20,7 @@ #include "chre_connection.h" #include "chre_connection_callback.h" #include "chre_host/fragmented_load_transaction.h" +#include "chre_host/host_protocol_host.h" #include "chre_host/log.h" #include "chre_host/log_message_parser.h" #include "chre_host/st_hal_lpma_handler.h" @@ -35,6 +36,7 @@ using ::android::chre::StHalLpmaHandler; namespace aidl::android::hardware::contexthub { using namespace ::android::hardware::contexthub::common::implementation; +using ::android::chre::HostProtocolHost; /** A class handling message transmission between context hub HAL and CHRE. */ // TODO(b/267188769): We should add comments explaining how IPI works. @@ -67,6 +69,27 @@ class TinysysChreConnection : public ChreConnection { bool sendMessage(void *data, size_t length) override; + void waitChreBackOnline(std::chrono::milliseconds timeoutMs) { + flatbuffers::FlatBufferBuilder builder(48); + HostProtocolHost::encodePulseRequest(builder); + + std::unique_lock<std::mutex> lock(mChrePulseMutex); + // reset mIsChreRecovered before sending a PulseRequest message + mIsChreBackOnline = false; + sendMessage(builder.GetBufferPointer(), builder.GetSize()); + mChrePulseCondition.wait_for( + lock, timeoutMs, + [&isChreBackOnline = mIsChreBackOnline] { return isChreBackOnline; }); + } + + void notifyChreBackOnline() { + { + std::unique_lock<std::mutex> lock(mChrePulseMutex); + mIsChreBackOnline = true; + } + mChrePulseCondition.notify_all(); + } + inline ChreConnectionCallback *getCallback() { return mCallback; } @@ -191,6 +214,12 @@ class TinysysChreConnection : public ChreConnection { // For messages sent to CHRE SynchronousMessageQueue mQueue; + + // Mutex and CV are used to get PulseResponse from CHRE synchronously. + std::mutex mChrePulseMutex; + std::condition_variable mChrePulseCondition; + bool mIsChreBackOnline = + false; // set to true after CHRE recovers from a restart }; } // namespace aidl::android::hardware::contexthub diff --git a/host/tinysys/hal/tinysys_context_hub.cc b/host/tinysys/hal/tinysys_context_hub.cc index 2f54aba7..2e290c69 100644 --- a/host/tinysys/hal/tinysys_context_hub.cc +++ b/host/tinysys/hal/tinysys_context_hub.cc @@ -18,28 +18,21 @@ namespace aidl::android::hardware::contexthub { TinysysContextHub::TinysysContextHub() { - mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( - AIBinder_DeathRecipient_new(onClientDied)); - AIBinder_DeathRecipient_setOnUnlinked( - mDeathRecipient.get(), [](void *cookie) { - LOGI("Callback is unlinked. Releasing the death recipient cookie."); - delete static_cast<HalDeathRecipientCookie *>(cookie); - }); mConnection = std::make_unique<TinysysChreConnection>(this); - mHalClientManager = std::make_unique<HalClientManager>(); + mHalClientManager = std::make_unique<HalClientManager>( + mDeadClientUnlinker, kClientIdMappingFilePath); mPreloadedNanoappLoader = std::make_unique<PreloadedNanoappLoader>( mConnection.get(), kPreloadedNanoappsConfigPath); if (mConnection->init()) { - if (!kPreloadedNanoappsConfigPath.empty()) { - mPreloadedNanoappLoader->loadPreloadedNanoapps(); - } + mPreloadedNanoappLoader->loadPreloadedNanoapps(); + } else { + LOGE("Failed to initialize the connection to CHRE. Restart."); + exit(-1); } } void TinysysContextHub::onChreRestarted() { - if (!kPreloadedNanoappsConfigPath.empty()) { - mPreloadedNanoappLoader->loadPreloadedNanoapps(); - } + mPreloadedNanoappLoader->loadPreloadedNanoapps(); MultiClientContextHubBase::onChreRestarted(); } } // namespace aidl::android::hardware::contexthub
\ No newline at end of file diff --git a/host/tinysys/hal/tinysys_context_hub.h b/host/tinysys/hal/tinysys_context_hub.h index 4be2d933..2fbf1bb2 100644 --- a/host/tinysys/hal/tinysys_context_hub.h +++ b/host/tinysys/hal/tinysys_context_hub.h @@ -36,6 +36,8 @@ class TinysysContextHub : public MultiClientContextHubBase { void onChreRestarted() override; const std::string kPreloadedNanoappsConfigPath = "/vendor/etc/chre/preloaded_nanoapps.json"; + const std::string kClientIdMappingFilePath = + "/data/vendor/chre/chre_hal_clients.json"; }; } // namespace aidl::android::hardware::contexthub #endif // ANDROID_HARDWARE_CONTEXTHUB_AIDL_CONTEXTHUB_H diff --git a/java/test/audio_concurrency/Android.bp b/java/test/audio_concurrency/Android.bp index 462f7216..064e3760 100644 --- a/java/test/audio_concurrency/Android.bp +++ b/java/test/audio_concurrency/Android.bp @@ -33,5 +33,5 @@ java_library { "chre-test-utils", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/audio_concurrency/src/com/google/android/chre/test/audioconcurrency/ContextHubAudioConcurrencyTestExecutor.java b/java/test/audio_concurrency/src/com/google/android/chre/test/audioconcurrency/ContextHubAudioConcurrencyTestExecutor.java index b5345869..b2aae5ec 100644 --- a/java/test/audio_concurrency/src/com/google/android/chre/test/audioconcurrency/ContextHubAudioConcurrencyTestExecutor.java +++ b/java/test/audio_concurrency/src/com/google/android/chre/test/audioconcurrency/ContextHubAudioConcurrencyTestExecutor.java @@ -25,6 +25,8 @@ import android.hardware.location.NanoAppMessage; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.util.Log; import com.google.android.chre.nanoapp.proto.ChreAudioConcurrencyTest; @@ -65,6 +67,8 @@ public class ContextHubAudioConcurrencyTestExecutor extends ContextHubClientCall private boolean mInitialized = false; + private boolean mVerifyAudioGaps = false; + private final AtomicBoolean mChreAudioEnabled = new AtomicBoolean(false); private final AtomicReference<ChreTestCommon.TestResult> mTestResult = new AtomicReference<>(); @@ -132,21 +136,24 @@ public class ContextHubAudioConcurrencyTestExecutor extends ContextHubClientCall Assert.assertFalse("init() must not be invoked when already initialized", mInitialized); ChreTestUtil.loadNanoAppAssertSuccess(mContextHubManager, mContextHubInfo, mNanoAppBinary); + mVerifyAudioGaps = shouldVerifyAudioGaps(); mInitialized = true; } /** * Runs the test. */ - public void run() { + public void run() throws InterruptedException { // Send a message to the nanoapp to enable CHRE audio mCountDownLatch = new CountDownLatch(1); - sendTestCommandMessage(ChreAudioConcurrencyTest.TestCommand.Step.ENABLE_AUDIO); - try { - mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); + if (mVerifyAudioGaps) { + sendTestCommandMessage( + ChreAudioConcurrencyTest.TestCommand.Step.ENABLE_AUDIO_WITH_GAP_VERIFICATION); + } else { + sendTestCommandMessage(ChreAudioConcurrencyTest.TestCommand.Step.ENABLE_AUDIO); } + boolean success = mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: ENABLE_AUDIO", success); Assert.assertTrue("Failed to enable CHRE audio", mChreAudioEnabled.get() || mTestResult.get() != null); @@ -155,11 +162,8 @@ public class ContextHubAudioConcurrencyTestExecutor extends ContextHubClientCall // Send a message to the nanoapp to verify that CHRE audio resumes mCountDownLatch = new CountDownLatch(1); sendTestCommandMessage(ChreAudioConcurrencyTest.TestCommand.Step.VERIFY_AUDIO_RESUME); - try { - mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + success = mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: VERIFY_AUDIO_RESUME", success); if (mTestResult.get() == null) { Assert.fail("No test result received"); @@ -231,4 +235,14 @@ public class ContextHubAudioConcurrencyTestExecutor extends ContextHubClientCall mCountDownLatch.countDown(); } } + + /** + * Returns whether we should verify audio gaps. This is only supported on devices + * that are currently running Android U or later and were shipped with Android U + * or later. + */ + private boolean shouldVerifyAudioGaps() { + return VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE && + VERSION.DEVICE_INITIAL_SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE; + } } diff --git a/java/test/ble_concurrency/src/com/google/android/chre/test/bleconcurrency/ContextHubBleConcurrencyTestExecutor.java b/java/test/ble_concurrency/src/com/google/android/chre/test/bleconcurrency/ContextHubBleConcurrencyTestExecutor.java index b961f64d..cd1db4d9 100644 --- a/java/test/ble_concurrency/src/com/google/android/chre/test/bleconcurrency/ContextHubBleConcurrencyTestExecutor.java +++ b/java/test/ble_concurrency/src/com/google/android/chre/test/bleconcurrency/ContextHubBleConcurrencyTestExecutor.java @@ -16,85 +16,18 @@ package com.google.android.chre.test.bleconcurrency; -import static com.google.common.truth.Truth.assertThat; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; -import android.bluetooth.le.BluetoothLeScanner; -import android.bluetooth.le.ScanCallback; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanRecord; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; import android.hardware.location.NanoAppBinary; -import com.google.android.chre.test.chqts.ContextHubChreApiTestExecutor; -import com.google.android.utils.chre.ChreApiTestUtil; -import com.google.common.collect.ImmutableList; -import com.google.protobuf.ByteString; - -import org.junit.Assert; - -import java.util.HexFormat; -import java.util.List; - -import dev.chre.rpc.proto.ChreApiTest; +import com.google.android.chre.test.chqts.ContextHubBleTestExecutor; /** * A class that can execute the CHRE BLE concurrency test. */ -public class ContextHubBleConcurrencyTestExecutor extends ContextHubChreApiTestExecutor { +public class ContextHubBleConcurrencyTestExecutor extends ContextHubBleTestExecutor { private static final String TAG = "ContextHubBleConcurrencyTestExecutor"; - /** - * The delay to report results in milliseconds. - */ - private static final int REPORT_DELAY_MS = 0; - - /** - * The RSSI threshold for the BLE scan filter. - */ - private static final int RSSI_THRESHOLD = -128; - - /** - * The advertisement type for service data. - */ - private static final int CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 = 0x16; - - /** - * CHRE BLE capabilities and filter capabilities. - */ - private static final int CHRE_BLE_CAPABILITIES_SCAN = 1 << 0; - private static final int CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA = 1 << 7; - - private BluetoothLeScanner mBluetoothLeScanner = null; - - private final ScanCallback mScanCallback = new ScanCallback() { - @Override - public void onBatchScanResults(List<ScanResult> results) { - // do nothing - } - - @Override - public void onScanFailed(int errorCode) { - Assert.fail("Failed to start a BLE scan on the host"); - } - - @Override - public void onScanResult(int callbackType, ScanResult result) { - // do nothing - } - }; - public ContextHubBleConcurrencyTestExecutor(NanoAppBinary nanoapp) { super(nanoapp); - - BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class); - assertThat(bluetoothManager).isNotNull(); - BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); - if (bluetoothAdapter != null) { - mBluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); - } } /** @@ -109,143 +42,6 @@ public class ContextHubBleConcurrencyTestExecutor extends ContextHubChreApiTestE } /** - * Generates a BLE scan filter that filters only for the known Google beacons: - * Google Eddystone and Nearby Fastpair. - */ - private static ChreApiTest.ChreBleScanFilter getDefaultScanFilter() { - ChreApiTest.ChreBleGenericFilter eddystoneFilter = - ChreApiTest.ChreBleGenericFilter.newBuilder() - .setType(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) - .setLength(2) - .setData(ByteString.copyFrom(HexFormat.of().parseHex("AAFE"))) - .setMask(ByteString.copyFrom(HexFormat.of().parseHex("FFFF"))) - .build(); - ChreApiTest.ChreBleGenericFilter nearbyFastpairFilter = - ChreApiTest.ChreBleGenericFilter.newBuilder() - .setType(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) - .setLength(2) - .setData(ByteString.copyFrom(HexFormat.of().parseHex("2CFE"))) - .setMask(ByteString.copyFrom(HexFormat.of().parseHex("FFFF"))) - .build(); - - return ChreApiTest.ChreBleScanFilter.newBuilder() - .setRssiThreshold(RSSI_THRESHOLD) - .setScanFilterCount(2) - .addScanFilters(eddystoneFilter) - .addScanFilters(nearbyFastpairFilter) - .build(); - } - - /** - * Generates a BLE scan filter that filters only for the known Google beacons: - * Google Eddystone and Nearby Fastpair. We specify the filter data in (little-endian) LE - * here as the CHRE code will take BE input and transform it to LE. - */ - private static List<ScanFilter> getDefaultScanFilterHost() { - assertThat(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) - .isEqualTo(ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT); - - ScanFilter scanFilter = new ScanFilter.Builder() - .setAdvertisingDataTypeWithData( - ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT, - ByteString.copyFrom(HexFormat.of().parseHex("AAFE")).toByteArray(), - ByteString.copyFrom(HexFormat.of().parseHex("FFFF")).toByteArray()) - .build(); - ScanFilter scanFilter2 = new ScanFilter.Builder() - .setAdvertisingDataTypeWithData( - ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT, - ByteString.copyFrom(HexFormat.of().parseHex("2CFE")).toByteArray(), - ByteString.copyFrom(HexFormat.of().parseHex("FFFF")).toByteArray()) - .build(); - - return ImmutableList.of(scanFilter, scanFilter2); - } - - /** - * Starts a BLE scan and asserts it was started successfully in a synchronous manner. - * This waits for the event to be received and returns the status in the event. - * - * @param scanFilter The scan filter. - */ - private void chreBleStartScanSync(ChreApiTest.ChreBleScanFilter scanFilter) throws Exception { - ChreApiTest.ChreBleStartScanAsyncInput.Builder inputBuilder = - ChreApiTest.ChreBleStartScanAsyncInput.newBuilder() - .setMode(ChreApiTest.ChreBleScanMode.CHRE_BLE_SCAN_MODE_FOREGROUND) - .setReportDelayMs(REPORT_DELAY_MS) - .setHasFilter(scanFilter != null); - if (scanFilter != null) { - inputBuilder.setFilter(scanFilter); - } - - ChreApiTestUtil util = new ChreApiTestUtil(); - List<ChreApiTest.GeneralSyncMessage> response = - util.callServerStreamingRpcMethodSync(getRpcClient(), - "chre.rpc.ChreApiTestService.ChreBleStartScanSync", - inputBuilder.build()); - assertThat(response).isNotEmpty(); - for (ChreApiTest.GeneralSyncMessage status: response) { - assertThat(status.getStatus()).isTrue(); - } - } - - /** - * Stops a BLE scan and asserts it was started successfully in a synchronous manner. - * This waits for the event to be received and returns the status in the event. - */ - private void chreBleStopScanSync() throws Exception { - ChreApiTestUtil util = new ChreApiTestUtil(); - List<ChreApiTest.GeneralSyncMessage> response = - util.callServerStreamingRpcMethodSync(getRpcClient(), - "chre.rpc.ChreApiTestService.ChreBleStopScanSync"); - assertThat(response).isNotEmpty(); - for (ChreApiTest.GeneralSyncMessage status: response) { - assertThat(status.getStatus()).isTrue(); - } - } - - /** - * Returns true if the required BLE capabilities and filter capabilities exist, - * otherwise returns false. - */ - private boolean doesNecessaryBleCapabilitiesExist() throws Exception { - if (mBluetoothLeScanner == null) { - return false; - } - - ChreApiTest.Capabilities capabilitiesResponse = - ChreApiTestUtil.callUnaryRpcMethodSync(getRpcClient(), - "chre.rpc.ChreApiTestService.ChreBleGetCapabilities"); - int capabilities = capabilitiesResponse.getCapabilities(); - if ((capabilities & CHRE_BLE_CAPABILITIES_SCAN) != 0) { - ChreApiTest.Capabilities filterCapabilitiesResponse = - ChreApiTestUtil.callUnaryRpcMethodSync(getRpcClient(), - "chre.rpc.ChreApiTestService.ChreBleGetFilterCapabilities"); - int filterCapabilities = filterCapabilitiesResponse.getCapabilities(); - return (filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA) != 0; - } - return false; - } - - /** - * Starts a BLE scan on the host side with known Google beacon filters. - */ - private void startBleScanOnHost() { - ScanSettings scanSettings = new ScanSettings.Builder() - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build(); - mBluetoothLeScanner.startScan(getDefaultScanFilterHost(), - scanSettings, mScanCallback); - } - - /** - * Stops a BLE scan on the host side. - */ - private void stopBleScanOnHost() { - mBluetoothLeScanner.stopScan(mScanCallback); - } - - /** * Tests with the host starting scanning first. */ private void testHostScanFirst() throws Exception { diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubBleTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubBleTestExecutor.java new file mode 100644 index 00000000..cdb0e98d --- /dev/null +++ b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubBleTestExecutor.java @@ -0,0 +1,400 @@ +/* + * 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 com.google.android.chre.test.chqts; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertisingSet; +import android.bluetooth.le.AdvertisingSetCallback; +import android.bluetooth.le.AdvertisingSetParameters; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.hardware.location.NanoAppBinary; +import android.os.ParcelUuid; + +import com.google.android.utils.chre.ChreApiTestUtil; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ByteString; + +import org.junit.Assert; + +import java.util.HexFormat; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import dev.chre.rpc.proto.ChreApiTest; + +/** + * A class that contains common BLE functionality using the CHRE API Test nanoapp. + */ +public class ContextHubBleTestExecutor extends ContextHubChreApiTestExecutor { + private static final String TAG = "ContextHubBleTestExecutor"; + + /** + * The Base UUID is used for calculating 128-bit UUIDs from "short UUIDs" (16- and 32-bit). + * + * @see {https://www.bluetooth.com/specifications/assigned-numbers/service-discovery} + */ + public static final UUID BASE_UUID = UUID.fromString("00000000-0000-1000-8000-00805F9B34FB"); + + /** + * Used for UUID conversion. This is the bit index where the 16-bit UUID is inserted. + */ + private static final int BIT_INDEX_OF_16_BIT_UUID = 32; + + /** + * The ID for the Google Eddystone beacon. + */ + public static final UUID EDDYSTONE_UUID = to128BitUuid((short) 0xFEAA); + + /** + * The delay to report results in milliseconds. + */ + private static final int REPORT_DELAY_MS = 0; + + /** + * The RSSI threshold for the BLE scan filter. + */ + private static final int RSSI_THRESHOLD = -128; + + /** + * The advertisement type for service data. + */ + public static final int CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 = 0x16; + + /** + * The BLE advertisement event ID. + */ + public static final int CHRE_EVENT_BLE_ADVERTISEMENT = 0x0350 + 1; + + /** + * CHRE BLE capabilities and filter capabilities. + */ + public static final int CHRE_BLE_CAPABILITIES_SCAN = 1 << 0; + public static final int CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA = 1 << 7; + + private BluetoothAdapter mBluetoothAdapter = null; + private BluetoothLeAdvertiser mBluetoothLeAdvertiser = null; + private BluetoothLeScanner mBluetoothLeScanner = null; + + /** + * The signal for advertising start. + */ + private CountDownLatch mAdvertisingStartLatch = new CountDownLatch(1); + + /** + * The signal for advertising stop. + */ + private CountDownLatch mAdvertisingStopLatch = new CountDownLatch(1); + + /** + * Indicates whether the device is advertising or not. + */ + private AtomicBoolean mIsAdvertising = new AtomicBoolean(); + + /** + * Callback for BLE scans. + */ + private final ScanCallback mScanCallback = new ScanCallback() { + @Override + public void onBatchScanResults(List<ScanResult> results) { + // do nothing + } + + @Override + public void onScanFailed(int errorCode) { + Assert.fail("Failed to start a BLE scan on the host"); + } + + @Override + public void onScanResult(int callbackType, ScanResult result) { + // do nothing + } + }; + + /** + * The BLE advertising callback. It updates the mIsAdvertising state + * and notifies any waiting threads that the state of advertising + * has changed. + */ + private AdvertisingSetCallback mAdvertisingSetCallback = new AdvertisingSetCallback() { + @Override + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, + int txPower, int status) { + mIsAdvertising.set(true); + mAdvertisingStartLatch.countDown(); + } + + @Override + public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) { + // do nothing + } + + @Override + public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) { + // do nothing + } + + @Override + public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) { + mIsAdvertising.set(false); + mAdvertisingStopLatch.countDown(); + } + }; + + public ContextHubBleTestExecutor(NanoAppBinary nanoapp) { + super(nanoapp); + + BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class); + assertThat(bluetoothManager).isNotNull(); + mBluetoothAdapter = bluetoothManager.getAdapter(); + if (mBluetoothAdapter != null) { + mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); + mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + } + } + + /** + * Generates a BLE scan filter that filters only for the known Google beacons: + * Google Eddystone and Nearby Fastpair. + * + * @param useEddystone if true, filter for Google Eddystone. + * @param useNearbyFastpair if true, filter for Nearby Fastpair. + */ + public static ChreApiTest.ChreBleScanFilter getDefaultScanFilter(boolean useEddystone, + boolean useNearbyFastpair) { + ChreApiTest.ChreBleScanFilter.Builder builder = + ChreApiTest.ChreBleScanFilter.newBuilder() + .setRssiThreshold(RSSI_THRESHOLD); + + if (useEddystone) { + ChreApiTest.ChreBleGenericFilter eddystoneFilter = + ChreApiTest.ChreBleGenericFilter.newBuilder() + .setType(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) + .setLength(2) + .setData(ByteString.copyFrom(HexFormat.of().parseHex("AAFE"))) + .setMask(ByteString.copyFrom(HexFormat.of().parseHex("FFFF"))) + .build(); + builder = builder.addScanFilters(eddystoneFilter); + } + + if (useNearbyFastpair) { + ChreApiTest.ChreBleGenericFilter nearbyFastpairFilter = + ChreApiTest.ChreBleGenericFilter.newBuilder() + .setType(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) + .setLength(2) + .setData(ByteString.copyFrom(HexFormat.of().parseHex("2CFE"))) + .setMask(ByteString.copyFrom(HexFormat.of().parseHex("FFFF"))) + .build(); + builder = builder.addScanFilters(nearbyFastpairFilter); + } + + return builder.build(); + } + + /** + * Generates a BLE scan filter that filters only for the known Google beacons: + * Google Eddystone and Nearby Fastpair. + */ + public static ChreApiTest.ChreBleScanFilter getDefaultScanFilter() { + return getDefaultScanFilter(true /* useEddystone */, true /* useNearbyFastpair */); + } + + /** + * Generates a BLE scan filter that filters only for Google Eddystone. + */ + public static ChreApiTest.ChreBleScanFilter getGoogleEddystoneScanFilter() { + return getDefaultScanFilter(true /* useEddystone */, false /* useNearbyFastpair */); + } + + /** + * Generates a BLE scan filter that filters only for the known Google beacons: + * Google Eddystone and Nearby Fastpair. We specify the filter data in (little-endian) LE + * here as the CHRE code will take BE input and transform it to LE. + */ + public static List<ScanFilter> getDefaultScanFilterHost() { + assertThat(CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16) + .isEqualTo(ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT); + + ScanFilter scanFilter = new ScanFilter.Builder() + .setAdvertisingDataTypeWithData( + ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT, + ByteString.copyFrom(HexFormat.of().parseHex("AAFE")).toByteArray(), + ByteString.copyFrom(HexFormat.of().parseHex("FFFF")).toByteArray()) + .build(); + ScanFilter scanFilter2 = new ScanFilter.Builder() + .setAdvertisingDataTypeWithData( + ScanRecord.DATA_TYPE_SERVICE_DATA_16_BIT, + ByteString.copyFrom(HexFormat.of().parseHex("2CFE")).toByteArray(), + ByteString.copyFrom(HexFormat.of().parseHex("FFFF")).toByteArray()) + .build(); + + return ImmutableList.of(scanFilter, scanFilter2); + } + + /** + * Starts a BLE scan and asserts it was started successfully in a synchronous manner. + * This waits for the event to be received and returns the status in the event. + * + * @param scanFilter The scan filter. + */ + public void chreBleStartScanSync(ChreApiTest.ChreBleScanFilter scanFilter) throws Exception { + ChreApiTest.ChreBleStartScanAsyncInput.Builder inputBuilder = + ChreApiTest.ChreBleStartScanAsyncInput.newBuilder() + .setMode(ChreApiTest.ChreBleScanMode.CHRE_BLE_SCAN_MODE_FOREGROUND) + .setReportDelayMs(REPORT_DELAY_MS) + .setHasFilter(scanFilter != null); + if (scanFilter != null) { + inputBuilder.setFilter(scanFilter); + } + + ChreApiTestUtil util = new ChreApiTestUtil(); + List<ChreApiTest.GeneralSyncMessage> response = + util.callServerStreamingRpcMethodSync(getRpcClient(), + "chre.rpc.ChreApiTestService.ChreBleStartScanSync", + inputBuilder.build()); + assertThat(response).isNotEmpty(); + for (ChreApiTest.GeneralSyncMessage status: response) { + assertThat(status.getStatus()).isTrue(); + } + } + + /** + * Stops a BLE scan and asserts it was started successfully in a synchronous manner. + * This waits for the event to be received and returns the status in the event. + */ + public void chreBleStopScanSync() throws Exception { + ChreApiTestUtil util = new ChreApiTestUtil(); + List<ChreApiTest.GeneralSyncMessage> response = + util.callServerStreamingRpcMethodSync(getRpcClient(), + "chre.rpc.ChreApiTestService.ChreBleStopScanSync"); + assertThat(response).isNotEmpty(); + for (ChreApiTest.GeneralSyncMessage status: response) { + assertThat(status.getStatus()).isTrue(); + } + } + + /** + * Returns true if the required BLE capabilities and filter capabilities exist, + * otherwise returns false. + */ + public boolean doesNecessaryBleCapabilitiesExist() throws Exception { + if (mBluetoothLeScanner == null) { + return false; + } + + ChreApiTest.Capabilities capabilitiesResponse = + ChreApiTestUtil.callUnaryRpcMethodSync(getRpcClient(), + "chre.rpc.ChreApiTestService.ChreBleGetCapabilities"); + int capabilities = capabilitiesResponse.getCapabilities(); + if ((capabilities & CHRE_BLE_CAPABILITIES_SCAN) != 0) { + ChreApiTest.Capabilities filterCapabilitiesResponse = + ChreApiTestUtil.callUnaryRpcMethodSync(getRpcClient(), + "chre.rpc.ChreApiTestService.ChreBleGetFilterCapabilities"); + int filterCapabilities = filterCapabilitiesResponse.getCapabilities(); + return (filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA) != 0; + } + return false; + } + + /** + * Returns true if the required BLE capabilities and filter capabilities exist + * on the AP, otherwise returns false. + */ + public boolean doesNecessaryBleCapabilitiesExistOnTheAP() throws Exception { + return mBluetoothLeAdvertiser != null; + } + + /** + * Starts a BLE scan on the host side with known Google beacon filters. + */ + public void startBleScanOnHost() { + ScanSettings scanSettings = new ScanSettings.Builder() + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build(); + mBluetoothLeScanner.startScan(getDefaultScanFilterHost(), + scanSettings, mScanCallback); + } + + /** + * Stops a BLE scan on the host side. + */ + public void stopBleScanOnHost() { + mBluetoothLeScanner.stopScan(mScanCallback); + } + + /** + * Starts broadcasting the Google Eddystone beacon from the AP. + */ + public void startBleAdvertisingGoogleEddystone() throws InterruptedException { + if (mIsAdvertising.get()) { + return; + } + + AdvertisingSetParameters parameters = (new AdvertisingSetParameters.Builder()) + .setLegacyMode(true) + .setConnectable(false) + .setInterval(AdvertisingSetParameters.INTERVAL_HIGH) + .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_HIGH) + .build(); + + AdvertiseData data = new AdvertiseData.Builder() + .addServiceData(new ParcelUuid(EDDYSTONE_UUID), new byte[] {0}) + .setIncludeDeviceName(false) + .setIncludeTxPowerLevel(true) + .build(); + + mBluetoothLeAdvertiser.startAdvertisingSet(parameters, data, + null, null, null, mAdvertisingSetCallback); + mAdvertisingStartLatch.await(); + assertThat(mIsAdvertising.get()).isTrue(); + } + + /** + * Stops advertising Google Eddystone from the AP. + */ + public void stopBleAdvertisingGoogleEddystone() throws InterruptedException { + if (!mIsAdvertising.get()) { + return; + } + + mBluetoothLeAdvertiser.stopAdvertisingSet(mAdvertisingSetCallback); + mAdvertisingStopLatch.await(); + assertThat(mIsAdvertising.get()).isFalse(); + } + + /** + * Converts short UUID to 128 bit UUID. + */ + private static UUID to128BitUuid(short shortUuid) { + return new UUID( + ((shortUuid & 0xFFFFL) << BIT_INDEX_OF_16_BIT_UUID) + | BASE_UUID.getMostSignificantBits(), + BASE_UUID.getLeastSignificantBits()); + } +} diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubChreApiTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubChreApiTestExecutor.java index 28b363f0..b3c23366 100644 --- a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubChreApiTestExecutor.java +++ b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubChreApiTestExecutor.java @@ -42,7 +42,6 @@ import dev.pigweed.pw_rpc.Service; */ public class ContextHubChreApiTestExecutor extends ContextHubClientCallback { private final List<NanoAppBinary> mNanoAppBinaries; - private final List<Long> mNanoAppIds = new ArrayList<Long>(); private final ContextHubClient mContextHubClient; private final AtomicBoolean mChreReset = new AtomicBoolean(false); protected final Context mContext = InstrumentationRegistry.getTargetContext(); @@ -64,7 +63,6 @@ public class ContextHubChreApiTestExecutor extends ContextHubClientCallback { mContextHubClient = mContextHubManager.createClient(mContextHub, this); for (NanoAppBinary nanoapp: nanoapps) { - mNanoAppIds.add(nanoapp.getNanoAppId()); Service chreApiService = ChreApiTestUtil.getChreApiService(); mRpcClients.add(new ChreRpcClient( mContextHubManager, mContextHub, nanoapp.getNanoAppId(), @@ -79,7 +77,6 @@ public class ContextHubChreApiTestExecutor extends ContextHubClientCallback { /** Should be invoked before run() is invoked to set up the test, e.g. in a @Before method. */ public void init() { - mContextHubManager.enableTestMode(); for (NanoAppBinary nanoapp: mNanoAppBinaries) { ChreTestUtil.loadNanoAppAssertSuccess(mContextHubManager, mContextHub, nanoapp); } @@ -91,10 +88,10 @@ public class ContextHubChreApiTestExecutor extends ContextHubClientCallback { Assert.fail("CHRE reset during the test"); } - for (Long nanoappId: mNanoAppIds) { - ChreTestUtil.unloadNanoAppAssertSuccess(mContextHubManager, mContextHub, nanoappId); + for (NanoAppBinary nanoapp: mNanoAppBinaries) { + ChreTestUtil.unloadNanoAppAssertSuccess(mContextHubManager, mContextHub, + nanoapp.getNanoAppId()); } - mContextHubManager.disableTestMode(); mContextHubClient.close(); } diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubGeneralTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubGeneralTestExecutor.java index 10680f2c..355d0ced 100644 --- a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubGeneralTestExecutor.java +++ b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubGeneralTestExecutor.java @@ -15,6 +15,12 @@ */ package com.google.android.chre.test.chqts; +import android.app.Instrumentation; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.location.ContextHubClient; import android.hardware.location.ContextHubClientCallback; import android.hardware.location.ContextHubInfo; @@ -25,6 +31,7 @@ import android.hardware.location.NanoAppMessage; import android.util.Log; import com.google.android.utils.chre.ChreTestUtil; +import com.google.android.utils.chre.SettingsUtil; import org.junit.Assert; @@ -74,6 +81,28 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall private long mThreadId; + private final Instrumentation mInstrumentation = + androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(); + + private final Context mContext = mInstrumentation.getTargetContext(); + + private final SettingsUtil mSettingsUtil; + + private boolean mInitialBluetoothEnabled = false; + + public static class BluetoothUpdateListener { + public CountDownLatch mBluetoothLatch = new CountDownLatch(1); + + public BroadcastReceiver mBluetoothUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { + mBluetoothLatch.countDown(); + } + } + }; + } + /** * A container class to describe a general_test nanoapp. */ @@ -145,6 +174,7 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall for (GeneralTestNanoApp test : mGeneralTestNanoAppList) { mNanoAppIdSet.add(test.getNanoAppBinary().getNanoAppId()); } + mSettingsUtil = new SettingsUtil(mContext); } @Override @@ -200,6 +230,9 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall mContextHubClient = mContextHubManager.createClient(mContextHubInfo, this); for (GeneralTestNanoApp test : mGeneralTestNanoAppList) { + if (test.getTestName() == ContextHubTestConstants.TestNames.BASIC_BLE_TEST) { + handleBleTestSetup(); + } if (test.loadAtInit()) { ChreTestUtil.loadNanoAppAssertSuccess(mContextHubManager, mContextHubInfo, test.getNanoAppBinary()); @@ -209,10 +242,33 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall mErrorString.set(null); } + private void handleBleTestSetup() { + mInitialBluetoothEnabled = mSettingsUtil.isBluetoothEnabled(); + Log.i(TAG, "Initial bluetooth setting enabled: " + mInitialBluetoothEnabled); + if (mInitialBluetoothEnabled) { + return; + } + BluetoothUpdateListener bluetoothUpdateListener = new BluetoothUpdateListener(); + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(bluetoothUpdateListener.mBluetoothUpdateReceiver, filter); + mSettingsUtil.setBluetooth(true /* enable */); + try { + bluetoothUpdateListener.mBluetoothLatch.await(10, TimeUnit.SECONDS); + Assert.assertTrue(mSettingsUtil.isBluetoothEnabled()); + Log.i(TAG, "Bluetooth enabled successfully"); + // Wait a few seconds to ensure setting is propagated to CHRE path + // TODO(b/302018530): Remove Thread.sleep calls for CHRE settings propagation + Thread.sleep(2000); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + } + /** * Run the test. */ - public void run(long timeoutSeconds) { + public void run(long timeoutSeconds) throws InterruptedException { mThreadId = Thread.currentThread().getId(); for (GeneralTestNanoApp test : mGeneralTestNanoAppList) { @@ -222,13 +278,7 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall } } - boolean success = false; - try { - success = mCountDownLatch.await(timeoutSeconds, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } - + boolean success = mCountDownLatch.await(timeoutSeconds, TimeUnit.SECONDS); Assert.assertTrue("Test timed out", success); } @@ -248,6 +298,10 @@ public abstract class ContextHubGeneralTestExecutor extends ContextHubClientCall // TODO: If the nanoapp aborted (i.e. test failed), wait for CHRE reset or nanoapp abort // callback, and otherwise assert unload success. for (GeneralTestNanoApp test : mGeneralTestNanoAppList) { + if (test.getTestName() == ContextHubTestConstants.TestNames.BASIC_BLE_TEST + && !mInitialBluetoothEnabled) { + mSettingsUtil.setBluetooth(mInitialBluetoothEnabled); + } ChreTestUtil.unloadNanoApp(mContextHubManager, mContextHubInfo, test.getNanoAppBinary().getNanoAppId()); } diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubHostEndpointInfoTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubHostEndpointInfoTestExecutor.java index 7290ab89..2843cb4d 100644 --- a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubHostEndpointInfoTestExecutor.java +++ b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubHostEndpointInfoTestExecutor.java @@ -19,30 +19,43 @@ package com.google.android.chre.test.chqts; import android.hardware.location.ContextHubClient; import android.hardware.location.NanoAppBinary; +import androidx.annotation.NonNull; + import com.google.android.utils.chre.ChreApiTestUtil; import org.junit.Assert; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import dev.chre.rpc.proto.ChreApiTest; /** * Test to ensure CHRE HostEndpoint related API works as expected. */ public class ContextHubHostEndpointInfoTestExecutor extends ContextHubChreApiTestExecutor { + private static final int HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT = 0; + private static final int CHRE_HOST_ENDPOINT_NOTIFICATION_EVENT = 0x0008; + public ContextHubHostEndpointInfoTestExecutor(NanoAppBinary nanoapp) { super(nanoapp); } /** - * Validates if the host endpoint info stored in ChreApiTest Nanoapp is as expected. + * Validates if the host endpoint info stored in ChreApiTest Nanoapp is as + * expected. * - * @param id the host endpoint id of the host endpoint. - * @param success true if we are expecting to retrieve host endpoint info by this id, otherwise - * false. + * @param id the host endpoint id of the host endpoint. + * @param success true if we are expecting to retrieve host endpoint info by + * this id, otherwise false. */ private void validateHostEndpointInfoById(int id, boolean success) throws Exception { ChreApiTest.ChreGetHostEndpointInfoInput input = - ChreApiTest.ChreGetHostEndpointInfoInput.newBuilder().setHostEndpointId(id).build(); + ChreApiTest.ChreGetHostEndpointInfoInput.newBuilder() + .setHostEndpointId(id) + .build(); ChreApiTest.ChreGetHostEndpointInfoOutput response = ChreApiTestUtil.callUnaryRpcMethodSync( getRpcClient(), "chre.rpc.ChreApiTestService.ChreGetHostEndpointInfo", @@ -84,25 +97,31 @@ public class ContextHubHostEndpointInfoTestExecutor extends ContextHubChreApiTes * Validates if the nanoapp received a proper host endpoint notification disconnection event. * * @param id host endpoint id for the most recent disconnected host endpoint. + * @param events host endpoint notification events received by the nanoapp. */ - private void validateLatestHostEndpointNotification(int id) throws Exception { - // TODO(b/274791978): Deprecate this once we can capture event in test mode. - ChreApiTest.RetrieveLatestDisconnectedHostEndpointEventOutput response = - ChreApiTestUtil.callUnaryRpcMethodSync( - getRpcClient(), - "chre.rpc.ChreApiTestService.RetrieveLatestDisconnectedHostEndpointEvent"); + private void validateChreHostEndpointNotification(int id, + @NonNull List<ChreApiTest.GeneralEventsMessage> events) throws Exception { + Objects.requireNonNull(events); Assert.assertEquals( "Should have exactly receive 1 host endpoint notification", - 1, - response.getDisconnectedCount()); - Assert.assertEquals("Host endpoint Id mismatch", response.getHostEndpointId(), id); + 1, events.size()); + ChreApiTest.ChreHostEndpointNotification hostEndpointNotification = + events.get(0).getChreHostEndpointNotification(); + Assert.assertTrue(events.get(0).getStatus()); + Assert.assertEquals("Host endpoint Id mismatch", id, + hostEndpointNotification.getHostEndpointId()); + + Assert.assertEquals("Not receive HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT", + HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT, + hostEndpointNotification.getNotificationType()); } /** * Asks the test nanoapp to configure host endpoint notification. * * @param hostEndpointId which host endpoint to get notification. - * @param enable true to enable host endpoint notification, false to disable. + * @param enable true to enable host endpoint notification, false to + * disable. */ private void configureHostEndpointNotification(int hostEndpointId, boolean enable) throws Exception { @@ -127,8 +146,15 @@ public class ContextHubHostEndpointInfoTestExecutor extends ContextHubChreApiTes ContextHubClient client = mContextHubManager.createClient(mContextHub, this); int clientHostEndpointId = client.getId(); configureHostEndpointNotification(clientHostEndpointId, true); + Future<List<ChreApiTest.GeneralEventsMessage>> eventsFuture = new ChreApiTestUtil() + .gatherEvents(mRpcClients.get(0), + CHRE_HOST_ENDPOINT_NOTIFICATION_EVENT, + /* eventCount= */ 1, + ChreApiTestUtil.RPC_TIMEOUT_IN_NS); client.close(); - Thread.sleep(1000); - validateLatestHostEndpointNotification(clientHostEndpointId); + List<ChreApiTest.GeneralEventsMessage> events = + eventsFuture.get(2 * ChreApiTestUtil.RPC_TIMEOUT_IN_NS, + TimeUnit.NANOSECONDS); + validateChreHostEndpointNotification(clientHostEndpointId, events); } } diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/multidevice/ContextHubMultiDeviceBleBeaconTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/multidevice/ContextHubMultiDeviceBleBeaconTestExecutor.java new file mode 100644 index 00000000..19f04915 --- /dev/null +++ b/java/test/chqts/src/com/google/android/chre/test/chqts/multidevice/ContextHubMultiDeviceBleBeaconTestExecutor.java @@ -0,0 +1,108 @@ +/* + * 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 com.google.android.chre.test.chqts.multidevice; + +import android.hardware.location.NanoAppBinary; + +import com.google.android.chre.test.chqts.ContextHubBleTestExecutor; +import com.google.android.utils.chre.ChreApiTestUtil; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Future; + +import dev.chre.rpc.proto.ChreApiTest; + +public class ContextHubMultiDeviceBleBeaconTestExecutor extends ContextHubBleTestExecutor { + private static final int NUM_EVENTS_TO_GATHER = 10; + + private static final long TIMEOUT_IN_S = 30; + + private static final long TIMEOUT_IN_NS = TIMEOUT_IN_S * 1000000000L; + + public ContextHubMultiDeviceBleBeaconTestExecutor(NanoAppBinary nanoapp) { + super(nanoapp); + } + + /** + * Gathers BLE advertisement events from the nanoapp for TIMEOUT_IN_NS or up to + * NUM_EVENTS_TO_GATHER events. This function returns true if all + * chreBleAdvertisingReport's contain advertisments for Google Eddystone and + * there is at least one advertisement, otherwise it returns false. + */ + public boolean gatherAndVerifyChreBleAdvertisementsForGoogleEddystone() throws Exception { + Future<List<ChreApiTest.GeneralEventsMessage>> eventsFuture = + new ChreApiTestUtil().gatherEvents( + mRpcClients.get(0), + Arrays.asList(CHRE_EVENT_BLE_ADVERTISEMENT), + NUM_EVENTS_TO_GATHER, + TIMEOUT_IN_NS); + + List<ChreApiTest.GeneralEventsMessage> events = eventsFuture.get(); + if (events == null) { + return false; + } + + boolean foundGoogleEddystoneBleAdvertisement = false; + for (ChreApiTest.GeneralEventsMessage event: events) { + if (!event.hasChreBleAdvertisementEvent()) { + continue; + } + + ChreApiTest.ChreBleAdvertisementEvent bleAdvertisementEvent = + event.getChreBleAdvertisementEvent(); + for (int i = 0; i < bleAdvertisementEvent.getReportsCount(); ++i) { + ChreApiTest.ChreBleAdvertisingReport report = bleAdvertisementEvent.getReports(i); + byte[] data = report.getData().toByteArray(); + if (data == null || data.length < 2) { + continue; + } + + if (!searchForGoogleEddystoneAdvertisement(data)) { + return false; + } + foundGoogleEddystoneBleAdvertisement = true; + } + } + return foundGoogleEddystoneBleAdvertisement; + } + + /** + * Starts a BLE scan with the Google Eddystone filter. + */ + public void chreBleStartScanSyncWithGoogleEddystoneFilter() throws Exception { + chreBleStartScanSync(getGoogleEddystoneScanFilter()); + } + + /** + * Returns true if the data contains an advertisement for Google Eddystone, + * otherwise returns false. + */ + private boolean searchForGoogleEddystoneAdvertisement(byte[] data) { + if (data.length < 2) { + return false; + } + + for (int j = 0; j < data.length - 1; ++j) { + if (Byte.compare(data[j], (byte) 0xAA) == 0 + && Byte.compare(data[j + 1], (byte) 0xFE) == 0) { + return true; + } + } + return false; + } +} diff --git a/java/test/chre_concurrency/src/com/google/android/chre/test/chreconcurrency/ContextHubChreConcurrencyTestExecutor.java b/java/test/chre_concurrency/src/com/google/android/chre/test/chreconcurrency/ContextHubChreConcurrencyTestExecutor.java index 46101794..81460af7 100644 --- a/java/test/chre_concurrency/src/com/google/android/chre/test/chreconcurrency/ContextHubChreConcurrencyTestExecutor.java +++ b/java/test/chre_concurrency/src/com/google/android/chre/test/chreconcurrency/ContextHubChreConcurrencyTestExecutor.java @@ -56,12 +56,18 @@ public class ContextHubChreConcurrencyTestExecutor extends ContextHubChreApiTest private static final long TIMEOUT_IN_NS = 5000000000L; /** - * The multiplier used to allow for jitter in the timestamps of the samples. This is + * The multiplier used to allow for jitter in the intervals of the samples. This is * multiplied against the requested interval between samples to produce the final interval * that is compared with the real interval between samples. This allows for 5% of jitter. */ private static final double JITTER_MULTIPLIER = 1.05; + /** + * The sum of the intervals in ns and timestamp count. Used to calculate the average interval. + */ + private double mIntervalSumNs = 0; + private int mTimestampCount = 0; + public ContextHubChreConcurrencyTestExecutor(NanoAppBinary nanoapp, NanoAppBinary nanoapp2) { super(Arrays.asList(nanoapp, nanoapp2)); } @@ -203,19 +209,30 @@ public class ContextHubChreConcurrencyTestExecutor extends ContextHubChreApiTest List<ChreApiTest.GeneralEventsMessage> events, long requestedIntervalNs) { assertThat(events).isNotNull(); + Long lastReadingTimestamp = null; + mIntervalSumNs = 0; + mTimestampCount = 0; for (ChreApiTest.GeneralEventsMessage event: events) { Assert.assertTrue(event.getStatus()); if (event.hasChreSensorThreeAxisData()) { lastReadingTimestamp = handleChreSensorThreeAxisData(event, requestedIntervalNs, lastReadingTimestamp, accelerometerHandle); } else if (event.hasChreSensorSamplingStatusEvent()) { + // The interval will change due to the sampling status event. + // Assert the past data was good and continue with fresh data. + assertAverageIntervalIsLessThanOrEqualToTheRequestedInterval(requestedIntervalNs); + + mIntervalSumNs = 0; + mTimestampCount = 0; requestedIntervalNs = handleChreSensorSamplingStatusEvent(event, requestedIntervalNs, accelerometerHandle); } else { Assert.fail("Event does not contain any requested data."); } } + + assertAverageIntervalIsLessThanOrEqualToTheRequestedInterval(requestedIntervalNs); } /** @@ -231,8 +248,8 @@ public class ContextHubChreConcurrencyTestExecutor extends ContextHubChreApiTest long requestedIntervalNs, Long lastReadingTimestamp, int accelerometerHandle) { ChreApiTest.ChreSensorThreeAxisData data = event.getChreSensorThreeAxisData(); Assert.assertEquals(data.getHeader().getSensorHandle(), accelerometerHandle); - assertThat(data.getReadingsCount()).isGreaterThan(0); + for (int i = 0; i < data.getReadingsCount(); ++i) { ChreApiTest.ChreSensorThreeAxisSampleData sampleData = data.getReadings(i); @@ -243,11 +260,8 @@ public class ContextHubChreConcurrencyTestExecutor extends ContextHubChreApiTest ? data.getHeader().getBaseTimestamp() : lastReadingTimestamp) + sampleData.getTimestampDelta(); if (lastReadingTimestamp != null) { - Assert.assertTrue("Invalid timestamp between samples: interval: " - + (readingTimestamp - lastReadingTimestamp) - + ", requestedIntervalNs: " + requestedIntervalNs, - readingTimestamp <= requestedIntervalNs * JITTER_MULTIPLIER - + lastReadingTimestamp); + mIntervalSumNs += readingTimestamp - lastReadingTimestamp; + ++mTimestampCount; } lastReadingTimestamp = readingTimestamp; } @@ -271,4 +285,22 @@ public class ContextHubChreConcurrencyTestExecutor extends ContextHubChreApiTest ChreApiTest.ChreSensorSamplingStatus samplingStatus = samplingStatusEvent.getStatus(); return samplingStatus.getEnabled() ? samplingStatus.getInterval() : requestedIntervalNs; } + + /** + * Asserts that the average interval is less than the requested interval (both in ns), + * accounting for jitter. + * + * @param requestedIntervalNs the requested interval in ns. + */ + private void assertAverageIntervalIsLessThanOrEqualToTheRequestedInterval( + long requestedIntervalNs) { + if (mTimestampCount <= 0) { + return; + } + + double averageIntervalNs = mIntervalSumNs / mTimestampCount; + Assert.assertTrue("Invalid average timestamp between samples: averageIntervalNs: " + + averageIntervalNs + ", requestedIntervalNs: " + requestedIntervalNs, + averageIntervalNs <= requestedIntervalNs * JITTER_MULTIPLIER); + } } diff --git a/java/test/cross_validation/Android.bp b/java/test/cross_validation/Android.bp index e56416cf..0d3a2d1e 100644 --- a/java/test/cross_validation/Android.bp +++ b/java/test/cross_validation/Android.bp @@ -35,5 +35,5 @@ java_library { "guava", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java index 5028b9bf..c5641274 100644 --- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java +++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java @@ -24,6 +24,8 @@ import android.hardware.location.ContextHubTransaction; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.util.Log; import com.google.android.utils.chre.ChreTestUtil; @@ -101,7 +103,7 @@ abstract class ChreCrossValidatorBase { * @param samplingDurationInMs The amount of time in milliseconds to collect samples from AP and * CHRE. */ - public abstract void validate() throws AssertionError; + public abstract void validate() throws AssertionError, InterruptedException; /** * Clean up resources allocated for cross validation. Subclasses should override this method and @@ -122,6 +124,13 @@ abstract class ChreCrossValidatorBase { * with data received. */ private void unloadAllNanoApps() { + // We only need to unload all nanoapps when the device has version < U, so the + // tests remain the same on those devices. On newer devices, test mode will + // handle this. + if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) { + return; + } + List<NanoAppState> nanoAppStateList = ChreTestUtil.queryNanoAppsAssertSuccess(mContextHubManager, mContextHubInfo); diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java index d53f4080..f6a43214 100644 --- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java +++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java @@ -150,7 +150,7 @@ public class ChreCrossValidatorSensor } @Override - public void validate() throws AssertionError { + public void validate() throws AssertionError, InterruptedException { HashSet<Integer> testedSensorIndices = new HashSet<>(); for (int i = 0; i < mSensorList.size(); i++) { mApDatapointsQueue.clear(); @@ -453,12 +453,9 @@ public class ChreCrossValidatorSensor * @param samplingDurationInMs The amount of time to wait for AP and CHRE to collected data in * ms. */ - private void waitForDataSampling() throws AssertionError { - try { - mAwaitDataLatch.await(getAwaitDataTimeoutInMs(), TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Assert.fail("await data latch interrupted"); - } + private void waitForDataSampling() throws AssertionError, InterruptedException { + mAwaitDataLatch.await(getAwaitDataTimeoutInMs(), TimeUnit.MILLISECONDS); + if (mErrorStr.get() != null) { Assert.fail(mErrorStr.get()); } @@ -543,21 +540,15 @@ public class ChreCrossValidatorSensor /** * Verify the CHRE sensor being evaluated is present on this device. */ - private void verifyChreSensorIsPresent() { + private void verifyChreSensorIsPresent() throws InterruptedException { mCollectingData.set(true); sendMessageToNanoApp(makeInfoCommandMessage()); waitForInfoResponse(); mCollectingData.set(false); } - private void waitForInfoResponse() { - boolean success = false; - try { - success = mAwaitDataLatch.await(INFO_RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Assert.fail("await data latch interrupted"); - } - + private void waitForInfoResponse() throws InterruptedException { + boolean success = mAwaitDataLatch.await(INFO_RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (!success) { Assert.fail("Timed out waiting for sensor info response"); } diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java index 812fe3a0..e9f9f92c 100644 --- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java +++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java @@ -41,6 +41,7 @@ import org.junit.Assert; import org.junit.Assume; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -104,7 +105,7 @@ public class ChreCrossValidatorWifi extends ChreCrossValidatorBase { context.registerReceiver(mWifiScanReceiver, intentFilter); } - @Override public void validate() throws AssertionError { + @Override public void validate() throws AssertionError, InterruptedException { mCollectingData.set(true); sendStepStartMessage(Step.CAPABILITIES); waitForMessageFromNanoapp(); @@ -183,12 +184,10 @@ public class ChreCrossValidatorWifi extends ChreCrossValidatorBase { /** * Wait for a messaage from the nanoapp. */ - private void waitForMessageFromNanoapp() { - try { - mAwaitDataLatch.await(AWAIT_STEP_RESULT_MESSAGE_TIMEOUT_SEC, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail("Interrupted while awaiting " + getCurrentStepName() + " step"); - } + private void waitForMessageFromNanoapp() throws InterruptedException { + boolean success = + mAwaitDataLatch.await(AWAIT_STEP_RESULT_MESSAGE_TIMEOUT_SEC, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: wait for message from nanoapp", success); mAwaitDataLatch = new CountDownLatch(1); Assert.assertTrue("Timed out while waiting for step result in " + getCurrentStepName() + " step", mDidReceiveNanoAppMessage.get()); @@ -207,18 +206,31 @@ public class ChreCrossValidatorWifi extends ChreCrossValidatorBase { && (capabilities.getWifiCapabilities() & WIFI_CAPABILITIES_ON_DEMAND_SCAN) != 0; } - private void waitForApScanResults() { - try { - mAwaitApWifiSetupScan.await(AWAIT_WIFI_SCAN_RESULT_TIMEOUT_SEC, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail("Interrupted while awaiting ap wifi scan result"); - } + private void waitForApScanResults() throws InterruptedException { + boolean success = + mAwaitApWifiSetupScan.await(AWAIT_WIFI_SCAN_RESULT_TIMEOUT_SEC, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: wait for ap scan results", success); Assert.assertTrue("AP wifi scan result failed asynchronously", mApWifiScanSuccess.get()); } private void sendWifiScanResultsToChre() { List<ScanResult> results = mWifiManager.getScanResults(); Assert.assertTrue("No wifi scan results returned from AP", !results.isEmpty()); + + // CHRE does not currently support 6 GHz results, so filter these results from the list + int logsRemoved = 0; + Iterator<ScanResult> iter = results.iterator(); + while (iter.hasNext()) { + ScanResult current = iter.next(); + if (current.getBand() == ScanResult.WIFI_BAND_6_GHZ) { + iter.remove(); + logsRemoved++; + } + } + if (logsRemoved > 0) { + Log.i(TAG, "Filtering out 6 GHz band scan result for CHRE, total=" + logsRemoved); + } + for (int i = 0; i < results.size(); i++) { sendMessageToNanoApp(makeWifiScanResultMessage(results.get(i), results.size(), i)); } diff --git a/java/test/permissions/Android.bp b/java/test/permissions/Android.bp index 6dd10bfc..c6860814 100644 --- a/java/test/permissions/Android.bp +++ b/java/test/permissions/Android.bp @@ -34,5 +34,5 @@ java_library { "chre-test-utils", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/rpc_service/Android.bp b/java/test/rpc_service/Android.bp index 2403d7a9..c848681e 100644 --- a/java/test/rpc_service/Android.bp +++ b/java/test/rpc_service/Android.bp @@ -35,5 +35,5 @@ java_library { "pw_rpc_java_client", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/rpc_service/src/com/google/android/chre/test/rpc_service/ContextHubRpcServiceTestExecutor.java b/java/test/rpc_service/src/com/google/android/chre/test/rpc_service/ContextHubRpcServiceTestExecutor.java index b6da0f1b..78b1a1aa 100644 --- a/java/test/rpc_service/src/com/google/android/chre/test/rpc_service/ContextHubRpcServiceTestExecutor.java +++ b/java/test/rpc_service/src/com/google/android/chre/test/rpc_service/ContextHubRpcServiceTestExecutor.java @@ -129,7 +129,6 @@ public class ContextHubRpcServiceTestExecutor extends ContextHubClientCallback { }; IntentFilter filter = new IntentFilter(ACTION); - mContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); Intent intent = new Intent(ACTION).setPackage(mContext.getPackageName()); PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); @@ -138,6 +137,7 @@ public class ContextHubRpcServiceTestExecutor extends ContextHubClientCallback { pendingIntent, mNanoAppId); mRpcClient = new ChreRpcClient(contextHubClient, mNanoAppId, List.of(mEchoService)); + mContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); invokeRpc(mRpcClient); diff --git a/java/test/settings/Android.bp b/java/test/settings/Android.bp index f26ff7f2..15976d21 100644 --- a/java/test/settings/Android.bp +++ b/java/test/settings/Android.bp @@ -32,5 +32,5 @@ java_library { "chre-test-utils", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubBleSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubBleSettingsTestExecutor.java index 4ada672e..e91785dd 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubBleSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubBleSettingsTestExecutor.java @@ -16,8 +16,6 @@ package com.google.android.chre.test.setting; -import static android.Manifest.permission.BLUETOOTH_CONNECT; - import android.app.Instrumentation; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; @@ -88,7 +86,7 @@ public class ContextHubBleSettingsTestExecutor { /** * Should be called in a @Before method. */ - public void setUp() { + public void setUp() throws InterruptedException { mInitialBluetoothEnabled = mSettingsUtil.isBluetoothEnabled(); mInitialBluetoothScanningEnabled = mSettingsUtil.isBluetoothScanningAlwaysEnabled(); mInitialAirplaneMode = mSettingsUtil.isAirplaneModeOn(); @@ -99,7 +97,7 @@ public class ContextHubBleSettingsTestExecutor { mExecutor.init(); } - public void runBleScanningTest() { + public void runBleScanningTest() throws InterruptedException { runBleScanningTest(false /* enableBluetooth */, false /* enableBluetoothScanning */); runBleScanningTest(true /* enableBluetooth */, false /* enableBluetoothScanning */); runBleScanningTest(false /* enableBluetooth */, true /* enableBluetoothScanning */); @@ -109,7 +107,7 @@ public class ContextHubBleSettingsTestExecutor { /** * Should be called in an @After method. */ - public void tearDown() { + public void tearDown() throws InterruptedException { mExecutor.deinit(); mSettingsUtil.setBluetooth(mInitialBluetoothEnabled); mSettingsUtil.setBluetoothScanningSettings(mInitialBluetoothScanningEnabled); @@ -122,6 +120,12 @@ public class ContextHubBleSettingsTestExecutor { * @param enableBluetoothScanning if true, enable BLE scanning; false, otherwise */ private void setBluetoothSettings(boolean enable, boolean enableBluetoothScanning) { + // Check if already in the desired state + if ((enable == mSettingsUtil.isBluetoothEnabled()) + && (enableBluetoothScanning == mSettingsUtil.isBluetoothScanningAlwaysEnabled())) { + return; + } + int state = BluetoothAdapter.STATE_OFF; if (enable) { state = BluetoothAdapter.STATE_ON; @@ -142,11 +146,11 @@ public class ContextHubBleSettingsTestExecutor { Assert.assertTrue(bluetoothManager != null); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); Assert.assertTrue(bluetoothAdapter != null); - mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(BLUETOOTH_CONNECT); Assert.assertTrue(bluetoothAdapter.enableBLE()); } try { - bluetoothUpdateListener.mBluetoothLatch.await(10, TimeUnit.SECONDS); + boolean success = bluetoothUpdateListener.mBluetoothLatch.await(10, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: bluetooth update listener", success); Assert.assertTrue(enable == mSettingsUtil.isBluetoothEnabled()); Assert.assertTrue(enableBluetoothScanning == mSettingsUtil.isBluetoothScanningAlwaysEnabled()); @@ -167,7 +171,7 @@ public class ContextHubBleSettingsTestExecutor { * @param enableBluetoothScanning if bluetooth scanning is always enabled */ private void runBleScanningTest(boolean enableBluetooth, - boolean enableBluetoothScanning) { + boolean enableBluetoothScanning) throws InterruptedException { setBluetoothSettings(enableBluetooth, enableBluetoothScanning); boolean enableFeature = enableBluetooth || enableBluetoothScanning; diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubGnssSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubGnssSettingsTestExecutor.java index 8b49dadb..e99fed1b 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubGnssSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubGnssSettingsTestExecutor.java @@ -48,12 +48,12 @@ public class ContextHubGnssSettingsTestExecutor { mExecutor.init(); } - public void runGnssLocationTest() { + public void runGnssLocationTest() throws InterruptedException { runTest(ChreSettingsTest.TestCommand.Feature.GNSS_LOCATION, false /* enableFeature */); runTest(ChreSettingsTest.TestCommand.Feature.GNSS_LOCATION, true /* enableFeature */); } - public void runGnssMeasurementTest() { + public void runGnssMeasurementTest() throws InterruptedException { runTest(ChreSettingsTest.TestCommand.Feature.GNSS_MEASUREMENT, false /* enableFeature */); runTest(ChreSettingsTest.TestCommand.Feature.GNSS_MEASUREMENT, true /* enableFeature */); } @@ -61,7 +61,7 @@ public class ContextHubGnssSettingsTestExecutor { /** * Should be called in an @After method. */ - public void tearDown() { + public void tearDown() throws InterruptedException { mExecutor.deinit(); mSettingsUtil.setLocationMode(mInitialLocationEnabled, 30 /* timeoutSeconds */); } @@ -71,7 +71,8 @@ public class ContextHubGnssSettingsTestExecutor { * @param feature The GNSS feature to test. * @param enableFeature True for enable. */ - private void runTest(ChreSettingsTest.TestCommand.Feature feature, boolean enableFeature) { + private void runTest(ChreSettingsTest.TestCommand.Feature feature, boolean enableFeature) + throws InterruptedException { mSettingsUtil.setLocationMode(enableFeature, 30 /* timeoutSeconds */); ChreSettingsTest.TestCommand.State state = enableFeature diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubMicDisableSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubMicDisableSettingsTestExecutor.java index 8d8c4e9e..c17dd318 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubMicDisableSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubMicDisableSettingsTestExecutor.java @@ -50,7 +50,7 @@ public class ContextHubMicDisableSettingsTestExecutor { mExecutor.init(); } - public void runMicDisableSettingsTest() { + public void runMicDisableSettingsTest() throws InterruptedException { setMicrophoneDisableSetting(false /* disableAccess */); runTest(ChreSettingsTest.TestCommand.Feature.AUDIO, false /* enableFeature */); @@ -87,7 +87,8 @@ public class ContextHubMicDisableSettingsTestExecutor { * @param feature The feature to test. * @param enableFeature True for enable. */ - private void runTest(ChreSettingsTest.TestCommand.Feature feature, boolean enableFeature) { + private void runTest(ChreSettingsTest.TestCommand.Feature feature, boolean enableFeature) + throws InterruptedException { ChreSettingsTest.TestCommand.State state = enableFeature ? ChreSettingsTest.TestCommand.State.ENABLED : ChreSettingsTest.TestCommand.State.DISABLED; diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubSettingsTestExecutor.java index 78a2f1bb..5c9a2173 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubSettingsTestExecutor.java @@ -138,7 +138,8 @@ public class ContextHubSettingsTestExecutor extends ContextHubClientCallback { * * @param feature The feature to set the test up for. */ - public void setupTestAssertSuccess(ChreSettingsTest.TestCommand.Feature feature) { + public void setupTestAssertSuccess(ChreSettingsTest.TestCommand.Feature feature) + throws InterruptedException { mTestResult.set(null); mTestSetupComplete.set(false); mCountDownLatch = new CountDownLatch(1); @@ -155,12 +156,8 @@ public class ContextHubSettingsTestExecutor extends ContextHubClientCallback { Assert.fail("Failed to send message: result = " + result); } - try { - mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } - + boolean success = mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: test setup", success); Assert.assertTrue( "Failed to set up test", mTestSetupComplete.get() || mTestResult.get() != null); } @@ -173,7 +170,7 @@ public class ContextHubSettingsTestExecutor extends ContextHubClientCallback { */ public void startTestAssertSuccess( ChreSettingsTest.TestCommand.Feature feature, - ChreSettingsTest.TestCommand.State state) { + ChreSettingsTest.TestCommand.State state) throws InterruptedException { mTestResult.set(null); mCountDownLatch = new CountDownLatch(1); @@ -188,11 +185,8 @@ public class ContextHubSettingsTestExecutor extends ContextHubClientCallback { Assert.fail("Failed to send message: result = " + result); } - try { - mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + boolean success = mCountDownLatch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: wait for test", success); if (mTestResult.get() == null) { Assert.fail("No test result received"); diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWifiSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWifiSettingsTestExecutor.java index 50d740dd..0cc477b4 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWifiSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWifiSettingsTestExecutor.java @@ -95,12 +95,12 @@ public class ContextHubWifiSettingsTestExecutor { mExecutor.init(); } - public void runWifiScanningTest() { + public void runWifiScanningTest() throws InterruptedException { runWifiScanningTest(false /* enableFeature */); runWifiScanningTest(true /* enableFeature */); } - public void runWifiRangingTest() { + public void runWifiRangingTest() throws InterruptedException { runWifiRangingTest(false /* enableFeature */); runWifiRangingTest(true /* enableFeature */); } @@ -108,7 +108,7 @@ public class ContextHubWifiSettingsTestExecutor { /** * Should be called in an @After method. */ - public void tearDown() { + public void tearDown() throws InterruptedException { mExecutor.deinit(); mSettingsUtil.setWifi(mInitialWifiEnabled); mSettingsUtil.setWifiScanningSettings(mInitialWifiScanningAlwaysEnabled); @@ -119,7 +119,7 @@ public class ContextHubWifiSettingsTestExecutor { * Sets the WiFi scanning settings on the device. * @param enable true to enable WiFi settings, false to disable it. */ - private void setWifiSettings(boolean enable) { + private void setWifiSettings(boolean enable) throws InterruptedException { WifiUpdateListener wifiUpdateListener = new WifiUpdateListener(enable); mContext.registerReceiver( wifiUpdateListener.mWifiUpdateReceiver, @@ -128,14 +128,11 @@ public class ContextHubWifiSettingsTestExecutor { mSettingsUtil.setWifiScanningSettings(enable); mSettingsUtil.setWifi(enable); - try { - wifiUpdateListener.mWifiLatch.await(30, TimeUnit.SECONDS); + boolean success = wifiUpdateListener.mWifiLatch.await(30, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: set wifi settings", success); - // Wait a few seconds to ensure setting is propagated to CHRE path - Thread.sleep(10000); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + // Wait a few seconds to ensure setting is propagated to CHRE path + Thread.sleep(10000); mContext.unregisterReceiver(wifiUpdateListener.mWifiUpdateReceiver); } @@ -144,7 +141,7 @@ public class ContextHubWifiSettingsTestExecutor { * Helper function to run the test. * @param enableFeature True for enable. */ - private void runWifiScanningTest(boolean enableFeature) { + private void runWifiScanningTest(boolean enableFeature) throws InterruptedException { setWifiSettings(enableFeature); ChreSettingsTest.TestCommand.State state = enableFeature @@ -158,7 +155,7 @@ public class ContextHubWifiSettingsTestExecutor { * Helper function to run the test. * @param enableFeature True for enable. */ - private void runWifiRangingTest(boolean enableFeature) { + private void runWifiRangingTest(boolean enableFeature) throws InterruptedException { if (!mSettingsUtil.isWifiEnabled() || !mSettingsUtil.isWifiScanningAlwaysEnabled()) { setWifiSettings(true /* enable */); } diff --git a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWwanSettingsTestExecutor.java b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWwanSettingsTestExecutor.java index e6d82e70..c7f726fc 100644 --- a/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWwanSettingsTestExecutor.java +++ b/java/test/settings/src/com/google/android/chre/test/setting/ContextHubWwanSettingsTestExecutor.java @@ -51,7 +51,7 @@ public class ContextHubWwanSettingsTestExecutor { mExecutor.init(); } - public void runWwanTest() { + public void runWwanTest() throws InterruptedException { runTest(false /* enableFeature */); runTest(true /* enableFeature */); } @@ -59,7 +59,7 @@ public class ContextHubWwanSettingsTestExecutor { /** * Should be called in an @After method. */ - public void tearDown() { + public void tearDown() throws InterruptedException { mExecutor.deinit(); mSettingsUtil.setAirplaneMode(mInitialAirplaneMode); } @@ -68,7 +68,7 @@ public class ContextHubWwanSettingsTestExecutor { * Helper function to run the test. * @param enableFeature True for enable. */ - private void runTest(boolean enableFeature) { + private void runTest(boolean enableFeature) throws InterruptedException { boolean airplaneModeExpected = !enableFeature; mSettingsUtil.setAirplaneMode(airplaneModeExpected); diff --git a/java/test/stress/Android.bp b/java/test/stress/Android.bp index c0c2cc84..cf3ccf4d 100644 --- a/java/test/stress/Android.bp +++ b/java/test/stress/Android.bp @@ -33,5 +33,5 @@ java_library { "chre-test-utils", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/stress/src/com/google/android/chre/test/stress/ContextHubStressTestExecutor.java b/java/test/stress/src/com/google/android/chre/test/stress/ContextHubStressTestExecutor.java index 378640a8..a907a000 100644 --- a/java/test/stress/src/com/google/android/chre/test/stress/ContextHubStressTestExecutor.java +++ b/java/test/stress/src/com/google/android/chre/test/stress/ContextHubStressTestExecutor.java @@ -175,7 +175,7 @@ public class ContextHubStressTestExecutor extends ContextHubClientCallback { * @param timeout The amount of time to run the stress test. * @param unit The unit for timeout. */ - public void runStressTest(long timeout, TimeUnit unit) { + public void runStressTest(long timeout, TimeUnit unit) throws InterruptedException { ChreStressTest.TestCommand.Feature[] features = { ChreStressTest.TestCommand.Feature.WIFI_ON_DEMAND_SCAN, ChreStressTest.TestCommand.Feature.GNSS_LOCATION, @@ -194,11 +194,8 @@ public class ContextHubStressTestExecutor extends ContextHubClientCallback { } if (!mLoadAndStartOnly) { - try { - mCountDownLatch.await(timeout, unit); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + boolean success = mCountDownLatch.await(timeout, unit); + Assert.assertTrue("Timeout waiting for signal", success); checkTestFailure(); @@ -233,7 +230,7 @@ public class ContextHubStressTestExecutor extends ContextHubClientCallback { * 4. Keep the nanoapp loaded, and then run this test. * 5. Unload the nanoapp after this test ends. */ - public void runWifiScanMonitorRestartTest() { + public void runWifiScanMonitorRestartTest() throws InterruptedException { // Since the host connection may have reset, inform the nanoapp about this event. NanoAppMessage message = NanoAppMessage.createMessageToNanoApp( mNanoAppId, ChreStressTest.MessageType.TEST_HOST_RESTARTED_VALUE, @@ -246,11 +243,8 @@ public class ContextHubStressTestExecutor extends ContextHubClientCallback { new byte[0]); sendMessageToNanoApp(message); - try { - mCountDownLatch.await(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + boolean success = mCountDownLatch.await(30, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: wifi scan monitor restart test", success); if ((mCapabilities.getWifi() & WIFI_CAPABILITIES_SCAN_MONITORING) != 0) { WifiManager manager = @@ -262,11 +256,8 @@ public class ContextHubStressTestExecutor extends ContextHubClientCallback { mCountDownLatch = new CountDownLatch(1); Assert.assertTrue(manager.startScan()); - try { - mCountDownLatch.await(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + success = mCountDownLatch.await(30, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: trigger scan monitor", success); Assert.assertTrue(mWifiScanMonitorTriggered.get()); checkTestFailure(); } diff --git a/java/test/utils/Android.bp b/java/test/utils/Android.bp index ca474dfb..adca53e3 100644 --- a/java/test/utils/Android.bp +++ b/java/test/utils/Android.bp @@ -28,11 +28,12 @@ java_library { static_libs: [ "androidx.test.rules", - "truth", "chre_api_test_proto_java_lite", "chre_pigweed_utils", + "compatibility-device-util-axt", "pw_rpc_java_client", + "truth", ], - sdk_version: "system_current", + sdk_version: "test_current", } diff --git a/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java b/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java index 7a0be9e6..d30d3ed3 100644 --- a/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java +++ b/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java @@ -19,6 +19,7 @@ package com.google.android.utils.chre; import androidx.annotation.NonNull; import com.google.android.chre.utils.pigweed.ChreRpcClient; +import com.google.protobuf.Empty; import com.google.protobuf.MessageLite; import java.util.ArrayList; @@ -55,6 +56,11 @@ public class ChreApiTestUtil { public static final int RPC_TIMEOUT_IN_MS = RPC_TIMEOUT_IN_SECONDS * 1000; /** + * The default timeout for an RPC call in nanosecond. + */ + public static final long RPC_TIMEOUT_IN_NS = RPC_TIMEOUT_IN_SECONDS * 1000000000L; + + /** * The number of threads for the executor that executes the futures. * We need at least 2 here. One to process the RPCs for server streaming * and one to process events (which has server streaming as a dependent). @@ -140,7 +146,7 @@ public class ChreApiTestUtil { /** * Calls a server streaming RPC method with RPC_TIMEOUT_IN_SECONDS seconds of - * timeout with a void request. + * timeout with an empty request. * * @param <ResponseType> the type of the response (proto generated type). * @param rpcClient the RPC client. @@ -155,7 +161,7 @@ public class ChreApiTestUtil { Objects.requireNonNull(rpcClient); Objects.requireNonNull(method); - ChreApiTest.Void request = ChreApiTest.Void.newBuilder().build(); + Empty request = Empty.newBuilder().build(); return callServerStreamingRpcMethodSync(rpcClient, method, request); } @@ -266,7 +272,7 @@ public class ChreApiTestUtil { } /** - * Calls an RPC method with RPC_TIMEOUT_IN_SECONDS seconds of timeout with a void request. + * Calls an RPC method with RPC_TIMEOUT_IN_SECONDS seconds of timeout with an empty request. * * @param <ResponseType> the type of the response (proto generated type). * @param rpcClient the RPC client. @@ -280,7 +286,7 @@ public class ChreApiTestUtil { Objects.requireNonNull(rpcClient); Objects.requireNonNull(method); - ChreApiTest.Void request = ChreApiTest.Void.newBuilder().build(); + Empty request = Empty.newBuilder().build(); return callUnaryRpcMethodSync(rpcClient, method, request); } @@ -303,7 +309,6 @@ public class ChreApiTestUtil { ChreApiTest.GatherEventsInput input = ChreApiTest.GatherEventsInput.newBuilder() .addAllEventTypes(eventTypes) - .setEventTypeCount(eventTypes.size()) .setEventCount(eventCount) .setTimeoutInNs(timeoutInNs) .build(); @@ -385,69 +390,56 @@ public class ChreApiTestUtil { return new Service("chre.rpc.ChreApiTestService", Service.unaryMethod( "ChreBleGetCapabilities", - ChreApiTest.Void.class, - ChreApiTest.Capabilities.class), + Empty.parser(), + ChreApiTest.Capabilities.parser()), Service.unaryMethod( "ChreBleGetFilterCapabilities", - ChreApiTest.Void.class, - ChreApiTest.Capabilities.class), - Service.unaryMethod( - "ChreBleStartScanAsync", - ChreApiTest.ChreBleStartScanAsyncInput.class, - ChreApiTest.Status.class), + Empty.parser(), + ChreApiTest.Capabilities.parser()), Service.serverStreamingMethod( "ChreBleStartScanSync", - ChreApiTest.ChreBleStartScanAsyncInput.class, - ChreApiTest.GeneralSyncMessage.class), - Service.unaryMethod( - "ChreBleStopScanAsync", - ChreApiTest.Void.class, - ChreApiTest.Status.class), + ChreApiTest.ChreBleStartScanAsyncInput.parser(), + ChreApiTest.GeneralSyncMessage.parser()), Service.serverStreamingMethod( "ChreBleStopScanSync", - ChreApiTest.Void.class, - ChreApiTest.GeneralSyncMessage.class), + Empty.parser(), + ChreApiTest.GeneralSyncMessage.parser()), Service.unaryMethod( "ChreSensorFindDefault", - ChreApiTest.ChreSensorFindDefaultInput.class, - ChreApiTest.ChreSensorFindDefaultOutput.class), + ChreApiTest.ChreSensorFindDefaultInput.parser(), + ChreApiTest.ChreSensorFindDefaultOutput.parser()), Service.unaryMethod( "ChreGetSensorInfo", - ChreApiTest.ChreHandleInput.class, - ChreApiTest.ChreGetSensorInfoOutput.class), + ChreApiTest.ChreHandleInput.parser(), + ChreApiTest.ChreGetSensorInfoOutput.parser()), Service.unaryMethod( "ChreGetSensorSamplingStatus", - ChreApiTest.ChreHandleInput.class, - ChreApiTest.ChreGetSensorSamplingStatusOutput.class), + ChreApiTest.ChreHandleInput.parser(), + ChreApiTest.ChreGetSensorSamplingStatusOutput.parser()), Service.unaryMethod( "ChreSensorConfigure", - ChreApiTest.ChreSensorConfigureInput.class, - ChreApiTest.Status.class), + ChreApiTest.ChreSensorConfigureInput.parser(), + ChreApiTest.Status.parser()), Service.unaryMethod( "ChreSensorConfigureModeOnly", - ChreApiTest.ChreSensorConfigureModeOnlyInput.class, - ChreApiTest.Status.class), + ChreApiTest.ChreSensorConfigureModeOnlyInput.parser(), + ChreApiTest.Status.parser()), Service.unaryMethod( "ChreAudioGetSource", - ChreApiTest.ChreHandleInput.class, - ChreApiTest.ChreAudioGetSourceOutput.class), + ChreApiTest.ChreHandleInput.parser(), + ChreApiTest.ChreAudioGetSourceOutput.parser()), Service.unaryMethod( "ChreConfigureHostEndpointNotifications", - ChreApiTest.ChreConfigureHostEndpointNotificationsInput.class, - ChreApiTest.Status.class), - Service.unaryMethod( - "RetrieveLatestDisconnectedHostEndpointEvent", - ChreApiTest.Void.class, - ChreApiTest.RetrieveLatestDisconnectedHostEndpointEventOutput - .class), + ChreApiTest.ChreConfigureHostEndpointNotificationsInput.parser(), + ChreApiTest.Status.parser()), Service.unaryMethod( "ChreGetHostEndpointInfo", - ChreApiTest.ChreGetHostEndpointInfoInput.class, - ChreApiTest.ChreGetHostEndpointInfoOutput.class), + ChreApiTest.ChreGetHostEndpointInfoInput.parser(), + ChreApiTest.ChreGetHostEndpointInfoOutput.parser()), Service.serverStreamingMethod( "GatherEvents", - ChreApiTest.GatherEventsInput.class, - ChreApiTest.GeneralEventsMessage.class)); + ChreApiTest.GatherEventsInput.parser(), + ChreApiTest.GeneralEventsMessage.parser())); } /** diff --git a/java/test/utils/src/com/google/android/utils/chre/ContextHubHostTestUtil.java b/java/test/utils/src/com/google/android/utils/chre/ContextHubHostTestUtil.java new file mode 100644 index 00000000..ba1b8a00 --- /dev/null +++ b/java/test/utils/src/com/google/android/utils/chre/ContextHubHostTestUtil.java @@ -0,0 +1,362 @@ +/* + * 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 com.google.android.utils.chre; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.location.ContextHubInfo; +import android.hardware.location.ContextHubManager; +import android.hardware.location.NanoAppBinary; +import android.os.Build; +import android.os.Bundle; + +import androidx.test.InstrumentationRegistry; + +import com.android.compatibility.common.util.DynamicConfigDeviceSide; + +import org.junit.Assert; +import org.junit.Assume; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A class that defines various utility functions to be used by Context Hub host tests. + */ +public class ContextHubHostTestUtil { + /** + * The names of the dynamic configs corresponding to each test suite. + */ + public static final String[] DEVICE_DYNAMIC_CONFIG_NAMES = + new String[] {"GtsGmscoreHostTestCases", "GtsLocationContextMultiDeviceTestCases"}; + + public static String multiDeviceExternalNanoappPath = null; + + /** + * Returns the path to the directory containing the nanoapp binaries. + * It is the external path if passed in, otherwise the relative path + * to the assets directory of the context of the calling app. + * + * @param context the Context to find the asset resources + * @param hubInfo the ContextHubInfo describing the hub + * @return the path to the nanoapps + */ + public static String getNanoAppBinaryPath(Context context, ContextHubInfo hubInfo) { + String path = getExternalNanoAppPath(); + + // Only check for bundled nanoapps if the test is not in debug mode + if (path == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + path = getNanoAppBinaryPathFromPlatformId(hubInfo.getChrePlatformId()); + } else { + path = getNanoAppBinaryPathFromHubName(hubInfo.getName()); + } + + boolean haveNanoApps = false; + try { + haveNanoApps = context.getAssets().list(path).length > 0; + } catch (IOException e) { + Assert.fail("IOException while getting asset list at " + path); + } + + // We anticipate this failure case (ContextHubInfo being of an unknown + // platform for us) much more than the case of knowing about a platform + // but not having a specific nanoapp built for it. So we separate + // out this error check to help the user debug this case more easily. + Assert.assertTrue("None of the test nanoapps are available on the platform: " + path, + haveNanoApps); + } + return path; + } + + /** + * Waits on a CountDownLatch or assert if it timed out or was interrupted. + * + * @param latch the CountDownLatch + * @param timeout the timeout duration + * @param unit the timeout unit + * @param timeoutErrorMessage the message to display on timeout assert + */ + public static void awaitCountDownLatchAssertOnFailure( + CountDownLatch latch, long timeout, TimeUnit unit, String timeoutErrorMessage) + throws InterruptedException { + boolean result = latch.await(timeout, unit); + Assert.assertTrue(timeoutErrorMessage, result); + } + + /** + * Creates a NanoAppBinary object from the nanoapp filename. + * + * @param hub the hub to create the binary for + * @param filename the filename of the nanoapp + * @return the NanoAppBinary object + */ + public static NanoAppBinary createNanoAppBinary(ContextHubInfo hub, String filename) { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + String fullName = getNanoAppBinaryPath(context, hub) + "/" + filename; + + InputStream stream = getNanoAppInputStream(context, fullName); + byte[] binary = null; + try { + binary = new byte[stream.available()]; + stream.read(binary); + } catch (IOException e) { + Assert.fail("IOException while reading binary for " + fullName + ": " + e.getMessage()); + } + + return new NanoAppBinary(binary); + } + + /** + * Read the nanoapp to an InputStream object. + * + * @param context the Context to find the asset resources + * @param fullName the fullName of the nanoapp + * @return the InputStream of the nanoapp + */ + public static InputStream getNanoAppInputStream(Context context, String fullName) { + InputStream inputStream = null; + try { + inputStream = (getExternalNanoAppPath() == null) + ? context.getAssets().open(fullName) : + new FileInputStream(new File(fullName)); + } catch (IOException e) { + Assert.fail("Could not find asset " + fullName + ": " + + e.toString()); + } + return inputStream; + } + + /** + * Converts a list of integers to a byte array. + * + * @param intArray the integer values + * @return the byte[] array containing the integer values in byte order + */ + public static byte[] intArrayToByteArray(int[] intArray) { + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * intArray.length); + for (int i = 0; i < intArray.length; i++) { + buffer.putInt(intArray[i]); + } + + return buffer.array(); + } + + /** + * Determines if the device under test is in the allowlist to run CHQTS. + * + * The device-side test currently skips the test only if the Context Hub + * Service does not exist, but the Android framework currently exposes it unconditionally. + * This method should be used to skip the test if the test device is not in the allowlist and + * no Context Hub exists in order to avoid false positive failures. In the future, the framework + * should be modified to only expose the service if CHRE is supported on the device. This hack + * should then only be used on Android version that do not have that capability. + * + * @return true if the device is in the allowlist, false otherwise + */ + public static boolean deviceInAllowlist() { + DynamicConfigDeviceSide deviceDynamicConfig = getDynamicConfig(); + List<String> configValues = deviceDynamicConfig.getValues("chre_device_whitelist"); + Assert.assertTrue("Could not find device allowlist from dynamic config", + configValues != null); + + String deviceName = Build.DEVICE; + for (String element : configValues) { + if (element.equals(deviceName)) { + return true; + } + } + + return false; + } + + /** + * Determines if the device under test is in the denylist to NOT run CHQTS. + * + * The denylist is structured as "device,platform_id,max_chre_version", and characterizes + * if a device should skip running CHQTS. For platform_id, and max_chre_version, "*" denotes + * "matches everything". + * + * @return true if the device is in the denylist, false otherwise + */ + public static boolean deviceInDenylist() { + DynamicConfigDeviceSide deviceDynamicConfig = getDynamicConfig(); + List<String> configValues = deviceDynamicConfig.getValues("chre_device_blacklist"); + Assert.assertTrue("Could not find device denylist from dynamic config", + configValues != null); + + String deviceName = Build.DEVICE; + for (String element : configValues) { + String[] delimited = element.split(","); + if (delimited.length != 0 && delimited[0].equals(deviceName)) { + return true; + } + } + + return false; + } + + /** + * Returns the path of the nanoapps for a CHRE implementation using the platform ID. + * + * The dynamic configuration file for GtsGmscoreHostTestCases defines the key + * 'chre_platform_id_nanoapp_dir_pairs', whose value is a list of strings of the form + * '{platformId},{assetDirectoryName}', where platformId is one defined by the Context Hub + * and is returned by ContextHubInfo.getChrePlatformId(). + * + * @param platformId the platform ID of the hub + * @return the path to the nanoapps + */ + private static String getNanoAppBinaryPathFromPlatformId(long platformId) { + DynamicConfigDeviceSide deviceDynamicConfig = getDynamicConfig(); + List<String> configValues = + deviceDynamicConfig.getValues("chre_platform_id_nanoapp_dir_pairs"); + Assert.assertTrue("Could not find nanoapp asset list from dynamic config", + configValues != null); + + String platformIdHexString = Long.toHexString(platformId); + String path = null; + for (String element : configValues) { + String[] delimited = element.split(","); + if (delimited.length == 2 && delimited[0].equals(platformIdHexString)) { + path = delimited[1]; + break; + } + } + + Assert.assertTrue("Could not find known asset directory for hub with platform ID = 0x" + + platformIdHexString, path != null); + return path; + } + + /** + * Returns the path for nanoapps of a CHRE implementation using the Context Hub name. + * + * The dynamic configuration file for GtsGmscoreHostTestCases defines the key + * 'chre_hubname_nanoapp_dir_pairs', whose value is a list of strings of the form + * '{hubName},{assetDirectoryName}', where hubName is one defined by the Context Hub + * and is returned by ContextHubInfo.getName(). This method should be used instead of + * getNanoAppBinaryPathFromPlatformId() prior to Android P. + * + * @param contextHubName the name of the hub + * @return the path to the nanoapps + */ + private static String getNanoAppBinaryPathFromHubName(String contextHubName) { + DynamicConfigDeviceSide deviceDynamicConfig = getDynamicConfig(); + List<String> configValues = + deviceDynamicConfig.getValues("chre_hubname_nanoapp_dir_pairs"); + Assert.assertTrue("Could not find nanoapp asset list from dynamic config", + configValues != null); + + String path = null; + for (String element : configValues) { + String[] delimited = element.split(","); + if (delimited.length == 2 && delimited[0].equals(contextHubName)) { + path = delimited[1]; + break; + } + } + + Assert.assertTrue("Could not find known asset directory for hub " + contextHubName, + path != null); + return path; + } + + /** + * @return the device side dynamic config for GtsGmscoreHostTestCases or + * GtsLocationContextMultiDeviceTestCases + */ + private static DynamicConfigDeviceSide getDynamicConfig() { + DynamicConfigDeviceSide deviceDynamicConfig = null; + for (String deviceDynamicConfigName: DEVICE_DYNAMIC_CONFIG_NAMES) { + try { + deviceDynamicConfig = new DynamicConfigDeviceSide(deviceDynamicConfigName); + } catch (XmlPullParserException e) { + Assert.fail(e.getMessage()); + } catch (IOException e) { + // Not found - try again + } + } + + if (deviceDynamicConfig == null) { + Assert.fail("Could not get the device dynamic config."); + } + return deviceDynamicConfig; + } + + /** + * Returns the path to external nanoapps. This method should be used to debug the test + * with custom unbundled nanoapps, and must not be used in actual certification. + * + * @return external nanoapp path, null if no externalNanoAppPath passed in + */ + public static String getExternalNanoAppPath() { + if (multiDeviceExternalNanoappPath != null) { + return multiDeviceExternalNanoappPath; + } + + Bundle extras = InstrumentationRegistry.getArguments(); + return (extras == null) ? null : extras.getString("externalNanoAppPath"); + } + + /** + * Runs various checks to decide if the platform should run CHQTS. + * + * @param context The context at which the test should run. + * @param manager The ContextHubManager on this app. + */ + public static void checkDeviceShouldRunTest(Context context, ContextHubManager manager) { + boolean supportsContextHub; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + supportsContextHub = + context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_CONTEXT_HUB); + Assert.assertTrue("ContextHubManager must be null if feature is not supported.", + supportsContextHub || manager == null); + } else { + supportsContextHub = (manager != null); + } + Assume.assumeTrue("Device does not support Context Hub, skipping test", supportsContextHub); + + int numContextHubs; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + numContextHubs = manager.getContextHubs().size(); + } else { + int[] handles = manager.getContextHubHandles(); + Assert.assertNotNull(handles); + numContextHubs = handles.length; + } + + // Only use allowlist logic on builds that do not require the Context Hub feature flag. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // Use allowlist on platforms that reports no Context Hubs to prevent false positive + // failures on devices that do not actually support CHRE. + Assume.assumeTrue( + "Device not in allowlist and does not have Context Hub, skipping test", + numContextHubs != 0 || deviceInAllowlist()); + } + + // Use a denylist on platforms that should not run CHQTS. + Assume.assumeTrue("Device is in denylist, skipping test", !deviceInDenylist()); + } +} diff --git a/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java b/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java index 1c0731a1..18623a0b 100644 --- a/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java +++ b/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java @@ -26,6 +26,8 @@ import android.hardware.location.ContextHubManager; import android.hardware.location.ContextHubTransaction; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppState; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import java.util.HashMap; import java.util.List; @@ -65,8 +67,14 @@ public class ContextHubServiceTestHelper { public void initAndUnloadAllNanoApps() throws InterruptedException, TimeoutException { init(); - // Unload all nanoapps to ensure test starts at a clean state. - unloadAllNanoApps(); + + // We only need to unload all nanoapps when the device has version < U, so the + // tests remain the same on those devices. On newer devices, test mode will + // handle this. + if (VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) { + // Unload all nanoapps to ensure test starts at a clean state. + unloadAllNanoApps(); + } } public void deinit() { diff --git a/java/test/utils/src/com/google/android/utils/chre/SettingsUtil.java b/java/test/utils/src/com/google/android/utils/chre/SettingsUtil.java index 98a2ace6..54401ac4 100644 --- a/java/test/utils/src/com/google/android/utils/chre/SettingsUtil.java +++ b/java/test/utils/src/com/google/android/utils/chre/SettingsUtil.java @@ -116,7 +116,7 @@ public class SettingsUtil { * @param enable True to enable location, false to disable it. * @param timeoutSeconds The maximum amount of time in seconds to wait. */ - public void setLocationMode(boolean enable, long timeoutSeconds) { + public void setLocationMode(boolean enable, long timeoutSeconds) throws InterruptedException { if (isLocationEnabled() != enable) { LocationUpdateListener listener = new LocationUpdateListener(); @@ -125,14 +125,11 @@ public class SettingsUtil { new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); mLocationManager.setLocationEnabledForUser(enable, UserHandle.CURRENT); - try { - listener.mLocationLatch.await(timeoutSeconds, TimeUnit.SECONDS); + boolean success = listener.mLocationLatch.await(timeoutSeconds, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: set location mode", success); - // Wait 1 additional second to make sure setting gets propagated to CHRE - Thread.sleep(1000); - } catch (InterruptedException e) { - Assert.fail("InterruptedException while waiting for location update"); - } + // Wait 1 additional second to make sure setting gets propagated to CHRE + Thread.sleep(1000); Assert.assertTrue(isLocationEnabled() == enable); @@ -172,7 +169,7 @@ public class SettingsUtil { public boolean isBluetoothEnabled() { String out = ChreTestUtil.executeShellCommand( mInstrumentation, "settings get global bluetooth_on"); - return ChreTestUtil.convertToIntegerOrFail(out) > 0; + return ChreTestUtil.convertToIntegerOrFail(out) == 1; } /** @@ -190,7 +187,7 @@ public class SettingsUtil { * Sets the airplane mode on the device. * @param enable True to enable airplane mode, false to disable it. */ - public void setAirplaneMode(boolean enable) { + public void setAirplaneMode(boolean enable) throws InterruptedException { if (isAirplaneModeOn() != enable) { AirplaneModeListener listener = new AirplaneModeListener(); mContext.registerReceiver( @@ -201,13 +198,10 @@ public class SettingsUtil { ChreTestUtil.executeShellCommand( mInstrumentation, "cmd connectivity airplane-mode " + value); - try { - listener.mAirplaneModeLatch.await(10, TimeUnit.SECONDS); - // Wait 1 additional second to make sure setting gets propagated to CHRE - Thread.sleep(1000); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } + boolean success = listener.mAirplaneModeLatch.await(10, TimeUnit.SECONDS); + Assert.assertTrue("Timeout waiting for signal: set airplane mode", success); + // Wait 1 additional second to make sure setting gets propagated to CHRE + Thread.sleep(1000); Assert.assertTrue(isAirplaneModeOn() == enable); mContext.unregisterReceiver(listener.mAirplaneModeReceiver); } diff --git a/java/utils/pigweed/Android.bp b/java/utils/pigweed/Android.bp index e7b15724..66d7d16d 100644 --- a/java/utils/pigweed/Android.bp +++ b/java/utils/pigweed/Android.bp @@ -27,7 +27,6 @@ java_library { srcs: ["src/**/*.java"], static_libs: [ - "androidx.annotation_annotation", "guava", "pw_rpc_java_client", ], diff --git a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreCallbackHandler.java b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreCallbackHandler.java index b84ebad4..291ebf7d 100644 --- a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreCallbackHandler.java +++ b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreCallbackHandler.java @@ -22,9 +22,6 @@ import android.hardware.location.ContextHubClient; import android.hardware.location.ContextHubClientCallback; import android.hardware.location.NanoAppMessage; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import java.util.Objects; import dev.pigweed.pw_rpc.Client; @@ -35,16 +32,17 @@ import dev.pigweed.pw_rpc.Client; */ public class ChreCallbackHandler extends ContextHubClientCallback { private final long mNanoappId; - @Nullable + // Nullable. private final ContextHubClientCallback mCallback; private Client mRpcClient; private ChreChannelOutput mChannelOutput; /** * @param nanoappId ID of the RPC Server nanoapp - * @param callback The callbacks receiving messages and life-cycle events from nanoapps + * @param callback The callbacks receiving messages and life-cycle events from nanoapps, + * nullable. */ - public ChreCallbackHandler(long nanoappId, @Nullable ContextHubClientCallback callback) { + public ChreCallbackHandler(long nanoappId, ContextHubClientCallback callback) { mNanoappId = nanoappId; mCallback = callback; } @@ -52,10 +50,10 @@ public class ChreCallbackHandler extends ContextHubClientCallback { /** * Completes the initialization. * - * @param rpcClient The Pigweed RPC client - * @param channelOutput The ChannelOutput used by Pigweed + * @param rpcClient The Pigweed RPC client, non null + * @param channelOutput The ChannelOutput used by Pigweed, non null */ - public void lateInit(@NonNull Client rpcClient, @NonNull ChreChannelOutput channelOutput) { + public void lateInit(Client rpcClient, ChreChannelOutput channelOutput) { mRpcClient = Objects.requireNonNull(rpcClient); mChannelOutput = Objects.requireNonNull(channelOutput); } diff --git a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreChannelOutput.java b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreChannelOutput.java index 43440b59..1e79693b 100644 --- a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreChannelOutput.java +++ b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreChannelOutput.java @@ -30,10 +30,9 @@ import dev.pigweed.pw_rpc.ChannelOutputException; */ public class ChreChannelOutput implements Channel.Output { /** - * Random value chosen not too close to max value to try to avoid conflicts with other messages - * in case pw rpc isn't the only way the client chooses to communicate. + * Message type to use for RPC messages. */ - public static final int PW_RPC_CHRE_MESSAGE_TYPE = Integer.MAX_VALUE - 10; + public static final int CHRE_MESSAGE_TYPE_RPC = 0x7FFFFFF5; // 1 denotes that a host endpoint is the client that created the channel. private static final int CHANNEL_ID_HOST_CLIENT = (1 << 16); @@ -55,7 +54,7 @@ public class ChreChannelOutput implements Channel.Output { @Override public void send(byte[] packet) throws ChannelOutputException { NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(mNanoappId, - PW_RPC_CHRE_MESSAGE_TYPE, packet); + CHRE_MESSAGE_TYPE_RPC, packet); if (mAuthDenied.get() || ContextHubTransaction.RESULT_SUCCESS != mClient.sendMessageToNanoApp(message)) { throw new ChannelOutputException(); diff --git a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreIntentHandler.java b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreIntentHandler.java index bec78fc8..91cdcf28 100644 --- a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreIntentHandler.java +++ b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreIntentHandler.java @@ -23,8 +23,6 @@ import android.hardware.location.ContextHubIntentEvent; import android.hardware.location.ContextHubManager; import android.util.Log; -import androidx.annotation.NonNull; - import java.util.Objects; import dev.pigweed.pw_rpc.Client; @@ -38,13 +36,13 @@ public class ChreIntentHandler { /** * Handles CHRE intents. * - * @param intent the intent. + * @param intent the intent, non null * @param nanoappId ID of the RPC Server nanoapp - * @param rpcClient The Pigweed RPC client - * @param channelOutput The ChannelOutput used by Pigweed + * @param rpcClient The Pigweed RPC client, non null + * @param channelOutput The ChannelOutput used by Pigweed, non null */ - public static void handle(@NonNull Intent intent, long nanoappId, @NonNull Client rpcClient, - @NonNull ChreChannelOutput channelOutput) { + public static void handle(Intent intent, long nanoappId, Client rpcClient, + ChreChannelOutput channelOutput) { Objects.requireNonNull(intent); Objects.requireNonNull(rpcClient); Objects.requireNonNull(channelOutput); diff --git a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreRpcClient.java b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreRpcClient.java index 0ba98ed5..205482ee 100644 --- a/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreRpcClient.java +++ b/java/utils/pigweed/src/com/google/android/chre/utils/pigweed/ChreRpcClient.java @@ -23,9 +23,6 @@ import android.hardware.location.ContextHubManager; import android.hardware.location.NanoAppRpcService; import android.hardware.location.NanoAppState; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import java.util.List; import java.util.Objects; @@ -40,14 +37,15 @@ import dev.pigweed.pw_rpc.Service; * See https://g3doc.corp.google.com/location/lbs/contexthub/g3doc/nanoapps/pw_rpc_host.md */ public class ChreRpcClient { - @NonNull + + // Non null. private final Client mRpcClient; - @NonNull + // Non null. private final Channel mChannel; - @NonNull + // Non null. private final ChreChannelOutput mChannelOutput; private final long mServerNanoappId; - @NonNull + // Non null. private final ContextHubClient mContextHubClient; private ChreIntentHandler mIntentHandler; @@ -56,14 +54,16 @@ public class ChreRpcClient { * * Use this constructor for persistent clients using callbacks. * - * @param manager The context manager used to create a client + * @param manager The context manager used to create a client, non null + * @param info Context hub info, non null * @param serverNanoappId The ID of the RPC server nanoapp - * @param services The list of services provided by the server - * @param callback The callbacks receiving messages and life-cycle events from nanoapps + * @param services The list of services provided by the server, non null + * @param callback The callbacks receiving messages and life-cycle events from nanoapps, + * nullable */ - public ChreRpcClient(@NonNull ContextHubManager manager, @NonNull ContextHubInfo info, - long serverNanoappId, @NonNull List<Service> services, - @Nullable ContextHubClientCallback callback) { + public ChreRpcClient(ContextHubManager manager, ContextHubInfo info, + long serverNanoappId, List<Service> services, + ContextHubClientCallback callback) { Objects.requireNonNull(manager); Objects.requireNonNull(info); Objects.requireNonNull(services); @@ -83,12 +83,12 @@ public class ChreRpcClient { * * handleIntent() must be called with any CHRE intent received by the BroadcastReceiver. * - * @param contextHubClient The context hub client providing the RPC server nanoapp + * @param contextHubClient The context hub client providing the RPC server nanoapp, non null * @param serverNanoappId The ID of the RPC server nanoapp - * @param services The list of services provided by the server + * @param services The list of services provided by the server, non null */ - public ChreRpcClient(@NonNull ContextHubClient contextHubClient, long serverNanoappId, - @NonNull List<Service> services) { + public ChreRpcClient(ContextHubClient contextHubClient, long serverNanoappId, + List<Service> services) { mContextHubClient = Objects.requireNonNull(contextHubClient); Objects.requireNonNull(services); mServerNanoappId = serverNanoappId; @@ -124,9 +124,9 @@ public class ChreRpcClient { /** * Handles CHRE intents. * - * @param intent The CHRE intent. + * @param intent The CHRE intent, non null */ - public void handleIntent(@NonNull Intent intent) { + public void handleIntent(Intent intent) { ChreIntentHandler.handle(intent, mServerNanoappId, mRpcClient, mChannelOutput); } diff --git a/pal/include/chre/pal/ble.h b/pal/include/chre/pal/ble.h index 4f27535e..aa1cb90d 100644 --- a/pal/include/chre/pal/ble.h +++ b/pal/include/chre/pal/ble.h @@ -46,9 +46,19 @@ extern "C" { #define CHRE_PAL_BLE_API_V1_8 CHRE_PAL_CREATE_API_VERSION(1, 8) /** + * Introduced alongside CHRE API v1.8, adds flush() API. + */ +#define CHRE_PAL_BLE_API_V1_9 CHRE_PAL_CREATE_API_VERSION(1, 9) + +/** + * Introduced alongside CHRE API v1.9, add broadcaster address filter. + */ +#define CHRE_PAL_BLE_API_V1_10 CHRE_PAL_CREATE_API_VERSION(1, 10) + +/** * The version of the CHRE BLE PAL defined in this header file. */ -#define CHRE_PAL_BLE_API_CURRENT_VERSION CHRE_PAL_BLE_API_V1_8 +#define CHRE_PAL_BLE_API_CURRENT_VERSION CHRE_PAL_BLE_API_V1_10 /** * The maximum amount of time allowed to elapse between the call to @@ -123,6 +133,18 @@ struct chrePalBleCallbacks { void (*readRssiCallback)(uint8_t errorCode, uint16_t handle, int8_t rssi); /** + * Callback used to inform CHRE of a completed flush event. + * + * @param errorCode An error code from enum chreError, with CHRE_ERROR_NONE + * indicating a successful response. + * + * @see chrePalBleApi.flush + * + * @since v1.9 + */ + void (*flushCallback)(uint8_t errorCode); + + /** * Sends a BT snoop log to the CHRE daemon. * * @param isTxToBtController True if the direction of the BT snoop log is Tx @@ -202,7 +224,7 @@ struct chrePalBleApi { * @see chreBleStartScanAsync() */ bool (*startScan)(enum chreBleScanMode mode, uint32_t reportDelayMs, - const struct chreBleScanFilter *filter); + const struct chreBleScanFilterV1_9 *filter); /** * Stops Bluetooth LE (BLE) scanning. * @@ -243,6 +265,17 @@ struct chrePalBleApi { * @since v1.8 */ bool (*readRssi)(uint16_t connectionHandle); + + /** + * Initiates a flush operation where all batched advertisement events will be + * immediately processed. + * + * @return true if the request was accepted, in which case a subsequent call + * to flushCallback() will be used to indicate the result of the operation. + * + * @since v1.9 + */ + bool (*flush)(); }; /** diff --git a/pal/tests/src/audio_pal_impl_test.cc b/pal/tests/src/audio_pal_impl_test.cc index 1b3c3068..daf8e1e0 100644 --- a/pal/tests/src/audio_pal_impl_test.cc +++ b/pal/tests/src/audio_pal_impl_test.cc @@ -125,8 +125,7 @@ TEST_F(PalAudioTest, GetDataEvent) { LockGuard<Mutex> lock(gCallbacks->mMutex); EXPECT_TRUE(mApi->requestAudioDataEvent(0 /*handle*/, 1000 /*numSamples*/, 100 /*eventDelaysNs*/)); - gCallbacks->mCondVarDataEvents.wait_for( - gCallbacks->mMutex, Nanoseconds(25 * kOneMillisecondInNanoseconds)); + gCallbacks->mCondVarDataEvents.wait(gCallbacks->mMutex); ASSERT_TRUE(gCallbacks->mDataEvent.has_value()); struct chreAudioDataEvent *event = gCallbacks->mDataEvent.value(); EXPECT_EQ(event->handle, 0); diff --git a/pal/tests/src/ble_pal_impl_test.cc b/pal/tests/src/ble_pal_impl_test.cc index 0d08a879..8c53aebd 100644 --- a/pal/tests/src/ble_pal_impl_test.cc +++ b/pal/tests/src/ble_pal_impl_test.cc @@ -35,6 +35,7 @@ namespace { using ::chre::ConditionVariable; using ::chre::createBleScanFilterForKnownBeacons; +using ::chre::createBleScanFilterForKnownBeaconsV1_9; using ::chre::FixedSizeVector; using ::chre::gChrePalSystemApi; using ::chre::LockGuard; @@ -53,6 +54,10 @@ constexpr uint32_t kBleBatchDurationMs = 0; class Callbacks { public: + Callbacks() = delete; + + explicit Callbacks(const struct chrePalBleApi *api) : mApi(api) {} + void requestStateResync() {} void scanStatusChangeCallback(bool enabled, uint8_t errorCode) { @@ -71,6 +76,8 @@ class Callbacks { if (mEventData.full()) { mCondVarEvents.notify_one(); } + } else { + mApi->releaseAdvertisingEvent(event); } } @@ -83,6 +90,9 @@ class Callbacks { Mutex mMutex; ConditionVariable mCondVarStatus; ConditionVariable mCondVarEvents; + + //! CHRE PAL implementation API. + const struct chrePalBleApi *mApi; }; UniquePtr<Callbacks> gCallbacks = nullptr; @@ -108,10 +118,10 @@ void advertisingEventCallback(struct chreBleAdvertisementEvent *event) { class PalBleTest : public testing::Test { protected: void SetUp() override { - gCallbacks = MakeUnique<Callbacks>(); chre::TaskManagerSingleton::deinit(); chre::TaskManagerSingleton::init(); mApi = chrePalBleGetApi(CHRE_PAL_BLE_API_CURRENT_VERSION); + gCallbacks = MakeUnique<Callbacks>(mApi); ASSERT_NE(mApi, nullptr); EXPECT_EQ(mApi->moduleVersion, CHRE_PAL_BLE_API_CURRENT_VERSION); ASSERT_TRUE(mApi->open(&gChrePalSystemApi, &mPalCallbacks)); @@ -168,19 +178,21 @@ TEST_F(PalBleTest, Capabilities) { // advertising BLE beacons with service data for either the Google eddystone // or fastpair UUIDs. TEST_F(PalBleTest, FilteredScan) { - struct chreBleScanFilter filter; + struct chreBleScanFilterV1_9 filterV1_9; chreBleGenericFilter uuidFilters[kNumScanFilters]; - createBleScanFilterForKnownBeacons(filter, uuidFilters, kNumScanFilters); + createBleScanFilterForKnownBeaconsV1_9(filterV1_9, uuidFilters, + kNumScanFilters); + + LockGuard<Mutex> lock(gCallbacks->mMutex); EXPECT_TRUE(mApi->startScan(CHRE_BLE_SCAN_MODE_BACKGROUND, - kBleBatchDurationMs, &filter)); + kBleBatchDurationMs, &filterV1_9)); - LockGuard<Mutex> lock(gCallbacks->mMutex); + EXPECT_TRUE(mApi->startScan(CHRE_BLE_SCAN_MODE_AGGRESSIVE, + kBleBatchDurationMs, &filterV1_9)); gCallbacks->mCondVarStatus.wait_for(gCallbacks->mMutex, kBleStatusTimeoutNs); - EXPECT_TRUE(gCallbacks->mEnabled.has_value()); - if (gCallbacks->mEnabled.has_value()) { - EXPECT_TRUE(gCallbacks->mEnabled.value()); - } + ASSERT_TRUE(gCallbacks->mEnabled.has_value()); + EXPECT_TRUE(gCallbacks->mEnabled.value()); gCallbacks->mCondVarEvents.wait_for(gCallbacks->mMutex, kBleEventTimeoutNs); EXPECT_TRUE(gCallbacks->mEventData.full()); diff --git a/pal/tests/src/sensor_pal_impl_test.cc b/pal/tests/src/sensor_pal_impl_test.cc index f76c6174..6b0aefa2 100644 --- a/pal/tests/src/sensor_pal_impl_test.cc +++ b/pal/tests/src/sensor_pal_impl_test.cc @@ -183,6 +183,8 @@ TEST_F(PalSensorTest, EnableAContinuousSensor) { ASSERT_TRUE(gCallbacks->mStatus.has_value()); EXPECT_TRUE(gCallbacks->mStatus.value()->enabled); gApi->releaseSamplingStatusEvent(gCallbacks->mStatus.value()); + gCallbacks->mStatus.reset(); + gCallbacks->mStatusSensorIndex.reset(); gCallbacks->mCondVarEvents.wait_for( gCallbacks->mMutex, @@ -197,6 +199,22 @@ TEST_F(PalSensorTest, EnableAContinuousSensor) { EXPECT_EQ(threeAxisData->header.readingCount, 1); gApi->releaseSensorDataEvent(data); } + // Need to unlock this mutex because the following disable sensor request + // needs it. + gCallbacks->mMutex.unlock(); + + EXPECT_TRUE(gApi->configureSensor( + 0 /* sensorInfoIndex */, CHRE_SENSOR_CONFIGURE_MODE_DONE, + kOneMillisecondInNanoseconds /* intervalNs */, 0 /* latencyNs */)); + gCallbacks->mMutex.lock(); + gCallbacks->mCondVarStatus.wait_for( + gCallbacks->mMutex, + Nanoseconds(kTimeoutMultiplier * kOneMillisecondInNanoseconds)); + ASSERT_TRUE(gCallbacks->mStatusSensorIndex.has_value()); + ASSERT_TRUE(gCallbacks->mStatus.has_value()); + gApi->releaseSamplingStatusEvent(gCallbacks->mStatus.value()); + gCallbacks->mStatus.reset(); + gCallbacks->mStatusSensorIndex.reset(); } TEST_F(PalSensorTest, DisableAContinuousSensor) { @@ -208,11 +226,13 @@ TEST_F(PalSensorTest, DisableAContinuousSensor) { gCallbacks->mCondVarStatus.wait_for( gCallbacks->mMutex, Nanoseconds(kTimeoutMultiplier * kOneMillisecondInNanoseconds)); - EXPECT_TRUE(gCallbacks->mStatusSensorIndex.has_value()); + ASSERT_TRUE(gCallbacks->mStatusSensorIndex.has_value()); EXPECT_EQ(gCallbacks->mStatusSensorIndex.value(), 0); - EXPECT_TRUE(gCallbacks->mStatus.has_value()); + ASSERT_TRUE(gCallbacks->mStatus.has_value()); EXPECT_FALSE(gCallbacks->mStatus.value()->enabled); gApi->releaseSamplingStatusEvent(gCallbacks->mStatus.value()); + gCallbacks->mStatus.reset(); + gCallbacks->mStatusSensorIndex.reset(); } -} // namespace
\ No newline at end of file +} // namespace diff --git a/platform/exynos/host_link.cc b/platform/exynos/host_link.cc index 3e03bec3..480e022c 100644 --- a/platform/exynos/host_link.cc +++ b/platform/exynos/host_link.cc @@ -139,6 +139,8 @@ void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) { // TODO(b/230134803): Implement this. } +void HostMessageHandlers::handlePulseRequest() {} + void HostMessageHandlers::sendFragmentResponse(uint16_t hostClientId, uint32_t transactionId, uint32_t fragmentId, diff --git a/platform/include/chre/platform/assert.h b/platform/include/chre/platform/assert.h index 218dd48a..be83a360 100644 --- a/platform/include/chre/platform/assert.h +++ b/platform/include/chre/platform/assert.h @@ -21,19 +21,26 @@ /** * @file - * Defines the CHRE_ASSERT and CHRE_ASSERT_LOG macros for CHRE platforms. - * Platforms must supply an implementation for assertCondition or use the shared - * implementation. + * Includes the platform-specific header file that supplies an assertion macro. + * The platform header must supply the following symbol as a macro or free + * function: + * + * CHRE_ASSERT(scalar expression) + * + * Where expression will be checked to be false (ie: compares equal to zero) and + * terminate the program if found to be the case. */ -#if defined(CHRE_ASSERTIONS_ENABLED) +#if defined(CHRE_ASSERTIONS_ENABLED) && defined(CHRE_ASSERTIONS_DISABLED) +#error "CHRE_ASSERT is both enabled and disabled!" -#define CHRE_ASSERT(condition) \ - do { \ - if (!(condition)) { \ - chreDoAssert(CHRE_FILENAME, __LINE__); \ - } \ - } while (0) +#elif defined(CHRE_ASSERTIONS_ENABLED) + +#include "chre/target_platform/assert.h" + +#ifndef CHRE_ASSERT +#error "CHRE_ASSERT must be defined by the target platform's assert.h" +#endif // CHRE_ASSERT #elif defined(CHRE_ASSERTIONS_DISABLED) @@ -81,20 +88,4 @@ #define CHRE_ASSERT_IF_NOT_TEST(condition) CHRE_ASSERT(condition) #endif -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Performs assertion while logging the filename and line provided. - * - * @param filename The filename containing the assertion being fired. - * @param line The line that contains the assertion being fired. - */ -void chreDoAssert(const char *filename, size_t line); - -#ifdef __cplusplus -} -#endif - #endif // CHRE_PLATFORM_ASSERT_H_ diff --git a/platform/include/chre/platform/atomic.h b/platform/include/chre/platform/atomic.h index 7688c499..445feb90 100644 --- a/platform/include/chre/platform/atomic.h +++ b/platform/include/chre/platform/atomic.h @@ -53,7 +53,7 @@ class AtomicBool : public AtomicBoolBase, public NonCopyable { */ operator bool() const { return load(); - }; + } /** * Atomically loads the current value of the atomic object. diff --git a/platform/include/chre/platform/fatal_error.h b/platform/include/chre/platform/fatal_error.h index 0c301db0..f272eba8 100644 --- a/platform/include/chre/platform/fatal_error.h +++ b/platform/include/chre/platform/fatal_error.h @@ -40,6 +40,9 @@ do { \ LOGE(fmt, ##__VA_ARGS__); \ FATAL_ERROR_QUIT(); \ + while (1) { \ + /* never return */ \ + } \ } while (0) /** diff --git a/platform/include/chre/platform/platform_ble.h b/platform/include/chre/platform/platform_ble.h index 3f9b56ad..fdb495f0 100644 --- a/platform/include/chre/platform/platform_ble.h +++ b/platform/include/chre/platform/platform_ble.h @@ -69,7 +69,7 @@ class PlatformBle : public PlatformBleBase { * @return true if scan was successfully enabled. */ bool startScanAsync(chreBleScanMode mode, uint32_t reportDelayMs, - const struct chreBleScanFilter *filter); + const struct chreBleScanFilterV1_9 *filter); /** * End a BLE scan asynchronously. The result is delivered through a @@ -105,6 +105,17 @@ class PlatformBle : public PlatformBleBase { * @since v1.8 */ bool readRssiAsync(uint16_t connectionHandle); + + /** + * Initiates a flush operation where all batched advertisement events will be + * immediately processed. + * + * @return true if the request was accepted, in which case a subsequent call + * to flushCallback() will be used to indicate the result of the operation. + * + * @since v1.9 + */ + bool flushAsync(); }; } // namespace chre diff --git a/platform/include/chre/platform/tracing.h b/platform/include/chre/platform/tracing.h index fc94deb4..7a5559be 100644 --- a/platform/include/chre/platform/tracing.h +++ b/platform/include/chre/platform/tracing.h @@ -19,36 +19,90 @@ #include <cstdint> -/** - * @file - * Tracing support for CHRE. - * Platforms must supply an implementation of the trace functions. - */ +// Needs to be a number because it's used in STRINGIFY and as a number. +#define CHRE_TRACE_STR_BUFFER_SIZE 11 +// Strings are placed into a buffer in the form: +// {<1-byte str len>, str chars...}. +// So the max string size is always one less than the total string buffer size. +#define CHRE_TRACE_MAX_STRING_SIZE CHRE_TRACE_STR_BUFFER_SIZE - 1 -namespace chre { -/** - * Registers a nanoapp instance with the tracing infrastructure. - * - * @param instanceId instance ID of the nanoapp. - * @param name name of the nanoapp. - */ -void traceRegisterNanoapp(uint16_t instanceId, const char *name); +// TODO(b/301497381): See if netstruct lib would be more useful here +// Field values defined by python struct docs: +// https://docs.python.org/3/library/struct.html. +#define TRACE_BOOL "?" +#define TRACE_U8 "B" +#define TRACE_U16 "H" +#define TRACE_U32 "L" +#define TRACE_U64 "Q" +#define TRACE_I8 "b" +#define TRACE_I16 "h" +#define TRACE_I32 "l" +#define TRACE_I64 "q" +#define TRACE_C "c" +#define TRACE_S STRINGIFY(CHRE_TRACE_STR_BUFFER_SIZE) "p" -/** - * Marks the start of the nanoappHandleEvent function. - * - * @param instanceId instance ID of the nanoapp. - * @param eventType event being handled. - */ -void traceNanoappHandleEventStart(uint16_t instanceId, uint16_t eventType); +// Check to make sure pointer size macro is defined. +#ifndef __SIZEOF_POINTER__ +#error "__SIZEOF_POINTER__ macro not defined - unsupported toolchain being used" +#else +static_assert(sizeof(void *) == __SIZEOF_POINTER__, + "Size of pointer does not match __SIZEOF_POINTER__ macro"); +#endif + +// Check the predefined pointer size to use the most accurate size +#if __SIZEOF_POINTER__ == 8 +#define TRACE_PTR TRACE_U64 +#elif __SIZEOF_POINTER__ == 4 +#define TRACE_PTR TRACE_U32 +#else +#error "Pointer size is of unsupported size" +#endif // __SIZEOF_POINTER__ == 8 || __SIZEOF_POINTER__ == 4 + +#ifdef CHRE_TRACING_ENABLED + +#include "chre/target_platform/tracing.h" /** - * Marks the end of the nanoappHandleEvent function. - * - * @param instanceId instance ID of the nanoapp. + * All tracing macros to be used in CHRE */ -void traceNanoappHandleEventEnd(uint16_t instanceId); +#ifndef CHRE_TRACE_INSTANT +#error "CHRE_TRACE_INSTANT must be defined by chre/target_platform/tracing.h" +#endif + +#ifndef CHRE_TRACE_START +#error "CHRE_TRACE_START must be defined by chre/target_platform/tracing.h" +#endif + +#ifndef CHRE_TRACE_END +#error "CHRE_TRACE_END must be defined by chre/target_platform/tracing.h" +#endif + +#ifndef CHRE_TRACE_INSTANT_DATA +#error \ + "CHRE_TRACE_INSTANT_DATA must be defined by chre/target_platform/tracing.h" +#endif + +#ifndef CHRE_TRACE_START_DATA +#error "CHRE_TRACE_START_DATA must be defined by chre/target_platform/tracing.h" +#endif + +#ifndef CHRE_TRACE_END_DATA +#error "CHRE_TRACE_END_DATA must be defined by chre/target_platform/tracing.h" +#endif + +#else // CHRE_TRACING_ENABLED + +#include "chre/util/macros.h" + +inline void chreTraceUnusedParams(...) {} + +#define CHRE_TRACE_INSTANT(...) chreTraceUnusedParams(__VA_ARGS__) +#define CHRE_TRACE_START(...) chreTraceUnusedParams(__VA_ARGS__) +#define CHRE_TRACE_END(...) chreTraceUnusedParams(__VA_ARGS__) +#define CHRE_TRACE_INSTANT_DATA(...) chreTraceUnusedParams(__VA_ARGS__) +#define CHRE_TRACE_START_DATA(...) chreTraceUnusedParams(__VA_ARGS__) +#define CHRE_TRACE_END_DATA(...) chreTraceUnusedParams(__VA_ARGS__) -} // namespace chre +#endif // CHRE_TRACING_ENABLED #endif // CHRE_PLATFORM_TRACING_H_ diff --git a/platform/linux/include/chre/platform/linux/task_util/task.h b/platform/linux/include/chre/platform/linux/task_util/task.h index 858cfb74..698df3b5 100644 --- a/platform/linux/include/chre/platform/linux/task_util/task.h +++ b/platform/linux/include/chre/platform/linux/task_util/task.h @@ -28,7 +28,7 @@ namespace task_manager_internal { /** * Represents a task to execute (a function to call) that can be executed once - * or repeatedly with interval: repeatInterval in milliseconds until + * or repeatedly with interval: intervalOrDelay in nanoseconds until * cancel() is called. * * Note: The Task class is not thread-safe nor synchronized properly. It is @@ -51,12 +51,14 @@ class Task { * Construct a new Task object. * * @param func the function to execute. - * @param repeatInterval the interval in which to repeat execution in - * milliseconds. + * @param intervalOrDelay the interval in which to repeat execution or the + * delay for a one-shot Task. * @param id the unique ID for use with the Task Manager. + * @param isOneShot if true, the task should only be executed once + * after a delay of intervalOrDelay. */ - Task(const TaskFunction &func, std::chrono::milliseconds repeatInterval, - uint32_t id); + Task(const TaskFunction &func, std::chrono::nanoseconds intervalOrDelay, + uint32_t id, bool isOneShot = false); /** * Construct a new Task object. @@ -68,8 +70,8 @@ class Task { /** * Assignment operator. * - * @param rhs rhs arg. - * @return this. + * @param rhs rhs arg. + * @return this. */ Task &operator=(const Task &rhs); @@ -101,8 +103,8 @@ class Task { /** * Returns true if the task has executed at least once, false if otherwise. * - * @return true if the task has executed at least once. - * @return false if the task has not executed at least once. + * @return true if the task has executed at least once. + * @return false if the task has not executed at least once. */ inline bool hasExecuted() const { return mHasExecuted; @@ -112,8 +114,8 @@ class Task { * Returns true if the task is ready to execute (time now is >= task * timestamp). * - * @return true the task can be executed. - * @return false do not yet execute the task. + * @return true the task can be executed. + * @return false do not yet execute the task. */ inline bool isReadyToExecute() const { return mExecutionTimestamp <= std::chrono::steady_clock::now(); @@ -121,10 +123,10 @@ class Task { /** * Returns true if the task is a repeating task - if it has has a - * repeatInterval > 0. + * intervalOrDelay > 0. * - * @return true if the task is a repeating task. - * @return false otherwise. + * @return true if the task is a repeating task. + * @return false otherwise. */ inline bool isRepeating() const { return mRepeatInterval.count() > 0; @@ -158,7 +160,7 @@ class Task { /** * The amount of time to wait in between repeating the task. */ - std::chrono::milliseconds mRepeatInterval; + std::chrono::nanoseconds mRepeatInterval; /** * The function to execute. diff --git a/platform/linux/include/chre/platform/linux/task_util/task_manager.h b/platform/linux/include/chre/platform/linux/task_util/task_manager.h index 9710c39c..ad322216 100644 --- a/platform/linux/include/chre/platform/linux/task_util/task_manager.h +++ b/platform/linux/include/chre/platform/linux/task_util/task_manager.h @@ -51,24 +51,29 @@ class TaskManager : public NonCopyable { /** * Adds a task to the queue for execution. The manager calls the function func - * during execution. If repeatInterval > 0, the task will repeat every - * repeatInterval milliseconds. If repeatInterval == 0, the task will be - * executed only once. + * during execution. If intervalOrDelay > 0 and isOneShot is false, the task + * will repeat every intervalOrDelay nanoseconds. If intervalOrDelay is > 0 + * and isOneShot is true, the task will be executed only once after a delay of + * intervalOrDelay. If intervalOrDelay == 0, the task will be executed only + * once with no delay. * * @param func the function to call. - * @param repeatInterval the interval to repeat. + * @param intervalOrDelay the interval to repeat. + * @param isOneShot if true, the task should be executed only + * once with a delay of intervalOrDelay. * @return the ID of the Task object or an empty * Optional<> when there is an error. */ std::optional<uint32_t> addTask( const Task::TaskFunction &func, - std::chrono::milliseconds repeatInterval = std::chrono::milliseconds(0)); + std::chrono::nanoseconds intervalOrDelay = std::chrono::nanoseconds(0), + bool isOneShot = false); /** * Cancels the task with the taskId. * - * @param taskId the ID of the task. - * @return bool success. + * @param taskId the ID of the task. + * @return bool success. */ bool cancelTask(uint32_t taskId); diff --git a/platform/linux/include/chre/target_platform/assert.h b/platform/linux/include/chre/target_platform/assert.h new file mode 100644 index 00000000..76f2450f --- /dev/null +++ b/platform/linux/include/chre/target_platform/assert.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef PLATFORM_LINUX_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H +#define PLATFORM_LINUX_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H + +#include "chre/platform/shared/assert_func.h" + +#endif // PLATFORM_LINUX_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H diff --git a/platform/linux/pal_audio.cc b/platform/linux/pal_audio.cc index 3664c0d3..5816232c 100644 --- a/platform/linux/pal_audio.cc +++ b/platform/linux/pal_audio.cc @@ -31,7 +31,7 @@ */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalAudioCallbacks *gCallbacks = nullptr; @@ -45,6 +45,7 @@ bool gIsHandle0Enabled = false; void stopHandle0Task() { if (gHandle0TaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gHandle0TaskId.value()); + gHandle0TaskId.reset(); } } @@ -80,9 +81,6 @@ void sendHandle0Events(uint32_t numSamples) { static_cast<const uint8_t *>(chre::memoryAlloc(numSamples)); gCallbacks->audioDataEventCallback(data.release()); - - // Cancel the task so this is only run once with a delay. - TaskManagerSingleton::get()->cancelTask(gHandle0TaskId.value()); } bool chrePalAudioApiRequestAudioDataEvent(uint32_t handle, uint32_t numSamples, @@ -96,8 +94,7 @@ bool chrePalAudioApiRequestAudioDataEvent(uint32_t handle, uint32_t numSamples, gIsHandle0Enabled = true; gHandle0TaskId = TaskManagerSingleton::get()->addTask( [numSamples]() { sendHandle0Events(numSamples); }, - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::nanoseconds(eventDelayNs))); + std::chrono::nanoseconds(eventDelayNs), true /*isOneShot*/); if (!gHandle0TaskId.has_value()) { return false; } diff --git a/platform/linux/pal_ble.cc b/platform/linux/pal_ble.cc index bb4b144f..341caea6 100644 --- a/platform/linux/pal_ble.cc +++ b/platform/linux/pal_ble.cc @@ -24,23 +24,30 @@ #include <chrono> #include <optional> +#include <vector> /** * A simulated implementation of the BLE PAL for the linux platform. */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalBleCallbacks *gCallbacks = nullptr; bool gBleEnabled = false; bool gDelayScanStart = false; -std::chrono::milliseconds gScanInterval(1400); + +std::mutex gBatchMutex; +std::vector<struct chreBleAdvertisementEvent *> gBatchedAdEvents; +std::chrono::time_point<std::chrono::steady_clock> gLastAdDataTimestamp; +std::optional<uint32_t> gReportDelayMs; +std::chrono::nanoseconds gScanInterval = std::chrono::milliseconds(1400); // Tasks for startScan, sendAdReportEvents, and stopScan. std::optional<uint32_t> gBleAdReportEventTaskId; +std::optional<uint32_t> gBleFlushTaskId; void updateScanInterval(chreBleScanMode mode) { gScanInterval = std::chrono::milliseconds(1400); @@ -57,6 +64,15 @@ void updateScanInterval(chreBleScanMode mode) { } } +void flush() { + std::lock_guard<std::mutex> lock(gBatchMutex); + for (struct chreBleAdvertisementEvent *batchedEvent : gBatchedAdEvents) { + gCallbacks->advertisingEventCallback(batchedEvent); + } + gBatchedAdEvents.clear(); + gLastAdDataTimestamp = std::chrono::steady_clock::now(); +} + void sendAdReportEvents() { auto event = chre::MakeUniqueZeroFill<struct chreBleAdvertisementEvent>(); auto report = chre::MakeUniqueZeroFill<struct chreBleAdvertisingReport>(); @@ -69,22 +85,58 @@ void sendAdReportEvents() { report->dataLength = 2; event->reports = report.release(); event->numReports = 1; - gCallbacks->advertisingEventCallback(event.release()); + + std::lock_guard<std::mutex> lock(gBatchMutex); + if (!gReportDelayMs.has_value() || gReportDelayMs.value() == 0) { + gCallbacks->advertisingEventCallback(event.release()); + } else { + gBatchedAdEvents.push_back(event.release()); + } } void stopAllTasks() { if (gBleAdReportEventTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gBleAdReportEventTaskId.value()); + gBleAdReportEventTaskId.reset(); + } + + if (gBleFlushTaskId.has_value()) { + TaskManagerSingleton::get()->cancelTask(gBleFlushTaskId.value()); + gBleFlushTaskId.reset(); } } bool startScan() { stopAllTasks(); - gCallbacks->scanStatusChangeCallback(true, CHRE_ERROR_NONE); - gBleEnabled = true; + + std::lock_guard<std::mutex> lock(gBatchMutex); + gLastAdDataTimestamp = std::chrono::steady_clock::now(); + gBleAdReportEventTaskId = TaskManagerSingleton::get()->addTask(sendAdReportEvents, gScanInterval); - return gBleAdReportEventTaskId.has_value(); + if (!gBleAdReportEventTaskId.has_value()) { + return false; + } + + if (gReportDelayMs.has_value()) { + gBleFlushTaskId = TaskManagerSingleton::get()->addTask( + flush, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(gReportDelayMs.value()))); + if (!gBleFlushTaskId.has_value()) { + stopAllTasks(); + return false; + } + } + + std::optional<uint32_t> callbackTaskId = TaskManagerSingleton::get()->addTask( + []() { gCallbacks->scanStatusChangeCallback(true, CHRE_ERROR_NONE); }); + if (!callbackTaskId.has_value()) { + stopAllTasks(); + return false; + } + + gBleEnabled = true; + return true; } uint32_t chrePalBleGetCapabilities() { @@ -98,20 +150,33 @@ uint32_t chrePalBleGetFilterCapabilities() { CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA; } -bool chrePalBleStartScan(chreBleScanMode mode, uint32_t /* reportDelayMs */, - const struct chreBleScanFilter * /* filter */) { - updateScanInterval(mode); - if (gDelayScanStart) { - return true; +bool chrePalBleStartScan(chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilterV1_9 * /* filter */) { + { + std::lock_guard<std::mutex> lock(gBatchMutex); + + if (gReportDelayMs.has_value()) { + gReportDelayMs = std::min(gReportDelayMs.value(), reportDelayMs); + } else { + gReportDelayMs = reportDelayMs; + } } - return startScan(); + + updateScanInterval(mode); + flush(); + return gDelayScanStart || startScan(); } bool chrePalBleStopScan() { stopAllTasks(); - gCallbacks->scanStatusChangeCallback(false, CHRE_ERROR_NONE); - gBleEnabled = false; - return true; + flush(); + + std::optional<uint32_t> callbackTaskId = TaskManagerSingleton::get()->addTask( + []() { gCallbacks->scanStatusChangeCallback(false, CHRE_ERROR_NONE); }); + + // If the callback is successfully scheduled, then BLE is disabled. + gBleEnabled = !callbackTaskId.has_value(); + return callbackTaskId.has_value(); } void chrePalBleReleaseAdvertisingEvent( @@ -125,12 +190,34 @@ void chrePalBleReleaseAdvertisingEvent( } bool chrePalBleReadRssi(uint16_t connectionHandle) { - gCallbacks->readRssiCallback(CHRE_ERROR_NONE, connectionHandle, -65); - return true; + std::optional<uint32_t> readRssiTaskId = + TaskManagerSingleton::get()->addTask([connectionHandle]() { + gCallbacks->readRssiCallback(CHRE_ERROR_NONE, connectionHandle, -65); + }); + + return readRssiTaskId.has_value(); +} + +bool chrePalBleFlush() { + std::optional<uint32_t> flushTaskId = + TaskManagerSingleton::get()->addTask([]() { + flush(); + gCallbacks->flushCallback(CHRE_ERROR_NONE); + }); + + return flushTaskId.has_value(); } void chrePalBleApiClose() { stopAllTasks(); + + { + std::lock_guard<std::mutex> lock(gBatchMutex); + + for (struct chreBleAdvertisementEvent *batchedEvent : gBatchedAdEvents) { + chrePalBleReleaseAdvertisingEvent(batchedEvent); + } + } } bool chrePalBleApiOpen(const struct chrePalSystemApi *systemApi, @@ -172,6 +259,7 @@ const struct chrePalBleApi *chrePalBleGetApi(uint32_t requestedApiVersion) { .stopScan = chrePalBleStopScan, .releaseAdvertisingEvent = chrePalBleReleaseAdvertisingEvent, .readRssi = chrePalBleReadRssi, + .flush = chrePalBleFlush, }; if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(kApi.moduleVersion, diff --git a/platform/linux/pal_gnss.cc b/platform/linux/pal_gnss.cc index ebb40b51..ae78a648 100644 --- a/platform/linux/pal_gnss.cc +++ b/platform/linux/pal_gnss.cc @@ -32,7 +32,7 @@ */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalGnssCallbacks *gCallbacks = nullptr; @@ -40,7 +40,6 @@ const struct chrePalGnssCallbacks *gCallbacks = nullptr; // Task to deliver asynchronous location data after a CHRE request. std::mutex gLocationEventsMutex; std::optional<uint32_t> gLocationEventsTaskId; -std::optional<uint32_t> gLocationEventsChangeCallbackTaskId; uint32_t gLocationEventsMinIntervalMs = 0; bool gDelaySendingLocationEvents = false; bool gIsLocationEnabled = false; @@ -49,7 +48,6 @@ bool gIsLocationEnabled = false; std::optional<uint32_t> gLocationStatusTaskId; // Task to deliver asynchronous measurement data after a CHRE request. -std::optional<uint32_t> gMeasurementEventsChangeCallbackTaskId; std::optional<uint32_t> gMeasurementEventsTaskId; bool gIsMeasurementEnabled = false; @@ -73,11 +71,12 @@ void startSendingLocationEvents(uint32_t minIntervalMs) { std::lock_guard<std::mutex> lock(gLocationEventsMutex); if (gLocationEventsTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gLocationEventsTaskId.value()); + gLocationEventsTaskId.reset(); } - gLocationEventsChangeCallbackTaskId = TaskManagerSingleton::get()->addTask( + TaskManagerSingleton::get()->addTask( []() { gCallbacks->locationStatusChangeCallback(true, CHRE_ERROR_NONE); }, - std::chrono::milliseconds(0)); + std::chrono::nanoseconds(0), true /* isOneShot */); gLocationEventsTaskId = TaskManagerSingleton::get()->addTask( sendLocationEvents, std::chrono::milliseconds(minIntervalMs)); @@ -108,33 +107,28 @@ void stopMeasurement() { void stopLocationTasks() { { std::lock_guard<std::mutex> lock(gLocationEventsMutex); - if (gLocationEventsChangeCallbackTaskId.has_value()) { - TaskManagerSingleton::get()->cancelTask( - gLocationEventsChangeCallbackTaskId.value()); - } if (gLocationEventsTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gLocationEventsTaskId.value()); + gLocationEventsTaskId.reset(); } } if (gLocationStatusTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gLocationStatusTaskId.value()); + gLocationStatusTaskId.reset(); } } void stopMeasurementTasks() { - if (gMeasurementEventsChangeCallbackTaskId.has_value()) { - TaskManagerSingleton::get()->cancelTask( - gMeasurementEventsChangeCallbackTaskId.value()); - } - if (gMeasurementEventsTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gMeasurementEventsTaskId.value()); + gMeasurementEventsTaskId.reset(); } if (gMeasurementStatusTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gMeasurementStatusTaskId.value()); + gMeasurementStatusTaskId.reset(); } } @@ -150,8 +144,7 @@ bool chrePalControlLocationSession(bool enable, uint32_t minIntervalMs, gLocationEventsMinIntervalMs = minIntervalMs; if (enable && !gDelaySendingLocationEvents) { startSendingLocationEvents(minIntervalMs); - if (!gLocationEventsChangeCallbackTaskId.has_value() || - !gLocationEventsTaskId.has_value()) { + if (!gLocationEventsTaskId.has_value()) { return false; } } else if (!enable) { @@ -173,14 +166,14 @@ bool chrePalControlMeasurementSession(bool enable, uint32_t minIntervalMs) { stopMeasurementTasks(); if (enable) { - gMeasurementEventsChangeCallbackTaskId = + std::optional<uint32_t> measurementEventsChangeCallbackTaskId = TaskManagerSingleton::get()->addTask( []() { gCallbacks->measurementStatusChangeCallback(true, CHRE_ERROR_NONE); }, - std::chrono::milliseconds(0)); - if (!gMeasurementEventsChangeCallbackTaskId.has_value()) { + std::chrono::nanoseconds(0), true /* isOneShot */); + if (!measurementEventsChangeCallbackTaskId.has_value()) { return false; } diff --git a/platform/linux/pal_sensor.cc b/platform/linux/pal_sensor.cc index d4df5d35..2fb2bf28 100644 --- a/platform/linux/pal_sensor.cc +++ b/platform/linux/pal_sensor.cc @@ -31,7 +31,7 @@ */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalSensorCallbacks *gCallbacks = nullptr; @@ -57,6 +57,7 @@ bool gIsSensor0Enabled = false; void stopSensor0Task() { if (gSensor0TaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gSensor0TaskId.value()); + gSensor0TaskId.reset(); } } @@ -127,9 +128,7 @@ bool chrePalSensorApiConfigureSensor(uint32_t sensorInfoIndex, gIsSensor0Enabled = true; sendSensor0StatusUpdate(intervalNs, true /*enabled*/); gSensor0TaskId = TaskManagerSingleton::get()->addTask( - sendSensor0Events, - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::nanoseconds(intervalNs))); + sendSensor0Events, std::chrono::nanoseconds(intervalNs)); return gSensor0TaskId.has_value(); } diff --git a/platform/linux/pal_wifi.cc b/platform/linux/pal_wifi.cc index a28205c2..7013b893 100644 --- a/platform/linux/pal_wifi.cc +++ b/platform/linux/pal_wifi.cc @@ -34,7 +34,7 @@ */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalWifiCallbacks *gCallbacks = nullptr; @@ -51,6 +51,9 @@ std::atomic_bool gEnableScanMonitorResponse(true); //! Whether PAL should respond to scan request. std::atomic_bool gEnableScanResponse(true); +//! Thread sync variable for TaskIds. +std::mutex gRequestScanMutex; + //! Task IDs for the scanning tasks std::optional<uint32_t> gScanMonitorTaskId; std::optional<uint32_t> gRequestScanTaskId; @@ -58,10 +61,19 @@ std::optional<uint32_t> gRequestRangingTaskId; //! How long should each the PAL hold before response. //! Use to mimic real world hardware process time. -std::chrono::milliseconds gAsyncRequestDelayResponseTime[chre::asBaseType( +std::chrono::nanoseconds gAsyncRequestDelayResponseTime[chre::asBaseType( PalWifiAsyncRequestTypes::NUM_WIFI_REQUEST_TYPE)]; void sendScanResponse() { + { + std::lock_guard<std::mutex> lock(gRequestScanMutex); + if (!gRequestScanTaskId.has_value()) { + LOGE("Sending scan response with no pending task"); + return; + } + gRequestScanTaskId.reset(); + } + if (gEnableScanResponse) { auto event = chre::MakeUniqueZeroFill<struct chreWifiScanEvent>(); auto result = chre::MakeUniqueZeroFill<struct chreWifiScanResult>(); @@ -71,9 +83,6 @@ void sendScanResponse() { event->results = result.release(); gCallbacks->scanEventCallback(event.release()); } - - // We just want to delay this task - only execute it once. - TaskManagerSingleton::get()->cancelTask(gRequestScanTaskId.value()); } void sendScanMonitorResponse(bool enable) { @@ -95,18 +104,21 @@ void sendRangingResponse() { void stopScanMonitorTask() { if (gScanMonitorTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gScanMonitorTaskId.value()); + gScanMonitorTaskId.reset(); } } void stopRequestScanTask() { if (gRequestScanTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gRequestScanTaskId.value()); + gRequestScanTaskId.reset(); } } void stopRequestRangingTask() { if (gRequestRangingTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(gRequestRangingTaskId.value()); + gRequestRangingTaskId.reset(); } } @@ -125,7 +137,11 @@ bool chrePalWifiConfigureScanMonitor(bool enable) { } bool chrePalWifiApiRequestScan(const struct chreWifiScanParams * /* params */) { - stopRequestScanTask(); + std::lock_guard<std::mutex> lock(gRequestScanMutex); + if (gRequestScanTaskId.has_value()) { + LOGE("Requesting scan when existing scan request still in process"); + return false; + } std::optional<uint32_t> requestScanTaskCallbackId = TaskManagerSingleton::get()->addTask([]() { @@ -135,8 +151,10 @@ bool chrePalWifiApiRequestScan(const struct chreWifiScanParams * /* params */) { }); if (requestScanTaskCallbackId.has_value()) { gRequestScanTaskId = TaskManagerSingleton::get()->addTask( - sendScanResponse, gAsyncRequestDelayResponseTime[chre::asBaseType( - PalWifiAsyncRequestTypes::SCAN)]); + sendScanResponse, + gAsyncRequestDelayResponseTime[chre::asBaseType( + PalWifiAsyncRequestTypes::SCAN)], + /* isOneShot= */ true); return gRequestScanTaskId.has_value(); } return false; @@ -257,7 +275,7 @@ bool chrePalWifiIsScanMonitoringActive() { void chrePalWifiDelayResponse(PalWifiAsyncRequestTypes requestType, std::chrono::seconds seconds) { gAsyncRequestDelayResponseTime[chre::asBaseType(requestType)] = - std::chrono::duration_cast<std::chrono::milliseconds>(seconds); + std::chrono::duration_cast<std::chrono::nanoseconds>(seconds); } const struct chrePalWifiApi *chrePalWifiGetApi(uint32_t requestedApiVersion) { diff --git a/platform/linux/pal_wwan.cc b/platform/linux/pal_wwan.cc index b7a488c0..64e384c7 100644 --- a/platform/linux/pal_wwan.cc +++ b/platform/linux/pal_wwan.cc @@ -29,7 +29,7 @@ */ namespace { -using chre::TaskManagerSingleton; +using ::chre::TaskManagerSingleton; const struct chrePalSystemApi *gSystemApi = nullptr; const struct chrePalWwanCallbacks *gCallbacks = nullptr; @@ -63,6 +63,7 @@ void sendCellInfoResult() { void stopCellInfoTask() { if (gCellInfosTaskId.has_value()) { TaskManagerSingleton::get()->cancelTask(*gCellInfosTaskId); + gCellInfosTaskId.reset(); } } diff --git a/platform/linux/task_util/task.cc b/platform/linux/task_util/task.cc index f224c107..7b50f8d8 100644 --- a/platform/linux/task_util/task.cc +++ b/platform/linux/task_util/task.cc @@ -19,16 +19,14 @@ namespace chre { namespace task_manager_internal { -Task::Task(const TaskFunction &func, std::chrono::milliseconds repeatInterval, - uint32_t id) - : mExecutionTimestamp(std::chrono::steady_clock::now() + repeatInterval), - mRepeatInterval(repeatInterval), +Task::Task(const TaskFunction &func, std::chrono::nanoseconds intervalOrDelay, + uint32_t id, bool isOneShot) + : mExecutionTimestamp(std::chrono::steady_clock::now() + intervalOrDelay), + mRepeatInterval(isOneShot ? std::chrono::nanoseconds(0) + : intervalOrDelay), + mFunc(func), mId(id), - mHasExecuted(false) { - if (func != nullptr) { - mFunc = func; - } -} + mHasExecuted(false) {} Task::Task(const Task &rhs) : mExecutionTimestamp(rhs.mExecutionTimestamp), @@ -56,7 +54,7 @@ Task &Task::operator=(const Task &rhs) { void Task::cancel() { std::lock_guard lock(mExecutionMutex); - mRepeatInterval = std::chrono::milliseconds(0); + mRepeatInterval = std::chrono::nanoseconds(0); mFunc.reset(); } diff --git a/platform/linux/task_util/task_manager.cc b/platform/linux/task_util/task_manager.cc index b2130b08..22bfad7b 100644 --- a/platform/linux/task_util/task_manager.cc +++ b/platform/linux/task_util/task_manager.cc @@ -46,7 +46,8 @@ TaskManager::~TaskManager() { } std::optional<uint32_t> TaskManager::addTask( - const Task::TaskFunction &func, std::chrono::milliseconds repeatInterval) { + const Task::TaskFunction &func, std::chrono::nanoseconds intervalOrDelay, + bool isOneShot) { std::lock_guard<std::mutex> lock(mMutex); bool success = false; @@ -57,7 +58,7 @@ std::optional<uint32_t> TaskManager::addTask( // select the next ID assert(mCurrentId < std::numeric_limits<uint32_t>::max()); returnId = mCurrentId++; - Task task(func, repeatInterval, returnId); + Task task(func, intervalOrDelay, returnId, isOneShot); success = mQueue.push(task); } diff --git a/platform/linux/tests/task_manager_test.cc b/platform/linux/tests/task_manager_test.cc index 6f52579e..28c0a8ef 100644 --- a/platform/linux/tests/task_manager_test.cc +++ b/platform/linux/tests/task_manager_test.cc @@ -16,8 +16,8 @@ #include <chrono> #include <cmath> +#include <mutex> #include <optional> -#include <thread> #include "gtest/gtest.h" @@ -25,70 +25,110 @@ namespace { -uint32_t gVarTaskManager = 0; -uint32_t gTask1Var = 0; -uint32_t gTask2Var = 0; - -constexpr auto incrementGVar = []() { ++gVarTaskManager; }; -constexpr auto task1Func = []() { ++gTask1Var; }; -constexpr auto task2Func = []() { ++gTask2Var; }; - -TEST(TaskManager, FlushTasks) { +TEST(TaskManager, FlushTasksCanBeCalledMultipleTimes) { chre::TaskManager taskManager; - for (uint32_t i = 0; i < 50; ++i) { + + constexpr uint32_t numCallsToFlush = 50; + for (uint32_t i = 0; i < numCallsToFlush; ++i) { taskManager.flushTasks(); } } -TEST(TaskManager, MultipleNonRepeatingTasks) { +TEST(TaskManager, MultipleNonRepeatingTasksAreExecuted) { + uint32_t counter = 0; + std::mutex mutex; + std::condition_variable condVar; chre::TaskManager taskManager; - gVarTaskManager = 0; + constexpr uint32_t numTasks = 50; + auto incrementFunc = [&mutex, &condVar, &counter]() { + { + std::unique_lock<std::mutex> lock(mutex); + ++counter; + } + + condVar.notify_all(); + }; for (uint32_t i = 0; i < numTasks; ++i) { - taskManager.addTask(incrementGVar, std::chrono::milliseconds(0)); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::optional<uint32_t> taskId = + taskManager.addTask(incrementFunc, + /* intervalOrDelay */ std::chrono::nanoseconds(0)); + EXPECT_TRUE(taskId.has_value()); } + + std::unique_lock<std::mutex> lock(mutex); + condVar.wait(lock, [&counter]() { return counter >= numTasks; }); taskManager.flushTasks(); - EXPECT_TRUE(gVarTaskManager == numTasks); + EXPECT_EQ(counter, numTasks); } -TEST(TaskManager, MultipleTypesOfTasks) { +TEST(TaskManager, RepeatingAndOneShotTasksCanExecuteTogether) { + uint32_t counter = 0; + std::mutex mutex; + std::condition_variable condVar; chre::TaskManager taskManager; - gVarTaskManager = 0; + constexpr uint32_t numTasks = 50; + auto incrementFunc = [&mutex, &condVar, &counter]() { + { + std::unique_lock<std::mutex> lock(mutex); + ++counter; + } + + condVar.notify_all(); + }; for (uint32_t i = 0; i < numTasks; ++i) { - taskManager.addTask(incrementGVar, std::chrono::milliseconds(0)); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::optional<uint32_t> taskId = + taskManager.addTask(incrementFunc, + /* intervalOrDelay */ std::chrono::nanoseconds(0)); + EXPECT_TRUE(taskId.has_value()); } - uint32_t millisecondsToRepeat = 100; - std::optional<uint32_t> taskId = taskManager.addTask( - incrementGVar, std::chrono::milliseconds(millisecondsToRepeat)); - EXPECT_TRUE(taskId.has_value()); - uint32_t taskRepeatTimesMax = 11; - std::this_thread::sleep_for( - std::chrono::milliseconds(millisecondsToRepeat * taskRepeatTimesMax)); + + constexpr std::chrono::nanoseconds interval(50); + std::optional<uint32_t> taskId = taskManager.addTask(incrementFunc, interval); + ASSERT_TRUE(taskId.has_value()); + + constexpr uint32_t taskRepeatTimesMax = 5; + std::unique_lock<std::mutex> lock(mutex); + condVar.wait( + lock, [&counter]() { return counter >= numTasks + taskRepeatTimesMax; }); EXPECT_TRUE(taskManager.cancelTask(taskId.value())); taskManager.flushTasks(); - EXPECT_TRUE(gVarTaskManager >= numTasks + taskRepeatTimesMax - 1); + EXPECT_GE(counter, numTasks + taskRepeatTimesMax); } -TEST(TaskManager, FlushTasksWithoutCancel) { +TEST(TaskManager, TasksCanBeFlushedEvenIfNotCancelled) { + uint32_t counter = 0; + std::mutex mutex; + std::condition_variable condVar; chre::TaskManager taskManager; - gVarTaskManager = 0; + constexpr uint32_t numTasks = 50; + auto incrementFunc = [&mutex, &condVar, &counter]() { + { + std::unique_lock<std::mutex> lock(mutex); + ++counter; + } + + condVar.notify_all(); + }; for (uint32_t i = 0; i < numTasks; ++i) { - taskManager.addTask(incrementGVar, std::chrono::milliseconds(0)); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::optional<uint32_t> taskId = + taskManager.addTask(incrementFunc, + /* intervalOrDelay */ std::chrono::nanoseconds(0)); + EXPECT_TRUE(taskId.has_value()); } - uint32_t millisecondsToRepeat = 100; - std::optional<uint32_t> taskId = taskManager.addTask( - incrementGVar, std::chrono::milliseconds(millisecondsToRepeat)); - EXPECT_TRUE(taskId.has_value()); - uint32_t taskRepeatTimesMax = 11; - std::this_thread::sleep_for( - std::chrono::milliseconds(millisecondsToRepeat * taskRepeatTimesMax)); + + constexpr std::chrono::nanoseconds interval(50); + std::optional<uint32_t> taskId = taskManager.addTask(incrementFunc, interval); + ASSERT_TRUE(taskId.has_value()); + + constexpr uint32_t taskRepeatTimesMax = 5; + std::unique_lock<std::mutex> lock(mutex); + condVar.wait( + lock, [&counter]() { return counter >= numTasks + taskRepeatTimesMax; }); taskManager.flushTasks(); - EXPECT_TRUE(gVarTaskManager >= numTasks + taskRepeatTimesMax - 1); + EXPECT_GE(counter, numTasks + taskRepeatTimesMax); } } // namespace diff --git a/platform/linux/tests/task_test.cc b/platform/linux/tests/task_test.cc index f20823dc..ed4656a5 100644 --- a/platform/linux/tests/task_test.cc +++ b/platform/linux/tests/task_test.cc @@ -31,7 +31,7 @@ constexpr auto incrementGVar = []() { ++gVarTask; }; TEST(Task, Execute) { gVarTask = 0; - std::chrono::milliseconds waitTime(1000); + std::chrono::milliseconds waitTime(100); Task task(incrementGVar, waitTime, 0); EXPECT_FALSE(task.isReadyToExecute()); std::this_thread::sleep_for(waitTime); @@ -43,7 +43,7 @@ TEST(Task, Execute) { auto timeDiff = std::chrono::steady_clock::now() - task.getExecutionTimestamp(); EXPECT_TRUE( - std::chrono::duration_cast<std::chrono::milliseconds>(timeDiff).count() <= + std::chrono::duration_cast<std::chrono::nanoseconds>(timeDiff).count() <= waitTime.count()); task.cancel(); EXPECT_FALSE(task.isRepeating()); @@ -51,7 +51,7 @@ TEST(Task, Execute) { TEST(Task, ExecuteNoRepeat) { gVarTask = 0; - std::chrono::milliseconds waitTime(0); + std::chrono::nanoseconds waitTime(0); Task task(incrementGVar, waitTime, 0); EXPECT_TRUE(task.isReadyToExecute()); task.execute(); @@ -62,12 +62,12 @@ TEST(Task, ExecuteNoRepeat) { TEST(Task, ComparisonOperators) { constexpr uint32_t numTasks = 6; - Task tasks[numTasks] = {Task(incrementGVar, std::chrono::milliseconds(0), 0), - Task(incrementGVar, std::chrono::milliseconds(1), 1), - Task(incrementGVar, std::chrono::milliseconds(2), 2), - Task(incrementGVar, std::chrono::milliseconds(3), 3), - Task(incrementGVar, std::chrono::milliseconds(4), 4), - Task(incrementGVar, std::chrono::milliseconds(5), 5)}; + Task tasks[numTasks] = {Task(incrementGVar, std::chrono::nanoseconds(0), 0), + Task(incrementGVar, std::chrono::nanoseconds(10), 1), + Task(incrementGVar, std::chrono::nanoseconds(20), 2), + Task(incrementGVar, std::chrono::nanoseconds(30), 3), + Task(incrementGVar, std::chrono::nanoseconds(40), 4), + Task(incrementGVar, std::chrono::nanoseconds(50), 5)}; for (uint32_t i = 0; i < numTasks; ++i) { if (i < numTasks - 1) { diff --git a/platform/platform.mk b/platform/platform.mk index 7e5046f8..5212c6d0 100644 --- a/platform/platform.mk +++ b/platform/platform.mk @@ -110,7 +110,6 @@ SLPI_SRCS += platform/shared/nanoapp/nanoapp_dso_util.cc SLPI_SRCS += platform/shared/pal_system_api.cc SLPI_SRCS += platform/shared/platform_debug_dump_manager.cc SLPI_SRCS += platform/shared/system_time.cc -SLPI_SRCS += platform/shared/tracing.cc SLPI_SRCS += platform/shared/version.cc SLPI_SRCS += platform/slpi/chre_api_re.cc SLPI_SRCS += platform/slpi/fatal_error.cc @@ -227,7 +226,6 @@ SIM_SRCS += platform/shared/memory_manager.cc SIM_SRCS += platform/shared/nanoapp/nanoapp_dso_util.cc SIM_SRCS += platform/shared/pal_system_api.cc SIM_SRCS += platform/shared/system_time.cc -SIM_SRCS += platform/shared/tracing.cc SIM_SRCS += platform/shared/version.cc # Optional audio support. @@ -336,11 +334,14 @@ endif # GoogleTest Compiler Flags #################################################### +GOOGLETEST_CFLAGS += $(FLATBUFFERS_CFLAGS) + # The order here is important so that the googletest target prefers shared, # linux and then SLPI. GOOGLETEST_CFLAGS += -Iplatform/shared/include GOOGLETEST_CFLAGS += -Iplatform/linux/include GOOGLETEST_CFLAGS += -Iplatform/slpi/include +GOOGLETEST_CFLAGS += -Iplatform/shared/pw_trace/include # GoogleTest Source Files ###################################################### @@ -350,6 +351,7 @@ GOOGLETEST_COMMON_SRCS += platform/linux/sim/platform_audio.cc GOOGLETEST_COMMON_SRCS += platform/linux/tests/task_test.cc GOOGLETEST_COMMON_SRCS += platform/linux/tests/task_manager_test.cc GOOGLETEST_COMMON_SRCS += platform/tests/log_buffer_test.cc +GOOGLETEST_COMMON_SRCS += platform/tests/trace_test.cc GOOGLETEST_COMMON_SRCS += platform/shared/log_buffer.cc ifeq ($(CHRE_WIFI_NAN_SUPPORT_ENABLED), true) GOOGLETEST_COMMON_SRCS += platform/linux/pal_nan.cc @@ -391,7 +393,6 @@ EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/pal_system_api.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/pal_sensor_stub.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/platform_debug_dump_manager.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/system_time.cc -EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/tracing.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/version.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/nanoapp/nanoapp_dso_util.cc EMBOS_SRCS += $(CHRE_PREFIX)/platform/shared/nanoapp_loader.cc @@ -481,7 +482,6 @@ TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/nanoapp_loader.cc TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/pal_system_api.cc TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/platform_debug_dump_manager.cc TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/system_time.cc -TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/tracing.cc TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/version.cc TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/nanoapp/nanoapp_dso_util.cc TINYSYS_SRCS += $(MBEDTLS_SRCS) diff --git a/platform/shared/audio_pal/platform_audio.cc b/platform/shared/audio_pal/platform_audio.cc index 4662a93c..e68ff361 100644 --- a/platform/shared/audio_pal/platform_audio.cc +++ b/platform/shared/audio_pal/platform_audio.cc @@ -48,6 +48,12 @@ void PlatformAudio::init() { if (mApi != nullptr) { if (!mApi->open(&gChrePalSystemApi, &sCallbacks)) { LOGE("Audio PAL open returned false"); + +#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED + EventLoopManagerSingleton::get()->getTelemetryManager().onPalOpenFailure( + TelemetryManager::PalType::AUDIO); +#endif // CHRE_TELEMETRY_SUPPORT_ENABLED + mApi = nullptr; } else { LOGD("Opened audio PAL version 0x%08" PRIx32, mApi->moduleVersion); diff --git a/platform/shared/chre_api_ble.cc b/platform/shared/chre_api_ble.cc index 5b725b17..1f630174 100644 --- a/platform/shared/chre_api_ble.cc +++ b/platform/shared/chre_api_ble.cc @@ -44,38 +44,67 @@ DLL_EXPORT uint32_t chreBleGetFilterCapabilities() { #endif // CHRE_BLE_SUPPORT_ENABLED } -DLL_EXPORT bool chreBleFlushAsync(const void * /* cookie */) { +DLL_EXPORT bool chreBleFlushAsync(const void *cookie) { +#ifdef CHRE_BLE_SUPPORT_ENABLED + chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__); + return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) && + EventLoopManagerSingleton::get()->getBleRequestManager().flushAsync( + nanoapp, cookie); +#else + UNUSED_VAR(cookie); return false; +#endif // CHRE_BLE_SUPPORT_ENABLED } -DLL_EXPORT bool chreBleStartScanAsync(chreBleScanMode mode, - uint32_t reportDelayMs, - const struct chreBleScanFilter *filter) { +DLL_EXPORT bool chreBleStartScanAsyncV1_9( + chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilterV1_9 *filter, const void *cookie) { #ifdef CHRE_BLE_SUPPORT_ENABLED chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__); return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) && EventLoopManagerSingleton::get() ->getBleRequestManager() - .startScanAsync(nanoapp, mode, reportDelayMs, filter); + .startScanAsync(nanoapp, mode, reportDelayMs, filter, cookie); #else UNUSED_VAR(mode); UNUSED_VAR(reportDelayMs); UNUSED_VAR(filter); + UNUSED_VAR(cookie); return false; #endif // CHRE_BLE_SUPPORT_ENABLED } -DLL_EXPORT bool chreBleStopScanAsync() { +DLL_EXPORT bool chreBleStartScanAsync(chreBleScanMode mode, + uint32_t reportDelayMs, + const struct chreBleScanFilter *filter) { + if (filter == nullptr) { + return chreBleStartScanAsyncV1_9(mode, reportDelayMs, nullptr /* filter */, + nullptr /* cookie */); + } + chreBleScanFilterV1_9 filterV1_9 = { + filter->rssiThreshold, filter->scanFilterCount, filter->scanFilters, + 0 /* broadcasterAddressFilterCount */, + nullptr /* broadcasterAddressFilters */}; + return chreBleStartScanAsyncV1_9(mode, reportDelayMs, &filterV1_9, + nullptr /* cookie */); +} + +DLL_EXPORT bool chreBleStopScanAsyncV1_9(const void *cookie) { #ifdef CHRE_BLE_SUPPORT_ENABLED chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__); return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) && EventLoopManagerSingleton::get()->getBleRequestManager().stopScanAsync( - nanoapp); + nanoapp, cookie); #else + UNUSED_VAR(cookie); return false; #endif // CHRE_BLE_SUPPORT_ENABLED } +DLL_EXPORT bool chreBleStopScanAsync() { + return chreBleStopScanAsyncV1_9(nullptr /* cookie */); +} + DLL_EXPORT bool chreBleReadRssiAsync(uint16_t connectionHandle, const void *cookie) { #ifdef CHRE_BLE_READ_RSSI_SUPPORT_ENABLED diff --git a/platform/shared/dram_vote_client.cc b/platform/shared/dram_vote_client.cc index 6cc484c4..165faf4d 100644 --- a/platform/shared/dram_vote_client.cc +++ b/platform/shared/dram_vote_client.cc @@ -18,6 +18,7 @@ #include <cinttypes> +#include "chre/core/event_loop_manager.h" #include "chre/platform/assert.h" #include "chre/platform/fatal_error.h" #include "chre/platform/log.h" @@ -77,7 +78,7 @@ void DramVoteClient::decrementDramVoteCount() { } } -Milliseconds DramVoteClient::checkDramDuration() const { +Milliseconds DramVoteClient::checkDramDuration() { Milliseconds duration(0); if (mDramVoteCount > 0) { duration = Milliseconds(SystemTime::getMonotonicTime()) - mVoteCountStart; @@ -87,8 +88,18 @@ Milliseconds DramVoteClient::checkDramDuration() const { // requests. If there's a prolonged period of memory fallback, this might // indicate a memory leak or inadequate SRAM heap size. if (duration > kMaxDramDuration) { - FATAL_ERROR("Forced into DRAM for %" PRIu64 " msec", - duration.getMilliseconds()); + if (EventLoopManagerSingleton::isInitialized() && + !EventLoopManagerSingleton::get() + ->getEventLoop() + .getPowerControlManager() + .hostIsAwake()) { + // AP is asleep + FATAL_ERROR("Forced into DRAM for %" PRIu64 " msec", + duration.getMilliseconds()); + } else { + // AP is awake, don't report error, just reset the starting time + mVoteCountStart = Milliseconds(SystemTime::getMonotonicTime()); + } } return duration; } diff --git a/platform/shared/host_link.cc b/platform/shared/host_link.cc index a66b7972..2542055c 100644 --- a/platform/shared/host_link.cc +++ b/platform/shared/host_link.cc @@ -104,6 +104,15 @@ void HostMessageHandlers::loadNanoappData( cbData->nanoapp = getLoadManager().releaseNanoapp(); cbData->sendFragmentResponse = !respondBeforeStart; + LOGD("Instance ID %" PRIu16 " assigned to app ID 0x%" PRIx64, + cbData->nanoapp->getInstanceId(), appId); + + // This message must be sent to the host before the nanoapp is started so + // that the host can correctly map the nanoapp instance ID to the app ID + // before processing tokenized logs from the nanoapp. + sendNanoappInstanceIdInfo(hostClientId, cbData->nanoapp->getInstanceId(), + appId); + // Note that if this fails, we'll generate the error response in // the normal deferred callback EventLoopManagerSingleton::get()->deferCallback( diff --git a/platform/shared/host_protocol_chre.cc b/platform/shared/host_protocol_chre.cc index d7b7feac..627a5a6a 100644 --- a/platform/shared/host_protocol_chre.cc +++ b/platform/shared/host_protocol_chre.cc @@ -186,6 +186,11 @@ bool HostProtocolChre::decodeMessageFromHost(const void *message, break; } + case fbs::ChreMessage::PulseRequest: { + HostMessageHandlers::handlePulseRequest(); + break; + } + default: LOGW("Got invalid/unexpected message type %" PRIu8, static_cast<uint8_t>(container->message_type())); @@ -251,6 +256,11 @@ void HostProtocolChre::finishNanoappListResponse( hostClientId); } +void HostProtocolChre::encodePulseResponse(ChreFlatBufferBuilder &builder) { + auto response = fbs::CreatePulseResponse(builder); + finalize(builder, fbs::ChreMessage::PulseResponse, response.Union()); +} + void HostProtocolChre::encodeLoadNanoappResponse(ChreFlatBufferBuilder &builder, uint16_t hostClientId, uint32_t transactionId, @@ -262,6 +272,14 @@ void HostProtocolChre::encodeLoadNanoappResponse(ChreFlatBufferBuilder &builder, hostClientId); } +void HostProtocolChre::encodeNanoappInstanceIdInfo( + ChreFlatBufferBuilder &builder, uint16_t hostClientId, uint16_t instanceId, + uint64_t appId) { + auto response = fbs::CreateNanoappInstanceIdInfo(builder, instanceId, appId); + finalize(builder, fbs::ChreMessage::NanoappInstanceIdInfo, response.Union(), + hostClientId); +} + void HostProtocolChre::encodeUnloadNanoappResponse( ChreFlatBufferBuilder &builder, uint16_t hostClientId, uint32_t transactionId, bool success) { diff --git a/platform/shared/idl/host_messages.fbs b/platform/shared/idl/host_messages.fbs index ff0dcd84..57b0d7cd 100644 --- a/platform/shared/idl/host_messages.fbs +++ b/platform/shared/idl/host_messages.fbs @@ -196,6 +196,11 @@ table LoadNanoappResponse { // TODO: detailed error code? } +table NanoappInstanceIdInfo { + instance_id: uint; + app_id:ulong; +} + table UnloadNanoappRequest { transaction_id:uint; @@ -323,7 +328,8 @@ table LogMessageV2 { /// uint8_t - Log metadata, encoded as follows: /// [EI(Upper nibble) | Level(Lower nibble)] /// * Log Type - /// (0 = No encoding, 1 = Tokenized log, 2 = BT snoop log) + /// (0 = No encoding, 1 = Tokenized log, + /// 2 = BT snoop log, 3 = Nanoapp Tokenized log) /// * LogBuffer log level (1 = error, 2 = warn, /// 3 = info, 4 = debug, /// 5 = verbose) @@ -335,7 +341,7 @@ table LogMessageV2 { /// terminated string (eg: pass to string manipulation functions, get its /// size via strlen(), etc.). /// - /// * Encoded logs: The first byte of the log buffer indicates the size of + /// * Tokenized logs: The first byte of the log buffer indicates the size of /// the actual encoded data to follow. For example, if a tokenized log of /// size 24 bytes were to be represented, a buffer of size 25 bytes would /// be needed to encode this as: [Size(1B) | Data(24B)]. A decoder would @@ -349,6 +355,15 @@ table LogMessageV2 { /// size 24 bytes were to be represented, a buffer of size 26 bytes would /// be needed to encode this as: [Direction(1B) | Size(1B) | Data(24B)]. /// + /// * Tokenized nanoapp logs: This log type is specifically for nanoapps with + /// tokenized logs enabled. Similar to tokenized logs, the first byte is the + /// size of the tokenized log data at the end. The next two bytes is the instance + /// ID of the nanoapp which sends this tokenized log message. This instance ID + /// will be used to map to the corresponding detokenizer in the log message parser. + /// For example, if a nanoapp tokenized log of size 24 bytes were to be sent, + /// a buffer of size 27 bytes would be needed to encode this as: + /// [Size(1B) | InstanceId (2B) | Data(24B)]. + /// /// This pattern repeats until the end of the buffer for multiple log /// messages. The last byte will always be a null-terminator. There are no /// padding bytes between these fields. Treat this like a packed struct and be @@ -428,6 +443,10 @@ table DebugConfiguration { health_monitor_failure_crash:bool; } +// Pulse messages are used to check if CHRE is up running. +table PulseRequest {} +table PulseResponse {} + /// A union that joins together all possible messages. Note that in FlatBuffers, /// unions have an implicit type union ChreMessage { @@ -475,6 +494,11 @@ union ChreMessage { NanConfigurationUpdate, DebugConfiguration, + + PulseRequest, + PulseResponse, + + NanoappInstanceIdInfo, } struct HostAddress { diff --git a/platform/shared/include/chre/platform/shared/assert_func.h b/platform/shared/include/chre/platform/shared/assert_func.h new file mode 100644 index 00000000..55f1ab38 --- /dev/null +++ b/platform/shared/include/chre/platform/shared/assert_func.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef PLATFORM_SHARED_INCLUDE_CHRE_PLATFORM_SHARED_ASSERT_FUNC_H +#define PLATFORM_SHARED_INCLUDE_CHRE_PLATFORM_SHARED_ASSERT_FUNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Performs assertion while logging the filename and line provided. + * + * @param filename The filename containing the assertion being fired. + * @param line The line that contains the assertion being fired. + */ +void chreDoAssert(const char *filename, size_t line); + +#ifdef __cplusplus +} +#endif + +#define CHRE_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + chreDoAssert(CHRE_FILENAME, __LINE__); \ + } \ + } while (0) + +#endif // PLATFORM_SHARED_INCLUDE_CHRE_PLATFORM_SHARED_ASSERT_FUNC_H diff --git a/platform/shared/include/chre/platform/shared/bt_snoop_log.h b/platform/shared/include/chre/platform/shared/bt_snoop_log.h index 35719b93..d217f4e3 100644 --- a/platform/shared/include/chre/platform/shared/bt_snoop_log.h +++ b/platform/shared/include/chre/platform/shared/bt_snoop_log.h @@ -17,6 +17,9 @@ #ifndef CHRE_PLATFORM_SHARED_BT_SNOOP_LOG_H_ #define CHRE_PLATFORM_SHARED_BT_SNOOP_LOG_H_ +#include <cinttypes> +#include <cstddef> + //! Indicates direction of a BT snoop log. //! TODO(b/294884658): Make the fbs definition as the single source of truth. enum class BtSnoopDirection : uint8_t { diff --git a/platform/shared/include/chre/platform/shared/dram_vote_client.h b/platform/shared/include/chre/platform/shared/dram_vote_client.h index 00476a49..50d51860 100644 --- a/platform/shared/include/chre/platform/shared/dram_vote_client.h +++ b/platform/shared/include/chre/platform/shared/dram_vote_client.h @@ -95,7 +95,7 @@ class DramVoteClient : public NonCopyable { * @return the duration in milliseconds since the system has been voted into * big image due to incrementDramVoteCount. */ - Milliseconds checkDramDuration() const; + Milliseconds checkDramDuration(); }; //! Provides an alias to the DramVoteClient singleton diff --git a/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h b/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h index ec71ea12..dad31313 100644 --- a/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h +++ b/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h @@ -36,6 +36,9 @@ struct LoadNanoappRequestBuilder; struct LoadNanoappResponse; struct LoadNanoappResponseBuilder; +struct NanoappInstanceIdInfo; +struct NanoappInstanceIdInfoBuilder; + struct UnloadNanoappRequest; struct UnloadNanoappRequestBuilder; @@ -99,6 +102,12 @@ struct NanConfigurationUpdateBuilder; struct DebugConfiguration; struct DebugConfigurationBuilder; +struct PulseRequest; +struct PulseRequestBuilder; + +struct PulseResponse; +struct PulseResponseBuilder; + struct HostAddress; struct MessageContainer; @@ -309,11 +318,14 @@ enum class ChreMessage : uint8_t { NanConfigurationRequest = 26, NanConfigurationUpdate = 27, DebugConfiguration = 28, + PulseRequest = 29, + PulseResponse = 30, + NanoappInstanceIdInfo = 31, MIN = NONE, - MAX = DebugConfiguration + MAX = NanoappInstanceIdInfo }; -inline const ChreMessage (&EnumValuesChreMessage())[29] { +inline const ChreMessage (&EnumValuesChreMessage())[32] { static const ChreMessage values[] = { ChreMessage::NONE, ChreMessage::NanoappMessage, @@ -343,13 +355,16 @@ inline const ChreMessage (&EnumValuesChreMessage())[29] { ChreMessage::BatchedMetricLog, ChreMessage::NanConfigurationRequest, ChreMessage::NanConfigurationUpdate, - ChreMessage::DebugConfiguration + ChreMessage::DebugConfiguration, + ChreMessage::PulseRequest, + ChreMessage::PulseResponse, + ChreMessage::NanoappInstanceIdInfo }; return values; } inline const char * const *EnumNamesChreMessage() { - static const char * const names[30] = { + static const char * const names[33] = { "NONE", "NanoappMessage", "HubInfoRequest", @@ -379,13 +394,16 @@ inline const char * const *EnumNamesChreMessage() { "NanConfigurationRequest", "NanConfigurationUpdate", "DebugConfiguration", + "PulseRequest", + "PulseResponse", + "NanoappInstanceIdInfo", nullptr }; return names; } inline const char *EnumNameChreMessage(ChreMessage e) { - if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::DebugConfiguration)) return ""; + if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::NanoappInstanceIdInfo)) return ""; const size_t index = static_cast<size_t>(e); return EnumNamesChreMessage()[index]; } @@ -506,6 +524,18 @@ template<> struct ChreMessageTraits<chre::fbs::DebugConfiguration> { static const ChreMessage enum_value = ChreMessage::DebugConfiguration; }; +template<> struct ChreMessageTraits<chre::fbs::PulseRequest> { + static const ChreMessage enum_value = ChreMessage::PulseRequest; +}; + +template<> struct ChreMessageTraits<chre::fbs::PulseResponse> { + static const ChreMessage enum_value = ChreMessage::PulseResponse; +}; + +template<> struct ChreMessageTraits<chre::fbs::NanoappInstanceIdInfo> { + static const ChreMessage enum_value = ChreMessage::NanoappInstanceIdInfo; +}; + bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type); bool VerifyChreMessageVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types); @@ -1429,6 +1459,58 @@ inline flatbuffers::Offset<LoadNanoappResponse> CreateLoadNanoappResponse( return builder_.Finish(); } +struct NanoappInstanceIdInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef NanoappInstanceIdInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_INSTANCE_ID = 4, + VT_APP_ID = 6 + }; + uint32_t instance_id() const { + return GetField<uint32_t>(VT_INSTANCE_ID, 0); + } + uint64_t app_id() const { + return GetField<uint64_t>(VT_APP_ID, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<uint32_t>(verifier, VT_INSTANCE_ID) && + VerifyField<uint64_t>(verifier, VT_APP_ID) && + verifier.EndTable(); + } +}; + +struct NanoappInstanceIdInfoBuilder { + typedef NanoappInstanceIdInfo Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_instance_id(uint32_t instance_id) { + fbb_.AddElement<uint32_t>(NanoappInstanceIdInfo::VT_INSTANCE_ID, instance_id, 0); + } + void add_app_id(uint64_t app_id) { + fbb_.AddElement<uint64_t>(NanoappInstanceIdInfo::VT_APP_ID, app_id, 0); + } + explicit NanoappInstanceIdInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + NanoappInstanceIdInfoBuilder &operator=(const NanoappInstanceIdInfoBuilder &); + flatbuffers::Offset<NanoappInstanceIdInfo> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<NanoappInstanceIdInfo>(end); + return o; + } +}; + +inline flatbuffers::Offset<NanoappInstanceIdInfo> CreateNanoappInstanceIdInfo( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t instance_id = 0, + uint64_t app_id = 0) { + NanoappInstanceIdInfoBuilder builder_(_fbb); + builder_.add_app_id(app_id); + builder_.add_instance_id(instance_id); + return builder_.Finish(); +} + struct UnloadNanoappRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef UnloadNanoappRequestBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -1963,7 +2045,8 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// uint8_t - Log metadata, encoded as follows: /// [EI(Upper nibble) | Level(Lower nibble)] /// * Log Type - /// (0 = No encoding, 1 = Tokenized log, 2 = BT snoop log) + /// (0 = No encoding, 1 = Tokenized log, + /// 2 = BT snoop log, 3 = Nanoapp Tokenized log) /// * LogBuffer log level (1 = error, 2 = warn, /// 3 = info, 4 = debug, /// 5 = verbose) @@ -1975,7 +2058,7 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// terminated string (eg: pass to string manipulation functions, get its /// size via strlen(), etc.). /// - /// * Encoded logs: The first byte of the log buffer indicates the size of + /// * Tokenized logs: The first byte of the log buffer indicates the size of /// the actual encoded data to follow. For example, if a tokenized log of /// size 24 bytes were to be represented, a buffer of size 25 bytes would /// be needed to encode this as: [Size(1B) | Data(24B)]. A decoder would @@ -1989,6 +2072,15 @@ struct LogMessageV2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { /// size 24 bytes were to be represented, a buffer of size 26 bytes would /// be needed to encode this as: [Direction(1B) | Size(1B) | Data(24B)]. /// + /// * Tokenized nanoapp logs: This log type is specifically for nanoapps with + /// tokenized logs enabled. Similar to tokenized logs, the first byte is the + /// size of the tokenized log data at the end. The next two bytes is the instance + /// ID of the nanoapp which sends this tokenized log message. This instance ID + /// will be used to map to the corresponding detokenizer in the log message parser. + /// For example, if a nanoapp tokenized log of size 24 bytes were to be sent, + /// a buffer of size 27 bytes would be needed to encode this as: + /// [Size(1B) | InstanceId (2B) | Data(24B)]. + /// /// This pattern repeats until the end of the buffer for multiple log /// messages. The last byte will always be a null-terminator. There are no /// padding bytes between these fields. Treat this like a packed struct and be @@ -2507,6 +2599,66 @@ inline flatbuffers::Offset<DebugConfiguration> CreateDebugConfiguration( return builder_.Finish(); } +struct PulseRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PulseRequestBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } +}; + +struct PulseRequestBuilder { + typedef PulseRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PulseRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PulseRequestBuilder &operator=(const PulseRequestBuilder &); + flatbuffers::Offset<PulseRequest> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<PulseRequest>(end); + return o; + } +}; + +inline flatbuffers::Offset<PulseRequest> CreatePulseRequest( + flatbuffers::FlatBufferBuilder &_fbb) { + PulseRequestBuilder builder_(_fbb); + return builder_.Finish(); +} + +struct PulseResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PulseResponseBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } +}; + +struct PulseResponseBuilder { + typedef PulseResponse Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PulseResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PulseResponseBuilder &operator=(const PulseResponseBuilder &); + flatbuffers::Offset<PulseResponse> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<PulseResponse>(end); + return o; + } +}; + +inline flatbuffers::Offset<PulseResponse> CreatePulseResponse( + flatbuffers::FlatBufferBuilder &_fbb) { + PulseResponseBuilder builder_(_fbb); + return builder_.Finish(); +} + /// The top-level container that encapsulates all possible messages. Note that /// per FlatBuffers requirements, we can't use a union as the top-level /// structure (root type), so we must wrap it in a table. @@ -2608,6 +2760,15 @@ struct MessageContainer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const chre::fbs::DebugConfiguration *message_as_DebugConfiguration() const { return message_type() == chre::fbs::ChreMessage::DebugConfiguration ? static_cast<const chre::fbs::DebugConfiguration *>(message()) : nullptr; } + const chre::fbs::PulseRequest *message_as_PulseRequest() const { + return message_type() == chre::fbs::ChreMessage::PulseRequest ? static_cast<const chre::fbs::PulseRequest *>(message()) : nullptr; + } + const chre::fbs::PulseResponse *message_as_PulseResponse() const { + return message_type() == chre::fbs::ChreMessage::PulseResponse ? static_cast<const chre::fbs::PulseResponse *>(message()) : nullptr; + } + const chre::fbs::NanoappInstanceIdInfo *message_as_NanoappInstanceIdInfo() const { + return message_type() == chre::fbs::ChreMessage::NanoappInstanceIdInfo ? static_cast<const chre::fbs::NanoappInstanceIdInfo *>(message()) : nullptr; + } /// The originating or destination client ID on the host side, used to direct /// responses only to the client that sent the request. Although initially /// populated by the requesting client, this is enforced to be the correct @@ -2739,6 +2900,18 @@ template<> inline const chre::fbs::DebugConfiguration *MessageContainer::message return message_as_DebugConfiguration(); } +template<> inline const chre::fbs::PulseRequest *MessageContainer::message_as<chre::fbs::PulseRequest>() const { + return message_as_PulseRequest(); +} + +template<> inline const chre::fbs::PulseResponse *MessageContainer::message_as<chre::fbs::PulseResponse>() const { + return message_as_PulseResponse(); +} + +template<> inline const chre::fbs::NanoappInstanceIdInfo *MessageContainer::message_as<chre::fbs::NanoappInstanceIdInfo>() const { + return message_as_NanoappInstanceIdInfo(); +} + struct MessageContainerBuilder { typedef MessageContainer Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -2895,6 +3068,18 @@ inline bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, auto ptr = reinterpret_cast<const chre::fbs::DebugConfiguration *>(obj); return verifier.VerifyTable(ptr); } + case ChreMessage::PulseRequest: { + auto ptr = reinterpret_cast<const chre::fbs::PulseRequest *>(obj); + return verifier.VerifyTable(ptr); + } + case ChreMessage::PulseResponse: { + auto ptr = reinterpret_cast<const chre::fbs::PulseResponse *>(obj); + return verifier.VerifyTable(ptr); + } + case ChreMessage::NanoappInstanceIdInfo: { + auto ptr = reinterpret_cast<const chre::fbs::NanoappInstanceIdInfo *>(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } diff --git a/platform/shared/include/chre/platform/shared/host_protocol_chre.h b/platform/shared/include/chre/platform/shared/host_protocol_chre.h index 27bf37fd..eab9e773 100644 --- a/platform/shared/include/chre/platform/shared/host_protocol_chre.h +++ b/platform/shared/include/chre/platform/shared/host_protocol_chre.h @@ -70,6 +70,8 @@ class HostMessageHandlers { static void handleNanoappListRequest(uint16_t hostClientId); + static void handlePulseRequest(); + static void handleDebugConfiguration( const fbs::DebugConfiguration *debugConfiguration); @@ -99,6 +101,9 @@ class HostMessageHandlers { uint32_t transactionId, uint32_t fragmentId, bool success); + static void sendNanoappInstanceIdInfo(uint16_t hostClientId, + uint16_t instanceId, uint64_t appId); + static void finishLoadingNanoappCallback( SystemCallbackType type, UniquePtr<LoadNanoappCallbackData> &&cbData); @@ -200,6 +205,11 @@ class HostProtocolChre : public HostProtocolCommon { uint16_t hostClientId); /** + * Encodes a response to the host indicating CHRE is up running. + */ + static void encodePulseResponse(ChreFlatBufferBuilder &builder); + + /** * Encodes a response to the host communicating the result of dynamically * loading a nanoapp. */ @@ -217,6 +227,13 @@ class HostProtocolChre : public HostProtocolCommon { uint32_t transactionId, bool success); /** + * Encodes a nanoapp's instance ID and app ID to the host. + */ + static void encodeNanoappInstanceIdInfo(ChreFlatBufferBuilder &builder, + uint16_t hostClientId, + uint16_t instanceId, uint64_t appId); + + /** * Encodes a buffer of log messages to the host. */ static void encodeLogMessages(ChreFlatBufferBuilder &builder, diff --git a/platform/shared/include/chre/platform/shared/log_buffer.h b/platform/shared/include/chre/platform/shared/log_buffer.h index 8deaded4..fb271300 100644 --- a/platform/shared/include/chre/platform/shared/log_buffer.h +++ b/platform/shared/include/chre/platform/shared/log_buffer.h @@ -23,9 +23,12 @@ #include "chre/platform/mutex.h" #include "chre/platform/shared/bt_snoop_log.h" +#include "chre/platform/shared/generated/host_messages_generated.h" namespace chre { +using LogType = fbs::LogType; + /** * Values that represent a preferred setting for when the LogBuffer should * notify the platform that logs are ready to be copied. @@ -86,6 +89,21 @@ class LogBuffer { //! message. static constexpr size_t kLogDataOffset = 5; + //! The number of overhead bytes in a printf style string entry. This value + //! indicates the size of the null terminator appended to the end of each log. + static constexpr size_t kStringLogOverhead = 1; + + //! The number of bytes in a tokenized log entry of the buffer after the + //! 'header' and before the tokenized log data is encountered. The value + //! indicate the size of the uint8_t logSize field. + static constexpr size_t kTokenizedLogOffset = 1; + + //! The number of bytes in a bt snoop log entry of the buffer after the + //! 'header' and before the bt snoop log data is encountered. The value + //! indicate the size of the uint8_t size field and the BtSnoopDirection + //! field. + static constexpr size_t kBtSnoopLogOffset = 2; + /** * @param callback The callback object that will receive notifications about * the state of the log buffer or nullptr if it is not needed. @@ -169,7 +187,8 @@ class LogBuffer { /** * - * @param logSize The size of the log text in bytes. + * @param logSize The size of the log payload, including overhead like + * metadata, null terminator, etc. * @return true if log would cause an overflow of the buffer and would * overwrite a log if it was pushed onto the buffer. */ @@ -233,6 +252,16 @@ class LogBuffer { */ size_t getNumLogsDropped(); + /** + * @param startingIndex The index to start from. + * @param type. The type of the log. See host_message.fbs. + * @return The length of the data portion of a log along with the null + * terminator. If a null terminator was not found at most + * kLogMaxSize - kLogDataOffset bytes away from the startingIndex + * then kLogMaxSize - kLogDataOffset + 1 is returned. + */ + size_t getLogDataLength(size_t startingIndex, LogType type); + private: /** * Increment the value and take the modulus of the max size of the buffer. @@ -253,6 +282,11 @@ class LogBuffer { */ void copyToBuffer(size_t size, const void *source); + template <typename Type> + void copyVarToBuffer(const Type *var) { + copyToBuffer(sizeof(Type), var); + } + /** * Copy from the buffer data to a destination memory location ensuring that * the copy wraps around the buffer data if needed. @@ -284,15 +318,6 @@ class LogBuffer { size_t getNextLogIndex(size_t startingIndex, size_t *logSize); /** - * @param startingIndex The index to start from. - * @return The length of the data portion of a log along with the null - * terminator. If a null terminator was not found at most - * kLogMaxSize - kLogDataOffset bytes away from the startingIndex - * then kLogMaxSize - kLogDataOffset + 1 is returned. - */ - size_t getLogDataLength(size_t startingIndex); - - /** * Encode the received log message (if tokenization or similar encoding * is used) and dispatch it. */ @@ -311,7 +336,7 @@ class LogBuffer { * than max size. This function must only be called with the log buffer mutex * locked. */ - void discardExcessOldLogsLocked(bool encoded, uint8_t currentLogLen); + void discardExcessOldLogsLocked(uint8_t currentLogLen); /** * Add an encoding header to the log message if the encoding param is true. @@ -328,6 +353,21 @@ class LogBuffer { void dispatch(); /** + * @param metadata The metadata of the log message. + * @return The log type of the log message. + */ + LogType getLogTypeFromMetadata(uint8_t metadata); + + /** + * Set the upper nibble of the log metadata based on log type and log level. + * + * @param type The log type of the log message. + * @param logLevel The log level of the log message. + * @return The metadata of the log message. + */ + uint8_t setLogMetadata(LogType type, LogBufferLogLevel logLevel); + + /** * The buffer data is stored in the format * * [ metadata (1B) , timestamp (4B), data (dataLenB) ] diff --git a/platform/shared/include/chre/platform/shared/log_buffer_manager.h b/platform/shared/include/chre/platform/shared/log_buffer_manager.h index cae854e9..44eb9391 100644 --- a/platform/shared/include/chre/platform/shared/log_buffer_manager.h +++ b/platform/shared/include/chre/platform/shared/log_buffer_manager.h @@ -21,6 +21,7 @@ #include "chre/platform/condition_variable.h" #include "chre/platform/mutex.h" #include "chre/platform/shared/bt_snoop_log.h" +#include "chre/platform/shared/generated/host_messages_generated.h" #include "chre/platform/shared/log_buffer.h" #include "chre/util/singleton.h" #include "chre_api/chre/re.h" @@ -31,6 +32,8 @@ namespace chre { +using LogType = fbs::LogType; + /** * A log buffer manager that platform code can use to buffer logs when the host * is not available and then send them off when the host becomes available. Uses @@ -132,7 +135,7 @@ class LogBufferManager : public LogBufferCallbackInterface { uint32_t getTimestampMs(); - void bufferOverflowGuard(size_t logSize); + void bufferOverflowGuard(size_t logSize, LogType type); LogBuffer mPrimaryLogBuffer; LogBuffer mSecondaryLogBuffer; diff --git a/platform/shared/include/chre/target_platform/platform_ble_base.h b/platform/shared/include/chre/target_platform/platform_ble_base.h index e37e88a6..fe47ed41 100644 --- a/platform/shared/include/chre/target_platform/platform_ble_base.h +++ b/platform/shared/include/chre/target_platform/platform_ble_base.h @@ -40,6 +40,7 @@ class PlatformBleBase : public PlatformPal { static void advertisingEventCallback(struct chreBleAdvertisementEvent *event); static void readRssiCallback(uint8_t errorCode, uint16_t connectionHandle, int8_t rssi); + static void flushCallback(uint8_t errorCode); static void handleBtSnoopLog(bool isTxToBtController, const uint8_t *buffer, size_t size); }; diff --git a/platform/shared/log_buffer.cc b/platform/shared/log_buffer.cc index 358c1752..8bfb5ad6 100644 --- a/platform/shared/log_buffer.cc +++ b/platform/shared/log_buffer.cc @@ -44,9 +44,8 @@ void LogBuffer::handleLog(LogBufferLogLevel logLevel, uint32_t timestampMs, void LogBuffer::handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs, const char *logFormat, va_list args) { - constexpr size_t maxLogLen = kLogMaxSize - kLogDataOffset; - char tempBuffer[maxLogLen]; - int logLenSigned = vsnprintf(tempBuffer, maxLogLen, logFormat, args); + char tempBuffer[kLogMaxSize]; + int logLenSigned = vsnprintf(tempBuffer, kLogMaxSize, logFormat, args); processLog(logLevel, timestampMs, tempBuffer, logLenSigned, false /* encoded */); } @@ -54,41 +53,44 @@ void LogBuffer::handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs, #ifdef CHRE_BLE_SUPPORT_ENABLED void LogBuffer::handleBtLog(BtSnoopDirection direction, uint32_t timestampMs, const uint8_t *buffer, size_t size) { - if (size > 0) { - auto logLen = static_cast<uint8_t>(size); + if (size == 0) { + return; + } + auto logLen = static_cast<uint8_t>(size); - if (size < kLogMaxSize) { - LockGuard<Mutex> lockGuard(mLock); + if (size < kLogMaxSize) { + LockGuard<Mutex> lockGuard(mLock); - // No additional terminator towards the end. - discardExcessOldLogsLocked(false, logLen); + static_assert(sizeof(LogType) == sizeof(uint8_t), + "LogType size is not equal to size of uint8_t"); + static_assert(sizeof(direction) == sizeof(uint8_t), + "BtSnoopDirection size is not equal to the size of uint8_t"); + uint8_t snoopLogDirection = static_cast<uint8_t>(direction); - uint8_t logType = static_cast<uint8_t>(LogType::BLUETOOTH); - uint8_t snoopLogDirection = static_cast<uint8_t>(direction); + discardExcessOldLogsLocked(logLen + kBtSnoopLogOffset); - // Set all BT logs to the CHRE_LOG_LEVEL_INFO. - uint8_t metadata = - (static_cast<uint8_t>(logType) << 4) | CHRE_LOG_LEVEL_INFO; - copyToBuffer(sizeof(metadata), &metadata); + // Set all BT logs to the CHRE_LOG_LEVEL_INFO. + uint8_t metadata = + setLogMetadata(LogType::BLUETOOTH, LogBufferLogLevel::INFO); - copyToBuffer(sizeof(timestampMs), ×tampMs); - copyToBuffer(sizeof(direction), &snoopLogDirection); - copyToBuffer(sizeof(logLen), &logLen); + copyVarToBuffer(&metadata); + copyVarToBuffer(×tampMs); + copyVarToBuffer(&snoopLogDirection); + copyVarToBuffer(&logLen); - copyToBuffer(logLen, buffer); - } else { - // Cannot truncate a BT event. Log a failure message instead. - constexpr char kBtSnoopLogGenericErrorMsg[] = - "Bt Snoop log message too large"; - static_assert( - sizeof(kBtSnoopLogGenericErrorMsg) <= kLogMaxSize, - "Error meessage size needs to be smaller than max log length"); - logLen = static_cast<uint8_t>(sizeof(kBtSnoopLogGenericErrorMsg)); - copyLogToBuffer(LogBufferLogLevel::INFO, timestampMs, - kBtSnoopLogGenericErrorMsg, logLen, false /* encoded */); - } - dispatch(); + copyToBuffer(logLen, buffer); + } else { + // Cannot truncate a BT event. Log a failure message instead. + constexpr char kBtSnoopLogGenericErrorMsg[] = + "Bt Snoop log message too large"; + static_assert( + sizeof(kBtSnoopLogGenericErrorMsg) <= kLogMaxSize, + "Error meessage size needs to be smaller than max log length"); + logLen = static_cast<uint8_t>(sizeof(kBtSnoopLogGenericErrorMsg)); + copyLogToBuffer(LogBufferLogLevel::INFO, timestampMs, + kBtSnoopLogGenericErrorMsg, logLen, false /* encoded */); } + dispatch(); } #endif // CHRE_BLE_SUPPORT_ENABLED @@ -106,8 +108,7 @@ size_t LogBuffer::copyLogs(void *destination, size_t size, bool LogBuffer::logWouldCauseOverflow(size_t logSize) { LockGuard<Mutex> lock(mLock); - return (mBufferDataSize + logSize + kLogDataOffset + 1 /* nullptr */ > - mBufferMaxSize); + return (mBufferDataSize + logSize + kLogDataOffset > mBufferMaxSize); } void LogBuffer::transferTo(LogBuffer &buffer) { @@ -202,8 +203,6 @@ size_t LogBuffer::copyLogsLocked(void *destination, size_t size, copySize = mBufferDataSize; } else { size_t logSize; - // There is guaranteed to be a null terminator within the max log length - // number of bytes so logStartIndex will not be maxBytes + 1 size_t logStartIndex = getNextLogIndex(mBufferDataHeadIndex, &logSize); while (copySize + logSize <= size && copySize + logSize <= mBufferDataSize) { @@ -229,69 +228,81 @@ void LogBuffer::resetLocked() { size_t LogBuffer::getNextLogIndex(size_t startingIndex, size_t *logSize) { size_t logDataStartIndex = incrementAndModByBufferMaxSize(startingIndex, kLogDataOffset); - - size_t logDataSize = getLogDataLength(logDataStartIndex); + LogType type = getLogTypeFromMetadata(mBufferData[startingIndex]); + size_t logDataSize = getLogDataLength(logDataStartIndex, type); *logSize = kLogDataOffset + logDataSize; return incrementAndModByBufferMaxSize(startingIndex, *logSize); } -size_t LogBuffer::getLogDataLength(size_t startingIndex) { +size_t LogBuffer::getLogDataLength(size_t startingIndex, LogType type) { size_t currentIndex = startingIndex; - constexpr size_t maxBytes = kLogMaxSize - kLogDataOffset; - size_t numBytes = maxBytes + 1; - - for (size_t i = 0; i < maxBytes; i++) { - if (mBufferData[currentIndex] == '\0') { - // +1 to include the null terminator - numBytes = i + 1; - break; + size_t numBytes = kLogMaxSize; + + if (type == LogType::STRING) { + for (size_t i = 0; i < kLogMaxSize; i++) { + if (mBufferData[currentIndex] == '\0') { + // +1 to include the null terminator + numBytes = i + 1; + break; + } + currentIndex = incrementAndModByBufferMaxSize(currentIndex, 1); } - currentIndex = incrementAndModByBufferMaxSize(currentIndex, 1); + } else if (type == LogType::TOKENIZED) { + numBytes = mBufferData[startingIndex] + kTokenizedLogOffset; + } else if (type == LogType::BLUETOOTH) { + currentIndex = incrementAndModByBufferMaxSize(startingIndex, 1); + numBytes = mBufferData[currentIndex] + kBtSnoopLogOffset; + } else { + CHRE_ASSERT_LOG(false, "Received unexpected log message type"); } + return numBytes; } void LogBuffer::processLog(LogBufferLogLevel logLevel, uint32_t timestampMs, const void *logBuffer, size_t size, bool encoded) { - if (size > 0) { - auto logLen = static_cast<uint8_t>(size); - if (size >= kLogMaxSize) { - if (!encoded) { - // Leave space for null terminator to be copied on end - logLen = static_cast<uint8_t>(kLogMaxSize - 1); - } else { - // There is no way of decoding an encoded message if we truncate it, so - // we do the next best thing and try to log a generic failure message - // reusing the logbuffer for as much as we can. Note that we also need - // flip the encoding flag for proper decoding by the host log message - // parser. - constexpr char kTokenizedLogGenericErrorMsg[] = - "Tokenized log message too large"; - static_assert( - sizeof(kTokenizedLogGenericErrorMsg) <= kLogMaxSize, - "Error meessage size needs to be smaller than max log length"); - logBuffer = kTokenizedLogGenericErrorMsg; - logLen = static_cast<uint8_t>(sizeof(kTokenizedLogGenericErrorMsg)); - encoded = false; - } - } - copyLogToBuffer(logLevel, timestampMs, logBuffer, logLen, encoded); - dispatch(); + if (size == 0) { + return; + } + auto logLen = static_cast<uint8_t>(size); + + constexpr char kTokenizedLogGenericErrorMsg[] = + "Tokenized log message too large"; + + // For tokenized logs, need to leave space for the message size offset. For + // string logs, need to leave 1 byte for the null terminator at the end. + if (!encoded && size >= kLogMaxSize - 1) { + // String logs longer than kLogMaxSize - 1 will be truncated. + logLen = static_cast<uint8_t>(kLogMaxSize - 1); + } else if (encoded && size >= kLogMaxSize - kTokenizedLogOffset) { + // There is no way of decoding an encoded message if we truncate it, so + // we do the next best thing and try to log a generic failure message + // reusing the logbuffer for as much as we can. Note that we also need + // flip the encoding flag for proper decoding by the host log message + // parser. + static_assert( + sizeof(kTokenizedLogGenericErrorMsg) <= kLogMaxSize - 1, + "Error meessage size needs to be smaller than max log length"); + logBuffer = kTokenizedLogGenericErrorMsg; + logLen = static_cast<uint8_t>(sizeof(kTokenizedLogGenericErrorMsg)); + encoded = false; } + copyLogToBuffer(logLevel, timestampMs, logBuffer, logLen, encoded); + dispatch(); } void LogBuffer::copyLogToBuffer(LogBufferLogLevel level, uint32_t timestampMs, const void *logBuffer, uint8_t logLen, bool encoded) { LockGuard<Mutex> lockGuard(mLock); - discardExcessOldLogsLocked(encoded, logLen); + // For STRING logs, add 1 byte for null terminator. For TOKENIZED logs, add 1 + // byte for the size metadata added to the message. + discardExcessOldLogsLocked(logLen + 1); encodeAndCopyLogLocked(level, timestampMs, logBuffer, logLen, encoded); } -void LogBuffer::discardExcessOldLogsLocked(bool encoded, - uint8_t currentLogLen) { - size_t totalLogSize = - kLogDataOffset + (encoded ? currentLogLen : currentLogLen + 1); +void LogBuffer::discardExcessOldLogsLocked(uint8_t currentLogLen) { + size_t totalLogSize = kLogDataOffset + currentLogLen; while (mBufferDataSize + totalLogSize > mBufferMaxSize) { mNumLogsDropped++; size_t logSize; @@ -305,13 +316,13 @@ void LogBuffer::encodeAndCopyLogLocked(LogBufferLogLevel level, const void *logBuffer, uint8_t logLen, bool encoded) { uint8_t metadata = - (static_cast<uint8_t>(encoded) << 4) | static_cast<uint8_t>(level); + setLogMetadata(encoded ? LogType::TOKENIZED : LogType::STRING, level); - copyToBuffer(sizeof(uint8_t), &metadata); - copyToBuffer(sizeof(timestampMs), ×tampMs); + copyVarToBuffer(&metadata); + copyVarToBuffer(×tampMs); if (encoded) { - copyToBuffer(sizeof(uint8_t), &logLen); + copyVarToBuffer(&logLen); } copyToBuffer(logLen, logBuffer); if (!encoded) { @@ -339,4 +350,20 @@ void LogBuffer::dispatch() { } } +LogType LogBuffer::getLogTypeFromMetadata(uint8_t metadata) { + LogType type; + if ((metadata & 0x20) != 0) { + type = LogType::BLUETOOTH; + } else if ((metadata & 0x10) != 0) { + type = LogType::TOKENIZED; + } else { + type = LogType::STRING; + } + return type; +} + +uint8_t LogBuffer::setLogMetadata(LogType type, LogBufferLogLevel logLevel) { + return static_cast<uint8_t>(type) << 4 | static_cast<uint8_t>(logLevel); +} + } // namespace chre diff --git a/platform/shared/log_buffer_manager.cc b/platform/shared/log_buffer_manager.cc index 82abee60..96b4633e 100644 --- a/platform/shared/log_buffer_manager.cc +++ b/platform/shared/log_buffer_manager.cc @@ -18,6 +18,7 @@ #include "chre/core/event_loop_manager.h" #include "chre/platform/shared/bt_snoop_log.h" +#include "chre/platform/shared/generated/host_messages_generated.h" #include "chre/util/lock_guard.h" void chrePlatformLogToBuffer(chreLogLevel chreLogLevel, const char *format, @@ -44,6 +45,8 @@ void chrePlatformBtSnoopLog(BtSnoopDirection direction, const uint8_t *buffer, namespace chre { +using LogType = fbs::LogType; + void LogBufferManager::onLogsReady() { LockGuard<Mutex> lockGuard(mFlushLogsMutex); if (!mLogFlushToHostPending) { @@ -124,7 +127,15 @@ uint32_t LogBufferManager::getTimestampMs() { return static_cast<uint32_t>(timeNs / kOneMillisecondInNanoseconds); } -void LogBufferManager::bufferOverflowGuard(size_t logSize) { +void LogBufferManager::bufferOverflowGuard(size_t logSize, LogType type) { + if (type == LogType::STRING) { + // Add one byte because of the null terminator added at the end. + logSize = logSize + LogBuffer::kStringLogOverhead; + } else if (type == LogType::TOKENIZED) { + logSize = logSize + LogBuffer::kTokenizedLogOffset; + } else if (type == LogType::BLUETOOTH) { + logSize = logSize + LogBuffer::kBtSnoopLogOffset; + } if (mPrimaryLogBuffer.logWouldCauseOverflow(logSize)) { LockGuard<Mutex> lockGuard(mFlushLogsMutex); if (!mLogFlushToHostPending) { @@ -142,7 +153,7 @@ void LogBufferManager::logVa(chreLogLevel logLevel, const char *formatStr, va_copy(getSizeArgs, args); size_t logSize = vsnprintf(nullptr, 0, formatStr, getSizeArgs); va_end(getSizeArgs); - bufferOverflowGuard(logSize); + bufferOverflowGuard(logSize, LogType::STRING); mPrimaryLogBuffer.handleLogVa(chreToLogBufferLogLevel(logLevel), getTimestampMs(), formatStr, args); } @@ -150,7 +161,7 @@ void LogBufferManager::logVa(chreLogLevel logLevel, const char *formatStr, void LogBufferManager::logBtSnoop(BtSnoopDirection direction, const uint8_t *buffer, size_t size) { #ifdef CHRE_BLE_SUPPORT_ENABLED - bufferOverflowGuard(size); + bufferOverflowGuard(size, LogType::BLUETOOTH); mPrimaryLogBuffer.handleBtLog(direction, getTimestampMs(), buffer, size); #else UNUSED_VAR(direction); @@ -162,7 +173,7 @@ void LogBufferManager::logBtSnoop(BtSnoopDirection direction, void LogBufferManager::logEncoded(chreLogLevel logLevel, const uint8_t *encodedLog, size_t encodedLogSize) { - bufferOverflowGuard(encodedLogSize); + bufferOverflowGuard(encodedLogSize, LogType::TOKENIZED); mPrimaryLogBuffer.handleEncodedLog(chreToLogBufferLogLevel(logLevel), getTimestampMs(), encodedLog, encodedLogSize); diff --git a/platform/shared/nanoapp/nanoapp_stack_check.cc b/platform/shared/nanoapp/nanoapp_stack_check.cc new file mode 100644 index 00000000..3f6b24bf --- /dev/null +++ b/platform/shared/nanoapp/nanoapp_stack_check.cc @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#include "chre/util/nanoapp/log.h" +#include "chre_api/chre/re.h" +#include "chre_api/chre/toolchain.h" + +/** + * @file + * Stack check support. + * + * The symbols defined in this file are needed when the code is compiled with + * the -fstack-protector flag. + */ + +#ifndef LOG_TAG +#define LOG_TAG "[STACK CHECK]" +#endif // LOG_TAG + +#ifdef __cplusplus +extern "C" { +#endif + +uintptr_t __stack_chk_guard = 0x56494342; + +// Terminate a function in case of stack overflow. +void __stack_chk_fail(void) CHRE_NO_RETURN; +void __stack_chk_fail(void) { + LOGE("Stack corruption detected"); + chreAbort(/*abortCode=*/0); +} + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/platform/shared/nanoapp/nanoapp_support_lib_dso.cc b/platform/shared/nanoapp/nanoapp_support_lib_dso.cc index 2f21df00..55b58403 100644 --- a/platform/shared/nanoapp/nanoapp_support_lib_dso.cc +++ b/platform/shared/nanoapp/nanoapp_support_lib_dso.cc @@ -304,12 +304,38 @@ bool chreBleStartScanAsync(chreBleScanMode mode, uint32_t reportDelayMs, } WEAK_SYMBOL +bool chreBleStartScanAsyncV1_9(chreBleScanMode mode, uint32_t reportDelayMs, + const struct chreBleScanFilterV1_9 *filter, + const void *cookie) { + if (chreGetApiVersion() < CHRE_API_VERSION_1_9) { + return false; + } + auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStartScanAsyncV1_9); + if (fptr == nullptr) { + return false; + } + return fptr(mode, reportDelayMs, filter, cookie); +} + +WEAK_SYMBOL bool chreBleStopScanAsync() { auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStopScanAsync); return (fptr != nullptr) ? fptr() : false; } WEAK_SYMBOL +bool chreBleStopScanAsyncV1_9(const void *cookie) { + if (chreGetApiVersion() < CHRE_API_VERSION_1_9) { + return false; + } + auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStopScanAsyncV1_9); + if (fptr == nullptr) { + return false; + } + return fptr(cookie); +} + +WEAK_SYMBOL bool chreBleReadRssiAsync(uint16_t connectionHandle, const void *cookie) { auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleReadRssiAsync); return (fptr != nullptr) ? fptr(connectionHandle, cookie) : false; diff --git a/platform/shared/nanoapp_loader.cc b/platform/shared/nanoapp_loader.cc index eb4ec31a..377d8ce7 100644 --- a/platform/shared/nanoapp_loader.cc +++ b/platform/shared/nanoapp_loader.cc @@ -55,6 +55,10 @@ NanoappLoader *gCurrentlyLoadingNanoapp = nullptr; //! Indicates whether a failure occurred during static initialization. bool gStaticInitFailure = false; +void deleteOpOverride(void* /* ptr */, unsigned int size) { + FATAL_ERROR("Nanoapp: delete(void *, unsigned int) override : sz = %u", size); +} + int atexitInternal(struct AtExitCallback &cb) { if (gCurrentlyLoadingNanoapp == nullptr) { CHRE_ASSERT_LOG(false, @@ -179,6 +183,7 @@ const ExportedData kExportedData[] = { ADD_EXPORTED_C_SYMBOL(__cxa_pure_virtual), ADD_EXPORTED_SYMBOL(cxaAtexitOverride, "__cxa_atexit"), ADD_EXPORTED_SYMBOL(atexitOverride, "atexit"), + ADD_EXPORTED_SYMBOL(deleteOpOverride, "_ZdlPvj"), ADD_EXPORTED_C_SYMBOL(dlsym), ADD_EXPORTED_C_SYMBOL(isgraph), ADD_EXPORTED_C_SYMBOL(memcmp), @@ -198,7 +203,9 @@ const ExportedData kExportedData[] = { ADD_EXPORTED_C_SYMBOL(chreBleGetFilterCapabilities), ADD_EXPORTED_C_SYMBOL(chreBleFlushAsync), ADD_EXPORTED_C_SYMBOL(chreBleStartScanAsync), + ADD_EXPORTED_C_SYMBOL(chreBleStartScanAsyncV1_9), ADD_EXPORTED_C_SYMBOL(chreBleStopScanAsync), + ADD_EXPORTED_C_SYMBOL(chreBleStopScanAsyncV1_9), ADD_EXPORTED_C_SYMBOL(chreBleReadRssiAsync), ADD_EXPORTED_C_SYMBOL(chreBleGetScanStatus), ADD_EXPORTED_C_SYMBOL(chreConfigureDebugDumpEvent), diff --git a/platform/shared/platform_ble.cc b/platform/shared/platform_ble.cc index 3039b13f..9d0bf90c 100644 --- a/platform/shared/platform_ble.cc +++ b/platform/shared/platform_ble.cc @@ -31,6 +31,7 @@ const chrePalBleCallbacks PlatformBleBase::sBleCallbacks = { PlatformBleBase::scanStatusChangeCallback, PlatformBleBase::advertisingEventCallback, PlatformBleBase::readRssiCallback, + PlatformBleBase::flushCallback, PlatformBleBase::handleBtSnoopLog, }; @@ -49,6 +50,12 @@ void PlatformBle::init() { if (mBleApi != nullptr) { if (!mBleApi->open(&gChrePalSystemApi, &sBleCallbacks)) { LOGE("BLE PAL open returned false"); + +#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED + EventLoopManagerSingleton::get()->getTelemetryManager().onPalOpenFailure( + TelemetryManager::PalType::BLE); +#endif // CHRE_TELEMETRY_SUPPORT_ENABLED + mBleApi = nullptr; } else { LOGD("Opened BLE PAL version 0x%08" PRIx32, mBleApi->moduleVersion); @@ -78,7 +85,7 @@ uint32_t PlatformBle::getFilterCapabilities() { } bool PlatformBle::startScanAsync(chreBleScanMode mode, uint32_t reportDelayMs, - const struct chreBleScanFilter *filter) { + const struct chreBleScanFilterV1_9 *filter) { if (mBleApi != nullptr) { prePalApiCall(PalType::BLE); return mBleApi->startScan(mode, reportDelayMs, filter); @@ -142,6 +149,20 @@ void PlatformBleBase::readRssiCallback(uint8_t errorCode, #endif } +bool PlatformBle::flushAsync() { + if (mBleApi != nullptr) { + prePalApiCall(PalType::BLE); + return mBleApi->flush(); + } else { + return false; + } +} + +void PlatformBleBase::flushCallback(uint8_t errorCode) { + EventLoopManagerSingleton::get()->getBleRequestManager().handleFlushComplete( + errorCode); +} + void PlatformBleBase::handleBtSnoopLog(bool isTxToBtController, const uint8_t *buffer, size_t size) { BtSnoopDirection direction = diff --git a/platform/shared/pw_trace/include/chre/target_platform/tracing.h b/platform/shared/pw_trace/include/chre/target_platform/tracing.h new file mode 100644 index 00000000..a48c9ff0 --- /dev/null +++ b/platform/shared/pw_trace/include/chre/target_platform/tracing.h @@ -0,0 +1,247 @@ +/* + * 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. + */ + +#ifndef CHRE_PLATFORM_SHARED_TRACING_H_ +#define CHRE_PLATFORM_SHARED_TRACING_H_ + +#include "chre/target_platform/tracing_util.h" +#include "pw_trace/trace.h" + +// Format strings gotten from https://pigweed.dev/pw_trace/#data. +// Must be macros for string concatenation at preprocessor time. +#define PW_MAP_PREFIX "@pw_py_map_fmt:{" +#define PW_MAP_SUFFIX "}" + +/** + * Traces an instantaneous event. + * + * @param label A string literal which describes the trace. + * @param group <optional> A string literal to group traces together. + * @param trace_id <optional> A uint32_t which groups this trace with others + * with the same group and trace_id. + * Every trace with a trace_id must also have a + * group. + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_INSTANT(label, ...) PW_TRACE_INSTANT(label, ##__VA_ARGS__) + +/** + * Used to trace the start of a duration event. Should be paired with a + * CHRE_TRACE_END (or CHRE_TRACE_END_DATA) with the same + * module/label/group/trace_id. + * + * @param label A string literal which describes the trace. + * @param group <optional> A string literal to group traces together. + * @param trace_id <optional> A uint32_t which groups this trace with others + * with the same group and trace_id. + * Every trace with a trace_id must also have a + * group. + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_START(label, ...) PW_TRACE_START(label, ##__VA_ARGS__) + +/** + * Used to trace the end of a duration event. Should be paired with a + * CHRE_TRACE_START (or CHRE_TRACE_START_DATA) with the same + * module/label/group/trace_id. + * + * @param label A string literal which describes the trace. + * @param group <optional> A string literal to group traces together. + * @param trace_id <optional> A uint32_t which groups this trace with others + * with the same group and trace_id. + * Every trace with a trace_id must also have a + * group. + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_END(label, ...) PW_TRACE_END(label, ##__VA_ARGS__) + +/** + * For the group of CHRE_TRACE_INSTANT_DATA... macros. + * Use the appropriate macro which contains the parameters you wish to pass to + * the trace. If you wish to specify a group you must use either the + * CHRE_TRACE_INSTANT_DATA_GROUP macro or CHRE_TRACE_INSTANT_DATA_TRACE_ID macro + * with a trace_id. + * + * Traces an instantaneous event with data variables or literals passed to the + * macro, correlating to the dataFmtString. + * + * @param label A string literal which describes the trace. + * @param group A string literal to group traces together. + * Must use CHRE_TRACE_INSTANT_DATA_GROUP or + * CHRE_TRACE_INSTANT_DATA_TRACE_ID with a trace_id to use this + * parameter. + * @param trace_id A uint32_t which groups this trace with others with the same + * group and trace_id. + * Every trace with a trace_id must also have a group. + * Must use CHRE_TRACE_INSTANT_DATA_TRACE_ID to use this + * parameter. + * @param dataFmtString A string literal which is used to relate data to its + * size. Use the defined macros "T_X" for ease of use in + * the format specifier. The format string must follow the + * format "<field name>:<specifier>,..." (omitting the + * final comma) + * Ex. "data1:" T_U8 ",data2:" T_I32 + * @param firstData First data variable. Used to enforce proper usage of this + * macro (with at least one data variable). + * @param VA_ARGS List of variables holding data in the order specified by the + * dataFmtString. + * + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_INSTANT_DATA(label, dataFmtString, firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_INSTANT_DATA(label, PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_INSTANT_DATA_GROUP(label, group, dataFmtString, firstData, \ + ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_INSTANT_DATA(label, group, \ + PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_INSTANT_DATA_TRACE_ID(label, group, trace_id, \ + dataFmtString, firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_INSTANT_DATA(label, group, trace_id, \ + PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +/** + * For the group of CHRE_TRACE_START_DATA... macros. + * Use the appropriate macro which contains the parameters you wish to pass to + * the trace. If you wish to specify a group you must use either the + * CHRE_TRACE_START_DATA_GROUP macro or CHRE_TRACE_START_DATA_TRACE_ID macro + * with a trace_id. + * + * Used to trace the start of a duration event with data variables or literals + * passed to the macro, correlating to the dataFmtString. This should be paired + * with a CHRE_TRACE_END (or CHRE_TRACE_END_DATA) with the same + * module/label/group/trace_id to measure the duration of this event. + * + * @param label A string literal which describes the trace. + * @param group A string literal to group traces together. + * Must use CHRE_TRACE_START_DATA_GROUP or + * CHRE_TRACE_START_DATA_TRACE_ID with a trace_id to use this + * parameter. + * @param trace_id A uint32_t which groups this trace with others with the same + * group and trace_id. + * Every trace with a trace_id must also have a group. + * Must use CHRE_TRACE_START_DATA_TRACE_ID to use this + * parameter. + * @param dataFmtString A string literal which is used to relate data to its + * size. Use the defined macros "T_X" for ease of use in + * the format specifier. The format string must follow the + * format "<field name>:<specifier>,..." (omitting the + * final comma) + * Ex. "data1:" T_U8 ",data2:" T_I32 + * @param firstData First data variable. Used to enforce proper usage of this + * macro (with at least one data variable). + * @param VA_ARGS List of variables holding data in the order specified by the + * dataFmtString. + * + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_START_DATA(label, dataFmtString, firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_START_DATA(label, PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_START_DATA_GROUP(label, group, dataFmtString, firstData, \ + ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_START_DATA(label, group, \ + PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_START_DATA_TRACE_ID(label, group, trace_id, dataFmtString, \ + firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_START_DATA(label, group, trace_id, \ + PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +/** + * For the group of CHRE_TRACE_END_DATA... macros. + * Use the appropriate macro which contains the parameters you wish to pass to + * the trace. If you wish to specify a group you must use either the + * CHRE_TRACE_END_DATA_GROUP macro or CHRE_TRACE_END_DATA_TRACE_ID macro + * with a trace_id. + * + * Used to trace the end of a duration event with data variables or literals + * passed to the macro, correlating to the dataFmtString. This should be paired + * with a CHRE_TRACE_START (or CHRE_TRACE_START_DATA) with the same + * module/label/group/trace_id to measure the duration of this event. + * + * @param label A string literal which describes the trace. + * @param group A string literal to group traces together. + * Must use CHRE_TRACE_END_DATA_GROUP or + * CHRE_TRACE_END_DATA_TRACE_ID with a trace_id to use this + * parameter. + * @param trace_id A uint32_t which groups this trace with others with the same + * group and trace_id. + * Every trace with a trace_id must also have a group. + * Must use CHRE_TRACE_END_DATA_TRACE_ID to use this + * parameter. + * @param dataFmtString A string literal which is used to relate data to its + * size. Use the defined macros "T_X" for ease of use in + * the format specifier. The format string must follow the + * format "<field name>:<specifier>,..." (omitting the + * final comma) + * Ex. "data1:" T_U8 ",data2:" T_I32 + * @param firstData First data variable. Used to enforce proper usage of this + * macro (with at least one data variable). + * @param VA_ARGS List of variables holding data in the order specified by the + * dataFmtString. + * + * @see https://pigweed.dev/pw_trace/#trace-macros + */ +#define CHRE_TRACE_END_DATA(label, dataFmtString, firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_END_DATA(label, PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_END_DATA_GROUP(label, group, dataFmtString, firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_END_DATA(label, group, PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#define CHRE_TRACE_END_DATA_TRACE_ID(label, group, trace_id, dataFmtString, \ + firstData, ...) \ + do { \ + CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(firstData, ##__VA_ARGS__); \ + PW_TRACE_END_DATA(label, group, trace_id, \ + PW_MAP_PREFIX dataFmtString PW_MAP_SUFFIX, \ + chreTraceDataBuffer, chreTraceDataSize); \ + } while (0) + +#endif // CHRE_PLATFORM_SHARED_TRACING_H_ diff --git a/platform/shared/pw_trace/include/chre/target_platform/tracing_util.h b/platform/shared/pw_trace/include/chre/target_platform/tracing_util.h new file mode 100644 index 00000000..d68ecec0 --- /dev/null +++ b/platform/shared/pw_trace/include/chre/target_platform/tracing_util.h @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#ifndef CHRE_PLATFORM_SHARED_TRACING_UTIL_H_ +#define CHRE_PLATFORM_SHARED_TRACING_UTIL_H_ + +#include "chre/util/macros.h" + +namespace tracing_internal { + +// Check to make sure pointer size macro is defined. +#ifndef __SIZEOF_POINTER__ +#error "__SIZEOF_POINTER__ macro not defined - unsupported toolchain being used" +#endif + +constexpr size_t kMaxTraceDataSize = 256; + +template <typename T> +inline constexpr std::size_t chreTraceGetSizeOf() { + if constexpr (std::is_pointer_v<T>) { + return __SIZEOF_POINTER__; + } + + return sizeof(T); +} + +/** + * Special case for strings. + * + * Due to how python struct unpacking works, reading strings requires the data + * format string to specify the length of buffer containing the string. + * PW_TRACE macros require the data format string to be a string literal, and we + * don't always know the str length at compile-time and thus opt to put all + * strings in a fixed size buffer of length CHRE_TRACE_MAX_STRING_SIZE. Using + * the pascal string option indicates the buffer's first byte contains the size + * of string, followed by the string characters. + */ +template <> +inline constexpr std::size_t chreTraceGetSizeOf<const char *>() { + return CHRE_TRACE_STR_BUFFER_SIZE; +} +template <> +inline constexpr std::size_t chreTraceGetSizeOf<char *>() { + return chreTraceGetSizeOf<const char *>(); +} + +template <typename... Types> +constexpr std::size_t chreTraceGetSizeOfVarArgs() { + return (chreTraceGetSizeOf<std::decay_t<Types>>() + ...); +} + +template <typename Data> +inline void chreTraceInsertVar(uint8_t *buffer, Data data, + std::size_t dataSize) { + static_assert(std::is_integral_v<Data> || std::is_pointer_v<Data>, + "Unsupported data type"); + memcpy(buffer, &data, dataSize); +} +template <> +inline void chreTraceInsertVar<const char *>(uint8_t *buffer, const char *data, + std::size_t) { + // Insert size byte metadata as the first byte of the pascal string. + *buffer = static_cast<uint8_t>(strnlen(data, CHRE_TRACE_MAX_STRING_SIZE)); + + // Insert string after size byte and zero out remainder of buffer. + strncpy(reinterpret_cast<char *>(buffer + 1), data, + CHRE_TRACE_MAX_STRING_SIZE); +} +template <> +inline void chreTraceInsertVar<char *>(uint8_t *buffer, char *data, + std::size_t dataSize) { + chreTraceInsertVar<const char *>(buffer, data, dataSize); +} + +/** + * Populate the pre-allocated buffer with data passed in. + * Should only be called in the CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER + * macro. + * + * Example Usage: + * uint_16_t data1; char data2; uint32_t data3; + * ... // define data + * chreTracePopulateBufferWithArgs(buf, data1, data2, data3); + * + * @param buffer A buffer to insert data into. + * Assumed to be large enough to hold all data since we use the + * same size logic to allocate space for the buffer. + * @param data Single piece of data to insert into the buffer. Assumed to + * have >= 1 data element to insert into the buffer. Strings + * assumed to be null-terminated or have length >= + * CHRE_TRACE_MAX_STRING_SIZE. + * @param args Variable length argument to hold the remainder of the data + * to insert into the buffer. + */ +template <typename Data, typename... Types> +void chreTracePopulateBufferWithArgs(uint8_t *buffer, Data data, + Types... args) { + constexpr std::size_t dataSize = chreTraceGetSizeOf<Data>(); + tracing_internal::chreTraceInsertVar(buffer, data, dataSize); + buffer += dataSize; + + if constexpr (sizeof...(args) > 0) { + chreTracePopulateBufferWithArgs(buffer, args...); + } +} + +// Create and populate the chreTraceDataBuffer. We allocate only the amount of +// space required to store all data. +// Unscoped to export the variables holding the buffer and data size. +#define CHRE_TRACE_ALLOCATE_AND_POPULATE_DATA_BUFFER(...) \ + constexpr std::size_t chreTraceDataSize = \ + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(__VA_ARGS__)>(); \ + static_assert(chreTraceDataSize <= tracing_internal::kMaxTraceDataSize, \ + "Trace data size too large"); \ + uint8_t chreTraceDataBuffer[chreTraceDataSize]; \ + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, \ + __VA_ARGS__); + +} // namespace tracing_internal + +#endif // CHRE_PLATFORM_SHARED_TRACING_UTIL_H_
\ No newline at end of file diff --git a/platform/shared/sensor_pal/platform_sensor_manager.cc b/platform/shared/sensor_pal/platform_sensor_manager.cc index 28a2c596..3b5af49a 100644 --- a/platform/shared/sensor_pal/platform_sensor_manager.cc +++ b/platform/shared/sensor_pal/platform_sensor_manager.cc @@ -44,6 +44,12 @@ void PlatformSensorManager::init() { if (mSensorApi != nullptr) { if (!mSensorApi->open(&gChrePalSystemApi, &sSensorCallbacks)) { LOGE("Sensor PAL open returned false"); + +#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED + EventLoopManagerSingleton::get()->getTelemetryManager().onPalOpenFailure( + TelemetryManager::PalType::SENSOR); +#endif // CHRE_TELEMETRY_SUPPORT_ENABLED + mSensorApi = nullptr; } else { LOGD("Opened Sensor PAL version 0x%08" PRIx32, mSensorApi->moduleVersion); diff --git a/platform/slpi/host_link.cc b/platform/slpi/host_link.cc index e1d6985a..7b2e0cd7 100644 --- a/platform/slpi/host_link.cc +++ b/platform/slpi/host_link.cc @@ -908,6 +908,8 @@ void HostMessageHandlers::handleSelfTestRequest(uint16_t hostClientId) { sendSelfTestResponse(hostClientId, success); } +void HostMessageHandlers::handlePulseRequest() {} + void HostMessageHandlers::handleNanConfigurationUpdate(bool enabled) { #ifdef CHRE_WIFI_NAN_SUPPORT_ENABLED EventLoopManagerSingleton::get() diff --git a/platform/slpi/include/chre/target_platform/assert.h b/platform/slpi/include/chre/target_platform/assert.h new file mode 100644 index 00000000..7d00b518 --- /dev/null +++ b/platform/slpi/include/chre/target_platform/assert.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef PLATFORM_SLPI_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H +#define PLATFORM_SLPI_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H + +#include "chre/platform/shared/assert_func.h" + +#endif // PLATFORM_SLPI_INCLUDE_CHRE_TARGET_PLATFORM_ASSERT_H diff --git a/platform/tests/log_buffer_test.cc b/platform/tests/log_buffer_test.cc index 674fe8f8..9a71cbc4 100644 --- a/platform/tests/log_buffer_test.cc +++ b/platform/tests/log_buffer_test.cc @@ -14,14 +14,18 @@ * limitations under the License. */ +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <string> #include "chre/platform/atomic.h" #include "chre/platform/condition_variable.h" #include "chre/platform/mutex.h" +#include "chre/platform/shared/bt_snoop_log.h" #include "chre/platform/shared/log_buffer.h" +using testing::ContainerEq; + namespace chre { // TODO(b/146164384): Test that the onLogsReady callback is called @@ -111,7 +115,7 @@ TEST(LogBuffer, FailOnHandleLargerLogThanBufferSize) { char outBuffer[kOutBufferSize]; // Note the size of this log is too big to fit in the buffer that we are // using for the LogBuffer object - std::string testLogStrStr(1025, 'a'); + std::string testLogStrStr(kDefaultBufferSize + 1, 'a'); TestLogBufferCallback callback; LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); @@ -125,32 +129,141 @@ TEST(LogBuffer, FailOnHandleLargerLogThanBufferSize) { EXPECT_EQ(bytesCopied, 0); } -TEST(LogBuffer, LogOverwritten) { +TEST(LogBuffer, StringLogOverwritten) { char buffer[kDefaultBufferSize]; constexpr size_t kOutBufferSize = 200; char outBuffer[kOutBufferSize]; - char testedBuffer[kOutBufferSize]; TestLogBufferCallback callback; LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); - // This for loop adds 1060 bytes of data through the buffer which is > than - // 1024 - for (size_t i = 0; i < 10; i++) { - std::string testLogStrStr(100, 'a' + i); + constexpr size_t kLogPayloadSize = 100; + constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset + + LogBuffer::kStringLogOverhead + + kLogPayloadSize; + constexpr int kNumInsertions = 10; + constexpr int kNumLogDropsExpected = + kNumInsertions - kDefaultBufferSize / kBufferUsePerLog; + static_assert(kNumLogDropsExpected > 0); + + // This for loop adds 1060 (kNumInsertions * kBufferUsePerLog) bytes of data + // through the buffer which is > than 1024. + for (size_t i = 0; i < kNumInsertions; i++) { + std::string testLogStrStr(kLogPayloadSize, 'a' + i); const char *testLogStr = testLogStrStr.c_str(); logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr); } - size_t numLogsDropped; - size_t bytesCopied = - logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped); - memcpy(testedBuffer, outBuffer + LogBuffer::kLogDataOffset, 101); - - // Should have read out the second from front test log string which is 'a' + 1 - // = 'b' - EXPECT_TRUE(strcmp(testedBuffer, std::string(100, 'b').c_str()) == 0); - EXPECT_EQ(bytesCopied, LogBuffer::kLogDataOffset + 100 + 1); - // Should have dropped the first log - EXPECT_EQ(numLogsDropped, 1); + EXPECT_EQ(logBuffer.getBufferSize(), + (kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog); + EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected); + + for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) { + // Should have read out the i-th from front test log string which a string + // log 'a' + i. + size_t numLogsDropped; + size_t bytesCopied = + logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped); + EXPECT_TRUE(strcmp(outBuffer + LogBuffer::kLogDataOffset, + std::string(kLogPayloadSize, 'a' + i).c_str()) == 0); + EXPECT_EQ(bytesCopied, kBufferUsePerLog); + } +} + +TEST(LogBuffer, TokenizedLogOverwritten) { + char buffer[kDefaultBufferSize]; + TestLogBufferCallback callback; + LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); + + constexpr size_t kLogPayloadSize = 100; + constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset + + LogBuffer::kTokenizedLogOffset + + kLogPayloadSize; + constexpr int kNumInsertions = 10; + constexpr int kNumLogDropsExpected = + kNumInsertions - kDefaultBufferSize / kBufferUsePerLog; + static_assert(kNumLogDropsExpected > 0); + + // This for loop adds 1060 (kNumInsertions * kBufferUsePerLog) bytes of data + // through the buffer which is > than 1024. + for (size_t i = 0; i < kNumInsertions; i++) { + std::vector<uint8_t> testData(kLogPayloadSize, i); + logBuffer.handleEncodedLog(LogBufferLogLevel::INFO, 0, testData.data(), + testData.size()); + } + EXPECT_EQ(logBuffer.getBufferSize(), + (kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog); + EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected); + + for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) { + // Should have read out the i-th from front test log data which is an + // integer i. + std::vector<uint8_t> outBuffer(kBufferUsePerLog, 0x77); + size_t numLogsDropped; + size_t bytesCopied = + logBuffer.copyLogs(outBuffer.data(), outBuffer.size(), &numLogsDropped); + + // Validate that the log size in Tokenized log header matches the expected + // log size. + EXPECT_EQ(outBuffer[LogBuffer::kLogDataOffset], kLogPayloadSize); + + outBuffer.erase(outBuffer.begin(), outBuffer.begin() + + LogBuffer::kLogDataOffset + + LogBuffer::kTokenizedLogOffset); + EXPECT_THAT(outBuffer, + ContainerEq(std::vector<uint8_t>(kLogPayloadSize, i))); + EXPECT_EQ(bytesCopied, kBufferUsePerLog); + } +} + +TEST(LogBuffer, BtSnoopLogOverwritten) { + char buffer[kDefaultBufferSize]; + TestLogBufferCallback callback; + LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); + + constexpr size_t kLogPayloadSize = 100; + constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset + + LogBuffer::kBtSnoopLogOffset + + kLogPayloadSize; + constexpr int kNumInsertions = 10; + constexpr int kNumLogDropsExpected = + kNumInsertions - kDefaultBufferSize / kBufferUsePerLog; + static_assert(kNumLogDropsExpected > 0); + + // This for loop adds 1070 (kNumInsertions * kBufferUsePerLog) bytes of data + // through the buffer which is > than 1024. + for (size_t i = 0; i < kNumInsertions; i++) { + std::vector<uint8_t> testData(kLogPayloadSize, i); + logBuffer.handleBtLog(BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER, 0, + testData.data(), testData.size()); + } + EXPECT_EQ(logBuffer.getBufferSize(), + (kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog); + EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected); + + for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) { + // Should have read out the i-th from front test log data which is an + // integer i. + std::vector<uint8_t> outBuffer(kBufferUsePerLog, 0x77); + size_t numLogsDropped; + size_t bytesCopied = + logBuffer.copyLogs(outBuffer.data(), outBuffer.size(), &numLogsDropped); + + // Validate that the Bt Snoop log header matches the expected log direction + // and size. + constexpr int kBtSnoopLogHeaderSizeOffset = 1; + EXPECT_EQ( + static_cast<BtSnoopDirection>(outBuffer[LogBuffer::kLogDataOffset]), + BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER); + EXPECT_EQ( + outBuffer[LogBuffer::kLogDataOffset + kBtSnoopLogHeaderSizeOffset], + kLogPayloadSize); + + outBuffer.erase(outBuffer.begin(), outBuffer.begin() + + LogBuffer::kLogDataOffset + + LogBuffer::kBtSnoopLogOffset); + EXPECT_THAT(outBuffer, + ContainerEq(std::vector<uint8_t>(kLogPayloadSize, i))); + EXPECT_EQ(bytesCopied, kBufferUsePerLog); + } } TEST(LogBuffer, CopyIntoEmptyBuffer) { @@ -204,16 +317,16 @@ TEST(LogBuffer, TruncateLongLog) { char outBuffer[kOutBufferSize]; TestLogBufferCallback callback; LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); - std::string testStr(256, 'a'); + std::string testStr(LogBuffer::kLogMaxSize + 1, 'a'); logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testStr.c_str()); size_t numLogsDropped; size_t bytesCopied = logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped); - // Should truncate the logs down to the kLogMaxSize value of 255 by the time - // it is copied out. - EXPECT_EQ(bytesCopied, 255); + // Should truncate the logs down to the kLogMaxSize + kLogDataOffset by the + // time it is copied out. + EXPECT_EQ(bytesCopied, LogBuffer::kLogMaxSize + LogBuffer::kLogDataOffset); } TEST(LogBuffer, WouldCauseOverflowTest) { @@ -221,28 +334,33 @@ TEST(LogBuffer, WouldCauseOverflowTest) { TestLogBufferCallback callback; LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); - // With an empty buffer inerting one character should not overflow - // ASSERT because if this fails the next ASSERT statement is undefined most - // likely. + // With an empty buffer inserting an empty string (only null terminator) + // should not overflow ASSERT because if this fails the next ASSERT statement + // is undefined most likely. ASSERT_FALSE(logBuffer.logWouldCauseOverflow(1)); - // This for loop adds 1000 bytes of data. There is 24 bytes of space left in - // the buffer after this loop. + // This for loop adds 1000 bytes of data (kLogPayloadSize + kStringLogOverhead + // + kLogDataOffset). There is 24 bytes of space left in the buffer after this + // loop. + constexpr size_t kLogPayloadSize = 94; for (size_t i = 0; i < 10; i++) { - std::string testLogStrStr(94, 'a'); + std::string testLogStrStr(kLogPayloadSize, 'a'); const char *testLogStr = testLogStrStr.c_str(); logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr); } - std::string testLogStrStr(11, 'a'); + // This adds 18 (kLogPayloadSize + kStringLogOverhead + kLogDataOffset) bytes + // of data. After this log entry there is room enough for a log of character + // size 1. + constexpr size_t kLastLogPayloadSize = 12; + std::string testLogStrStr(kLastLogPayloadSize, 'a'); const char *testLogStr = testLogStrStr.c_str(); - // After this log entry there is room enough for a log of character size 1. logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr); - // There should be just enough space for this log + // There should be just enough space for this log. ASSERT_FALSE(logBuffer.logWouldCauseOverflow(1)); - // Inserting any more than a one char log should cause overflow + // Inserting any more than a one char log should cause overflow. ASSERT_TRUE(logBuffer.logWouldCauseOverflow(2)); } @@ -280,6 +398,47 @@ TEST(LogBuffer, TransferTest) { ASSERT_EQ(bytesCopied, 0); } +TEST(LogBuffer, GetLogDataLengthTest) { + char buffer[kDefaultBufferSize]; + + TestLogBufferCallback callback; + LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize); + + constexpr size_t kLogPayloadSize = 10; + constexpr size_t kBufferUseStringLog = LogBuffer::kLogDataOffset + + LogBuffer::kStringLogOverhead + + kLogPayloadSize; + constexpr size_t kBufferUseTokenizedLog = LogBuffer::kLogDataOffset + + LogBuffer::kTokenizedLogOffset + + kLogPayloadSize; + uint8_t mCurrentLogStartingIndex = 0; + + std::string testLogStr(kLogPayloadSize, 'a'); + logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr.c_str()); + EXPECT_EQ(logBuffer.getLogDataLength( + mCurrentLogStartingIndex + LogBuffer::kLogDataOffset, + LogType::STRING), + LogBuffer::kStringLogOverhead + kLogPayloadSize); + mCurrentLogStartingIndex += kBufferUseStringLog; + + std::vector<uint8_t> testLogTokenized(kLogPayloadSize, 0x77); + logBuffer.handleEncodedLog(LogBufferLogLevel::INFO, 0, + testLogTokenized.data(), testLogTokenized.size()); + EXPECT_EQ(logBuffer.getLogDataLength( + mCurrentLogStartingIndex + LogBuffer::kLogDataOffset, + LogType::TOKENIZED), + LogBuffer::kTokenizedLogOffset + kLogPayloadSize); + mCurrentLogStartingIndex += kBufferUseTokenizedLog; + + std::vector<uint8_t> testLogBtSnoop(kLogPayloadSize, 0x77); + logBuffer.handleBtLog(BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER, 0, + testLogBtSnoop.data(), testLogBtSnoop.size()); + EXPECT_EQ(logBuffer.getLogDataLength( + mCurrentLogStartingIndex + LogBuffer::kLogDataOffset, + LogType::BLUETOOTH), + LogBuffer::kBtSnoopLogOffset + kLogPayloadSize); +} + // TODO(srok): Add multithreaded tests } // namespace chre diff --git a/platform/tests/trace_test.cc b/platform/tests/trace_test.cc new file mode 100644 index 00000000..ce162a56 --- /dev/null +++ b/platform/tests/trace_test.cc @@ -0,0 +1,338 @@ +/* + * 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. + */ + +// Ensuring this macro is not defined since we include the tracing macros and +// tracing utilities separately and do not want to test or include the pw_trace +// functions. +#ifdef CHRE_TRACING_ENABLED +#undef CHRE_TRACING_ENABLED +#endif // CHRE_TRACING_ENABLED + +#include <stdlib.h> +#include <cstdlib> +#include <cstring> +#include <string> + +#include "chre/platform/tracing.h" +#include "chre/target_platform/tracing_util.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace tracing_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; + +TEST(Trace, PopulateBufferWithTracedPtr) { + const uint8_t var = 0x12; + const uint8_t *data = &var; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, __SIZEOF_POINTER__); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + +// Already verified in chre/platform/tracing.h this value is either 8 or 4. +#if __SIZEOF_POINTER__ == 8 + EXPECT_EQ(*((uint64_t *)chreTraceDataBuffer), (uint64_t)data); +#elif __SIZEOF_POINTER__ == 4 + EXPECT_EQ(*((uint32_t *)chreTraceDataBuffer), (uint32_t)data); +#else +#error "Pointer size is of unsupported size" +#endif +} + +TEST(Trace, PopulateBufferWithTracedBool) { + const bool data = true; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(bool)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(1)); +} + +TEST(Trace, PopulateBufferWithTracedUInt8) { + const uint8_t data = 0x12; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(uint8_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x12)); +} + +TEST(Trace, PopulateBufferWithTracedUInt16) { + uint16_t data = 0x1234; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(uint16_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedUInt32) { + const uint32_t data = 0x12345678; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(uint32_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x78, 0x56, 0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedUInt64) { + const uint64_t data = 0x1234567890123456; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(uint64_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, + ElementsAre(0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedInt8) { + const int8_t data = 0x12; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(int8_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x12)); +} + +TEST(Trace, PopulateBufferWithTracedInt16) { + const int16_t data = 0x1234; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(int16_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedInt32) { + const int32_t data = 0x12345678; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(int32_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre(0x78, 0x56, 0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedInt64) { + const int64_t data = 0x1234567890123456; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(int64_t)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, + ElementsAre(0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12)); +} + +TEST(Trace, PopulateBufferWithTracedChar) { + char data = 'a'; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, sizeof(char)); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAre('a')); +} + +TEST(Trace, PopulateBufferWithTracedStrDoesNotOverflow) { + const char data[] = "test"; + const size_t kBufferPadding = 5; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + uint8_t chreTraceDataBuffer[chreTraceDataSize + kBufferPadding]; + memset(chreTraceDataBuffer, 0xFF, chreTraceDataSize + kBufferPadding); + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + for (std::size_t i = 0; i < kBufferPadding; i++) { + EXPECT_EQ(chreTraceDataBuffer[chreTraceDataSize + i], 0xFF); + } +} + +TEST(Trace, PopulateBufferWithTracedShortStrAndNullBytePadding) { + // expected variable + length + const char data[] = "test"; + uint8_t dataLen = static_cast<uint8_t>(strlen(data)); + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(data)>(); + + EXPECT_EQ(chreTraceDataSize, CHRE_TRACE_STR_BUFFER_SIZE); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, data); + + // Fully populated buffer with data len and null-byte padding at the end. + uint8_t expectedBuffer[CHRE_TRACE_STR_BUFFER_SIZE] = {dataLen, 't', 'e', 's', + 't'}; + for (std::size_t i = dataLen + 1; i < CHRE_TRACE_STR_BUFFER_SIZE; i++) { + expectedBuffer[i] = '\0'; + } + + EXPECT_THAT(chreTraceDataBuffer, ElementsAreArray(expectedBuffer)); +} + +TEST(Trace, PopulateBufferWithTracedMaxLenStrNoPadding) { + // String data buffer to hold max-len string. + char dataBuffer[CHRE_TRACE_MAX_STRING_SIZE + 1]; + // Fully populated buffer with data len and no null-byte padding. + // In this case data len should equal CHRE_TRACE_MAX_STRING_SIZE. + uint8_t expectedBuffer[CHRE_TRACE_STR_BUFFER_SIZE] = { + CHRE_TRACE_MAX_STRING_SIZE}; + + // Populate the buffer with str "0123456789..." until we hit max size. + for (std::size_t i = 0; i < CHRE_TRACE_MAX_STRING_SIZE; i++) { + dataBuffer[i] = '0' + (i % 10); + expectedBuffer[i + 1] = '0' + (i % 10); + } + dataBuffer[CHRE_TRACE_MAX_STRING_SIZE] = '\0'; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(dataBuffer)>(); + + EXPECT_EQ(chreTraceDataSize, CHRE_TRACE_STR_BUFFER_SIZE); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, + dataBuffer); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAreArray(expectedBuffer)); +} + +TEST(Trace, PopulateBufferWithTracedStringTuncateToMaxLength) { + const size_t kBufferPadding = 5; + const std::size_t kOversizeStrLen = + CHRE_TRACE_MAX_STRING_SIZE + kBufferPadding; + // String data buffer to hold oversized string. + char dataBuffer[kOversizeStrLen + 1]; + + // Populate the buffer with str "0123456789..." until we hit kOversizeStrLen. + for (std::size_t i = 0; i < kOversizeStrLen; i++) { + dataBuffer[i] = '0' + (i % 10); + } + dataBuffer[kOversizeStrLen] = '\0'; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST(dataBuffer)>(); + + EXPECT_EQ(chreTraceDataSize, CHRE_TRACE_STR_BUFFER_SIZE); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs(chreTraceDataBuffer, + dataBuffer); + + // Fully populated buffer with data len and truncated string. + // In this case data len should equal CHRE_TRACE_MAX_STRING_SIZE, not + // kOversizeStrLen. + uint8_t expectedBuffer[CHRE_TRACE_STR_BUFFER_SIZE] = { + CHRE_TRACE_MAX_STRING_SIZE}; + + // Populate the buffer with str "0123456789..." until we hit + // CHRE_TRACE_MAX_STRING_SIZE. + for (std::size_t i = 0; i < CHRE_TRACE_MAX_STRING_SIZE; i++) { + expectedBuffer[i + 1] = '0' + (i % 10); + } + + EXPECT_THAT(chreTraceDataBuffer, ElementsAreArray(expectedBuffer)); +} + +TEST(Trace, PopulateBufferWithMultipleTracedData) { + uint8_t dataUint8 = 0x12; + char dataChar = 'a'; + uint32_t dataUint32 = 0x12345678; + char dataStr[CHRE_TRACE_MAX_STRING_SIZE]; + strncpy(dataStr, "test", CHRE_TRACE_MAX_STRING_SIZE); + uint8_t dataStrLen = static_cast<uint8_t>(strlen(dataStr)); + std::size_t totalDataSize = sizeof(uint8_t) + sizeof(char) + + sizeof(uint32_t) + CHRE_TRACE_STR_BUFFER_SIZE; + + constexpr std::size_t chreTraceDataSize = + tracing_internal::chreTraceGetSizeOfVarArgs<TYPE_LIST( + dataUint8, dataChar, dataUint32, dataStr)>(); + + EXPECT_EQ(chreTraceDataSize, totalDataSize); + + uint8_t expectedBuffer[chreTraceDataSize] = {0x12, 'a', 0x78, 0x56, + 0x34, 0x12, dataStrLen}; + strncpy((char *)(&expectedBuffer[7]), dataStr, CHRE_TRACE_MAX_STRING_SIZE); + + uint8_t chreTraceDataBuffer[chreTraceDataSize]; + tracing_internal::chreTracePopulateBufferWithArgs( + chreTraceDataBuffer, dataUint8, dataChar, dataUint32, dataStr); + + EXPECT_THAT(chreTraceDataBuffer, ElementsAreArray(expectedBuffer)); +} + +// TODO(b/302232350): Add a death test for unsupported data types. Currently +// testing unsupported types (structs) with manual testing. + +} // namespace +} // namespace tracing_internal
\ No newline at end of file diff --git a/platform/tinysys/authentication.cc b/platform/tinysys/authentication.cc index e33555e4..259ba678 100644 --- a/platform/tinysys/authentication.cc +++ b/platform/tinysys/authentication.cc @@ -24,9 +24,14 @@ #include "mbedtls/pk.h" #include "mbedtls/sha256.h" +#include "cpufreq_vote.h" + namespace chre { namespace { +// A data structure needed for SCP chip frequency change +DECLARE_OPPDEV(gChreScpFreqVote); + // All the size below are in bytes constexpr uint32_t kEcdsaP256SigSize = 64; constexpr uint32_t kEcdsaP256PublicKeySize = 64; @@ -101,6 +106,7 @@ struct ImageHeader { class Authenticator { public: Authenticator() { + scp_vote_opp(&gChreScpFreqVote, CLK_OPP2); mbedtls_ecp_group_init(&mGroup); mbedtls_ecp_point_init(&mQ); mbedtls_mpi_init(&mR); @@ -112,6 +118,7 @@ class Authenticator { mbedtls_mpi_free(&mR); mbedtls_ecp_point_free(&mQ); mbedtls_ecp_group_free(&mGroup); + scp_unvote_opp(&gChreScpFreqVote, CLK_OPP2); } bool loadEcpGroup() { diff --git a/platform/tinysys/condition_variable_base.cc b/platform/tinysys/condition_variable_base.cc index 715b46ba..77b993de 100644 --- a/platform/tinysys/condition_variable_base.cc +++ b/platform/tinysys/condition_variable_base.cc @@ -32,8 +32,7 @@ void ConditionVariableBase::conditionVariablTimerCallback( } ConditionVariable::ConditionVariable() { - // TODO(b/256870101): Use xSemaphoreCreateBinaryStatic when possible - semaphoreHandle = xSemaphoreCreateBinary(); + semaphoreHandle = xSemaphoreCreateBinaryStatic(&mSemaphoreBuffer); if (semaphoreHandle == nullptr) { FATAL_ERROR("Failed to create cv semaphore"); } @@ -79,4 +78,4 @@ bool ConditionVariable::wait_for(Mutex &mutex, Nanoseconds timeout) { taskEXIT_CRITICAL(); return isTimedOut; } -} // namespace chre
\ No newline at end of file +} // namespace chre diff --git a/platform/tinysys/host_link.cc b/platform/tinysys/host_link.cc index 3df05170..5ef720a0 100644 --- a/platform/tinysys/host_link.cc +++ b/platform/tinysys/host_link.cc @@ -15,6 +15,7 @@ */ #include "FreeRTOS.h" +#include "encoding.h" #include "task.h" #include "chre/core/event_loop_manager.h" @@ -83,10 +84,11 @@ size_t gChreSubregionRecvSize; void *gChreSubregionSendAddr; size_t gChreSubregionSendSize; -// TODO(b/263958729): move it to HostLinkBase, and revisit buffer size +// TODO(b/277235389): move it to HostLinkBase, and revisit buffer size // payload buffers #define CHRE_IPI_RECV_BUFFER_SIZE (CHRE_MESSAGE_TO_HOST_MAX_SIZE + 128) -uint32_t gChreRecvBuffer[CHRE_IPI_RECV_BUFFER_SIZE / sizeof(uint32_t)] +DRAM_REGION_VARIABLE uint32_t + gChreRecvBuffer[CHRE_IPI_RECV_BUFFER_SIZE / sizeof(uint32_t)] __attribute__((aligned(CACHE_LINE_SIZE))); #ifdef SCP_CHRE_USE_DMA @@ -123,6 +125,8 @@ enum class PendingMessageType { SelfTestResponse, MetricLog, NanConfigurationRequest, + PulseRequest, + PulseResponse, }; struct PendingMessage { @@ -151,7 +155,8 @@ struct PendingMessage { }; constexpr size_t kOutboundQueueSize = 100; -FixedSizeBlockingQueue<PendingMessage, kOutboundQueueSize> gOutboundQueue; +DRAM_REGION_VARIABLE FixedSizeBlockingQueue<PendingMessage, kOutboundQueueSize> + gOutboundQueue; typedef void(MessageBuilderFunction)(ChreFlatBufferBuilder &builder, void *cookie); @@ -160,7 +165,8 @@ inline HostCommsManager &getHostCommsManager() { return EventLoopManagerSingleton::get()->getHostCommsManager(); } -bool generateMessageFromBuilder(ChreFlatBufferBuilder *builder) { +DRAM_REGION_FUNCTION bool generateMessageFromBuilder( + ChreFlatBufferBuilder *builder) { CHRE_ASSERT(builder != nullptr); LOGV("%s: message size %d", __func__, builder->GetSize()); bool result = @@ -172,9 +178,9 @@ bool generateMessageFromBuilder(ChreFlatBufferBuilder *builder) { return result; } -bool generateMessageToHost(const HostMessage *message) { +DRAM_REGION_FUNCTION bool generateMessageToHost(const HostMessage *message) { LOGV("%s: message size %zu", __func__, message->message.size()); - // TODO(b/263958729): ideally we'd construct our flatbuffer directly in the + // TODO(b/285219398): ideally we'd construct our flatbuffer directly in the // host-supplied buffer constexpr size_t kFixedReserveSize = 80; ChreFlatBufferBuilder builder(message->message.size() + kFixedReserveSize); @@ -191,7 +197,7 @@ bool generateMessageToHost(const HostMessage *message) { return result; } -int generateHubInfoResponse(uint16_t hostClientId) { +DRAM_REGION_FUNCTION int generateHubInfoResponse(uint16_t hostClientId) { constexpr size_t kInitialBufferSize = 192; constexpr char kHubName[] = "CHRE on Tinysys"; @@ -219,7 +225,7 @@ int generateHubInfoResponse(uint16_t hostClientId) { return HostLinkBase::send(builder.GetBufferPointer(), builder.GetSize()); } -bool dequeueMessage(PendingMessage pendingMsg) { +DRAM_REGION_FUNCTION bool dequeueMessage(PendingMessage pendingMsg) { LOGV("%s: message type %d", __func__, pendingMsg.type); bool result = false; switch (pendingMsg.type) { @@ -243,6 +249,7 @@ bool dequeueMessage(PendingMessage pendingMsg) { case PendingMessageType::SelfTestResponse: case PendingMessageType::MetricLog: case PendingMessageType::NanConfigurationRequest: + case PendingMessageType::PulseResponse: result = generateMessageFromBuilder(pendingMsg.data.builder); break; @@ -260,7 +267,7 @@ bool dequeueMessage(PendingMessage pendingMsg) { * * @return true if the message was successfully added to the queue. */ -bool enqueueMessage(PendingMessage pendingMsg) { +DRAM_REGION_FUNCTION bool enqueueMessage(PendingMessage pendingMsg) { return gOutboundQueue.push(pendingMsg); } @@ -278,9 +285,9 @@ bool enqueueMessage(PendingMessage pendingMsg) { * * @return true if the message was successfully added to the queue */ -bool buildAndEnqueueMessage(PendingMessageType msgType, - size_t initialBufferSize, - MessageBuilderFunction *msgBuilder, void *cookie) { +DRAM_REGION_FUNCTION bool buildAndEnqueueMessage( + PendingMessageType msgType, size_t initialBufferSize, + MessageBuilderFunction *msgBuilder, void *cookie) { LOGV("%s: message type %d, size %zu", __func__, msgType, initialBufferSize); bool pushed = false; @@ -291,8 +298,6 @@ bool buildAndEnqueueMessage(PendingMessageType msgType, } else { msgBuilder(*builder, cookie); - // TODO(b/263958729): if this fails, ideally we should block for some - // timeout until there's space in the queue if (!enqueueMessage(PendingMessage(msgType, builder.get()))) { LOGE("Couldn't push message type %d to outbound queue", static_cast<int>(msgType)); @@ -308,7 +313,16 @@ bool buildAndEnqueueMessage(PendingMessageType msgType, /** * FlatBuffer message builder callback used with handleNanoappListRequest() */ -void buildNanoappListResponse(ChreFlatBufferBuilder &builder, void *cookie) { +DRAM_REGION_FUNCTION void buildPulseResponse(ChreFlatBufferBuilder &builder, + void * /*cookie*/) { + HostProtocolChre::encodePulseResponse(builder); +} + +/** + * FlatBuffer message builder callback used with handleNanoappListRequest() + */ +DRAM_REGION_FUNCTION void buildNanoappListResponse( + ChreFlatBufferBuilder &builder, void *cookie) { LOGV("%s", __func__); auto nanoappAdderCallback = [](const Nanoapp *nanoapp, void *data) { auto *cbData = static_cast<NanoappListData *>(data); @@ -327,8 +341,9 @@ void buildNanoappListResponse(ChreFlatBufferBuilder &builder, void *cookie) { cbData->hostClientId); } -void handleUnloadNanoappCallback(uint16_t /*type*/, void *data, - void * /*extraData*/) { +DRAM_REGION_FUNCTION void handleUnloadNanoappCallback(uint16_t /*type*/, + void *data, + void * /*extraData*/) { auto *cbData = static_cast<UnloadNanoappCallbackData *>(data); bool success = false; uint16_t instanceId; @@ -356,25 +371,79 @@ void handleUnloadNanoappCallback(uint16_t /*type*/, void *data, memoryFree(data); } +DRAM_REGION_FUNCTION void sendDebugDumpData(uint16_t hostClientId, + const char *debugStr, + size_t debugStrSize) { + struct DebugDumpMessageData { + uint16_t hostClientId; + const char *debugStr; + size_t debugStrSize; + }; + + auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { + const auto *data = static_cast<const DebugDumpMessageData *>(cookie); + HostProtocolChre::encodeDebugDumpData(builder, data->hostClientId, + data->debugStr, data->debugStrSize); + }; + + constexpr size_t kFixedSizePortion = 52; + DebugDumpMessageData data; + data.hostClientId = hostClientId; + data.debugStr = debugStr; + data.debugStrSize = debugStrSize; + buildAndEnqueueMessage(PendingMessageType::DebugDumpData, + kFixedSizePortion + debugStrSize, msgBuilder, &data); +} + +DRAM_REGION_FUNCTION void sendDebugDumpResponse(uint16_t hostClientId, + bool success, + uint32_t dataCount) { + struct DebugDumpResponseData { + uint16_t hostClientId; + bool success; + uint32_t dataCount; + }; + + auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { + const auto *data = static_cast<const DebugDumpResponseData *>(cookie); + HostProtocolChre::encodeDebugDumpResponse(builder, data->hostClientId, + data->success, data->dataCount); + }; + + constexpr size_t kInitialSize = 52; + DebugDumpResponseData data; + data.hostClientId = hostClientId; + data.success = success; + data.dataCount = dataCount; + buildAndEnqueueMessage(PendingMessageType::DebugDumpResponse, kInitialSize, + msgBuilder, &data); +} } // anonymous namespace -void sendDebugDumpResultToHost(uint16_t hostClientId, const char * /*debugStr*/, - size_t /*debugStrSize*/, bool /*complete*/, - uint32_t /*dataCount*/) { +DRAM_REGION_FUNCTION void sendDebugDumpResultToHost(uint16_t hostClientId, + const char *debugStr, + size_t debugStrSize, + bool complete, + uint32_t dataCount) { LOGV("%s: host client id %d", __func__, hostClientId); - // TODO(b/263958729): Implement this. + if (debugStrSize > 0) { + sendDebugDumpData(hostClientId, debugStr, debugStrSize); + } + if (complete) { + sendDebugDumpResponse(hostClientId, /* success= */ true, dataCount); + } } -HostLinkBase::HostLinkBase() { +DRAM_REGION_FUNCTION HostLinkBase::HostLinkBase() { LOGV("HostLinkBase::%s", __func__); initializeIpi(); } -HostLinkBase::~HostLinkBase() { +DRAM_REGION_FUNCTION HostLinkBase::~HostLinkBase() { LOGV("HostLinkBase::%s", __func__); } -void HostLinkBase::vChreReceiveTask(void *pvParameters) { +DRAM_REGION_FUNCTION void HostLinkBase::vChreReceiveTask(void *pvParameters) { int i = 0; int ret = 0; @@ -389,15 +458,16 @@ void HostLinkBase::vChreReceiveTask(void *pvParameters) { } } -void HostLinkBase::vChreSendTask(void *pvParameters) { +DRAM_REGION_FUNCTION void HostLinkBase::vChreSendTask(void *pvParameters) { while (true) { auto msg = gOutboundQueue.pop(); dequeueMessage(msg); } } -void HostLinkBase::chreIpiHandler(unsigned int id, void *prdata, void *data, - unsigned int len) { +DRAM_REGION_FUNCTION void HostLinkBase::chreIpiHandler(unsigned int id, + void *prdata, void *data, + unsigned int len) { /* receive magic and cmd */ struct ScpChreIpiMsg msg = *(struct ScpChreIpiMsg *)data; @@ -441,7 +511,7 @@ void HostLinkBase::chreIpiHandler(unsigned int id, void *prdata, void *data, gChreIpiAckToHost[1] = msg.size; } -void HostLinkBase::initializeIpi(void) { +DRAM_REGION_FUNCTION void HostLinkBase::initializeIpi(void) { LOGV("%s", __func__); bool success = false; int ret; @@ -482,11 +552,11 @@ void HostLinkBase::initializeIpi(void) { } } -void HostLinkBase::receive(HostLinkBase *instance, void *message, - int messageLen) { +DRAM_REGION_FUNCTION void HostLinkBase::receive(HostLinkBase *instance, + void *message, int messageLen) { LOGV("%s: message len %d", __func__, messageLen); - // TODO(b/263958729): A crude way to initially determine daemon's up - set + // TODO(b/277128368): A crude way to initially determine daemon's up - set // a flag on the first message received. This is temporary until a better // way to do this is available. instance->setInitialized(true); @@ -496,7 +566,7 @@ void HostLinkBase::receive(HostLinkBase *instance, void *message, } } -bool HostLinkBase::send(uint8_t *data, size_t dataLen) { +DRAM_REGION_FUNCTION bool HostLinkBase::send(uint8_t *data, size_t dataLen) { #ifndef HOST_LINK_IPI_SEND_TIMEOUT_MS #define HOST_LINK_IPI_SEND_TIMEOUT_MS 100 #endif @@ -513,7 +583,7 @@ bool HostLinkBase::send(uint8_t *data, size_t dataLen) { ap_to_scp(reinterpret_cast<uint32_t>(gChreSubregionSendAddr))); #ifdef SCP_CHRE_USE_DMA - // TODO(b/263958729): use DMA for larger payload + // TODO(b/288415339): use DMA for larger payload // No need cache operation, because src_dst handled by SCP CPU and dstAddr is // non-cacheable #else @@ -548,14 +618,10 @@ bool HostLinkBase::send(uint8_t *data, size_t dataLen) { return ret == IPI_ACTION_DONE; } -void HostLinkBase::sendTimeSyncRequest() { - LOGV("%s", __func__); - // TODO(b/263958729): Implement this. -} +DRAM_REGION_FUNCTION void HostLinkBase::sendTimeSyncRequest() {} -void HostLinkBase::sendLogMessageV2(const uint8_t *logMessage, - size_t logMessageSize, - uint32_t numLogsDropped) { +DRAM_REGION_FUNCTION void HostLinkBase::sendLogMessageV2( + const uint8_t *logMessage, size_t logMessageSize, uint32_t numLogsDropped) { LOGV("%s: size %zu", __func__, logMessageSize); struct LogMessageData { const uint8_t *logMsg; @@ -589,7 +655,7 @@ void HostLinkBase::sendLogMessageV2(const uint8_t *logMessage, #endif } -bool HostLink::sendMessage(HostMessage const *message) { +DRAM_REGION_FUNCTION bool HostLink::sendMessage(HostMessage const *message) { LOGV("HostLink::%s size(%zu)", __func__, message->message.size()); bool success = false; @@ -602,32 +668,31 @@ bool HostLink::sendMessage(HostMessage const *message) { return success; } -// TODO(b/263958729): HostMessageHandlers member function implementations are +// TODO(b/285219398): HostMessageHandlers member function implementations are // expected to be (mostly) identical for any platform that uses flatbuffers // to encode messages - refactor the host link to merge the multiple copies // we currently have. -void HostMessageHandlers::handleNanoappMessage(uint64_t appId, - uint32_t messageType, - uint16_t hostEndpoint, - const void *messageData, - size_t messageDataLen) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleNanoappMessage( + uint64_t appId, uint32_t messageType, uint16_t hostEndpoint, + const void *messageData, size_t messageDataLen) { LOGV("Parsed nanoapp message from host: app ID 0x%016" PRIx64 ", endpoint " "0x%" PRIx16 ", msgType %" PRIu32 ", payload size %zu", appId, hostEndpoint, messageType, messageDataLen); - // TODO(b/263958729): Implement this. getHostCommsManager().sendMessageToNanoappFromHost( appId, messageType, hostEndpoint, messageData, messageDataLen); } -void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleHubInfoRequest( + uint16_t hostClientId) { LOGV("%s: host client id %d", __func__, hostClientId); enqueueMessage( PendingMessage(PendingMessageType::HubInfoResponse, hostClientId)); } -void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleNanoappListRequest( + uint16_t hostClientId) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { uint16_t cbHostClientId = NestedDataPtr<uint16_t>(data); @@ -656,10 +721,9 @@ void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) { NestedDataPtr<uint16_t>(hostClientId), callback); } -void HostMessageHandlers::sendFragmentResponse(uint16_t hostClientId, - uint32_t transactionId, - uint32_t fragmentId, - bool success) { +DRAM_REGION_FUNCTION void HostMessageHandlers::sendFragmentResponse( + uint16_t hostClientId, uint32_t transactionId, uint32_t fragmentId, + bool success) { struct FragmentedLoadInfoResponse { uint16_t hostClientId; uint32_t transactionId; @@ -685,7 +749,17 @@ void HostMessageHandlers::sendFragmentResponse(uint16_t hostClientId, kInitialBufferSize, msgBuilder, &response); } -void HostMessageHandlers::handleLoadNanoappRequest( +DRAM_REGION_FUNCTION void HostMessageHandlers::handlePulseRequest() { + auto callback = [](uint16_t /*type*/, void * /*data*/, void * /*extraData*/) { + buildAndEnqueueMessage(PendingMessageType::PulseResponse, + /*initialBufferSize= */ 48, buildPulseResponse, + /* cookie= */ nullptr); + }; + EventLoopManagerSingleton::get()->deferCallback( + SystemCallbackType::PulseResponse, /* data= */ nullptr, callback); +} + +DRAM_REGION_FUNCTION void HostMessageHandlers::handleLoadNanoappRequest( uint16_t hostClientId, uint32_t transactionId, uint64_t appId, uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion, const void *buffer, size_t bufferLen, const char *appFileName, @@ -697,7 +771,7 @@ void HostMessageHandlers::handleLoadNanoappRequest( respondBeforeStart); } -void HostMessageHandlers::handleUnloadNanoappRequest( +DRAM_REGION_FUNCTION void HostMessageHandlers::handleUnloadNanoappRequest( uint16_t hostClientId, uint32_t transactionId, uint64_t appId, bool allowSystemNanoappUnload) { LOGD("Unload nanoapp request from client %" PRIu16 " (txnID %" PRIu32 @@ -718,22 +792,31 @@ void HostMessageHandlers::handleUnloadNanoappRequest( } } -void HostLink::flushMessagesSentByNanoapp(uint64_t /* appId */) { +DRAM_REGION_FUNCTION void HostLink::flushMessagesSentByNanoapp( + uint64_t /* appId */) { // Not implemented } -void HostMessageHandlers::handleTimeSyncMessage(int64_t offset) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleTimeSyncMessage( + int64_t offset) { LOGE("%s unsupported.", __func__); } -void HostMessageHandlers::handleDebugDumpRequest(uint16_t hostClientId) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleDebugDumpRequest( + uint16_t hostClientId) { LOGV("%s: host client id %d", __func__, hostClientId); - // TODO(b/263958729): Implement this. + if (!EventLoopManagerSingleton::get() + ->getDebugDumpManager() + .onDebugDumpRequested(hostClientId)) { + LOGE("Couldn't trigger debug dump process"); + sendDebugDumpResponse(hostClientId, /* success= */ false, + /* dataCount= */ 0); + } } -void HostMessageHandlers::handleSettingChangeMessage(fbs::Setting setting, - fbs::SettingState state) { - // TODO(b/267207477): Refactor handleSettingChangeMessage to shared code +DRAM_REGION_FUNCTION void HostMessageHandlers::handleSettingChangeMessage( + fbs::Setting setting, fbs::SettingState state) { + // TODO(b/285219398): Refactor handleSettingChangeMessage to shared code Setting chreSetting; bool chreSettingEnabled; if (HostProtocolChre::getSettingFromFbs(setting, &chreSetting) && @@ -743,16 +826,17 @@ void HostMessageHandlers::handleSettingChangeMessage(fbs::Setting setting, } } -void HostMessageHandlers::handleSelfTestRequest(uint16_t hostClientId) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleSelfTestRequest( + uint16_t hostClientId) { LOGV("%s: host client id %d", __func__, hostClientId); - // TODO(b/263958729): Implement this. } -void HostMessageHandlers::handleNanConfigurationUpdate(bool /* enabled */) { +DRAM_REGION_FUNCTION void HostMessageHandlers::handleNanConfigurationUpdate( + bool /* enabled */) { LOGE("%s NAN unsupported.", __func__); } -void sendAudioRequest() { +DRAM_REGION_FUNCTION void sendAudioRequest() { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void * /*cookie*/) { HostProtocolChre::encodeLowPowerMicAccessRequest(builder); }; @@ -761,7 +845,7 @@ void sendAudioRequest() { kInitialSize, msgBuilder, /* cookie= */ nullptr); } -void sendAudioRelease() { +DRAM_REGION_FUNCTION void sendAudioRelease() { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void * /*cookie*/) { HostProtocolChre::encodeLowPowerMicAccessRelease(builder); }; diff --git a/platform/tinysys/include/chre/target_platform/condition_variable_base.h b/platform/tinysys/include/chre/target_platform/condition_variable_base.h index ba030217..46f2f939 100644 --- a/platform/tinysys/include/chre/target_platform/condition_variable_base.h +++ b/platform/tinysys/include/chre/target_platform/condition_variable_base.h @@ -47,6 +47,9 @@ class ConditionVariableBase { /** semaphore implementing the condition variable */ SemaphoreHandle_t semaphoreHandle; + /** Buffer used to store state used by the semaphore */ + StaticSemaphore_t mSemaphoreBuffer; + /** True if wait_for() times out before semaphoreHandle is given */ bool isTimedOut = false; diff --git a/run_pal_impl_tests.sh b/run_pal_impl_tests.sh index 4b0daedb..9e7426ef 100755 --- a/run_pal_impl_tests.sh +++ b/run_pal_impl_tests.sh @@ -3,6 +3,15 @@ # Quit if any command produces an error. set -e +BUILD_ONLY="false" +while getopts "b" opt; do + case ${opt} in + b) + BUILD_ONLY="true" + ;; + esac +done + # Build and run the CHRE unit test binary. JOB_COUNT=$((`grep -c ^processor /proc/cpuinfo`)) @@ -14,4 +23,13 @@ export RUN_PAL_IMPL_TESTS=true make clean make google_x86_googletest_debug -j$JOB_COUNT -./out/google_x86_googletest_debug/libchre $1 + +if [ "$BUILD_ONLY" = "false" ]; then +./out/google_x86_googletest_debug/libchre ${@:1} +else + if [ ! -f ./out/google_x86_googletest_debug/libchre ]; then + echo "./out/google_x86_googletest_debug/libchre does not exist." + echo "run_pal_impl_test.sh failed to build the binary." + exit 1 + fi +fi
\ No newline at end of file @@ -30,4 +30,9 @@ make clean && make google_x86_linux_debug -j$JOB_COUNT if [ "$BUILD_ONLY" = "false" ]; then ./out/google_x86_linux_debug/libchre ${@:1} +else + if [ ! -f ./out/google_x86_linux_debug/libchre ]; then + echo "./out/google_x86_linux_debug/libchre does not exist." + exit 1 + fi fi diff --git a/run_tests.sh b/run_tests.sh index b73c2534..ddb2271e 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -3,6 +3,15 @@ # Quit if any command produces an error. set -e +BUILD_ONLY="false" +while getopts "b" opt; do + case ${opt} in + b) + BUILD_ONLY="true" + ;; + esac +done + # Build and run the CHRE unit test binary. JOB_COUNT=$((`grep -c ^processor /proc/cpuinfo`)) @@ -12,4 +21,12 @@ export CHRE_VARIANT_MK_INCLUDES="$CHRE_VARIANT_MK_INCLUDES \ make clean make google_x86_googletest_debug -j$JOB_COUNT -./out/google_x86_googletest_debug/libchre "$@" + +if [ "$BUILD_ONLY" = "false" ]; then +./out/google_x86_googletest_debug/libchre ${@:1} +else + if [ ! -f ./out/google_x86_googletest_debug/libchre ]; then + echo "./out/google_x86_googletest_debug/libchre does not exist." + exit 1 + fi +fi
\ No newline at end of file diff --git a/test/simulation/README.md b/test/simulation/README.md index db380ea7..86938ad3 100644 --- a/test/simulation/README.md +++ b/test/simulation/README.md @@ -31,9 +31,8 @@ TEST_F(TestBase, <PrefixedTestName>) { CREATE_CHRE_TEST_EVENT(MY_TEST_EVENT, 0); // 2. Create a test Nanpoapp by inheriting TestNanoapp. - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public TestNanoapp { + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { // 3. Handle system events. case CHRE_EVENT_WIFI_ASYNC_RESULT: { @@ -54,36 +53,45 @@ TEST_F(TestBase, <PrefixedTestName>) { } } } - }; + } }; // 6. Load the app and add initial expectations. - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>());; EXPECT_TRUE(...); // 7. Send test events to the Nanoapp to execute some actions and add // expectations about the result. - sendEventToNanoapp(app, MY_TEST_EVENT); + sendEventToNanoapp(appId, MY_TEST_EVENT); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT); EXPECT_TRUE(...); // 8. Optionally unload the Nanoapp - unloadNanoapp(app); + unloadNanoapp(appId); } ``` ##### Test app (#2, #6, #8) -Inherit from `TestNanoapp` to create a test nanoapp. The following -properties oif a nanoapp can be overridden `name`, `id`, `version`, `perms`, -`start`, `handleEvent`, and `end`. +Inherit from `TestNanoapp` to create a test nanoapp. + +If you need to customize any of the nanoapp `name`, `id`, `version`, or `perms`, +you will need to add a constructor calling the `TestNanoapp` constructor with that info, i.e.: + +``` +class App: public TestNanoapp { + public: + explicit App(TestNanoappInfo info): TestNanoapp(info) {} + + // ... +}; +``` -Typical tests only override of few of the above properties: +The nanoapp entry points are implemented as methods of the class: -* `perms` to set the permissions required for the test, -* `start` to put the system in a known state before each test, -* `handleEvent` is probably the most important function where system and test - events are handled. See the sections below for more details. +- `start`, +- `handleEvent`, +- `end`. ##### Test events (#1) @@ -97,15 +105,14 @@ Add code to `handleEvent` to handle the system events you are interested in for the test: ```cpp -decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { +void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { // ... break; } } -}; +} ``` The handler would typically send an event back to the nanoapp, see the next @@ -137,20 +144,18 @@ Waiting for an event as described above is sufficient to express a boolean expectation. For example the status of an event: ```cpp - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT); - } - break; +void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT); } + break; } - }; -}; + } +} ``` With the above snippet `waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT)` will timeout @@ -160,20 +165,19 @@ Sometimes you want to attach additional data alongside the event. Simply pass the data as the second argument to pushEvent: ```cpp - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); } + break; } - }; + } + } ``` The data must be trivially copyable (a scalar or a struct of scalar are safe). @@ -197,15 +201,14 @@ CREATE_CHRE_TEST_EVENT(MY_TEST_EVENT, 0); // ... -sendEventToNanoapp(app, MY_TEST_EVENT); +sendEventToNanoapp(appId, MY_TEST_EVENT); ``` The code to be executed in the context of the nanoapp should be added to its `handleEvent` function: ```cpp -decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { +void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { // Test event are received with a CHRE_EVENT_TEST_EVENT type. case CHRE_EVENT_TEST_EVENT: { @@ -218,14 +221,14 @@ decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, } } } -}; +} ``` It is possible to send data alongside a test event: ```cpp bool enable = true; -sendEventToNanoapp(app, MY_TEST_EVENT, &enable); +sendEventToNanoapp(appId, MY_TEST_EVENT, &enable); ``` The data should be a scalar type or a struct of scalars. Be careful not to send @@ -236,8 +239,7 @@ The `handleEvent` function receives a copy of the data in the `data` field of the `TestEvent`: ```cpp -decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { +void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { // Test event are received with a CHRE_EVENT_TEST_EVENT type. case CHRE_EVENT_TEST_EVENT: { @@ -250,5 +252,5 @@ decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, } } } -}; +} ``` diff --git a/test/simulation/audio_test.cc b/test/simulation/audio_test.cc index df03aaa9..85ddc6d3 100644 --- a/test/simulation/audio_test.cc +++ b/test/simulation/audio_test.cc @@ -36,31 +36,33 @@ namespace chre { namespace { -struct AudioNanoapp : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_AUDIO; +class AudioNanoapp : public TestNanoapp { + public: + AudioNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_AUDIO}) {} - decltype(nanoappStart) *start = []() { + bool start() override { chreUserSettingConfigureEvents(CHRE_USER_SETTING_MICROPHONE, true /* enable */); return true; - }; + } }; TEST_F(TestBase, AudioCanSubscribeAndUnsubscribeToDataEvents) { CREATE_CHRE_TEST_EVENT(CONFIGURE, 0); - struct App : public AudioNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static int count = 0; - + class App : public AudioNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_AUDIO_DATA: { auto event = static_cast<const struct chreAudioDataEvent *>(eventData); if (event->handle == 0) { - count++; - if (count == 3) { + mCount++; + if (mCount == 3) { TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_AUDIO_DATA); } } @@ -91,15 +93,18 @@ TEST_F(TestBase, AudioCanSubscribeAndUnsubscribeToDataEvents) { } } } - }; + } + + protected: + int mCount = 0; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EXPECT_FALSE(chrePalAudioIsHandle0Enabled()); bool enable = true; bool success; - sendEventToNanoapp(app, CONFIGURE, enable); + sendEventToNanoapp(appId, CONFIGURE, enable); waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_AUDIO_SAMPLING_CHANGE); @@ -108,7 +113,7 @@ TEST_F(TestBase, AudioCanSubscribeAndUnsubscribeToDataEvents) { waitForEvent(CHRE_EVENT_AUDIO_DATA); enable = false; - sendEventToNanoapp(app, CONFIGURE, enable); + sendEventToNanoapp(appId, CONFIGURE, enable); waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); EXPECT_FALSE(chrePalAudioIsHandle0Enabled()); @@ -117,9 +122,9 @@ TEST_F(TestBase, AudioCanSubscribeAndUnsubscribeToDataEvents) { TEST_F(TestBase, AudioUnsubscribeToDataEventsOnUnload) { CREATE_CHRE_TEST_EVENT(CONFIGURE, 0); - struct App : public AudioNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public AudioNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { case CHRE_EVENT_AUDIO_SAMPLING_CHANGE: { auto event = @@ -145,21 +150,21 @@ TEST_F(TestBase, AudioUnsubscribeToDataEventsOnUnload) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EXPECT_FALSE(chrePalAudioIsHandle0Enabled()); bool enable = true; bool success; - sendEventToNanoapp(app, CONFIGURE, enable); + sendEventToNanoapp(appId, CONFIGURE, enable); waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_AUDIO_SAMPLING_CHANGE); EXPECT_TRUE(chrePalAudioIsHandle0Enabled()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalAudioIsHandle0Enabled()); } diff --git a/test/simulation/ble_test.cc b/test/simulation/ble_test.cc index 24fe044e..a0714202 100644 --- a/test/simulation/ble_test.cc +++ b/test/simulation/ble_test.cc @@ -43,11 +43,14 @@ TEST_F(TestBase, BleCapabilitiesTest) { CREATE_CHRE_TEST_EVENT(GET_CAPABILITIES, 0); CREATE_CHRE_TEST_EVENT(GET_FILTER_CAPABILITIES, 1); - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); @@ -66,37 +69,40 @@ TEST_F(TestBase, BleCapabilitiesTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); uint32_t capabilities; - sendEventToNanoapp(app, GET_CAPABILITIES); + sendEventToNanoapp(appId, GET_CAPABILITIES); waitForEvent(GET_CAPABILITIES, &capabilities); ASSERT_EQ(capabilities, CHRE_BLE_CAPABILITIES_SCAN | CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING | CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT); - sendEventToNanoapp(app, GET_FILTER_CAPABILITIES); + sendEventToNanoapp(appId, GET_FILTER_CAPABILITIES); waitForEvent(GET_FILTER_CAPABILITIES, &capabilities); ASSERT_EQ(capabilities, CHRE_BLE_FILTER_CAPABILITIES_RSSI | CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA); } -struct BleTestNanoapp : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_BLE; +class BleTestNanoapp : public TestNanoapp { + public: + BleTestNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_BLE}) {} - decltype(nanoappStart) *start = []() { + bool start() override { chreUserSettingConfigureEvents(CHRE_USER_SETTING_BLE_AVAILABLE, true /* enable */); return true; - }; + } - decltype(nanoappEnd) *end = []() { + void end() override { chreUserSettingConfigureEvents(CHRE_USER_SETTING_BLE_AVAILABLE, false /* enable */); - }; + } }; /** @@ -109,9 +115,10 @@ TEST_F(TestBase, BleSimpleScanTest) { CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -150,20 +157,20 @@ TEST_F(TestBase, BleSimpleScanTest) { break; } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); bool success; - sendEventToNanoapp(app, START_SCAN); + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STARTED); ASSERT_TRUE(chrePalIsBleEnabled()); waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT); - sendEventToNanoapp(app, STOP_SCAN); + sendEventToNanoapp(appId, STOP_SCAN); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); @@ -174,9 +181,10 @@ TEST_F(TestBase, BleStopScanOnUnload) { CREATE_CHRE_TEST_EVENT(START_SCAN, 0); CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -200,19 +208,19 @@ TEST_F(TestBase, BleStopScanOnUnload) { break; } } - }; + } }; - auto app = loadNanoapp<App>(); - bool success; + uint64_t appId = loadNanoapp(MakeUnique<App>()); - sendEventToNanoapp(app, START_SCAN); + bool success; + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STARTED); ASSERT_TRUE(chrePalIsBleEnabled()); - unloadNanoapp(app); + unloadNanoapp(appId); ASSERT_FALSE(chrePalIsBleEnabled()); } @@ -226,9 +234,9 @@ TEST_F(TestBase, BleStartTwiceScanTest) { CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -266,24 +274,24 @@ TEST_F(TestBase, BleStartTwiceScanTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); bool success; - sendEventToNanoapp(app, START_SCAN); + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STARTED); - sendEventToNanoapp(app, START_SCAN); + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STARTED); waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT); - sendEventToNanoapp(app, STOP_SCAN); + sendEventToNanoapp(appId, STOP_SCAN); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); @@ -299,9 +307,10 @@ TEST_F(TestBase, BleStopTwiceScanTest) { CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -331,23 +340,23 @@ TEST_F(TestBase, BleStopTwiceScanTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); - bool success; + uint64_t appId = loadNanoapp(MakeUnique<App>()); - sendEventToNanoapp(app, STOP_SCAN); + bool success; + sendEventToNanoapp(appId, STOP_SCAN); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); - sendEventToNanoapp(app, STOP_SCAN); + sendEventToNanoapp(appId, STOP_SCAN); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); - unloadNanoapp(app); + unloadNanoapp(appId); } /** @@ -362,9 +371,10 @@ TEST_F(TestBase, BleSettingChangeTest) { CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -406,13 +416,13 @@ TEST_F(TestBase, BleSettingChangeTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); - bool success; + uint64_t appId = loadNanoapp(MakeUnique<App>()); - sendEventToNanoapp(app, START_SCAN); + bool success; + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); @@ -448,9 +458,10 @@ TEST_F(TestBase, BleSettingChangeTest) { TEST_F(TestBase, BleSettingDisabledStartScanTest) { CREATE_CHRE_TEST_EVENT(START_SCAN, 0); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -483,10 +494,10 @@ TEST_F(TestBase, BleSettingDisabledStartScanTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::BLE_AVAILABLE, false /* enable */); @@ -496,7 +507,7 @@ TEST_F(TestBase, BleSettingDisabledStartScanTest) { EXPECT_FALSE(enabled); bool success; - sendEventToNanoapp(app, START_SCAN); + sendEventToNanoapp(appId, START_SCAN); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_BLE_ASYNC_RESULT); @@ -511,9 +522,10 @@ TEST_F(TestBase, BleSettingDisabledStopScanTest) { CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -548,10 +560,10 @@ TEST_F(TestBase, BleSettingDisabledStopScanTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::BLE_AVAILABLE, false /* enable */); @@ -561,7 +573,7 @@ TEST_F(TestBase, BleSettingDisabledStopScanTest) { EXPECT_FALSE(enabled); bool success; - sendEventToNanoapp(app, STOP_SCAN); + sendEventToNanoapp(appId, STOP_SCAN); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); @@ -571,15 +583,14 @@ TEST_F(TestBase, BleSettingDisabledStopScanTest) { * Test that a nanoapp can read RSSI successfully. */ TEST_F(TestBase, BleReadRssi) { - constexpr auto kConnectionHandle = 6; - constexpr auto kCookie = 123; + constexpr uint16_t kConnectionHandle = 6; + constexpr uint32_t kCookie = 123; CREATE_CHRE_TEST_EVENT(RSSI_REQUEST, 1); CREATE_CHRE_TEST_EVENT(RSSI_REQUEST_SENT, 2); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public BleTestNanoapp { + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { case CHRE_EVENT_BLE_RSSI_READ: { auto *event = @@ -611,10 +622,10 @@ TEST_F(TestBase, BleReadRssi) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::BLE_AVAILABLE, true /* enabled */); @@ -623,7 +634,7 @@ TEST_F(TestBase, BleReadRssi) { EXPECT_TRUE(enabled); bool success; - sendEventToNanoapp(app, RSSI_REQUEST); + sendEventToNanoapp(appId, RSSI_REQUEST); waitForEvent(RSSI_REQUEST_SENT, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_BLE_RSSI_READ); @@ -640,9 +651,12 @@ TEST_F(TestBase, BleStartScanTwiceBeforeAsyncResponseTest) { CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); - struct App : public BleTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + struct testData { + void *cookie; + }; + + class App : public BleTestNanoapp { + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { case CHRE_EVENT_BLE_ASYNC_RESULT: { auto *event = static_cast<const struct chreAsyncResult *>(eventData); @@ -650,60 +664,203 @@ TEST_F(TestBase, BleStartScanTwiceBeforeAsyncResponseTest) { (event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN) ? SCAN_STARTED : SCAN_STOPPED; - TestEventQueueSingleton::get()->pushEvent(type, event->errorCode); + TestEventQueueSingleton::get()->pushEvent(type, *event); break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); switch (event->type) { case START_SCAN: { - const bool success = chreBleStartScanAsync( - CHRE_BLE_SCAN_MODE_BACKGROUND, 0, nullptr); + auto data = static_cast<testData *>(event->data); + const bool success = chreBleStartScanAsyncV1_9( + CHRE_BLE_SCAN_MODE_BACKGROUND, 0, nullptr, data->cookie); TestEventQueueSingleton::get()->pushEvent(START_SCAN, success); break; } case STOP_SCAN: { - const bool success = chreBleStopScanAsync(); + auto data = static_cast<testData *>(event->data); + const bool success = chreBleStopScanAsyncV1_9(data->cookie); TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success); break; } } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); bool success; delayBleScanStart(true /* delay */); - sendEventToNanoapp(app, START_SCAN); + testData data; + uint32_t cookieOne = 1; + data.cookie = &cookieOne; + sendEventToNanoapp(appId, START_SCAN, data); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); - sendEventToNanoapp(app, START_SCAN); + uint32_t cookieTwo = 2; + data.cookie = &cookieTwo; + sendEventToNanoapp(appId, START_SCAN, data); waitForEvent(START_SCAN, &success); EXPECT_TRUE(success); - uint8_t errorCode; - waitForEvent(SCAN_STARTED, &errorCode); - EXPECT_EQ(errorCode, CHRE_ERROR_OBSOLETE_REQUEST); + chreAsyncResult result; + waitForEvent(SCAN_STARTED, &result); + EXPECT_EQ(result.errorCode, CHRE_ERROR_OBSOLETE_REQUEST); + EXPECT_EQ(result.cookie, &cookieOne); // Respond to the first scan request. CHRE will then attempt the next scan // request at which point the PAL should no longer delay the response. delayBleScanStart(false /* delay */); EXPECT_TRUE(startBleScan()); - waitForEvent(SCAN_STARTED, &errorCode); - EXPECT_EQ(errorCode, CHRE_ERROR_NONE); + waitForEvent(SCAN_STARTED, &result); + EXPECT_EQ(result.errorCode, CHRE_ERROR_NONE); + EXPECT_EQ(result.cookie, &cookieTwo); - sendEventToNanoapp(app, STOP_SCAN); + sendEventToNanoapp(appId, STOP_SCAN, data); waitForEvent(STOP_SCAN, &success); EXPECT_TRUE(success); waitForEvent(SCAN_STOPPED); } +/** + * This test validates that a nanoapp can call flush only when an existing scan + * is enabled for the nanoapp. This test validates that batching will hold the + * data and flush will send the batched data and then a flush complete event. + */ +TEST_F(TestBase, BleFlush) { + CREATE_CHRE_TEST_EVENT(START_SCAN, 0); + CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1); + CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2); + CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3); + CREATE_CHRE_TEST_EVENT(CALL_FLUSH, 4); + CREATE_CHRE_TEST_EVENT(SAW_BLE_AD_AND_FLUSH_COMPLETE, 5); + + class App : public BleTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_BLE_ASYNC_RESULT: { + auto *event = static_cast<const struct chreAsyncResult *>(eventData); + if (event->errorCode == CHRE_ERROR_NONE) { + uint16_t type = + (event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN) + ? SCAN_STARTED + : SCAN_STOPPED; + TestEventQueueSingleton::get()->pushEvent(type); + } + break; + } + + case CHRE_EVENT_BLE_ADVERTISEMENT: { + mSawBleAdvertisementEvent = true; + break; + } + + case CHRE_EVENT_BLE_FLUSH_COMPLETE: { + auto *event = static_cast<const struct chreAsyncResult *>(eventData); + mSawFlushCompleteEvent = event->success && event->cookie == &mCookie; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case START_SCAN: { + const bool success = chreBleStartScanAsync( + CHRE_BLE_SCAN_MODE_AGGRESSIVE, 60000, nullptr); + TestEventQueueSingleton::get()->pushEvent(START_SCAN, success); + break; + } + + case STOP_SCAN: { + const bool success = chreBleStopScanAsync(); + TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success); + break; + } + + case CALL_FLUSH: { + const bool success = chreBleFlushAsync(&mCookie); + TestEventQueueSingleton::get()->pushEvent(CALL_FLUSH, success); + break; + } + } + break; + } + } + + if (mSawBleAdvertisementEvent && mSawFlushCompleteEvent) { + TestEventQueueSingleton::get()->pushEvent( + SAW_BLE_AD_AND_FLUSH_COMPLETE); + mSawBleAdvertisementEvent = false; + mSawFlushCompleteEvent = false; + } + } + + private: + uint32_t mCookie; + bool mSawBleAdvertisementEvent = false; + bool mSawFlushCompleteEvent = false; + }; + + uint64_t appId = loadNanoapp(MakeUnique<App>()); + + // Flushing before a scan should fail. + bool success; + sendEventToNanoapp(appId, CALL_FLUSH); + waitForEvent(CALL_FLUSH, &success); + ASSERT_FALSE(success); + + // Start a scan with batching. + sendEventToNanoapp(appId, START_SCAN); + waitForEvent(START_SCAN, &success); + ASSERT_TRUE(success); + waitForEvent(SCAN_STARTED); + ASSERT_TRUE(chrePalIsBleEnabled()); + + // Call flush again multiple times and get the complete event. + // We should only receive data when flush is called as the batch + // delay is extremely large. + constexpr uint32_t kNumFlushCalls = 3; + for (uint32_t i = 0; i < kNumFlushCalls; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + sendEventToNanoapp(appId, CALL_FLUSH); + waitForEvent(CALL_FLUSH, &success); + ASSERT_TRUE(success); + + // Wait for some data and a flush complete. + // This ensures we receive both advertisement events + // and a flush complete event. We are not guaranteed + // that the advertisement events will come after + // the CALL_FLUSH event or before. If they come + // before, then they will be ignored. This + // change allows the advertisement events to come + // after during the normal expiration of the + // batch timer, which is valid (call flush, get + // any advertisement events, flush complete event + // might get some advertisement events afterwards). + waitForEvent(SAW_BLE_AD_AND_FLUSH_COMPLETE); + } + + // Stop a scan. + sendEventToNanoapp(appId, STOP_SCAN); + waitForEvent(STOP_SCAN, &success); + ASSERT_TRUE(success); + waitForEvent(SCAN_STOPPED); + ASSERT_FALSE(chrePalIsBleEnabled()); + + // Flushing after a scan should fail. + sendEventToNanoapp(appId, CALL_FLUSH); + waitForEvent(CALL_FLUSH, &success); + ASSERT_FALSE(success); +} + } // namespace } // namespace chre diff --git a/test/simulation/gnss_test.cc b/test/simulation/gnss_test.cc index 5b2a6dc0..f9e388bd 100644 --- a/test/simulation/gnss_test.cc +++ b/test/simulation/gnss_test.cc @@ -61,73 +61,77 @@ TEST_F(TestBase, GnssSubscriptionWithSettingChange) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} - decltype(nanoappStart) *start = []() { + bool start() override { chreUserSettingConfigureEvents(CHRE_USER_SETTING_LOCATION, true /*enabled*/); return true; - }; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - switch (eventType) { - case CHRE_EVENT_GNSS_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_GNSS_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; - } + } - case CHRE_EVENT_SETTING_CHANGED_LOCATION: { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_SETTING_CHANGED_LOCATION); - break; - } + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_GNSS_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_GNSS_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case LOCATION_REQUEST: { - auto request = - static_cast<const LocationRequest *>(event->data); - bool success; - if (request->enable) { - cookie = request->cookie; - success = chreGnssLocationSessionStartAsync( - 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/, - &cookie); - } else { - cookie = request->cookie; - success = chreGnssLocationSessionStopAsync(&cookie); - } - TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST, - success); - break; - } + case CHRE_EVENT_SETTING_CHANGED_LOCATION: { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_SETTING_CHANGED_LOCATION); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case LOCATION_REQUEST: { + auto request = static_cast<const LocationRequest *>(event->data); + bool success; + mCookie = request->cookie; + if (request->enable) { + success = chreGnssLocationSessionStartAsync( + 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/, + &mCookie); + } else { + success = chreGnssLocationSessionStopAsync(&mCookie); } + TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST, + success); + break; } } - }; + } + } + } - decltype(nanoappEnd) *end = []() { + void end() override { chreUserSettingConfigureEvents(CHRE_USER_SETTING_LOCATION, false /*enabled*/); - }; + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; EXPECT_FALSE(chrePalGnssIsLocationEnabled()); chrePalGnssDelaySendingLocationEvents(true); LocationRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, LOCATION_REQUEST, request); + sendEventToNanoapp(appId, LOCATION_REQUEST, request); waitForEvent(LOCATION_REQUEST, &success); EXPECT_TRUE(success); chrePalGnssStartSendingLocationEvents(); @@ -155,7 +159,7 @@ TEST_F(TestBase, GnssSubscriptionWithSettingChange) { std::chrono::milliseconds(1000))); request.enable = false; - sendEventToNanoapp(app, LOCATION_REQUEST, request); + sendEventToNanoapp(appId, LOCATION_REQUEST, request); waitForEvent(LOCATION_REQUEST, &success); EXPECT_TRUE(success); chrePalGnssStartSendingLocationEvents(); @@ -173,12 +177,14 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToLocation) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static uint32_t cookie; + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_GNSS_ASYNC_RESULT: { auto *event = static_cast<const chreAsyncResult *>(eventData); @@ -196,14 +202,13 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToLocation) { case LOCATION_REQUEST: { auto request = static_cast<const LocationRequest *>(event->data); bool success; + mCookie = request->cookie; if (request->enable) { - cookie = request->cookie; success = chreGnssLocationSessionStartAsync( 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/, - &cookie); + &mCookie); } else { - cookie = request->cookie; - success = chreGnssLocationSessionStopAsync(&cookie); + success = chreGnssLocationSessionStopAsync(&mCookie); } TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST, success); @@ -212,15 +217,19 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToLocation) { } } } - }; + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; EXPECT_FALSE(chrePalGnssIsLocationEnabled()); LocationRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, LOCATION_REQUEST, request); + sendEventToNanoapp(appId, LOCATION_REQUEST, request); waitForEvent(LOCATION_REQUEST, &success); EXPECT_TRUE(success); uint32_t cookie; @@ -229,7 +238,7 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToLocation) { EXPECT_TRUE(chrePalGnssIsLocationEnabled()); request.enable = false; - sendEventToNanoapp(app, LOCATION_REQUEST, request); + sendEventToNanoapp(appId, LOCATION_REQUEST, request); waitForEvent(LOCATION_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie); @@ -245,12 +254,14 @@ TEST_F(TestBase, GnssUnsubscribeToLocationOnUnload) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static uint32_t cookie; + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_GNSS_ASYNC_RESULT: { auto *event = static_cast<const chreAsyncResult *>(eventData); @@ -268,10 +279,10 @@ TEST_F(TestBase, GnssUnsubscribeToLocationOnUnload) { case LOCATION_REQUEST: { auto request = static_cast<const LocationRequest *>(event->data); if (request->enable) { - cookie = request->cookie; + mCookie = request->cookie; const bool success = chreGnssLocationSessionStartAsync( 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/, - &cookie); + &mCookie); TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST, success); } @@ -280,14 +291,18 @@ TEST_F(TestBase, GnssUnsubscribeToLocationOnUnload) { } } } - }; + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalGnssIsLocationEnabled()); LocationRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, LOCATION_REQUEST, request); + sendEventToNanoapp(appId, LOCATION_REQUEST, request); bool success; waitForEvent(LOCATION_REQUEST, &success); EXPECT_TRUE(success); @@ -296,7 +311,7 @@ TEST_F(TestBase, GnssUnsubscribeToLocationOnUnload) { EXPECT_EQ(cookie, request.cookie); EXPECT_TRUE(chrePalGnssIsLocationEnabled()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalGnssIsLocationEnabled()); } @@ -308,54 +323,61 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToMeasurement) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - switch (eventType) { - case CHRE_EVENT_GNSS_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_GNSS_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + static uint32_t cookie; + switch (eventType) { + case CHRE_EVENT_GNSS_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_GNSS_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case MEASUREMENT_REQUEST: { - auto request = - static_cast<const MeasurementRequest *>(event->data); - bool success; - if (request->enable) { - cookie = request->cookie; - success = chreGnssMeasurementSessionStartAsync( - 1000 /*minIntervalMs*/, &cookie); - } else { - cookie = request->cookie; - success = chreGnssMeasurementSessionStopAsync(&cookie); - } - TestEventQueueSingleton::get()->pushEvent(MEASUREMENT_REQUEST, - success); - break; - } + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case MEASUREMENT_REQUEST: { + auto request = + static_cast<const MeasurementRequest *>(event->data); + bool success; + mCookie = request->cookie; + if (request->enable) { + success = chreGnssMeasurementSessionStartAsync( + 1000 /*minIntervalMs*/, &mCookie); + } else { + cookie = request->cookie; + success = chreGnssMeasurementSessionStopAsync(&mCookie); } + TestEventQueueSingleton::get()->pushEvent(MEASUREMENT_REQUEST, + success); + break; } } - }; + } + } + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; EXPECT_FALSE(chrePalGnssIsLocationEnabled()); MeasurementRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, MEASUREMENT_REQUEST, request); + sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request); waitForEvent(MEASUREMENT_REQUEST, &success); EXPECT_TRUE(success); uint32_t cookie; @@ -364,7 +386,7 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToMeasurement) { EXPECT_TRUE(chrePalGnssIsMeasurementEnabled()); request.enable = false; - sendEventToNanoapp(app, MEASUREMENT_REQUEST, request); + sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request); waitForEvent(MEASUREMENT_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie); @@ -380,49 +402,55 @@ TEST_F(TestBase, GnssUnsubscribeToMeasurementOnUnload) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - switch (eventType) { - case CHRE_EVENT_GNSS_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_GNSS_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_GNSS_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_GNSS_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case MEASUREMENT_REQUEST: { + auto request = + static_cast<const MeasurementRequest *>(event->data); + if (request->enable) { + mCookie = request->cookie; + const bool success = chreGnssMeasurementSessionStartAsync( + 1000 /*minIntervalMs*/, &mCookie); + TestEventQueueSingleton::get()->pushEvent(MEASUREMENT_REQUEST, + success); } break; } + } + } + } + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case MEASUREMENT_REQUEST: { - auto request = - static_cast<const MeasurementRequest *>(event->data); - if (request->enable) { - cookie = request->cookie; - const bool success = chreGnssMeasurementSessionStartAsync( - 1000 /*minIntervalMs*/, &cookie); - TestEventQueueSingleton::get()->pushEvent( - MEASUREMENT_REQUEST, success); - } - break; - } - } - } - }; - }; + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalGnssIsLocationEnabled()); MeasurementRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, MEASUREMENT_REQUEST, request); + sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request); bool success; waitForEvent(MEASUREMENT_REQUEST, &success); EXPECT_TRUE(success); @@ -431,46 +459,50 @@ TEST_F(TestBase, GnssUnsubscribeToMeasurementOnUnload) { EXPECT_EQ(cookie, request.cookie); EXPECT_TRUE(chrePalGnssIsMeasurementEnabled()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalGnssIsMeasurementEnabled()); } TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToPassiveListener) { CREATE_CHRE_TEST_EVENT(LISTENER_REQUEST, 0); - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case LISTENER_REQUEST: { - auto enable = *(static_cast<const bool *>(event->data)); - const bool success = - chreGnssConfigurePassiveLocationListener(enable); - TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST, - success); - break; - } - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case LISTENER_REQUEST: { + auto enable = *(static_cast<const bool *>(event->data)); + const bool success = + chreGnssConfigurePassiveLocationListener(enable); + TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST, + success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled()); - sendEventToNanoapp(app, LISTENER_REQUEST, true); + sendEventToNanoapp(appId, LISTENER_REQUEST, true); waitForEvent(LISTENER_REQUEST, &success); EXPECT_TRUE(success); EXPECT_TRUE(chrePalGnssIsPassiveLocationListenerEnabled()); - sendEventToNanoapp(app, LISTENER_REQUEST, false); + sendEventToNanoapp(appId, LISTENER_REQUEST, false); waitForEvent(LISTENER_REQUEST, &success); EXPECT_TRUE(success); EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled()); @@ -479,38 +511,42 @@ TEST_F(TestBase, GnssCanSubscribeAndUnsubscribeToPassiveListener) { TEST_F(TestBase, GnssUnsubscribeToPassiveListenerOnUnload) { CREATE_CHRE_TEST_EVENT(LISTENER_REQUEST, 0); - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_GNSS; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case LISTENER_REQUEST: { - auto enable = *(static_cast<const bool *>(event->data)); - const bool success = - chreGnssConfigurePassiveLocationListener(enable); - TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST, - success); - } - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case LISTENER_REQUEST: { + auto enable = *(static_cast<const bool *>(event->data)); + const bool success = + chreGnssConfigurePassiveLocationListener(enable); + TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST, + success); } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled()); - sendEventToNanoapp(app, LISTENER_REQUEST, true); + sendEventToNanoapp(appId, LISTENER_REQUEST, true); bool success; waitForEvent(LISTENER_REQUEST, &success); EXPECT_TRUE(success); EXPECT_TRUE(chrePalGnssIsPassiveLocationListenerEnabled()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled()); } diff --git a/test/simulation/host_endpoint_notification_test.cc b/test/simulation/host_endpoint_notification_test.cc index 67d3f981..75e3a7eb 100644 --- a/test/simulation/host_endpoint_notification_test.cc +++ b/test/simulation/host_endpoint_notification_test.cc @@ -52,31 +52,31 @@ TEST_F(TestBase, HostEndpointDisconnectedTest) { uint16_t endpointId; }; - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: { - auto notification = - *(struct chreHostEndpointNotification *)eventData; - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION, notification); - } break; - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case SETUP_NOTIFICATION: { - auto config = static_cast<const Config *>(event->data); - const bool success = chreConfigureHostEndpointNotifications( - config->endpointId, config->enable); - TestEventQueueSingleton::get()->pushEvent(SETUP_NOTIFICATION, - success); - } - } + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: { + auto notification = *(struct chreHostEndpointNotification *)eventData; + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION, notification); + } break; + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case SETUP_NOTIFICATION: { + auto config = static_cast<const Config *>(event->data); + const bool success = chreConfigureHostEndpointNotifications( + config->endpointId, config->enable); + TestEventQueueSingleton::get()->pushEvent(SETUP_NOTIFICATION, + success); } } - }; + } + } + } }; struct chreHostEndpointInfo info; @@ -88,10 +88,11 @@ TEST_F(TestBase, HostEndpointDisconnectedTest) { strcpy(&info.endpointTag[0], "Test tag"); getHostEndpointManager().postHostEndpointConnected(info); - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + Config config = {.enable = true, .endpointId = kHostEndpointId}; - sendEventToNanoapp(app, SETUP_NOTIFICATION, config); + sendEventToNanoapp(appId, SETUP_NOTIFICATION, config); bool success; waitForEvent(SETUP_NOTIFICATION, &success); EXPECT_TRUE(success); diff --git a/test/simulation/inc/rpc_test.h b/test/simulation/inc/rpc_test.h index ce71d4e4..1fcc7df6 100644 --- a/test/simulation/inc/rpc_test.h +++ b/test/simulation/inc/rpc_test.h @@ -42,12 +42,21 @@ class RpcTestService final chre_rpc_NumberMessage &response); }; -struct Env { +class Env { + public: RpcTestService mRpcTestService; chre::RpcServer mServer; chre::RpcClient mClient{kPwRcpServerAppId}; pw::rpc::NanopbUnaryReceiver<chre_rpc_NumberMessage> mIncrementCall; uint32_t mNumber; + + void closeServer() { + mServer.close(); + } + + void closeClient() { + mClient.close(); + } }; typedef Singleton<Env> EnvSingleton; diff --git a/test/simulation/inc/test_base.h b/test/simulation/inc/test_base.h index 0b02741b..95ac29c7 100644 --- a/test/simulation/inc/test_base.h +++ b/test/simulation/inc/test_base.h @@ -21,6 +21,8 @@ #include <cstdint> #include <thread> +#include "chre/core/event_loop_manager.h" +#include "chre/core/nanoapp.h" #include "chre/platform/system_timer.h" #include "chre/util/time.h" #include "test_event_queue.h" @@ -74,6 +76,24 @@ class TestBase : public testing::Test { TestEventQueueSingleton::get()->waitForEvent(eventType, eventData); } + /** + * Retrieves the Nanoapp instance from its ID. + * + * @param id Nanoapp ID + * @return A pointer to the Nanoapp instance or nullptr if not found. + */ + Nanoapp *getNanoappByAppId(uint64_t id) { + uint16_t instanceId; + EXPECT_TRUE(EventLoopManagerSingleton::get() + ->getEventLoop() + .findNanoappInstanceIdByAppId(id, &instanceId)); + Nanoapp *nanoapp = EventLoopManagerSingleton::get() + ->getEventLoop() + .findNanoappByInstanceId(instanceId); + EXPECT_NE(nanoapp, nullptr); + return nanoapp; + } + std::thread mChreThread; SystemTimer mSystemTimer; }; diff --git a/test/simulation/inc/test_util.h b/test/simulation/inc/test_util.h index 0a12e4c8..9e796018 100644 --- a/test/simulation/inc/test_util.h +++ b/test/simulation/inc/test_util.h @@ -28,7 +28,77 @@ namespace chre { -struct TestNanoapp; +constexpr uint64_t kDefaultTestNanoappId = 0x0123456789abcdef; + +/** + * Unregister all nanoapps. + * + * This is called by the test framework to unregister all nanoapps after each + * test. The destructor is called when the nanoapp is unregistered. + */ +void unregisterAllTestNanoapps(); + +/** + * Information about a test nanoapp. + */ +struct TestNanoappInfo { + const char *name = "Test"; + uint64_t id = kDefaultTestNanoappId; + uint32_t version = 0; + uint32_t perms = NanoappPermissions::CHRE_PERMS_NONE; +}; + +/** + * Test nanoapp. + * + * Tests typically inherit this class and override the entry points to test the + * nanoapp behavior. + * + * The bulk of the code should be in the handleEvent method to respond to + * events sent to the nanoapp by the platform and by the sendEventToNanoapp + * function. start and end can be use to setup and cleanup the test environment + * around each test. + * + * Note: end is only executed when the nanoapp is explicitly unloaded. + */ +class TestNanoapp { + public: + TestNanoapp() = default; + explicit TestNanoapp(TestNanoappInfo info) : mTestNanoappInfo(info) {} + virtual ~TestNanoapp() {} + + // NanoappStart Entrypoint. + virtual bool start() { + return true; + } + + // nanoappHandleEvent Entrypoint. + virtual void handleEvent(uint32_t /*senderInstanceId*/, + uint16_t /*eventType*/, const void * /*eventData*/) { + } + + // nanoappEnd Entrypoint. + virtual void end() {} + + const char *name() { + return mTestNanoappInfo.name; + } + + uint64_t id() { + return mTestNanoappInfo.id; + } + + uint32_t version() { + return mTestNanoappInfo.version; + } + + uint32_t perms() { + return mTestNanoappInfo.perms; + } + + private: + const TestNanoappInfo mTestNanoappInfo; +}; /** * @return the statically loaded nanoapp based on the arguments. @@ -85,30 +155,9 @@ void loadNanoapp(const char *name, uint64_t appId, uint32_t appVersion, * * This function returns after the nanoapp start has been executed. * - * @return An instance of the TestNanoapp. + * @return The id of the nanoapp. */ -template <class Nanoapp> -Nanoapp loadNanoapp() { - static_assert(std::is_base_of<TestNanoapp, Nanoapp>::value); - Nanoapp app; - loadNanoapp(app.name, app.id, app.version, app.perms, app.start, - app.handleEvent, app.end); - - return app; -} - -/** - * Unload a test nanoapp. - * - * This function returns after the nanoapp end has been executed. - * - * @param app An instance of TestNanoapp. - */ -template <class Nanoapp> -void unloadNanoapp(Nanoapp app) { - static_assert(std::is_base_of<TestNanoapp, Nanoapp>::value); - unloadNanoapp(app.id); -} +uint64_t loadNanoapp(UniquePtr<TestNanoapp> app); /** * Unload nanoapp corresponding to appId. @@ -117,8 +166,7 @@ void unloadNanoapp(Nanoapp app) { * * @param appId App Id of nanoapp to be unloaded. */ -template <> -void unloadNanoapp<uint64_t>(uint64_t appId); +void unloadNanoapp(uint64_t appId); /** * A convenience deferred callback function that can be used to start an already @@ -141,31 +189,6 @@ void testFinishUnloadingNanoappCallback(uint16_t type, void *data, void *extraData); /** - * Test nanoapp. - * - * Tests typically inherit this struct to test the nanoapp behavior. - * The bulk of the code should be in the handleEvent closure to respond to - * events sent to the nanoapp by the platform and by the sendEventToNanoapp - * function. start and end can be use to setup and cleanup the test environment - * around each test. - * - * Note: end is only executed when the nanoapp is explicitly unloaded. - */ -struct TestNanoapp { - const char *name = "Test"; - uint64_t id = 0x0123456789abcdef; - uint32_t version = 0; - uint32_t perms = NanoappPermissions::CHRE_PERMS_NONE; - - decltype(nanoappStart) *start = []() { return true; }; - - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t, - const void *) {}; - - decltype(nanoappEnd) *end = []() {}; -}; - -/** * Deallocate the memory allocated for a TestEvent. */ void freeTestEventDataCallback(uint16_t /*eventType*/, void *eventData); @@ -176,27 +199,10 @@ void freeTestEventDataCallback(uint16_t /*eventType*/, void *eventData); * This function is typically used to execute code in the context of the * nanoapp in its handleEvent method. * - * @param app An instance of TestNanoapp. + * @param appId ID of the nanoapp. * @param eventType The event to send. */ -template <class Nanoapp> -void sendEventToNanoapp(const Nanoapp &app, uint16_t eventType) { - static_assert(std::is_base_of<TestNanoapp, Nanoapp>::value); - uint16_t instanceId; - if (EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)) { - auto event = memoryAlloc<TestEvent>(); - ASSERT_NE(event, nullptr); - event->type = eventType; - EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( - CHRE_EVENT_TEST_EVENT, static_cast<void *>(event), - freeTestEventDataCallback, instanceId); - - } else { - LOGE("No instance found for nanoapp id = 0x%016" PRIx64, app.id); - } -} +void sendEventToNanoapp(uint64_t appId, uint16_t eventType); /** * Sends a message to a nanoapp with data. @@ -208,19 +214,18 @@ void sendEventToNanoapp(const Nanoapp &app, uint16_t eventType) { * populated with the eventType and a pointer to as copy of the evenData as * a CHRE_EVENT_TEST_EVENT event. * - * @param app An instance of TestNanoapp. + * @param appId ID of the nanoapp. * @param eventType The event to send. * @param eventData The data to send. */ -template <class Nanoapp, class T> -void sendEventToNanoapp(const Nanoapp &app, uint16_t eventType, +template <class T> +void sendEventToNanoapp(uint64_t appId, uint16_t eventType, const T &eventData) { - static_assert(std::is_base_of<TestNanoapp, Nanoapp>::value); static_assert(std::is_trivial<T>::value); uint16_t instanceId; if (EventLoopManagerSingleton::get() ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)) { + .findNanoappInstanceIdByAppId(appId, &instanceId)) { auto event = memoryAlloc<TestEvent>(); ASSERT_NE(event, nullptr); event->type = eventType; @@ -232,7 +237,7 @@ void sendEventToNanoapp(const Nanoapp &app, uint16_t eventType, CHRE_EVENT_TEST_EVENT, static_cast<void *>(event), freeTestEventDataCallback, instanceId); } else { - LOGE("No instance found for nanoapp id = 0x%016" PRIx64, app.id); + LOGE("No instance found for nanoapp id = 0x%016" PRIx64, appId); } } diff --git a/test/simulation/memory_test.cc b/test/simulation/memory_test.cc index 5acd2e97..a3047346 100644 --- a/test/simulation/memory_test.cc +++ b/test/simulation/memory_test.cc @@ -33,25 +33,14 @@ namespace chre { namespace { -Nanoapp *getNanoappByAppId(uint64_t id) { - uint16_t instanceId; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(id, &instanceId)); - Nanoapp *nanoapp = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId); - EXPECT_NE(nanoapp, nullptr); - return nanoapp; -} - TEST_F(TestBase, MemoryAllocateAndFree) { CREATE_CHRE_TEST_EVENT(ALLOCATE, 0); CREATE_CHRE_TEST_EVENT(FREE, 1); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); @@ -71,21 +60,22 @@ TEST_F(TestBase, MemoryAllocateAndFree) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); MemoryManager &memManager = EventLoopManagerSingleton::get()->getMemoryManager(); - Nanoapp *nanoapp = getNanoappByAppId(app.id); + Nanoapp *nanoapp = getNanoappByAppId(appId); + ASSERT_NE(nanoapp, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); void *ptr1; - sendEventToNanoapp(app, ALLOCATE, 100); + sendEventToNanoapp(appId, ALLOCATE, 100); waitForEvent(ALLOCATE, &ptr1); EXPECT_NE(ptr1, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 100); @@ -93,20 +83,20 @@ TEST_F(TestBase, MemoryAllocateAndFree) { EXPECT_EQ(memManager.getAllocationCount(), 1); void *ptr2; - sendEventToNanoapp(app, ALLOCATE, 200); + sendEventToNanoapp(appId, ALLOCATE, 200); waitForEvent(ALLOCATE, &ptr2); EXPECT_NE(ptr2, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 100 + 200); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 100 + 200); EXPECT_EQ(memManager.getAllocationCount(), 2); - sendEventToNanoapp(app, FREE, ptr1); + sendEventToNanoapp(appId, FREE, ptr1); waitForEvent(FREE); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 200); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 200); EXPECT_EQ(memManager.getAllocationCount(), 1); - sendEventToNanoapp(app, FREE, ptr2); + sendEventToNanoapp(appId, FREE, ptr2); waitForEvent(FREE); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); @@ -116,9 +106,10 @@ TEST_F(TestBase, MemoryAllocateAndFree) { TEST_F(TestBase, MemoryFreeOnNanoappUnload) { CREATE_CHRE_TEST_EVENT(ALLOCATE, 0); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); @@ -132,21 +123,22 @@ TEST_F(TestBase, MemoryFreeOnNanoappUnload) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); MemoryManager &memManager = EventLoopManagerSingleton::get()->getMemoryManager(); - Nanoapp *nanoapp = getNanoappByAppId(app.id); + Nanoapp *nanoapp = getNanoappByAppId(appId); + ASSERT_NE(nanoapp, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); void *ptr1; - sendEventToNanoapp(app, ALLOCATE, 100); + sendEventToNanoapp(appId, ALLOCATE, 100); waitForEvent(ALLOCATE, &ptr1); EXPECT_NE(ptr1, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 100); @@ -154,14 +146,14 @@ TEST_F(TestBase, MemoryFreeOnNanoappUnload) { EXPECT_EQ(memManager.getAllocationCount(), 1); void *ptr2; - sendEventToNanoapp(app, ALLOCATE, 200); + sendEventToNanoapp(appId, ALLOCATE, 200); waitForEvent(ALLOCATE, &ptr2); EXPECT_NE(ptr2, nullptr); EXPECT_EQ(nanoapp->getTotalAllocatedBytes(), 100 + 200); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 100 + 200); EXPECT_EQ(memManager.getAllocationCount(), 2); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); } @@ -170,9 +162,10 @@ TEST_F(TestBase, MemoryStressTestShouldNotTriggerErrors) { CREATE_CHRE_TEST_EVENT(ALLOCATE, 0); CREATE_CHRE_TEST_EVENT(FREE, 1); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); @@ -192,13 +185,13 @@ TEST_F(TestBase, MemoryStressTestShouldNotTriggerErrors) { } } } - }; + } }; MemoryManager &memManager = EventLoopManagerSingleton::get()->getMemoryManager(); - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); @@ -207,55 +200,55 @@ TEST_F(TestBase, MemoryStressTestShouldNotTriggerErrors) { void *ptr2; void *ptr3; - sendEventToNanoapp(app, ALLOCATE, 100); + sendEventToNanoapp(appId, ALLOCATE, 100); waitForEvent(ALLOCATE, &ptr1); - sendEventToNanoapp(app, ALLOCATE, 200); + sendEventToNanoapp(appId, ALLOCATE, 200); waitForEvent(ALLOCATE, &ptr2); - sendEventToNanoapp(app, ALLOCATE, 300); + sendEventToNanoapp(appId, ALLOCATE, 300); waitForEvent(ALLOCATE, &ptr3); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 100 + 200 + 300); EXPECT_EQ(memManager.getAllocationCount(), 3); // Free middle, last, and first blocks. - sendEventToNanoapp(app, FREE, ptr2); + sendEventToNanoapp(appId, FREE, ptr2); waitForEvent(FREE); - sendEventToNanoapp(app, FREE, ptr3); + sendEventToNanoapp(appId, FREE, ptr3); waitForEvent(FREE); - sendEventToNanoapp(app, FREE, ptr1); + sendEventToNanoapp(appId, FREE, ptr1); waitForEvent(FREE); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); - sendEventToNanoapp(app, ALLOCATE, 100); + sendEventToNanoapp(appId, ALLOCATE, 100); waitForEvent(ALLOCATE, &ptr1); - sendEventToNanoapp(app, ALLOCATE, 200); + sendEventToNanoapp(appId, ALLOCATE, 200); waitForEvent(ALLOCATE, &ptr2); - sendEventToNanoapp(app, ALLOCATE, 300); + sendEventToNanoapp(appId, ALLOCATE, 300); waitForEvent(ALLOCATE, &ptr3); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 100 + 200 + 300); EXPECT_EQ(memManager.getAllocationCount(), 3); // Free last, last and last blocks. - sendEventToNanoapp(app, FREE, ptr3); + sendEventToNanoapp(appId, FREE, ptr3); waitForEvent(FREE); - sendEventToNanoapp(app, FREE, ptr2); + sendEventToNanoapp(appId, FREE, ptr2); waitForEvent(FREE); - sendEventToNanoapp(app, FREE, ptr1); + sendEventToNanoapp(appId, FREE, ptr1); waitForEvent(FREE); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); - sendEventToNanoapp(app, ALLOCATE, 100); + sendEventToNanoapp(appId, ALLOCATE, 100); waitForEvent(ALLOCATE, &ptr1); - sendEventToNanoapp(app, ALLOCATE, 200); + sendEventToNanoapp(appId, ALLOCATE, 200); waitForEvent(ALLOCATE, &ptr2); - sendEventToNanoapp(app, ALLOCATE, 300); + sendEventToNanoapp(appId, ALLOCATE, 300); waitForEvent(ALLOCATE, &ptr3); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 100 + 200 + 300); EXPECT_EQ(memManager.getAllocationCount(), 3); // Automatic cleanup. - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_EQ(memManager.getTotalAllocatedBytes(), 0); EXPECT_EQ(memManager.getAllocationCount(), 0); } diff --git a/test/simulation/rpc_test.cc b/test/simulation/rpc_test.cc index ffcd564e..a023f640 100644 --- a/test/simulation/rpc_test.cc +++ b/test/simulation/rpc_test.cc @@ -46,8 +46,9 @@ pw::Status RpcTestService::Increment(const chre_rpc_NumberMessage &request, namespace { TEST_F(TestBase, PwRpcCanPublishServicesInNanoappStart) { - struct App : public TestNanoapp { - decltype(nanoappStart) *start = []() -> bool { + class App : public TestNanoapp { + public: + bool start() override { struct chreNanoappRpcService servicesA[] = { {.id = 1, .version = 0}, {.id = 2, .version = 0}, @@ -60,21 +61,12 @@ TEST_F(TestBase, PwRpcCanPublishServicesInNanoappStart) { return chrePublishRpcServices(servicesA, 2 /* numServices */) && chrePublishRpcServices(servicesB, 2 /* numServices */); - }; + } }; - auto app = loadNanoapp<App>(); - - uint16_t instanceId; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)); - - Nanoapp *napp = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId); - - ASSERT_FALSE(napp == nullptr); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + Nanoapp *napp = getNanoappByAppId(appId); + ASSERT_NE(napp, nullptr); EXPECT_EQ(napp->getRpcServices().size(), 4); EXPECT_EQ(napp->getRpcServices()[0].id, 1); @@ -84,8 +76,8 @@ TEST_F(TestBase, PwRpcCanPublishServicesInNanoappStart) { } TEST_F(TestBase, PwRpcCanNotPublishDuplicateServices) { - struct App : public TestNanoapp { - decltype(nanoappStart) *start = []() -> bool { + class App : public TestNanoapp { + bool start() override { struct chreNanoappRpcService servicesA[] = { {.id = 1, .version = 0}, {.id = 2, .version = 0}, @@ -103,21 +95,12 @@ TEST_F(TestBase, PwRpcCanNotPublishDuplicateServices) { EXPECT_FALSE(chrePublishRpcServices(servicesB, 2 /* numServices */)); return success; - }; + } }; - auto app = loadNanoapp<App>(); - - uint16_t instanceId; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)); - - Nanoapp *napp = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId); - - ASSERT_FALSE(napp == nullptr); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + Nanoapp *napp = getNanoappByAppId(appId); + ASSERT_NE(napp, nullptr); EXPECT_EQ(napp->getRpcServices().size(), 2); EXPECT_EQ(napp->getRpcServices()[0].id, 1); @@ -125,51 +108,31 @@ TEST_F(TestBase, PwRpcCanNotPublishDuplicateServices) { } TEST_F(TestBase, PwRpcDifferentAppCanPublishSameServices) { - struct App1 : public TestNanoapp { - uint64_t id = 0x01; + class App : public TestNanoapp { + public: + explicit App(uint64_t id) : TestNanoapp(TestNanoappInfo{.id = id}) {} - decltype(nanoappStart) *start = []() -> bool { + bool start() override { struct chreNanoappRpcService services[] = { {.id = 1, .version = 0}, {.id = 2, .version = 0}, }; return chrePublishRpcServices(services, 2 /* numServices */); - }; - }; - - struct App2 : public App1 { - uint64_t id = 0x02; + } }; - auto app1 = loadNanoapp<App1>(); - auto app2 = loadNanoapp<App2>(); - - uint16_t instanceId1; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app1.id, &instanceId1)); - - Nanoapp *napp1 = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId1); - - ASSERT_FALSE(napp1 == nullptr); + uint64_t app1Id = loadNanoapp(MakeUnique<App>(0x01)); + uint64_t app2Id = loadNanoapp(MakeUnique<App>(0x02)); + Nanoapp *napp1 = getNanoappByAppId(app1Id); + ASSERT_NE(napp1, nullptr); EXPECT_EQ(napp1->getRpcServices().size(), 2); EXPECT_EQ(napp1->getRpcServices()[0].id, 1); EXPECT_EQ(napp1->getRpcServices()[1].id, 2); - uint16_t instanceId2; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app2.id, &instanceId2)); - - Nanoapp *napp2 = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId2); - - ASSERT_FALSE(napp2 == nullptr); + Nanoapp *napp2 = getNanoappByAppId(app2Id); + ASSERT_NE(napp2, nullptr); EXPECT_EQ(napp2->getRpcServices().size(), 2); EXPECT_EQ(napp2->getRpcServices()[0].id, 1); @@ -179,57 +142,51 @@ TEST_F(TestBase, PwRpcDifferentAppCanPublishSameServices) { TEST_F(TestBase, PwRpcCanNotPublishServicesOutsideOfNanoappStart) { CREATE_CHRE_TEST_EVENT(PUBLISH_SERVICES, 0); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case PUBLISH_SERVICES: { - struct chreNanoappRpcService services[] = { - {.id = 1, .version = 0}, - {.id = 2, .version = 0}, - }; - - bool success = - chrePublishRpcServices(services, 2 /* numServices */); - TestEventQueueSingleton::get()->pushEvent(PUBLISH_SERVICES, - success); - break; - } - } + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case PUBLISH_SERVICES: { + struct chreNanoappRpcService services[] = { + {.id = 1, .version = 0}, + {.id = 2, .version = 0}, + }; + + bool success = + chrePublishRpcServices(services, 2 /* numServices */); + TestEventQueueSingleton::get()->pushEvent(PUBLISH_SERVICES, + success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); bool success = true; - sendEventToNanoapp(app, PUBLISH_SERVICES); + sendEventToNanoapp(appId, PUBLISH_SERVICES); waitForEvent(PUBLISH_SERVICES, &success); EXPECT_FALSE(success); - uint16_t instanceId; - EXPECT_TRUE(EventLoopManagerSingleton::get() - ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)); - - Nanoapp *napp = - EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( - instanceId); - - ASSERT_FALSE(napp == nullptr); + Nanoapp *napp = getNanoappByAppId(appId); + ASSERT_NE(napp, nullptr); EXPECT_EQ(napp->getRpcServices().size(), 0); } TEST_F(TestBase, PwRpcRegisterServicesShouldGracefullyFailOnDuplicatedService) { - struct App : public TestNanoapp { - decltype(nanoappStart) *start = []() -> bool { + class App : public TestNanoapp { + public: + bool start() override { static RpcTestService testService; - EnvSingleton::init(); + chre::RpcServer::Service service = {.service = testService, .id = 0xca8f7150a3f05847, .version = 0x01020034}; @@ -243,49 +200,59 @@ TEST_F(TestBase, PwRpcRegisterServicesShouldGracefullyFailOnDuplicatedService) { EXPECT_FALSE(server.registerServices(1, &service)); return status; - }; + } + + void end() override { + EnvSingleton::get()->closeServer(); + } }; - auto app = loadNanoapp<App>(); + EnvSingleton::init(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + unloadNanoapp(appId); + EnvSingleton::deinit(); } TEST_F(TestBase, PwRpcGetNanoappInfoByAppIdReturnsServices) { CREATE_CHRE_TEST_EVENT(QUERY_INFO, 0); - struct App : public TestNanoapp { - decltype(nanoappStart) *start = []() -> bool { + class App : public TestNanoapp { + public: + bool start() override { struct chreNanoappRpcService services[] = { {.id = 1, .version = 2}, {.id = 2, .version = 3}, }; return chrePublishRpcServices(services, 2 /* numServices */); - }; + } - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static struct chreNanoappInfo info; + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); switch (event->type) { case QUERY_INFO: { auto id = static_cast<uint64_t *>(event->data); - bool success = chreGetNanoappInfoByAppId(*id, &info); - const struct chreNanoappInfo *pInfo = success ? &info : nullptr; + bool success = chreGetNanoappInfoByAppId(*id, &mInfo); + const struct chreNanoappInfo *pInfo = success ? &mInfo : nullptr; TestEventQueueSingleton::get()->pushEvent(QUERY_INFO, pInfo); break; } } } } - }; + } + + protected: + struct chreNanoappInfo mInfo; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); struct chreNanoappInfo *pInfo = nullptr; - sendEventToNanoapp(app, QUERY_INFO, app.id); + sendEventToNanoapp(appId, QUERY_INFO, appId); waitForEvent(QUERY_INFO, &pInfo); EXPECT_TRUE(pInfo != nullptr); EXPECT_EQ(pInfo->rpcServiceCount, 2); @@ -301,12 +268,12 @@ TEST_F(TestBase, PwRpcGetNanoappInfoByAppIdReturnsServices) { TEST_F(TestBase, PwRpcClientNanoappCanRequestServerNanoapp) { CREATE_CHRE_TEST_EVENT(INCREMENT_REQUEST, 0); - struct ClientApp : public TestNanoapp { - uint64_t id = kPwRcpClientAppId; + class ClientApp : public TestNanoapp { + public: + ClientApp() : TestNanoapp(TestNanoappInfo{.id = kPwRcpClientAppId}) {} - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t senderInstanceId, - uint16_t eventType, - const void *eventData) { + void handleEvent(uint32_t senderInstanceId, uint16_t eventType, + const void *eventData) override { Env *env = EnvSingleton::get(); env->mClient.handleEvent(senderInstanceId, eventType, eventData); @@ -341,37 +308,49 @@ TEST_F(TestBase, PwRpcClientNanoappCanRequestServerNanoapp) { } } } - }; + } + + void end() { + EnvSingleton::get()->closeClient(); + } }; - struct ServerApp : public TestNanoapp { - uint64_t id = kPwRcpServerAppId; - decltype(nanoappStart) *start = []() { - EnvSingleton::init(); + class ServerApp : public TestNanoapp { + public: + ServerApp() : TestNanoapp(TestNanoappInfo{.id = kPwRcpServerAppId}) {} + + bool start() override { chre::RpcServer::Service service = { .service = EnvSingleton::get()->mRpcTestService, .id = 0xca8f7150a3f05847, .version = 0x01020034}; return EnvSingleton::get()->mServer.registerServices(1, &service); - }; + } - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t senderInstanceId, - uint16_t eventType, - const void *eventData) { + void handleEvent(uint32_t senderInstanceId, uint16_t eventType, + const void *eventData) override { EnvSingleton::get()->mServer.handleEvent(senderInstanceId, eventType, eventData); - }; + } + + void end() { + EnvSingleton::get()->closeServer(); + } }; - auto server = loadNanoapp<ServerApp>(); - auto client = loadNanoapp<ClientApp>(); + EnvSingleton::init(); + uint64_t serverId = loadNanoapp(MakeUnique<ServerApp>()); + uint64_t clientId = loadNanoapp(MakeUnique<ClientApp>()); bool status; constexpr uint32_t kNumber = 101; - sendEventToNanoapp(client, INCREMENT_REQUEST, kNumber); + sendEventToNanoapp(clientId, INCREMENT_REQUEST, kNumber); waitForEvent(INCREMENT_REQUEST, &status); EXPECT_TRUE(status); EXPECT_EQ(EnvSingleton::get()->mNumber, kNumber + 1); + unloadNanoapp(serverId); + unloadNanoapp(clientId); + EnvSingleton::deinit(); } TEST_F(TestBase, PwRpcRpcClientHasServiceCheckForAMatchingService) { @@ -383,16 +362,15 @@ TEST_F(TestBase, PwRpcRpcClientHasServiceCheckForAMatchingService) { uint64_t appId; }; - struct App : public TestNanoapp { - decltype(nanoappStart) *start = []() -> bool { + class App : public TestNanoapp { + public: + bool start() override { struct chreNanoappRpcService services[] = {{.id = 1, .version = 2}}; return chrePublishRpcServices(services, 1 /* numServices */); - }; + } - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static struct chreNanoappInfo info; + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); @@ -411,20 +389,20 @@ TEST_F(TestBase, PwRpcRpcClientHasServiceCheckForAMatchingService) { break; } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); ServiceInfo service; bool hasService = false; - service = {.id = 1, .version = 2, .appId = app.id}; - sendEventToNanoapp(app, QUERY_HAS_SERVICE, service); + service = {.id = 1, .version = 2, .appId = appId}; + sendEventToNanoapp(appId, QUERY_HAS_SERVICE, service); waitForEvent(QUERY_HAS_SERVICE, &hasService); EXPECT_TRUE(hasService); - service = {.id = 10, .version = 2, .appId = app.id}; - sendEventToNanoapp(app, QUERY_HAS_SERVICE, service); + service = {.id = 10, .version = 2, .appId = appId}; + sendEventToNanoapp(appId, QUERY_HAS_SERVICE, service); waitForEvent(QUERY_HAS_SERVICE, &hasService); EXPECT_FALSE(hasService); } diff --git a/test/simulation/sensor_test.cc b/test/simulation/sensor_test.cc index f75a2c17..91d9ad1a 100644 --- a/test/simulation/sensor_test.cc +++ b/test/simulation/sensor_test.cc @@ -44,36 +44,38 @@ TEST_F(TestBase, SensorCanSubscribeAndUnsubscribeToDataEvents) { enum chreSensorConfigureMode mode; }; - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { - auto *event = - static_cast<const struct chreSensorSamplingStatusEvent *>( - eventData); - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_SENSOR_SAMPLING_CHANGE, *event); + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { + auto *event = + static_cast<const struct chreSensorSamplingStatusEvent *>( + eventData); + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_SENSOR_SAMPLING_CHANGE, *event); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case CONFIGURE: { + auto config = static_cast<const Configuration *>(event->data); + const bool success = chreSensorConfigure( + config->sensorHandle, config->mode, config->interval, 0); + TestEventQueueSingleton::get()->pushEvent(CONFIGURE, success); break; } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case CONFIGURE: { - auto config = static_cast<const Configuration *>(event->data); - const bool success = chreSensorConfigure( - config->sensorHandle, config->mode, config->interval, 0); - TestEventQueueSingleton::get()->pushEvent(CONFIGURE, success); - break; - } - } - } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; EXPECT_FALSE(chrePalSensorIsSensor0Enabled()); @@ -81,7 +83,7 @@ TEST_F(TestBase, SensorCanSubscribeAndUnsubscribeToDataEvents) { Configuration config{.sensorHandle = 0, .interval = 100, .mode = CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS}; - sendEventToNanoapp(app, CONFIGURE, config); + sendEventToNanoapp(appId, CONFIGURE, config); waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); struct chreSensorSamplingStatusEvent event; @@ -94,7 +96,7 @@ TEST_F(TestBase, SensorCanSubscribeAndUnsubscribeToDataEvents) { config = {.sensorHandle = 0, .interval = 50, .mode = CHRE_SENSOR_CONFIGURE_MODE_DONE}; - sendEventToNanoapp(app, CONFIGURE, config); + sendEventToNanoapp(appId, CONFIGURE, config); waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); EXPECT_FALSE(chrePalSensorIsSensor0Enabled()); @@ -109,42 +111,44 @@ TEST_F(TestBase, SensorUnsubscribeToDataEventsOnUnload) { enum chreSensorConfigureMode mode; }; - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - switch (eventType) { - case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { - auto *event = - static_cast<const struct chreSensorSamplingStatusEvent *>( - eventData); - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_SENSOR_SAMPLING_CHANGE, *event); + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { + auto *event = + static_cast<const struct chreSensorSamplingStatusEvent *>( + eventData); + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_SENSOR_SAMPLING_CHANGE, *event); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case CONFIGURE: { + auto config = static_cast<const Configuration *>(event->data); + const bool success = chreSensorConfigure( + config->sensorHandle, config->mode, config->interval, 0); + TestEventQueueSingleton::get()->pushEvent(CONFIGURE, success); break; } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case CONFIGURE: { - auto config = static_cast<const Configuration *>(event->data); - const bool success = chreSensorConfigure( - config->sensorHandle, config->mode, config->interval, 0); - TestEventQueueSingleton::get()->pushEvent(CONFIGURE, success); - break; - } - } - } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalSensorIsSensor0Enabled()); Configuration config{.sensorHandle = 0, .interval = 100, .mode = CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS}; - sendEventToNanoapp(app, CONFIGURE, config); + sendEventToNanoapp(appId, CONFIGURE, config); bool success; waitForEvent(CONFIGURE, &success); EXPECT_TRUE(success); @@ -155,7 +159,7 @@ TEST_F(TestBase, SensorUnsubscribeToDataEventsOnUnload) { EXPECT_TRUE(event.status.enabled); EXPECT_TRUE(chrePalSensorIsSensor0Enabled()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalSensorIsSensor0Enabled()); } diff --git a/test/simulation/settings_test.cc b/test/simulation/settings_test.cc index c11731e1..8dd5314a 100644 --- a/test/simulation/settings_test.cc +++ b/test/simulation/settings_test.cc @@ -35,8 +35,8 @@ namespace chre { namespace { -int8_t gExpectedLocationSettingState = CHRE_USER_SETTING_STATE_DISABLED; -int8_t gExpectedWifiSettingState = CHRE_USER_SETTING_STATE_DISABLED; +int8_t gExpectedLocationSettingState; +int8_t gExpectedWifiSettingState; bool start() { bool success = chreGnssLocationSessionStartAsync(50, 50, nullptr); @@ -120,6 +120,9 @@ void startTestNanoapp() { constexpr uint32_t kAppPerms = NanoappPermissions::CHRE_PERMS_GNSS | NanoappPermissions::CHRE_PERMS_WIFI; + gExpectedLocationSettingState = CHRE_USER_SETTING_STATE_DISABLED; + gExpectedWifiSettingState = CHRE_USER_SETTING_STATE_DISABLED; + UniquePtr<Nanoapp> nanoapp = createStaticNanoapp( "Test nanoapp", kAppId, kAppVersion, kAppPerms, start, handleEvent, end); EventLoopManagerSingleton::get()->deferCallback( diff --git a/test/simulation/test_base.cc b/test/simulation/test_base.cc index 622b84cc..bdec520e 100644 --- a/test/simulation/test_base.cc +++ b/test/simulation/test_base.cc @@ -76,6 +76,7 @@ void TestBase::TearDown() { TestEventQueueSingleton::deinit(); TaskManagerSingleton::deinit(); deleteNanoappInfos(); + unregisterAllTestNanoapps(); } TEST_F(TestBase, CanLoadAndStartSingleNanoapp) { @@ -118,6 +119,36 @@ TEST_F(TestBase, CanLoadAndStartMultipleNanoapps) { EXPECT_NE(id1, id2); } +TEST_F(TestBase, methods) { + CREATE_CHRE_TEST_EVENT(SOME_EVENT, 0); + + class App : public TestNanoapp { + public: + explicit App(TestNanoappInfo info) : TestNanoapp(info) {} + bool start() override { + LOGE("start"); + mTest = 0xc0ffee; + return true; + } + + void handleEvent(uint32_t /*senderInstanceId*/, uint16_t /*eventType*/, + const void * /**eventData*/) override { + LOGE("handleEvent %" PRIx16, mTest); + } + + void end() override { + LOGE("end"); + } + + protected: + uint32_t mTest = 0; + }; + + uint64_t appId = loadNanoapp(MakeUnique<App>(TestNanoappInfo{.id = 0x456})); + + sendEventToNanoapp(appId, SOME_EVENT); +} + // Explicitly instantiate the TestEventQueueSingleton to reduce codesize. template class Singleton<TestEventQueue>; diff --git a/test/simulation/test_util.cc b/test/simulation/test_util.cc index acc17fbb..44398ab7 100644 --- a/test/simulation/test_util.cc +++ b/test/simulation/test_util.cc @@ -17,6 +17,7 @@ #include "test_util.h" #include <gtest/gtest.h> +#include <unordered_map> #include "chre/core/event_loop_manager.h" #include "chre/core/nanoapp.h" @@ -29,13 +30,102 @@ namespace chre { namespace { - -// Keep the chreNslNanoappInfo instances alive for the lifetime of the -// test nanoapps. +/** + * List of chreNslNanoappInfo. + * + * Keep the chreNslNanoappInfo instances alive for the lifetime of the test + * nanoapps. + */ DynamicVector<UniquePtr<chreNslNanoappInfo>> gNanoappInfos; +/** Registry of nanoapp by ID. */ +std::unordered_map<uint64_t, UniquePtr<TestNanoapp>> nanoapps; + +/** + * @return a pointer to a registered nanoapp or nullptr if the appId is not + * registered. + */ +TestNanoapp *queryNanoapp(uint64_t appId) { + return nanoapps.count(appId) == 0 ? nullptr : nanoapps[appId].get(); +} + +/** + * Nanoapp start. + * + * Invokes the start method of a registered TestNanoapp. + */ +bool start() { + uint64_t id = chreGetAppId(); + TestNanoapp *app = queryNanoapp(id); + if (app == nullptr) { + LOGE("[start] unregistered nanoapp 0x%016" PRIx64, id); + return false; + } + return app->start(); +} + +/** + * Nanoapp handleEvent. + * + * Invokes the handleMethod method of a registered TestNanoapp. + */ +void handleEvent(uint32_t senderInstanceId, uint16_t eventType, + const void *eventData) { + uint64_t id = chreGetAppId(); + TestNanoapp *app = queryNanoapp(id); + if (app == nullptr) { + LOGE("[handleEvent] unregistered nanoapp 0x%016" PRIx64, id); + } else { + app->handleEvent(senderInstanceId, eventType, eventData); + } +} + +/** + * Nanoapp end. + * + * Invokes the end method of a registered TestNanoapp. + */ +void end() { + uint64_t id = chreGetAppId(); + TestNanoapp *app = queryNanoapp(id); + if (app == nullptr) { + LOGE("[end] unregistered nanoapp 0x%016" PRIx64, id); + } else { + app->end(); + } +} + +/** + * Registers a test nanoapp. + * + * TestNanoapps are registered when they are loaded so that their entry point + * methods can be called. + */ +void registerNanoapp(UniquePtr<TestNanoapp> app) { + if (nanoapps.count(app->id()) != 0) { + LOGE("A nanoapp with the same id is already registered"); + } else { + nanoapps[app->id()] = std::move(app); + } +} + +/** + * Unregisters a nanoapp. + * + * Calls the TestNanoapp destructor. + */ +void unregisterNanoapp(uint64_t appId) { + if (nanoapps.erase(appId) == 0) { + LOGE("The nanoapp is not registered"); + } +} + } // namespace +void unregisterAllTestNanoapps() { + nanoapps.clear(); +} + UniquePtr<Nanoapp> createStaticNanoapp( const char *name, uint64_t appId, uint32_t appVersion, uint32_t appPerms, decltype(nanoappStart) *startFunc, @@ -83,14 +173,11 @@ bool defaultNanoappStart() { return true; } -void defaultNanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType, - const void *eventData) { - UNUSED_VAR(senderInstanceId); - UNUSED_VAR(eventType); - UNUSED_VAR(eventData); -} +void defaultNanoappHandleEvent(uint32_t /*senderInstanceId*/, + uint16_t /*eventType*/, + const void * /*eventData*/) {} -void defaultNanoappEnd(){}; +void defaultNanoappEnd() {} void loadNanoapp(const char *name, uint64_t appId, uint32_t appVersion, uint32_t appPerms, decltype(nanoappStart) *startFunc, @@ -107,8 +194,33 @@ void loadNanoapp(const char *name, uint64_t appId, uint32_t appVersion, CHRE_EVENT_SIMULATION_TEST_NANOAPP_LOADED); } -template <> -void unloadNanoapp<uint64_t>(uint64_t appId) { +uint64_t loadNanoapp(UniquePtr<TestNanoapp> app) { + TestNanoapp *pApp = app.get(); + registerNanoapp(std::move(app)); + loadNanoapp(pApp->name(), pApp->id(), pApp->version(), pApp->perms(), &start, + &handleEvent, &end); + + return pApp->id(); +} + +void sendEventToNanoapp(uint64_t appId, uint16_t eventType) { + uint16_t instanceId; + if (EventLoopManagerSingleton::get() + ->getEventLoop() + .findNanoappInstanceIdByAppId(appId, &instanceId)) { + auto event = memoryAlloc<TestEvent>(); + ASSERT_NE(event, nullptr); + event->type = eventType; + EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( + CHRE_EVENT_TEST_EVENT, static_cast<void *>(event), + freeTestEventDataCallback, instanceId); + + } else { + LOGE("No instance found for nanoapp id = 0x%016" PRIx64, appId); + } +} + +void unloadNanoapp(uint64_t appId) { uint64_t *ptr = memoryAlloc<uint64_t>(); ASSERT_NE(ptr, nullptr); *ptr = appId; @@ -118,6 +230,8 @@ void unloadNanoapp<uint64_t>(uint64_t appId) { TestEventQueueSingleton::get()->waitForEvent( CHRE_EVENT_SIMULATION_TEST_NANOAPP_UNLOADED); + + unregisterNanoapp(appId); } void testFinishLoadingNanoappCallback(SystemCallbackType /* type */, diff --git a/test/simulation/timer_test.cc b/test/simulation/timer_test.cc index 1e81da02..ff76ddbe 100644 --- a/test/simulation/timer_test.cc +++ b/test/simulation/timer_test.cc @@ -46,18 +46,16 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { CREATE_CHRE_TEST_EVENT(START_TIMER, 0); CREATE_CHRE_TEST_EVENT(STOP_TIMER, 1); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static const uint32_t cookie = 123; + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { - static int count = 0; - case CHRE_EVENT_TIMER: { auto data = static_cast<const uint32_t *>(eventData); - if (*data == cookie) { - count++; - if (count == 3) { + if (*data == mCookie) { + mCount++; + if (mCount == 3) { TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_TIMER); } } @@ -68,8 +66,8 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { auto event = static_cast<const TestEvent *>(eventData); switch (event->type) { case START_TIMER: { - uint32_t handle = chreTimerSet(kOneMillisecondInNanoseconds, - &cookie, false /*oneShot*/); + uint32_t handle = chreTimerSet(10 * kOneMillisecondInNanoseconds, + &mCookie, false /*oneShot*/); TestEventQueueSingleton::get()->pushEvent(START_TIMER, handle); break; } @@ -82,10 +80,14 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { } } } - }; + } + + protected: + const uint32_t mCookie = 123; + int mCount = 0; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); TimerPool &timerPool = EventLoopManagerSingleton::get()->getEventLoop().getTimerPool(); @@ -93,10 +95,10 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { uint16_t instanceId; EXPECT_TRUE(EventLoopManagerSingleton::get() ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)); + .findNanoappInstanceIdByAppId(appId, &instanceId)); uint32_t handle; - sendEventToNanoapp(app, START_TIMER); + sendEventToNanoapp(appId, START_TIMER); waitForEvent(START_TIMER, &handle); EXPECT_NE(handle, CHRE_TIMER_INVALID); EXPECT_TRUE(hasNanoappTimers(timerPool, instanceId)); @@ -106,13 +108,13 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { bool success; // Cancelling an active timer should be successful. - sendEventToNanoapp(app, STOP_TIMER, handle); + sendEventToNanoapp(appId, STOP_TIMER, handle); waitForEvent(STOP_TIMER, &success); EXPECT_TRUE(success); EXPECT_FALSE(hasNanoappTimers(timerPool, instanceId)); // Cancelling an inactive time should return false. - sendEventToNanoapp(app, STOP_TIMER, handle); + sendEventToNanoapp(appId, STOP_TIMER, handle); waitForEvent(STOP_TIMER, &success); EXPECT_FALSE(success); } @@ -120,18 +122,15 @@ TEST_F(TestTimer, SetupAndCancelPeriodicTimer) { TEST_F(TestTimer, CancelPeriodicTimerOnUnload) { CREATE_CHRE_TEST_EVENT(START_TIMER, 0); - struct App : public TestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static const uint32_t cookie = 123; + class App : public TestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, const void *eventData) { switch (eventType) { - static int count = 0; - case CHRE_EVENT_TIMER: { auto data = static_cast<const uint32_t *>(eventData); - if (*data == cookie) { - count++; - if (count == 3) { + if (*data == mCookie) { + mCount++; + if (mCount == 3) { TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_TIMER); } } @@ -142,18 +141,22 @@ TEST_F(TestTimer, CancelPeriodicTimerOnUnload) { auto event = static_cast<const TestEvent *>(eventData); switch (event->type) { case START_TIMER: { - uint32_t handle = chreTimerSet(kOneMillisecondInNanoseconds, - &cookie, false /*oneShot*/); + uint32_t handle = chreTimerSet(10 * kOneMillisecondInNanoseconds, + &mCookie, false /*oneShot*/); TestEventQueueSingleton::get()->pushEvent(START_TIMER, handle); break; } } } } - }; + } + + protected: + const uint32_t mCookie = 123; + int mCount = 0; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); TimerPool &timerPool = EventLoopManagerSingleton::get()->getEventLoop().getTimerPool(); @@ -161,17 +164,17 @@ TEST_F(TestTimer, CancelPeriodicTimerOnUnload) { uint16_t instanceId; EXPECT_TRUE(EventLoopManagerSingleton::get() ->getEventLoop() - .findNanoappInstanceIdByAppId(app.id, &instanceId)); + .findNanoappInstanceIdByAppId(appId, &instanceId)); uint32_t handle; - sendEventToNanoapp(app, START_TIMER); + sendEventToNanoapp(appId, START_TIMER); waitForEvent(START_TIMER, &handle); EXPECT_NE(handle, CHRE_TIMER_INVALID); EXPECT_TRUE(hasNanoappTimers(timerPool, instanceId)); waitForEvent(CHRE_EVENT_TIMER); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(hasNanoappTimers(timerPool, instanceId)); } diff --git a/test/simulation/wifi_nan_test.cc b/test/simulation/wifi_nan_test.cc index 560e97a7..bab012ee 100644 --- a/test/simulation/wifi_nan_test.cc +++ b/test/simulation/wifi_nan_test.cc @@ -53,15 +53,18 @@ namespace { * - Grant WiFi permissions, * - Initialize the WiFi state in start. */ -struct NanTestNanoapp : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; +class NanTestNanoapp : public TestNanoapp { + public: + NanTestNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} - decltype(nanoappStart) *start = []() { + bool start() override { EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); PalNanEngineSingleton::get()->setFlags(PalNanEngine::Flags::NONE); return true; - }; + } }; /** @@ -71,37 +74,38 @@ struct NanTestNanoapp : public TestNanoapp { TEST_F(TestBase, WifiNanDisabledViaSettings) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - constexpr uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->requestType == CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE) { - ASSERT_EQ(event->errorCode, CHRE_ERROR_FUNCTION_DISABLED); - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + constexpr uint32_t kSubscribeCookie = 0x10aded; - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - chreWifiNanSubscribe(config, &kSubscribeCookie); - break; - } - } + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->requestType == CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE) { + ASSERT_EQ(event->errorCode, CHRE_ERROR_FUNCTION_DISABLED); + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT); + } + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + chreWifiNanSubscribe(config, &kSubscribeCookie); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, false /* enabled */); @@ -110,7 +114,7 @@ TEST_F(TestBase, WifiNanDisabledViaSettings) { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT); } @@ -122,54 +126,54 @@ TEST_F(TestBase, WifiNanDisabledViaSettings) { TEST_F(TestBase, WifiNanSuccessfulSubscribe) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { - auto event = - static_cast<const chreWifiNanIdentifierEvent *>(eventData); - if (event->result.errorCode == CHRE_ERROR_NONE) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kSubscribeCookie = 0x10aded; - case CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT: { - auto event = - static_cast<const chreWifiNanDiscoveryEvent *>(eventData); - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT, event->subscribeId); - break; - } + switch (eventType) { + case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { + auto event = + static_cast<const chreWifiNanIdentifierEvent *>(eventData); + if (event->result.errorCode == CHRE_ERROR_NONE) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); + } + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - const bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, - success); - break; - } - } + case CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT: { + auto event = + static_cast<const chreWifiNanDiscoveryEvent *>(eventData); + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT, event->subscribeId); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + const bool success = + chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); bool success; waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); @@ -188,46 +192,46 @@ TEST_F(TestBase, WifiNanSuccessfulSubscribe) { TEST_F(TestBase, WifiNanUnsSubscribeOnNanoappUnload) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { - auto event = - static_cast<const chreWifiNanIdentifierEvent *>(eventData); - if (event->result.errorCode == CHRE_ERROR_NONE) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kSubscribeCookie = 0x10aded; + + switch (eventType) { + case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { + auto event = + static_cast<const chreWifiNanIdentifierEvent *>(eventData); + if (event->result.errorCode == CHRE_ERROR_NONE) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); + } + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - const bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, - success); - break; - } - } + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + const bool success = + chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); bool success; waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); @@ -236,7 +240,7 @@ TEST_F(TestBase, WifiNanUnsSubscribeOnNanoappUnload) { waitForEvent(CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, &id); EXPECT_TRUE(PalNanEngineSingleton::get()->isSubscriptionActive(id)); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(PalNanEngineSingleton::get()->isSubscriptionActive(id)); } @@ -249,40 +253,40 @@ TEST_F(TestBase, WifiNanUnsSubscribeOnNanoappUnload) { TEST_F(TestBase, WifiNanUnuccessfulSubscribeTest) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { - auto event = - static_cast<const chreWifiNanIdentifierEvent *>(eventData); - if (event->result.errorCode != CHRE_ERROR_NONE) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kSubscribeCookie = 0x10aded; - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - const bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, - success); - break; - } - } + switch (eventType) { + case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { + auto event = + static_cast<const chreWifiNanIdentifierEvent *>(eventData); + if (event->result.errorCode != CHRE_ERROR_NONE) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT); + } + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + const bool success = + chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); PalNanEngineSingleton::get()->setFlags(PalNanEngine::Flags::FAIL_SUBSCRIBE); @@ -290,7 +294,7 @@ TEST_F(TestBase, WifiNanUnuccessfulSubscribeTest) { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); bool success; waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); @@ -305,9 +309,10 @@ TEST_F(TestBase, WifiNanUnuccessfulSubscribeTest) { TEST_F(TestBase, WifiNanServiceTerminatedTest) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { const uint32_t kSubscribeCookie = 0x10aded; switch (eventType) { @@ -350,16 +355,16 @@ TEST_F(TestBase, WifiNanServiceTerminatedTest) { } } } - }; + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); bool success; waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); @@ -390,63 +395,63 @@ TEST_F(TestBase, WifiNanServiceLostTest) { uint32_t publish; }; - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { - auto event = - static_cast<const chreWifiNanIdentifierEvent *>(eventData); - if (event->result.errorCode == CHRE_ERROR_NONE) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kSubscribeCookie = 0x10aded; - case CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT: { - auto event = - static_cast<const chreWifiNanDiscoveryEvent *>(eventData); - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT, event->subscribeId); - break; - } + switch (eventType) { + case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { + auto event = + static_cast<const chreWifiNanIdentifierEvent *>(eventData); + if (event->result.errorCode == CHRE_ERROR_NONE) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); + } + break; + } - case CHRE_EVENT_WIFI_NAN_SESSION_LOST: { - auto event = - static_cast<const chreWifiNanSessionLostEvent *>(eventData); - Ids ids = {.subscribe = event->id, .publish = event->peerId}; - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_SESSION_LOST, ids); - break; - } + case CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT: { + auto event = + static_cast<const chreWifiNanDiscoveryEvent *>(eventData); + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT, event->subscribeId); + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - const bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, - success); - break; - } - } + case CHRE_EVENT_WIFI_NAN_SESSION_LOST: { + auto event = + static_cast<const chreWifiNanSessionLostEvent *>(eventData); + Ids ids = {.subscribe = event->id, .publish = event->peerId}; + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_SESSION_LOST, ids); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + const bool success = + chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); bool success; waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); @@ -474,70 +479,71 @@ TEST_F(TestBase, WifiNanRangingTest) { CREATE_CHRE_TEST_EVENT(NAN_SUBSCRIBE, 0); CREATE_CHRE_TEST_EVENT(REQUEST_RANGING, 1); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kRangingCookie = 0xfa11; - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->requestType == CHRE_WIFI_REQUEST_TYPE_RANGING) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT); - } - break; - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kRangingCookie = 0xfa11; + const uint32_t kSubscribeCookie = 0x10aded; + + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->requestType == CHRE_WIFI_REQUEST_TYPE_RANGING) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT); + } + break; + } + + case CHRE_EVENT_WIFI_RANGING_RESULT: { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_RANGING_RESULT); + break; + } - case CHRE_EVENT_WIFI_RANGING_RESULT: { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_RANGING_RESULT); + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + const bool success = + chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, success); break; } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - const bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE, - success); - break; - } - - case REQUEST_RANGING: { - uint8_t fakeMacAddress[CHRE_WIFI_BSSID_LEN] = {0x1, 0x2, 0x3, - 0x4, 0x5, 0x6}; - struct chreWifiNanRangingParams fakeRangingParams; - std::memcpy(fakeRangingParams.macAddress, fakeMacAddress, - CHRE_WIFI_BSSID_LEN); - const bool success = chreWifiNanRequestRangingAsync( - &fakeRangingParams, &kRangingCookie); - TestEventQueueSingleton::get()->pushEvent(REQUEST_RANGING, - success); - break; - } - } + case REQUEST_RANGING: { + uint8_t fakeMacAddress[CHRE_WIFI_BSSID_LEN] = {0x1, 0x2, 0x3, + 0x4, 0x5, 0x6}; + struct chreWifiNanRangingParams fakeRangingParams; + std::memcpy(fakeRangingParams.macAddress, fakeMacAddress, + CHRE_WIFI_BSSID_LEN); + const bool success = chreWifiNanRequestRangingAsync( + &fakeRangingParams, &kRangingCookie); + TestEventQueueSingleton::get()->pushEvent(REQUEST_RANGING, + success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + bool success; chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, .service = "SomeServiceName", }; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); waitForEvent(NAN_SUBSCRIBE, &success); EXPECT_TRUE(success); - sendEventToNanoapp(app, REQUEST_RANGING, config); + sendEventToNanoapp(appId, REQUEST_RANGING, config); waitForEvent(REQUEST_RANGING, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT); @@ -550,52 +556,52 @@ TEST_F(TestBase, WifiNanSubscribeCancelTest) { CREATE_CHRE_TEST_EVENT(NAN_UNSUBSCRIBE, 2); CREATE_CHRE_TEST_EVENT(NAN_UNSUBSCRIBE_DONE, 3); - struct App : public NanTestNanoapp { - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - const uint32_t kSubscribeCookie = 0x10aded; - - switch (eventType) { - case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { - auto event = - static_cast<const chreWifiNanIdentifierEvent *>(eventData); - if (event->result.errorCode == CHRE_ERROR_NONE) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); - } + class App : public NanTestNanoapp { + public: + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + const uint32_t kSubscribeCookie = 0x10aded; + + switch (eventType) { + case CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT: { + auto event = + static_cast<const chreWifiNanIdentifierEvent *>(eventData); + if (event->result.errorCode == CHRE_ERROR_NONE) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event->id); + } + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case NAN_SUBSCRIBE: { + auto config = (chreWifiNanSubscribeConfig *)(event->data); + bool success = chreWifiNanSubscribe(config, &kSubscribeCookie); + TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE_DONE, + success); break; } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case NAN_SUBSCRIBE: { - auto config = (chreWifiNanSubscribeConfig *)(event->data); - bool success = - chreWifiNanSubscribe(config, &kSubscribeCookie); - TestEventQueueSingleton::get()->pushEvent(NAN_SUBSCRIBE_DONE, - success); - break; - } - case NAN_UNSUBSCRIBE: { - auto *id = static_cast<uint32_t *>(event->data); - bool success = chreWifiNanSubscribeCancel(*id); - // Note that since we're 'simulating' NAN functionality here, - // the async subscribe cancel event will be handled before - // the return event below is posted. For a real on-device (or - // non-simulated) test, this won't be the case, and care must - // be taken to handle the asynchronicity appropriately. - TestEventQueueSingleton::get()->pushEvent( - NAN_UNSUBSCRIBE_DONE, success); - break; - } - } + case NAN_UNSUBSCRIBE: { + auto *id = static_cast<uint32_t *>(event->data); + bool success = chreWifiNanSubscribeCancel(*id); + // Note that since we're 'simulating' NAN functionality here, + // the async subscribe cancel event will be handled before + // the return event below is posted. For a real on-device (or + // non-simulated) test, this won't be the case, and care must + // be taken to handle the asynchronicity appropriately. + TestEventQueueSingleton::get()->pushEvent(NAN_UNSUBSCRIBE_DONE, + success); + break; } } - }; + } + } + } }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); chreWifiNanSubscribeConfig config = { .subscribeType = CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE, @@ -603,7 +609,7 @@ TEST_F(TestBase, WifiNanSubscribeCancelTest) { }; bool success = false; - sendEventToNanoapp(app, NAN_SUBSCRIBE, config); + sendEventToNanoapp(appId, NAN_SUBSCRIBE, config); waitForEvent(NAN_SUBSCRIBE_DONE, &success); ASSERT_TRUE(success); @@ -615,7 +621,7 @@ TEST_F(TestBase, WifiNanSubscribeCancelTest) { EXPECT_EQ(wifiRequestManager.getNumNanSubscriptions(), 1); success = false; - sendEventToNanoapp(app, NAN_UNSUBSCRIBE, id); + sendEventToNanoapp(appId, NAN_UNSUBSCRIBE, id); waitForEvent(NAN_UNSUBSCRIBE_DONE, &success); ASSERT_TRUE(success); // TODO(b/272351526): consider adding an async result event to catch when the diff --git a/test/simulation/wifi_scan_test.cc b/test/simulation/wifi_scan_test.cc index 4f8d80d3..70bfa7c9 100644 --- a/test/simulation/wifi_scan_test.cc +++ b/test/simulation/wifi_scan_test.cc @@ -40,6 +40,9 @@ struct WifiAsyncData { chreError errorCode; }; +constexpr uint64_t kAppOneId = 0x0123456789000001; +constexpr uint64_t kAppTwoId = 0x0123456789000002; + class WifiScanRequestQueueTestBase : public TestBase { public: void SetUp() { @@ -56,15 +59,18 @@ class WifiScanRequestQueueTestBase : public TestBase { } }; -struct WifiScanTestNanoapp : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; +class WifiScanTestNanoapp : public TestNanoapp { + public: + WifiScanTestNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - constexpr uint8_t kMaxPendingCookie = 10; - static uint32_t cookies[kMaxPendingCookie]; - static uint8_t nextFreeCookieIndex = 0; + explicit WifiScanTestNanoapp(uint64_t id) + : TestNanoapp(TestNanoappInfo{ + .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast<const chreAsyncResult *>(eventData); @@ -86,12 +92,12 @@ struct WifiScanTestNanoapp : public TestNanoapp { switch (event->type) { case SCAN_REQUEST: bool success = false; - if (nextFreeCookieIndex < kMaxPendingCookie) { - cookies[nextFreeCookieIndex] = + if (mNextFreeCookieIndex < kMaxPendingCookie) { + mCookies[mNextFreeCookieIndex] = *static_cast<uint32_t *>(event->data); success = chreWifiRequestScanAsyncDefault( - &cookies[nextFreeCookieIndex]); - nextFreeCookieIndex++; + &mCookies[mNextFreeCookieIndex]); + mNextFreeCookieIndex++; } else { LOGE("Too many cookies passed from test body!"); } @@ -99,11 +105,16 @@ struct WifiScanTestNanoapp : public TestNanoapp { } } } - }; + } + + protected: + static constexpr uint8_t kMaxPendingCookie = 10; + uint32_t mCookies[kMaxPendingCookie]; + uint8_t mNextFreeCookieIndex = 0; }; TEST_F(TestBase, WifiScanBasicSettingTest) { - auto app = loadNanoapp<WifiScanTestNanoapp>(); + uint64_t appId = loadNanoapp(MakeUnique<WifiScanTestNanoapp>()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); @@ -112,7 +123,7 @@ TEST_F(TestBase, WifiScanBasicSettingTest) { bool success; WifiAsyncData wifiAsyncData; - sendEventToNanoapp(app, SCAN_REQUEST, firstCookie); + sendEventToNanoapp(appId, SCAN_REQUEST, firstCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); @@ -125,7 +136,7 @@ TEST_F(TestBase, WifiScanBasicSettingTest) { Setting::WIFI_AVAILABLE, false /* enabled */); constexpr uint32_t secondCookie = 0x2020; - sendEventToNanoapp(app, SCAN_REQUEST, secondCookie); + sendEventToNanoapp(appId, SCAN_REQUEST, secondCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); @@ -135,67 +146,197 @@ TEST_F(TestBase, WifiScanBasicSettingTest) { EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); - unloadNanoapp(app); + unloadNanoapp(appId); } TEST_F(WifiScanRequestQueueTestBase, WifiQueuedScanSettingChangeTest) { - struct WifiScanTestNanoappTwo : public WifiScanTestNanoapp { - uint64_t id = 0x1123456789abcdef; + CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT, + 1); + CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, 2); + // Expecting to receive two event, one from each nanoapp. + constexpr uint8_t kExpectedReceiveAsyncResultCount = 2; + // receivedAsyncEventCount is shared across apps and must be static. + // But we want it initialized each time the test is executed. + static uint8_t receivedAsyncEventCount; + receivedAsyncEventCount = 0; + + class WifiScanTestConcurrentNanoapp : public TestNanoapp { + public: + explicit WifiScanTestConcurrentNanoapp(uint64_t id) + : TestNanoapp(TestNanoappInfo{ + .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + mReceivedAsyncResult = WifiAsyncData{ + .cookie = static_cast<const uint32_t *>(event->cookie), + .errorCode = static_cast<chreError>(event->errorCode)}; + ++receivedAsyncEventCount; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + bool success = false; + switch (event->type) { + case SCAN_REQUEST: + mSentCookie = *static_cast<uint32_t *>(event->data); + success = chreWifiRequestScanAsyncDefault(&(mSentCookie)); + TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); + break; + case CONCURRENT_NANOAPP_READ_ASYNC_EVENT: + TestEventQueueSingleton::get()->pushEvent( + CONCURRENT_NANOAPP_READ_ASYNC_EVENT, mReceivedAsyncResult); + break; + } + } + } + + if (receivedAsyncEventCount == kExpectedReceiveAsyncResultCount) { + TestEventQueueSingleton::get()->pushEvent( + CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); + } + } + + protected: + uint32_t mSentCookie; + WifiAsyncData mReceivedAsyncResult; }; - auto firstApp = loadNanoapp<WifiScanTestNanoapp>(); - auto secondApp = loadNanoapp<WifiScanTestNanoappTwo>(); + uint64_t appOneId = + loadNanoapp(MakeUnique<WifiScanTestConcurrentNanoapp>(kAppOneId)); + uint64_t appTwoId = + loadNanoapp(MakeUnique<WifiScanTestConcurrentNanoapp>(kAppTwoId)); - constexpr uint32_t firstRequestCookie = 0x1010; - constexpr uint32_t secondRequestCookie = 0x2020; + constexpr uint32_t appOneRequestCookie = 0x1010; + constexpr uint32_t appTwoRequestCookie = 0x2020; bool success; - sendEventToNanoapp(firstApp, SCAN_REQUEST, firstRequestCookie); + sendEventToNanoapp(appOneId, SCAN_REQUEST, appOneRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); - sendEventToNanoapp(secondApp, SCAN_REQUEST, secondRequestCookie); + sendEventToNanoapp(appTwoId, SCAN_REQUEST, appTwoRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, false /* enabled */); + // We need to make sure that each nanoapp has received one async result before + // further analysis. + waitForEvent(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); + WifiAsyncData wifiAsyncData; - waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &wifiAsyncData); + sendEventToNanoapp(appOneId, CONCURRENT_NANOAPP_READ_ASYNC_EVENT); + waitForEvent(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_NONE); - EXPECT_EQ(*wifiAsyncData.cookie, firstRequestCookie); - waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); + EXPECT_EQ(*wifiAsyncData.cookie, appOneRequestCookie); - waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &wifiAsyncData); + sendEventToNanoapp(appTwoId, CONCURRENT_NANOAPP_READ_ASYNC_EVENT); + waitForEvent(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_FUNCTION_DISABLED); - EXPECT_EQ(*wifiAsyncData.cookie, secondRequestCookie); + EXPECT_EQ(*wifiAsyncData.cookie, appTwoRequestCookie); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); - unloadNanoapp(firstApp); - unloadNanoapp(secondApp); + unloadNanoapp(appOneId); + unloadNanoapp(appTwoId); } TEST_F(WifiScanRequestQueueTestBase, WifiScanRejectRequestFromSameNanoapp) { - auto app = loadNanoapp<WifiScanTestNanoapp>(); + CREATE_CHRE_TEST_EVENT(RECEIVED_ALL_EXPECTED_EVENTS, 1); + CREATE_CHRE_TEST_EVENT(READ_ASYNC_EVENT, 2); - constexpr uint32_t firstRequestCookie = 0x1010; - constexpr uint32_t secondRequestCookie = 0x2020; + static constexpr uint8_t kExpectedReceivedScanRequestCount = 2; + + class WifiScanTestBufferedAsyncResultNanoapp : public TestNanoapp { + public: + WifiScanTestBufferedAsyncResultNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + mReceivedAsyncResult = WifiAsyncData{ + .cookie = static_cast<const uint32_t *>(event->cookie), + .errorCode = static_cast<chreError>(event->errorCode)}; + ++mReceivedAsyncEventCount; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + bool success = false; + switch (event->type) { + case SCAN_REQUEST: + if (mReceivedScanRequestCount >= + kExpectedReceivedScanRequestCount) { + LOGE("Asking too many scan request"); + } else { + mReceivedCookies[mReceivedScanRequestCount] = + *static_cast<uint32_t *>(event->data); + success = chreWifiRequestScanAsyncDefault( + &(mReceivedCookies[mReceivedScanRequestCount])); + ++mReceivedScanRequestCount; + TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, + success); + } + break; + case READ_ASYNC_EVENT: + TestEventQueueSingleton::get()->pushEvent(READ_ASYNC_EVENT, + mReceivedAsyncResult); + break; + } + } + } + if (mReceivedAsyncEventCount == kExpectedReceivedAsyncResultCount && + mReceivedScanRequestCount == kExpectedReceivedScanRequestCount) { + TestEventQueueSingleton::get()->pushEvent(RECEIVED_ALL_EXPECTED_EVENTS); + } + } + + protected: + // We are only expecting to receive one async result since the second + // request is expected to fail. + const uint8_t kExpectedReceivedAsyncResultCount = 1; + uint8_t mReceivedAsyncEventCount = 0; + uint8_t mReceivedScanRequestCount = 0; + + // We need to have two cookie storage to separate the two scan request. + uint32_t mReceivedCookies[kExpectedReceivedScanRequestCount]; + WifiAsyncData mReceivedAsyncResult; + }; + + uint64_t appId = + loadNanoapp(MakeUnique<WifiScanTestBufferedAsyncResultNanoapp>()); + + constexpr uint32_t kFirstRequestCookie = 0x1010; + constexpr uint32_t kSecondRequestCookie = 0x2020; bool success; - sendEventToNanoapp(app, SCAN_REQUEST, firstRequestCookie); + sendEventToNanoapp(appId, SCAN_REQUEST, kFirstRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); - sendEventToNanoapp(app, SCAN_REQUEST, secondRequestCookie); + sendEventToNanoapp(appId, SCAN_REQUEST, kSecondRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_FALSE(success); + // We need to make sure that the nanoapp has received one async result and did + // two scan requests before further analysis. + waitForEvent(RECEIVED_ALL_EXPECTED_EVENTS); + WifiAsyncData wifiAsyncData; - waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &wifiAsyncData); + sendEventToNanoapp(appId, READ_ASYNC_EVENT); + waitForEvent(READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_NONE); - EXPECT_EQ(*wifiAsyncData.cookie, firstRequestCookie); - waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); + EXPECT_EQ(*wifiAsyncData.cookie, kFirstRequestCookie); - unloadNanoapp(app); + unloadNanoapp(appId); } TEST_F(WifiScanRequestQueueTestBase, WifiScanActiveScanFromDistinctNanoapps) { @@ -203,36 +344,25 @@ TEST_F(WifiScanRequestQueueTestBase, WifiScanActiveScanFromDistinctNanoapps) { 1); CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_READ_COOKIE, 2); - struct AppCookies { - uint32_t sent = 0; - uint32_t received = 0; - }; - - constexpr uint64_t kAppOneId = 0x0123456789000001; - constexpr uint64_t kAppTwoId = 0x0123456789000002; - - struct WifiScanTestConcurrentNanoappOne : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - uint64_t id = kAppOneId; + constexpr uint8_t kExpectedReceiveAsyncResultCount = 2; + // receivedCookieCount is shared across apps and must be static. + // But we want it initialized each time the test is executed. + static uint8_t receivedCookieCount; + receivedCookieCount = 0; - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - constexpr uint8_t kExpectedReceiveAsyncResultCount = 2; - static uint8_t receivedCookieCount = 0; - static AppCookies appOneCookies; - static AppCookies appTwoCookies; - - // Retrieve cookies from different apps that have the same access to - // static storage due to inheritance. - AppCookies *appCookies = - chreGetAppId() == kAppTwoId ? &appTwoCookies : &appOneCookies; + class WifiScanTestConcurrentNanoapp : public TestNanoapp { + public: + explicit WifiScanTestConcurrentNanoapp(uint64_t id) + : TestNanoapp(TestNanoappInfo{ + .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast<const chreAsyncResult *>(eventData); if (event->errorCode == CHRE_ERROR_NONE) { - appCookies->received = - *static_cast<const uint32_t *>(event->cookie); + mReceivedCookie = *static_cast<const uint32_t *>(event->cookie); ++receivedCookieCount; } else { LOGE("Received failed async result"); @@ -248,54 +378,54 @@ TEST_F(WifiScanRequestQueueTestBase, WifiScanActiveScanFromDistinctNanoapps) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); bool success = false; - uint32_t expectedCookie; switch (event->type) { case SCAN_REQUEST: - appCookies->sent = *static_cast<uint32_t *>(event->data); - success = chreWifiRequestScanAsyncDefault(&(appCookies->sent)); + mSentCookie = *static_cast<uint32_t *>(event->data); + success = chreWifiRequestScanAsyncDefault(&(mSentCookie)); TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); break; case CONCURRENT_NANOAPP_READ_COOKIE: TestEventQueueSingleton::get()->pushEvent( - CONCURRENT_NANOAPP_READ_COOKIE, appCookies->received); + CONCURRENT_NANOAPP_READ_COOKIE, mReceivedCookie); break; } } - }; - }; - }; + } + } - struct WifiScanTestConcurrentNanoappTwo - : public WifiScanTestConcurrentNanoappOne { - uint64_t id = kAppTwoId; + protected: + uint32_t mSentCookie; + uint32_t mReceivedCookie; }; - auto appOne = loadNanoapp<WifiScanTestConcurrentNanoappOne>(); - auto appTwo = loadNanoapp<WifiScanTestConcurrentNanoappTwo>(); + uint64_t appOneId = + loadNanoapp(MakeUnique<WifiScanTestConcurrentNanoapp>(kAppOneId)); + uint64_t appTwoId = + loadNanoapp(MakeUnique<WifiScanTestConcurrentNanoapp>(kAppTwoId)); constexpr uint32_t kAppOneRequestCookie = 0x1010; constexpr uint32_t kAppTwoRequestCookie = 0x2020; bool success; - sendEventToNanoapp(appOne, SCAN_REQUEST, kAppOneRequestCookie); + sendEventToNanoapp(appOneId, SCAN_REQUEST, kAppOneRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); - sendEventToNanoapp(appTwo, SCAN_REQUEST, kAppTwoRequestCookie); + sendEventToNanoapp(appTwoId, SCAN_REQUEST, kAppTwoRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); uint32_t receivedCookie; - sendEventToNanoapp(appOne, CONCURRENT_NANOAPP_READ_COOKIE); + sendEventToNanoapp(appOneId, CONCURRENT_NANOAPP_READ_COOKIE); waitForEvent(CONCURRENT_NANOAPP_READ_COOKIE, &receivedCookie); EXPECT_EQ(kAppOneRequestCookie, receivedCookie); - sendEventToNanoapp(appTwo, CONCURRENT_NANOAPP_READ_COOKIE); + sendEventToNanoapp(appTwoId, CONCURRENT_NANOAPP_READ_COOKIE); waitForEvent(CONCURRENT_NANOAPP_READ_COOKIE, &receivedCookie); EXPECT_EQ(kAppTwoRequestCookie, receivedCookie); - unloadNanoapp(appOne); - unloadNanoapp(appTwo); + unloadNanoapp(appOneId); + unloadNanoapp(appTwoId); } } // namespace diff --git a/test/simulation/wifi_test.cc b/test/simulation/wifi_test.cc index 43c9384c..deca31c8 100644 --- a/test/simulation/wifi_test.cc +++ b/test/simulation/wifi_test.cc @@ -41,53 +41,58 @@ TEST_F(TestBase, WifiCanSubscribeAndUnsubscribeToScanMonitoring) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; - } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case MONITORING_REQUEST: - auto request = - static_cast<const MonitoringRequest *>(event->data); - cookie = request->cookie; - bool success = chreWifiConfigureScanMonitorAsync( - request->enable, &cookie); - TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, - success); - } - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); } - }; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case MONITORING_REQUEST: + auto request = + static_cast<const MonitoringRequest *>(event->data); + mCookie = request->cookie; + bool success = + chreWifiConfigureScanMonitorAsync(request->enable, &mCookie); + TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, + success); + } + } + } + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); MonitoringRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, MONITORING_REQUEST, request); + sendEventToNanoapp(appId, MONITORING_REQUEST, request); uint32_t cookie; waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie); EXPECT_EQ(cookie, request.cookie); EXPECT_TRUE(chrePalWifiIsScanMonitoringActive()); request = {.enable = false, .cookie = 0x456}; - sendEventToNanoapp(app, MONITORING_REQUEST, request); + sendEventToNanoapp(appId, MONITORING_REQUEST, request); bool success; waitForEvent(MONITORING_REQUEST, &success); EXPECT_TRUE(success); @@ -104,46 +109,51 @@ TEST_F(TestBase, WifiScanMonitoringDisabledOnUnload) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; - } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case MONITORING_REQUEST: - auto request = - static_cast<const MonitoringRequest *>(event->data); - cookie = request->cookie; - bool success = chreWifiConfigureScanMonitorAsync( - request->enable, &cookie); - TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, - success); - } - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); } - }; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case MONITORING_REQUEST: + auto request = + static_cast<const MonitoringRequest *>(event->data); + mCookie = request->cookie; + bool success = + chreWifiConfigureScanMonitorAsync(request->enable, &mCookie); + TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, + success); + } + } + } + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); MonitoringRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, MONITORING_REQUEST, request); + sendEventToNanoapp(appId, MONITORING_REQUEST, request); bool success; waitForEvent(MONITORING_REQUEST, &success); EXPECT_TRUE(success); @@ -152,7 +162,7 @@ TEST_F(TestBase, WifiScanMonitoringDisabledOnUnload) { EXPECT_EQ(cookie, request.cookie); EXPECT_TRUE(chrePalWifiIsScanMonitoringActive()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); } @@ -164,46 +174,51 @@ TEST_F(TestBase, WifiScanMonitoringDisabledOnUnloadAndCanBeReEnabled) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; - } - - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case MONITORING_REQUEST: - auto request = - static_cast<const MonitoringRequest *>(event->data); - cookie = request->cookie; - bool success = chreWifiConfigureScanMonitorAsync( - request->enable, &cookie); - TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, - success); - } - } + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case MONITORING_REQUEST: + auto request = + static_cast<const MonitoringRequest *>(event->data); + mCookie = request->cookie; + bool success = + chreWifiConfigureScanMonitorAsync(request->enable, &mCookie); + TestEventQueueSingleton::get()->pushEvent(MONITORING_REQUEST, + success); } - }; + } + } + } + + protected: + uint32_t mCookie; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); MonitoringRequest request{.enable = true, .cookie = 0x123}; - sendEventToNanoapp(app, MONITORING_REQUEST, request); + sendEventToNanoapp(appId, MONITORING_REQUEST, request); bool success; waitForEvent(MONITORING_REQUEST, &success); EXPECT_TRUE(success); @@ -212,14 +227,14 @@ TEST_F(TestBase, WifiScanMonitoringDisabledOnUnloadAndCanBeReEnabled) { EXPECT_EQ(cookie, request.cookie); EXPECT_TRUE(chrePalWifiIsScanMonitoringActive()); - unloadNanoapp(app); + unloadNanoapp(appId); EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); - app = loadNanoapp<App>(); + appId = loadNanoapp(MakeUnique<App>()); EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); request = {.enable = true, .cookie = 0x456}; - sendEventToNanoapp(app, MONITORING_REQUEST, request); + sendEventToNanoapp(appId, MONITORING_REQUEST, request); waitForEvent(MONITORING_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie); diff --git a/test/simulation/wifi_timeout_test.cc b/test/simulation/wifi_timeout_test.cc index 6e448b0b..7c310fd7 100644 --- a/test/simulation/wifi_timeout_test.cc +++ b/test/simulation/wifi_timeout_test.cc @@ -20,8 +20,10 @@ #include "chre/core/settings.h" #include "chre/platform/linux/pal_wifi.h" #include "chre/platform/log.h" +#include "chre/util/nanoapp/app_id.h" #include "chre/util/system/napp_permissions.h" #include "chre_api/chre/event.h" +#include "chre_api/chre/re.h" #include "chre_api/chre/wifi.h" #include "gtest/gtest.h" #include "test_base.h" @@ -31,29 +33,142 @@ namespace chre { namespace { -// WifiTimeoutTestBase needs to set timeout more than max wifi async timeout -// time. If not, waitForEvent will timeout before actual timeout happens in -// CHRE, making us unable to observe how system handles timeout. +// WifiTimeoutTestBase needs to set timeout more than the max waitForEvent() +// should process (Currently it is +// WifiCanDispatchSecondScanRequestInQueueAfterFirstTimeout). If not, +// waitForEvent will timeout before actual timeout happens in CHRE, making us +// unable to observe how system handles timeout. class WifiTimeoutTestBase : public TestBase { protected: uint64_t getTimeoutNs() const override { - return 2 * CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS; + return 3 * CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS; } }; +CREATE_CHRE_TEST_EVENT(SCAN_REQUEST, 20); +CREATE_CHRE_TEST_EVENT(REQUEST_TIMED_OUT, 21); + TEST_F(WifiTimeoutTestBase, WifiScanRequestTimeoutTest) { - CREATE_CHRE_TEST_EVENT(SCAN_REQUEST, 1); + class ScanTestNanoapp : public TestNanoapp { + public: + explicit ScanTestNanoapp() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + bool start() override { + mRequestTimer = CHRE_TIMER_INVALID; + return true; + } + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (mRequestTimer != CHRE_TIMER_INVALID) { + chreTimerCancel(mRequestTimer); + mRequestTimer = CHRE_TIMER_INVALID; + } + if (event->success) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } + + case CHRE_EVENT_WIFI_SCAN_RESULT: { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_SCAN_RESULT); + break; + } + + case CHRE_EVENT_TIMER: { + TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT); + mRequestTimer = CHRE_TIMER_INVALID; + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case SCAN_REQUEST: + bool success = false; + mCookie = *static_cast<uint32_t *>(event->data); + if (chreWifiRequestScanAsyncDefault(&mCookie)) { + mRequestTimer = + chreTimerSet(CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS, nullptr, + true /* oneShot */); + success = mRequestTimer != CHRE_TIMER_INVALID; + } + TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); + break; + } + break; + } + } + } - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; + protected: + uint32_t mCookie; + uint32_t mRequestTimer; + }; - decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType, - const void *eventData) { - static uint32_t cookie; + uint64_t appId = loadNanoapp(MakeUnique<ScanTestNanoapp>()); + constexpr uint32_t timeOutCookie = 0xdead; + chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, + false /* enableResponse */); + sendEventToNanoapp(appId, SCAN_REQUEST, timeOutCookie); + bool success; + waitForEvent(SCAN_REQUEST, &success); + EXPECT_TRUE(success); + + waitForEvent(REQUEST_TIMED_OUT); + + // Make sure that we can still request scan after a timedout + // request. + constexpr uint32_t successCookie = 0x0101; + chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, + true /* enableResponse */); + sendEventToNanoapp(appId, SCAN_REQUEST, successCookie); + waitForEvent(SCAN_REQUEST, &success); + EXPECT_TRUE(success); + waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); + + unloadNanoapp(appId); +} + +TEST_F(WifiTimeoutTestBase, WifiCanDispatchQueuedRequestAfterOneTimeout) { + constexpr uint8_t kNanoappNum = 2; + // receivedTimeout is shared across apps and must be static. + // But we want it initialized each time the test is executed. + static uint8_t receivedTimeout; + receivedTimeout = 0; + + class ScanTestNanoapp : public TestNanoapp { + public: + explicit ScanTestNanoapp(uint64_t id = kDefaultTestNanoappId) + : TestNanoapp(TestNanoappInfo{ + .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + bool start() override { + for (uint8_t i = 0; i < kNanoappNum; ++i) { + mRequestTimers[i] = CHRE_TIMER_INVALID; + } + return true; + } + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + size_t index = id() - CHRE_VENDOR_ID_EXAMPLE - 1; switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast<const chreAsyncResult *>(eventData); + if (mRequestTimers[index] != CHRE_TIMER_INVALID) { + chreTimerCancel(mRequestTimers[index]); + mRequestTimers[index] = CHRE_TIMER_INVALID; + } if (event->success) { TestEventQueueSingleton::get()->pushEvent( CHRE_EVENT_WIFI_ASYNC_RESULT, @@ -68,43 +183,76 @@ TEST_F(WifiTimeoutTestBase, WifiScanRequestTimeoutTest) { break; } + case CHRE_EVENT_TIMER: { + if (eventData == &mCookie[index]) { + receivedTimeout++; + mRequestTimers[index] = CHRE_TIMER_INVALID; + } + if (receivedTimeout == 2) { + TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT); + } + break; + } + case CHRE_EVENT_TEST_EVENT: { auto event = static_cast<const TestEvent *>(eventData); switch (event->type) { case SCAN_REQUEST: - cookie = *static_cast<uint32_t *>(event->data); - bool success = chreWifiRequestScanAsyncDefault(&cookie); + bool success = false; + mCookie[index] = *static_cast<uint32_t *>(event->data); + if (chreWifiRequestScanAsyncDefault(&mCookie[index])) { + mRequestTimers[index] = + chreTimerSet(CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS, + &mCookie[index], true /* oneShot */); + success = mRequestTimers[index] != CHRE_TIMER_INVALID; + } TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); + break; } + break; } } - }; + } + + protected: + uint32_t mCookie[kNanoappNum]; + uint32_t mRequestTimers[kNanoappNum]; }; + constexpr uint64_t kAppOneId = makeExampleNanoappId(1); + constexpr uint64_t kAppTwoId = makeExampleNanoappId(2); - auto app = loadNanoapp<App>(); + uint64_t firstAppId = loadNanoapp(MakeUnique<ScanTestNanoapp>(kAppOneId)); + uint64_t secondAppId = loadNanoapp(MakeUnique<ScanTestNanoapp>(kAppTwoId)); constexpr uint32_t timeOutCookie = 0xdead; - chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, false); - sendEventToNanoapp(app, SCAN_REQUEST, timeOutCookie); + chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, + false /* enableResponse */); bool success; + sendEventToNanoapp(firstAppId, SCAN_REQUEST, timeOutCookie); + waitForEvent(SCAN_REQUEST, &success); + EXPECT_TRUE(success); + sendEventToNanoapp(secondAppId, SCAN_REQUEST, timeOutCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); - // Add 1 second to prevent race condition. - constexpr uint8_t kWifiScanRequestTimeoutSec = - (CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS / CHRE_NSEC_PER_SEC) + 1; - std::this_thread::sleep_for(std::chrono::seconds(kWifiScanRequestTimeoutSec)); + waitForEvent(REQUEST_TIMED_OUT); - // Make sure that we can still request scan after a timedout + // Make sure that we can still request scan for both nanoapps after a timedout // request. constexpr uint32_t successCookie = 0x0101; - chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, true); - sendEventToNanoapp(app, SCAN_REQUEST, successCookie); + chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN, + true /* enableResponse */); + sendEventToNanoapp(firstAppId, SCAN_REQUEST, successCookie); + waitForEvent(SCAN_REQUEST, &success); + EXPECT_TRUE(success); + waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); + sendEventToNanoapp(secondAppId, SCAN_REQUEST, successCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); - unloadNanoapp(app); + unloadNanoapp(firstAppId); + unloadNanoapp(secondAppId); } TEST_F(WifiTimeoutTestBase, WifiScanMonitorTimeoutTest) { @@ -115,61 +263,83 @@ TEST_F(WifiTimeoutTestBase, WifiScanMonitorTimeoutTest) { uint32_t cookie; }; - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; + bool start() override { + mRequestTimer = CHRE_TIMER_INVALID; + return true; + } - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + if (mRequestTimer != CHRE_TIMER_INVALID) { + chreTimerCancel(mRequestTimer); + mRequestTimer = CHRE_TIMER_INVALID; } + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); + } + break; + } + + case CHRE_EVENT_TIMER: { + mRequestTimer = CHRE_TIMER_INVALID; + TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT); + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case SCAN_MONITOR_REQUEST: - auto request = - static_cast<const MonitoringRequest *>(event->data); - cookie = request->cookie; - bool success = chreWifiConfigureScanMonitorAsync( - request->enable, &cookie); - TestEventQueueSingleton::get()->pushEvent( - SCAN_MONITOR_REQUEST, success); + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case SCAN_MONITOR_REQUEST: + bool success = false; + auto request = + static_cast<const MonitoringRequest *>(event->data); + if (chreWifiConfigureScanMonitorAsync(request->enable, + &mCookie)) { + mCookie = request->cookie; + mRequestTimer = chreTimerSet(CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS, + nullptr, true /* oneShot */); + success = mRequestTimer != CHRE_TIMER_INVALID; } - } + + TestEventQueueSingleton::get()->pushEvent(SCAN_MONITOR_REQUEST, + success); } - }; + } + } + } + + protected: + uint32_t mCookie; + uint32_t mRequestTimer; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); MonitoringRequest timeoutRequest{.enable = true, .cookie = 0xdead}; chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN_MONITORING, false); - sendEventToNanoapp(app, SCAN_MONITOR_REQUEST, timeoutRequest); + sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, timeoutRequest); bool success; waitForEvent(SCAN_MONITOR_REQUEST, &success); EXPECT_TRUE(success); - // Add 1 second to prevent race condition. - constexpr uint8_t kWifiConfigureScanMonitorTimeoutSec = - (CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS / CHRE_NSEC_PER_SEC) + 1; - std::this_thread::sleep_for( - std::chrono::seconds(kWifiConfigureScanMonitorTimeoutSec)); + waitForEvent(REQUEST_TIMED_OUT); // Make sure that we can still request to change scan monitor after a timedout // request. MonitoringRequest enableRequest{.enable = true, .cookie = 0x1010}; chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN_MONITORING, true); - sendEventToNanoapp(app, SCAN_MONITOR_REQUEST, enableRequest); + sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, enableRequest); waitForEvent(SCAN_MONITOR_REQUEST, &success); EXPECT_TRUE(success); @@ -179,7 +349,7 @@ TEST_F(WifiTimeoutTestBase, WifiScanMonitorTimeoutTest) { EXPECT_TRUE(chrePalWifiIsScanMonitoringActive()); MonitoringRequest disableRequest{.enable = false, .cookie = 0x0101}; - sendEventToNanoapp(app, SCAN_MONITOR_REQUEST, disableRequest); + sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, disableRequest); waitForEvent(SCAN_MONITOR_REQUEST, &success); EXPECT_TRUE(success); @@ -187,87 +357,105 @@ TEST_F(WifiTimeoutTestBase, WifiScanMonitorTimeoutTest) { EXPECT_EQ(cookie, disableRequest.cookie); EXPECT_FALSE(chrePalWifiIsScanMonitoringActive()); - unloadNanoapp(app); + unloadNanoapp(appId); } TEST_F(WifiTimeoutTestBase, WifiRequestRangingTimeoutTest) { CREATE_CHRE_TEST_EVENT(RANGING_REQUEST, 0); - CREATE_CHRE_TEST_EVENT(RANGING_RESULT_TIMEOUT, 1); - - struct App : public TestNanoapp { - uint32_t perms = NanoappPermissions::CHRE_PERMS_WIFI; - - decltype(nanoappHandleEvent) *handleEvent = - [](uint32_t, uint16_t eventType, const void *eventData) { - static uint32_t cookie; - - switch (eventType) { - case CHRE_EVENT_WIFI_ASYNC_RESULT: { - auto *event = static_cast<const chreAsyncResult *>(eventData); - if (event->success) { - if (event->errorCode == 0) { - TestEventQueueSingleton::get()->pushEvent( - CHRE_EVENT_WIFI_ASYNC_RESULT, - *(static_cast<const uint32_t *>(event->cookie))); - } - } else if (event->errorCode == CHRE_ERROR_TIMEOUT) { - TestEventQueueSingleton::get()->pushEvent( - RANGING_RESULT_TIMEOUT, - *(static_cast<const uint32_t *>(event->cookie))); - } - break; + + class App : public TestNanoapp { + public: + App() + : TestNanoapp( + TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} + + bool start() override { + mRequestTimer = CHRE_TIMER_INVALID; + return true; + } + + void handleEvent(uint32_t, uint16_t eventType, + const void *eventData) override { + switch (eventType) { + case CHRE_EVENT_WIFI_ASYNC_RESULT: { + if (mRequestTimer != CHRE_TIMER_INVALID) { + chreTimerCancel(mRequestTimer); + mRequestTimer = CHRE_TIMER_INVALID; + } + + auto *event = static_cast<const chreAsyncResult *>(eventData); + if (event->success) { + if (event->errorCode == 0) { + TestEventQueueSingleton::get()->pushEvent( + CHRE_EVENT_WIFI_ASYNC_RESULT, + *(static_cast<const uint32_t *>(event->cookie))); } + } + break; + } - case CHRE_EVENT_TEST_EVENT: { - auto event = static_cast<const TestEvent *>(eventData); - switch (event->type) { - case RANGING_REQUEST: - cookie = *static_cast<uint32_t *>(event->data); - - // Placeholder parameters since linux PAL does not use this to - // generate response - struct chreWifiRangingTarget dummyRangingTarget = { - .macAddress = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, - .primaryChannel = 0xdef02468, - .centerFreqPrimary = 0xace13579, - .centerFreqSecondary = 0xbdf369cf, - .channelWidth = 0x48, - }; - - struct chreWifiRangingParams dummyRangingParams = { - .targetListLen = 1, - .targetList = &dummyRangingTarget, - }; - - bool success = - chreWifiRequestRangingAsync(&dummyRangingParams, &cookie); - TestEventQueueSingleton::get()->pushEvent(RANGING_REQUEST, - success); + case CHRE_EVENT_TIMER: { + mRequestTimer = CHRE_TIMER_INVALID; + TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT); + break; + } + + case CHRE_EVENT_TEST_EVENT: { + auto event = static_cast<const TestEvent *>(eventData); + switch (event->type) { + case RANGING_REQUEST: + bool success = false; + mCookie = *static_cast<uint32_t *>(event->data); + + // Placeholder parameters since linux PAL does not use this to + // generate response + struct chreWifiRangingTarget dummyRangingTarget = { + .macAddress = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, + .primaryChannel = 0xdef02468, + .centerFreqPrimary = 0xace13579, + .centerFreqSecondary = 0xbdf369cf, + .channelWidth = 0x48, + }; + + struct chreWifiRangingParams dummyRangingParams = { + .targetListLen = 1, + .targetList = &dummyRangingTarget, + }; + + if (chreWifiRequestRangingAsync(&dummyRangingParams, &mCookie)) { + mRequestTimer = + chreTimerSet(CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS, + nullptr, true /* oneShot */); + success = mRequestTimer != CHRE_TIMER_INVALID; } - } + TestEventQueueSingleton::get()->pushEvent(RANGING_REQUEST, + success); } - }; + } + } + } + + protected: + uint32_t mCookie; + uint32_t mRequestTimer; }; - auto app = loadNanoapp<App>(); + uint64_t appId = loadNanoapp(MakeUnique<App>()); + uint32_t timeOutCookie = 0xdead; chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::RANGING, false); - sendEventToNanoapp(app, RANGING_REQUEST, timeOutCookie); + sendEventToNanoapp(appId, RANGING_REQUEST, timeOutCookie); bool success; waitForEvent(RANGING_REQUEST, &success); EXPECT_TRUE(success); - // Add 1 second to prevent race condition - constexpr uint8_t kWifiRequestRangingTimeoutSec = - (CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS / CHRE_NSEC_PER_SEC) + 1; - std::this_thread::sleep_for( - std::chrono::seconds(kWifiRequestRangingTimeoutSec)); + waitForEvent(REQUEST_TIMED_OUT); // Make sure that we can still request ranging after a timedout request uint32_t successCookie = 0x0101; chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::RANGING, true); - sendEventToNanoapp(app, RANGING_REQUEST, successCookie); + sendEventToNanoapp(appId, RANGING_REQUEST, successCookie); waitForEvent(RANGING_REQUEST, &success); EXPECT_TRUE(success); @@ -275,7 +463,7 @@ TEST_F(WifiTimeoutTestBase, WifiRequestRangingTimeoutTest) { waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie); EXPECT_EQ(cookie, successCookie); - unloadNanoapp(app); + unloadNanoapp(appId); } } // namespace diff --git a/util/dynamic_vector_base.cc b/util/dynamic_vector_base.cc index ee7570bc..23cc1209 100644 --- a/util/dynamic_vector_base.cc +++ b/util/dynamic_vector_base.cc @@ -35,8 +35,10 @@ bool DynamicVectorBase::doReserve(size_t newCapacity, size_t elementSize) { if (!success) { void *newData = memoryAlloc(newCapacity * elementSize); if (newData != nullptr) { - memcpy(newData, mData, mSize * elementSize); - memoryFree(mData); + if (mData != nullptr) { + memcpy(newData, mData, mSize * elementSize); + memoryFree(mData); + } mData = newData; mCapacity = newCapacity; success = true; diff --git a/util/include/chre/util/array_queue_impl.h b/util/include/chre/util/array_queue_impl.h index c2982f4b..0172c0e3 100644 --- a/util/include/chre/util/array_queue_impl.h +++ b/util/include/chre/util/array_queue_impl.h @@ -132,8 +132,7 @@ void ArrayQueueCore<ElementType, StorageType>::pop() { template <typename ElementType, typename StorageType> void ArrayQueueCore<ElementType, StorageType>::pop_back() { if (mSize > 0) { - size_t absoluteIndex = relativeIndexToAbsolute(mSize - 1); - StorageType::data()[absoluteIndex].~ElementType(); + StorageType::data()[mTail].~ElementType(); pullTail(); } } diff --git a/util/include/chre/util/dynamic_vector_impl.h b/util/include/chre/util/dynamic_vector_impl.h index b3221986..4ce6e7df 100644 --- a/util/include/chre/util/dynamic_vector_impl.h +++ b/util/include/chre/util/dynamic_vector_impl.h @@ -189,9 +189,11 @@ bool DynamicVector<ElementType>::doReserve(size_type newCapacity, ElementType *newData = static_cast<ElementType *>( memoryAlloc(newCapacity * sizeof(ElementType))); if (newData != nullptr) { - uninitializedMoveOrCopy(data(), mSize, newData); - destroy(data(), mSize); - memoryFree(data()); + if (data() != nullptr) { + uninitializedMoveOrCopy(data(), mSize, newData); + destroy(data(), mSize); + memoryFree(data()); + } mData = newData; mCapacity = newCapacity; success = true; diff --git a/util/include/chre/util/intrusive_list_base.h b/util/include/chre/util/intrusive_list_base.h index 212b5243..76c89092 100644 --- a/util/include/chre/util/intrusive_list_base.h +++ b/util/include/chre/util/intrusive_list_base.h @@ -53,7 +53,7 @@ class IntrusiveListBase : public NonCopyable { IntrusiveListBase() { mSentinelNode.next = &mSentinelNode; mSentinelNode.prev = &mSentinelNode; - }; + } /** * Link a new node to the end of the linked list. diff --git a/util/include/chre/util/macros.h b/util/include/chre/util/macros.h index 4f4877cd..f6de1816 100644 --- a/util/include/chre/util/macros.h +++ b/util/include/chre/util/macros.h @@ -63,6 +63,50 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif +/** + * Obtain the number of arguments passed into a macro up till 20 args + */ +#define VA_NUM_ARGS(...) \ + VA_NUM_ARGS_IMPL(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, \ + 8, 7, 6, 5, 4, 3, 2, 1) +#define VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ + _13, _14_, _15, _16, _17, _18, _19, _20, N, ...) \ + N + +/** + * Concats two preprocessor tokens together. + * If passed in args are macros, they are resolved first, then concat'd + */ +#define MACRO_CONCAT2(x, y) x##y +#define MACRO_CONCAT(x, y) MACRO_CONCAT2(x, y) + +/** + * Get a list of types of the passed in parameters to TYPE_LIST(...) + */ +#define TYPE_LIST_1(a) decltype(a) +#define TYPE_LIST_2(a, ...) decltype(a), TYPE_LIST_1(__VA_ARGS__) +#define TYPE_LIST_3(a, ...) decltype(a), TYPE_LIST_2(__VA_ARGS__) +#define TYPE_LIST_4(a, ...) decltype(a), TYPE_LIST_3(__VA_ARGS__) +#define TYPE_LIST_5(a, ...) decltype(a), TYPE_LIST_4(__VA_ARGS__) +#define TYPE_LIST_6(a, ...) decltype(a), TYPE_LIST_5(__VA_ARGS__) +#define TYPE_LIST_7(a, ...) decltype(a), TYPE_LIST_6(__VA_ARGS__) +#define TYPE_LIST_8(a, ...) decltype(a), TYPE_LIST_7(__VA_ARGS__) +#define TYPE_LIST_9(a, ...) decltype(a), TYPE_LIST_8(__VA_ARGS__) +#define TYPE_LIST_10(a, ...) decltype(a), TYPE_LIST_9(__VA_ARGS__) +#define TYPE_LIST_11(a, ...) decltype(a), TYPE_LIST_10(__VA_ARGS__) +#define TYPE_LIST_12(a, ...) decltype(a), TYPE_LIST_11(__VA_ARGS__) +#define TYPE_LIST_13(a, ...) decltype(a), TYPE_LIST_12(__VA_ARGS__) +#define TYPE_LIST_14(a, ...) decltype(a), TYPE_LIST_13(__VA_ARGS__) +#define TYPE_LIST_15(a, ...) decltype(a), TYPE_LIST_14(__VA_ARGS__) +#define TYPE_LIST_16(a, ...) decltype(a), TYPE_LIST_15(__VA_ARGS__) +#define TYPE_LIST_17(a, ...) decltype(a), TYPE_LIST_16(__VA_ARGS__) +#define TYPE_LIST_18(a, ...) decltype(a), TYPE_LIST_17(__VA_ARGS__) +#define TYPE_LIST_19(a, ...) decltype(a), TYPE_LIST_18(__VA_ARGS__) +#define TYPE_LIST_20(a, ...) decltype(a), TYPE_LIST_19(__VA_ARGS__) + +#define TYPE_LIST(...) \ + MACRO_CONCAT(TYPE_LIST_, VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + // Compiler-specific functionality #if defined(__clang__) || defined(__GNUC__) diff --git a/util/include/chre/util/nanoapp/app_id.h b/util/include/chre/util/nanoapp/app_id.h index edc9803f..2ec40c79 100644 --- a/util/include/chre/util/nanoapp/app_id.h +++ b/util/include/chre/util/nanoapp/app_id.h @@ -50,7 +50,7 @@ constexpr uint64_t makeNanoappId(uint64_t vendor, uint32_t appNumber) { } /** - * @return App ID combining the given 3-byte app number and Google's 5-byte + * @return App ID combining the given 3-byte app number and the example 5-byte * vendor ID */ constexpr uint64_t makeExampleNanoappId(uint32_t appNumber) { @@ -67,8 +67,8 @@ constexpr uint64_t makeGoogleNanoappId(uint32_t appNumber) { // clang-format off constexpr uint64_t kHelloWorldAppId = makeExampleNanoappId(1); -constexpr uint64_t kMessageWorldAppId = makeExampleNanoappId(2); -constexpr uint64_t kTimerWorldAppId = makeExampleNanoappId(3); +constexpr uint64_t kTimerWorldAppId = makeExampleNanoappId(2); +constexpr uint64_t kMessageWorldAppId = makeExampleNanoappId(3); constexpr uint64_t kSensorWorldAppId = makeExampleNanoappId(4); constexpr uint64_t kGnssWorldAppId = makeExampleNanoappId(5); constexpr uint64_t kWifiWorldAppId = makeExampleNanoappId(6); diff --git a/util/include/chre/util/nanoapp/ble.h b/util/include/chre/util/nanoapp/ble.h index c00ce305..ba9e4443 100644 --- a/util/include/chre/util/nanoapp/ble.h +++ b/util/include/chre/util/nanoapp/ble.h @@ -87,6 +87,15 @@ bool createBleScanFilterForKnownBeacons(struct chreBleScanFilter &filter, chreBleGenericFilter *genericFilters, uint8_t numGenericFilters); +/** + * Similar to createBleScanFilterForKnownBeacons but creates a + * chreBleScanFilterV1_9 instead of a chreBleScanFilter. The + * broadcasterAddressFilters are set to empty. + */ +bool createBleScanFilterForKnownBeaconsV1_9( + struct chreBleScanFilterV1_9 &filter, chreBleGenericFilter *genericFilters, + uint8_t numGenericFilters); + } // namespace chre #endif // CHRE_UTIL_NANOAPP_BLE_H_ diff --git a/util/include/chre/util/nanoapp/math.h b/util/include/chre/util/nanoapp/math.h index 8346bb8f..bfcdb9e8 100644 --- a/util/include/chre/util/nanoapp/math.h +++ b/util/include/chre/util/nanoapp/math.h @@ -26,4 +26,7 @@ //! The constant pi in single-precision floating point format. #define CHRE_PI_F (3.14159265358979f) +//! The constant pi in double-precision floating point format. +#define CHRE_PI_D (3.1415926535897932) + #endif // CHRE_UTIL_NANOAPP_MATH_H_ diff --git a/util/include/chre/util/nanoapp/string.h b/util/include/chre/util/nanoapp/string.h new file mode 100644 index 00000000..4b277e97 --- /dev/null +++ b/util/include/chre/util/nanoapp/string.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef CHRE_UTIL_NANOAPP_STRING_H_ +#define CHRE_UTIL_NANOAPP_STRING_H_ + +#include <cstdint> + +#include "chre_api/chre.h" + +namespace chre { + +/** + * Copies a null-terminated string from source to destination up to the length + * of the source string or the destinationBufferSize - 1. Pads with null + * characters. + * + * @param destination the destination null-terminated string. + * @param source the source null-terminated string. + * @param destinationBufferSize the size of the destination buffer. + */ +void copyString(char *destination, const char *source, + size_t destinationBufferSize); + +} // namespace chre + +#endif // CHRE_UTIL_NANOAPP_STRING_H_ diff --git a/util/include/chre/util/pigweed/chre_channel_output.h b/util/include/chre/util/pigweed/chre_channel_output.h index 9f815b8f..69c7f604 100644 --- a/util/include/chre/util/pigweed/chre_channel_output.h +++ b/util/include/chre/util/pigweed/chre_channel_output.h @@ -32,47 +32,26 @@ namespace chre { */ struct ChrePigweedNanoappMessage { size_t msgSize; - void *msg; -}; - -/** - * ChannelOutput that can be used for nanoapps wishing to utilize - * pw::rpc::Server and pw::rpc::Client for RPC communication between other - * nanoapps and Android app host clients. - */ -class ChreChannelOutputBase : public pw::rpc::ChannelOutput { - public: - // Random value chosen that matches Java client util, but is random enough - // to not conflict with other CHRE messages the nanoapp and client may send. - static constexpr uint32_t PW_RPC_CHRE_HOST_MESSAGE_TYPE = INT32_MAX - 10; - - // Random values chosen to be towards the end of the nanoapp event type region - // so it doesn't conflict with existing nanoapp messages that can be sent. - static constexpr uint16_t PW_RPC_CHRE_NAPP_REQUEST_EVENT_TYPE = - UINT16_MAX - 10; - static constexpr uint16_t PW_RPC_CHRE_NAPP_RESPONSE_EVENT_TYPE = - UINT16_MAX - 9; - - size_t MaximumTransmissionUnit() override; - - protected: - ChreChannelOutputBase(); + uint8_t msg[]; }; /** * Channel output that must be used on the server side of the channel between * two nanoapps. */ -class ChreServerNanoappChannelOutput : public ChreChannelOutputBase { +class ChreServerNanoappChannelOutput : public pw::rpc::ChannelOutput { public: explicit ChreServerNanoappChannelOutput(RpcPermission &permission) - : mPermission(permission) {} + : ChannelOutput("CHRE"), mPermission(permission) {} + /** * Sets the nanoapp instance ID that is being communicated with over this * channel output. */ void setClient(uint32_t nanoappInstanceId); + size_t MaximumTransmissionUnit() override; + pw::Status Send(pw::span<const std::byte> buffer) override; private: @@ -84,8 +63,10 @@ class ChreServerNanoappChannelOutput : public ChreChannelOutputBase { * Channel output that must be used on the client side of the channel between * two nanoapps. */ -class ChreClientNanoappChannelOutput : public ChreChannelOutputBase { +class ChreClientNanoappChannelOutput : public pw::rpc::ChannelOutput { public: + ChreClientNanoappChannelOutput() : ChannelOutput("CHRE") {} + /** * Sets the server instance ID. * @@ -95,6 +76,8 @@ class ChreClientNanoappChannelOutput : public ChreChannelOutputBase { */ void setServer(uint32_t instanceId); + size_t MaximumTransmissionUnit() override; + pw::Status Send(pw::span<const std::byte> buffer) override; private: @@ -105,15 +88,18 @@ class ChreClientNanoappChannelOutput : public ChreChannelOutputBase { * Channel output that must be used if the channel is between a nanoapp and * host client. */ -class ChreServerHostChannelOutput : public ChreChannelOutputBase { +class ChreServerHostChannelOutput : public pw::rpc::ChannelOutput { public: explicit ChreServerHostChannelOutput(RpcPermission &permission) - : mPermission(permission) {} + : ChannelOutput("CHRE"), mPermission(permission) {} + /** * Sets the host endpoint being communicated with. */ void setHostEndpoint(uint16_t hostEndpoint); + size_t MaximumTransmissionUnit() override; + pw::Status Send(pw::span<const std::byte> buffer) override; private: diff --git a/util/include/chre/util/pigweed/rpc_client.h b/util/include/chre/util/pigweed/rpc_client.h index 17756dc8..4920b648 100644 --- a/util/include/chre/util/pigweed/rpc_client.h +++ b/util/include/chre/util/pigweed/rpc_client.h @@ -46,15 +46,11 @@ class RpcClient : public NonCopyable { explicit RpcClient(uint64_t serverNanoappId) : mServerNanoappId((serverNanoappId)) {} - ~RpcClient() { - chreConfigureNanoappInfoEvents(false); - } - /** * Handles events related to RPC services. * * Handles the following events: - * - PW_RPC_CHRE_NAPP_RESPONSE_EVENT_TYPE: handle the server responses, + * - CHRE_EVENT_RPC_RESPONSE: handle the server responses, * - CHRE_EVENT_NANOAPP_STOPPED: close the channel when the server nanoapp * terminates. * @@ -87,12 +83,17 @@ class RpcClient : public NonCopyable { */ bool hasService(uint64_t id, uint32_t version); + /** + * Must be called from nanoapp end. + */ + void close(); + private: /** * Handles responses from the server. * - * This method must be called when nanoapps receive a - * PW_RPC_CHRE_NAPP_RESPONSE_EVENT_TYPE event. + * This method must be called when nanoapps receive a CHRE_EVENT_RPC_RESPONSE + * event. * * @param senderInstanceId The Instance ID for the source of this event. * @param eventData The associated data, if any. diff --git a/platform/shared/tracing.cc b/util/include/chre/util/pigweed/rpc_common.h index 876fe199..ee9096db 100644 --- a/platform/shared/tracing.cc +++ b/util/include/chre/util/pigweed/rpc_common.h @@ -14,23 +14,26 @@ * limitations under the License. */ -#include "chre/platform/tracing.h" -#include "chre/util/macros.h" +#ifndef CHRE_UTIL_PIGWEED_RPC_COMMON_H_ +#define CHRE_UTIL_PIGWEED_RPC_COMMON_H_ -namespace chre { +#include <cstdint> -void traceRegisterNanoapp(uint16_t instanceId, const char *name) { - UNUSED_VAR(instanceId); - UNUSED_VAR(name); -} +/** + * @file + * Common definitions across nanoapp and host sides. + */ -void traceNanoappHandleEventStart(uint16_t instanceId, uint16_t eventType) { - UNUSED_VAR(instanceId); - UNUSED_VAR(eventType); -} +namespace chre { +/** The upper 16b of a channel ID are set to 1 for host clients. */ +constexpr uint32_t kChannelIdHostClient = 1 << 16; -void traceNanoappHandleEventEnd(uint16_t instanceId) { - UNUSED_VAR(instanceId); -} +/** Mask to extract the host ID / nanoapp ID from a channel ID. */ +constexpr uint32_t kRpcClientIdMask = 0xffff; + +/** Maximum ID for a nanoapp as the value is encoded on 16b. */ +constexpr uint32_t kRpcNanoappMaxId = 0xffff; } // namespace chre + +#endif // CHRE_UTIL_PIGWEED_RPC_COMMON_H_
\ No newline at end of file diff --git a/util/include/chre/util/pigweed/rpc_helper.h b/util/include/chre/util/pigweed/rpc_helper.h index 032693a1..08bd2bf6 100644 --- a/util/include/chre/util/pigweed/rpc_helper.h +++ b/util/include/chre/util/pigweed/rpc_helper.h @@ -19,19 +19,11 @@ #include <cstdint> +#include "chre/util/pigweed/rpc_common.h" #include "chre_api/chre.h" namespace chre { -/** The upper 16b of a channel ID are set to 1 for host clients. */ -constexpr uint32_t kChannelIdHostClient = 1 << 16; - -/** Mask to extract the host ID / nanoapp ID from a channel ID. */ -constexpr uint32_t kRpcClientIdMask = 0xffff; - -/** Maximum ID for a nanoapp as the value is encoded on 16b. */ -constexpr uint32_t kRpcNanoappMaxId = 0xffff; - /** * Returns whether the endpoint matches. * diff --git a/util/include/chre/util/pigweed/rpc_server.h b/util/include/chre/util/pigweed/rpc_server.h index df02621e..ec0f86d6 100644 --- a/util/include/chre/util/pigweed/rpc_server.h +++ b/util/include/chre/util/pigweed/rpc_server.h @@ -58,7 +58,6 @@ class RpcServer : public NonCopyable { }; RpcServer() : mHostOutput(mPermission), mNanoappOutput(mPermission) {} - ~RpcServer(); /** * Registers services to the server and to CHRE. @@ -101,7 +100,7 @@ class RpcServer : public NonCopyable { * * Handles the following events: * - CHRE_EVENT_MESSAGE_FROM_HOST: respond to host RPC requests, - * - PW_RPC_CHRE_NAPP_REQUEST_EVENT_TYPE: respond to nanoapp RPC requests, + * - CHRE_EVENT_RPC_REQUEST: respond to nanoapp RPC requests, * - CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: close the channel when the host * terminates, * - CHRE_EVENT_NANOAPP_STOPPED: close the channel when a nanoapp @@ -116,6 +115,13 @@ class RpcServer : public NonCopyable { bool handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData); + /** + * Close all connections to the server. + * + * Must be called from the nanoapp end. + */ + void close(); + private: /** * Handles messages from host clients. @@ -131,8 +137,8 @@ class RpcServer : public NonCopyable { /** * Handles messages from nanoapp clients. * - * This method must be called when nanoapps receive a - * PW_RPC_CHRE_NAPP_REQUEST_EVENT_TYPE event. + * This method must be called when nanoapps receive a CHRE_EVENT_RPC_REQUEST + * event. * * @param eventData The associated data, if any. * @return whether the RPC was handled successfully. diff --git a/util/include/chre/util/segmented_queue.h b/util/include/chre/util/segmented_queue.h index d3d7fb5c..025d2f02 100644 --- a/util/include/chre/util/segmented_queue.h +++ b/util/include/chre/util/segmented_queue.h @@ -92,7 +92,7 @@ class SegmentedQueue : public NonCopyable { */ bool empty() const { return mSize == 0; - }; + } /** * Push a element to the end of the segmented queue. @@ -403,4 +403,4 @@ class SegmentedQueue : public NonCopyable { #include "chre/util/segmented_queue_impl.h" -#endif // CHRE_UTIL_SEGMENTED_QUEUE_H_
\ No newline at end of file +#endif // CHRE_UTIL_SEGMENTED_QUEUE_H_ diff --git a/util/include/chre/util/segmented_queue_impl.h b/util/include/chre/util/segmented_queue_impl.h index 8bd16457..049ab615 100644 --- a/util/include/chre/util/segmented_queue_impl.h +++ b/util/include/chre/util/segmented_queue_impl.h @@ -20,9 +20,8 @@ #include <type_traits> #include <utility> -#include "chre/util/segmented_queue.h" - #include "chre/util/container_support.h" +#include "chre/util/segmented_queue.h" namespace chre { @@ -205,11 +204,29 @@ void SegmentedQueue<ElementType, kBlockSize>::fillGaps( return; } - // TODO(b/264326627): Check if gapIndices is reverse order. - // TODO(b/264326627): Give a detailed explanation (example)\. // Move the elements between each gap indices section by section from the // section that is closest to the head. The destination index = the gap index // - how many gaps has been filled. + // + // For instance, assuming we have elements that we want to remove (gaps) at + // these indices = [8, 7, 5, 2] and the last element is at index 10. + // + // The first iteration will move the items at index 3, 4, which is the first + // section, to index 2, 3 and overwrite the original item at index 2, making + // the queue: [0, 1, 3, 4, x, 5, 6, ...., 10] where x means empty slot. + // + // The second iteration will do a similar thing, move item 6 to the empty + // slot, which could be calculated by using the index of the last gap and how + // many gaps has been filled. So the queue turns into: + // [0, 1, 3, 4, 6, x, x, 7, 8, 9, 10], note that there are now two empty slots + // since there are two gaps filled. + // + // The third iteration does not move anything since there are no items between + // 7 and 8. + // + // The final iteration is a special case to close the final gap. After the + // final iteration, the queue will become: [1, 3, 4, 6, 9, 10]. + for (size_t i = gapCount - 1; i > 0; --i) { moveElements(advanceOrWrapAround(gapIndices[i]), subtractOrWrapAround(gapIndices[i], gapCount - 1 - i), diff --git a/util/include/chre/util/synchronized_expandable_memory_pool_impl.h b/util/include/chre/util/synchronized_expandable_memory_pool_impl.h index 8b58ce78..8e20913f 100644 --- a/util/include/chre/util/synchronized_expandable_memory_pool_impl.h +++ b/util/include/chre/util/synchronized_expandable_memory_pool_impl.h @@ -17,6 +17,8 @@ #ifndef CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_ #define CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_ +#include <algorithm> + #include "chre/util/lock_guard.h" #include "chre/util/memory_pool.h" #include "chre/util/synchronized_expandable_memory_pool.h" diff --git a/util/include/chre/util/synchronized_memory_pool.h b/util/include/chre/util/synchronized_memory_pool.h index bce38863..2c6245b1 100644 --- a/util/include/chre/util/synchronized_memory_pool.h +++ b/util/include/chre/util/synchronized_memory_pool.h @@ -38,7 +38,7 @@ class SynchronizedMemoryPool : public NonCopyable { * fails. */ template <typename... Args> - ElementType *allocate(Args &&... args); + ElementType *allocate(Args &&...args); /** * Releases the memory of a previously allocated element. The pointer provided diff --git a/util/include/chre/util/synchronized_memory_pool_impl.h b/util/include/chre/util/synchronized_memory_pool_impl.h index 9d117187..526c3f92 100644 --- a/util/include/chre/util/synchronized_memory_pool_impl.h +++ b/util/include/chre/util/synchronized_memory_pool_impl.h @@ -25,7 +25,7 @@ namespace chre { template <typename ElementType, size_t kSize> template <typename... Args> ElementType *SynchronizedMemoryPool<ElementType, kSize>::allocate( - Args &&... args) { + Args &&...args) { LockGuard<Mutex> lock(mMutex); return mMemoryPool.allocate(args...); } diff --git a/util/include/chre/util/system/ble_util.h b/util/include/chre/util/system/ble_util.h new file mode 100644 index 00000000..50720080 --- /dev/null +++ b/util/include/chre/util/system/ble_util.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef CHRE_UTIL_SYSTEM_BLE_UTIL_H_ +#define CHRE_UTIL_SYSTEM_BLE_UTIL_H_ + +#include "chre_api/chre.h" + +namespace chre { + +/** + * Populates a legacy chreBleAdvertisingReport's fields with values from the + * payload. The chreBleAdvertisingReport is based on the LE Extended Advertising + * Report Event defined in the BT Core Spec v5.3, Vol 4, Part E, + * Section 7.7.65.13. But for legacy LE Advertising Report Events (BT Core Spec + * v5.3, Vol 4, Part E, Section 7.7.65.2), some of these fields are only + * included in the payload. We parse out these fields to make it easier for the + * nanoapp to access this data. + * + * @param report CHRE BLE Advertising Report + */ +void populateLegacyAdvertisingReportFields(chreBleAdvertisingReport &report); + +} // namespace chre + +#endif // CHRE_UTIL_SYSTEM_BLE_UTIL_H_ diff --git a/util/include/chre/util/system/stats_container.h b/util/include/chre/util/system/stats_container.h index d2e18d4d..4a5e4041 100644 --- a/util/include/chre/util/system/stats_container.h +++ b/util/include/chre/util/system/stats_container.h @@ -41,7 +41,7 @@ class StatsContainer { * it should not be bigger than the default value to prevent rounding to 0 */ StatsContainer(uint32_t averageWindow_ = 512) - : mAverageWindow(averageWindow_){}; + : mAverageWindow(averageWindow_) {} /** * Add a new value to the metric collection and update mean/max value @@ -68,14 +68,14 @@ class StatsContainer { */ T getMean() const { return mMean; - }; + } /** * @return the max value */ T getMax() const { return mMax; - }; + } /** * @return the average window @@ -97,4 +97,4 @@ class StatsContainer { } // namespace chre -#endif // CHRE_UTIL_SYSTEM_STATS_CONTAINER_H_
\ No newline at end of file +#endif // CHRE_UTIL_SYSTEM_STATS_CONTAINER_H_ diff --git a/util/include/chre/util/time.h b/util/include/chre/util/time.h index b3fef77e..a0e210ba 100644 --- a/util/include/chre/util/time.h +++ b/util/include/chre/util/time.h @@ -21,6 +21,9 @@ namespace chre { +//! The number of seconds in one day. +constexpr uint64_t kOneDayInSeconds(60 * 60 * 24); + //! The number of milliseconds in one min. constexpr uint64_t kOneMinuteInMilliseconds(60000); diff --git a/util/nanoapp/ble.cc b/util/nanoapp/ble.cc index 09de5c43..50e0fb79 100644 --- a/util/nanoapp/ble.cc +++ b/util/nanoapp/ble.cc @@ -57,4 +57,28 @@ bool createBleScanFilterForKnownBeacons(struct chreBleScanFilter &filter, return true; } +bool createBleScanFilterForKnownBeaconsV1_9( + struct chreBleScanFilterV1_9 &filter, chreBleGenericFilter *genericFilters, + uint8_t numGenericFilters) { + if (numGenericFilters < kNumScanFilters) { + return false; + } + memset(&filter, 0, sizeof(filter)); + + genericFilters[0] = createBleGenericFilter( + CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE, kNumScanFilters, + kGoogleEddystoneUuid, kGoogleUuidMask); + genericFilters[1] = createBleGenericFilter( + CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE, kNumScanFilters, + kGoogleNearbyFastpairUuid, kGoogleUuidMask); + + filter.rssiThreshold = kRssiThreshold; + filter.genericFilterCount = kNumScanFilters; + filter.genericFilters = genericFilters; + + filter.broadcasterAddressFilterCount = 0; + filter.broadcasterAddressFilters = nullptr; + return true; +} + } // namespace chre diff --git a/util/nanoapp/string.cc b/util/nanoapp/string.cc new file mode 100644 index 00000000..3d160ad8 --- /dev/null +++ b/util/nanoapp/string.cc @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#include "chre/util/nanoapp/string.h" +#include "chre/util/nanoapp/assert.h" + +namespace chre { + +void copyString(char *destination, const char *source, + size_t destinationBufferSize) { + CHRE_ASSERT_NOT_NULL(destination); + CHRE_ASSERT_NOT_NULL(source); + + if (destinationBufferSize == 0) { + return; + } + + uint32_t i; + for (i = 0; i < destinationBufferSize - 1 && source[i] != '\0'; ++i) { + destination[i] = source[i]; + } + + memset(&destination[i], 0, destinationBufferSize - i); +} + +} // namespace chre diff --git a/util/pigweed/chre_channel_output.cc b/util/pigweed/chre_channel_output.cc index f88e5203..9fdce478 100644 --- a/util/pigweed/chre_channel_output.cc +++ b/util/pigweed/chre_channel_output.cc @@ -50,7 +50,6 @@ pw::Status sendToNanoapp(uint32_t targetInstanceId, uint16_t eventType, } data->msgSize = buffer.size(); - data->msg = &data[1]; memcpy(data->msg, buffer.data(), buffer.size()); if (!chreSendEvent(eventType, data, nappMessageFreeCb, targetInstanceId)) { @@ -63,12 +62,6 @@ pw::Status sendToNanoapp(uint32_t targetInstanceId, uint16_t eventType, } // namespace -ChreChannelOutputBase::ChreChannelOutputBase() : ChannelOutput("CHRE") {} - -size_t ChreChannelOutputBase::MaximumTransmissionUnit() { - return CHRE_MESSAGE_TO_HOST_MAX_SIZE - sizeof(ChrePigweedNanoappMessage); -} - void ChreServerNanoappChannelOutput::setClient(uint32_t nanoappInstanceId) { CHRE_ASSERT(nanoappInstanceId <= kRpcNanoappMaxId); if (nanoappInstanceId <= kRpcNanoappMaxId) { @@ -78,14 +71,17 @@ void ChreServerNanoappChannelOutput::setClient(uint32_t nanoappInstanceId) { } } +size_t ChreServerNanoappChannelOutput::MaximumTransmissionUnit() { + return CHRE_MESSAGE_TO_HOST_MAX_SIZE - sizeof(ChrePigweedNanoappMessage); +} + pw::Status ChreServerNanoappChannelOutput::Send( pw::span<const std::byte> buffer) { // The permission is not enforced across nanoapps but we still need to // reset the value as it is only applicable to the next message. mPermission.getAndReset(); - return sendToNanoapp(mClientInstanceId, PW_RPC_CHRE_NAPP_RESPONSE_EVENT_TYPE, - buffer); + return sendToNanoapp(mClientInstanceId, CHRE_EVENT_RPC_RESPONSE, buffer); } void ChreClientNanoappChannelOutput::setServer(uint32_t instanceId) { @@ -97,16 +93,23 @@ void ChreClientNanoappChannelOutput::setServer(uint32_t instanceId) { } } +size_t ChreClientNanoappChannelOutput::MaximumTransmissionUnit() { + return CHRE_MESSAGE_TO_HOST_MAX_SIZE - sizeof(ChrePigweedNanoappMessage); +} + pw::Status ChreClientNanoappChannelOutput::Send( pw::span<const std::byte> buffer) { - return sendToNanoapp(mServerInstanceId, PW_RPC_CHRE_NAPP_REQUEST_EVENT_TYPE, - buffer); + return sendToNanoapp(mServerInstanceId, CHRE_EVENT_RPC_REQUEST, buffer); } void ChreServerHostChannelOutput::setHostEndpoint(uint16_t hostEndpoint) { mEndpointId = hostEndpoint; } +size_t ChreServerHostChannelOutput::MaximumTransmissionUnit() { + return CHRE_MESSAGE_TO_HOST_MAX_SIZE - sizeof(ChrePigweedNanoappMessage); +} + pw::Status ChreServerHostChannelOutput::Send(pw::span<const std::byte> buffer) { CHRE_ASSERT(mEndpointId != CHRE_HOST_ENDPOINT_UNSPECIFIED); pw::Status returnCode = PW_STATUS_OK; @@ -119,7 +122,7 @@ pw::Status ChreServerHostChannelOutput::Send(pw::span<const std::byte> buffer) { } else { memcpy(data, buffer.data(), buffer.size()); if (!chreSendMessageWithPermissions( - data, buffer.size(), PW_RPC_CHRE_HOST_MESSAGE_TYPE, mEndpointId, + data, buffer.size(), CHRE_MESSAGE_TYPE_RPC, mEndpointId, permission, heapFreeMessageCallback)) { returnCode = PW_STATUS_INVALID_ARGUMENT; } diff --git a/util/pigweed/rpc_client.cc b/util/pigweed/rpc_client.cc index 72c034a5..6cf84626 100644 --- a/util/pigweed/rpc_client.cc +++ b/util/pigweed/rpc_client.cc @@ -33,7 +33,7 @@ namespace chre { bool RpcClient::handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData) { switch (eventType) { - case chre::ChreChannelOutputBase::PW_RPC_CHRE_NAPP_RESPONSE_EVENT_TYPE: + case CHRE_EVENT_RPC_RESPONSE: return handleMessageFromServer(senderInstanceId, eventData); case CHRE_EVENT_NANOAPP_STOPPED: handleNanoappStopped(eventData); @@ -58,10 +58,15 @@ bool RpcClient::hasService(uint64_t id, uint32_t version) { return false; } +void RpcClient::close() { + chreConfigureNanoappInfoEvents(false); +} + bool RpcClient::handleMessageFromServer(uint32_t senderInstanceId, const void *eventData) { auto data = static_cast<const chre::ChrePigweedNanoappMessage *>(eventData); - pw::span packet(static_cast<const std::byte *>(data->msg), data->msgSize); + pw::span packet(reinterpret_cast<const std::byte *>(data->msg), + data->msgSize); struct chreNanoappInfo info; if (!chreGetNanoappInfoByAppId(mServerNanoappId, &info) || diff --git a/util/pigweed/rpc_server.cc b/util/pigweed/rpc_server.cc index 1ee225a9..0bf7d671 100644 --- a/util/pigweed/rpc_server.cc +++ b/util/pigweed/rpc_server.cc @@ -29,15 +29,6 @@ namespace chre { -RpcServer::~RpcServer() { - chreConfigureNanoappInfoEvents(false); - // TODO(b/251257328): Disable all notifications at once. - while (!mConnectedHosts.empty()) { - chreConfigureHostEndpointNotifications(mConnectedHosts[0], false); - mConnectedHosts.erase(0); - } -} - bool RpcServer::registerServices(size_t numServices, RpcServer::Service *services) { // Avoid blowing up the stack with chreServices. @@ -77,7 +68,7 @@ bool RpcServer::handleEvent(uint32_t senderInstanceId, uint16_t eventType, switch (eventType) { case CHRE_EVENT_MESSAGE_FROM_HOST: return handleMessageFromHost(eventData); - case ChreChannelOutputBase::PW_RPC_CHRE_NAPP_REQUEST_EVENT_TYPE: + case CHRE_EVENT_RPC_REQUEST: return handleMessageFromNanoapp(senderInstanceId, eventData); case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: handleHostClientNotification(eventData); @@ -90,11 +81,19 @@ bool RpcServer::handleEvent(uint32_t senderInstanceId, uint16_t eventType, } } +void RpcServer::close() { + chreConfigureNanoappInfoEvents(false); + // TODO(b/251257328): Disable all notifications at once. + while (!mConnectedHosts.empty()) { + chreConfigureHostEndpointNotifications(mConnectedHosts[0], false); + mConnectedHosts.erase(0); + } +} + bool RpcServer::handleMessageFromHost(const void *eventData) { auto *hostMessage = static_cast<const chreMessageFromHostData *>(eventData); - if (hostMessage->messageType != - ChreChannelOutputBase::PW_RPC_CHRE_HOST_MESSAGE_TYPE) { + if (hostMessage->messageType != CHRE_MESSAGE_TYPE_RPC) { return false; } @@ -138,7 +137,8 @@ bool RpcServer::handleMessageFromHost(const void *eventData) { bool RpcServer::handleMessageFromNanoapp(uint32_t senderInstanceId, const void *eventData) { const auto data = static_cast<const ChrePigweedNanoappMessage *>(eventData); - pw::span packet(static_cast<const std::byte *>(data->msg), data->msgSize); + pw::span packet(reinterpret_cast<const std::byte *>(data->msg), + data->msgSize); pw::Result<uint32_t> result = pw::rpc::ExtractChannelId(packet); if (result.status() != PW_STATUS_OK) { diff --git a/util/system/ble_util.cc b/util/system/ble_util.cc new file mode 100644 index 00000000..eb614260 --- /dev/null +++ b/util/system/ble_util.cc @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#include "chre/util/system/ble_util.h" + +#include <cinttypes> + +#include "chre/platform/log.h" + +namespace chre { + +namespace { + +// Tx Power Level AD Type defined in the BT Core Spec v5.3 Assigned Numbers, +// Generic Access Profile (ref: +// https://www.bluetooth.com/specifications/assigned-numbers/). +constexpr uint8_t kTxPowerLevelAdType = 0x0A; +constexpr uint8_t kAdTypeOffset = 1; +constexpr uint8_t kExpectedAdDataLength = 2; + +/** + * Gets the TX Power from the data field of legacy AD reports. This function + * parses the advertising data which is defined in the BT Core Spec v5.3, Vol 3, + * Part C, Section 11, Advertising and Scan Response Data Format, and checks for + * the presence of the Tx Power Level AD Type. + * + * @param data Advertising data. + * @param dataLength Length of advertising data. + * @return int8_t Tx Power value. + */ +int8_t getTxPowerFromLegacyReport(const uint8_t *data, size_t dataLength) { + size_t i = 0; + while (i < dataLength) { + uint8_t adDataLength = data[i]; + if (adDataLength == 0 || (adDataLength >= dataLength - i)) { + break; + } + if (data[i + kAdTypeOffset] == kTxPowerLevelAdType && + adDataLength == kExpectedAdDataLength) { + return static_cast<int8_t>(data[i + kExpectedAdDataLength]); + } + i += kAdTypeOffset + adDataLength; + } + return CHRE_BLE_TX_POWER_NONE; +} + +} // namespace + +void populateLegacyAdvertisingReportFields(chreBleAdvertisingReport &report) { + if ((report.eventTypeAndDataStatus & CHRE_BLE_EVENT_TYPE_FLAG_LEGACY) != 0 && + report.txPower == CHRE_BLE_TX_POWER_NONE) { + report.txPower = getTxPowerFromLegacyReport(report.data, report.dataLength); + } +} + +} // namespace chre diff --git a/util/tests/array_queue_test.cc b/util/tests/array_queue_test.cc index fbacaedf..d1e0d6de 100644 --- a/util/tests/array_queue_test.cc +++ b/util/tests/array_queue_test.cc @@ -103,6 +103,15 @@ TEST(ArrayQueueTest, SimplePushPopBackPush) { EXPECT_EQ(5, q[0]); EXPECT_EQ(6, q[1]); EXPECT_EQ(7, q[2]); + + q.pop_back(); + + EXPECT_EQ(5, q[0]); + EXPECT_EQ(6, q[1]); + + q.pop(); + + EXPECT_EQ(6, q[0]); } TEST(ArrayQueueTest, TestSize) { @@ -192,12 +201,19 @@ TEST(ArrayQueueTest, TestFront) { TEST(ArrayQueueTest, TestBack) { ArrayQueue<int, 3> q; q.push(1); - EXPECT_EQ(1, q.back()); - q.pop(); + EXPECT_EQ(1, q.back()); // 1 x x q.push(2); - EXPECT_EQ(2, q.back()); + EXPECT_EQ(2, q.back()); // 1 2 x + q.pop(); + EXPECT_EQ(2, q.back()); // x 2 x q.push(3); - EXPECT_EQ(3, q.back()); + EXPECT_EQ(3, q.back()); // x 2 3 + q.push(4); + EXPECT_EQ(4, q.back()); // 4 2 3 (forward wrap-around) + q.pop_back(); + EXPECT_EQ(3, q.back()); // x 2 3 (backwards wrap-around) + q.pop(); + EXPECT_EQ(3, q.back()); // x x 3 } TEST(ArrayQueueDeathTest, InvalidSubscript) { diff --git a/util/tests/ble_util_test.cc b/util/tests/ble_util_test.cc new file mode 100644 index 00000000..762a6aa7 --- /dev/null +++ b/util/tests/ble_util_test.cc @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#include "gtest/gtest.h" + +#include <string.h> + +#include "chre/util/system/ble_util.h" + +TEST(BleUtil, PopulateTxPower) { + chreBleAdvertisingReport report; + memset(&report, 0, sizeof(report)); + report.txPower = CHRE_BLE_TX_POWER_NONE; + report.eventTypeAndDataStatus = CHRE_BLE_EVENT_TYPE_FLAG_LEGACY; + int8_t txPower = -11; + uint8_t data[3] = {0x02, 0x0A, static_cast<uint8_t>(txPower)}; + report.data = data; + report.dataLength = 3; + chre::populateLegacyAdvertisingReportFields(report); + EXPECT_EQ(report.txPower, txPower); +} diff --git a/util/tests/string_test.cc b/util/tests/string_test.cc new file mode 100644 index 00000000..74c2df71 --- /dev/null +++ b/util/tests/string_test.cc @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#include "gtest/gtest.h" + +#include "chre/util/nanoapp/string.h" + +using ::chre::copyString; + +TEST(StringDeathTest, InvalidInput) { + char destination[100]; + ASSERT_DEATH(copyString(nullptr, nullptr, 0), ".*"); + ASSERT_DEATH(copyString(nullptr, destination, 1), ".*"); + ASSERT_DEATH(copyString(destination, nullptr, 2), ".*"); +} + +TEST(String, ZeroCharsToCopy) { + const char *source = "hello world"; + constexpr size_t destinationLength = 100; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, 0); + for (size_t i = 0; i < destinationLength; ++i) { + ASSERT_EQ(destination[i], fillValue); + } +} + +TEST(String, EmptyStringPadsWithZeroes) { + const char *source = ""; + constexpr size_t destinationLength = 100; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, destinationLength); + for (size_t i = 0; i < destinationLength; ++i) { + ASSERT_EQ(destination[i], 0); + } +} + +TEST(String, NormalCopyOneChar) { + const char *source = "hello world"; + constexpr size_t destinationLength = 100; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, 2); // one char + '\0' + ASSERT_EQ(destination[0], source[0]); + ASSERT_EQ(destination[1], 0); + for (size_t i = 2; i < destinationLength; ++i) { + ASSERT_EQ(destination[i], fillValue); + } +} + +TEST(String, NormalCopyAllChars) { + const char *source = "hello world"; + constexpr size_t sourceLength = 11; + constexpr size_t destinationLength = 100; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, sourceLength + 1); // account for '\0' + size_t i = 0; + for (; i < sourceLength; ++i) { + ASSERT_EQ(destination[i], source[i]); + } + + ASSERT_EQ(destination[i], 0); + ++i; + + for (; i < destinationLength; ++i) { + ASSERT_EQ(destination[i], fillValue); + } +} + +TEST(String, NormalCopyGreaterThanSourceLength) { + const char *source = "hello world"; + constexpr size_t sourceLength = 11; + constexpr size_t destinationLength = 100; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, destinationLength); + size_t i = 0; + for (; i < sourceLength; ++i) { + ASSERT_EQ(destination[i], source[i]); + } + + for (; i < destinationLength; ++i) { + ASSERT_EQ(destination[i], 0); + } +} + +TEST(String, NormalCopyLessThanSourceLength) { + const char *source = "hello world"; + constexpr size_t sourceLength = 11; + constexpr size_t destinationLength = 5; + char destination[destinationLength]; + char fillValue = 123; + + memset(destination, fillValue, destinationLength); + + copyString(destination, source, destinationLength); + size_t i = 0; + for (; i < destinationLength - 1; ++i) { + ASSERT_EQ(destination[i], source[i]); + } + ASSERT_EQ(destination[i], 0); +} diff --git a/util/tests/synchronized_expandable_memory_pool_test.cc b/util/tests/synchronized_expandable_memory_pool_test.cc index 3b88e598..f5957a9e 100644 --- a/util/tests/synchronized_expandable_memory_pool_test.cc +++ b/util/tests/synchronized_expandable_memory_pool_test.cc @@ -16,7 +16,6 @@ #include "chre/util/synchronized_expandable_memory_pool.h" -#include "chre/util/synchronized_memory_pool.h" #include "gtest/gtest.h" using chre::SynchronizedExpandableMemoryPool; @@ -102,4 +101,4 @@ TEST(SynchronizedExpandAbleMemoryPool, HysteresisDeallocation) { // Once it is empty, it should not still hold maxBlockCount as before. EXPECT_EQ(testMemoryPool.getFreeSpaceCount(), blockSize * maxBlockCount); EXPECT_EQ(testMemoryPool.getBlockCount(), staticBlockCount); -}
\ No newline at end of file +} diff --git a/util/tests/synchronized_memory_pool_test.cc b/util/tests/synchronized_memory_pool_test.cc new file mode 100644 index 00000000..cecff880 --- /dev/null +++ b/util/tests/synchronized_memory_pool_test.cc @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include "chre/util/synchronized_memory_pool.h" + +#include "gtest/gtest.h" + +using chre::SynchronizedMemoryPool; + +namespace { + +class ConstructorCount { + public: + ConstructorCount(int value_) : value(value_) { + sConstructedCounter++; + } + ~ConstructorCount() { + sConstructedCounter--; + } + int getValue() { + return value; + } + + static ssize_t sConstructedCounter; + + private: + const int value; +}; + +ssize_t ConstructorCount::sConstructedCounter = 0; + +} // namespace + +TEST(SynchronizedMemoryPool, FreeBlockCheck) { + constexpr uint8_t maxSize = 12; + constexpr uint8_t blankSpace = 2; + + SynchronizedMemoryPool<int, maxSize> testMemoryPool; + int *tempDataPtrs[maxSize]; + + for (int i = 0; i < maxSize - blankSpace; ++i) { + tempDataPtrs[i] = testMemoryPool.allocate(i); + } + + EXPECT_EQ(testMemoryPool.getFreeBlockCount(), blankSpace); + + for (int i = 0; i < maxSize - blankSpace; ++i) { + testMemoryPool.deallocate(tempDataPtrs[i]); + } +} diff --git a/util/util.mk b/util/util.mk index 7c6337e8..1a7ae3e8 100644 --- a/util/util.mk +++ b/util/util.mk @@ -16,7 +16,9 @@ COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/audio.cc COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/ble.cc COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/callbacks.cc COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/debug.cc +COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/string.cc COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/wifi.cc +COMMON_SRCS += $(CHRE_PREFIX)/util/system/ble_util.cc COMMON_SRCS += $(CHRE_PREFIX)/util/system/event_callbacks.cc COMMON_SRCS += $(CHRE_PREFIX)/util/system/debug_dump.cc @@ -43,6 +45,7 @@ GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/shared_ptr_test.cc GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/singleton_test.cc GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/stats_container_test.cc GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/synchronized_expandable_memory_pool_test.cc +GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/synchronized_memory_pool_test.cc GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/time_test.cc GOOGLETEST_SRCS += $(CHRE_PREFIX)/util/tests/unique_ptr_test.cc diff --git a/variant/exynos-embos/variant.mk b/variant/exynos-embos/variant.mk index 03868f49..a35e3688 100644 --- a/variant/exynos-embos/variant.mk +++ b/variant/exynos-embos/variant.mk @@ -27,9 +27,6 @@ COMMON_CFLAGS += -DCHRE_VARIANT_SUPPLIES_STATIC_NANOAPP_LIST EMBOS_CFLAGS += -DCHRE_EVENT_PER_BLOCK=32 EMBOS_CFLAGS += -DCHRE_MAX_EVENT_BLOCKS=4 -EMBOS_CFLAGS += -DCHRE_UNSCHEDULED_EVENT_PER_BLOCK=32 -EMBOS_CFLAGS += -DCHRE_MAX_UNSCHEDULED_EVENT_BLOCKS=4 - # Optional Features ############################################################ CHRE_AUDIO_SUPPORT_ENABLED = true diff --git a/variant/tinysys/variant.mk b/variant/tinysys/variant.mk index 15cf183b..69ab3f9c 100644 --- a/variant/tinysys/variant.mk +++ b/variant/tinysys/variant.mk @@ -60,9 +60,6 @@ COMMON_CFLAGS += -DCHRE_VARIANT_SUPPLIES_STATIC_NANOAPP_LIST TINYSYS_CFLAGS += -DCHRE_EVENT_PER_BLOCK=32 TINYSYS_CFLAGS += -DCHRE_MAX_EVENT_BLOCKS=4 -TINYSYS_CFLAGS += -DCHRE_UNSCHEDULED_EVENT_PER_BLOCK=32 -TINYSYS_CFLAGS += -DCHRE_MAX_UNSCHEDULED_EVENT_BLOCKS=4 - # Optional Features ############################################################ CHRE_AUDIO_SUPPORT_ENABLED = true |