From a6fd6ce388bcb120b3d0128779fa0ecf40a5ee16 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Fri, 23 Feb 2024 09:55:29 -0500 Subject: [PATCH 01/52] Create a separate 'constants' source set in `src/app` (#32228) * Create constants file * Restyle * Place everything into a `Revision` namespace to avoid name collision with a Tags enum * Rename --------- Co-authored-by: Andrei Litvin --- src/app/BUILD.gn | 23 +++++++------ src/app/DataModelRevision.h | 31 ----------------- src/app/InteractionModelTimeout.h | 1 - src/app/MessageDef/BUILD.gn | 2 +- src/app/MessageDef/InvokeRequestMessage.cpp | 2 +- src/app/MessageDef/InvokeResponseMessage.cpp | 2 +- src/app/MessageDef/MessageBuilder.cpp | 3 +- src/app/MessageDef/MessageBuilder.h | 2 +- src/app/MessageDef/MessageDefHelper.cpp | 2 +- src/app/MessageDef/MessageParser.cpp | 4 +-- src/app/MessageDef/MessageParser.h | 2 +- src/app/MessageDef/ReadRequestMessage.cpp | 2 +- src/app/MessageDef/ReportDataMessage.cpp | 2 +- src/app/MessageDef/StatusResponseMessage.cpp | 2 +- .../MessageDef/SubscribeRequestMessage.cpp | 2 +- .../MessageDef/SubscribeResponseMessage.cpp | 2 +- src/app/MessageDef/TimedRequestMessage.cpp | 2 +- src/app/MessageDef/WriteRequestMessage.cpp | 2 +- src/app/MessageDef/WriteResponseMessage.cpp | 2 +- ...sion.h => SpecificationDefinedRevisions.h} | 33 +++++++++++++++---- src/app/SpecificationVersion.h | 31 ----------------- .../basic-information/basic-information.cpp | 7 ++-- src/protocols/secure_channel/BUILD.gn | 2 +- .../secure_channel/PairingSession.cpp | 10 +++--- 24 files changed, 65 insertions(+), 108 deletions(-) delete mode 100644 src/app/DataModelRevision.h rename src/app/{InteractionModelRevision.h => SpecificationDefinedRevisions.h} (51%) delete mode 100644 src/app/SpecificationVersion.h diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index f714560afc15cb..69f5926909756d 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -76,14 +76,6 @@ source_set("app_config") { deps = [ ":app_buildconfig" ] } -source_set("revision_info") { - sources = [ - "DataModelRevision.h", - "InteractionModelRevision.h", - "SpecificationVersion.h", - ] -} - source_set("paths") { sources = [ "AttributePathParams.h", @@ -130,6 +122,17 @@ config("config-controller-dynamic-server") { ] } +source_set("constants") { + sources = [ + "InteractionModelTimeout.h", + "SpecificationDefinedRevisions.h", + ] + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/system", + ] +} + # interaction-model is a static-library because it currently requires global functions (app/util/...) that are stubbed in different test files that depend on the app static_library # which in tern depens on the interaction-model. # Using source_set prevents the unit test to build correctly. @@ -149,7 +152,6 @@ static_library("interaction-model") { "InteractionModelEngine.cpp", "InteractionModelEngine.h", "InteractionModelHelper.h", - "InteractionModelTimeout.h", "OperationalSessionSetup.cpp", "OperationalSessionSetup.h", "OperationalSessionSetupPool.h", @@ -181,6 +183,7 @@ static_library("interaction-model") { public_deps = [ ":app_config", + ":constants", ":paths", ":subscription-manager", "${chip_root}/src/app/MessageDef", @@ -276,9 +279,9 @@ static_library("app") { public_deps = [ ":app_config", + ":constants", ":global-attributes", ":interaction-model", - ":revision_info", "${chip_root}/src/app/data-model", "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/lib/address_resolve", diff --git a/src/app/DataModelRevision.h b/src/app/DataModelRevision.h deleted file mode 100644 index 47ecb626a13ffa..00000000000000 --- a/src/app/DataModelRevision.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors - * - * 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. - */ - -#pragma once - -/** - * CHIP_DEVICE_DATA_MODEL_REVISION - * - * A monotonic number identifying the revision number of the Data Model against - * which the Node is certified. - * - * See section 7.1.1. "Revision History" in the "Data Model Specification" - * chapter of the core Matter specification. - */ -#ifndef CHIP_DEVICE_DATA_MODEL_REVISION -#define CHIP_DEVICE_DATA_MODEL_REVISION 17 -#endif diff --git a/src/app/InteractionModelTimeout.h b/src/app/InteractionModelTimeout.h index 3511ed5412049d..43153c679f2361 100644 --- a/src/app/InteractionModelTimeout.h +++ b/src/app/InteractionModelTimeout.h @@ -18,7 +18,6 @@ #pragma once #include -#include namespace chip { namespace app { diff --git a/src/app/MessageDef/BUILD.gn b/src/app/MessageDef/BUILD.gn index 4880a9a8b126e4..18b254038fe2b8 100644 --- a/src/app/MessageDef/BUILD.gn +++ b/src/app/MessageDef/BUILD.gn @@ -113,8 +113,8 @@ source_set("MessageDef") { deps = [ "${chip_root}/src/app:app_config", + "${chip_root}/src/app:constants", "${chip_root}/src/app:paths", - "${chip_root}/src/app:revision_info", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", "${chip_root}/src/protocols/interaction_model", diff --git a/src/app/MessageDef/InvokeRequestMessage.cpp b/src/app/MessageDef/InvokeRequestMessage.cpp index 50bd5cd45f70c0..d334b05dfa0362 100644 --- a/src/app/MessageDef/InvokeRequestMessage.cpp +++ b/src/app/MessageDef/InvokeRequestMessage.cpp @@ -74,7 +74,7 @@ CHIP_ERROR InvokeRequestMessage::Parser::PrettyPrint() const PRETTY_PRINT_DECDEPTH(); } break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/InvokeResponseMessage.cpp b/src/app/MessageDef/InvokeResponseMessage.cpp index f9887530c15e15..53568ce14d9b5e 100644 --- a/src/app/MessageDef/InvokeResponseMessage.cpp +++ b/src/app/MessageDef/InvokeResponseMessage.cpp @@ -73,7 +73,7 @@ CHIP_ERROR InvokeResponseMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/MessageBuilder.cpp b/src/app/MessageDef/MessageBuilder.cpp index b9ad0e58968d23..4effd887c6814c 100644 --- a/src/app/MessageDef/MessageBuilder.cpp +++ b/src/app/MessageDef/MessageBuilder.cpp @@ -24,8 +24,7 @@ namespace chip { namespace app { CHIP_ERROR MessageBuilder::EncodeInteractionModelRevision() { - return mpWriter->Put(TLV::ContextTag(kInteractionModelRevisionTag), - static_cast(CHIP_DEVICE_INTERACTION_MODEL_REVISION)); + return mpWriter->Put(TLV::ContextTag(Revision::kInteractionModelRevisionTag), Revision::kInteractionModelRevision); } } // namespace app } // namespace chip diff --git a/src/app/MessageDef/MessageBuilder.h b/src/app/MessageDef/MessageBuilder.h index 03210b3011cf21..fac114ad1379b5 100644 --- a/src/app/MessageDef/MessageBuilder.h +++ b/src/app/MessageDef/MessageBuilder.h @@ -18,7 +18,7 @@ #pragma once #include "StructBuilder.h" -#include +#include #include namespace chip { diff --git a/src/app/MessageDef/MessageDefHelper.cpp b/src/app/MessageDef/MessageDefHelper.cpp index db24b2623dcc00..82fcf286d1751b 100644 --- a/src/app/MessageDef/MessageDefHelper.cpp +++ b/src/app/MessageDef/MessageDefHelper.cpp @@ -24,7 +24,7 @@ #include "MessageDefHelper.h" #include #include -#include +#include #include #include #include diff --git a/src/app/MessageDef/MessageParser.cpp b/src/app/MessageDef/MessageParser.cpp index b855a93afcd7eb..7d74378b49f0c2 100644 --- a/src/app/MessageDef/MessageParser.cpp +++ b/src/app/MessageDef/MessageParser.cpp @@ -16,7 +16,7 @@ #include "MessageParser.h" #include "MessageDefHelper.h" -#include +#include namespace chip { namespace app { @@ -52,7 +52,7 @@ CHIP_ERROR MessageParser::CheckInteractionModelRevision(TLV::TLVReader & aReader CHIP_ERROR MessageParser::GetInteractionModelRevision(InteractionModelRevision * const apInteractionModelRevision) const { - return GetUnsignedInteger(kInteractionModelRevisionTag, apInteractionModelRevision); + return GetUnsignedInteger(Revision::kInteractionModelRevisionTag, apInteractionModelRevision); } } // namespace app diff --git a/src/app/MessageDef/MessageParser.h b/src/app/MessageDef/MessageParser.h index e307d5b905d965..4886f295e253eb 100644 --- a/src/app/MessageDef/MessageParser.h +++ b/src/app/MessageDef/MessageParser.h @@ -20,7 +20,7 @@ #include "StructParser.h" #include -#include +#include #include namespace chip { diff --git a/src/app/MessageDef/ReadRequestMessage.cpp b/src/app/MessageDef/ReadRequestMessage.cpp index 8b96b044d774c5..855d0a3e14d936 100644 --- a/src/app/MessageDef/ReadRequestMessage.cpp +++ b/src/app/MessageDef/ReadRequestMessage.cpp @@ -91,7 +91,7 @@ CHIP_ERROR ReadRequestMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/ReportDataMessage.cpp b/src/app/MessageDef/ReportDataMessage.cpp index c3c569572ae9fe..a2a8ec47968ec1 100644 --- a/src/app/MessageDef/ReportDataMessage.cpp +++ b/src/app/MessageDef/ReportDataMessage.cpp @@ -112,7 +112,7 @@ CHIP_ERROR ReportDataMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/StatusResponseMessage.cpp b/src/app/MessageDef/StatusResponseMessage.cpp index f702998c90ed23..fbf3a870d542c6 100644 --- a/src/app/MessageDef/StatusResponseMessage.cpp +++ b/src/app/MessageDef/StatusResponseMessage.cpp @@ -52,7 +52,7 @@ CHIP_ERROR StatusResponseMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/SubscribeRequestMessage.cpp b/src/app/MessageDef/SubscribeRequestMessage.cpp index b536570891bade..7224c850baaa20 100644 --- a/src/app/MessageDef/SubscribeRequestMessage.cpp +++ b/src/app/MessageDef/SubscribeRequestMessage.cpp @@ -115,7 +115,7 @@ CHIP_ERROR SubscribeRequestMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/SubscribeResponseMessage.cpp b/src/app/MessageDef/SubscribeResponseMessage.cpp index 99528895e49796..25ef8ec0bd3038 100644 --- a/src/app/MessageDef/SubscribeResponseMessage.cpp +++ b/src/app/MessageDef/SubscribeResponseMessage.cpp @@ -59,7 +59,7 @@ CHIP_ERROR SubscribeResponseMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/TimedRequestMessage.cpp b/src/app/MessageDef/TimedRequestMessage.cpp index bf6bdc5b79a45b..830a3d66894377 100644 --- a/src/app/MessageDef/TimedRequestMessage.cpp +++ b/src/app/MessageDef/TimedRequestMessage.cpp @@ -49,7 +49,7 @@ CHIP_ERROR TimedRequestMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/WriteRequestMessage.cpp b/src/app/MessageDef/WriteRequestMessage.cpp index 8cf3d4048d9b2f..5278fff2c94166 100644 --- a/src/app/MessageDef/WriteRequestMessage.cpp +++ b/src/app/MessageDef/WriteRequestMessage.cpp @@ -88,7 +88,7 @@ CHIP_ERROR WriteRequestMessage::Parser::PrettyPrint() const } #endif // CHIP_DETAIL_LOGGING break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/MessageDef/WriteResponseMessage.cpp b/src/app/MessageDef/WriteResponseMessage.cpp index 4426d5e3db7c5e..e8da708bfc94e5 100644 --- a/src/app/MessageDef/WriteResponseMessage.cpp +++ b/src/app/MessageDef/WriteResponseMessage.cpp @@ -54,7 +54,7 @@ CHIP_ERROR WriteResponseMessage::Parser::PrettyPrint() const ReturnErrorOnFailure(writeResponses.PrettyPrint()); PRETTY_PRINT_DECDEPTH(); break; - case kInteractionModelRevisionTag: + case Revision::kInteractionModelRevisionTag: ReturnErrorOnFailure(MessageParser::CheckInteractionModelRevision(reader)); break; default: diff --git a/src/app/InteractionModelRevision.h b/src/app/SpecificationDefinedRevisions.h similarity index 51% rename from src/app/InteractionModelRevision.h rename to src/app/SpecificationDefinedRevisions.h index 529ac0f839c80e..cad67aced413b0 100644 --- a/src/app/InteractionModelRevision.h +++ b/src/app/SpecificationDefinedRevisions.h @@ -20,16 +20,37 @@ #include #include +#include + +namespace chip { +namespace Revision { + /** - * CHIP_DEVICE_INTERACTION_MODEL_REVISION - * * A monothonic number identifying the interaction model revision. * * See section 8.1.1. "Revision History" in the "Interaction Model * Specification" chapter of the core Matter specification. */ -#ifndef CHIP_DEVICE_INTERACTION_MODEL_REVISION -#define CHIP_DEVICE_INTERACTION_MODEL_REVISION 11 -#endif +inline constexpr InteractionModelRevision kInteractionModelRevision = 11; +inline constexpr uint8_t kInteractionModelRevisionTag = 0xFF; + +/** + * A monotonic number identifying the revision number of the Data Model against + * which the Node is certified. + * + * See section 7.1.1. "Revision History" in the "Data Model Specification" + * chapter of the core Matter specification. + */ +inline constexpr uint16_t kDataModelRevision = 17; + +/* + * A number identifying the specification version against which the + * Node is certified. + * + * See section 11.1.5.22. "SpecificationVersion Attribute" in "Service and + * Device Management" chapter of the core Matter specification. + */ +inline constexpr uint32_t kSpecificationVersion = 0x01030000; -inline constexpr uint8_t kInteractionModelRevisionTag = 0xFF; +} // namespace Revision +} // namespace chip diff --git a/src/app/SpecificationVersion.h b/src/app/SpecificationVersion.h deleted file mode 100644 index b0570dad14c4ce..00000000000000 --- a/src/app/SpecificationVersion.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright (c) 2023 Project CHIP Authors - * - * 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. - */ - -#pragma once - -/** - * CHIP_DEVICE_SPECIFICATION_VERSION - * - * A number identifying the specification version against which the - * Node is certified. - * - * See section 11.1.5.22. "SpecificationVersion Attribute" in "Service and - * Device Management" chapter of the core Matter specification. - */ -#ifndef CHIP_DEVICE_SPECIFICATION_VERSION -#define CHIP_DEVICE_SPECIFICATION_VERSION 0x01030000 -#endif diff --git a/src/app/clusters/basic-information/basic-information.cpp b/src/app/clusters/basic-information/basic-information.cpp index 641dc59a853dd6..91eef9d9dae899 100644 --- a/src/app/clusters/basic-information/basic-information.cpp +++ b/src/app/clusters/basic-information/basic-information.cpp @@ -20,10 +20,9 @@ #include #include -#include #include #include -#include +#include #include #include #include @@ -314,7 +313,7 @@ CHIP_ERROR BasicAttrAccess::Read(const ConcreteReadAttributePath & aPath, Attrib CHIP_ERROR BasicAttrAccess::ReadDataModelRevision(AttributeValueEncoder & aEncoder) { - uint16_t revision = CHIP_DEVICE_DATA_MODEL_REVISION; + uint16_t revision = Revision::kDataModelRevision; return aEncoder.Encode(revision); } @@ -399,7 +398,7 @@ CHIP_ERROR BasicAttrAccess::ReadProductAppearance(AttributeValueEncoder & aEncod CHIP_ERROR BasicAttrAccess::ReadSpecificationVersion(AttributeValueEncoder & aEncoder) { - uint32_t specification_version = CHIP_DEVICE_SPECIFICATION_VERSION; + uint32_t specification_version = Revision::kSpecificationVersion; return aEncoder.Encode(specification_version); } diff --git a/src/protocols/secure_channel/BUILD.gn b/src/protocols/secure_channel/BUILD.gn index dcae65a6f4d271..1202c0bb88a9ae 100644 --- a/src/protocols/secure_channel/BUILD.gn +++ b/src/protocols/secure_channel/BUILD.gn @@ -88,5 +88,5 @@ static_library("secure_channel") { "${chip_root}/src/transport", ] - deps = [ "${chip_root}/src/app:revision_info" ] + deps = [ "${chip_root}/src/app:constants" ] } diff --git a/src/protocols/secure_channel/PairingSession.cpp b/src/protocols/secure_channel/PairingSession.cpp index 23daea30f2800c..80e048597578be 100644 --- a/src/protocols/secure_channel/PairingSession.cpp +++ b/src/protocols/secure_channel/PairingSession.cpp @@ -18,9 +18,7 @@ #include -#include -#include -#include +#include #include #include #include @@ -112,13 +110,13 @@ CHIP_ERROR PairingSession::EncodeSessionParameters(TLV::Tag tag, const Optional< ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveThreshold), mrpLocalConfig.mActiveThresholdTime.count())); - uint16_t dataModel = CHIP_DEVICE_DATA_MODEL_REVISION; + uint16_t dataModel = Revision::kDataModelRevision; ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kDataModelRevision), dataModel)); - uint16_t interactionModel = CHIP_DEVICE_INTERACTION_MODEL_REVISION; + uint16_t interactionModel = Revision::kInteractionModelRevision; ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kInteractionModelRevision), interactionModel)); - uint32_t specVersion = CHIP_DEVICE_SPECIFICATION_VERSION; + uint32_t specVersion = Revision::kSpecificationVersion; ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSpecificationVersion), specVersion)); uint16_t maxPathsPerInvoke = CHIP_CONFIG_MAX_PATHS_PER_INVOKE; From 52f5f019eeb0274a4f929cd607edf761dfbff482 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Fri, 23 Feb 2024 11:07:22 -0500 Subject: [PATCH 02/52] Move `declare_args` sections from `data_model` into AppConfig.h (#32270) * Move defines into buildconfig_headers rather than data_model templates * Restyle * Update src/app/BUILD.gn Co-authored-by: Boris Zbarsky * Add minssing include --------- Co-authored-by: Andrei Litvin Co-authored-by: Boris Zbarsky --- src/app/BUILD.gn | 11 +++++++++++ src/app/chip_data_model.gni | 13 ------------- .../ota-requestor/DefaultOTARequestorDriver.cpp | 4 +++- .../time-synchronization-server.cpp | 11 +++++------ .../time-synchronization-server.h | 14 +++++++------- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 69f5926909756d..e6754bd8c32a31 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -43,6 +43,15 @@ declare_args() { enable_eventlist_attribute = false + # Allow building ota-requestor-app with a non-spec-compliant floor + # (i.e. smaller than 2 minutes) for action delays. + non_spec_compliant_ota_action_delay_floor = -1 + + # enable time sync client for use in `time-synchronization-server` (if linked) + # TODO: this should probably be migrated to be time-synchronization-server specific + # if the cluster build targets are decoupled as stand-alone units. + time_sync_enable_tsc_feature = 1 + # Systems that can spare a bit of RAM for InteractionModelEngine/delegate # pointers should do so (allows InteractionModelEngine decoupling and less usage # of global pointers) @@ -65,6 +74,8 @@ buildconfig_header("app_buildconfig") { "CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE=${enable_eventlist_attribute}", "CHIP_CONFIG_ENABLE_READ_CLIENT=${chip_enable_read_client}", "CHIP_CONFIG_STATIC_GLOBAL_INTERACTION_MODEL_ENGINE=${chip_im_static_global_interaction_model_engine}", + "TIME_SYNC_ENABLE_TSC_FEATURE=${time_sync_enable_tsc_feature}", + "NON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR=${non_spec_compliant_ota_action_delay_floor}", ] visibility = [ ":app_config" ] diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 3763243e404407..42bc2f9b74ad64 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -35,13 +35,6 @@ _app_root = get_path_info(".", "abspath") template("chip_data_model") { _data_model_name = target_name - declare_args() { - # Allow building ota-requestor-app with a non-spec-compliant floor - # (i.e. smaller than 2 minutes) for action delays. - non_spec_compliant_ota_action_delay_floor = -1 - time_sync_enable_tsc_feature = 1 - } - if (defined(invoker.idl)) { _idl = invoker.idl } else { @@ -244,8 +237,6 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/DefaultTimeSyncDelegate.cpp", "${_app_root}/clusters/${cluster}/TimeSyncDataProvider.cpp", ] - defines += - [ "TIME_SYNC_ENABLE_TSC_FEATURE=${time_sync_enable_tsc_feature}" ] } else if (cluster == "scenes-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", @@ -359,9 +350,5 @@ template("chip_data_model") { } cflags += [ "-Wconversion" ] - - if (non_spec_compliant_ota_action_delay_floor >= 0) { - cflags += [ "-DNON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR=${non_spec_compliant_ota_action_delay_floor}" ] - } } } diff --git a/src/app/clusters/ota-requestor/DefaultOTARequestorDriver.cpp b/src/app/clusters/ota-requestor/DefaultOTARequestorDriver.cpp index 60cc54277c36cd..d3efeaee6fb3a9 100644 --- a/src/app/clusters/ota-requestor/DefaultOTARequestorDriver.cpp +++ b/src/app/clusters/ota-requestor/DefaultOTARequestorDriver.cpp @@ -38,6 +38,8 @@ #include "DefaultOTARequestorDriver.h" #include "OTARequestorInterface.h" +#include + namespace chip { namespace DeviceLayer { namespace { @@ -49,7 +51,7 @@ constexpr uint8_t kMaxInvalidSessionRetries = 1; // Max # of query image constexpr uint32_t kDelayQueryUponCommissioningSec = 30; // Delay before sending the initial image query after commissioning constexpr uint32_t kImmediateStartDelaySec = 1; // Delay before sending a query in response to UrgentUpdateAvailable -#ifdef NON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR +#if NON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR >= 0 constexpr System::Clock::Seconds32 kDefaultDelayedActionTime = System::Clock::Seconds32(NON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR); #else constexpr System::Clock::Seconds32 kDefaultDelayedActionTime = System::Clock::Seconds32(120); diff --git a/src/app/clusters/time-synchronization-server/time-synchronization-server.cpp b/src/app/clusters/time-synchronization-server/time-synchronization-server.cpp index cc6c3f509c8937..a8433397ee6751 100644 --- a/src/app/clusters/time-synchronization-server/time-synchronization-server.cpp +++ b/src/app/clusters/time-synchronization-server/time-synchronization-server.cpp @@ -18,11 +18,8 @@ #include "DefaultTimeSyncDelegate.h" #include "time-synchronization-delegate.h" -#if TIME_SYNC_ENABLE_TSC_FEATURE -#include -#endif - #include +#include #include #include #include @@ -37,10 +34,12 @@ #include #include -#include - #include +#if TIME_SYNC_ENABLE_TSC_FEATURE +#include +#endif + using namespace chip; using namespace chip::app; using namespace chip::DeviceLayer; diff --git a/src/app/clusters/time-synchronization-server/time-synchronization-server.h b/src/app/clusters/time-synchronization-server/time-synchronization-server.h index c6012b19bb0836..ef40eaacc790c4 100644 --- a/src/app/clusters/time-synchronization-server/time-synchronization-server.h +++ b/src/app/clusters/time-synchronization-server/time-synchronization-server.h @@ -21,16 +21,10 @@ #pragma once -#ifndef TIME_SYNC_ENABLE_TSC_FEATURE -#define TIME_SYNC_ENABLE_TSC_FEATURE 1 -#endif - #include "TimeSyncDataProvider.h" #include "time-synchronization-delegate.h" -#if TIME_SYNC_ENABLE_TSC_FEATURE -#include -#endif +#include #include #include #include @@ -40,6 +34,12 @@ #include #include +// NOTE: this is part of AppConfig, so this has to be checked for AFTER the inclusion +// of that header +#if TIME_SYNC_ENABLE_TSC_FEATURE +#include +#endif + namespace chip { namespace app { namespace Clusters { From c49e7844d071866720fea23bed5bbdeb764fd991 Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Sat, 24 Feb 2024 01:19:00 +0900 Subject: [PATCH 03/52] [Android] Fix NsdService crash (#32284) * Fix NsdService crash in Android Platform * Restyled by google-java-format --------- Co-authored-by: Restyled.io --- .../java/chip/platform/NsdServiceFinderAndResolver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java index 7ee8f979e5ac33..fc3425f7bd8889 100644 --- a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java +++ b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java @@ -71,9 +71,6 @@ public NsdServiceFinderAndResolver( public void start() { multicastLock.acquire(); - this.nsdManager.discoverServices( - targetServiceInfo.getServiceType(), NsdManager.PROTOCOL_DNS_SD, this); - NsdServiceFinderAndResolver serviceFinderResolver = this; this.stopDiscoveryRunnable = Executors.newSingleThreadScheduledExecutor() @@ -92,6 +89,9 @@ public void run() { }, BROWSE_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS); + + this.nsdManager.discoverServices( + targetServiceInfo.getServiceType(), NsdManager.PROTOCOL_DNS_SD, this); } @Override From 52ab3dbbfe2bbb79e590aa6396fbee8985cdc469 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 23 Feb 2024 12:46:55 -0500 Subject: [PATCH 04/52] TC-IDM-10.1: Fix guard around non-standard attributes (#32231) --- src/controller/python/chip/clusters/ClusterObjects.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/controller/python/chip/clusters/ClusterObjects.py b/src/controller/python/chip/clusters/ClusterObjects.py index 111274c884a8dd..bf07544af9e888 100644 --- a/src/controller/python/chip/clusters/ClusterObjects.py +++ b/src/controller/python/chip/clusters/ClusterObjects.py @@ -301,10 +301,11 @@ def __init_subclass__(cls, *args, **kwargs) -> None: """Register a subclass.""" super().__init_subclass__(*args, **kwargs) try: - if cls.standard_attribute and cls.cluster_id not in ALL_ATTRIBUTES: - ALL_ATTRIBUTES[cls.cluster_id] = {} - # register this clusterattribute in the ALL_ATTRIBUTES dict for quick lookups - ALL_ATTRIBUTES[cls.cluster_id][cls.attribute_id] = cls + if cls.standard_attribute: + if cls.cluster_id not in ALL_ATTRIBUTES: + ALL_ATTRIBUTES[cls.cluster_id] = {} + # register this clusterattribute in the ALL_ATTRIBUTES dict for quick lookups + ALL_ATTRIBUTES[cls.cluster_id][cls.attribute_id] = cls except NotImplementedError: # handle case where the ClusterAttribute class is not (fully) subclassed # and accessing the id property throws a NotImplementedError. From 3903f6cbef862b691645eec706fc669f33646ab9 Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Sat, 24 Feb 2024 02:24:47 +0800 Subject: [PATCH 05/52] Add more ICD menuconfig options for ICD server (#32286) --- config/esp32/components/chip/CMakeLists.txt | 19 ++++++++ config/esp32/components/chip/Kconfig | 52 ++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index 7f62ce6cc0087a..14594a03b74352 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -131,6 +131,25 @@ endif() if(CONFIG_ENABLE_ICD_SERVER) chip_gn_arg_append("chip_enable_icd_server" "true") + if(CONFIG_ICD_ENFORCE_SIT_SLOW_POLL_LIMIT) + chip_gn_arg_append("icd_enforce_sit_slow_poll_limit" "true") + endif() + if(CONFIG_ICD_REPORT_ON_ACTIVE_MODE) + chip_gn_arg_append("chip_icd_report_on_active_mode" "true") + endif() + if(CONFIG_ENABLE_ICD_LIT) + chip_gn_arg_append("chip_enable_icd_lit" "true") + if(CONFIG_ENABLE_ICD_CIP) + chip_gn_arg_append("chip_enable_icd_checkin" "true") + else() + chip_gn_arg_append("chip_enable_icd_checkin" "false") + endif() + if(CONFIG_ENABLE_ICD_USER_ACTIVE_MODE_TRIGGER) + chip_gn_arg_append("chip_enable_icd_user_active_mode_trigger" "true") + else() + chip_gn_arg_append("chip_enable_icd_user_active_mode_trigger" "false") + endif() + endif() endif() if(CONFIG_ENABLE_PW_RPC) diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index 0dce48b5f720be..b39105d1285fac 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -340,13 +340,27 @@ menu "CHIP Device Layer" help Enables or Disables the support for Commissionable Device Type. - config ENABLE_ICD_SERVER + menuconfig ENABLE_ICD_SERVER bool "Enable ICD server" depends on OPENTHREAD_MTD default n help Enables or Disables ICD server + config ICD_ENFORCE_SIT_SLOW_POLL_LIMIT + bool "Enforce SIT Slow Polling Max value to 15 seconds" + depends on ENABLE_ICD_SERVER + default n + help + Set to true to enforce SIT Slow Polling Max value to 15seconds + + config ICD_REPORT_ON_ACTIVE_MODE + bool "Emit a report on entering active mode" + depends on ENABLE_ICD_SERVER + default n + help + Make the ICD manager emit a report on entering active mode + config ICD_SLOW_POLL_INTERVAL_MS int "ICD Slow Polling Interval(ms)" depends on ENABLE_ICD_SERVER @@ -389,14 +403,48 @@ menu "CHIP Device Layer" This value indicates the minimum amount of time in milliseconds the server typically will stay active after network activity when in active mode. + config ENABLE_ICD_LIT + bool "Enable Long Idle Time ICD" + depends on ENABLE_ICD_SERVER + default n + help + Enables or Disables LIT ICD + + config ENABLE_ICD_CIP + bool "Enable Check-in protocol" + depends on ENABLE_ICD_LIT + default y + help + Enables or Disables ICD Check-in protocol + config ICD_CLIENTS_SUPPORTED_PER_FABRIC int "ICD Clients Number Supported Per Fabric" - depends on ENABLE_ICD_SERVER + depends on ENABLE_ICD_CIP default 1 help This value indicates the maximum number of entries that the ICD server is able to store for each fabric in the RegisteredClients attribute. + config ICD_MAX_NOTIFICATION_SUBSCRIBERS + int "Max ICD notification subscribers" + depends on ENABLE_ICD_SERVER + default 1 + help + The ICDManager implements the ICDListener functions and is always subscribed to the ICDNotifier + This allows other Matter modules to inform the ICDManager that it needs to go and may have to stay in Active Mode, + outside of its standard ActiveModeDuration and IdleModeDuration, without being tightly coupled the application + data model + + This implementation also allows other modules to implement an ICDListener and subscribe to ICDNotifier + to couple behaviours with the ICD cycles. In such cases, ICD_MAX_NOTIFICATION_SUBSCRIBERS need to be adjusted + + config ENABLE_ICD_USER_ACTIVE_MODE_TRIGGER + bool "Enable User Active Mode Trigger feature" + depends on ENABLE_ICD_LIT + default y + help + Enables or Disables ICD User Active Mode Trigger feature + config ENABLE_BG_EVENT_PROCESSING bool "Enable Background event processing" default n From 7ba98fb892a5e80e5872c0546e05338e0b7de9e6 Mon Sep 17 00:00:00 2001 From: Hasty Granbery Date: Sat, 24 Feb 2024 03:28:18 +0900 Subject: [PATCH 06/52] [Messages] Change message duration to uint64 (#32248) * Change message duration to uint64 * Fixes to Java build * Missed a HandlePresentMessagesRequest signature --- data_model/clusters/Messages.xml | 2 +- .../include/messages/MessagesManager.cpp | 2 +- .../include/messages/MessagesManager.h | 2 +- .../tv-app/android/java/MessagesManager.cpp | 12 +++++------ .../tv-app/android/java/MessagesManager.h | 2 +- .../com/matter/tv/server/tvapp/Message.java | 4 ++-- .../tv/server/tvapp/MessagesManager.java | 2 +- .../tv/server/tvapp/MessagesManagerStub.java | 2 +- .../clusters/messages/MessagesManager.cpp | 2 +- .../clusters/messages/MessagesManager.h | 6 +++--- examples/tv-app/tv-common/tv-app.matter | 4 ++-- .../tv-casting-common/tv-casting-app.matter | 4 ++-- .../messages-server/messages-delegate.h | 2 +- .../zcl/data-model/chip/messages-cluster.xml | 4 ++-- .../data_model/controller-clusters.matter | 4 ++-- .../chip/devicecontroller/ChipClusters.java | 4 ++-- .../chip/devicecontroller/ChipStructs.java | 8 +++---- .../devicecontroller/ClusterInfoMapping.java | 4 ++-- .../structs/MessagesClusterMessageStruct.kt | 4 ++-- .../cluster/clusters/MessagesCluster.kt | 2 +- .../structs/MessagesClusterMessageStruct.kt | 4 ++-- .../CHIPAttributeTLVValueDecoder.cpp | 21 +++++++++---------- .../MTRAttributeTLVValueDecoder.mm | 2 +- .../zap-generated/MTRCommandPayloadsObjc.mm | 2 +- .../zap-generated/cluster-objects.h | 8 +++---- .../zap-generated/cluster/Commands.h | 2 +- .../zap-generated/cluster/Commands.h | 4 ++-- 27 files changed, 59 insertions(+), 60 deletions(-) diff --git a/data_model/clusters/Messages.xml b/data_model/clusters/Messages.xml index cea28ac15f54c9..5bc3ce81fa2a0d 100644 --- a/data_model/clusters/Messages.xml +++ b/data_model/clusters/Messages.xml @@ -216,7 +216,7 @@ Davis, CA 95616, USA - + diff --git a/examples/tv-app/android/include/messages/MessagesManager.cpp b/examples/tv-app/android/include/messages/MessagesManager.cpp index dcd48f028b961e..00848c67312751 100644 --- a/examples/tv-app/android/include/messages/MessagesManager.cpp +++ b/examples/tv-app/android/include/messages/MessagesManager.cpp @@ -25,7 +25,7 @@ using namespace chip::app::Clusters::Messages; void MessagesManager::HandlePresentMessagesRequest( const chip::ByteSpan & messageId, const MessagePriorityEnum & priority, const chip::BitMask & messageControl, const chip::app::DataModel::Nullable & startTime, - const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, + const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, const chip::Optional> & responses) { // TODO: Present Message diff --git a/examples/tv-app/android/include/messages/MessagesManager.h b/examples/tv-app/android/include/messages/MessagesManager.h index 5f06d253846c4f..d6759061800621 100644 --- a/examples/tv-app/android/include/messages/MessagesManager.h +++ b/examples/tv-app/android/include/messages/MessagesManager.h @@ -27,7 +27,7 @@ class MessagesManager : public chip::app::Clusters::Messages::Delegate void HandlePresentMessagesRequest( const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, const chip::BitMask & messageControl, - const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, + const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, const chip::Optional> & responses) override; diff --git a/examples/tv-app/android/java/MessagesManager.cpp b/examples/tv-app/android/java/MessagesManager.cpp index 9203d7b54510f5..dbcb5c5994504b 100644 --- a/examples/tv-app/android/java/MessagesManager.cpp +++ b/examples/tv-app/android/java/MessagesManager.cpp @@ -183,10 +183,10 @@ CHIP_ERROR MessagesManager::HandleGetMessages(AttributeValueEncoder & aEncoder) } jfieldID durationField = env->GetFieldID(messageClass, "duration", "I"); - jint jduration = env->GetIntField(messageObject, durationField); + jlong jduration = env->GetLongField(messageObject, durationField); if (jduration >= 0) { - message.duration = DataModel::Nullable(static_cast(jduration)); + message.duration = DataModel::Nullable(static_cast(jduration)); } jfieldID getResponseOptionsField = @@ -301,7 +301,7 @@ CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(AttributeValueEncoder & aE CHIP_ERROR MessagesManager::HandlePresentMessagesRequest( const ByteSpan & messageId, const MessagePriorityEnum & priority, const BitMask & messageControl, - const DataModel::Nullable & startTime, const DataModel::Nullable & duration, const CharSpan & messageText, + const DataModel::Nullable & startTime, const DataModel::Nullable & duration, const CharSpan & messageText, const Optional> & responses) { DeviceLayer::StackUnlock unlock; @@ -336,11 +336,11 @@ CHIP_ERROR MessagesManager::HandlePresentMessagesRequest( return CHIP_ERROR_INTERNAL; } - jint jcontrol = static_cast(messageControl.Raw()); - jint jduration = -1; + jint jcontrol = static_cast(messageControl.Raw()); + jlong jduration = -1; if (!duration.IsNull()) { - jduration = static_cast(duration.Value()); + jduration = static_cast(duration.Value()); } jlong jstartTime = -1; if (!startTime.IsNull()) diff --git a/examples/tv-app/android/java/MessagesManager.h b/examples/tv-app/android/java/MessagesManager.h index 563192a542bdf6..089d3e4360afe0 100644 --- a/examples/tv-app/android/java/MessagesManager.h +++ b/examples/tv-app/android/java/MessagesManager.h @@ -35,7 +35,7 @@ class MessagesManager : public chip::app::Clusters::Messages::Delegate CHIP_ERROR HandlePresentMessagesRequest( const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, const chip::BitMask & messageControl, - const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, + const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, const chip::Optional< chip::app::DataModel::DecodableList> & diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/Message.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/Message.java index c194ffb44f9ee1..8949ab8283b10d 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/Message.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/Message.java @@ -23,7 +23,7 @@ public class Message { public int priority; public int messageControl; public long startTime; - public int duration; + public long duration; public String messageText; public MessageResponseOption responseOptions[]; @@ -32,7 +32,7 @@ public Message( int priority, int messageControl, long startTime, - int duration, + long duration, String messageText, MessageResponseOption responseOptions[]) { this.messageId = messageId; diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManager.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManager.java index 0a5680866714c6..5d97083cafe8a4 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManager.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManager.java @@ -28,7 +28,7 @@ boolean presentMessages( int priority, int messageControl, long startTime, - int duration, + long duration, String messageText, HashMap responseOptions); diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java index 63fef69d35c936..55d94209197559 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java @@ -52,7 +52,7 @@ public boolean presentMessages( int priority, int messageControl, long startTime, - int duration, + long duration, String messageText, HashMap responseOptions) { Log.d( diff --git a/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp b/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp index 6ffbb9a3476258..461e35524c0736 100644 --- a/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp +++ b/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp @@ -30,7 +30,7 @@ using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageRes // Commands CHIP_ERROR MessagesManager::HandlePresentMessagesRequest( const ByteSpan & messageId, const MessagePriorityEnum & priority, const BitMask & messageControl, - const DataModel::Nullable & startTime, const DataModel::Nullable & duration, const CharSpan & messageText, + const DataModel::Nullable & startTime, const DataModel::Nullable & duration, const CharSpan & messageText, const Optional> & responses) { ChipLogProgress(Zcl, "HandlePresentMessagesRequest message:%s", std::string(messageText.data(), messageText.size()).c_str()); diff --git a/examples/tv-app/tv-common/clusters/messages/MessagesManager.h b/examples/tv-app/tv-common/clusters/messages/MessagesManager.h index 325cd56fc788af..777f202e28548a 100644 --- a/examples/tv-app/tv-common/clusters/messages/MessagesManager.h +++ b/examples/tv-app/tv-common/clusters/messages/MessagesManager.h @@ -64,7 +64,7 @@ struct CachedMessage CachedMessage(const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, const chip::BitMask & messageControl, const chip::app::DataModel::Nullable & startTime, - const chip::app::DataModel::Nullable & duration, std::string messageText) : + const chip::app::DataModel::Nullable & duration, std::string messageText) : mPriority(priority), mMessageControl(messageControl), mStartTime(startTime), mDuration(duration), mMessageText(messageText) { @@ -110,7 +110,7 @@ struct CachedMessage const chip::app::Clusters::Messages::MessagePriorityEnum mPriority; const chip::BitMask mMessageControl; const chip::app::DataModel::Nullable mStartTime; - const chip::app::DataModel::Nullable mDuration; + const chip::app::DataModel::Nullable mDuration; std::string mMessageText; uint8_t mMessageIdBuffer[chip::app::Clusters::Messages::kMessageIdLength]; @@ -126,7 +126,7 @@ class MessagesManager : public chip::app::Clusters::Messages::Delegate CHIP_ERROR HandlePresentMessagesRequest( const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, const chip::BitMask & messageControl, - const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, + const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, const chip::Optional< chip::app::DataModel::DecodableList> & diff --git a/examples/tv-app/tv-common/tv-app.matter b/examples/tv-app/tv-common/tv-app.matter index 6effacc92c0812..37fd9cb777b98b 100644 --- a/examples/tv-app/tv-common/tv-app.matter +++ b/examples/tv-app/tv-common/tv-app.matter @@ -2067,7 +2067,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } @@ -2101,7 +2101,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } diff --git a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter index 4bece078774075..35a766e68307aa 100644 --- a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter +++ b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter @@ -1545,7 +1545,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } @@ -1579,7 +1579,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } diff --git a/src/app/clusters/messages-server/messages-delegate.h b/src/app/clusters/messages-server/messages-delegate.h index aba771aad23953..6be09de781d340 100644 --- a/src/app/clusters/messages-server/messages-delegate.h +++ b/src/app/clusters/messages-server/messages-delegate.h @@ -42,7 +42,7 @@ class Delegate virtual CHIP_ERROR HandlePresentMessagesRequest( const ByteSpan & messageId, const MessagePriorityEnum & priority, const chip::BitMask & messageControl, const DataModel::Nullable & startTime, - const DataModel::Nullable & duration, const CharSpan & messageText, + const DataModel::Nullable & duration, const CharSpan & messageText, const chip::Optional> & responses) = 0; virtual CHIP_ERROR HandleCancelMessagesRequest(const DataModel::DecodableList & messageIds) = 0; diff --git a/src/app/zap-templates/zcl/data-model/chip/messages-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/messages-cluster.xml index 2da346cd49a6f7..16b1527625d320 100644 --- a/src/app/zap-templates/zcl/data-model/chip/messages-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/messages-cluster.xml @@ -58,7 +58,7 @@ limitations under the License. - + @@ -88,7 +88,7 @@ limitations under the License. - + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 235366f4206e78..8611f4bdcf62d8 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -4539,7 +4539,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } @@ -4573,7 +4573,7 @@ provisional cluster Messages = 151 { MessagePriorityEnum priority = 1; MessageControlBitmap messageControl = 2; nullable epoch_s startTime = 3; - nullable int16u duration = 4; + nullable int64u duration = 4; char_string<256> messageText = 5; optional MessageResponseOptionStruct responses[] = 6; } diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index fcc9154511d2b7..7025f3cde37858 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -29906,11 +29906,11 @@ public long initWithDevice(long devicePtr, int endpointId) { return 0L; } - public void presentMessagesRequest(DefaultClusterCallback callback, byte[] messageID, Integer priority, Integer messageControl, @Nullable Long startTime, @Nullable Integer duration, String messageText, Optional> responses) { + public void presentMessagesRequest(DefaultClusterCallback callback, byte[] messageID, Integer priority, Integer messageControl, @Nullable Long startTime, @Nullable Long duration, String messageText, Optional> responses) { presentMessagesRequest(callback, messageID, priority, messageControl, startTime, duration, messageText, responses, 0); } - public void presentMessagesRequest(DefaultClusterCallback callback, byte[] messageID, Integer priority, Integer messageControl, @Nullable Long startTime, @Nullable Integer duration, String messageText, Optional> responses, int timedInvokeTimeoutMs) { + public void presentMessagesRequest(DefaultClusterCallback callback, byte[] messageID, Integer priority, Integer messageControl, @Nullable Long startTime, @Nullable Long duration, String messageText, Optional> responses, int timedInvokeTimeoutMs) { final long commandId = 0L; ArrayList elements = new ArrayList<>(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index 6fb0469aded45c..7730f05efe734a 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -7130,7 +7130,7 @@ public static class MessagesClusterMessageStruct { public Integer priority; public Integer messageControl; public @Nullable Long startTime; - public @Nullable Integer duration; + public @Nullable Long duration; public String messageText; public Optional> responses; private static final long MESSAGE_I_D_ID = 0L; @@ -7146,7 +7146,7 @@ public MessagesClusterMessageStruct( Integer priority, Integer messageControl, @Nullable Long startTime, - @Nullable Integer duration, + @Nullable Long duration, String messageText, Optional> responses ) { @@ -7180,7 +7180,7 @@ public static MessagesClusterMessageStruct decodeTlv(BaseTLVType tlvValue) { Integer priority = null; Integer messageControl = null; @Nullable Long startTime = null; - @Nullable Integer duration = null; + @Nullable Long duration = null; String messageText = null; Optional> responses = Optional.empty(); for (StructElement element: ((StructType)tlvValue).value()) { @@ -7207,7 +7207,7 @@ public static MessagesClusterMessageStruct decodeTlv(BaseTLVType tlvValue) { } else if (element.contextTagNum() == DURATION_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { UIntType castingValue = element.value(UIntType.class); - duration = castingValue.value(Integer.class); + duration = castingValue.value(Long.class); } } else if (element.contextTagNum() == MESSAGE_TEXT_ID) { if (element.value(BaseTLVType.class).type() == TLVType.String) { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 14b4a97284f173..5162f9766a74da 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -24325,7 +24325,7 @@ public Map> getCommandMap() { CommandParameterInfo messagespresentMessagesRequeststartTimeCommandParameterInfo = new CommandParameterInfo("startTime", Long.class, Long.class); messagespresentMessagesRequestCommandParams.put("startTime",messagespresentMessagesRequeststartTimeCommandParameterInfo); - CommandParameterInfo messagespresentMessagesRequestdurationCommandParameterInfo = new CommandParameterInfo("duration", Integer.class, Integer.class); + CommandParameterInfo messagespresentMessagesRequestdurationCommandParameterInfo = new CommandParameterInfo("duration", Long.class, Long.class); messagespresentMessagesRequestCommandParams.put("duration",messagespresentMessagesRequestdurationCommandParameterInfo); CommandParameterInfo messagespresentMessagesRequestmessageTextCommandParameterInfo = new CommandParameterInfo("messageText", String.class, String.class); @@ -24343,7 +24343,7 @@ public Map> getCommandMap() { commandArguments.get("messageControl") , (Long) commandArguments.get("startTime") - , (Integer) + , (Long) commandArguments.get("duration") , (String) commandArguments.get("messageText") diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageStruct.kt index d1749289a3b652..03a544caf41c32 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageStruct.kt @@ -29,7 +29,7 @@ class MessagesClusterMessageStruct( val priority: UInt, val messageControl: UInt, val startTime: ULong?, - val duration: UInt?, + val duration: ULong?, val messageText: String, val responses: Optional> ) { @@ -97,7 +97,7 @@ class MessagesClusterMessageStruct( } val duration = if (!tlvReader.isNull()) { - tlvReader.getUInt(ContextSpecificTag(TAG_DURATION)) + tlvReader.getULong(ContextSpecificTag(TAG_DURATION)) } else { tlvReader.getNull(ContextSpecificTag(TAG_DURATION)) null diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/MessagesCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/MessagesCluster.kt index 5ce55e2e0dd21b..3e3fcd8f02d2f8 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/MessagesCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/MessagesCluster.kt @@ -106,7 +106,7 @@ class MessagesCluster(private val controller: MatterController, private val endp priority: UByte, messageControl: UByte, startTime: UInt?, - duration: UShort?, + duration: ULong?, messageText: String, responses: List?, timedInvokeTimeout: Duration? = null diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageStruct.kt index 8560beb0a72ce1..14c573c396ca18 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageStruct.kt @@ -29,7 +29,7 @@ class MessagesClusterMessageStruct( val priority: UByte, val messageControl: UByte, val startTime: UInt?, - val duration: UShort?, + val duration: ULong?, val messageText: String, val responses: Optional> ) { @@ -97,7 +97,7 @@ class MessagesClusterMessageStruct( } val duration = if (!tlvReader.isNull()) { - tlvReader.getUShort(ContextSpecificTag(TAG_DURATION)) + tlvReader.getULong(ContextSpecificTag(TAG_DURATION)) } else { tlvReader.getNull(ContextSpecificTag(TAG_DURATION)) null diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index 7a8aebe5b5c493..2400ba818c7cb9 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -23068,12 +23068,12 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } else { - std::string newElement_0_durationClassName = "java/lang/Integer"; - std::string newElement_0_durationCtorSignature = "(I)V"; - jint jninewElement_0_duration = static_cast(entry_0.duration.Value()); - chip::JniReferences::GetInstance().CreateBoxedObject(newElement_0_durationClassName.c_str(), - newElement_0_durationCtorSignature.c_str(), - jninewElement_0_duration, newElement_0_duration); + std::string newElement_0_durationClassName = "java/lang/Long"; + std::string newElement_0_durationCtorSignature = "(J)V"; + jlong jninewElement_0_duration = static_cast(entry_0.duration.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject(newElement_0_durationClassName.c_str(), + newElement_0_durationCtorSignature.c_str(), + jninewElement_0_duration, newElement_0_duration); } jobject newElement_0_messageText; LogErrorOnFailure( @@ -23163,11 +23163,10 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } jmethodID messageStructStructCtor_1; - err = - chip::JniReferences::GetInstance().FindMethod(env, messageStructStructClass_1, "", - "([BLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/" - "lang/Integer;Ljava/lang/String;Ljava/util/Optional;)V", - &messageStructStructCtor_1); + err = chip::JniReferences::GetInstance().FindMethod(env, messageStructStructClass_1, "", + "([BLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/" + "Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/Optional;)V", + &messageStructStructCtor_1); if (err != CHIP_NO_ERROR || messageStructStructCtor_1 == nullptr) { ChipLogError(Zcl, "Could not find ChipStructs$MessagesClusterMessageStruct constructor"); diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index 298c127feeb02d..bae1643f3beb39 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -8750,7 +8750,7 @@ static id _Nullable DecodeAttributeValueForMessagesCluster(AttributeId aAttribut if (entry_0.duration.IsNull()) { newElement_0.duration = nil; } else { - newElement_0.duration = [NSNumber numberWithUnsignedShort:entry_0.duration.Value()]; + newElement_0.duration = [NSNumber numberWithUnsignedLongLong:entry_0.duration.Value()]; } newElement_0.messageText = AsString(entry_0.messageText); if (newElement_0.messageText == nil) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index b2c13559d07aec..60757c92d0dfdb 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -15516,7 +15516,7 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader encodableStruct.duration.SetNull(); } else { auto & nonNullValue_0 = encodableStruct.duration.SetNonNull(); - nonNullValue_0 = self.duration.unsignedShortValue; + nonNullValue_0 = self.duration.unsignedLongLongValue; } } { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 99e6e3bb1d63f7..dfdf2e691686f2 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -21506,7 +21506,7 @@ struct Type MessagePriorityEnum priority = static_cast(0); chip::BitMask messageControl = static_cast>(0); DataModel::Nullable startTime; - DataModel::Nullable duration; + DataModel::Nullable duration; chip::CharSpan messageText; Optional> responses; @@ -21522,7 +21522,7 @@ struct DecodableType MessagePriorityEnum priority = static_cast(0); chip::BitMask messageControl = static_cast>(0); DataModel::Nullable startTime; - DataModel::Nullable duration; + DataModel::Nullable duration; chip::CharSpan messageText; Optional> responses; @@ -21573,7 +21573,7 @@ struct Type MessagePriorityEnum priority = static_cast(0); chip::BitMask messageControl = static_cast>(0); DataModel::Nullable startTime; - DataModel::Nullable duration; + DataModel::Nullable duration; chip::CharSpan messageText; Optional> responses; @@ -21594,7 +21594,7 @@ struct DecodableType MessagePriorityEnum priority = static_cast(0); chip::BitMask messageControl = static_cast>(0); DataModel::Nullable startTime; - DataModel::Nullable duration; + DataModel::Nullable duration; chip::CharSpan messageText; Optional> responses; CHIP_ERROR Decode(TLV::TLVReader & reader); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 3a7a4e981629b6..6191eb2aa3ec2d 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -6837,7 +6837,7 @@ class MessagesPresentMessagesRequest : public ClusterCommand AddArgument("Priority", 0, UINT8_MAX, &mRequest.priority); AddArgument("MessageControl", 0, UINT8_MAX, &mRequest.messageControl); AddArgument("StartTime", 0, UINT32_MAX, &mRequest.startTime); - AddArgument("Duration", 0, UINT16_MAX, &mRequest.duration); + AddArgument("Duration", 0, UINT64_MAX, &mRequest.duration); AddArgument("MessageText", &mRequest.messageText); AddArgument("Responses", &mComplex_Responses, "", Argument::kOptional); ClusterCommand::AddArguments(); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 9a1389e416deb4..60843e78e103e7 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -81043,7 +81043,7 @@ class MessagesPresentMessagesRequest : public ClusterCommand { AddArgument("StartTime", 0, UINT32_MAX, &mRequest.startTime); #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL - AddArgument("Duration", 0, UINT16_MAX, &mRequest.duration); + AddArgument("Duration", 0, UINT64_MAX, &mRequest.duration); #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL AddArgument("MessageText", &mRequest.messageText); @@ -81085,7 +81085,7 @@ class MessagesPresentMessagesRequest : public ClusterCommand { if (mRequest.duration.IsNull()) { params.duration = nil; } else { - params.duration = [NSNumber numberWithUnsignedShort:mRequest.duration.Value()]; + params.duration = [NSNumber numberWithUnsignedLongLong:mRequest.duration.Value()]; } #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL From 9cf74ab126b0f80068a3600ca9f08ebca59a53e0 Mon Sep 17 00:00:00 2001 From: yunhanw-google Date: Fri, 23 Feb 2024 10:30:36 -0800 Subject: [PATCH 07/52] Update logEvent restriction description in doc (#32150) * Update logEvent restriction description in doc * Restyled by prettier-markdown --------- Co-authored-by: Restyled.io --- .../cluster_and_device_type_dev.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md b/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md index 9e34e961ae6d87..ca8e295a1cabf9 100644 --- a/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md +++ b/docs/cluster_and_device_type_dev/cluster_and_device_type_dev.md @@ -238,7 +238,9 @@ pure CommandHandlerInterface implementations. - **MatterReportingAttributeChangeCallback** - **Events** - No direct ember support - - Call LogEvent function in EventLogging.h + - Call LogEvent function in EventLogging.h. Caller has to either lock the + Matter stack lock or queue the event to the Matter event queue when + using LogEvent. #### A note on Dynamic Endpoints From b7a781a66ebfa68d4576c472f0ed3b11b24de0e6 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Fri, 23 Feb 2024 12:14:07 -0800 Subject: [PATCH 08/52] Support for encoding Field IDs with an MC source (#32276) * Support for encoding Field IDs with an MC source * Update src/lib/support/jsontlv/JsonToTlv.h Co-authored-by: Tennessee Carmel-Veilleux * Address review comments * Fix linter * Fix linter * restyle --------- Co-authored-by: Tennessee Carmel-Veilleux --- src/lib/support/jsontlv/JsonToTlv.cpp | 52 ++++++++++--- src/lib/support/jsontlv/JsonToTlv.h | 12 ++- src/lib/support/jsontlv/TlvToJson.cpp | 11 +-- src/lib/support/tests/TestJsonToTlv.cpp | 73 ++++++++++++++++++- src/lib/support/tests/TestJsonToTlvToJson.cpp | 53 +++++++++++--- 5 files changed, 167 insertions(+), 34 deletions(-) diff --git a/src/lib/support/jsontlv/JsonToTlv.cpp b/src/lib/support/jsontlv/JsonToTlv.cpp index 387c5f94594da1..0cc816133be9e7 100644 --- a/src/lib/support/jsontlv/JsonToTlv.cpp +++ b/src/lib/support/jsontlv/JsonToTlv.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -179,26 +180,34 @@ bool CompareByTag(const ElementContext & a, const ElementContext & b) return IsContextTag(a.tag); } -CHIP_ERROR InternalConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId) +// The profileId parameter is used when encoding a tag for a TLV element to specify the profile that the tag belongs to. +// If the vendor ID is zero but the tag ID does not fit within an 8-bit value, the function uses Implicit Profile Tag. +// Here, the kTemporaryImplicitProfileId serves as a default value for cases where no explicit profile ID is provided by +// the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead +// associated with a temporary implicit profile ID (0xFF01). +CHIP_ERROR InternalConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId) { - if (tagNumber <= UINT8_MAX) + uint16_t vendor_id = static_cast(tagNumber >> 16); + uint16_t tag_id = static_cast(tagNumber & 0xFFFF); + + if (vendor_id != 0) { - tag = TLV::ContextTag(static_cast(tagNumber)); + tag = TLV::ProfileTag(vendor_id, /*profileNum=*/0, tag_id); } - else if (tagNumber <= UINT32_MAX) + else if (tag_id <= UINT8_MAX) { - tag = TLV::ProfileTag(profileId, static_cast(tagNumber)); + tag = TLV::ContextTag(static_cast(tagNumber)); } else { - return CHIP_ERROR_INVALID_ARGUMENT; + tag = TLV::ProfileTag(profileId, tagNumber); } return CHIP_NO_ERROR; } CHIP_ERROR ParseJsonName(const std::string name, ElementContext & elementCtx, uint32_t implicitProfileId) { - uint64_t tagNumber = 0; + uint32_t tagNumber = 0; const char * elementType = nullptr; std::vector nameFields = SplitIntoFieldsBySeparator(name, ':'); TLV::Tag tag = TLV::AnonymousTag(); @@ -208,13 +217,27 @@ CHIP_ERROR ParseJsonName(const std::string name, ElementContext & elementCtx, ui if (nameFields.size() == 2) { VerifyOrReturnError(IsUnsignedInteger(nameFields[0]), CHIP_ERROR_INVALID_ARGUMENT); - tagNumber = std::strtoull(nameFields[0].c_str(), nullptr, 10); + + char * endPtr; + errno = 0; + unsigned long result = strtoul(nameFields[0].c_str(), &endPtr, 10); + VerifyOrReturnError(nameFields[0].c_str() != endPtr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((errno != ERANGE && result <= UINT32_MAX), CHIP_ERROR_INVALID_ARGUMENT); + + tagNumber = static_cast(result); elementType = nameFields[1].c_str(); } else if (nameFields.size() == 3) { VerifyOrReturnError(IsUnsignedInteger(nameFields[1]), CHIP_ERROR_INVALID_ARGUMENT); - tagNumber = std::strtoull(nameFields[1].c_str(), nullptr, 10); + + char * endPtr; + errno = 0; + unsigned long result = strtoul(nameFields[1].c_str(), &endPtr, 10); + VerifyOrReturnError(nameFields[1].c_str() != endPtr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((errno != ERANGE && result <= UINT32_MAX), CHIP_ERROR_INVALID_ARGUMENT); + + tagNumber = static_cast(result); elementType = nameFields[2].c_str(); } else @@ -459,10 +482,19 @@ CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer) ElementContext elementCtx; elementCtx.type = { TLV::kTLVType_Structure, false }; + + // Use kTemporaryImplicitProfileId as the default value for cases where no explicit implicit profile ID is provided by + // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead + // associated with a temporary implicit profile ID (0xFF01). + if (writer.ImplicitProfileId == TLV::kProfileIdNotSpecified) + { + writer.ImplicitProfileId = kTemporaryImplicitProfileId; + } + return EncodeTlvElement(json, writer, elementCtx); } -CHIP_ERROR ConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag) +CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag) { return InternalConvertTlvTag(tagNumber, tag); } diff --git a/src/lib/support/jsontlv/JsonToTlv.h b/src/lib/support/jsontlv/JsonToTlv.h index 287f0d7279f645..9b0ff194fa8182 100644 --- a/src/lib/support/jsontlv/JsonToTlv.h +++ b/src/lib/support/jsontlv/JsonToTlv.h @@ -33,10 +33,14 @@ CHIP_ERROR JsonToTlv(const std::string & jsonString, MutableByteSpan & tlv); CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer); /* - * Convert a uint64_t tagNumber to a TLV tag. When tagNumber is less than or equal to UINT8_MAX, - * the tag is encoded using ContextTag. When tagNumber is larger than UINT8_MAX and less than or equal to UINT32_MAX, - * the tag is encoded using an implicit profile tag. + * Convert a uint32_t tagNumber (from MEI) to a TLV tag. + * The upper 16 bits of tag_number represent the vendor_id. + * The lower 16 bits of tag_number represent the tag_id. + * When the MEI prefix encodes a standard/scoped source, the tag is encoded using ContextSpecific tag if tag_id is less than or + * equal to UINT8_MAX, and ImplicitProfile tag if tag_id is larger than UINT8_MAX. When the MEI prefix encodes a manufacturer code, + * the tag is encoded using FullyQualified_6Bytes tag, the Vendor ID SHALL be set to the manufacturer code, the profile number set + * to 0 and the tag number set to the MEI suffix. */ -CHIP_ERROR ConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag); +CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag); } // namespace chip diff --git a/src/lib/support/jsontlv/TlvToJson.cpp b/src/lib/support/jsontlv/TlvToJson.cpp index 9bafeccf431b2c..3169dbb79faa27 100644 --- a/src/lib/support/jsontlv/TlvToJson.cpp +++ b/src/lib/support/jsontlv/TlvToJson.cpp @@ -108,14 +108,12 @@ struct JsonObjectElementContext { if (TLV::ProfileIdFromTag(tag) == implicitProfileId) { - // Explicit assume implicit tags are just things we want - // 32-bit numbers for str = std::to_string(TLV::TagNumFromTag(tag)); } else { - // UNEXPECTED, create a full 64-bit number here - str = std::to_string(TLV::ProfileIdFromTag(tag)) + "/" + std::to_string(TLV::TagNumFromTag(tag)); + uint32_t tagNumber = (static_cast(TLV::VendorIdFromTag(tag)) << 16) | TLV::TagNumFromTag(tag); + str = std::to_string(tagNumber); } } str = str + ":" + GetJsonElementStrFromType(type); @@ -173,11 +171,8 @@ CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj) TLV::Tag tag = reader.GetTag(); VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG); - // Profile tags are expected to be implicit profile tags and they are - // used to encode > 8bit values from json - if (TLV::IsProfileTag(tag)) + if (TLV::IsProfileTag(tag) && TLV::VendorIdFromTag(tag) == 0) { - VerifyOrReturnError(TLV::ProfileIdFromTag(tag) == reader.ImplicitProfileId, CHIP_ERROR_INVALID_TLV_TAG); VerifyOrReturnError(TLV::TagNumFromTag(tag) > UINT8_MAX, CHIP_ERROR_INVALID_TLV_TAG); } diff --git a/src/lib/support/tests/TestJsonToTlv.cpp b/src/lib/support/tests/TestJsonToTlv.cpp index c6f5df36333238..539d2720caf191 100644 --- a/src/lib/support/tests/TestJsonToTlv.cpp +++ b/src/lib/support/tests/TestJsonToTlv.cpp @@ -300,7 +300,7 @@ void Test32BitConvert(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(kImplicitProfileId, 0xFEDCBA98u)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag((4275878552 >> 16) & 0xFFFF, 0, 4275878552 & 0xFFFF)) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, value == 321); @@ -312,6 +312,76 @@ void Test32BitConvert(nlTestSuite * inSuite, void * inContext) // FIXME: implement } +void TestMEIConvert(nlTestSuite * inSuite, void * inContext) +{ + TLV::TLVReader reader; + TLV::TLVType tlvType; + int32_t value = 0; + + // Vendor ID = 1, Tag ID = 0 + { + SetupWriters(); + JsonToTlv("{\"65536:INT\": 321}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(1, 0, 0)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 321); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } + + // Vendor ID = 0xFFFF, Tag ID = 0 + { + SetupWriters(); + JsonToTlv("{\"4294901760:INT\": 123}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(0xFFFF, 0, 0)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 123); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } + + // Vendor ID = 0xFFFF, Tag ID = 0xFFFF + { + SetupWriters(); + JsonToTlv("{\"4294967295:INT\": 123}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(0xFFFF, 0, 0xFFFF)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 123); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } +} + int Initialize(void * apSuite) { VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE); @@ -329,6 +399,7 @@ const nlTest sTests[] = { NL_TEST_DEF("TestConverter", TestConverter), NL_TEST_DEF("Test32BitConvert", Test32BitConvert), + NL_TEST_DEF("TestMEIConvert", TestMEIConvert), NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/lib/support/tests/TestJsonToTlvToJson.cpp b/src/lib/support/tests/TestJsonToTlvToJson.cpp index b14d84ceed4e03..576203461b5384 100644 --- a/src/lib/support/tests/TestJsonToTlvToJson.cpp +++ b/src/lib/support/tests/TestJsonToTlvToJson.cpp @@ -705,7 +705,8 @@ void TestConverter_Array_Empty_ImplicitProfileTag4(nlTestSuite * inSuite, void * NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == - writer.StartContainer(TLV::ProfileTag(kImplicitProfileId, 1000000), TLV::kTLVType_Array, containerType2)); + writer.StartContainer(TLV::ProfileTag((1000000 >> 16) & 0xFFFF, 0, 1000000 & 0xFFFF), TLV::kTLVType_Array, + containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); @@ -1070,7 +1071,8 @@ void TestConverter_Array_Strings(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == - writer.StartContainer(TLV::ProfileTag(kImplicitProfileId, 100000), TLV::kTLVType_Array, containerType2)); + writer.StartContainer(TLV::ProfileTag((100000 >> 16) & 0xFFFF, 0, 100000 & 0xFFFF), TLV::kTLVType_Array, + containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "ABC")); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "Options")); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "more")); @@ -1202,11 +1204,10 @@ void TestConverter_Struct_MixedTags(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::ContextTag(0), TLV::kTLVType_Structure, containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ContextTag(255), static_cast(42))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0x0001u, 0, 0), static_cast(345678))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 256), static_cast(17000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0xFFFFu), static_cast(500000000000))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 65535), static_cast(1))); - NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 65536), static_cast(345678))); - NL_TEST_ASSERT( - gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 4294967295), static_cast(500000000000))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); @@ -1706,7 +1707,7 @@ void TestConverter_TlvToJson_ErrorCases(nlTestSuite * inSuite, void * inContext) uint8_t buf9[32]; writer.Init(buf9); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); - NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xAA55FEED, 234), static_cast(42))); + NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFEED, 234), static_cast(42))); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Finalize()); ByteSpan useFullyQualifiedTag(buf9, writer.GetLengthWritten()); @@ -1773,10 +1774,6 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) " \"UINT\" : 42\n" "}\n"; - std::string invalidNameTagValueTooBig = "{\n" - " \"invalid:4294967296:UINT\" : 42\n" - "}\n"; - std::string invalidNameWithNegativeTag = "{\n" " \"-1:UINT\" : 42\n" "}\n"; @@ -1814,7 +1811,6 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) { arrayElementsWithName, CHIP_ERROR_INTERNAL, "Array Elements With Json Name" }, { invalidNameWithoutTagField, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Without Tag Field" }, { invalidNameWithoutTagField2, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Without Tag Field 2" }, - { invalidNameTagValueTooBig, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Tag Value Larger than UINT32_MAX" }, { invalidNameWithNegativeTag, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name With Negative Tag Value" }, { invalidNameWithInvalidTypeField, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name With Invalid Type Field" }, { invalidBytesBase64Value1, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Base64 Encoding: Invalid Character" }, @@ -1834,6 +1830,40 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) } } +// Full Qualified Profile tags, Unsigned Integer structure: {65536 = 42, 4294901760 = 17000, 4294967295 = 500000000000} +void TestConverter_Struct_MEITags(nlTestSuite * inSuite, void * inContext) +{ + gSuite = inSuite; + + uint8_t buf[256]; + TLV::TLVWriter writer; + TLV::TLVType containerType; + TLV::TLVType containerType2; + + writer.Init(buf); + writer.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::ContextTag(0), TLV::kTLVType_Structure, containerType2)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0), static_cast(17000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0x0001u, 0, 0), static_cast(42))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0xFFFFu), static_cast(500000000000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); + + std::string jsonString = "{\n" + " \"0:STRUCT\" : {\n" + " \"65536:UINT\" : 42,\n" + " \"4294901760:UINT\" : 17000,\n" + " \"4294967295:UINT\" : \"500000000000\"\n" + " }\n" + "}\n"; + + ByteSpan tlvSpan(buf, writer.GetLengthWritten()); + CheckValidConversion(jsonString, tlvSpan, jsonString); +} + int Initialize(void * apSuite) { VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE); @@ -1899,6 +1929,7 @@ const nlTest sTests[] = { NL_TEST_DEF("Test Json Tlv Converter - Complex Structure from the README File", TestConverter_Structure_FromReadme), NL_TEST_DEF("Test Json Tlv Converter - Tlv to Json Error Cases", TestConverter_TlvToJson_ErrorCases), NL_TEST_DEF("Test Json Tlv Converter - Json To Tlv Error Cases", TestConverter_JsonToTlv_ErrorCases), + NL_TEST_DEF("Test Json Tlv Converter - Structure with MEI Elements", TestConverter_Struct_MEITags), NL_TEST_SENTINEL() }; From 1d429eaba679f73d2ee06ea5c688344dd9048468 Mon Sep 17 00:00:00 2001 From: chrisdecenzo <61757564+chrisdecenzo@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:32:42 -0800 Subject: [PATCH 09/52] TV Android Sample App: Dialogs for new commissioning feedback and message cluster (#32281) * Dialogs for new commissioning feedback and message cluster * Address comments * Restyle TV Android Sample App: Dialogs for new commissioning feedback and message cluster (#32282) * Restyled by whitespace * Restyled by google-java-format --------- Co-authored-by: Restyled.io --------- Co-authored-by: restyled-io[bot] <32688539+restyled-io[bot]@users.noreply.github.com> Co-authored-by: Restyled.io --- .../server/MatterCommissioningPrompter.java | 311 ++++++++++++++---- .../tv-app/android/java/MessagesManager.cpp | 6 +- .../android/java/MyUserPrompter-JNI.cpp | 165 ++++++++-- .../tv-app/android/java/MyUserPrompter-JNI.h | 3 + .../java/MyUserPrompterResolver-JNI.cpp | 35 ++ .../tv/server/tvapp/MessagesManagerStub.java | 19 +- .../matter/tv/server/tvapp/UserPrompter.java | 31 ++ .../tv/server/tvapp/UserPrompterResolver.java | 16 + .../linux/CastingShellCommands.cpp | 7 + .../CommissionerDiscoveryController.cpp | 4 +- 10 files changed, 507 insertions(+), 90 deletions(-) diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java index 29690a408c562f..7879af41158438 100644 --- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java @@ -12,6 +12,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.app.NotificationCompat; import com.matter.tv.server.service.MatterServant; +import com.matter.tv.server.tvapp.Message; import com.matter.tv.server.tvapp.UserPrompter; import com.matter.tv.server.tvapp.UserPrompterResolver; @@ -26,6 +27,7 @@ public class MatterCommissioningPrompter extends UserPrompterResolver implements public MatterCommissioningPrompter(Context context) { this.context = context; this.createNotificationChannel(); + setUserPrompter(this); } private Activity getActivity() { @@ -34,7 +36,6 @@ private Activity getActivity() { public void promptForCommissionOkPermission( int vendorId, int productId, String commissioneeName) { - // TODO: find app by vendorId and productId Log.d( TAG, "Received prompt for OK permission vendor id:" @@ -43,28 +44,33 @@ public void promptForCommissionOkPermission( + productId + ". Commissionee: " + commissioneeName); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - builder - .setMessage(commissioneeName + " is requesting permission to cast to this device, approve?") - .setTitle("Allow access to " + commissioneeName) - .setPositiveButton( - "Ok", - (dialog, which) -> { - OnPromptAccepted(); - }) - .setNegativeButton( - "Cancel", - (dialog, which) -> { - OnPromptDeclined(); - }) - .create() - .show(); + + getActivity() + .runOnUiThread( + () -> { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder + .setMessage( + commissioneeName + + " is requesting permission to cast to this device, approve?") + .setTitle("Allow access to " + commissioneeName) + .setPositiveButton( + "Ok", + (dialog, which) -> { + OnPromptAccepted(); + }) + .setNegativeButton( + "Cancel", + (dialog, which) -> { + OnPromptDeclined(); + }) + .create() + .show(); + }); } @Override public void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName) { - // TODO: find app by vendorId and productId Log.d( TAG, "Received prompt for PIN code vendor id:" @@ -73,26 +79,143 @@ public void promptForCommissionPinCode(int vendorId, int productId, String commi + productId + ". Commissionee: " + commissioneeName); - EditText editText = new EditText(getActivity()); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - builder - .setMessage("Please enter PIN displayed in casting app.") - .setTitle("Allow access to " + commissioneeName) - .setView(editText) - .setPositiveButton( - "Ok", - (dialog, which) -> { - String pinCode = editText.getText().toString(); - OnPinCodeEntered(Integer.parseInt(pinCode)); - }) - .setNegativeButton( - "Cancel", - (dialog, which) -> { - OnPinCodeDeclined(); - }) - .create() - .show(); + + getActivity() + .runOnUiThread( + () -> { + EditText editText = new EditText(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder + .setMessage("Please enter PIN displayed in casting app.") + .setTitle("Allow access to " + commissioneeName) + .setView(editText) + .setPositiveButton( + "Ok", + (dialog, which) -> { + String pinCode = editText.getText().toString(); + OnPinCodeEntered(Integer.parseInt(pinCode)); + }) + .setNegativeButton( + "Cancel", + (dialog, which) -> { + OnPinCodeDeclined(); + }) + .create() + .show(); + }); + } + + public void hidePromptsOnCancel(int vendorId, int productId, String commissioneeName) { + Log.d( + TAG, + "Received Cancel from vendor id:" + + vendorId + + " productId:" + + productId + + ". Commissionee: " + + commissioneeName); + + getActivity() + .runOnUiThread( + () -> { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage("Cancelled connection to " + commissioneeName) + .setTitle("Connection Cancelled") + .create() + .show(); + + NotificationCompat.Builder builder = + new NotificationCompat.Builder(getActivity(), CHANNEL_ID) + .setSmallIcon(R.drawable.ic_baseline_check_24) + .setContentTitle("Connection Cancelled") + .setContentText("Cancelled connection to " + commissioneeName) + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + + notificationManager.notify(SUCCESS_ID, builder.build()); + }); + } + + public void promptWithCommissionerPasscode( + int vendorId, + int productId, + String commissioneeName, + long passcode, + int pairingHint, + String pairingInstruction) { + Log.d( + TAG, + "Received prompt for Commissioner Passcode:" + + passcode + + " vendor id:" + + vendorId + + " productId:" + + productId + + ". Commissionee: " + + commissioneeName); + + getActivity() + .runOnUiThread( + () -> { + EditText editText = new EditText(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder + .setMessage( + "Please enter " + + passcode + + " in " + + commissioneeName + + " app. " + + pairingInstruction + + " [" + + pairingHint + + "]") + .setTitle("Passcode" + passcode) + .setPositiveButton( + "Ok", + (dialog, which) -> { + OnCommissionerPasscodeOK(); + }) + .setNegativeButton( + "Cancel", + (dialog, which) -> { + OnCommissionerPasscodeCancel(); + }) + .create() + .show(); + }); + } + + public void promptCommissioningStarted(int vendorId, int productId, String commissioneeName) { + Log.d( + TAG, + "Received prompt for started vendor id:" + + vendorId + + " productId:" + + productId + + ". Commissionee: " + + commissioneeName); + getActivity() + .runOnUiThread( + () -> { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage("Starting connection to " + commissioneeName) + .setTitle("Connection Starting") + .create() + .show(); + + NotificationCompat.Builder builder = + new NotificationCompat.Builder(getActivity(), CHANNEL_ID) + .setSmallIcon(R.drawable.ic_baseline_check_24) + .setContentTitle("Connection Starting") + .setContentText("Starting connection to " + commissioneeName) + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + + notificationManager.notify(SUCCESS_ID, builder.build()); + }); } public void promptCommissioningSucceeded(int vendorId, int productId, String commissioneeName) { @@ -104,35 +227,105 @@ public void promptCommissioningSucceeded(int vendorId, int productId, String com + productId + ". Commissionee: " + commissioneeName); - NotificationCompat.Builder builder = - new NotificationCompat.Builder(getActivity(), CHANNEL_ID) - .setSmallIcon(R.drawable.ic_baseline_check_24) - .setContentTitle("Connection Complete") - .setContentText( - "Success. " - + commissioneeName - + " can now cast to this device. Visit settings to manage access control for casting.") - .setPriority(NotificationCompat.PRIORITY_DEFAULT); - - notificationManager.notify(SUCCESS_ID, builder.build()); + getActivity() + .runOnUiThread( + () -> { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage( + "Success. " + + commissioneeName + + " can now cast to this device. Visit settings to manage access control for casting.") + .setTitle("Connection Complete") + .create() + .show(); + + NotificationCompat.Builder builder = + new NotificationCompat.Builder(getActivity(), CHANNEL_ID) + .setSmallIcon(R.drawable.ic_baseline_check_24) + .setContentTitle("Connection Complete") + .setContentText( + "Success. " + + commissioneeName + + " can now cast to this device. Visit settings to manage access control for casting.") + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + + notificationManager.notify(SUCCESS_ID, builder.build()); + }); } public void promptCommissioningFailed(String commissioneeName, String error) { Log.d(TAG, "Received prompt for failure Commissionee: " + commissioneeName); - NotificationCompat.Builder builder = - new NotificationCompat.Builder(getActivity(), CHANNEL_ID) - .setSmallIcon(R.drawable.ic_baseline_clear_24) - .setContentTitle("Connection Failed") - .setContentText("Failed. " + commissioneeName + " experienced error: " + error + ".") - .setPriority(NotificationCompat.PRIORITY_DEFAULT); - - notificationManager.notify(FAIL_ID, builder.build()); + getActivity() + .runOnUiThread( + () -> { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage("Failed. " + commissioneeName + " experienced error: " + error + ".") + .setTitle("Connection Failed") + .create() + .show(); + + NotificationCompat.Builder builder = + new NotificationCompat.Builder(getActivity(), CHANNEL_ID) + .setSmallIcon(R.drawable.ic_baseline_clear_24) + .setContentTitle("Connection Failed") + .setContentText( + "Failed. " + commissioneeName + " experienced error: " + error + ".") + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + + notificationManager.notify(FAIL_ID, builder.build()); + }); + } + + public void promptWithMessage(Message message) { + Log.d(TAG, "Received message prompt for " + message.messageText); + getActivity() + .runOnUiThread( + () -> { + if (message.responseOptions.length != 2) { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage("" + message.messageId + ":" + message.messageText) + .setTitle("New Message from Test") + .setPositiveButton( + "Ok", + (dialog, which) -> { + OnMessageResponse(message.messageId, 0); // ack + }) + .setNegativeButton( + "Ignore", + (dialog, which) -> { + OnMessageResponse(message.messageId, -1); // ignore + }) + .create() + .show(); + } else { + AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity()); + abuilder + .setMessage("" + message.messageId + ":" + message.messageText) + .setTitle("New Message from Test") + .setPositiveButton( + message.responseOptions[0].label, + (dialog, which) -> { + OnMessageResponse(message.messageId, message.responseOptions[0].id); + }) + .setNegativeButton( + message.responseOptions[1].label, + (dialog, which) -> { + OnMessageResponse(message.messageId, message.responseOptions[1].id); + }) + .create() + .show(); + } + }); } private void createNotificationChannel() { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Log.d(TAG, " ------------- createNotificationChannel"); CharSequence name = "MatterPromptNotificationChannel"; String description = "Matter Channel for sending notifications"; int importance = NotificationManager.IMPORTANCE_DEFAULT; @@ -142,6 +335,8 @@ private void createNotificationChannel() { // or other notification behaviors after this this.notificationManager = getSystemService(context, NotificationManager.class); notificationManager.createNotificationChannel(channel); + } else { + Log.d(TAG, " ------------- NOT createNotificationChannel"); } } } diff --git a/examples/tv-app/android/java/MessagesManager.cpp b/examples/tv-app/android/java/MessagesManager.cpp index dbcb5c5994504b..680c96b3a34807 100644 --- a/examples/tv-app/android/java/MessagesManager.cpp +++ b/examples/tv-app/android/java/MessagesManager.cpp @@ -382,11 +382,11 @@ CHIP_ERROR MessagesManager::HandlePresentMessagesRequest( return CHIP_ERROR_INTERNAL; } - jobject jlong = env->NewObject(longClass, longCtor, response.messageResponseID.Value()); - VerifyOrReturnError(jlong != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Could not create Long")); + jobject jlongobj = env->NewObject(longClass, longCtor, static_cast(response.messageResponseID.Value())); + VerifyOrReturnError(jlongobj != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Could not create Long")); // add to HashMap - env->CallObjectMethod(joptions, hashMapPut, jlong, jlabel); + env->CallObjectMethod(joptions, hashMapPut, jlongobj, jlabel); if (env->ExceptionCheck()) { ChipLogError(DeviceLayer, "Java exception in MessagesManager::HandlePresentMessagesRequest"); diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp index 82b06e210d7853..6d586d99c1c67f 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp @@ -51,6 +51,29 @@ JNIMyUserPrompter::JNIMyUserPrompter(jobject provider) env->ExceptionClear(); } + mHidePromptsOnCancelMethod = env->GetMethodID(JNIMyUserPrompterClass, "hidePromptsOnCancel", "(IILjava/lang/String;)V"); + if (mHidePromptsOnCancelMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'hidePromptsOnCancel' method"); + env->ExceptionClear(); + } + + mPromptWithCommissionerPasscodeMethod = + env->GetMethodID(JNIMyUserPrompterClass, "promptWithCommissionerPasscode", "(IILjava/lang/String;JILjava/lang/String;)V"); + if (mPromptWithCommissionerPasscodeMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptWithCommissionerPasscode' method"); + env->ExceptionClear(); + } + + mPromptCommissioningStartedMethod = + env->GetMethodID(JNIMyUserPrompterClass, "promptCommissioningStarted", "(IILjava/lang/String;)V"); + if (mPromptCommissioningStartedMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptCommissioningStarted' method"); + env->ExceptionClear(); + } + mPromptCommissioningSucceededMethod = env->GetMethodID(JNIMyUserPrompterClass, "promptCommissioningSucceeded", "(IILjava/lang/String;)V"); if (mPromptCommissioningSucceededMethod == nullptr) @@ -81,17 +104,18 @@ void JNIMyUserPrompter::PromptForCommissionOKPermission(uint16_t vendorId, uint1 DeviceLayer::StackUnlock unlock; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - std::string stringCommissioneeName(commissioneeName); VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mPromptForCommissionOKPermissionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); { - UtfString jniCommissioneeName(env, stringCommissioneeName.data()); + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + env->ExceptionClear(); env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptForCommissionOKPermissionMethod, - static_cast(vendorId), static_cast(productId), jniCommissioneeName.jniValue()); + static_cast(vendorId), static_cast(productId), jcommissioneeName); if (env->ExceptionCheck()) { ChipLogError(DeviceLayer, "Java exception in PromptForCommissionOKPermission"); @@ -123,17 +147,18 @@ void JNIMyUserPrompter::PromptForCommissionPasscode(uint16_t vendorId, uint16_t DeviceLayer::StackUnlock unlock; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - std::string stringCommissioneeName(commissioneeName); VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mPromptForCommissionPincodeMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); { - UtfString jniCommissioneeName(env, stringCommissioneeName.data()); + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + env->ExceptionClear(); env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptForCommissionPincodeMethod, static_cast(vendorId), - static_cast(productId), jniCommissioneeName.jniValue()); + static_cast(productId), jcommissioneeName); if (env->ExceptionCheck()) { ChipLogError(Zcl, "Java exception in PromptForCommissionPincode"); @@ -158,8 +183,38 @@ void JNIMyUserPrompter::PromptForCommissionPasscode(uint16_t vendorId, uint16_t */ void JNIMyUserPrompter::HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) { - // TODO - ChipLogError(Zcl, "JNIMyUserPrompter::HidePromptsOnCancel Needs Implementation"); + ChipLogError(Zcl, "JNIMyUserPrompter::HidePromptsOnCancel"); + + DeviceLayer::StackUnlock unlock; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mPromptForCommissionOKPermissionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + + { + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + + env->ExceptionClear(); + env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mHidePromptsOnCancelMethod, static_cast(vendorId), + static_cast(productId), jcommissioneeName); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in HidePromptsOnCancel"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "HidePromptsOnCancel error: %s", err.AsString()); + } } /** @@ -168,7 +223,6 @@ void JNIMyUserPrompter::HidePromptsOnCancel(uint16_t vendorId, uint16_t productI */ bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode() { - // TODO ChipLogError(Zcl, "JNIMyUserPrompter::DisplaysPasscodeAndQRCode Needs Implementation"); return false; } @@ -182,8 +236,42 @@ bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode() void JNIMyUserPrompter::PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) { - // TODO - ChipLogError(Zcl, "JNIMyUserPrompter::PromptWithCommissionerPasscode Needs Implementation"); + ChipLogError(Zcl, "JNIMyUserPrompter::PromptWithCommissionerPasscode"); + + DeviceLayer::StackUnlock unlock; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mPromptForCommissionOKPermissionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + + { + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + + jstring jpairingInstruction = env->NewStringUTF(pairingInstruction); + VerifyOrExit(jpairingInstruction != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + + env->ExceptionClear(); + env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptWithCommissionerPasscodeMethod, + static_cast(vendorId), static_cast(productId), jcommissioneeName, + static_cast(passcode), static_cast(pairingHint), jpairingInstruction); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in PromptWithCommissionerPasscode"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "PromptWithCommissionerPasscode error: %s", err.AsString()); + } } /** @@ -191,8 +279,38 @@ void JNIMyUserPrompter::PromptWithCommissionerPasscode(uint16_t vendorId, uint16 */ void JNIMyUserPrompter::PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) { - // TODO - ChipLogError(Zcl, "JNIMyUserPrompter::PromptCommissioningStarted Needs Implementation"); + ChipLogError(Zcl, "JNIMyUserPrompter::PromptCommissioningStarted"); + + DeviceLayer::StackUnlock unlock; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mPromptForCommissionOKPermissionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + + { + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + + env->ExceptionClear(); + env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptCommissioningStartedMethod, static_cast(vendorId), + static_cast(productId), jcommissioneeName); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in PromptCommissioningStarted"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "PromptCommissioningStarted error: %s", err.AsString()); + } } /* @@ -203,17 +321,18 @@ void JNIMyUserPrompter::PromptCommissioningSucceeded(uint16_t vendorId, uint16_t DeviceLayer::StackUnlock unlock; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - std::string stringCommissioneeName(commissioneeName); VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mPromptCommissioningSucceededMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); { - UtfString jniCommissioneeName(env, stringCommissioneeName.data()); + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + env->ExceptionClear(); env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptCommissioningSucceededMethod, static_cast(vendorId), - static_cast(productId), jniCommissioneeName.jniValue()); + static_cast(productId), jcommissioneeName); if (env->ExceptionCheck()) { @@ -240,19 +359,21 @@ void JNIMyUserPrompter::PromptCommissioningFailed(const char * commissioneeName, DeviceLayer::StackUnlock unlock; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - std::string stringCommissioneeName(commissioneeName); VerifyOrExit(mJNIMyUserPrompterObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mPromptCommissioningFailedMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); { - std::string stringError(error.AsString()); - UtfString jniCommissioneeName(env, stringCommissioneeName.data()); - UtfString jniCommissioneeError(env, stringError.data()); + jstring jcommissioneeError = env->NewStringUTF(error.AsString()); + VerifyOrExit(jcommissioneeError != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + + jstring jcommissioneeName = env->NewStringUTF(commissioneeName); + VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL); + env->ExceptionClear(); - env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptCommissioningFailedMethod, jniCommissioneeName.jniValue(), - jniCommissioneeError.jniValue()); + env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptCommissioningFailedMethod, jcommissioneeName, + jcommissioneeError); if (env->ExceptionCheck()) { diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.h b/examples/tv-app/android/java/MyUserPrompter-JNI.h index 03fb88d2fd7887..408346326a6fd6 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.h +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.h @@ -41,6 +41,9 @@ class JNIMyUserPrompter : public UserPrompter chip::JniGlobalReference mJNIMyUserPrompterObject; jmethodID mPromptForCommissionOKPermissionMethod = nullptr; jmethodID mPromptForCommissionPincodeMethod = nullptr; + jmethodID mHidePromptsOnCancelMethod = nullptr; + jmethodID mPromptWithCommissionerPasscodeMethod = nullptr; + jmethodID mPromptCommissioningStartedMethod = nullptr; jmethodID mPromptCommissioningSucceededMethod = nullptr; jmethodID mPromptCommissioningFailedMethod = nullptr; }; diff --git a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp index 2e6bd0aa1f54e3..94f56f98e49db5 100644 --- a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp +++ b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp @@ -64,3 +64,38 @@ JNI_METHOD(void, OnPromptDeclined)(JNIEnv *, jobject) GetCommissionerDiscoveryController()->Cancel(); #endif } + +JNI_METHOD(void, OnCommissionerPasscodeOK)(JNIEnv *, jobject) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + chip::DeviceLayer::StackLock lock; + ChipLogProgress(Zcl, "OnCommissionerPasscodeOK"); + // GetCommissionerDiscoveryController()->Ok(); +#endif +} + +JNI_METHOD(void, OnCommissionerPasscodeCancel)(JNIEnv *, jobject) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + chip::DeviceLayer::StackLock lock; + ChipLogProgress(Zcl, "OnCommissionerPasscodeCancel"); + GetCommissionerDiscoveryController()->Cancel(); +#endif +} + +JNI_METHOD(void, OnMessageResponse)(JNIEnv * env, jobject, jstring jMessageId, jlong jOptionId) +{ + chip::DeviceLayer::StackLock lock; + uint32_t optionid = static_cast(jOptionId); + ChipLogProgress(Zcl, "OnMessageResponse option id: %u", optionid); + + JniUtfString messageId(env, jMessageId); + if (jMessageId != nullptr) + { + ChipLogProgress(Zcl, "OnMessageResponse message id: %s", messageId.c_str()); + } + else + { + ChipLogProgress(Zcl, "OnMessageResponse message id null"); + } +} diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java index 55d94209197559..054218a78c6a54 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java @@ -61,15 +61,24 @@ public boolean presentMessages( int i = 0; for (Map.Entry set : responseOptions.entrySet()) { - Log.d(TAG, "presentMessages option: key:" + set.getKey() + " value:" + set.getValue()); + Log.d( + TAG, + "presentMessages option: key:" + set.getKey().longValue() + " value:" + set.getValue()); options[i] = new MessageResponseOption(set.getKey().longValue(), set.getValue()); i++; } - messages.put( - messageId, - new Message( - messageId, priority, messageControl, startTime, duration, messageText, options)); + Message message = + new Message(messageId, priority, messageControl, startTime, duration, messageText, options); + messages.put(messageId, message); + + UserPrompter prompter = UserPrompterResolver.getUserPrompter(); + if (prompter != null) { + prompter.promptWithMessage(message); + } else { + Log.d(TAG, "presentMessages no global user prompter"); + } + return true; } diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java index d0aeb5c97df412..dc6fb34ba61261 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java @@ -39,6 +39,32 @@ public interface UserPrompter { */ void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName); + /** + * Called to when CancelCommissioning is received via UDC. Indicates that commissioner can stop + * showing the passcode entry or display dialog. For example, can show text such as "Commissioning + * cancelled by client" before hiding dialog. + */ + void hidePromptsOnCancel(int vendorId, int productId, String commissioneeName); + + /** + * Called to display the given setup passcode to the user, for commissioning the given + * commissioneeName with the given vendorId and productId, and provide instructions for where to + * enter it in the commissionee (when pairingHint and pairingInstruction are provided). For + * example "Casting Passcode: [passcode]. For more instructions, click here." + */ + void promptWithCommissionerPasscode( + int vendorId, + int productId, + String commissioneeName, + long passcode, + int pairingHint, + String pairingInstruction); + + /* + * Called to notify the user that commissioning succeeded. It can be in form of UI Notification. + */ + void promptCommissioningStarted(int vendorId, int productId, String commissioneeName); + /* * Called to notify the user that commissioning succeeded. It can be in form of UI Notification. */ @@ -48,4 +74,9 @@ public interface UserPrompter { * Called to notify the user that commissioning succeeded. It can be in form of UI Notification. */ void promptCommissioningFailed(String commissioneeName, String error); + + /* + * Called to display a message on the screen + */ + void promptWithMessage(Message message); } diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompterResolver.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompterResolver.java index f90c18aba1648a..4d81173aca6f9d 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompterResolver.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompterResolver.java @@ -29,7 +29,23 @@ public class UserPrompterResolver { public native void OnPromptDeclined(); + public native void OnCommissionerPasscodeOK(); + + public native void OnCommissionerPasscodeCancel(); + + public native void OnMessageResponse(String messageId, long optionid); + static { System.loadLibrary("TvApp"); } + + static UserPrompter globalPrompter = null; + + public static void setUserPrompter(UserPrompter prompter) { + globalPrompter = prompter; + } + + public static UserPrompter getUserPrompter() { + return globalPrompter; + } } diff --git a/examples/tv-casting-app/linux/CastingShellCommands.cpp b/examples/tv-casting-app/linux/CastingShellCommands.cpp index f6b245b42dc13a..bf20ccdec73b7f 100644 --- a/examples/tv-casting-app/linux/CastingShellCommands.cpp +++ b/examples/tv-casting-app/linux/CastingShellCommands.cpp @@ -192,6 +192,13 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) { id.SetPairingInst(argv[5]); } + if (argc > 6) + { + uint16_t vid = (uint16_t) strtol(argv[6], &eptr, 10); + Protocols::UserDirectedCommissioning::TargetAppInfo info; + info.vendorId = vid; + id.AddTargetAppInfo(info); + } return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), id); } diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp index c1c6492636ecd0..86849a32f91271 100644 --- a/src/controller/CommissionerDiscoveryController.cpp +++ b/src/controller/CommissionerDiscoveryController.cpp @@ -47,7 +47,7 @@ void CommissionerDiscoveryController::OnUserDirectedCommissioningRequest(UDCClie { if (!mReady) { - ChipLogDetail(Controller, "CommissionerDiscoveryController not read. Current instance=%s", mCurrentInstance); + ChipLogDetail(Controller, "CommissionerDiscoveryController not ready. Current instance=%s", mCurrentInstance); return; } // first check if this is a cancel @@ -163,7 +163,7 @@ void CommissionerDiscoveryController::Ok() } } // handle NoAppsFound CDC case - if (!hasTargetApp) + if (!hasTargetApp && client->GetNoPasscode()) { ChipLogError(AppServer, "UX Ok: target apps specified but none found, sending CDC"); CommissionerDeclaration cd; From 22bb7373669171c47374e85729da3bda465ca0c0 Mon Sep 17 00:00:00 2001 From: Robert Szewczyk Date: Fri, 23 Feb 2024 14:15:47 -0800 Subject: [PATCH 10/52] Fix a a few gaps in jumping between Java and C++ for Messages cluster (#32295) Fix dangling problems arising from uint16 seconds for duration to uint64_t milliseconds --- examples/tv-app/android/java/MessagesManager.cpp | 4 ++-- .../src/com/matter/tv/server/tvapp/MessagesManagerStub.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/tv-app/android/java/MessagesManager.cpp b/examples/tv-app/android/java/MessagesManager.cpp index 680c96b3a34807..ad214e117d33d8 100644 --- a/examples/tv-app/android/java/MessagesManager.cpp +++ b/examples/tv-app/android/java/MessagesManager.cpp @@ -74,7 +74,7 @@ void MessagesManager::InitializeWithObjects(jobject managerObject) } mPresentMessagesMethod = - env->GetMethodID(managerClass, "presentMessages", "(Ljava/lang/String;IIJILjava/lang/String;Ljava/util/HashMap;)Z"); + env->GetMethodID(managerClass, "presentMessages", "(Ljava/lang/String;IIJJLjava/lang/String;Ljava/util/HashMap;)Z"); if (mPresentMessagesMethod == nullptr) { ChipLogError(Zcl, "Failed to access MessagesManager 'presentMessages' method"); @@ -182,7 +182,7 @@ CHIP_ERROR MessagesManager::HandleGetMessages(AttributeValueEncoder & aEncoder) message.startTime = DataModel::Nullable(static_cast(jstartTime)); } - jfieldID durationField = env->GetFieldID(messageClass, "duration", "I"); + jfieldID durationField = env->GetFieldID(messageClass, "duration", "J"); jlong jduration = env->GetLongField(messageObject, durationField); if (jduration >= 0) { diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java index 054218a78c6a54..b31f14a112304b 100644 --- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java +++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/MessagesManagerStub.java @@ -36,7 +36,7 @@ public MessagesManagerStub(int endpoint) { responseOptions.put(new Long(1), "Yes"); responseOptions.put(new Long(2), "No"); presentMessages( - "31323334353637383930313233343536", 1, 1, 30, 60, "TestMessage", responseOptions); + "31323334353637383930313233343536", 1, 1, 30, 60000, "TestMessage", responseOptions); Log.d(TAG, "MessagesManagerStub: added dummy message"); } From c5a07207ea37df369e3685bb9c67fc5b9a87b732 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 23 Feb 2024 23:25:50 -0500 Subject: [PATCH 11/52] DM XMLs: Remove some in-progress that are not going to cert. (#32294) Removing LIT-icd, aliro, per device credentials --- data_model/clusters/DoorLock.xml | 181 +----------------- data_model/clusters/ICDManagement.xml | 38 +--- .../clusters/NetworkCommissioningCluster.xml | 80 +------- 3 files changed, 13 insertions(+), 286 deletions(-) diff --git a/data_model/clusters/DoorLock.xml b/data_model/clusters/DoorLock.xml index 6284b973eb63e1..16d26420fe2009 100644 --- a/data_model/clusters/DoorLock.xml +++ b/data_model/clusters/DoorLock.xml @@ -57,7 +57,7 @@ Davis, CA 95616, USA :xrefstyle: basic --> - + @@ -66,7 +66,6 @@ Davis, CA 95616, USA - @@ -104,19 +103,14 @@ Davis, CA 95616, USA - - - - - - - - - - - - - + + + + + + + + @@ -130,14 +124,6 @@ Davis, CA 95616, USA - - - - - - - - @@ -220,23 +206,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - @@ -370,21 +339,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - @@ -533,11 +487,6 @@ Davis, CA 95616, USA - - - - - @@ -1109,81 +1058,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2059,13 +1933,6 @@ Davis, CA 95616, USA - - - - - - - @@ -2092,36 +1959,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data_model/clusters/ICDManagement.xml b/data_model/clusters/ICDManagement.xml index 2a770a4f97f2b4..2de6842d1e0dd9 100644 --- a/data_model/clusters/ICDManagement.xml +++ b/data_model/clusters/ICDManagement.xml @@ -57,10 +57,9 @@ Davis, CA 95616, USA // Update Name --> - + - @@ -69,26 +68,12 @@ Davis, CA 95616, USA - - - - - - - - - - - + - - - - @@ -153,25 +138,6 @@ Davis, CA 95616, USA - - - - - - - - - - - - - - - - - - - diff --git a/data_model/clusters/NetworkCommissioningCluster.xml b/data_model/clusters/NetworkCommissioningCluster.xml index 86c7a01ce9f951..745a659f1b900b 100644 --- a/data_model/clusters/NetworkCommissioningCluster.xml +++ b/data_model/clusters/NetworkCommissioningCluster.xml @@ -59,8 +59,7 @@ Davis, CA 95616, USA - + @@ -76,11 +75,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - @@ -177,9 +171,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - @@ -189,22 +180,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - - - - - @@ -431,18 +406,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - @@ -492,18 +455,6 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - @@ -559,32 +510,5 @@ Added support for Wi-Fi Per-Device Credentials (PDC feature; QueryIdentity and Q - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From 8f2a4d227839637d7b3244c4d865542a0f443a66 Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Mon, 26 Feb 2024 03:48:13 +0530 Subject: [PATCH 12/52] Framework for emitting metrics data (#32223) * Rerouted tracing macros to Darwin signposts * Initial framework for logging scalar data event * Handled the new metrics event changes in collector * Modified VerifyOrExit macro to accept an optional metric key as third argument * Removed direct use of chrono in metrics_event.h Switched MTRMetrics to vend dictionary for metric keys * Modified SuccessOrExit to optionally accept metric key * Moved metric keys to separate header Reworked metric_event names for more clarity * Switched MATTER_TRACE_METRIC usage to MATTER_LOG_METRIC * Restyle fixes * Fixed unit tests * Fixed build failure due to MetricEvent hidden inside tracing enabled * Fixing one source of build error * Fixing darwin build failure * Code Review: Rename LogMetric to LogMetricEvent * Code Review Suggestions: 1. Metric Macros take full string constants and no longer use preprocessor to prefix. Allows free flowing strings 2. Reworked MetricEvent class and documented 3. Handled LogEventMetric for Darwin, ESP32, Perfetto, JSON to account for all types 4. Removed timePoint from MetricEvent class. Timestamps and duration calculation is now responsibility for the handlers of the event 5. Reverted BUILD.gn in system to not break out SystemClock.h * Code Review Feedback #2: 1. Added SuccessOrExitWithMetric and VerifyOrExitWithMetric 2. Cleaned up support .gn to remove dependedency on metrics * Code Review Feedback #3: 1. Added ScopedMetricEvent to use RAII to track begin and end within a scope * Code Review #4: Reverted an accidental removal * Added MTRMetricData to description as per review comment * Restyler fixes * Sample code of how Begin and End log metrics can be used * Fixed compilation error when tracing is disabled * Fixes for build failures when tracing is disabled * Picked up code review suggestion accidently dropped * Code Review Feedback: 1. Begin metric does not take value 2. Allow undefined value for metric 3. Misc other feedback * Handle undefined value and error value * Revert a comment change * Review Feedback: Changed ScopedMetricEvent to capture error by reference * Fixed another build failure * Reverting usage of LOG_METRICS * Review feedback: Fix incorrect documentation * Code Review Feedback: Remove access to Value in MetricEvent to avoid incorrect access * Restyler fixes * Unregistering backend in Darwin shutdown * Resytler fixes... --- .../wifi-network-diagnostics-server.cpp | 3 +- .../CHIP/MTRDeviceControllerDelegateBridge.mm | 14 +- .../CHIP/MTRDeviceControllerFactory.mm | 7 + src/darwin/Framework/CHIP/MTRFramework.mm | 4 + src/darwin/Framework/CHIP/MTRMetrics.h | 16 +- src/darwin/Framework/CHIP/MTRMetrics.mm | 10 +- .../Framework/CHIP/MTRMetricsCollector.h | 59 ++++ .../Framework/CHIP/MTRMetricsCollector.mm | 256 ++++++++++++++++++ .../Framework/CHIP/MTRMetrics_Internal.h | 22 +- .../Framework/CHIPTests/MTRMetricsTests.m | 12 +- .../Matter.xcodeproj/project.pbxproj | 10 + src/lib/support/CodeUtils.h | 8 +- src/platform/Darwin/BUILD.gn | 13 + src/platform/Darwin/Tracing.h | 77 ++++++ src/platform/Darwin/Tracing.mm | 55 ++++ src/tracing/BUILD.gn | 4 + src/tracing/backend.h | 2 +- src/tracing/darwin/BUILD.gn | 25 ++ .../include/matter/tracing/macros_impl.h | 25 ++ src/tracing/esp32_trace/esp32_tracing.cpp | 33 ++- src/tracing/esp32_trace/esp32_tracing.h | 2 +- .../include/matter/tracing/macros_impl.h | 1 - src/tracing/json/json_tracing.cpp | 27 +- src/tracing/json/json_tracing.h | 2 +- src/tracing/log_declares.h | 1 + src/tracing/macros.h | 5 - src/tracing/metric_event.h | 189 +++++++++++++ src/tracing/metric_keys.h | 44 +++ src/tracing/metric_macros.h | 243 +++++++++++++++++ .../include/matter/tracing/macros_impl.h | 1 - .../none/include/matter/tracing/macros_impl.h | 1 - .../include/matter/tracing/macros_impl.h | 2 - src/tracing/perfetto/perfetto_tracing.cpp | 24 ++ src/tracing/perfetto/perfetto_tracing.h | 1 + src/tracing/registry.cpp | 16 +- src/tracing/registry.h | 2 +- src/tracing/tracing_args.gni | 5 +- 37 files changed, 1167 insertions(+), 54 deletions(-) create mode 100644 src/darwin/Framework/CHIP/MTRMetricsCollector.h create mode 100644 src/darwin/Framework/CHIP/MTRMetricsCollector.mm create mode 100644 src/platform/Darwin/Tracing.h create mode 100644 src/platform/Darwin/Tracing.mm create mode 100644 src/tracing/darwin/BUILD.gn create mode 100644 src/tracing/darwin/include/matter/tracing/macros_impl.h create mode 100644 src/tracing/metric_event.h create mode 100644 src/tracing/metric_keys.h create mode 100644 src/tracing/metric_macros.h diff --git a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp index f116def50c089a..3cf9239ddff59e 100644 --- a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp +++ b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace chip; using namespace chip::app; @@ -163,7 +164,7 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiRssi(AttributeValueEncoder & aEncod { rssi.SetNonNull(value); ChipLogProgress(Zcl, "The current RSSI of the Node’s Wi-Fi radio in dB: %d", value); - MATTER_TRACE_METRIC("wifi_rssi", value); + MATTER_LOG_METRIC(chip::Tracing::kMetricWiFiRSSI, value); } else { diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm index e1c49031e3abb2..04ac68a6dae1fe 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm @@ -17,9 +17,10 @@ #import "MTRDeviceControllerDelegateBridge.h" #import "MTRDeviceController.h" +#import "MTRDeviceController_Internal.h" #import "MTRError_Internal.h" #import "MTRLogging_Internal.h" -#import "MTRMetrics_Internal.h" +#import "MTRMetricsCollector.h" MTRDeviceControllerDelegateBridge::MTRDeviceControllerDelegateBridge(void) : mDelegate(nil) @@ -130,15 +131,10 @@ nodeID = @(nodeId); } + // If the client implements the metrics delegate, prefer that over others if ([strongDelegate respondsToSelector:@selector(controller:commissioningComplete:nodeID:metrics:)]) { - MTRMetrics * metrics = [MTRMetrics new]; - - if (nsError) { - [metrics setValue:nsError forKey:MTRMetricCommissioningStatusKey]; - } else { - auto * error = [NSError errorWithDomain:MTRErrorDomain code:0 userInfo:nil]; - [metrics setValue:error forKey:MTRMetricCommissioningStatusKey]; - } + // Create a snapshot and clear for next operation + MTRMetrics * metrics = [[MTRMetricsCollector sharedInstance] metricSnapshot:TRUE]; [strongDelegate controller:strongController commissioningComplete:nsError nodeID:nodeID metrics:metrics]; } else { [strongDelegate controller:strongController commissioningComplete:nsError nodeID:nodeID]; diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 0430558e8a4b32..d89df5a4b09c4d 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -39,6 +39,7 @@ #import "MTRFabricInfo_Internal.h" #import "MTRFramework.h" #import "MTRLogging_Internal.h" +#import "MTRMetricsCollector.h" #import "MTROTAProviderDelegateBridge.h" #import "MTROperationalBrowser.h" #import "MTRP256KeypairBridge.h" @@ -349,6 +350,8 @@ - (void)cleanupStartupObjects } _diagnosticLogsDownloader = nil; + + ShutdownMetricsCollection(); } - (CHIP_ERROR)_initFabricTable:(FabricTable &)fabricTable @@ -414,6 +417,10 @@ - (BOOL)startControllerFactory:(MTRDeviceControllerFactoryParams *)startupParams return YES; } + // Register any tracing backends. This has to be done before starting the event loop to run registering + // the tracing backend in the right queue context + StartupMetricsCollection(); + DeviceLayer::PlatformMgrImpl().StartEventLoopTask(); __block CHIP_ERROR errorCode = CHIP_NO_ERROR; diff --git a/src/darwin/Framework/CHIP/MTRFramework.mm b/src/darwin/Framework/CHIP/MTRFramework.mm index e6a0e672955be3..3eff8a1b8dbfc3 100644 --- a/src/darwin/Framework/CHIP/MTRFramework.mm +++ b/src/darwin/Framework/CHIP/MTRFramework.mm @@ -15,6 +15,7 @@ */ #import "MTRFramework.h" +#import "MTRMetricsCollector.h" #include #include @@ -34,5 +35,8 @@ void MTRFrameworkInit() // Suppress CHIP logging until we actually need it for redirection // (see MTRSetLogCallback()). Logging to os_log is always enabled. chip::Logging::SetLogFilter(chip::Logging::kLogCategory_None); + + // Startup metrics collection and tracing framework + StartupMetricsCollection(); }); } diff --git a/src/darwin/Framework/CHIP/MTRMetrics.h b/src/darwin/Framework/CHIP/MTRMetrics.h index 81161ee049bedd..483de01df3ebd6 100644 --- a/src/darwin/Framework/CHIP/MTRMetrics.h +++ b/src/darwin/Framework/CHIP/MTRMetrics.h @@ -21,14 +21,28 @@ NS_ASSUME_NONNULL_BEGIN /** - * A representation of metrics data for an operation. + * A representation of a collection of metrics data for an operation. */ MTR_NEWLY_AVAILABLE @interface MTRMetrics : NSObject +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * @brief Returns the names of all the metrics data items collected. + */ @property (nonatomic, readonly, copy) NSArray * allKeys; +/** + * @brief Returns metric object corresponding to the metric identified by its key + * + * @param [in] key Name of the metric + * + * @return An object containing the metric data, nil if key is invalid + */ - (nullable id)valueForKey:(NSString *)key; @end + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRMetrics.mm b/src/darwin/Framework/CHIP/MTRMetrics.mm index c1135c1cfbc0b6..8c338b70285e08 100644 --- a/src/darwin/Framework/CHIP/MTRMetrics.mm +++ b/src/darwin/Framework/CHIP/MTRMetrics.mm @@ -16,16 +16,24 @@ */ #import "MTRLogging_Internal.h" #import "MTRMetrics_Internal.h" +#include #import +#include @implementation MTRMetrics { NSMutableDictionary * _metricsData; } - (instancetype)init +{ + NSAssert(false, @"'init' unavailable, use initWithCapacity: instead"); + return nil; +} + +- (instancetype)initWithCapacity:(NSUInteger)numItems { if (self = [super init]) { - _metricsData = [NSMutableDictionary dictionary]; + _metricsData = [NSMutableDictionary dictionaryWithCapacity:numItems]; } return self; } diff --git a/src/darwin/Framework/CHIP/MTRMetricsCollector.h b/src/darwin/Framework/CHIP/MTRMetricsCollector.h new file mode 100644 index 00000000000000..71d0afc26c039a --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRMetricsCollector.h @@ -0,0 +1,59 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * This function initializes any backend required to collect metrics data. + */ +void StartupMetricsCollection(); + +/** + * This function shuts down any backend created to collect metrics data. + */ +void ShutdownMetricsCollection(); + +/** + * A representation of metrics data for an operation. + */ +@interface MTRMetricsCollector : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Return the singleton MTRMetricsCollector to vend MTRMetrics snapshots + */ ++ (instancetype)sharedInstance; + +/** + * @brief This method creates a snapshot of the metrics collected until the current point in time + * and returns an object with the stats. + * + * @param [in] resetCollection Boolean that specifies whether or not to clear the stats collected after + * creating the snapshot. + * + * @return MTRMetric object representing the metric data. + */ +- (MTRMetrics *)metricSnapshot:(BOOL)resetCollection; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRMetricsCollector.mm b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm new file mode 100644 index 00000000000000..6387738a7c13c8 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm @@ -0,0 +1,256 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 "MTRMetricsCollector.h" +#import "MTRLogging_Internal.h" +#include "MTRMetrics_Internal.h" +#import +#include +#include +#include +#include + +using MetricEvent = chip::Tracing::MetricEvent; + +static NSString * kMTRMetricDataValueKey = @"value"; +static NSString * kMTRMetricDataTimepointKey = @"time_point"; +static NSString * kMTRMetricDataDurationKey = @"duration_us"; + +@implementation MTRMetricsData { + chip::System::Clock::Microseconds64 _timePoint; + chip::System::Clock::Microseconds64 _duration; +} + +- (instancetype)initWithMetricEvent:(const MetricEvent &)event +{ + if (!(self = [super init])) { + return nil; + } + + using ValueType = MetricEvent::Value::Type; + switch (event.ValueType()) { + case ValueType::kInt32: + _value = [NSNumber numberWithInteger:event.ValueInt32()]; + break; + case ValueType::kUInt32: + _value = [NSNumber numberWithInteger:event.ValueUInt32()]; + break; + case ValueType::kChipErrorCode: + _value = [NSNumber numberWithInteger:event.ValueErrorCode()]; + break; + case ValueType::kUndefined: + default: + _value = nil; + } + + _timePoint = chip::System::SystemClock().GetMonotonicMicroseconds64(); + _duration = chip::System::Clock::Microseconds64(0); + return self; +} + +- (void)setDurationFromMetricData:(MTRMetricsData *)fromData +{ + _duration = _timePoint - fromData->_timePoint; +} + +- (NSNumber *)timePointMicroseconds +{ + return [NSNumber numberWithUnsignedLongLong:_timePoint.count()]; +} + +- (NSNumber *)durationMicroseconds +{ + return [NSNumber numberWithUnsignedLongLong:_duration.count()]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"MTRMetricsData: Value = %@, TimePoint = %@, Duration = %@ us", self.value, self.timePointMicroseconds, self.durationMicroseconds]; +} + +- (NSDictionary *)toDictionary +{ + NSMutableDictionary * dictRepresentation = [NSMutableDictionary dictionary]; + if (self.value) { + [dictRepresentation setValue:self.value forKey:kMTRMetricDataValueKey]; + } + if (auto tmPt = self.timePointMicroseconds) { + [dictRepresentation setValue:tmPt forKey:kMTRMetricDataTimepointKey]; + } + if (auto duration = self.durationMicroseconds) { + [dictRepresentation setValue:duration forKey:kMTRMetricDataDurationKey]; + } + return dictRepresentation; +} + +@end + +@interface MTRMetricsCollector () + +- (void)registerTracingBackend; + +- (void)unregisterTracingBackend; + +@end + +void StartupMetricsCollection() +{ + if ([MTRMetricsCollector sharedInstance]) { + MTR_LOG_INFO("Initialized metrics collection backend for Darwin"); + + [[MTRMetricsCollector sharedInstance] registerTracingBackend]; + } +} + +void ShutdownMetricsCollection() +{ + [[MTRMetricsCollector sharedInstance] unregisterTracingBackend]; +} + +@implementation MTRMetricsCollector { + os_unfair_lock _lock; + NSMutableDictionary * _metricsDataCollection; + chip::Tracing::signposts::DarwinTracingBackend _tracingBackend; +} + ++ (instancetype)sharedInstance +{ + static MTRMetricsCollector * singleton = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Initialize the singleton and register the event handler + singleton = [[MTRMetricsCollector alloc] init]; + if (singleton) { + singleton->_tracingBackend.SetMetricEventHandler(^(MetricEvent event) { + if (singleton) { + [singleton handleMetricEvent:event]; + } + }); + } + }); + return singleton; +} + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + _lock = OS_UNFAIR_LOCK_INIT; + _metricsDataCollection = [NSMutableDictionary dictionary]; + return self; +} + +- (void)registerTracingBackend +{ + std::lock_guard lock(_lock); + chip::Tracing::Register(_tracingBackend); + MTR_LOG_INFO("Registered tracing backend with the registry"); +} + +- (void)unregisterTracingBackend +{ + std::lock_guard lock(_lock); + chip::Tracing::Unregister(_tracingBackend); + MTR_LOG_INFO("Unregistered tracing backend with the registry"); +} + +static inline NSString * suffixNameForMetricType(MetricEvent::Type type) +{ + switch (type) { + case MetricEvent::Type::kBeginEvent: + return @"-begin"; + case MetricEvent::Type::kEndEvent: + return @"-end"; + case MetricEvent::Type::kInstantEvent: + return @"-instant"; + } +} + +static inline NSString * suffixNameForMetric(const MetricEvent & event) +{ + return suffixNameForMetricType(event.type()); +} + +- (void)handleMetricEvent:(MetricEvent)event +{ + std::lock_guard lock(_lock); + + using ValueType = MetricEvent::Value::Type; + switch (event.ValueType()) { + case ValueType::kInt32: + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %d", event.key(), event.type(), event.ValueInt32()); + break; + case ValueType::kUInt32: + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %u", event.key(), event.type(), event.ValueUInt32()); + break; + case ValueType::kChipErrorCode: + MTR_LOG_INFO("Received metric event, key: %s, type: %d, error value: %u", event.key(), event.type(), event.ValueErrorCode()); + break; + case ValueType::kUndefined: + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: nil", event.key(), event.type()); + break; + default: + MTR_LOG_INFO("Received metric event, key: %s, type: %d, unknown value", event.key(), event.type()); + return; + } + + // Create the new metric key based event type + auto metricsKey = [NSString stringWithFormat:@"%s%@", event.key(), suffixNameForMetric(event)]; + MTRMetricsData * data = [[MTRMetricsData alloc] initWithMetricEvent:event]; + + // If End event, compute its duration using the Begin event + if (event.type() == MetricEvent::Type::kEndEvent) { + auto metricsBeginKey = [NSString stringWithFormat:@"%s%@", event.key(), suffixNameForMetricType(MetricEvent::Type::kBeginEvent)]; + MTRMetricsData * beginMetric = _metricsDataCollection[metricsBeginKey]; + if (beginMetric) { + [data setDurationFromMetricData:beginMetric]; + } else { + // Unbalanced end + MTR_LOG_ERROR("Unable to find Begin event corresponding to Metric Event: %s", event.key()); + } + } + + [_metricsDataCollection setValue:data forKey:metricsKey]; + + // If the event is a begin or end event, implicitly emit a corresponding instant event + if (event.type() == MetricEvent::Type::kBeginEvent || event.type() == MetricEvent::Type::kEndEvent) { + MetricEvent instantEvent(MetricEvent::Type::kInstantEvent, event.key()); + data = [[MTRMetricsData alloc] initWithMetricEvent:instantEvent]; + metricsKey = [NSString stringWithFormat:@"%s%@", event.key(), suffixNameForMetric(instantEvent)]; + [_metricsDataCollection setValue:data forKey:metricsKey]; + } +} + +- (MTRMetrics *)metricSnapshot:(BOOL)resetCollection +{ + std::lock_guard lock(_lock); + + // Copy the MTRMetrics as NSDictionary + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:[_metricsDataCollection count]]; + for (NSString * key in _metricsDataCollection) { + [metrics setValue:[_metricsDataCollection[key] toDictionary] forKey:key]; + } + + // Clear curent stats, if specified + if (resetCollection) { + [_metricsDataCollection removeAllObjects]; + } + return metrics; +} + +@end diff --git a/src/darwin/Framework/CHIP/MTRMetrics_Internal.h b/src/darwin/Framework/CHIP/MTRMetrics_Internal.h index 3e69c79dd53802..757ff0731ada0c 100644 --- a/src/darwin/Framework/CHIP/MTRMetrics_Internal.h +++ b/src/darwin/Framework/CHIP/MTRMetrics_Internal.h @@ -15,13 +15,33 @@ * limitations under the License. */ #import "MTRMetrics.h" +#include NS_ASSUME_NONNULL_BEGIN -#define MTRMetricCommissioningStatusKey @"com.matter.metric.commissioningStatus" +/** + * A representation of a metric data for an operation. + */ +@interface MTRMetricsData : NSObject + +// Value for the metric. This can be null if the metric is just a fire event with no value +@property (nonatomic, nullable, readonly, copy) NSNumber * value; + +// Relative time point at which the metric was emitted. This may be null. +@property (nonatomic, nullable, readonly, copy) NSNumber * timePointMicroseconds; + +// During for the event. This may be null. +@property (nonatomic, nullable, readonly, copy) NSNumber * durationMicroseconds; + +// Convert contents to a dictionary +- (NSDictionary *)toDictionary; + +@end @interface MTRMetrics () +- (instancetype)initWithCapacity:(NSUInteger)numItems; + - (void)setValue:(id _Nullable)value forKey:(NSString *)key; - (void)removeValueForKey:(NSString *)key; diff --git a/src/darwin/Framework/CHIPTests/MTRMetricsTests.m b/src/darwin/Framework/CHIPTests/MTRMetricsTests.m index ed5de20b5c433b..696f55bef89df4 100644 --- a/src/darwin/Framework/CHIPTests/MTRMetricsTests.m +++ b/src/darwin/Framework/CHIPTests/MTRMetricsTests.m @@ -49,7 +49,7 @@ - (void)test000_SetUp - (void)test001_TestAllKeys { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:4]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; [metrics setValue:@"metricsCounter2" forKey:@"com.matter.metrics.counter2"]; [metrics setValue:@"metricsCounter3" forKey:@"com.matter.metrics.counter3"]; @@ -65,7 +65,7 @@ - (void)test001_TestAllKeys } - (void)test002_TestOneKey { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:1]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; NSArray * keys = [metrics allKeys]; @@ -75,7 +75,7 @@ - (void)test002_TestOneKey - (void)test003_TestMultipleKeys { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:3]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; [metrics setValue:@"metricsCounter2" forKey:@"com.matter.metrics.counter2"]; [metrics setValue:[NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil] forKey:@"com.matter.metrics.counter3"]; @@ -90,7 +90,7 @@ - (void)test003_TestMultipleKeys - (void)test004_TestValueForKey { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:1]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; XCTAssertEqualObjects([metrics valueForKey:@"com.matter.metrics.counter1"], @"metricsCounter1"); @@ -98,7 +98,7 @@ - (void)test004_TestValueForKey - (void)test005_TestMultipleValueForKeys { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:3]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; [metrics setValue:@"metricsCounter2" forKey:@"com.matter.metrics.counter2"]; [metrics setValue:[NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil] forKey:@"com.matter.metrics.counter3"]; @@ -110,7 +110,7 @@ - (void)test005_TestMultipleValueForKeys - (void)test006_TestValueRemoval { - MTRMetrics * metrics = [MTRMetrics new]; + MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:2]; [metrics setValue:@"metricsCounter1" forKey:@"com.matter.metrics.counter1"]; [metrics setValue:@"metricsCounter2" forKey:@"com.matter.metrics.counter2"]; diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index a49b33fa3a1ea4..698fe2ad47114a 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -266,6 +266,9 @@ 88EBF8CE27FABDD500686BC1 /* MTRDeviceAttestationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EBF8CB27FABDD500686BC1 /* MTRDeviceAttestationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88EBF8CF27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88EBF8CC27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.mm */; }; 88EBF8D027FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EBF8CD27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.h */; }; + 88FA798D2B7B257100CD4B6F /* MTRMetricsCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 88FA798B2B7B257100CD4B6F /* MTRMetricsCollector.h */; }; + 88FA798E2B7B257100CD4B6F /* MTRMetricsCollector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88FA798C2B7B257100CD4B6F /* MTRMetricsCollector.mm */; }; + 88FA79902B7BE42500CD4B6F /* MTRMetricsCollector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88FA798C2B7B257100CD4B6F /* MTRMetricsCollector.mm */; }; 93B2CF9A2B56E45C00E4D187 /* MTRClusterNames.mm in Sources */ = {isa = PBXBuildFile; fileRef = 93B2CF992B56E45C00E4D187 /* MTRClusterNames.mm */; }; 93E610AA2B626E290077F02A /* MTRClusterNames.h in Headers */ = {isa = PBXBuildFile; fileRef = 93E610A92B626E290077F02A /* MTRClusterNames.h */; settings = {ATTRIBUTES = (Public, ); }; }; 991DC0842475F45400C13860 /* MTRDeviceController.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC0822475F45400C13860 /* MTRDeviceController.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -664,6 +667,8 @@ 88EBF8CB27FABDD500686BC1 /* MTRDeviceAttestationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceAttestationDelegate.h; sourceTree = ""; }; 88EBF8CC27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceAttestationDelegateBridge.mm; sourceTree = ""; }; 88EBF8CD27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceAttestationDelegateBridge.h; sourceTree = ""; }; + 88FA798B2B7B257100CD4B6F /* MTRMetricsCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRMetricsCollector.h; sourceTree = ""; }; + 88FA798C2B7B257100CD4B6F /* MTRMetricsCollector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRMetricsCollector.mm; sourceTree = ""; }; 93B2CF992B56E45C00E4D187 /* MTRClusterNames.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRClusterNames.mm; sourceTree = ""; }; 93E610A92B626E290077F02A /* MTRClusterNames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRClusterNames.h; sourceTree = ""; }; 991DC0822475F45400C13860 /* MTRDeviceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceController.h; sourceTree = ""; }; @@ -1160,6 +1165,8 @@ B202528F2459E34F00F97062 /* CHIP */ = { isa = PBXGroup; children = ( + 88FA798B2B7B257100CD4B6F /* MTRMetricsCollector.h */, + 88FA798C2B7B257100CD4B6F /* MTRMetricsCollector.mm */, 88E6C9442B6334ED001A1FE0 /* MTRMetrics_Internal.h */, 88E6C9432B6334ED001A1FE0 /* MTRMetrics.h */, 88E6C9452B6334ED001A1FE0 /* MTRMetrics.mm */, @@ -1518,6 +1525,7 @@ 2C222AD0255C620600E446B9 /* MTRBaseDevice.h in Headers */, 7596A84F2877E6A9004DAE0E /* MTRCluster_Internal.h in Headers */, 991DC0842475F45400C13860 /* MTRDeviceController.h in Headers */, + 88FA798D2B7B257100CD4B6F /* MTRMetricsCollector.h in Headers */, 88E6C9462B6334ED001A1FE0 /* MTRMetrics.h in Headers */, AF1CB86E2874B03B00865A96 /* MTROTAProviderDelegate.h in Headers */, 51D0B1402B61B3A4006E3511 /* MTRServerCluster.h in Headers */, @@ -1739,6 +1747,7 @@ 039546A62991E151006D42A8 /* InteractionModel.cpp in Sources */, B45373E72A9FEBA400807602 /* parsers.c in Sources */, B4FCD5722B603A6300832859 /* DownloadLogCommand.mm in Sources */, + 88FA79902B7BE42500CD4B6F /* MTRMetricsCollector.mm in Sources */, B45373BF2A9FEA9100807602 /* adopt.c in Sources */, B45373F32A9FEC1A00807602 /* server-ws.c in Sources */, 03F430AA2994113500166449 /* sysunix.c in Sources */, @@ -1893,6 +1902,7 @@ 5A6FEC9827B5C6AF00F25F42 /* MTRDeviceOverXPC.mm in Sources */, 514654492A72F9DF00904E61 /* MTRDemuxingStorage.mm in Sources */, 1E4D655229C30A8700BC3478 /* MTRCommissionableBrowser.mm in Sources */, + 88FA798E2B7B257100CD4B6F /* MTRMetricsCollector.mm in Sources */, 3DA1A3562ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm in Sources */, 51D0B1272B617246006E3511 /* MTRServerEndpoint.mm in Sources */, 3DECCB722934AFE200585AEC /* MTRLogging.mm in Sources */, diff --git a/src/lib/support/CodeUtils.h b/src/lib/support/CodeUtils.h index 58094a01731c37..2b47538b8faebf 100644 --- a/src/lib/support/CodeUtils.h +++ b/src/lib/support/CodeUtils.h @@ -362,10 +362,10 @@ constexpr inline const _T & max(const _T & a, const _T & b) } while (false) /** - * @def SuccessOrExit(aStatus) + * @def SuccessOrExit(error) * * @brief - * This checks for the specified status, which is expected to + * This checks for the specified error, which is expected to * commonly be successful (CHIP_NO_ERROR), and branches to * the local label 'exit' if the status is unsuccessful. * @@ -387,10 +387,10 @@ constexpr inline const _T & max(const _T & a, const _T & b) * } * @endcode * - * @param[in] aStatus A scalar status to be evaluated against zero (0). + * @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR). * */ -#define SuccessOrExit(aStatus) nlEXPECT(::chip::ChipError::IsSuccess((aStatus)), exit) +#define SuccessOrExit(error) nlEXPECT(::chip::ChipError::IsSuccess((error)), exit) /** * @def VerifyOrExit(aCondition, anAction) diff --git a/src/platform/Darwin/BUILD.gn b/src/platform/Darwin/BUILD.gn index 11372cb5f4c627..321076da241cb5 100644 --- a/src/platform/Darwin/BUILD.gn +++ b/src/platform/Darwin/BUILD.gn @@ -97,6 +97,7 @@ static_library("Darwin") { deps = [ ":logging", + ":tracing", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", @@ -131,6 +132,18 @@ static_library("Darwin") { } } +source_set("tracing") { + sources = [ + "Tracing.h", + "Tracing.mm", + ] + + deps = [ + ":logging", + "${chip_root}/src/tracing", + ] +} + static_library("logging") { sources = [ "Logging.h", diff --git a/src/platform/Darwin/Tracing.h b/src/platform/Darwin/Tracing.h new file mode 100644 index 00000000000000..cefc5c326fc3d5 --- /dev/null +++ b/src/platform/Darwin/Tracing.h @@ -0,0 +1,77 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include + +#define MATTER_TRACE_BEGIN(label, group) os_signpost_interval_begin(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, group "-" label) +#define MATTER_TRACE_END(label, group) os_signpost_interval_end(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, group "-" label) +#define MATTER_TRACE_INSTANT(label, group) os_signpost_event_emit(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, group "-" label) + +#define MATTER_TRACE_COUNTER(label) \ + do { \ + static unsigned int count##_label = 0; \ + os_signpost_event_emit(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, label, "%u", ++count##_label); \ + } while (0) + +#define _CONCAT_IMPL(a, b) a##b +#define _MACRO_CONCAT(a, b) _CONCAT_IMPL(a, b) + +#define MATTER_TRACE_SCOPE(label, group) ::chip::Tracing::signposts::Scoped _MACRO_CONCAT(_trace_scope, __COUNTER__)(label, group) + +#define MATTER_SDK_SIGNPOST_NAME "com.csa.matter.signpost" +#define __DARWIN_MATTER_SIGNPOST_LOGGER() chip::Tracing::signposts::GetMatterSignpostLogger() + +namespace chip { +namespace Tracing { + namespace signposts { + + os_log_t GetMatterSignpostLogger(); + + class Scoped { + public: + inline Scoped(const char * label, const char * group) + : mLabel(label) + , mGroup(group) + { + os_signpost_interval_begin(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, MATTER_SDK_SIGNPOST_NAME, "%s-%s", group, label); + } + inline ~Scoped() { os_signpost_interval_end(__DARWIN_MATTER_SIGNPOST_LOGGER(), OS_SIGNPOST_ID_EXCLUSIVE, MATTER_SDK_SIGNPOST_NAME, "%s-%s", mGroup, mLabel); } + + private: + const char * mLabel; + const char * mGroup; + }; + + class DarwinTracingBackend : public ::chip::Tracing::Backend { + public: + DarwinTracingBackend(); + + typedef void (^MetricEventHandler)(MetricEvent event); + + void SetMetricEventHandler(MetricEventHandler callback); + void LogMetricEvent(const MetricEvent & event) override; + + private: + MetricEventHandler mClientCallback; + }; + + } // namespace signposts +} // namespace Tracing +} // namespace chip diff --git a/src/platform/Darwin/Tracing.mm b/src/platform/Darwin/Tracing.mm new file mode 100644 index 00000000000000..a729c32e8079a1 --- /dev/null +++ b/src/platform/Darwin/Tracing.mm @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include + +namespace chip { +namespace Tracing { + namespace signposts { + os_log_t GetMatterSignpostLogger() + { + static dispatch_once_t onceToken; + static os_log_t logger; + dispatch_once(&onceToken, ^{ + logger = os_log_create("com.csa.matter.signposts", "com.csa.matter.sdk"); + }); + return logger; + } + + DarwinTracingBackend::DarwinTracingBackend() + : mClientCallback(nullptr) + { + } + + void DarwinTracingBackend::SetMetricEventHandler(MetricEventHandler callback) + { + mClientCallback = callback; + } + + void DarwinTracingBackend::LogMetricEvent(const MetricEvent & event) + { + // Pass along to the client to handle the event + if (mClientCallback) { + mClientCallback(event); + } + } + + } // namespace signposts +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn index 84af8b09ef2449..cdb50a710c9c00 100644 --- a/src/tracing/BUILD.gn +++ b/src/tracing/BUILD.gn @@ -36,12 +36,16 @@ static_library("tracing") { sources = [ "backend.h", "log_declares.h", + "metric_event.h", + "metric_keys.h", + "metric_macros.h", "registry.cpp", "registry.h", ] public_deps = [ ":tracing_buildconfig", + "${chip_root}/src/lib/core:error", "${chip_root}/src/lib/support", ] } diff --git a/src/tracing/backend.h b/src/tracing/backend.h index c57e97ddd27638..9bc4e8bd096ebd 100644 --- a/src/tracing/backend.h +++ b/src/tracing/backend.h @@ -64,13 +64,13 @@ class Backend : public ::chip::IntrusiveListNodeBase<> virtual void TraceInstant(const char * label, const char * group) {} virtual void TraceCounter(const char * label) {} - virtual void TraceMetric(const char * label, int32_t value) {} virtual void LogMessageSend(MessageSendInfo &) { TraceInstant("MessageSent", "Messaging"); } virtual void LogMessageReceived(MessageReceivedInfo &) { TraceInstant("MessageReceived", "Messaging"); } virtual void LogNodeLookup(NodeLookupInfo &) { TraceInstant("Lookup", "DNSSD"); } virtual void LogNodeDiscovered(NodeDiscoveredInfo &) { TraceInstant("Node Discovered", "DNSSD"); } virtual void LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo &) { TraceInstant("Discovery Failed", "DNSSD"); } + virtual void LogMetricEvent(const MetricEvent &) { TraceInstant("Metric Event", "Metric"); } }; } // namespace Tracing diff --git a/src/tracing/darwin/BUILD.gn b/src/tracing/darwin/BUILD.gn new file mode 100644 index 00000000000000..3bd9b7685b594e --- /dev/null +++ b/src/tracing/darwin/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright (c) 2024 Project CHIP Authors +# +# 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("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +config("tracing") { + include_dirs = [ "include" ] +} + +source_set("darwin_tracing") { + public = [ "include/matter/tracing/macros_impl.h" ] + public_configs = [ ":tracing" ] +} diff --git a/src/tracing/darwin/include/matter/tracing/macros_impl.h b/src/tracing/darwin/include/matter/tracing/macros_impl.h new file mode 100644 index 00000000000000..536618dc2875b2 --- /dev/null +++ b/src/tracing/darwin/include/matter/tracing/macros_impl.h @@ -0,0 +1,25 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +/* Ensure we do not have double tracing macros defined */ +#if defined(MATTER_TRACE_BEGIN) +#error "Tracing macros seem to be double defined" +#endif + +#include diff --git a/src/tracing/esp32_trace/esp32_tracing.cpp b/src/tracing/esp32_trace/esp32_tracing.cpp index 6193587be49c7d..db5f09ee9a751c 100644 --- a/src/tracing/esp32_trace/esp32_tracing.cpp +++ b/src/tracing/esp32_trace/esp32_tracing.cpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace chip { namespace Tracing { @@ -154,16 +155,38 @@ void ESP32Backend::TraceCounter(const char * label) ::Insights::ESPInsightsCounter::GetInstance(label)->ReportMetrics(); } -void ESP32Backend::TraceMetric(const char * label, int32_t value) +void ESP32Backend::LogMetricEvent(const MetricEvent & event) { if (!mRegistered) { - esp_diag_metrics_register("SYS_MTR" /*Tag of metrics */, label /* Unique key 8 */, label /* label displayed on dashboard */, - "insights.mtr" /* hierarchical path */, ESP_DIAG_DATA_TYPE_INT /* data_type */); + esp_diag_metrics_register("SYS_MTR" /*Tag of metrics */, event.key() /* Unique key 8 */, + event.key() /* label displayed on dashboard */, "insights.mtr" /* hierarchical path */, + ESP_DIAG_DATA_TYPE_INT /* data_type */); mRegistered = true; } - ESP_LOGI("mtr", "The value of %s is %ld ", label, value); - esp_diag_metrics_add_int(label, value); + + using ValueType = MetricEvent::Value::Type; + switch (event.ValueType()) + { + case ValueType::kInt32: + ESP_LOGI("mtr", "The value of %s is %ld ", event.key(), event.ValueInt32()); + esp_diag_metrics_add_int(event.key(), event.ValueInt32()); + break; + case ValueType::kUInt32: + ESP_LOGI("mtr", "The value of %s is %lu ", event.key(), event.ValueUInt32()); + esp_diag_metrics_add_uint(event.key(), event.ValueUInt32()); + break; + case ValueType::kChipErrorCode: + ESP_LOGI("mtr", "The value of %s is error with code %lu ", event.key(), event.ValueErrorCode()); + esp_diag_metrics_add_uint(event.key(), event.ValueErrorCode()); + break; + case ValueType::kUndefined: + ESP_LOGI("mtr", "The value of %s is undefined", event.key()); + break; + default: + ESP_LOGI("mtr", "The value of %s is of an UNKNOWN TYPE", event.key()); + break; + } } void ESP32Backend::TraceBegin(const char * label, const char * group) diff --git a/src/tracing/esp32_trace/esp32_tracing.h b/src/tracing/esp32_trace/esp32_tracing.h index 3eb127c1d3367f..2e08e69d616158 100644 --- a/src/tracing/esp32_trace/esp32_tracing.h +++ b/src/tracing/esp32_trace/esp32_tracing.h @@ -29,7 +29,6 @@ class ESP32Backend : public ::chip::Tracing::Backend void TraceInstant(const char * label, const char * group) override; void TraceCounter(const char * label) override; - void TraceMetric(const char * label, int32_t value) override; void LogMessageSend(MessageSendInfo &) override; void LogMessageReceived(MessageReceivedInfo &) override; @@ -37,6 +36,7 @@ class ESP32Backend : public ::chip::Tracing::Backend void LogNodeLookup(NodeLookupInfo &) override; void LogNodeDiscovered(NodeDiscoveredInfo &) override; void LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo &) override; + void LogMetricEvent(const MetricEvent &) override; private: bool mRegistered = false; diff --git a/src/tracing/esp32_trace/include/matter/tracing/macros_impl.h b/src/tracing/esp32_trace/include/matter/tracing/macros_impl.h index 8ebde173d8297b..b95c20754237a1 100644 --- a/src/tracing/esp32_trace/include/matter/tracing/macros_impl.h +++ b/src/tracing/esp32_trace/include/matter/tracing/macros_impl.h @@ -29,7 +29,6 @@ #define MATTER_TRACE_END(label, group) ::chip::Tracing::Internal::End(label, group) #define MATTER_TRACE_INSTANT(label, group) ::chip::Tracing::Internal::Instant(label, group) #define MATTER_TRACE_COUNTER(label) ::chip::Tracing::Internal::Counter(label) -#define MATTER_TRACE_METRIC(label, value) ::chip::Tracing::Internal::Metric(label, value) namespace chip { namespace Tracing { diff --git a/src/tracing/json/json_tracing.cpp b/src/tracing/json/json_tracing.cpp index 64627f079d04dd..91b4474388ecd0 100644 --- a/src/tracing/json/json_tracing.cpp +++ b/src/tracing/json/json_tracing.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -295,11 +296,31 @@ void JsonBackend::TraceCounter(const char * label) OutputValue(value); } -void JsonBackend::TraceMetric(const char * label, int32_t val) +void JsonBackend::LogMetricEvent(const MetricEvent & event) { ::Json::Value value; - value["label"] = label; - value["value"] = val; + + value["label"] = event.key(); + + using ValueType = MetricEvent::Value::Type; + switch (event.ValueType()) + { + case ValueType::kInt32: + value["value"] = event.ValueInt32(); + break; + case ValueType::kUInt32: + value["value"] = event.ValueUInt32(); + break; + case ValueType::kChipErrorCode: + value["value"] = event.ValueErrorCode(); + break; + case ValueType::kUndefined: + value["value"] = ::Json::Value(); + break; + default: + value["value"] = "UNKNOWN"; + break; + } OutputValue(value); } diff --git a/src/tracing/json/json_tracing.h b/src/tracing/json/json_tracing.h index e8ad8ee5894b03..14f3d163a04c46 100644 --- a/src/tracing/json/json_tracing.h +++ b/src/tracing/json/json_tracing.h @@ -52,12 +52,12 @@ class JsonBackend : public ::chip::Tracing::Backend void TraceEnd(const char * label, const char * group) override; void TraceInstant(const char * label, const char * group) override; void TraceCounter(const char * label) override; - void TraceMetric(const char * label, int32_t val) override; void LogMessageSend(MessageSendInfo &) override; void LogMessageReceived(MessageReceivedInfo &) override; void LogNodeLookup(NodeLookupInfo &) override; void LogNodeDiscovered(NodeDiscoveredInfo &) override; void LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo &) override; + void LogMetricEvent(const MetricEvent &) override; void Close() override { CloseFile(); } private: diff --git a/src/tracing/log_declares.h b/src/tracing/log_declares.h index 5e502d0c93af6c..72ebbce7c18011 100644 --- a/src/tracing/log_declares.h +++ b/src/tracing/log_declares.h @@ -27,6 +27,7 @@ struct MessageReceivedInfo; struct NodeLookupInfo; struct NodeDiscoveredInfo; struct NodeDiscoveryFailedInfo; +class MetricEvent; } // namespace Tracing } // namespace chip diff --git a/src/tracing/macros.h b/src/tracing/macros.h index 0643a466c2a77d..1a9b4569e2a35b 100644 --- a/src/tracing/macros.h +++ b/src/tracing/macros.h @@ -30,10 +30,6 @@ // Tracing macro to trace monotonically increasing counter values. // MATTER_TRACE_COUNTER(label) -// Tracing macro to represent historical metric data i.e the data points which represent different -// values at different point of time. -// MATTER_TRACE_METRIC(label, value) - #include #include #include @@ -87,7 +83,6 @@ #define MATTER_TRACE_INSTANT(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_TRACE_SCOPE(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_TRACE_COUNTER(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) -#define MATTER_TRACE_METRIC(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_LOG_MESSAGE_SEND(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_LOG_MESSAGE_RECEIVED(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) diff --git a/src/tracing/metric_event.h b/src/tracing/metric_event.h new file mode 100644 index 00000000000000..7bb5d8b5b81b51 --- /dev/null +++ b/src/tracing/metric_event.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace chip { +namespace Tracing { + +/** + * Define a metric that can be logged. A metric consists of a key and an optional value pair. + * The value is currently limited to simple scalar values. + * + * Additionally a metric is tagged as either an instant event or marked with a begin/end + * for the event. When the latter is used, a duration can be associated between the two events. + */ +class MetricEvent +{ +public: + MetricEvent(const MetricEvent &) = default; + MetricEvent(MetricEvent &&) = default; + MetricEvent & operator=(const MetricEvent &) = default; + MetricEvent & operator=(MetricEvent &&) = default; + + // This specifies the different categories of metric events that can created. In addition to + // emitting an event, events paired with a kBeginEvent and kEndEvent can be used to track + // duration for the event. A kInstantEvent represents a one shot event. + enum class Type + { + // This specifies an event marked to track the Begin of an operation + kBeginEvent, + + // This specifies an event marked to track the End of an operation + kEndEvent, + + // This specifies a one shot event + kInstantEvent + }; + + // This defines the different types of values that can stored when a metric is emitted + struct Value + { + Value(const Value &) = default; + Value(Value &&) = default; + Value & operator=(const Value &) = default; + Value & operator=(Value &&) = default; + + enum class Type : uint8_t + { + kUndefined, // Value is not valid + kInt32, // int32_t + kUInt32, // uint32_t + kChipErrorCode // chip::ChipError + }; + + union Store + { + int32_t int32_value; + uint32_t uint32_value; + + Store() {} + + Store(int32_t v) : int32_value(v) {} + + Store(uint32_t v) : uint32_value(v) {} + }; + + Store store; + Type type; + + Value() : type(Type::kUndefined) {} + + Value(uint32_t value) : store(value), type(Type::kUInt32) {} + + Value(int32_t value) : store(value), type(Type::kInt32) {} + + Value(const ChipError & err) : store(err.AsInteger()), type(Type::kChipErrorCode) {} + }; + + MetricEvent(Type type, MetricKey key) : mType(type), mKey(key) {} + + MetricEvent(Type type, MetricKey key, int32_t value) : mType(type), mKey(key), mValue(value) {} + + MetricEvent(Type type, MetricKey key, uint32_t value) : mType(type), mKey(key), mValue(value) {} + + MetricEvent(Type type, MetricKey key, const ChipError & error) : mType(type), mKey(key), mValue(error) {} + + MetricEvent(Type type, MetricKey key, Value value) : mType(type), mKey(key), mValue(value) {} + + MetricEvent(Type type, MetricKey key, int8_t value) : MetricEvent(type, key, int32_t(value)) {} + + MetricEvent(Type type, MetricKey key, uint8_t value) : MetricEvent(type, key, uint32_t(value)) {} + + MetricEvent(Type type, MetricKey key, int16_t value) : MetricEvent(type, key, int32_t(value)) {} + + MetricEvent(Type type, MetricKey key, uint16_t value) : MetricEvent(type, key, uint32_t(value)) {} + + Type type() const { return mType; } + + MetricKey key() const { return mKey; } + + Value::Type ValueType() const { return mValue.type; } + + uint32_t ValueUInt32() const + { + VerifyOrDie(mValue.type == Value::Type::kUInt32); + return mValue.store.uint32_value; + } + + int32_t ValueInt32() const + { + VerifyOrDie(mValue.type == Value::Type::kInt32); + return mValue.store.int32_value; + } + + uint32_t ValueErrorCode() const + { + VerifyOrDie(mValue.type == Value::Type::kChipErrorCode); + return mValue.store.uint32_value; + } + +private: + Type mType; + MetricKey mKey; + Value mValue; +}; + +namespace ErrorHandling { + +/** + * Utility to emit an instant metric if the error is not a success. + */ +inline bool LogMetricIfError(MetricKey metricKey, const ::chip::ChipError & err) +{ + bool success = ::chip::ChipError::IsSuccess(err); + if (!success) + { + MATTER_LOG_METRIC(metricKey, err); + } + return success; +} + +} // namespace ErrorHandling + +/** + * This utility class helps generate a Begin and End metric event within the scope of a block using RAII. + */ +class ScopedMetricEvent +{ +public: + ScopedMetricEvent(const ScopedMetricEvent &) = delete; + ScopedMetricEvent(ScopedMetricEvent &&) = delete; + ScopedMetricEvent & operator=(const ScopedMetricEvent &) = delete; + ScopedMetricEvent & operator=(ScopedMetricEvent &&) = delete; + + ScopedMetricEvent(MetricKey key, ChipError & error) : mKey(key), mError(error) + { + MATTER_LOG_METRIC_BEGIN(mKey); + IgnoreUnusedVariable(mKey); + IgnoreUnusedVariable(mError); + } + + ~ScopedMetricEvent() { MATTER_LOG_METRIC_END(mKey, mError); } + +private: + MetricKey mKey; + ChipError & mError; +}; + +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/metric_keys.h b/src/tracing/metric_keys.h new file mode 100644 index 00000000000000..f52b2ab3244776 --- /dev/null +++ b/src/tracing/metric_keys.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +#include + +namespace chip { +namespace Tracing { + +/** + * Defines the key type use to identity a specific metric + */ +typedef const char * MetricKey; + +/** + * List of supported metric keys + */ +constexpr MetricKey kMetricDiscoveryOverBLE = "disc-over-ble"; +constexpr MetricKey kMetricDiscoveryOnNetwork = "disc-on-nw"; +constexpr MetricKey kMetricPASESession = "pase-session"; +constexpr MetricKey kMetricPASESessionPair = "pase-session-pair"; +constexpr MetricKey kMetricPASESessionBLE = "pase-session-ble"; +constexpr MetricKey kMetricAttestationResult = "attestation-result"; +constexpr MetricKey kMetricAttestationOverridden = "attestation-overridden"; +constexpr MetricKey kMetricCASESession = "case-session"; +constexpr MetricKey kMetricCASESessionEstState = "case-conn-est"; +constexpr MetricKey kMetricWiFiRSSI = "wifi_rssi"; + +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/metric_macros.h b/src/tracing/metric_macros.h new file mode 100644 index 00000000000000..4ff85f353701b3 --- /dev/null +++ b/src/tracing/metric_macros.h @@ -0,0 +1,243 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include +#include + +#define __LOG_METRIC_CONCAT_IMPL(a, b) a##b +#define __LOG_METRIC_MACRO_CONCAT(a, b) __LOG_METRIC_CONCAT_IMPL(a, b) + +#if MATTER_TRACING_ENABLED + +/** + * @def SuccessOrExitWithMetric(kMetriKey, error) + * + * @brief + * This checks for the specified error, which is expected to + * commonly be successful (CHIP_NO_ERROR), and branches to + * the local label 'exit' if the error is not success. + * If error is not a success, a metric with key kMetriKey is emitted with + * the error code as the value of the metric. + * + * Example Usage: + * + * @code + * CHIP_ERROR TryHard() + * { + * CHIP_ERROR err; + * + * err = TrySomething(); + * SuccessOrExitWithMetric(kMetricKey, err); + * + * err = TrySomethingElse(); + * SuccessOrExitWithMetric(kMetricKey, err); + * + * exit: + * return err; + * } + * @endcode + * + * @param[in] kMetricKey Metric key for the metric event to be emitted + * if the condition evaluates to false. The value + * for the metric is result of the expression aStatus. + * @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR). + * + */ +#define SuccessOrExitWithMetric(kMetricKey, error) \ + nlEXPECT(::chip::Tracing::ErrorHandling::LogMetricIfError((kMetricKey), (error)), exit) + +/** + * @def VerifyOrExitWithMetric(kMetricKey, aCondition, anAction) + * + * @brief + * This checks for the specified condition, which is expected to + * commonly be true, and both executes @a anAction and branches to + * the local label 'exit' if the condition is false. If the condition + * is false a metric event with the specified key is emitted with value + * set to the result of the expression anAction. + * + * Example Usage: + * + * @code + * CHIP_ERROR MakeBuffer(const uint8_t *& buf) + * { + * CHIP_ERROR err = CHIP_NO_ERROR; + * + * buf = (uint8_t *)malloc(1024); + * VerifyOrExitWithMetric(kMetricKey, buf != NULL, err = CHIP_ERROR_NO_MEMORY); + * + * memset(buf, 0, 1024); + * + * exit: + * return err; + * } + * @endcode + * + * @param[in] kMetricKey Metric key for the metric event to be emitted + * if the aCondition evaluates to false. The value + * for the metric is result of the expression anAction. + * @param[in] aCondition A Boolean expression to be evaluated. + * @param[in] anAction An expression or block to execute when the + * assertion fails. + */ +#define VerifyOrExitWithMetric(kMetricKey, aCondition, anAction) \ + nlEXPECT_ACTION(aCondition, exit, MATTER_LOG_METRIC((kMetricKey), (anAction))) + +/* + * Utility Macros to support optional arguments for MATTER_LOG_METRIC_XYZ macros + */ + +// Utility to always return the 4th argument from macro parameters +#define __GET_4TH_ARG(_a1, _a2, _a3, _a4, ...) _a4 + +// Utility macro to select the macro with the correct signature based on the invoked MATTER_LOG_METRIC_XYZ macro +#define __SELECT_MACRO_FOR_EVENT_METRIC(...) \ + __GET_4TH_ARG(__VA_ARGS__, __MATTER_LOG_METRIC_3ARGS, __MATTER_LOG_METRIC_2ARGS, __MATTER_LOG_METRIC_1ARGS, ) + +// Wrapper to capture all arguments and invoke the real wrapper for logging metrics +#define __MATTER_LOG_METRIC(...) __SELECT_MACRO_FOR_EVENT_METRIC(__VA_ARGS__)(__VA_ARGS__) + +// Wrapper macro that accepts metric key and logs and instant event +#define __MATTER_LOG_METRIC_1ARGS(key) \ + do \ + { \ + using Type = chip::Tracing::MetricEvent::Type; \ + ::chip::Tracing::MetricEvent _metric_event(Type::kInstantEvent, key); \ + ::chip::Tracing::Internal::LogMetricEvent(_metric_event); \ + } while (false) + +// Wrapper macro that accepts metric type and key and logs an event corresponding to the type +#define __MATTER_LOG_METRIC_2ARGS(type, key) \ + do \ + { \ + ::chip::Tracing::MetricEvent _metric_event(type, key); \ + ::chip::Tracing::Internal::LogMetricEvent(_metric_event); \ + } while (false) + +// Wrapper macro that accepts metric type, key and value and logs the corresponding event +#define __MATTER_LOG_METRIC_3ARGS(type, key, value) \ + do \ + { \ + ::chip::Tracing::MetricEvent _metric_event(type, key, value); \ + ::chip::Tracing::Internal::LogMetricEvent(_metric_event); \ + } while (false) + +//////////////////////// +// Metric logging macros +//////////////////////// + +/** + * @def MATTER_LOG_METRIC + * + * @brief + * When tracing is enabled, this macro generates a metric event and logs it to the tracing backend. + * + * Example usage: + * @code + * MATTER_LOG_METRIC(chip::Tracing::kMetricPASESession, err); + * @endcode + * The above example generates an instant metric event with key kMetricPASESession. + * The metric also holds the 32 bit value corresponding to the ChipError object 'err'. + * + * @param[in] key The key representing the metric name/event. + * + * @param[in] value An optional value for the metric. This value corresponds to one of the values supported + * in MetricEvent::Value + */ +#define MATTER_LOG_METRIC(key, ...) __MATTER_LOG_METRIC(chip::Tracing::MetricEvent::Type::kInstantEvent, key, ##__VA_ARGS__) + +/** + * @def MATTER_LOG_METRIC_BEGIN + * + * @brief + * Generate a metric with the Begin Type + * + * Example usage: + * @code + * MATTER_LOG_METRIC_BEGIN(chip::Tracing::kMetricPASESession); + * @endcode + * The above example generates a Begin metric event with key kMetricPASESession. + * + * @param[in] key The key representing the metric name/event. + */ +#define MATTER_LOG_METRIC_BEGIN(key) __MATTER_LOG_METRIC(chip::Tracing::MetricEvent::Type::kBeginEvent, key) + +/** + * @def MATTER_LOG_METRIC_END + * + * @brief + * Generate a metric with the End Type + * + * Example usage: + * @code + * MATTER_LOG_METRIC_END(chip::Tracing::kMetricPASESession); + * @endcode + * The above example generates an End metric event with key kMetricPASESession. + * + * @param[in] key The key representing the metric name/event. + * + * @param[in] value An optional value for the metric. This value corresponds to one of the values supported + * in MetricEvent::Value + */ +#define MATTER_LOG_METRIC_END(key, ...) __MATTER_LOG_METRIC(chip::Tracing::MetricEvent::Type::kEndEvent, key, ##__VA_ARGS__) + +/** + * @def MATTER_LOG_METRIC_SCOPE + * + * @brief + * Generate a scoped metric tracking Begin and End within a given scope. + * + * Example usage: + * @code + * MATTER_LOG_METRIC_SCOPE(chip::Tracing::kMetricPASESession); + * @endcode + * The above example generates an Begin and the End metric using RAII. + * + * @param[in] key The key representing the metric name/event. + * @param[in] error Reference to a ChipError object that is used as the value of the End event. + */ +#define MATTER_LOG_METRIC_SCOPE(key, error) \ + ::chip::Tracing::ScopedMetricEvent __LOG_METRIC_MACRO_CONCAT(_metric_scope, __COUNTER__)(key, error) + +#else // Tracing is disabled + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Remap Success, Return, and Verify macros to the ones without metrics +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SuccessOrExitWithMetric(kMetricKey, aStatus) SuccessOrExit(aStatus) + +#define VerifyOrExitWithMetric(kMetricKey, aCondition, anAction) VerifyOrExit(aCondition, anAction) + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Map all MATTER_LOG_METRIC_XYZ macros to noops +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define __MATTER_LOG_METRIC_DISABLE(...) \ + do \ + { \ + } while (false) + +#define MATTER_LOG_METRIC(...) __MATTER_LOG_METRIC_DISABLE(__VA_ARGS__) +#define MATTER_LOG_METRIC_BEGIN(...) __MATTER_LOG_METRIC_DISABLE(__VA_ARGS__) +#define MATTER_LOG_METRIC_END(...) __MATTER_LOG_METRIC_DISABLE(__VA_ARGS__) +#define MATTER_LOG_METRIC_SCOPE(...) __MATTER_LOG_METRIC_DISABLE(__VA_ARGS__) + +#endif // MATTER_TRACING_ENABLED diff --git a/src/tracing/multiplexed/include/matter/tracing/macros_impl.h b/src/tracing/multiplexed/include/matter/tracing/macros_impl.h index eda5f2ed5142a4..8b3e728289dc1d 100644 --- a/src/tracing/multiplexed/include/matter/tracing/macros_impl.h +++ b/src/tracing/multiplexed/include/matter/tracing/macros_impl.h @@ -29,7 +29,6 @@ #define MATTER_TRACE_END(label, group) ::chip::Tracing::Internal::End(label, group) #define MATTER_TRACE_INSTANT(label, group) ::chip::Tracing::Internal::Instant(label, group) #define MATTER_TRACE_COUNTER(label) ::chip::Tracing::Internal::Counter(label) -#define MATTER_TRACE_METRIC(label, value) ::chip::Tracing::Internal::Metric(label, value) namespace chip { namespace Tracing { diff --git a/src/tracing/none/include/matter/tracing/macros_impl.h b/src/tracing/none/include/matter/tracing/macros_impl.h index 1fc940683d75e2..3119dce2a2b49f 100644 --- a/src/tracing/none/include/matter/tracing/macros_impl.h +++ b/src/tracing/none/include/matter/tracing/macros_impl.h @@ -32,4 +32,3 @@ #define MATTER_TRACE_INSTANT(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_TRACE_SCOPE(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) #define MATTER_TRACE_COUNTER(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) -#define MATTER_TRACE_METRIC(...) _MATTER_TRACE_DISABLE(__VA_ARGS__) diff --git a/src/tracing/perfetto/include/matter/tracing/macros_impl.h b/src/tracing/perfetto/include/matter/tracing/macros_impl.h index 559e877db1fe11..9b05d2f3539a45 100644 --- a/src/tracing/perfetto/include/matter/tracing/macros_impl.h +++ b/src/tracing/perfetto/include/matter/tracing/macros_impl.h @@ -37,5 +37,3 @@ PERFETTO_DEFINE_CATEGORIES(perfetto::Category("Matter").SetDescription("Matter t static int count##_label = 0; \ TRACE_COUNTER("Matter", label, ++count##_label); \ } while (0) - -#define MATTER_TRACE_METRIC(label, value) TRACE_COUNTER("Matter", label, value) diff --git a/src/tracing/perfetto/perfetto_tracing.cpp b/src/tracing/perfetto/perfetto_tracing.cpp index b68ec33d33101d..f9b00cc7ad96d3 100644 --- a/src/tracing/perfetto/perfetto_tracing.cpp +++ b/src/tracing/perfetto/perfetto_tracing.cpp @@ -16,6 +16,7 @@ * limitations under the License. */ +#include #include #include @@ -126,6 +127,29 @@ void PerfettoBackend::LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo & info) ); } +void PerfettoBackend::LogMetricEvent(const MetricEvent & event) +{ + using ValueType = MetricEvent::Value::Type; + switch (event.ValueType()) + { + case ValueType::kInt32: + TRACE_EVENT_INSTANT("Matter", event.key(), "value", event.ValueInt32()); + break; + case ValueType::kUInt32: + TRACE_EVENT_INSTANT("Matter", event.key(), "value", event.ValueUInt32()); + break; + case ValueType::kChipErrorCode: + TRACE_EVENT_INSTANT("Matter", event.key(), "error", event.ValueErrorCode()); + break; + case ValueType::kUndefined: + TRACE_EVENT_INSTANT("Matter", event.key()); + break; + default: + TRACE_EVENT_INSTANT("Matter", event.key(), "type", "UNKNOWN"); + break; + } +} + } // namespace Perfetto } // namespace Tracing } // namespace chip diff --git a/src/tracing/perfetto/perfetto_tracing.h b/src/tracing/perfetto/perfetto_tracing.h index 901165e35a548d..05fee4b49934de 100644 --- a/src/tracing/perfetto/perfetto_tracing.h +++ b/src/tracing/perfetto/perfetto_tracing.h @@ -44,6 +44,7 @@ class PerfettoBackend : public ::chip::Tracing::Backend void LogNodeLookup(NodeLookupInfo &) override; void LogNodeDiscovered(NodeDiscoveredInfo &) override; void LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo &) override; + void LogMetricEvent(const MetricEvent &) override; }; } // namespace Perfetto diff --git a/src/tracing/registry.cpp b/src/tracing/registry.cpp index ee46b542e7f5f2..a4fcdbe81c650f 100644 --- a/src/tracing/registry.cpp +++ b/src/tracing/registry.cpp @@ -84,14 +84,6 @@ void Counter(const char * label) } } -void Metric(const char * label, int32_t value) -{ - for (auto & backend : gTracingBackends) - { - backend.TraceMetric(label, value); - } -} - void LogMessageSend(::chip::Tracing::MessageSendInfo & info) { for (auto & backend : gTracingBackends) @@ -132,6 +124,14 @@ void LogNodeDiscoveryFailed(::chip::Tracing::NodeDiscoveryFailedInfo & info) } } +void LogMetricEvent(const ::chip::Tracing::MetricEvent & event) +{ + for (auto & backend : gTracingBackends) + { + backend.LogMetricEvent(event); + } +} + } // namespace Internal #endif // MATTTER_TRACING_ENABLED diff --git a/src/tracing/registry.h b/src/tracing/registry.h index 5f3b3c1a6cea84..7d14522c56e708 100644 --- a/src/tracing/registry.h +++ b/src/tracing/registry.h @@ -77,13 +77,13 @@ void Begin(const char * label, const char * group); void End(const char * label, const char * group); void Instant(const char * label, const char * group); void Counter(const char * label); -void Metric(const char * label, int32_t value); void LogMessageSend(::chip::Tracing::MessageSendInfo & info); void LogMessageReceived(::chip::Tracing::MessageReceivedInfo & info); void LogNodeLookup(::chip::Tracing::NodeLookupInfo & info); void LogNodeDiscovered(::chip::Tracing::NodeDiscoveredInfo & info); void LogNodeDiscoveryFailed(::chip::Tracing::NodeDiscoveryFailedInfo & info); +void LogMetricEvent(const ::chip::Tracing::MetricEvent & event); } // namespace Internal diff --git a/src/tracing/tracing_args.gni b/src/tracing/tracing_args.gni index d3741b5eba5f10..d6ddb1fd2e99b8 100644 --- a/src/tracing/tracing_args.gni +++ b/src/tracing/tracing_args.gni @@ -23,7 +23,8 @@ declare_args() { # # Additionally, if tracing is enabled, the main() function has to add # backends explicitly - matter_enable_tracing_support = current_os == "android" + matter_enable_tracing_support = + current_os == "android" || chip_device_platform == "darwin" # Defines the trace backend. Current matter tracing splits the logic # into two parts: @@ -46,6 +47,8 @@ declare_args() { # if (current_os == "linux" || current_os == "android") { matter_trace_config = "${chip_root}/src/tracing/perfetto:perfetto_tracing" + } else if (chip_device_platform == "darwin") { + matter_trace_config = "${chip_root}/src/tracing/darwin:darwin_tracing" } else { matter_trace_config = "${chip_root}/src/tracing/none" } From 6a0a0569b1d8c90e737348b6190f26764e494e9d Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Mon, 26 Feb 2024 08:53:11 -0500 Subject: [PATCH 13/52] Adjust the default discovery timeout when BLE extend announcement is enabled. (#32300) * add build arg to enable BLE extended advertisement feature * Set a increased based value for the Discovery timeout when BLE extended advertising is set --- src/include/platform/CHIPDeviceConfig.h | 13 ++++++++++++- src/platform/silabs/CHIPDevicePlatformConfig.h | 2 ++ third_party/silabs/efr32_sdk.gni | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index 88ef78d1005314..9d1f7804da63b4 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -713,8 +713,19 @@ static_assert(CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING_INTERVAL_MIN <= CHIP_DEVICE * Time in seconds that a factory new device will advertise commissionable node discovery. */ #ifndef CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS +#if CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING +/** + * By default, the extended announcement, when enabled, starts its extended advertising 15 mins + * after the standard slow advertisement. Time at which the default discovery time would close the + * commissioning window and stop the BLE. + * Therefore, when CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING is enabled bump the default Discovery timeout + * to the maximum allowed by the spec. 48h. + */ +#define CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS (60 * 60 * 48) +#else #define CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS (15 * 60) -#endif +#endif // CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING +#endif // CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS /** * CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES diff --git a/src/platform/silabs/CHIPDevicePlatformConfig.h b/src/platform/silabs/CHIPDevicePlatformConfig.h index fa3a50974f6a50..e8601e5feedaf9 100644 --- a/src/platform/silabs/CHIPDevicePlatformConfig.h +++ b/src/platform/silabs/CHIPDevicePlatformConfig.h @@ -140,6 +140,8 @@ #define CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE 25 +#define CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING SL_MATTER_BLE_EXTENDED_ADV + /* ICD Configuration Defines */ diff --git a/third_party/silabs/efr32_sdk.gni b/third_party/silabs/efr32_sdk.gni index 8510295bf7c2e0..55934ae5128b0f 100644 --- a/third_party/silabs/efr32_sdk.gni +++ b/third_party/silabs/efr32_sdk.gni @@ -46,6 +46,9 @@ declare_args() { # Enable Segger System View use_system_view = false + # Enable the BLE extended advertisement + sl_matter_ble_extended_adv = false + # ICD Openthread Configuration flags sl_ot_idle_interval_ms = 15000 # 15s Idle Intervals sl_ot_active_interval_ms = 200 # 200ms Active Intervals @@ -297,6 +300,7 @@ template("efr32_sdk") { "SL_RAIL_LIB_MULTIPROTOCOL_SUPPORT=1", "SL_RAIL_UTIL_PA_CONFIG_HEADER=", "RADIO_CONFIG_DMP_SUPPORT=1", + "SL_MATTER_BLE_EXTENDED_ADV=${sl_matter_ble_extended_adv}", #"__STACK_SIZE=0", ] From 3799c4192ce20100264e399f8b64f8d58c8b3d4f Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:11:48 -0500 Subject: [PATCH 14/52] [ICD] StayActiveRequest (#32247) * Implementation of StayActive request with test * Regenerated zap files * Added StayActiveRequest scenarios to the yaml test * Co-authored-by: Boris Zbarsky Applied comments about #if and clarifying maximum guaranteed stay active * Added the #if back for platforms that build the icd-management-server cluster without being ICD devices * Applied suggestions --- .../nxp/zap-lit/contact-sensor-app.matter | 6 +- .../nxp/zap-sit/contact-sensor-app.matter | 6 +- .../light-switch-app.matter | 6 +- .../light-switch-app/qpg/zap/switch.matter | 6 +- .../lit-icd-common/lit-icd-server-app.matter | 6 +- examples/lock-app/lock-common/lock-app.matter | 6 +- examples/lock-app/lock-common/lock-app.zap | 9 ++- examples/lock-app/qpg/zap/lock.matter | 6 +- .../smoke-co-alarm-app.matter | 6 +- examples/window-app/common/window-app.matter | 6 +- .../icd-management-server.cpp | 20 ++--- .../icd-management-server.h | 2 - src/app/icd/server/ICDConfigurationData.h | 5 ++ src/app/icd/server/ICDManager.cpp | 46 +++++++---- src/app/icd/server/ICDManager.h | 14 ++++ src/app/icd/server/ICDNotifier.h | 3 +- src/app/tests/TestICDManager.cpp | 78 ++++++++++++++++++ .../suites/TestIcdManagementCluster.yaml | 79 +++++++++++++++++++ .../chip/icd-management-cluster.xml | 1 + .../data_model/controller-clusters.matter | 6 +- .../chip/devicecontroller/ChipClusters.java | 10 ++- .../devicecontroller/ClusterIDMapping.java | 17 ++++ .../devicecontroller/ClusterInfoMapping.java | 6 ++ .../cluster/clusters/IcdManagementCluster.kt | 8 +- .../python/chip/clusters/CHIPClusters.py | 1 + .../python/chip/clusters/Objects.py | 3 + .../CHIP/zap-generated/MTRBaseClusters.h | 4 +- .../CHIP/zap-generated/MTRBaseClusters.mm | 6 +- .../CHIP/zap-generated/MTRClusters.h | 4 +- .../CHIP/zap-generated/MTRClusters.mm | 6 +- .../zap-generated/MTRCommandPayloadsObjc.h | 2 + .../zap-generated/MTRCommandPayloadsObjc.mm | 8 +- src/system/SystemLayer.h | 14 ++++ src/system/SystemLayerImplFreeRTOS.cpp | 5 ++ src/system/SystemLayerImplFreeRTOS.h | 1 + src/system/SystemLayerImplSelect.cpp | 5 ++ src/system/SystemLayerImplSelect.h | 1 + .../zap-generated/cluster-objects.cpp | 14 ++++ .../zap-generated/cluster-objects.h | 4 + .../zap-generated/cluster/Commands.h | 1 + .../zap-generated/cluster/Commands.h | 7 ++ 41 files changed, 377 insertions(+), 67 deletions(-) diff --git a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter index 9a6eeb77ef10f2..fa6101b2586a63 100644 --- a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter @@ -1337,6 +1337,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1346,7 +1350,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } endpoint 0 { diff --git a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter index 479f8baa86284c..f82f6a9ea079d4 100644 --- a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter @@ -1337,6 +1337,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1346,7 +1350,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } endpoint 0 { diff --git a/examples/light-switch-app/light-switch-common/light-switch-app.matter b/examples/light-switch-app/light-switch-common/light-switch-app.matter index 90e60581be27da..b96bf908f766ab 100644 --- a/examples/light-switch-app/light-switch-common/light-switch-app.matter +++ b/examples/light-switch-app/light-switch-common/light-switch-app.matter @@ -2007,6 +2007,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -2016,7 +2020,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** Attributes and commands for scene configuration and manipulation. */ diff --git a/examples/light-switch-app/qpg/zap/switch.matter b/examples/light-switch-app/qpg/zap/switch.matter index b7f217d7221b27..0475721b798fe9 100644 --- a/examples/light-switch-app/qpg/zap/switch.matter +++ b/examples/light-switch-app/qpg/zap/switch.matter @@ -1804,6 +1804,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1813,7 +1817,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** Attributes and commands for scene configuration and manipulation. */ diff --git a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter index db80008fef240c..d4b7f3faa245db 100644 --- a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter +++ b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter @@ -1465,6 +1465,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1474,7 +1478,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } endpoint 0 { diff --git a/examples/lock-app/lock-common/lock-app.matter b/examples/lock-app/lock-common/lock-app.matter index f07b85cfe6de45..1b34782e4ea180 100644 --- a/examples/lock-app/lock-common/lock-app.matter +++ b/examples/lock-app/lock-common/lock-app.matter @@ -1825,6 +1825,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1834,7 +1838,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** An interface to a generic way to secure a door */ diff --git a/examples/lock-app/lock-common/lock-app.zap b/examples/lock-app/lock-common/lock-app.zap index c5201e7a6fbb0c..8ec233c5250e71 100644 --- a/examples/lock-app/lock-common/lock-app.zap +++ b/examples/lock-app/lock-common/lock-app.zap @@ -2412,10 +2412,10 @@ "side": "server", "type": "bitmap32", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2428,10 +2428,10 @@ "side": "server", "type": "int16u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "0x0002", + "defaultValue": null, "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -4939,6 +4939,7 @@ "define": "ICD_MANAGEMENT_CLUSTER", "side": "server", "enabled": 1, + "commands": [], "attributes": [ { "name": "IdleModeDuration", diff --git a/examples/lock-app/qpg/zap/lock.matter b/examples/lock-app/qpg/zap/lock.matter index 91ff76970813f1..9815defa7f8cef 100644 --- a/examples/lock-app/qpg/zap/lock.matter +++ b/examples/lock-app/qpg/zap/lock.matter @@ -1481,6 +1481,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1490,7 +1494,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** An interface to a generic way to secure a door */ diff --git a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter index 6c1938c578ea27..f3188350035291 100644 --- a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter +++ b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter @@ -1801,6 +1801,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1810,7 +1814,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** This cluster provides an interface for observing and managing the state of smoke and CO alarms. */ diff --git a/examples/window-app/common/window-app.matter b/examples/window-app/common/window-app.matter index 7e0053fd83ee39..7f1b3d34943911 100644 --- a/examples/window-app/common/window-app.matter +++ b/examples/window-app/common/window-app.matter @@ -1899,6 +1899,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -1908,7 +1912,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** Provides an interface for controlling and adjusting automatic window coverings. */ diff --git a/src/app/clusters/icd-management-server/icd-management-server.cpp b/src/app/clusters/icd-management-server/icd-management-server.cpp index 27ecfe778a33bb..0f71345a38d0f1 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.cpp +++ b/src/app/clusters/icd-management-server/icd-management-server.cpp @@ -364,14 +364,6 @@ void ICDManagementServer::TriggerICDMTableUpdatedEvent() #endif // CHIP_CONFIG_ENABLE_ICD_CIP -Status ICDManagementServer::StayActiveRequest(FabricIndex fabricIndex) -{ - // TODO: Implementent stay awake logic for end device - // https://github.com/project-chip/connectedhomeip/issues/24259 - ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDListener::ICDManagementEvents::kStayActiveRequestReceived); - return InteractionModel::Status::UnsupportedCommand; -} - void ICDManagementServer::Init(PersistentStorageDelegate & storage, Crypto::SymmetricKeystore * symmetricKeystore, ICDConfigurationData & icdConfigurationData) { @@ -433,10 +425,14 @@ bool emberAfIcdManagementClusterUnregisterClientCallback(CommandHandler * comman bool emberAfIcdManagementClusterStayActiveRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, const Commands::StayActiveRequest::DecodableType & commandData) { - ICDManagementServer server; - InteractionModel::Status status = server.StayActiveRequest(commandObj->GetAccessingFabricIndex()); - - commandObj->AddStatus(commandPath, status); +// Note: We only need this #if statement for platform examples that enable the ICD management server without building the sample +// as an ICD. Since this is not spec compliant, we should remove this #if statement once we stop compiling the ICD management +// server in those examples. +#if CHIP_CONFIG_ENABLE_ICD_SERVER + IcdManagement::Commands::StayActiveResponse::Type response; + response.promisedActiveDuration = Server::GetInstance().GetICDManager().StayActiveRequest(commandData.stayActiveDuration); + commandObj->AddResponse(commandPath, response); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER return true; } diff --git a/src/app/clusters/icd-management-server/icd-management-server.h b/src/app/clusters/icd-management-server/icd-management-server.h index 4462cb96d9c9f2..5c6b838fa9b527 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.h +++ b/src/app/clusters/icd-management-server/icd-management-server.h @@ -68,8 +68,6 @@ class ICDManagementServer const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData); #endif // CHIP_CONFIG_ENABLE_ICD_CIP - chip::Protocols::InteractionModel::Status StayActiveRequest(chip::FabricIndex fabricIndex); - private: #if CHIP_CONFIG_ENABLE_ICD_CIP /** diff --git a/src/app/icd/server/ICDConfigurationData.h b/src/app/icd/server/ICDConfigurationData.h index 3fb3e9a92d2304..8b87d090f769e1 100644 --- a/src/app/icd/server/ICDConfigurationData.h +++ b/src/app/icd/server/ICDConfigurationData.h @@ -61,6 +61,8 @@ class ICDConfigurationData System::Clock::Milliseconds16 GetActiveModeThreshold() { return mActiveThreshold; } + System::Clock::Milliseconds32 GetGuaranteedStayActiveDuration() { return kGuaranteedStayActiveDuration; } + Protocols::SecureChannel::CheckInCounter & GetICDCounter() { return mICDCounter; } uint16_t GetClientsSupportedPerFabric() { return mFabricClientsSupported; } @@ -123,6 +125,9 @@ class ICDConfigurationData static constexpr System::Clock::Seconds32 kMaxIdleModeDuration = System::Clock::Seconds32(18 * kSecondsPerHour); static constexpr System::Clock::Seconds32 kMinIdleModeDuration = System::Clock::Seconds32(1); + // As defined in the spec, the maximum guaranteed duration for the StayActiveDuration is 30s "Matter Application + // Clusters: 9.17.7.5.1. PromisedActiveDuration Field" + static constexpr System::Clock::Milliseconds32 kGuaranteedStayActiveDuration = System::Clock::Milliseconds32(30000); static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= kMaxIdleModeDuration.count(), "Spec requires the IdleModeDuration to be equal or inferior to 64800s."); diff --git a/src/app/icd/server/ICDManager.cpp b/src/app/icd/server/ICDManager.cpp index 057cc089e9624f..931737783dde9f 100644 --- a/src/app/icd/server/ICDManager.cpp +++ b/src/app/icd/server/ICDManager.cpp @@ -120,6 +120,22 @@ bool ICDManager::SupportsFeature(Feature feature) #endif // !CONFIG_BUILD_FOR_HOST_UNIT_TEST } +uint32_t ICDManager::StayActiveRequest(uint32_t stayActiveDuration) +{ + // This should only be called when the device is in ActiveMode + VerifyOrReturnValue(mOperationalState == OperationalState::ActiveMode, 0); + + uint32_t promisedActiveDuration = + std::min(ICDConfigurationData::GetInstance().GetGuaranteedStayActiveDuration().count(), stayActiveDuration); + + // If the device is already in ActiveMode, we need to extend the active mode duration + // for whichever is smallest between 30000 milliseconds and stayActiveDuration, taking in account the remaining active time. + ExtendActiveMode(System::Clock::Milliseconds16(promisedActiveDuration)); + promisedActiveDuration = DeviceLayer::SystemLayer().GetRemainingTime(OnActiveModeDone, this).count(); + + return promisedActiveDuration; +} + #if CHIP_CONFIG_ENABLE_ICD_CIP void ICDManager::SendCheckInMsgs() { @@ -366,17 +382,7 @@ void ICDManager::UpdateOperationState(OperationalState state) } else { - Milliseconds16 activeModeThreshold = ICDConfigurationData::GetInstance().GetActiveModeThreshold(); - DeviceLayer::SystemLayer().ExtendTimerTo(activeModeThreshold, OnActiveModeDone, this); - - Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS); - activeModeJitterThreshold = - (activeModeThreshold >= activeModeJitterThreshold) ? activeModeThreshold - activeModeJitterThreshold : kZero; - - if (!mTransitionToIdleCalled) - { - DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this); - } + ExtendActiveMode(ICDConfigurationData::GetInstance().GetActiveModeThreshold()); } } } @@ -521,11 +527,6 @@ void ICDManager::OnICDManagementServerEvent(ICDManagementEvents event) case ICDManagementEvents::kTableUpdated: this->UpdateICDMode(); break; - - case ICDManagementEvents::kStayActiveRequestReceived: - // TODO : Implement the StayActiveRequest - // https://github.com/project-chip/connectedhomeip/issues/24259 - break; default: break; } @@ -540,6 +541,19 @@ void ICDManager::OnSubscriptionReport() this->UpdateOperationState(OperationalState::ActiveMode); } +void ICDManager::ExtendActiveMode(Milliseconds16 extendDuration) +{ + DeviceLayer::SystemLayer().ExtendTimerTo(extendDuration, OnActiveModeDone, this); + + Milliseconds32 activeModeJitterThreshold = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS); + activeModeJitterThreshold = (extendDuration >= activeModeJitterThreshold) ? extendDuration - activeModeJitterThreshold : kZero; + + if (!mTransitionToIdleCalled) + { + DeviceLayer::SystemLayer().ExtendTimerTo(activeModeJitterThreshold, OnTransitionToIdle, this); + } +} + ICDManager::ObserverPointer * ICDManager::RegisterObserver(ICDStateObserver * observer) { return mStateObserverPool.CreateObject(observer); diff --git a/src/app/icd/server/ICDManager.h b/src/app/icd/server/ICDManager.h index 396e6be20d0606..5e391c1469143c 100644 --- a/src/app/icd/server/ICDManager.h +++ b/src/app/icd/server/ICDManager.h @@ -109,6 +109,14 @@ class ICDManager : public ICDListener void postObserverEvent(ObserverEventType event); OperationalState GetOperationalState() { return mOperationalState; } + /** + * @brief Ensures that the remaining Active Mode duration is at least the smaller of 30000 milliseconds and stayActiveDuration. + * + * @param stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode + * @return The duration (in milliseconds) the device will stay in Active Mode + */ + uint32_t StayActiveRequest(uint32_t stayActiveDuration); + #if CHIP_CONFIG_ENABLE_ICD_CIP void SendCheckInMsgs(); @@ -131,6 +139,12 @@ class ICDManager : public ICDListener void OnSubscriptionReport() override; protected: + /** + * @brief Hepler function that extends the Active Mode duration as well as the Active Mode Jitter timer for the transition to + * iddle mode. + */ + void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration); + friend class TestICDManager; static void OnIdleModeDone(System::Layer * aLayer, void * appState); diff --git a/src/app/icd/server/ICDNotifier.h b/src/app/icd/server/ICDNotifier.h index 1223732e3fc05f..5db14503a296d7 100644 --- a/src/app/icd/server/ICDNotifier.h +++ b/src/app/icd/server/ICDNotifier.h @@ -48,8 +48,7 @@ class ICDListener enum class ICDManagementEvents : uint8_t { - kTableUpdated = 0x01, - kStayActiveRequestReceived = 0x02, + kTableUpdated = 0x01, }; using KeepActiveFlags = BitFlags; diff --git a/src/app/tests/TestICDManager.cpp b/src/app/tests/TestICDManager.cpp index da549ff5062f34..ba311f889989f8 100644 --- a/src/app/tests/TestICDManager.cpp +++ b/src/app/tests/TestICDManager.cpp @@ -540,6 +540,83 @@ class TestICDManager // After the init we should be in Idle mode NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); } + + /* Test that verifies the logic of the ICDManager when it receives a StayActiveRequest*/ + static void TestICDMStayActive(nlTestSuite * aSuite, void * aContext) + { + TestContext * ctx = static_cast(aContext); + ICDNotifier notifier = ICDNotifier::GetInstance(); + ICDConfigurationData & icdConfigData = ICDConfigurationData::GetInstance(); + + // Verify That ICDManager starts in Idle + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); + + // Trigger a subscription report. Put the ICD manager into active mode. + notifier.NotifySubscriptionReport(); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + // Advance time by the ActiveModeDuration - 1 + AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms32); + // Confirm ICD manager is in active mode + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + uint32_t stayActiveRequestedMs = 20000; + // Send a stay active request for 20 seconds + uint32_t stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs); + // confirm the promised time is the same as the requested time + NL_TEST_ASSERT(aSuite, stayActivePromisedMs == stayActiveRequestedMs); + + // Advance time by the duration of the stay stayActiveRequestedMs - 1 ms + AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 1_ms32); + // Confirm ICD manager is in active mode + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + // Advance time by 1ms and Confirm ICD manager is in idle mode + AdvanceClockAndRunEventLoop(ctx, 1_ms32); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); + + // Trigger a subscription report Put the ICD manager into active mode + notifier.NotifySubscriptionReport(); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + // Advance time by the duration of the stay active request - 1 ms + AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms32); + stayActiveRequestedMs = 35000; + // Send a stay active request for 35 seconds, which is higher than the maximum stay active duration (30 seconds) + stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs); + // confirm the promised time is the maximum stay active duration (30 seconds) + NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000); + + // Advance time by the duration of the max stay active duration - 1 ms + AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(30000) - 1_ms32); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + // Advance time by 1ms and Confirm ICD manager is in idle mode + AdvanceClockAndRunEventLoop(ctx, 1_ms32); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode); + + // Trigger a subscription report Put the ICD manager into active mode + notifier.NotifySubscriptionReport(); + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + // Advance time by the duration of the stay active request - 1 ms + AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms32); + stayActiveRequestedMs = 30000; + // Send a stay active request for 30 seconds + stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs); + // confirm the promised time is the same as the requested time + NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000); + + // Advance time by the duration of the stay active request - 20000 ms + AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 20000_ms32); + // Confirm ICD manager is in active mode, we should have 20000 seconds left at that point + NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode); + + stayActiveRequestedMs = 10000; + stayActivePromisedMs = ctx->mICDManager.StayActiveRequest(stayActiveRequestedMs); + // confirm the promised time is 20000 since the device is already planing to stay active longer than the requested time + NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 20000); + } }; } // namespace app @@ -556,6 +633,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("TestKeepActivemodeRequests", TestICDManager::TestKeepActivemodeRequests), NL_TEST_DEF("TestICDMRegisterUnregisterEvents", TestICDManager::TestICDMRegisterUnregisterEvents), NL_TEST_DEF("TestICDCounter", TestICDManager::TestICDCounter), + NL_TEST_DEF("TestICDStayActive", TestICDManager::TestICDMStayActive), NL_TEST_SENTINEL(), }; diff --git a/src/app/tests/suites/TestIcdManagementCluster.yaml b/src/app/tests/suites/TestIcdManagementCluster.yaml index d9d275b90faf3c..3d0081504c19bf 100644 --- a/src/app/tests/suites/TestIcdManagementCluster.yaml +++ b/src/app/tests/suites/TestIcdManagementCluster.yaml @@ -372,3 +372,82 @@ tests: value: 102 response: error: NOT_FOUND + + - label: + "Wait for a little bit less than the active mode duration (10000ms)" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 9000 + + - label: + "StayActive Scenario 1: Confirm the promised active duration is + increased to a specific if a value less than 30000ms is requested and + the device does not intend to stay active longer" + command: "StayActiveRequest" + arguments: + values: + - name: "StayActiveDuration" + value: 20000 + response: + values: + - name: "PromisedActiveDuration" + constraints: + type: int32u + minValue: 19500 + maxValue: 20500 + + - label: + "Wait for a little bit less than the new promied active mode duration + (20000ms)" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 19000 + + - label: + "StayActive Scenario 2: Confirm the promised active duration is + reduced to 30000ms if a value greater than 30000ms is requested" + command: "StayActiveRequest" + arguments: + values: + - name: "StayActiveDuration" + value: 35000 + response: + values: + - name: "PromisedActiveDuration" + constraints: + type: int32u + minValue: 29500 + maxValue: 30500 + + - label: + "Wait for a 20000 less than the new promied active mode duration + (30000ms)" + cluster: "DelayCommands" + command: "WaitForMs" + arguments: + values: + - name: "ms" + value: 10000 + + - label: + "StayActive Scenario 3: confirm that the device ignores the request if + the device intends to stay active longer than the requested duration + we should have about 20000ms left here" + command: "StayActiveRequest" + arguments: + values: + - name: "StayActiveDuration" + value: 10000 + response: + values: + - name: "PromisedActiveDuration" + constraints: + type: int32u + minValue: 19500 + maxValue: 20500 diff --git a/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml index a2665916e23204..d68cc30ea95f9c 100644 --- a/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml @@ -111,6 +111,7 @@ limitations under the License. Request the end device to stay in Active Mode for an additional ActiveModeThreshold + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 8611f4bdcf62d8..5dece392f3cd97 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -2740,6 +2740,10 @@ cluster IcdManagement = 70 { optional octet_string<16> verificationKey = 1; } + request struct StayActiveRequestRequest { + int32u stayActiveDuration = 0; + } + response struct StayActiveResponse = 4 { int32u promisedActiveDuration = 0; } @@ -2749,7 +2753,7 @@ cluster IcdManagement = 70 { /** Unregister a client from an end device */ fabric command access(invoke: manage) UnregisterClient(UnregisterClientRequest): DefaultSuccess = 2; /** Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ - command access(invoke: manage) StayActiveRequest(): StayActiveResponse = 3; + command access(invoke: manage) StayActiveRequest(StayActiveRequestRequest): StayActiveResponse = 3; } /** This cluster supports creating a simple timer functionality. */ diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 7025f3cde37858..c55a45b77aaca8 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -18170,14 +18170,18 @@ public void onResponse(StructType invokeStructValue) { }}, commandId, commandArgs, timedInvokeTimeoutMs); } - public void stayActiveRequest(StayActiveResponseCallback callback) { - stayActiveRequest(callback, 0); + public void stayActiveRequest(StayActiveResponseCallback callback, Long stayActiveDuration) { + stayActiveRequest(callback, stayActiveDuration, 0); } - public void stayActiveRequest(StayActiveResponseCallback callback, int timedInvokeTimeoutMs) { + public void stayActiveRequest(StayActiveResponseCallback callback, Long stayActiveDuration, int timedInvokeTimeoutMs) { final long commandId = 3L; ArrayList elements = new ArrayList<>(); + final long stayActiveDurationFieldID = 0L; + BaseTLVType stayActiveDurationtlvValue = new UIntType(stayActiveDuration); + elements.add(new StructElement(stayActiveDurationFieldID, stayActiveDurationtlvValue)); + StructType commandArgs = new StructType(elements); invoke(new InvokeCallbackImpl(callback) { @Override diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index a1d43e3a7933a5..3437b8a981e476 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -5881,6 +5881,23 @@ public static UnregisterClientCommandField value(int id) throws NoSuchFieldError } throw new NoSuchFieldError(); } + }public enum StayActiveRequestCommandField {StayActiveDuration(0),; + private final int id; + StayActiveRequestCommandField(int id) { + this.id = id; + } + + public int getID() { + return id; + } + public static StayActiveRequestCommandField value(int id) throws NoSuchFieldError { + for (StayActiveRequestCommandField field : StayActiveRequestCommandField.values()) { + if (field.getID() == id) { + return field; + } + } + throw new NoSuchFieldError(); + } }@Override public String getAttributeName(long id) throws NoSuchFieldError { return Attribute.value(id).toString(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 5162f9766a74da..4ded328fad6ed5 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -23352,10 +23352,16 @@ public Map> getCommandMap() { icdManagementClusterInteractionInfoMap.put("unregisterClient", icdManagementunregisterClientInteractionInfo); Map icdManagementstayActiveRequestCommandParams = new LinkedHashMap(); + + CommandParameterInfo icdManagementstayActiveRequeststayActiveDurationCommandParameterInfo = new CommandParameterInfo("stayActiveDuration", Long.class, Long.class); + icdManagementstayActiveRequestCommandParams.put("stayActiveDuration",icdManagementstayActiveRequeststayActiveDurationCommandParameterInfo); InteractionInfo icdManagementstayActiveRequestInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { ((ChipClusters.IcdManagementCluster) cluster) .stayActiveRequest((ChipClusters.IcdManagementCluster.StayActiveResponseCallback) callback + , (Long) + commandArguments.get("stayActiveDuration") + ); }, () -> new DelegatedIcdManagementClusterStayActiveResponseCallback(), diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt index 9421d92a1742f7..008aa6349da2ce 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt @@ -193,11 +193,17 @@ class IcdManagementCluster( logger.log(Level.FINE, "Invoke command succeeded: ${response}") } - suspend fun stayActiveRequest(timedInvokeTimeout: Duration? = null): StayActiveResponse { + suspend fun stayActiveRequest( + stayActiveDuration: UInt, + timedInvokeTimeout: Duration? = null + ): StayActiveResponse { val commandId: UInt = 3u val tlvWriter = TlvWriter() tlvWriter.startStructure(AnonymousTag) + + val TAG_STAY_ACTIVE_DURATION_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_STAY_ACTIVE_DURATION_REQ), stayActiveDuration) tlvWriter.endStructure() val request: InvokeRequest = diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 853fd1f98c38cd..3bc715829f27db 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -4078,6 +4078,7 @@ class ChipClusters: "commandId": 0x00000003, "commandName": "StayActiveRequest", "args": { + "stayActiveDuration": "int", }, }, }, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 3f5c116cc5515b..cd411b93d43301 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -14304,8 +14304,11 @@ class StayActiveRequest(ClusterCommand): def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( Fields=[ + ClusterObjectFieldDescriptor(Label="stayActiveDuration", Tag=0, Type=uint), ]) + stayActiveDuration: 'uint' = 0 + @dataclass class StayActiveResponse(ClusterCommand): cluster_id: typing.ClassVar[int] = 0x00000046 diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 9f7d6f8ffd5c64..6232b6424b8eb1 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -4491,9 +4491,7 @@ MTR_PROVISIONALLY_AVAILABLE * * Request the end device to stay in Active Mode for an additional ActiveModeThreshold */ -- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams * _Nullable)params completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; -- (void)stayActiveRequestWithCompletion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion - MTR_PROVISIONALLY_AVAILABLE; +- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams *)params completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; - (void)readAttributeIdleModeDurationWithCompletion:(void (^)(NSNumber * _Nullable value, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; - (void)subscribeAttributeIdleModeDurationWithParams:(MTRSubscribeParams *)params diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm index 8cca4c8ed5a7d5..b375488f292fc8 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.mm @@ -37049,11 +37049,7 @@ - (void)unregisterClientWithParams:(MTRICDManagementClusterUnregisterClientParam queue:self.callbackQueue completion:responseHandler]; } -- (void)stayActiveRequestWithCompletion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion -{ - [self stayActiveRequestWithParams:nil completion:completion]; -} -- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams * _Nullable)params completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion +- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams *)params completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion { if (params == nil) { params = [[MTRICDManagementClusterStayActiveRequestParams diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h index d8bc6458a2680d..7ce97cfe09442f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.h @@ -2090,9 +2090,7 @@ MTR_PROVISIONALLY_AVAILABLE - (void)registerClientWithParams:(MTRICDManagementClusterRegisterClientParams *)params expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterRegisterClientResponseParams * _Nullable data, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; - (void)unregisterClientWithParams:(MTRICDManagementClusterUnregisterClientParams *)params expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(MTRStatusCompletion)completion MTR_PROVISIONALLY_AVAILABLE; -- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams * _Nullable)params expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; -- (void)stayActiveRequestWithExpectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion - MTR_PROVISIONALLY_AVAILABLE; +- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams *)params expectedValues:(NSArray *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion MTR_PROVISIONALLY_AVAILABLE; - (NSDictionary * _Nullable)readAttributeIdleModeDurationWithParams:(MTRReadParams * _Nullable)params MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm index 2374d7f3cf9531..db279250cac8bf 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusters.mm @@ -6080,11 +6080,7 @@ - (void)unregisterClientWithParams:(MTRICDManagementClusterUnregisterClientParam completion:responseHandler]; } -- (void)stayActiveRequestWithExpectedValues:(NSArray *> *)expectedValues expectedValueInterval:(NSNumber *)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion -{ - [self stayActiveRequestWithParams:nil expectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs completion:completion]; -} -- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams * _Nullable)params expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion +- (void)stayActiveRequestWithParams:(MTRICDManagementClusterStayActiveRequestParams *)params expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completion:(void (^)(MTRICDManagementClusterStayActiveResponseParams * _Nullable data, NSError * _Nullable error))completion { if (params == nil) { params = [[MTRICDManagementClusterStayActiveRequestParams diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index 8f2b0705fe3283..ccd23b30f0aceb 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -3697,6 +3697,8 @@ MTR_PROVISIONALLY_AVAILABLE MTR_PROVISIONALLY_AVAILABLE @interface MTRICDManagementClusterStayActiveRequestParams : NSObject + +@property (nonatomic, copy) NSNumber * _Nonnull stayActiveDuration MTR_PROVISIONALLY_AVAILABLE; /** * Controls whether the command is a timed command (using Timed Invoke). * diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 60757c92d0dfdb..3105c9a9d21f80 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -9783,6 +9783,8 @@ @implementation MTRICDManagementClusterStayActiveRequestParams - (instancetype)init { if (self = [super init]) { + + _stayActiveDuration = @(0); _timedInvokeTimeoutMs = nil; _serverSideProcessingTimeout = nil; } @@ -9793,6 +9795,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; { auto other = [[MTRICDManagementClusterStayActiveRequestParams alloc] init]; + other.stayActiveDuration = self.stayActiveDuration; other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; @@ -9801,7 +9804,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: >", NSStringFromClass([self class])]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: stayActiveDuration:%@; >", NSStringFromClass([self class]), _stayActiveDuration]; return descriptionString; } @@ -9813,6 +9816,9 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader { chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::Type encodableStruct; ListFreer listFreer; + { + encodableStruct.stayActiveDuration = self.stayActiveDuration.unsignedIntValue; + } auto buffer = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); if (buffer.IsNull()) { diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 427bf00c99c3eb..d91a80d9be8f57 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -142,6 +142,9 @@ class DLL_EXPORT Layer * This method searches for the timer matching the provided parameters. * and returns whether it is still "running" and waiting to trigger or not. * + * @note This is used to verify by how long the ExtendTimer method extends the timer, as it may ignore an extension request + * if it is shorter than the current timer's remaining time. + * * @param[in] onComplete A pointer to the function called when timer expires. * @param[in] appState A pointer to the application state object used when timer expires. * @@ -150,6 +153,17 @@ class DLL_EXPORT Layer */ virtual bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) = 0; + /** + * @brief + * This method searches for the timer matching the provided parameters + * and returns the remaining time left before it expires. + * @param[in] onComplete A pointer to the function called when timer expires. + * @param[in] appState A pointer to the application state object used when timer expires. + * + * @return The remaining time left before the timer expires. + */ + virtual Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) = 0; + /** * @brief This method cancels a one-shot timer, started earlier through @p StartTimer(). This method must * be called while in the Matter context (from the Matter event loop, or while holding the Matter diff --git a/src/system/SystemLayerImplFreeRTOS.cpp b/src/system/SystemLayerImplFreeRTOS.cpp index cfc8e091208f86..59265f61d03693 100644 --- a/src/system/SystemLayerImplFreeRTOS.cpp +++ b/src/system/SystemLayerImplFreeRTOS.cpp @@ -96,6 +96,11 @@ bool LayerImplFreeRTOS::IsTimerActive(TimerCompleteCallback onComplete, void * a return (mTimerList.GetRemainingTime(onComplete, appState) > Clock::kZero); } +Clock::Timeout LayerImplFreeRTOS::GetRemainingTime(TimerCompleteCallback onComplete, void * appState) +{ + return mTimerList.GetRemainingTime(onComplete, appState); +} + void LayerImplFreeRTOS::CancelTimer(TimerCompleteCallback onComplete, void * appState) { assertChipStackLockedByCurrentThread(); diff --git a/src/system/SystemLayerImplFreeRTOS.h b/src/system/SystemLayerImplFreeRTOS.h index 2e7401dfce03ce..fe0e9be4bc7e33 100644 --- a/src/system/SystemLayerImplFreeRTOS.h +++ b/src/system/SystemLayerImplFreeRTOS.h @@ -42,6 +42,7 @@ class LayerImplFreeRTOS : public LayerFreeRTOS CHIP_ERROR StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override; CHIP_ERROR ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override; bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) override; + Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) override; void CancelTimer(TimerCompleteCallback onComplete, void * appState) override; CHIP_ERROR ScheduleWork(TimerCompleteCallback onComplete, void * appState) override; diff --git a/src/system/SystemLayerImplSelect.cpp b/src/system/SystemLayerImplSelect.cpp index 7ae25a0a59d134..86ff1cb62da42f 100644 --- a/src/system/SystemLayerImplSelect.cpp +++ b/src/system/SystemLayerImplSelect.cpp @@ -255,6 +255,11 @@ bool LayerImplSelect::IsTimerActive(TimerCompleteCallback onComplete, void * app return timerIsActive; } +Clock::Timeout LayerImplSelect::GetRemainingTime(TimerCompleteCallback onComplete, void * appState) +{ + return mTimerList.GetRemainingTime(onComplete, appState); +} + void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState) { assertChipStackLockedByCurrentThread(); diff --git a/src/system/SystemLayerImplSelect.h b/src/system/SystemLayerImplSelect.h index 060657e77a72ef..1bab3db9b5f39c 100644 --- a/src/system/SystemLayerImplSelect.h +++ b/src/system/SystemLayerImplSelect.h @@ -65,6 +65,7 @@ class LayerImplSelect : public LayerSocketsLoop CHIP_ERROR StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override; CHIP_ERROR ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override; bool IsTimerActive(TimerCompleteCallback onComplete, void * appState) override; + Clock::Timeout GetRemainingTime(TimerCompleteCallback onComplete, void * appState) override; void CancelTimer(TimerCompleteCallback onComplete, void * appState) override; CHIP_ERROR ScheduleWork(TimerCompleteCallback onComplete, void * appState) override; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index fc5a2bd789227b..32b62631ed30fd 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -9794,6 +9794,7 @@ namespace StayActiveRequest { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + encoder.Encode(to_underlying(Fields::kStayActiveDuration), stayActiveDuration); return encoder.Finalize(); } @@ -9807,6 +9808,19 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { return std::get(__element); } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kStayActiveDuration)) + { + err = DataModel::Decode(reader, stayActiveDuration); + } + else + { + } + + ReturnErrorOnFailure(err); } } } // namespace StayActiveRequest. diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index dfdf2e691686f2..89c36f3c6f147d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -13302,6 +13302,7 @@ struct DecodableType namespace StayActiveRequest { enum class Fields : uint8_t { + kStayActiveDuration = 0, }; struct Type @@ -13311,6 +13312,8 @@ struct Type static constexpr CommandId GetCommandId() { return Commands::StayActiveRequest::Id; } static constexpr ClusterId GetClusterId() { return Clusters::IcdManagement::Id; } + uint32_t stayActiveDuration = static_cast(0); + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; using ResponseType = Clusters::IcdManagement::Commands::StayActiveResponse::DecodableType; @@ -13324,6 +13327,7 @@ struct DecodableType static constexpr CommandId GetCommandId() { return Commands::StayActiveRequest::Id; } static constexpr ClusterId GetClusterId() { return Clusters::IcdManagement::Id; } + uint32_t stayActiveDuration = static_cast(0); CHIP_ERROR Decode(TLV::TLVReader & reader); }; }; // namespace StayActiveRequest diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 6191eb2aa3ec2d..8a3081edc0dc1b 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -4314,6 +4314,7 @@ class IcdManagementStayActiveRequest : public ClusterCommand IcdManagementStayActiveRequest(CredentialIssuerCommands * credsIssuerConfig) : ClusterCommand("stay-active-request", credsIssuerConfig) { + AddArgument("StayActiveDuration", 0, UINT32_MAX, &mRequest.stayActiveDuration); ClusterCommand::AddArguments(); } diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 60843e78e103e7..be046cb6155993 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -47653,6 +47653,9 @@ class IcdManagementStayActiveRequest : public ClusterCommand { IcdManagementStayActiveRequest() : ClusterCommand("stay-active-request") { +#if MTR_ENABLE_PROVISIONAL + AddArgument("StayActiveDuration", 0, UINT32_MAX, &mRequest.stayActiveDuration); +#endif // MTR_ENABLE_PROVISIONAL ClusterCommand::AddArguments(); } @@ -47667,6 +47670,9 @@ class IcdManagementStayActiveRequest : public ClusterCommand { __auto_type * cluster = [[MTRBaseClusterICDManagement alloc] initWithDevice:device endpointID:@(endpointId) queue:callbackQueue]; __auto_type * params = [[MTRICDManagementClusterStayActiveRequestParams alloc] init]; params.timedInvokeTimeoutMs = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; +#if MTR_ENABLE_PROVISIONAL + params.stayActiveDuration = [NSNumber numberWithUnsignedInt:mRequest.stayActiveDuration]; +#endif // MTR_ENABLE_PROVISIONAL uint16_t repeatCount = mRepeatCount.ValueOr(1); uint16_t __block responsesNeeded = repeatCount; while (repeatCount--) { @@ -47693,6 +47699,7 @@ class IcdManagementStayActiveRequest : public ClusterCommand { } private: + chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::Type mRequest; }; #endif // MTR_ENABLE_PROVISIONAL From 10ff1f6fb9949043fc955646e29878f1c4a6c816 Mon Sep 17 00:00:00 2001 From: Jean-Francois Penven <67962328+jepenven-silabs@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:13:35 -0500 Subject: [PATCH 15/52] Fix header guard since 2 headers had the same (#32297) --- src/platform/silabs/rs911x/rsi_ble_config.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/platform/silabs/rs911x/rsi_ble_config.h b/src/platform/silabs/rs911x/rsi_ble_config.h index bf40cad514dada..72f830b423ef9d 100644 --- a/src/platform/silabs/rs911x/rsi_ble_config.h +++ b/src/platform/silabs/rs911x/rsi_ble_config.h @@ -14,9 +14,7 @@ * sections of the MSLA applicable to Source Code. * ******************************************************************************/ - -#ifndef RSI_BLE_CONFIG_H -#define RSI_BLE_CONFIG_H +#pragma once #include "rsi_ble_apis.h" #if (SIWX_917 | EXP_BOARD) @@ -315,6 +313,4 @@ typedef struct rsi_ble_s uint16_t DATA_ix; uint16_t att_rec_list_count; rsi_ble_att_list_t att_rec_list[NO_OF_VAL_ATT]; -} rsi_ble_t; - -#endif \ No newline at end of file +} rsi_ble_t; \ No newline at end of file From d812a168a3c69ceab56969e265c08d85033b1949 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Mon, 26 Feb 2024 17:47:00 +0100 Subject: [PATCH 16/52] [Matter.framework] Get downloadLogOfType to work over XPC (#32319) * [Matter.framework] Move downloadLogOfType from MTRDevice to MTRBaseDevice * [Matter.framework] Get downloadLogOfType to work over XPC * Add a simple test to MTRXPCProtocolTests.m to check that the XPC protocol works * Add a simple test to MTRXPCListenerSampleTests * [Matter.framework] Do not delete the file containing the log data once the downloadLogOfType completion is done --- .github/workflows/darwin.yaml | 3 +- .../commands/bdx/DownloadLogCommand.mm | 2 +- src/darwin/Framework/CHIP/MTRBaseDevice.h | 21 ++++++++ src/darwin/Framework/CHIP/MTRBaseDevice.mm | 13 +++++ src/darwin/Framework/CHIP/MTRDevice.h | 22 -------- src/darwin/Framework/CHIP/MTRDevice.mm | 12 ----- .../Framework/CHIP/MTRDeviceController+XPC.h | 10 ++++ .../Framework/CHIP/MTRDeviceController+XPC.mm | 4 ++ src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm | 32 ++++++++++++ .../CHIP/MTRDiagnosticLogsDownloader.mm | 1 - .../CHIPTests/MTRXPCListenerSampleTests.m | 46 ++++++++++++++++ .../Framework/CHIPTests/MTRXPCProtocolTests.m | 52 +++++++++++++++++++ 12 files changed, 181 insertions(+), 37 deletions(-) diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml index a58617131a035d..2662853b1f790b 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -103,7 +103,8 @@ jobs: # target versions instead? run: | mkdir -p /tmp/darwin/framework-tests - ../../../out/debug/chip-all-clusters-app --interface-id -1 > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & + echo "This is a simple log" > /tmp/darwin/framework-tests/end_user_support_log.txt + ../../../out/debug/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & ../../../out/debug/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & # Disable BLE (CHIP_IS_BLE=NO) because the app does not have the permission to use it and that may crash the CI. diff --git a/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm index b105b6cb3098c8..dd693b4358304b 100644 --- a/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm +++ b/examples/darwin-framework-tool/commands/bdx/DownloadLogCommand.mm @@ -28,7 +28,7 @@ ChipLogProgress(chipTool, "Downloading logs from node 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId)); MTRDeviceController * commissioner = CurrentCommissioner(); - auto * device = [MTRDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + auto * device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; auto logType = static_cast(mLogType); auto queue = dispatch_queue_create("com.chip.bdx.downloader", DISPATCH_QUEUE_SERIAL); diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.h b/src/darwin/Framework/CHIP/MTRBaseDevice.h index af0a15bd1a6857..1fe9735932a6d0 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.h +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.h @@ -19,6 +19,7 @@ #import #import +#import @class MTRSetupPayload; @class MTRDeviceController; @@ -542,6 +543,26 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) reportHandler:(MTRDeviceResponseHandler)reportHandler subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); + +/** + * Download log of the desired type from the device. + * + * @param type The type of log being requested. This should correspond to a value in the enum MTRDiagnosticLogType. + * @param timeout The timeout for getting the log. If the timeout expires, completion will be called with whatever + * has been retrieved by that point (which might be none or a partial log). + * If the timeout is set to 0, the request will not expire and completion will not be called until + * the log is fully retrieved or an error occurs. + * @param queue The queue on which completion will be called. + * @param completion The completion handler that is called after attempting to retrieve the requested log. + * - In case of success, the completion handler is called with a non-nil URL and a nil error. + * - If there is an error, a non-nil error is used and the url can be non-nil too if some logs have already been downloaded. + */ +- (void)downloadLogOfType:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + queue:(dispatch_queue_t)queue + completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion + MTR_NEWLY_AVAILABLE; + @end /** diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm index 2a981cdd860fff..13154f8ac97aa2 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm @@ -2147,6 +2147,19 @@ + (NSDictionary *)eventReportForHeader:(const chip::app::EventHeader &)header an return buffer; } + +- (void)downloadLogOfType:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + queue:(dispatch_queue_t)queue + completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion +{ + [_deviceController downloadLogFromNodeWithID:@(_nodeID) + type:type + timeout:timeout + queue:queue + completion:completion]; +} + @end @implementation MTRBaseDevice (Deprecated) diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h index d4641d5dd321b3..23a1ecf51ad56c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.h +++ b/src/darwin/Framework/CHIP/MTRDevice.h @@ -18,7 +18,6 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN @@ -326,27 +325,6 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) */ - (void)removeClientDataForKey:(NSString *)key endpointID:(NSNumber *)endpointID MTR_UNSTABLE_API; -/** - * Download log of the desired type from the device. - * - * Note: The consumer of this API should move the file that the url points to or open it for reading before the - * completion handler returns. Otherwise, the file will be deleted, and the data will be lost. - * - * @param type The type of log being requested. This should correspond to a value in the enum MTRDiagnosticLogType. - * @param timeout The timeout for getting the log. If the timeout expires, completion will be called with whatever - * has been retrieved by that point (which might be none or a partial log). - * If the timeout is set to 0, the request will not expire and completion will not be called until - * the log is fully retrieved or an error occurs. - * @param queue The queue on which completion will be called. - * @param completion The completion handler that is called after attempting to retrieve the requested log. - * - In case of success, the completion handler is called with a non-nil URL and a nil error. - * - If there is an error, a non-nil error is used and the url can be non-nil too if some logs have already been downloaded. - */ -- (void)downloadLogOfType:(MTRDiagnosticLogType)type - timeout:(NSTimeInterval)timeout - queue:(dispatch_queue_t)queue - completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion - MTR_NEWLY_AVAILABLE; @end MTR_EXTERN NSString * const MTRPreviousDataKey MTR_NEWLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 1b42caecf80c76..dbf1785c9bb6ba 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -1713,18 +1713,6 @@ - (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator [baseDevice openCommissioningWindowWithDiscriminator:discriminator duration:duration queue:queue completion:completion]; } -- (void)downloadLogOfType:(MTRDiagnosticLogType)type - timeout:(NSTimeInterval)timeout - queue:(dispatch_queue_t)queue - completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion -{ - [_deviceController downloadLogFromNodeWithID:_nodeID - type:type - timeout:timeout - queue:queue - completion:completion]; -} - #pragma mark - Cache management // assume lock is held diff --git a/src/darwin/Framework/CHIP/MTRDeviceController+XPC.h b/src/darwin/Framework/CHIP/MTRDeviceController+XPC.h index 4316ba65f25198..2c4ec56e180113 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController+XPC.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController+XPC.h @@ -20,6 +20,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -183,6 +184,15 @@ typedef void (^MTRValuesHandler)(id _Nullable values, NSError * _Nullable error) attributeId:(NSNumber * _Nullable)attributeId completion:(MTRValuesHandler)completion; +/** + * Requests downloading some logs + */ +- (void)downloadLogWithController:(id _Nullable)controller + nodeId:(uint64_t)nodeId + type:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + completion:(void (^)(NSString * _Nullable url, NSError * _Nullable error))completion; + @end /** diff --git a/src/darwin/Framework/CHIP/MTRDeviceController+XPC.mm b/src/darwin/Framework/CHIP/MTRDeviceController+XPC.mm index fa532f19aa833b..87ce5560381cca 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController+XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController+XPC.mm @@ -220,6 +220,10 @@ + (NSXPCInterface *)xpcInterfaceForServerProtocol forSelector:@selector(readAttributeCacheWithController:nodeId:endpointId:clusterId:attributeId:completion:) argumentIndex:0 ofReply:YES]; + [xpcInterface setClasses:GetXPCAllowedClasses() + forSelector:@selector(downloadLogWithController:nodeId:type:timeout:completion:) + argumentIndex:0 + ofReply:YES]; return xpcInterface; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm index 36dc5b27410da1..dee3ae86331812 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm @@ -427,6 +427,38 @@ - (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator }); } +- (void)downloadLogOfType:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + queue:(dispatch_queue_t)queue + completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion +{ + MTR_LOG_DEBUG("Downloading log ..."); + + __auto_type workBlock = ^(MTRDeviceControllerXPCProxyHandle * _Nullable handle, NSError * _Nullable error) { + if (error != nil) { + completion(nil, error); + return; + } + + [handle.proxy downloadLogWithController:self.controllerID + nodeId:self.nodeID.unsignedLongLongValue + type:type + timeout:timeout + completion:^(NSString * _Nullable url, NSError * _Nullable error) { + dispatch_async(queue, ^{ + MTR_LOG_DEBUG("Download log"); + completion([NSURL URLWithString:url], error); + // The following captures the proxy handle in the closure so that the + // handle won't be released prior to block call. + __auto_type handleRetainer = handle; + (void) handleRetainer; + }); + }]; + }; + + [self fetchProxyHandleWithQueue:queue completion:workBlock]; +} + - (void)fetchProxyHandleWithQueue:(dispatch_queue_t)queue completion:(MTRFetchProxyHandleCompletion)completion { if (self.controllerID != nil) { diff --git a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm index 46226d69357058..8d2832052979e7 100644 --- a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm +++ b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm @@ -165,7 +165,6 @@ - (instancetype)initWithType:(MTRDiagnosticLogType)type // data in the logs that the caller may find useful. For this reason, fileURL is passed in even // when there is an error but fileHandle is not nil. completion(strongSelf->_fileHandle ? fileURL : nil, bdxError); - [strongSelf deleteFile]; done(strongSelf); } diff --git a/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m b/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m index eabd682a5a0d59..3bf727db3dabb3 100644 --- a/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m +++ b/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m @@ -167,6 +167,28 @@ - (void)getAnyDeviceControllerWithCompletion:(void (^)(id _Nullable controller, completion(MTRDeviceControllerId, nil); } +- (void)downloadLogWithController:(id)controller + nodeId:(uint64_t)nodeId + type:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + completion:(void (^)(NSString * _Nullable url, NSError * _Nullable error))completion +{ + (void) controller; + __auto_type sharedController = sController; + if (sharedController) { + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device downloadLogOfType:type + timeout:timeout + queue:dispatch_get_main_queue() + completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + completion([url absoluteString], error); + }]; + } else { + NSLog(@"Failed to get shared controller"); + completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); + } +} + - (void)readAttributeWithController:(id)controller nodeId:(uint64_t)nodeId endpointId:(NSNumber * _Nullable)endpointId @@ -1865,6 +1887,30 @@ - (void)test015_MTRDeviceInteraction }, @(NO)); } +- (void)test016_DownloadLog +{ + XCTestExpectation * expectation = + [self expectationWithDescription:@"Download EndUserSupport log"]; + + MTRBaseDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + + [device downloadLogOfType:MTRDiagnosticLogTypeEndUserSupport + timeout:10 + queue:queue + completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + NSLog(@"downloadLogOfType: url: %@, error: %@", url, error); + XCTAssertNil(error); + + NSError * readError; + NSString * fileContent = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&readError]; + XCTAssertNil(readError); + XCTAssertTrue([fileContent isEqualToString:@"This is a simple log\n"]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} + - (void)test900_SubscribeClusterStateCache { XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe attributes by cache"]; diff --git a/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m b/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m index d3d010ff7f0764..f626323cefb162 100644 --- a/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m +++ b/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m @@ -116,6 +116,8 @@ @interface MTRXPCProtocolTests @property (readwrite, strong) void (^handleReadClusterStateCache) (id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)); +@property (readwrite, strong) void (^handleDownloadLog)(id controller, NSNumber * nodeId, MTRDiagnosticLogType type, NSTimeInterval timeout, + void (^completion)(NSString * _Nullable url, NSError * _Nullable error)); @end @@ -274,6 +276,18 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller }); } +- (void)downloadLogWithController:(id)controller + nodeId:(uint64_t)nodeId + type:(MTRDiagnosticLogType)type + timeout:(NSTimeInterval)timeout + completion:(void (^)(NSString * _Nullable url, NSError * _Nullable error))completion +{ + dispatch_async(dispatch_get_main_queue(), ^{ + XCTAssertNotNil(self.handleDownloadLog); + self.handleDownloadLog(controller, @(nodeId), type, timeout, completion); + }); +} + - (void)setUp { [self setContinueAfterFailure:NO]; @@ -300,6 +314,44 @@ - (void)tearDown _xpcDisconnectExpectation = nil; } +- (void)testDownloadLogSuccess +{ + uint64_t myNodeId = 9876543210; + NSString * myBdxURL = @"bdx://foo"; + NSTimeInterval myTimeout = 10; + + XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; + XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; + + __auto_type uuid = self.controllerUUID; + _handleDownloadLog = ^(id controller, NSNumber * nodeId, MTRDiagnosticLogType type, NSTimeInterval timeout, + void (^completion)(NSString * _Nullable url, NSError * _Nullable error)) { + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId); + [callExpectation fulfill]; + completion(myBdxURL, nil); + }; + + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Downloading..."); + [device downloadLogOfType:MTRDiagnosticLogTypeEndUserSupport + timeout:myTimeout + queue:dispatch_get_main_queue() + completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + NSLog(@"Read url: %@", url); + XCTAssertNotNil(url); + XCTAssertNil(error); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; + + [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; + + // When download is done, connection should have been released + [self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds]; + XCTAssertNil(_xpcConnection); +} + - (void)testReadAttributeSuccess { uint64_t myNodeId = 9876543210; From 116317ff924a3c36ccd5ed9d2c6e1dc709edc57a Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Mon, 26 Feb 2024 12:01:00 -0500 Subject: [PATCH 17/52] remove the header `src/app/util/common.h` (#32320) * remove `src/app/util/common.h * Fix some typos * Remove some references of util/common.h * Fix up comment * Fix lint - a file got removed * Make the paths to the files full-paths --- .github/workflows/lint.yml | 1 - .../src/ThermostaticRadiatorValveManager.cpp | 2 +- .../identify-server/identify-server.cpp | 3 --- src/app/util/attribute-storage.cpp | 9 +++---- src/app/util/attribute-table.cpp | 6 +---- src/app/util/common.h | 24 ------------------- src/app/util/util.cpp | 4 +++- 7 files changed, 8 insertions(+), 41 deletions(-) delete mode 100644 src/app/util/common.h diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 69613217033e8b..e0255646dbc487 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -114,7 +114,6 @@ jobs: --known-failure app/util/attribute-table.h \ --known-failure app/util/binding-table.cpp \ --known-failure app/util/binding-table.h \ - --known-failure app/util/common.h \ --known-failure app/util/config.h \ --known-failure app/util/DataModelHandler.cpp \ --known-failure app/util/DataModelHandler.h \ diff --git a/examples/thermostat/qpg/src/ThermostaticRadiatorValveManager.cpp b/examples/thermostat/qpg/src/ThermostaticRadiatorValveManager.cpp index 55173272977964..a7375d54dc45ac 100644 --- a/examples/thermostat/qpg/src/ThermostaticRadiatorValveManager.cpp +++ b/examples/thermostat/qpg/src/ThermostaticRadiatorValveManager.cpp @@ -23,7 +23,7 @@ #include "AppConfig.h" #include "AppTask.h" -#include "app/util/common.h" + #include #include diff --git a/src/app/clusters/identify-server/identify-server.cpp b/src/app/clusters/identify-server/identify-server.cpp index 257adf934a5328..6ea8a34ca4db33 100644 --- a/src/app/clusters/identify-server/identify-server.cpp +++ b/src/app/clusters/identify-server/identify-server.cpp @@ -24,10 +24,7 @@ #include #include #include - #include -#include -#include #include #include #include diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index bd785c5b9682b1..0b94cc8f4c211c 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "app/util/common.h" +#include + #include #include #include #include -#include #include #include #include @@ -36,10 +36,7 @@ using chip::Protocols::InteractionModel::Status; // - zap-generated/callback.h is needed because endpoint_config will call the // corresponding callbacks (via GENERATED_FUNCTION_ARRAYS) and the include // for it is: -// util/common.h -// -> util/af.h -// -> util/config.h -// -> zap-generated/endpoint_config.h +// util/config.h -> zap-generated/endpoint_config.h #include using namespace chip; diff --git a/src/app/util/attribute-table.cpp b/src/app/util/attribute-table.cpp index d16942646ba929..de68b7e6a77f98 100644 --- a/src/app/util/attribute-table.cpp +++ b/src/app/util/attribute-table.cpp @@ -14,13 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// this file contains all the common includes for clusters in the zcl-util +#include #include - -// for pulling in defines dealing with EITHER server or client -#include "app/util/common.h" #include #include #include diff --git a/src/app/util/common.h b/src/app/util/common.h deleted file mode 100644 index c2e42889313ddf..00000000000000 --- a/src/app/util/common.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * 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. - */ - -#pragma once - -// App framework -#include -#include -#include -#include diff --git a/src/app/util/util.cpp b/src/app/util/util.cpp index e327673fc3cbf4..e8163547d94d53 100644 --- a/src/app/util/util.cpp +++ b/src/app/util/util.cpp @@ -15,12 +15,14 @@ * limitations under the License. */ -#include "app/util/common.h" +#include + #include #include #include #include #include +#include #include #include #include From 0a54586c2e73b4571c489938ad8aefe00cc511ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:10:32 +0000 Subject: [PATCH 18/52] Bump third_party/imgui/repo from `5360903` to `277ae93` (#32317) Bumps [third_party/imgui/repo](https://github.com/ocornut/imgui) from `5360903` to `277ae93`. - [Release notes](https://github.com/ocornut/imgui/releases) - [Commits](https://github.com/ocornut/imgui/compare/536090303a8fca7d896f77d6d63dc59249bc87f4...277ae93c41314ba5f4c7444f37c4319cdf07e8cf) --- updated-dependencies: - dependency-name: third_party/imgui/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third_party/imgui/repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/imgui/repo b/third_party/imgui/repo index 536090303a8fca..277ae93c41314b 160000 --- a/third_party/imgui/repo +++ b/third_party/imgui/repo @@ -1 +1 @@ -Subproject commit 536090303a8fca7d896f77d6d63dc59249bc87f4 +Subproject commit 277ae93c41314ba5f4c7444f37c4319cdf07e8cf From 5b1b617d62fecb655a3634fe61924b3fd4702ee7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:10:45 +0000 Subject: [PATCH 19/52] Bump third_party/nanopb/repo from `1f0c2e1` to `41319af` (#32314) Bumps [third_party/nanopb/repo](https://github.com/nanopb/nanopb) from `1f0c2e1` to `41319af`. - [Commits](https://github.com/nanopb/nanopb/compare/1f0c2e19c661f18dd88428858b8e965a26589e03...41319af88e569d4af31ea28a08fd2580a1f6655c) --- updated-dependencies: - dependency-name: third_party/nanopb/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third_party/nanopb/repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/nanopb/repo b/third_party/nanopb/repo index 1f0c2e19c661f1..41319af88e569d 160000 --- a/third_party/nanopb/repo +++ b/third_party/nanopb/repo @@ -1 +1 @@ -Subproject commit 1f0c2e19c661f18dd88428858b8e965a26589e03 +Subproject commit 41319af88e569d4af31ea28a08fd2580a1f6655c From 9c6cf9a1b2e2130bd98d522bb4076cc6152b9d73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:28:45 +0000 Subject: [PATCH 20/52] Bump third_party/mbedtls/repo from `039c903` to `36e6bd6` (#32316) Bumps [third_party/mbedtls/repo](https://github.com/ARMmbed/mbedtls) from `039c903` to `36e6bd6`. - [Release notes](https://github.com/ARMmbed/mbedtls/releases) - [Commits](https://github.com/ARMmbed/mbedtls/compare/039c903e7b2882af8e85ce5e090fd44e6a9d2289...36e6bd6926560933583dc04a7f92f69bdbafe8bd) --- updated-dependencies: - dependency-name: third_party/mbedtls/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third_party/mbedtls/repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/mbedtls/repo b/third_party/mbedtls/repo index 039c903e7b2882..36e6bd69265609 160000 --- a/third_party/mbedtls/repo +++ b/third_party/mbedtls/repo @@ -1 +1 @@ -Subproject commit 039c903e7b2882af8e85ce5e090fd44e6a9d2289 +Subproject commit 36e6bd6926560933583dc04a7f92f69bdbafe8bd From ee133f7879c95101325156cf60e6aa6359db0faa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:27:27 +0000 Subject: [PATCH 21/52] Bump third_party/openthread/repo from `33574ad` to `f0b6fce` (#32313) Bumps [third_party/openthread/repo](https://github.com/openthread/openthread) from `33574ad` to `f0b6fce`. - [Release notes](https://github.com/openthread/openthread/releases) - [Commits](https://github.com/openthread/openthread/compare/33574ad4175ffb088bcca047f4c8d5fb240d1495...f0b6fcea6ef77c9a54ab11767593f9a8798e3662) --- updated-dependencies: - dependency-name: third_party/openthread/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third_party/openthread/repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/openthread/repo b/third_party/openthread/repo index 33574ad4175ffb..f0b6fcea6ef77c 160000 --- a/third_party/openthread/repo +++ b/third_party/openthread/repo @@ -1 +1 @@ -Subproject commit 33574ad4175ffb088bcca047f4c8d5fb240d1495 +Subproject commit f0b6fcea6ef77c9a54ab11767593f9a8798e3662 From effff5953e51d55931399f1764c142b54569d0b5 Mon Sep 17 00:00:00 2001 From: andrei-menzopol <96489227+andrei-menzopol@users.noreply.github.com> Date: Mon, 26 Feb 2024 20:53:46 +0200 Subject: [PATCH 22/52] [Docker][NXP] Update Docker image for K32W1 SDK (#32312) Signed-off-by: Andrei Menzopol --- integrations/docker/images/base/chip-build/version | 2 +- .../docker/images/stage-2/chip-build-k32w/Dockerfile | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 7c7cf32a71a021..fe142046be7c1c 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -37 : [NXP] Update Docker image for RW61X SDK +38 : [NXP] Update Docker image for K32W1 SDK diff --git a/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile b/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile index 309bc061d5fc20..908c882da75191 100644 --- a/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-k32w/Dockerfile @@ -24,9 +24,9 @@ RUN set -x \ RUN set -x \ && mkdir -p k32w1 \ - && wget https://cache.nxp.com/lgfiles/bsps/SDK_2_12_6_K32W148-EVK.zip \ - && unzip SDK_2_12_6_K32W148-EVK.zip -d k32w1 \ - && rm -rf SDK_2_12_6_K32W148-EVK.zip + && wget https://cache.nxp.com/lgfiles/bsps/SDK_2_12_7_K32W148-EVK.zip \ + && unzip SDK_2_12_7_K32W148-EVK.zip -d k32w1 \ + && rm -rf SDK_2_12_7_K32W148-EVK.zip FROM ghcr.io/project-chip/chip-build:${VERSION} From 3b407b1209aeddfa0cd1bb9a35e31c6be87a8613 Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Tue, 27 Feb 2024 04:07:45 +0900 Subject: [PATCH 23/52] [Android] Support General value TLV parsing (#32285) * Support General value parsing * Restyled by clang-format * Add List, Array TLV * Restyled by clang-format --------- Co-authored-by: Restyled.io --- src/controller/java/AndroidCallbacks.cpp | 126 ++++++++++++++++++++++- 1 file changed, 121 insertions(+), 5 deletions(-) diff --git a/src/controller/java/AndroidCallbacks.cpp b/src/controller/java/AndroidCallbacks.cpp index 01ef820f82c6ed..fb7975262dff57 100644 --- a/src/controller/java/AndroidCallbacks.cpp +++ b/src/controller/java/AndroidCallbacks.cpp @@ -41,6 +41,8 @@ static const int MILLIS_SINCE_EPOCH = 1; // Add the bytes for attribute tag(1:control + 8:tag + 8:length) and structure(1:struct + 1:close container) static const int EXTRA_SPACE_FOR_ATTRIBUTE_TAG = 19; +jobject DecodeGeneralTLVValue(JNIEnv * env, TLV::TLVReader & readerForGeneralValueObject, CHIP_ERROR & err); + GetConnectedDeviceCallback::GetConnectedDeviceCallback(jobject wrapperCallback, jobject javaCallback, const char * callbackClassSignature) : mOnSuccess(OnDeviceConnectedFn, this), @@ -314,20 +316,24 @@ void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPat readerForJavaTLV.Init(*apData); jobject value = nullptr; -#ifdef USE_JAVA_TLV_ENCODE_DECODE TLV::TLVReader readerForJavaObject; readerForJavaObject.Init(*apData); - +#ifdef USE_JAVA_TLV_ENCODE_DECODE value = DecodeAttributeValue(aPath, readerForJavaObject, &err); // If we don't know this attribute, suppress it. if (err == CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB) { - err = CHIP_NO_ERROR; + TLV::TLVReader readerForGeneralValueObject; + readerForGeneralValueObject.Init(*apData); + value = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err); + err = CHIP_NO_ERROR; } VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to decode attribute with error %s", ErrorStr(err)); aPath.LogPath()); VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +#else + value = DecodeGeneralTLVValue(env, readerForJavaObject, err); #endif // Create TLV byte array to pass to Java layer size_t bufferLen = readerForJavaTLV.GetRemainingLength() + readerForJavaTLV.GetLengthRead(); @@ -468,18 +474,23 @@ void ReportCallback::OnEventData(const app::EventHeader & aEventHeader, TLV::TLV } jobject value = nullptr; -#ifdef USE_JAVA_TLV_ENCODE_DECODE TLV::TLVReader readerForJavaObject; readerForJavaObject.Init(*apData); +#ifdef USE_JAVA_TLV_ENCODE_DECODE value = DecodeEventValue(aEventHeader.mPath, readerForJavaObject, &err); // If we don't know this event, just skip it. if (err == CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB) { - err = CHIP_NO_ERROR; + TLV::TLVReader readerForGeneralValueObject; + readerForGeneralValueObject.Init(*apData); + value = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err); + err = CHIP_NO_ERROR; } VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Fail to decode event with error %s", ErrorStr(err)); aEventHeader.LogPath()); VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +#else + value = DecodeGeneralTLVValue(env, readerForJavaObject, err); #endif // Create TLV byte array to pass to Java layer @@ -907,6 +918,111 @@ void InvokeCallback::ReportError(const char * message, ChipError::StorageType er VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); } +jobject DecodeGeneralTLVValue(JNIEnv * env, TLV::TLVReader & readerForGeneralValueObject, CHIP_ERROR & err) +{ + jobject retValue = nullptr; + + switch (readerForGeneralValueObject.GetType()) + { + case TLV::kTLVType_SignedInteger: { + int64_t signedValue = 0; + VerifyOrReturnValue(readerForGeneralValueObject.Get(signedValue) == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Get TLV Value fail!")); + err = JniReferences::GetInstance().CreateBoxedObject("java/lang/Long", "(J)V", static_cast(signedValue), + retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!")); + return retValue; + } + case TLV::kTLVType_UnsignedInteger: { + uint64_t unsignedValue = 0; + VerifyOrReturnValue(readerForGeneralValueObject.Get(unsignedValue) == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Get TLV Value fail!")); + err = JniReferences::GetInstance().CreateBoxedObject("java/lang/Long", "(J)V", static_cast(unsignedValue), + retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!")); + return retValue; + } + case TLV::kTLVType_Boolean: { + bool booleanValue = false; + VerifyOrReturnValue(readerForGeneralValueObject.Get(booleanValue) == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Get TLV Value fail!")); + err = JniReferences::GetInstance().CreateBoxedObject("java/lang/Boolean", "(Z)V", + static_cast(booleanValue), retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!")); + return retValue; + } + case TLV::kTLVType_FloatingPointNumber: { + double doubleValue = 0.0; + VerifyOrReturnValue(readerForGeneralValueObject.Get(doubleValue) == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Get TLV Value fail!")); + err = JniReferences::GetInstance().CreateBoxedObject("java/lang/Double", "(D)V", static_cast(doubleValue), + retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Create Boxed Object fail!")); + return retValue; + } + case TLV::kTLVType_UTF8String: { + uint32_t bufferLen = readerForGeneralValueObject.GetLength(); + std::unique_ptr buffer = std::unique_ptr(new char[bufferLen + 1]); + err = readerForGeneralValueObject.GetString(buffer.get(), bufferLen + 1); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Get TLV Value fail!")); + chip::CharSpan valueSpan(buffer.get(), bufferLen); + chip::JniReferences::GetInstance().CharToStringUTF(valueSpan, retValue); + return retValue; + } + case TLV::kTLVType_ByteString: { + uint32_t bufferLen = readerForGeneralValueObject.GetLength(); + std::unique_ptr buffer = std::unique_ptr(new uint8_t[bufferLen + 1]); + err = readerForGeneralValueObject.GetBytes(buffer.get(), bufferLen + 1); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, ChipLogProgress(Controller, "Get TLV Value fail!")); + + jbyteArray valueByteArray = env->NewByteArray(static_cast(bufferLen)); + env->SetByteArrayRegion(valueByteArray, 0, static_cast(bufferLen), reinterpret_cast(buffer.get())); + + return static_cast(valueByteArray); + } + case TLV::kTLVType_Array: { + TLV::TLVType containerType; + err = readerForGeneralValueObject.EnterContainer(containerType); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "EnterContainer fail! : %" CHIP_ERROR_FORMAT, err.Format())); + err = chip::JniReferences::GetInstance().CreateArrayList(retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "CreateArrayList fail! : %" CHIP_ERROR_FORMAT, err.Format())); + while (readerForGeneralValueObject.Next() == CHIP_NO_ERROR) + { + jobject inValue = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Can't parse general value : %" CHIP_ERROR_FORMAT, err.Format())); + err = chip::JniReferences::GetInstance().AddToList(retValue, inValue); + } + return retValue; + } + case TLV::kTLVType_List: { + TLV::TLVType containerType; + err = readerForGeneralValueObject.EnterContainer(containerType); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "EnterContainer fail! : %" CHIP_ERROR_FORMAT, err.Format())); + err = chip::JniReferences::GetInstance().CreateArrayList(retValue); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "CreateArrayList fail! : %" CHIP_ERROR_FORMAT, err.Format())); + while (readerForGeneralValueObject.Next() == CHIP_NO_ERROR) + { + jobject inValue = DecodeGeneralTLVValue(env, readerForGeneralValueObject, err); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogProgress(Controller, "Can't parse general value : %" CHIP_ERROR_FORMAT, err.Format())); + err = chip::JniReferences::GetInstance().AddToList(retValue, inValue); + } + return retValue; + } + case TLV::kTLVType_Null: { + return nullptr; + } + default: + err = CHIP_ERROR_WRONG_TLV_TYPE; + return nullptr; + } +} + jlong newConnectedDeviceCallback(JNIEnv * env, jobject self, jobject callback) { chip::DeviceLayer::StackLock lock; From 49895313241524d9a90c65aae9a195435fc5aaab Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Tue, 27 Feb 2024 03:13:57 +0800 Subject: [PATCH 24/52] ESP32: fix conversion error for EndpointQueueFilter (#32287) --- src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp | 4 ++-- src/platform/ESP32/ESP32EndpointQueueFilter.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp b/src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp index 8fb52e7bb71629..5fefcb5490f059 100644 --- a/src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp +++ b/src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp @@ -1119,8 +1119,8 @@ void ConnectivityManagerImpl::OnStationIPv6AddressAvailable(const ip_event_got_i { uint8_t dig1 = (station_mac[i] & 0xF0) >> 4; uint8_t dig2 = station_mac[i] & 0x0F; - station_mac_str[2 * i] = dig1 > 9 ? ('A' + dig1 - 0xA) : ('0' + dig1); - station_mac_str[2 * i + 1] = dig2 > 9 ? ('A' + dig2 - 0xA) : ('0' + dig2); + station_mac_str[2 * i] = static_cast(dig1 > 9 ? ('A' + dig1 - 0xA) : ('0' + dig1)); + station_mac_str[2 * i + 1] = static_cast(dig2 > 9 ? ('A' + dig2 - 0xA) : ('0' + dig2)); } if (sEndpointQueueFilter.SetMdnsHostName(chip::CharSpan(station_mac_str)) == CHIP_NO_ERROR) { diff --git a/src/platform/ESP32/ESP32EndpointQueueFilter.h b/src/platform/ESP32/ESP32EndpointQueueFilter.h index e7d154dcf458af..031ea29b64d798 100644 --- a/src/platform/ESP32/ESP32EndpointQueueFilter.h +++ b/src/platform/ESP32/ESP32EndpointQueueFilter.h @@ -103,7 +103,7 @@ class ESP32EndpointQueueFilter : public EndpointQueueFilter { if (hostNameLowerCase[i] <= 'F' && hostNameLowerCase[i] >= 'A') { - hostNameLowerCase[i] = 'a' + hostNameLowerCase[i] - 'A'; + hostNameLowerCase[i] = static_cast('a' + hostNameLowerCase[i] - 'A'); } } return PayloadContains(payload, ByteSpan(mHostNameBuffer)) || PayloadContains(payload, ByteSpan(hostNameLowerCase)); From 44ca3957f15ed42f2e6d72fb1a4e1e827c8ed1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Mon, 26 Feb 2024 20:14:55 +0100 Subject: [PATCH 25/52] Add particulate matter (PM) events in AirQuality sensor README.md (#32255) * Update AirQuality sensor README.md Generate Particulate matter (PM) event * Update README.md --- .../air-quality-sensor-app/linux/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/air-quality-sensor-app/linux/README.md b/examples/air-quality-sensor-app/linux/README.md index 73139ef4aeeacb..38a0adfb2fe7bf 100644 --- a/examples/air-quality-sensor-app/linux/README.md +++ b/examples/air-quality-sensor-app/linux/README.md @@ -165,3 +165,21 @@ value. ``` $ echo '{"Name":"NitrogenDioxideConcentrationMeasurement","NewValue":1}' > /tmp/chip_air_quality_fifo_ ``` + +Generate event `Pm1ConcentrationMeasurement`, to change the PM1 value. + +``` +echo '{"Name":"Pm1ConcentrationMeasurement","NewValue":1}' > /tmp/chip_air_quality_fifo_ +``` + +Generate event `Pm25ConcentrationMeasurement`, to change the PM2.5 value. + +``` +echo '{"Name":"Pm25ConcentrationMeasurement","NewValue":2.5}' > /tmp/chip_air_quality_fifo_ +``` + +Generate event `Pm10ConcentrationMeasurement`, to change the PM10 value. + +``` +echo '{"Name":"Pm10ConcentrationMeasurement","NewValue":10}' > /tmp/chip_air_quality_fifo_ +``` From 86a0265e9d894a6c23a1c778c786b8c4614af4e6 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Mon, 26 Feb 2024 15:44:12 -0500 Subject: [PATCH 26/52] Add a `ObjectPoolIterator::Type` class to allow begin/end iteration on active objects in memory pools. (#32126) * Remove PoolCommon: bad name, non-member method, very very limited usage * Restyle * Have a working iterator (and unit test) for static bitmap iterator * Start unit testing for nested loops * Fix tests and implementation * Added another test * Extra unit test that we iterate correctly * Added comment about returning null on failure * Fix compile on fixed size pools * Restyle * Support iteration depth since it seems we need to delete objects during looping ... ouch! * Update to also have a post-iteration clean when using iterators * Make sure iterators on pools have a type that can be passed around * Switch to not use a cast * Fix types ... use auto because types are messy, unsure why * Rename for smaller diff * Rename for smaller diff * Correct the comment * More comments cleanup * Remove useless comment * make things compile for NRF * Restyle * Update the comment * Update allocated to active in naming * Add comment regarding iteration depth * Undo submodule update --------- Co-authored-by: Andrei Litvin --- src/lib/support/Pool.cpp | 74 ++++++++++--- src/lib/support/Pool.h | 170 ++++++++++++++++++++++++++++- src/lib/support/tests/TestPool.cpp | 157 +++++++++++++++++++++++--- 3 files changed, 369 insertions(+), 32 deletions(-) diff --git a/src/lib/support/Pool.cpp b/src/lib/support/Pool.cpp index c32b46be45e819..555127a7082d67 100644 --- a/src/lib/support/Pool.cpp +++ b/src/lib/support/Pool.cpp @@ -127,6 +127,46 @@ Loop StaticAllocatorBitmap::ForEachActiveObjectInner(void * context, Lambda lamb return Loop::Finish; } +size_t StaticAllocatorBitmap::FirstActiveIndex() +{ + size_t idx = 0; + for (size_t word = 0; word * kBitChunkSize < Capacity(); ++word) + { + auto & usage = mUsage[word]; + auto value = usage.load(std::memory_order_relaxed); + for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < Capacity(); ++offset) + { + if ((value & (kBit1 << offset)) != 0) + { + return idx; + } + idx++; + } + } + VerifyOrDie(idx == mCapacity); + return mCapacity; +} + +size_t StaticAllocatorBitmap::NextActiveIndexAfter(size_t start) +{ + size_t idx = 0; + for (size_t word = 0; word * kBitChunkSize < Capacity(); ++word) + { + auto & usage = mUsage[word]; + auto value = usage.load(std::memory_order_relaxed); + for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < Capacity(); ++offset) + { + if (((value & (kBit1 << offset)) != 0) && (start < idx)) + { + return idx; + } + idx++; + } + } + VerifyOrDie(idx == mCapacity); + return mCapacity; +} + #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP HeapObjectListNode * HeapObjectList::FindNode(void * object) const @@ -159,24 +199,30 @@ Loop HeapObjectList::ForEachNode(void * context, Lambda lambda) p = p->mNext; } --mIterationDepth; - if (mIterationDepth == 0 && mHaveDeferredNodeRemovals) + CleanupDeferredReleases(); + return result; +} + +void HeapObjectList::CleanupDeferredReleases() +{ + if (mIterationDepth != 0 || !mHaveDeferredNodeRemovals) { - // Remove nodes for released objects. - p = mNext; - while (p != this) + return; + } + // Remove nodes for released objects. + HeapObjectListNode * p = mNext; + while (p != this) + { + HeapObjectListNode * next = p->mNext; + if (p->mObject == nullptr) { - HeapObjectListNode * next = p->mNext; - if (p->mObject == nullptr) - { - p->Remove(); - Platform::Delete(p); - } - p = next; + p->Remove(); + Platform::Delete(p); } - - mHaveDeferredNodeRemovals = false; + p = next; } - return result; + + mHaveDeferredNodeRemovals = false; } #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP diff --git a/src/lib/support/Pool.h b/src/lib/support/Pool.h index a3959fd1e868f9..e4fb31769f4193 100644 --- a/src/lib/support/Pool.h +++ b/src/lib/support/Pool.h @@ -36,6 +36,9 @@ namespace chip { +template +class BitmapActiveObjectIterator; + namespace internal { class Statistics @@ -91,6 +94,16 @@ class StaticAllocatorBitmap : public internal::StaticAllocatorBase void * At(size_t index) { return static_cast(mElements) + mElementSize * index; } size_t IndexOf(void * element); + /// Returns the first index that is active (i.e. allocated data). + /// + /// If nothing is active, this will return mCapacity + size_t FirstActiveIndex(); + + /// Returns the next active index after `start`. + /// + /// If nothing else active/allocated, returns mCapacity + size_t NextActiveIndexAfter(size_t start); + using Lambda = Loop (*)(void * context, void * object); Loop ForEachActiveObjectInner(void * context, Lambda lambda); Loop ForEachActiveObjectInner(void * context, Loop lambda(void * context, const void * object)) const @@ -102,6 +115,10 @@ class StaticAllocatorBitmap : public internal::StaticAllocatorBase void * mElements; const size_t mElementSize; std::atomic * mUsage; + + /// allow accessing direct At() calls + template + friend class ::chip::BitmapActiveObjectIterator; }; template @@ -132,9 +149,9 @@ struct HeapObjectListNode mPrev->mNext = mNext; } - void * mObject; - HeapObjectListNode * mNext; - HeapObjectListNode * mPrev; + void * mObject = nullptr; + HeapObjectListNode * mNext = nullptr; + HeapObjectListNode * mPrev = nullptr; }; struct HeapObjectList : HeapObjectListNode @@ -158,6 +175,9 @@ struct HeapObjectList : HeapObjectListNode return const_cast(this)->ForEachNode(context, reinterpret_cast(lambda)); } + /// Cleans up any deferred releases IFF iteration depth is 0 + void CleanupDeferredReleases(); + size_t mIterationDepth = 0; bool mHaveDeferredNodeRemovals = false; }; @@ -166,6 +186,46 @@ struct HeapObjectList : HeapObjectListNode } // namespace internal +/// Provides iteration over active objects in a Bitmap pool. +/// +/// Creating and releasing items within a pool does not invalidate +/// an iterator, however there are no guarantees which objects the +/// iterator will return (i.e. newly created objects while iterating +/// may be visible or not to the iterator depending where they are +/// allocated). +/// +/// You are not prevented from releasing the object the iterator +/// currently points at. In that case, iterator should be advanced. +template +class BitmapActiveObjectIterator +{ +public: + using value_type = T; + using pointer = T *; + using reference = T &; + + explicit BitmapActiveObjectIterator(internal::StaticAllocatorBitmap * pool, size_t idx) : mPool(pool), mIndex(idx) {} + BitmapActiveObjectIterator() {} + + bool operator==(const BitmapActiveObjectIterator & other) const + { + return (AtEnd() && other.AtEnd()) || ((mPool == other.mPool) && (mIndex == other.mIndex)); + } + bool operator!=(const BitmapActiveObjectIterator & other) const { return !(*this == other); } + BitmapActiveObjectIterator & operator++() + { + mIndex = mPool->NextActiveIndexAfter(mIndex); + return *this; + } + T * operator*() const { return static_cast(mPool->At(mIndex)); } + +private: + bool AtEnd() const { return (mPool == nullptr) || (mIndex >= mPool->Capacity()); } + + internal::StaticAllocatorBitmap * mPool = nullptr; // pool that this belongs to + size_t mIndex = std::numeric_limits::max(); +}; + /** * @class ObjectPool * @@ -205,6 +265,9 @@ class BitMapObjectPool : public internal::StaticAllocatorBitmap BitMapObjectPool() : StaticAllocatorBitmap(mData.mMemory, mUsage, N, sizeof(T)) {} ~BitMapObjectPool() { VerifyOrDie(Allocated() == 0); } + BitmapActiveObjectIterator begin() { return BitmapActiveObjectIterator(this, FirstActiveIndex()); } + BitmapActiveObjectIterator end() { return BitmapActiveObjectIterator(this, N); } + template T * CreateObject(Args &&... args) { @@ -331,6 +394,91 @@ class HeapObjectPool : public internal::Statistics, public HeapObjectPoolExitHan #endif // __SANITIZE_ADDRESS__ } + /// Provides iteration over active objects in the pool. + /// + /// NOTE: There is extra logic to allow objects release WHILE the iterator is + /// active while still allowing to advance the iterator. + /// This is done by flagging an iteration depth whenever an active + /// iterator exists. This also means that while a pool iterator exists, releasing + /// of tracking memory objects may be deferred until the last active iterator is + /// released. + class ActiveObjectIterator + { + public: + using value_type = T; + using pointer = T *; + using reference = T &; + + ActiveObjectIterator() {} + ActiveObjectIterator(const ActiveObjectIterator & other) : mCurrent(other.mCurrent), mEnd(other.mEnd) + { + if (mEnd != nullptr) + { + // Iteration depth is used to support `Release` while an iterator is active. + // + // Code was historically using this functionality, so we support it here + // as well: while iteration is active, iteration depth is > 0. When it + // goes to 0, then any deferred `Release()` calls are executed. + mEnd->mIterationDepth++; + } + } + + ActiveObjectIterator & operator=(const ActiveObjectIterator & other) + { + if (mEnd != nullptr) + { + mEnd->mIterationDepth--; + mEnd->CleanupDeferredReleases(); + } + mCurrent = other.mCurrent; + mEnd = other.mEnd; + mEnd->mIterationDepth++; + } + + ~ActiveObjectIterator() + { + if (mEnd != nullptr) + { + mEnd->mIterationDepth--; + mEnd->CleanupDeferredReleases(); + } + } + + bool operator==(const ActiveObjectIterator & other) const + { + // extra current/end compare is to have all "end iterators" + // compare as equal (in particular default active object iterator is the end + // of an iterator) + return (mCurrent == other.mCurrent) || ((mCurrent == mEnd) && (other.mCurrent == other.mEnd)); + } + bool operator!=(const ActiveObjectIterator & other) const { return !(*this == other); } + ActiveObjectIterator & operator++() + { + do + { + mCurrent = mCurrent->mNext; + } while ((mCurrent != mEnd) && (mCurrent->mObject == nullptr)); + return *this; + } + T * operator*() const { return static_cast(mCurrent->mObject); } + + protected: + friend class HeapObjectPool; + + explicit ActiveObjectIterator(internal::HeapObjectListNode * current, internal::HeapObjectList * end) : + mCurrent(current), mEnd(end) + { + mEnd->mIterationDepth++; + } + + private: + internal::HeapObjectListNode * mCurrent = nullptr; + internal::HeapObjectList * mEnd = nullptr; + }; + + ActiveObjectIterator begin() { return ActiveObjectIterator(mObjects.mNext, &mObjects); } + ActiveObjectIterator end() { return ActiveObjectIterator(&mObjects, &mObjects); } + template T * CreateObject(Args &&... args) { @@ -456,6 +604,15 @@ enum class ObjectPoolMem #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP }; +template +struct ObjectPoolIterator; + +template +struct ObjectPoolIterator +{ + using Type = BitmapActiveObjectIterator; +}; + template class ObjectPool; @@ -465,6 +622,13 @@ class ObjectPool : public BitMapObjectPool }; #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +template +struct ObjectPoolIterator +{ + using Type = typename HeapObjectPool::ActiveObjectIterator; +}; + template class ObjectPool : public HeapObjectPool { diff --git a/src/lib/support/tests/TestPool.cpp b/src/lib/support/tests/TestPool.cpp index b78d2b3e281107..963a7b3c52f7ea 100644 --- a/src/lib/support/tests/TestPool.cpp +++ b/src/lib/support/tests/TestPool.cpp @@ -234,25 +234,54 @@ void TestForEachActiveObject(nlTestSuite * inSuite, void * inContext) objIds.insert(i); } + // Default constructor of an iterator should be pointing to the pool end. + { + typename ObjectPoolIterator::Type defaultIterator; + NL_TEST_ASSERT(inSuite, defaultIterator == pool.end()); + } + // Verify that iteration visits all objects. size_t count = 0; - size_t sum = 0; - pool.ForEachActiveObject([&](S * object) { - NL_TEST_ASSERT(inSuite, object != nullptr); - if (object == nullptr) - { - // NL_TEST_ASSERT doesn't stop running the test and we want to avoid nullptr dereference. + { + size_t sum = 0; + pool.ForEachActiveObject([&](S * object) { + NL_TEST_ASSERT(inSuite, object != nullptr); + if (object == nullptr) + { + // NL_TEST_ASSERT doesn't stop running the test and we want to avoid nullptr dereference. + return Loop::Continue; + } + NL_TEST_ASSERT(inSuite, objIds.count(object->mId) == 1); + objIds.erase(object->mId); + ++count; + sum += object->mId; return Loop::Continue; + }); + NL_TEST_ASSERT(inSuite, count == kSize); + NL_TEST_ASSERT(inSuite, sum == kSize * (kSize - 1) / 2); + NL_TEST_ASSERT(inSuite, objIds.size() == 0); + } + + // Test begin/end iteration + { + // re-create the above test environment, this time using iterators + for (size_t i = 0; i < kSize; ++i) + { + objIds.insert(i); } - NL_TEST_ASSERT(inSuite, objIds.count(object->mId) == 1); - objIds.erase(object->mId); - ++count; - sum += object->mId; - return Loop::Continue; - }); - NL_TEST_ASSERT(inSuite, count == kSize); - NL_TEST_ASSERT(inSuite, sum == kSize * (kSize - 1) / 2); - NL_TEST_ASSERT(inSuite, objIds.size() == 0); + count = 0; + size_t sum = 0; + for (auto v = pool.begin(); v != pool.end(); ++v) + { + NL_TEST_ASSERT(inSuite, objIds.count((*v)->mId) == 1); + objIds.erase((*v)->mId); + ++count; + sum += (*v)->mId; + } + NL_TEST_ASSERT(inSuite, count == kSize); + NL_TEST_ASSERT(inSuite, sum == kSize * (kSize - 1) / 2); + NL_TEST_ASSERT(inSuite, objIds.size() == 0); + } // Verify that returning Loop::Break stops iterating. count = 0; @@ -285,6 +314,42 @@ void TestForEachActiveObject(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, count == (kSize - 1) * kSize / 2); NL_TEST_ASSERT(inSuite, objIds.size() == 0); + // Verify that iteration can be nested for iterator types + { + count = 0; + for (auto v : pool) + { + objIds.insert(v->mId); + if (++count == kSize / 2) + { + break; + } + } + + count = 0; + for (auto outer : pool) + { + if (objIds.count(outer->mId) != 1) + { + continue; + } + + for (auto inner : pool) + { + if (inner == outer) + { + objIds.erase(inner->mId); + } + else + { + ++count; + } + } + } + NL_TEST_ASSERT(inSuite, count == (kSize - 1) * kSize / 2); + NL_TEST_ASSERT(inSuite, objIds.size() == 0); + } + count = 0; pool.ForEachActiveObject([&](S * object) { ++count; @@ -334,6 +399,68 @@ void TestForEachActiveObject(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, count >= kSize / 2); NL_TEST_ASSERT(inSuite, count <= kSize); + // Test begin/end iteration + { + count = 0; + for (auto object : pool) + { + ++count; + if ((object->mId % 2) == 0) + { + objArray[object->mId] = nullptr; + // NOTE: this explicitly tests if pool supports releasing while iterating + // this MUST be supported by contract of Pool iterators + pool.ReleaseObject(object); + } + else + { + objIds.insert(object->mId); + } + } + NL_TEST_ASSERT(inSuite, count == kSize); + NL_TEST_ASSERT(inSuite, objIds.size() == kSize / 2); + + // validate we iterate only over active objects + for (auto object : pool) + { + NL_TEST_ASSERT(inSuite, (object->mId % 2) == 1); + } + + for (size_t i = 0; i < kSize; ++i) + { + if ((i % 2) == 0) + { + NL_TEST_ASSERT(inSuite, objArray[i] == nullptr); + } + else + { + NL_TEST_ASSERT(inSuite, objArray[i] != nullptr); + NL_TEST_ASSERT(inSuite, objArray[i]->mId == i); + } + } + + count = 0; + for (auto object : pool) + { + ++count; + if ((object->mId % 2) != 1) + { + continue; + } + size_t id = object->mId - 1; + NL_TEST_ASSERT(inSuite, objArray[id] == nullptr); + objArray[id] = pool.CreateObject(id); + NL_TEST_ASSERT(inSuite, objArray[id] != nullptr); + } + for (size_t i = 0; i < kSize; ++i) + { + NL_TEST_ASSERT(inSuite, objArray[i] != nullptr); + NL_TEST_ASSERT(inSuite, objArray[i]->mId == i); + } + NL_TEST_ASSERT(inSuite, count >= kSize / 2); + NL_TEST_ASSERT(inSuite, count <= kSize); + } + pool.ReleaseAll(); } From dbfd4b53d64be6109cdd157bad4edc4bfe785c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:09:53 +0000 Subject: [PATCH 27/52] Bump third_party/pigweed/repo from `5165b9c` to `544a7b5` (#32315) * Bump third_party/pigweed/repo from `5165b9c` to `544a7b5` Bumps [third_party/pigweed/repo](https://github.com/google/pigweed) from `5165b9c` to `544a7b5`. - [Commits](https://github.com/google/pigweed/compare/5165b9c17a9933b8d0714ef701421bf065d5d66a...544a7b57c35e11690c73c390625a86a9e9a3ae51) --- updated-dependencies: - dependency-name: third_party/pigweed/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] * zap regen ... again.. we seem to go back and forth on format here --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrei Litvin --- .../app-templates/endpoint_config.h | 34 +++++++++---------- third_party/pigweed/repo | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index 38f04fbfea5857..6985b44ac27da5 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -302,15 +302,15 @@ { (uint16_t) 0x0, (uint16_t) 0x0, (uint16_t) 0x7 }, /* ControlMode */ \ \ /* Endpoint: 1, Cluster: Thermostat (server) */ \ - { (uint16_t) 0xA28, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* OccupiedCoolingSetpoint */ \ - { (uint16_t) 0x7D0, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* OccupiedHeatingSetpoint */ \ - { (uint16_t) 0x2BC, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* MinHeatSetpointLimit */ \ - { (uint16_t) 0xBB8, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* MaxHeatSetpointLimit */ \ - { (uint16_t) 0x640, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* MinCoolSetpointLimit */ \ - { (uint16_t) 0xC80, (uint16_t) - 0x6AB3, (uint16_t) 0x7FFF }, /* MaxCoolSetpointLimit */ \ - { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x19 }, /* MinSetpointDeadBand */ \ - { (uint16_t) 0x4, (uint16_t) 0x0, (uint16_t) 0x5 }, /* ControlSequenceOfOperation */ \ - { (uint16_t) 0x1, (uint16_t) 0x0, (uint16_t) 0x9 }, /* SystemMode */ \ + { (uint16_t) 0xA28, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* OccupiedCoolingSetpoint */ \ + { (uint16_t) 0x7D0, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* OccupiedHeatingSetpoint */ \ + { (uint16_t) 0x2BC, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MinHeatSetpointLimit */ \ + { (uint16_t) 0xBB8, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxHeatSetpointLimit */ \ + { (uint16_t) 0x640, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MinCoolSetpointLimit */ \ + { (uint16_t) 0xC80, (uint16_t) -0x6AB3, (uint16_t) 0x7FFF }, /* MaxCoolSetpointLimit */ \ + { (uint16_t) 0x19, (uint16_t) 0x0, (uint16_t) 0x19 }, /* MinSetpointDeadBand */ \ + { (uint16_t) 0x4, (uint16_t) 0x0, (uint16_t) 0x5 }, /* ControlSequenceOfOperation */ \ + { (uint16_t) 0x1, (uint16_t) 0x0, (uint16_t) 0x9 }, /* SystemMode */ \ \ /* Endpoint: 1, Cluster: Fan Control (server) */ \ { (uint16_t) 0x0, (uint16_t) 0x0, (uint16_t) 0x6 }, /* FanMode */ \ @@ -334,14 +334,14 @@ { (uint16_t) 0x0, (uint16_t) 0x0, (uint16_t) 0xFEFF }, /* StartUpColorTemperatureMireds */ \ \ /* Endpoint: 1, Cluster: Unit Testing (server) */ \ - { (uint16_t) 0x46, (uint16_t) 0x14, (uint16_t) 0x64 }, /* range_restricted_int8u */ \ - { (uint16_t) - 0x14, (uint16_t) - 0x28, (uint16_t) 0x32 }, /* range_restricted_int8s */ \ - { (uint16_t) 0xC8, (uint16_t) 0x64, (uint16_t) 0x3E8 }, /* range_restricted_int16u */ \ - { (uint16_t) - 0x64, (uint16_t) - 0x96, (uint16_t) 0xC8 }, /* range_restricted_int16s */ \ - { (uint16_t) 0x46, (uint16_t) 0x14, (uint16_t) 0x64 }, /* nullable_range_restricted_int8u */ \ - { (uint16_t) - 0x14, (uint16_t) - 0x28, (uint16_t) 0x32 }, /* nullable_range_restricted_int8s */ \ - { (uint16_t) 0xC8, (uint16_t) 0x64, (uint16_t) 0x3E8 }, /* nullable_range_restricted_int16u */ \ - { (uint16_t) - 0x64, (uint16_t) - 0x96, (uint16_t) 0xC8 }, /* nullable_range_restricted_int16s */ \ + { (uint16_t) 0x46, (uint16_t) 0x14, (uint16_t) 0x64 }, /* range_restricted_int8u */ \ + { (uint16_t) -0x14, (uint16_t) -0x28, (uint16_t) 0x32 }, /* range_restricted_int8s */ \ + { (uint16_t) 0xC8, (uint16_t) 0x64, (uint16_t) 0x3E8 }, /* range_restricted_int16u */ \ + { (uint16_t) -0x64, (uint16_t) -0x96, (uint16_t) 0xC8 }, /* range_restricted_int16s */ \ + { (uint16_t) 0x46, (uint16_t) 0x14, (uint16_t) 0x64 }, /* nullable_range_restricted_int8u */ \ + { (uint16_t) -0x14, (uint16_t) -0x28, (uint16_t) 0x32 }, /* nullable_range_restricted_int8s */ \ + { (uint16_t) 0xC8, (uint16_t) 0x64, (uint16_t) 0x3E8 }, /* nullable_range_restricted_int16u */ \ + { (uint16_t) -0x64, (uint16_t) -0x96, (uint16_t) 0xC8 }, /* nullable_range_restricted_int16s */ \ \ /* Endpoint: 2, Cluster: On/Off (server) */ \ { \ diff --git a/third_party/pigweed/repo b/third_party/pigweed/repo index 5165b9c17a9933..544a7b57c35e11 160000 --- a/third_party/pigweed/repo +++ b/third_party/pigweed/repo @@ -1 +1 @@ -Subproject commit 5165b9c17a9933b8d0714ef701421bf065d5d66a +Subproject commit 544a7b57c35e11690c73c390625a86a9e9a3ae51 From 2e35d01d984d29df4bc6f174d58b7888c72ebfe7 Mon Sep 17 00:00:00 2001 From: Carolina Lopes <116589288+ccruzagralopes@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:12:11 -0300 Subject: [PATCH 28/52] Support user input for python tests in TH (#32299) * Add supporting functions * Update TC-RVCOPSTATE-2.4 * Fix linting errors * Restyled by isort * Update src/python_testing/matter_testing_support.py --------- Co-authored-by: Restyled.io --- .../matter_yamltests/hooks.py | 11 ++++++++ src/python_testing/TC_RVCOPSTATE_2_4.py | 20 +++++++------ src/python_testing/matter_testing_support.py | 28 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/scripts/py_matter_yamltests/matter_yamltests/hooks.py b/scripts/py_matter_yamltests/matter_yamltests/hooks.py index 575a05d95871ad..9b202c7e94d122 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/hooks.py +++ b/scripts/py_matter_yamltests/matter_yamltests/hooks.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional + from .parser import TestStep @@ -213,6 +215,15 @@ async def step_manual(self): """ pass + def show_prompt(self, + msg: str, + placeholder: Optional[str] = None, + default_value: Optional[str] = None) -> None: + """ + This method is called when the step needs to ask the user to perform some action or provide some value. + """ + pass + class WebSocketRunnerHooks(): def connecting(self, url: str): diff --git a/src/python_testing/TC_RVCOPSTATE_2_4.py b/src/python_testing/TC_RVCOPSTATE_2_4.py index 7352fbd089f172..9c07fd15de042f 100644 --- a/src/python_testing/TC_RVCOPSTATE_2_4.py +++ b/src/python_testing/TC_RVCOPSTATE_2_4.py @@ -119,47 +119,51 @@ async def test_TC_RVCOPSTATE_2_4(self): self.write_to_app_pipe('{"Name": "Reset"}') if self.check_pics("RVCOPSTATE.S.M.ST_ERROR"): - self.print_step(2, "Manually put the device in the ERROR operational state") + step_name = "Manually put the device in the ERROR operational state" + self.print_step(2, step_name) if self.is_ci: self.write_to_app_pipe('{"Name": "ErrorEvent", "Error": "UnableToStartOrResume"}') else: - input("Press Enter when done.\n") + self.wait_for_user_input(step_name) await self.read_operational_state_with_check(3, op_states.kError) await self.send_go_home_cmd_with_check(4, op_errors.kCommandInvalidInState) if self.check_pics("RVCOPSTATE.S.M.ST_CHARGING"): - self.print_step(5, "Manually put the device in the CHARGING operational state") + step_name = "Manually put the device in the CHARGING operational state" + self.print_step(5, step_name) if self.is_ci: self.write_to_app_pipe('{"Name": "Reset"}') self.write_to_app_pipe('{"Name": "Docked"}') self.write_to_app_pipe('{"Name": "Charging"}') else: - input("Press Enter when done.\n") + self.wait_for_user_input(step_name) await self.read_operational_state_with_check(6, rvc_op_states.kCharging) await self.send_go_home_cmd_with_check(7, op_errors.kCommandInvalidInState) if self.check_pics("RVCOPSTATE.S.M.ST_DOCKED"): - self.print_step(8, "Manually put the device in the DOCKED operational state") + step_name = "Manually put the device in the DOCKED operational state" + self.print_step(8, step_name) if self.is_ci: self.write_to_app_pipe('{"Name": "Charged"}') else: - input("Press Enter when done.\n") + self.wait_for_user_input(step_name) await self.read_operational_state_with_check(9, rvc_op_states.kDocked) await self.send_go_home_cmd_with_check(10, op_errors.kCommandInvalidInState) if self.check_pics("PICS_M_ST_SEEKING_CHARGER"): - self.print_step(8, "Manually put the device in the SEEKING CHARGER operational state") + step_name = "Manually put the device in the SEEKING CHARGER operational state" + self.print_step(8, step_name) if self.is_ci: await self.send_run_change_to_mode_cmd(rvc_app_run_mode_cleaning) await self.send_run_change_to_mode_cmd(rvc_app_run_mode_idle) else: - input("Press Enter when done.\n") + self.wait_for_user_input(step_name) await self.read_operational_state_with_check(9, rvc_op_states.kSeekingCharger) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 7efe5ab948966b..aee09dd1b93f09 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -346,6 +346,12 @@ def step_unknown(self): """ pass + def show_prompt(self, + msg: str, + placeholder: Optional[str] = None, + default_value: Optional[str] = None) -> None: + pass + @dataclass class MatterTestConfig: @@ -1091,6 +1097,28 @@ def get_setup_payload_info(self) -> SetupPayloadInfo: return info + def wait_for_user_input(self, + prompt_msg: str, + input_msg: str = "Press Enter when done.\n", + prompt_msg_placeholder: str = "Submit anything to continue", + default_value: str = "y") -> str: + """Ask for user input and wait for it. + + Args: + prompt_msg (str): Message for TH UI prompt. Indicates what is expected from the user. + input_msg (str, optional): Prompt for input function, used when running tests manually. Defaults to "Press Enter when done.\n". + prompt_msg_placeholder (str, optional): TH UI prompt input placeholder. Defaults to "Submit anything to continue". + default_value (str, optional): TH UI prompt default value. Defaults to "y". + + Returns: + str: User input + """ + if self.runner_hook: + self.runner_hook.show_prompt(msg=prompt_msg, + placeholder=prompt_msg_placeholder, + default_value=default_value) + return input(input_msg) + def generate_mobly_test_config(matter_test_config: MatterTestConfig): test_run_config = TestRunConfig() From d89901913ff288ae314377c2cbf24026d44d434d Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Mon, 26 Feb 2024 16:19:42 -0500 Subject: [PATCH 29/52] Remove `prune_outputs` support from code generation build scripts (#32322) * Remove output prunning - files are now separated enough for this to not be needed * Restyle * Remove more references of prune outputs --- build/chip/chip_codegen.gni | 55 ++---------------------------- scripts/tools/zap/prune_outputs.py | 42 ----------------------- src/app/chip_data_model.gni | 12 ------- 3 files changed, 3 insertions(+), 106 deletions(-) delete mode 100644 scripts/tools/zap/prune_outputs.py diff --git a/build/chip/chip_codegen.gni b/build/chip/chip_codegen.gni index 02acd08150aae8..08d9f17da6c1be 100644 --- a/build/chip/chip_codegen.gni +++ b/build/chip/chip_codegen.gni @@ -152,7 +152,7 @@ template("_chip_build_time_zapgen") { _output_subdir = "zap-generated" } - pw_python_action("${_name}_zap_pregen") { + pw_python_action("${_name}_zap") { script = "${chip_root}/scripts/tools/zap/generate.py" # TODO: this seems to touch internals. Is this ok? speeds up builds! @@ -165,7 +165,7 @@ template("_chip_build_time_zapgen") { "--templates", _template_path, "--output-dir", - rebase_path(target_gen_dir) + "/zap_pregen/" + _output_subdir, + rebase_path(target_gen_dir) + "/zapgen/" + _output_subdir, # TODO: lock file support should be removed as this serializes zap # (slower), however this is currently done because on Darwin zap startup @@ -188,54 +188,10 @@ template("_chip_build_time_zapgen") { sources = [ _idl_file ] - outputs = [] - foreach(name, invoker.outputs) { - outputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - - forward_variables_from(invoker, [ "prune_outputs" ]) - if (defined(prune_outputs)) { - foreach(name, prune_outputs) { - outputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - } - } - - # This action ensures that any "extra" files generated by zap codegen - # are actually deleted. - # - # This is to avoid double-codegen of configurations like endpoint config - # or access credentials being generated for both "controller client" and - # application-specific - pw_python_action("${_name}_files") { - # TODO: this seems to touch internals. Is this ok? speeds up builds! - _pw_internal_run_in_venv = false - - script = "${chip_root}/scripts/tools/zap/prune_outputs.py" - - _keep_file = rebase_path("${target_gen_dir}/${_name}.keep.outputs") - write_file(_keep_file, invoker.outputs, "list lines") - - args = [ - "--keep", - _keep_file, - "--input-dir", - rebase_path("${target_gen_dir}/zap_pregen/"), - "--output-dir", - rebase_path("${target_gen_dir}/zapgen/"), - ] - - inputs = [] - foreach(name, invoker.outputs) { - inputs += [ "${target_gen_dir}/zap_pregen/${name}" ] - } - outputs = [] foreach(name, invoker.outputs) { outputs += [ "${target_gen_dir}/zapgen/${name}" ] } - - deps = [ ":${_name}_zap_pregen" ] } source_set(_name) { @@ -255,10 +211,7 @@ template("_chip_build_time_zapgen") { if (!defined(public_deps)) { public_deps = [] } - public_deps += [ - ":${_name}_files", - ":${_name}_zap_pregen", - ] + public_deps += [ ":${_name}_zap" ] } } @@ -420,7 +373,6 @@ template("chip_zapgen") { "input", "outputs", "public_configs", - "prune_outputs", ]) } } else { @@ -482,7 +434,6 @@ template("chip_zapgen") { [ "deps", "public_configs", - "prune_outputs", ]) if (!defined(public_configs)) { public_configs = [] diff --git a/scripts/tools/zap/prune_outputs.py b/scripts/tools/zap/prune_outputs.py deleted file mode 100644 index 09f92a9378ab33..00000000000000 --- a/scripts/tools/zap/prune_outputs.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2022 Project CHIP Authors -# -# 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 shutil - - -def main(): - parser = argparse.ArgumentParser( - description='Delete files based on an input file listing files to be removed') - - parser.add_argument('--keep', required=True, help="File containing names to keep (copy over)") - parser.add_argument('--output-dir', required=True, help="Output directory to copy files into") - parser.add_argument('--input-dir', required=True, help="Input directory to get the files from") - - args = parser.parse_args() - - with open(args.keep, "rt") as f: - for source in f.readlines(): - source = source.strip() - if not source: - continue - target = os.path.join(args.output_dir, source) - os.makedirs(os.path.dirname(target), exist_ok=True) - shutil.copyfile(os.path.join(args.input_dir, source), target) - - -if __name__ == '__main__': - main() diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 42bc2f9b74ad64..a065d833b43846 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -56,20 +56,8 @@ template("chip_data_model") { "zap-generated/endpoint_config.h", ] - # NOTE: these are ALSO auto-generated but handled below: - # "zap-generated/IMClusterCommandHandler.cpp" - # -> contains one large DispatchSingleClusterCommand and DispatchServerCommand - - if (chip_code_pre_generated_directory == "") { - prune_outputs = [] - } - if (!chip_build_controller_dynamic_server) { outputs += [ "zap-generated/IMClusterCommandHandler.cpp" ] - } else { - if (defined(prune_outputs)) { - prune_outputs += [ "zap-generated/IMClusterCommandHandler.cpp" ] - } } if (!defined(deps)) { From d12311a496f5dc59399792fb1638b215722f9248 Mon Sep 17 00:00:00 2001 From: yunhanw-google Date: Mon, 26 Feb 2024 16:22:38 -0800 Subject: [PATCH 30/52] call OnActiveModeNotification out from onCheckInComplete (#32159) --- examples/chip-tool/commands/common/CHIPCommand.cpp | 6 ++++-- src/app/icd/client/CheckInHandler.cpp | 9 +++++++-- src/app/icd/client/CheckInHandler.h | 6 ++++-- src/app/icd/client/DefaultCheckInDelegate.cpp | 11 ++++------- src/app/icd/client/DefaultCheckInDelegate.h | 7 +++++-- src/app/icd/client/RefreshKeySender.cpp | 13 +++++++++---- src/app/icd/client/RefreshKeySender.h | 7 ++++--- 7 files changed, 37 insertions(+), 22 deletions(-) diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 78abff12ada208..125ab46433e0ef 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -149,9 +149,11 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); - ReturnLogErrorOnFailure(sCheckInDelegate.Init(&sICDClientStorage)); + auto engine = chip::app::InteractionModelEngine::GetInstance(); + VerifyOrReturnError(engine != nullptr, CHIP_ERROR_INCORRECT_STATE); + ReturnLogErrorOnFailure(sCheckInDelegate.Init(&sICDClientStorage, engine)); ReturnLogErrorOnFailure(sCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(), - &sICDClientStorage, &sCheckInDelegate)); + &sICDClientStorage, &sCheckInDelegate, engine)); CommissionerIdentity nullIdentity{ kIdentityNull, chip::kUndefinedNodeId }; ReturnLogErrorOnFailure(InitializeCommissioner(nullIdentity, kIdentityNullFabricId)); diff --git a/src/app/icd/client/CheckInHandler.cpp b/src/app/icd/client/CheckInHandler.cpp index 165301c45480fd..f6ef50a0019d66 100644 --- a/src/app/icd/client/CheckInHandler.cpp +++ b/src/app/icd/client/CheckInHandler.cpp @@ -22,6 +22,8 @@ * */ +#include +#include #include #include #include @@ -44,7 +46,7 @@ inline constexpr uint32_t kKeyRefreshLimit = (1U << 31); CheckInHandler::CheckInHandler() {} CHIP_ERROR CheckInHandler::Init(Messaging::ExchangeManager * exchangeManager, ICDClientStorage * clientStorage, - CheckInDelegate * delegate) + CheckInDelegate * delegate, InteractionModelEngine * engine) { VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(clientStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -54,7 +56,7 @@ CHIP_ERROR CheckInHandler::Init(Messaging::ExchangeManager * exchangeManager, IC mpExchangeManager = exchangeManager; mpICDClientStorage = clientStorage; mpCheckInDelegate = delegate; - + mpImEngine = engine; return mpExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::ICD_CheckIn, this); } @@ -127,6 +129,9 @@ CHIP_ERROR CheckInHandler::OnMessageReceived(Messaging::ExchangeContext * ec, co else { mpCheckInDelegate->OnCheckInComplete(clientInfo); +#if CHIP_CONFIG_ENABLE_READ_CLIENT + mpImEngine->OnActiveModeNotification(clientInfo.peer_node); +#endif // CHIP_CONFIG_ENABLE_READ_CLIENT } return CHIP_NO_ERROR; diff --git a/src/app/icd/client/CheckInHandler.h b/src/app/icd/client/CheckInHandler.h index 22170a1b332694..b2489aaac34812 100644 --- a/src/app/icd/client/CheckInHandler.h +++ b/src/app/icd/client/CheckInHandler.h @@ -36,12 +36,13 @@ namespace chip { namespace app { - +class InteractionModelEngine; class CheckInHandler : public Messaging::ExchangeDelegate, public Messaging::UnsolicitedMessageHandler { public: - CHIP_ERROR Init(Messaging::ExchangeManager * exchangeManager, ICDClientStorage * clientStorage, CheckInDelegate * delegate); + CHIP_ERROR Init(Messaging::ExchangeManager * exchangeManager, ICDClientStorage * clientStorage, CheckInDelegate * delegate, + InteractionModelEngine * engine); void Shutdown(); CheckInHandler(); @@ -87,6 +88,7 @@ class CheckInHandler : public Messaging::ExchangeDelegate, public Messaging::Uns Messaging::ExchangeManager * mpExchangeManager = nullptr; CheckInDelegate * mpCheckInDelegate = nullptr; ICDClientStorage * mpICDClientStorage = nullptr; + InteractionModelEngine * mpImEngine = nullptr; }; } // namespace app diff --git a/src/app/icd/client/DefaultCheckInDelegate.cpp b/src/app/icd/client/DefaultCheckInDelegate.cpp index 8df98006f6e9e0..e967138310c0f4 100644 --- a/src/app/icd/client/DefaultCheckInDelegate.cpp +++ b/src/app/icd/client/DefaultCheckInDelegate.cpp @@ -15,7 +15,6 @@ * limitations under the License. */ -#include #include #include #include @@ -25,11 +24,12 @@ namespace chip { namespace app { -CHIP_ERROR DefaultCheckInDelegate::Init(ICDClientStorage * storage) +CHIP_ERROR DefaultCheckInDelegate::Init(ICDClientStorage * storage, InteractionModelEngine * engine) { VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mpStorage == nullptr, CHIP_ERROR_INCORRECT_STATE); - mpStorage = storage; + mpStorage = storage; + mpImEngine = engine; return CHIP_NO_ERROR; } @@ -38,9 +38,6 @@ void DefaultCheckInDelegate::OnCheckInComplete(const ICDClientInfo & clientInfo) ChipLogProgress( ICD, "Check In Message processing complete: start_counter=%" PRIu32 " offset=%" PRIu32 " nodeid=" ChipLogFormatScopedNodeId, clientInfo.start_icd_counter, clientInfo.offset, ChipLogValueScopedNodeId(clientInfo.peer_node)); -#if CHIP_CONFIG_ENABLE_READ_CLIENT - InteractionModelEngine::GetInstance()->OnActiveModeNotification(clientInfo.peer_node); -#endif } RefreshKeySender * DefaultCheckInDelegate::OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage) @@ -55,7 +52,7 @@ RefreshKeySender * DefaultCheckInDelegate::OnKeyRefreshNeeded(ICDClientInfo & cl return nullptr; } - auto refreshKeySender = Platform::New(this, clientInfo, clientStorage, newKey); + auto refreshKeySender = Platform::New(this, clientInfo, clientStorage, mpImEngine, newKey); if (refreshKeySender == nullptr) { return nullptr; diff --git a/src/app/icd/client/DefaultCheckInDelegate.h b/src/app/icd/client/DefaultCheckInDelegate.h index e7b856677734bd..5465994ef1bda0 100644 --- a/src/app/icd/client/DefaultCheckInDelegate.h +++ b/src/app/icd/client/DefaultCheckInDelegate.h @@ -26,18 +26,21 @@ namespace app { using namespace std; +class InteractionModelEngine; + /// Callbacks for check in protocol class DefaultCheckInDelegate : public CheckInDelegate { public: virtual ~DefaultCheckInDelegate() {} - CHIP_ERROR Init(ICDClientStorage * storage); + CHIP_ERROR Init(ICDClientStorage * storage, InteractionModelEngine * engine); void OnCheckInComplete(const ICDClientInfo & clientInfo) override; RefreshKeySender * OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage) override; void OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error) override; private: - ICDClientStorage * mpStorage = nullptr; + ICDClientStorage * mpStorage = nullptr; + InteractionModelEngine * mpImEngine = nullptr; }; } // namespace app diff --git a/src/app/icd/client/RefreshKeySender.cpp b/src/app/icd/client/RefreshKeySender.cpp index 45cbbd9c987a68..f0fbdbae7721d8 100644 --- a/src/app/icd/client/RefreshKeySender.cpp +++ b/src/app/icd/client/RefreshKeySender.cpp @@ -19,6 +19,7 @@ #include "CheckInDelegate.h" #include "controller/InvokeInteraction.h" #include +#include #include #include #include @@ -28,10 +29,11 @@ namespace chip { namespace app { RefreshKeySender::RefreshKeySender(CheckInDelegate * checkInDelegate, const ICDClientInfo & icdClientInfo, - ICDClientStorage * icdClientStorage, const RefreshKeyBuffer & refreshKeyBuffer) : - mICDClientInfo(icdClientInfo), - mpICDClientStorage(icdClientStorage), mpCheckInDelegate(checkInDelegate), mOnConnectedCallback(HandleDeviceConnected, this), - mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) + ICDClientStorage * icdClientStorage, InteractionModelEngine * engine, + const RefreshKeyBuffer & refreshKeyBuffer) : + mpCheckInDelegate(checkInDelegate), + mICDClientInfo(icdClientInfo), mpICDClientStorage(icdClientStorage), mpImEngine(engine), + mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) { mNewKey = refreshKeyBuffer; @@ -64,6 +66,9 @@ CHIP_ERROR RefreshKeySender::RegisterClientWithNewKey(Messaging::ExchangeManager } mpCheckInDelegate->OnCheckInComplete(mICDClientInfo); +#if CHIP_CONFIG_ENABLE_READ_CLIENT + mpImEngine->OnActiveModeNotification(mICDClientInfo.peer_node); +#endif // CHIP_CONFIG_ENABLE_READ_CLIENT mpCheckInDelegate->OnKeyRefreshDone(this, CHIP_NO_ERROR); }; diff --git a/src/app/icd/client/RefreshKeySender.h b/src/app/icd/client/RefreshKeySender.h index a0cf822fa446f9..a92345fbe8ab83 100644 --- a/src/app/icd/client/RefreshKeySender.h +++ b/src/app/icd/client/RefreshKeySender.h @@ -34,7 +34,7 @@ namespace chip { namespace app { class CheckInDelegate; - +class InteractionModelEngine; /** * @brief RefreshKeySender contains all the data and methods needed for key refresh and re-registration of an ICD client. */ @@ -44,7 +44,7 @@ class RefreshKeySender typedef Crypto::SensitiveDataBuffer RefreshKeyBuffer; RefreshKeySender(CheckInDelegate * checkInDelegate, const ICDClientInfo & icdClientInfo, ICDClientStorage * icdClientStorage, - const RefreshKeyBuffer & refreshKeyBuffer); + InteractionModelEngine * engine, const RefreshKeyBuffer & refreshKeyBuffer); /** * @brief Sets up a CASE session to the peer for re-registering a client with the peer when a key refresh is required to avoid @@ -82,9 +82,10 @@ class RefreshKeySender */ CHIP_ERROR RegisterClientWithNewKey(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle); + CheckInDelegate * mpCheckInDelegate = nullptr; ICDClientInfo mICDClientInfo; ICDClientStorage * mpICDClientStorage = nullptr; - CheckInDelegate * mpCheckInDelegate = nullptr; + InteractionModelEngine * mpImEngine = nullptr; RefreshKeyBuffer mNewKey; Callback::Callback mOnConnectedCallback; Callback::Callback mOnConnectionFailureCallback; From 28c78af5643dbca32ee99a7f464e489db26430a3 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Mon, 26 Feb 2024 20:01:14 -0500 Subject: [PATCH 31/52] Define dependencies for some files in zzz_generated and compatenums.h (#32327) * Add dependency on CompatEnumNames as this is a requirement and direct include of cluster-enums.h * define ids as a source set for zzz generated * Fix dependencies * CompatEnumNames is NOT in a subdirectory of app, which is wrong however for now roll with it * Move CompatEnumNames to common so linter catches it * Restyle --- .github/workflows/lint.yml | 1 - src/app/common/BUILD.gn | 15 +++++++++++++++ src/app/{ => common}/CompatEnumNames.h | 0 .../templates/app/cluster-enums.zapt | 2 +- .../app-common/zap-generated/cluster-enums.h | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) rename src/app/{ => common}/CompatEnumNames.h (100%) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e0255646dbc487..e5afb69fabd2b1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -96,7 +96,6 @@ jobs: --known-failure app/CommandHandler.h \ --known-failure app/CommandHandlerInterface.h \ --known-failure app/CommandSenderLegacyCallback.h \ - --known-failure app/CompatEnumNames.h \ --known-failure app/data-model/ListLargeSystemExtensions.h \ --known-failure app/EventHeader.h \ --known-failure app/EventLoggingTypes.h \ diff --git a/src/app/common/BUILD.gn b/src/app/common/BUILD.gn index 1af268a477efaf..de7eef3008588d 100644 --- a/src/app/common/BUILD.gn +++ b/src/app/common/BUILD.gn @@ -18,6 +18,19 @@ config("includes") { include_dirs = [ "${chip_root}/zzz_generated/app-common" ] } +source_set("ids") { + sources = [ + "${chip_root}/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h", + "${chip_root}/zzz_generated/app-common/app-common/zap-generated/ids/Clusters.h", + "${chip_root}/zzz_generated/app-common/app-common/zap-generated/ids/Commands.h", + "${chip_root}/zzz_generated/app-common/app-common/zap-generated/ids/Events.h", + ] + + public_deps = [ "${chip_root}/src/app/util:types" ] + + public_configs = [ ":includes" ] +} + static_library("cluster-objects") { output_name = "libClusterObjects" @@ -27,6 +40,7 @@ static_library("cluster-objects") { ] public_deps = [ + ":ids", "${chip_root}/src/app:paths", "${chip_root}/src/app/data-model", "${chip_root}/src/app/util:types", @@ -42,6 +56,7 @@ source_set("enums") { sources = [ "${chip_root}/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h", "${chip_root}/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h", + "CompatEnumNames.h", ] public_configs = [ ":includes" ] diff --git a/src/app/CompatEnumNames.h b/src/app/common/CompatEnumNames.h similarity index 100% rename from src/app/CompatEnumNames.h rename to src/app/common/CompatEnumNames.h diff --git a/src/app/zap-templates/templates/app/cluster-enums.zapt b/src/app/zap-templates/templates/app/cluster-enums.zapt index 612b3dd6a29d4b..17ce795200795f 100644 --- a/src/app/zap-templates/templates/app/cluster-enums.zapt +++ b/src/app/zap-templates/templates/app/cluster-enums.zapt @@ -70,4 +70,4 @@ k{{asUpperCamelCase label}} = {{asHex mask}}, } // namespace chip // Included at the end, so all our definitions above are available. -#include +#include diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 1a1afc11a522d1..42a54b95ee944f 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -4893,4 +4893,4 @@ namespace SampleMei {} // namespace SampleMei } // namespace chip // Included at the end, so all our definitions above are available. -#include +#include From a2d98f869562ae1efc9a6c1c40f5049a12fa71e0 Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Tue, 27 Feb 2024 11:28:00 +0530 Subject: [PATCH 32/52] Fix MTRMetricsCollector.mm build failure using clang (#32330) - Missing static_cast to int for enum class in a format string --- src/darwin/Framework/CHIP/MTRMetricsCollector.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRMetricsCollector.mm b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm index 6387738a7c13c8..e829c57f486729 100644 --- a/src/darwin/Framework/CHIP/MTRMetricsCollector.mm +++ b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm @@ -193,19 +193,19 @@ - (void)handleMetricEvent:(MetricEvent)event using ValueType = MetricEvent::Value::Type; switch (event.ValueType()) { case ValueType::kInt32: - MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %d", event.key(), event.type(), event.ValueInt32()); + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %d", event.key(), static_cast(event.type()), event.ValueInt32()); break; case ValueType::kUInt32: - MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %u", event.key(), event.type(), event.ValueUInt32()); + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: %u", event.key(), static_cast(event.type()), event.ValueUInt32()); break; case ValueType::kChipErrorCode: - MTR_LOG_INFO("Received metric event, key: %s, type: %d, error value: %u", event.key(), event.type(), event.ValueErrorCode()); + MTR_LOG_INFO("Received metric event, key: %s, type: %d, error value: %u", event.key(), static_cast(event.type()), event.ValueErrorCode()); break; case ValueType::kUndefined: - MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: nil", event.key(), event.type()); + MTR_LOG_INFO("Received metric event, key: %s, type: %d, value: nil", event.key(), static_cast(event.type())); break; default: - MTR_LOG_INFO("Received metric event, key: %s, type: %d, unknown value", event.key(), event.type()); + MTR_LOG_INFO("Received metric event, key: %s, type: %d, unknown value", event.key(), static_cast(event.type())); return; } From 8428564bed7736f8572e4df3d592bba8f1687e9a Mon Sep 17 00:00:00 2001 From: weicheng Date: Tue, 27 Feb 2024 22:12:36 +0800 Subject: [PATCH 33/52] [ASR] fix double init in BLEManager and OTA (#31579) * fix BLE pairing issue because the initialization time of ble is earlier the callback registration time * fix ota fail issue * Restyled by clang-format * Update ASR582X SDK to v1.9.1 --- src/platform/ASR/ASROTAImageProcessor.cpp | 6 ++---- src/platform/ASR/BLEManagerImpl.cpp | 3 +++ third_party/asr/asr582x/asr_sdk | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/platform/ASR/ASROTAImageProcessor.cpp b/src/platform/ASR/ASROTAImageProcessor.cpp index 92a3ab08a4da7b..d83ba75362cb69 100644 --- a/src/platform/ASR/ASROTAImageProcessor.cpp +++ b/src/platform/ASR/ASROTAImageProcessor.cpp @@ -20,9 +20,6 @@ #include #include -/// No error, operation OK -#define LEGA_OTA_OK 0L - namespace chip { CHIP_ERROR ASROTAImageProcessor::PrepareDownload() @@ -121,7 +118,8 @@ void ASROTAImageProcessor::HandlePrepareDownload(intptr_t context) imageProcessor->mHeaderParser.Init(); - imageProcessor->mDownloader->OnPreparedForDownload(err == LEGA_OTA_OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL); + imageProcessor->mDownloader->OnPreparedForDownload( + ((err == LEGA_OTA_OK) || (err == LEGA_OTA_INIT_ALREADY)) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL); } void ASROTAImageProcessor::HandleFinalize(intptr_t context) diff --git a/src/platform/ASR/BLEManagerImpl.cpp b/src/platform/ASR/BLEManagerImpl.cpp index 51bd920b832dc7..e960a617b4e33b 100644 --- a/src/platform/ASR/BLEManagerImpl.cpp +++ b/src/platform/ASR/BLEManagerImpl.cpp @@ -110,6 +110,9 @@ CHIP_ERROR BLEManagerImpl::_Init() } else { + matter_ble_stack_open(); + matter_ble_add_service(); + BLEMgrImpl().SetStackInit(); mFlags.Set(Flags::kFlag_StackInitialized, true); log_i("ble is alread open!\n"); } diff --git a/third_party/asr/asr582x/asr_sdk b/third_party/asr/asr582x/asr_sdk index 54644715210812..8d6fd5e9f7f04e 160000 --- a/third_party/asr/asr582x/asr_sdk +++ b/third_party/asr/asr582x/asr_sdk @@ -1 +1 @@ -Subproject commit 54644715210812fa6c1e6e9433b5b824ba735c67 +Subproject commit 8d6fd5e9f7f04efd06d12a923a94f288e9d8c24b From 6d8613dfb673d3419d9ed0802db5e7c36069b935 Mon Sep 17 00:00:00 2001 From: fesseha-eve <88329315+fessehaeve@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:43:59 +0100 Subject: [PATCH 34/52] fix crash caused by lacking null pointer, which is caused by using wrong cluster id (#32339) --- src/app/clusters/on-off-server/on-off-server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp index ac1c4a0c3bf3c6..e4ba82bb8bdc2f 100644 --- a/src/app/clusters/on-off-server/on-off-server.cpp +++ b/src/app/clusters/on-off-server/on-off-server.cpp @@ -214,6 +214,7 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl ScenesManagement::ScenesServer::Instance().IsHandlerRegistered(endpoint, LevelControlServer::GetSceneHandler()))) #endif { + VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT); OnOffServer::Instance().scheduleTimerCallbackMs(mTransitionTimeInterface.sceneEventControl(endpoint), timeMs); } @@ -221,7 +222,7 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl } private: - OnOffTransitionTimeInterface mTransitionTimeInterface = OnOffTransitionTimeInterface(Attributes::OnOff::Id, sceneOnOffCallback); + OnOffTransitionTimeInterface mTransitionTimeInterface = OnOffTransitionTimeInterface(OnOff::Id, sceneOnOffCallback); }; static DefaultOnOffSceneHandler sOnOffSceneHandler; From db76ad7420a19e10a6b2f9c5e7187683f2a91b02 Mon Sep 17 00:00:00 2001 From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:15:45 +1300 Subject: [PATCH 35/52] Improve DeviceCommissioner::StopPairing behavior (#32233) * Prefix DeviceCommissioner members with 'm' * Move OnBasic* into DeviceCommissioner as private helpers * Consistently check for presence of mDefaultCommissioner first * Darwin: Reduce boilerplate in MTRError and use switch * Darwin: Add MTRErrorCodeCancelled * Darwin: Extend MTRPairingTests to cover cancellation * Improve DeviceCommissioner::StopPairing behavior Improve StopPairing behavior by cancelling the current read or invoke interaction to avoid stray callbacks messing up a future commissioning attempt. Rename SendCommand to SendCommissioningCommand. * Make gLogFilter atomic to avoid races / TSAN failures Note that most constrained platforms build with CHIP_LOG_FILTERING=0 so will not be incurring any extra runtime cost because of this change. * Exclude Darwin code from PRI*64 lint * Document InvokeCancelFn as internal-only * Address review comments - Log VerifyOrDie failures at Error level instead of Detail - Use reset() for clearing UniquePtr * Improve logging for commissioning - Log when commissioning is complete (SendCommissioningCompleteCallbacks) - Consolidate logging in AutoCommissioner::CommissioningStepFinished - Also rely on VerifyOrDie in Variant instead of bug log message * Another logging tweak --- .github/workflows/lint.yml | 2 +- src/controller/AutoCommissioner.cpp | 27 +-- src/controller/CHIPDeviceController.cpp | 170 ++++++++++------ src/controller/CHIPDeviceController.h | 44 ++-- src/controller/InvokeInteraction.h | 20 +- src/darwin/Framework/CHIP/MTRError.h | 9 + src/darwin/Framework/CHIP/MTRError.mm | 192 +++++++++--------- .../Framework/CHIPTests/MTRPairingTests.m | 86 +++++++- src/lib/support/CodeUtils.h | 4 +- src/lib/support/logging/TextOnlyLogging.cpp | 8 +- 10 files changed, 342 insertions(+), 220 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e5afb69fabd2b1..a5398ccb7567a2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -203,7 +203,7 @@ jobs: # TODO: TLVDebug should ideally not be excluded here. # TODO: protocol_decoder.cpp should ideally not be excluded here. # TODO: PersistentStorageMacros.h should ideally not be excluded here. - git grep -I -n "PRI.64" -- './*' ':(exclude).github/workflows/lint.yml' ':(exclude)examples/chip-tool' ':(exclude)examples/tv-casting-app' ':(exclude)src/app/MessageDef/MessageDefHelper.cpp' ':(exclude)src/app/tests/integration/chip_im_initiator.cpp' ':(exclude)src/lib/core/TLVDebug.cpp' ':(exclude)src/lib/dnssd/tests/TestTxtFields.cpp' ':(exclude)src/lib/format/protocol_decoder.cpp' ':(exclude)src/lib/support/PersistentStorageMacros.h' ':(exclude)src/messaging/tests/echo/echo_requester.cpp' ':(exclude)src/platform/Linux' ':(exclude)src/platform/Ameba' ':(exclude)src/platform/ESP32' ':(exclude)src/platform/webos' ':(exclude)zzz_generated/chip-tool' ':(exclude)src/tools/chip-cert/Cmd_PrintCert.cpp' && exit 1 || exit 0 + git grep -I -n "PRI.64" -- './*' ':(exclude).github/workflows/lint.yml' ':(exclude)examples/chip-tool' ':(exclude)examples/tv-casting-app' ':(exclude)src/app/MessageDef/MessageDefHelper.cpp' ':(exclude)src/app/tests/integration/chip_im_initiator.cpp' ':(exclude)src/lib/core/TLVDebug.cpp' ':(exclude)src/lib/dnssd/tests/TestTxtFields.cpp' ':(exclude)src/lib/format/protocol_decoder.cpp' ':(exclude)src/lib/support/PersistentStorageMacros.h' ':(exclude)src/messaging/tests/echo/echo_requester.cpp' ':(exclude)src/platform/Linux' ':(exclude)src/platform/Ameba' ':(exclude)src/platform/ESP32' ':(exclude)src/platform/Darwin' ':(exclude)src/darwin' ':(exclude)src/platform/webos' ':(exclude)zzz_generated/chip-tool' ':(exclude)src/tools/chip-cert/Cmd_PrintCert.cpp' && exit 1 || exit 0 # git grep exits with 0 if it finds a match, but we want # to fail (exit nonzero) on match. And we want to exclude this file, diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index 69a9c9b9c6bbf3..557cdf43a6ad9c 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -673,20 +673,10 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio { CompletionStatus completionStatus; completionStatus.err = err; - - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(Controller, "Successfully finished commissioning step '%s'", StageToString(report.stageCompleted)); - } - else - { - ChipLogProgress(Controller, "Error on commissioning step '%s': '%s'", StageToString(report.stageCompleted), err.AsString()); - } - if (err != CHIP_NO_ERROR) { + ChipLogError(Controller, "Error on commissioning step '%s': '%s'", StageToString(report.stageCompleted), err.AsString()); completionStatus.failedStage = MakeOptional(report.stageCompleted); - ChipLogError(Controller, "Failed to perform commissioning step %d", static_cast(report.stageCompleted)); if (report.Is()) { completionStatus.attestationResult = MakeOptional(report.Get().attestationResult); @@ -727,19 +717,14 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio } else { + ChipLogProgress(Controller, "Successfully finished commissioning step '%s'", StageToString(report.stageCompleted)); switch (report.stageCompleted) { case CommissioningStage::kReadCommissioningInfo: break; case CommissioningStage::kReadCommissioningInfo2: { - if (!report.Is()) - { - ChipLogError(Controller, - "[BUG] Should read commissioning info, but report is not ReadCommissioningInfo. THIS IS A BUG."); - } - ReadCommissioningInfo commissioningInfo = report.Get(); - mDeviceCommissioningInfo = report.Get(); + if (!mParams.GetFailsafeTimerSeconds().HasValue() && mDeviceCommissioningInfo.general.recommendedFailsafe > 0) { mParams.SetFailsafeTimerSeconds(mDeviceCommissioningInfo.general.recommendedFailsafe); @@ -751,11 +736,11 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio // Don't send DST unless the device says it needs it mNeedsDST = false; - mParams.SetSupportsConcurrentConnection(commissioningInfo.supportsConcurrentConnection); + mParams.SetSupportsConcurrentConnection(mDeviceCommissioningInfo.supportsConcurrentConnection); if (mParams.GetCheckForMatchingFabric()) { - chip::NodeId nodeId = commissioningInfo.remoteNodeId; + chip::NodeId nodeId = mDeviceCommissioningInfo.remoteNodeId; if (nodeId != kUndefinedNodeId) { mParams.SetRemoteNodeId(nodeId); @@ -764,7 +749,7 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore) { - if (commissioningInfo.icd.isLIT && commissioningInfo.icd.checkInProtocolSupport) + if (mDeviceCommissioningInfo.icd.isLIT && mDeviceCommissioningInfo.icd.checkInProtocolSupport) { mNeedIcdRegistration = true; ChipLogDetail(Controller, "AutoCommissioner: ICD supports the check-in protocol."); diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 69e95244dcce01..35c616ac0426db 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -38,11 +38,11 @@ #include #include -#include - #include #include +#include #include +#include #include #include #include @@ -400,11 +400,7 @@ DeviceCommissioner::DeviceCommissioner() : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES mDeviceAttestationInformationVerificationCallback(OnDeviceAttestationInformationVerification, this), mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this), mSetUpCodePairer(this) -{ - mPairingDelegate = nullptr; - mDeviceBeingCommissioned = nullptr; - mDeviceInPASEEstablishment = nullptr; -} +{} CHIP_ERROR DeviceCommissioner::Init(CommissionerInitParams params) { @@ -483,7 +479,8 @@ void DeviceCommissioner::Shutdown() ChipLogDetail(Controller, "Setup in progress, stopping setup before shutting down"); OnSessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED); } - // TODO: If we have a commissioning step in progress, is there a way to cancel that callback? + + CancelCommissioningInteractions(); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY // make this commissioner discoverable if (mUdcTransportMgr != nullptr) @@ -816,6 +813,13 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId, CommissioningPa CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId) { MATTER_TRACE_SCOPE("Commission", "DeviceCommissioner"); + + if (mDefaultCommissioner == nullptr) + { + ChipLogError(Controller, "No default commissioner is specified"); + return CHIP_ERROR_INCORRECT_STATE; + } + CommissioneeDeviceProxy * device = FindCommissioneeDevice(remoteDeviceId); if (device == nullptr || (!device->IsSecureConnected() && !device->IsSessionSetupInProgress())) { @@ -831,13 +835,8 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId) if (mCommissioningStage != CommissioningStage::kSecurePairing) { - ChipLogError(Controller, "Commissioning already in progress - not restarting"); - return CHIP_ERROR_INCORRECT_STATE; - } - - if (mDefaultCommissioner == nullptr) - { - ChipLogError(Controller, "No default commissioner is specified"); + ChipLogError(Controller, "Commissioning already in progress (stage '%s') - not restarting", + StageToString(mCommissioningStage)); return CHIP_ERROR_INCORRECT_STATE; } @@ -860,6 +859,13 @@ DeviceCommissioner::ContinueCommissioningAfterDeviceAttestation(DeviceProxy * de Credentials::AttestationVerificationResult attestationResult) { MATTER_TRACE_SCOPE("continueCommissioningDevice", "DeviceCommissioner"); + + if (mDefaultCommissioner == nullptr) + { + ChipLogError(Controller, "No default commissioner is specified"); + return CHIP_ERROR_INCORRECT_STATE; + } + if (device == nullptr || device != mDeviceBeingCommissioned) { ChipLogError(Controller, "Invalid device for commissioning %p", device); @@ -884,12 +890,6 @@ DeviceCommissioner::ContinueCommissioningAfterDeviceAttestation(DeviceProxy * de return CHIP_ERROR_INCORRECT_STATE; } - if (mDefaultCommissioner == nullptr) - { - ChipLogError(Controller, "No default commissioner is specified"); - return CHIP_ERROR_INCORRECT_STATE; - } - ChipLogProgress(Controller, "Continuing commissioning after attestation failure for device ID 0x" ChipLogFormatX64, ChipLogValueX64(commissioneeDevice->GetDeviceId())); @@ -920,6 +920,7 @@ CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId) // If we're still in the process of discovering the device, just stop the SetUpCodePairer if (mSetUpCodePairer.StopPairing(remoteDeviceId)) { + mRunCommissioningAfterConnection = false; return CHIP_NO_ERROR; } @@ -929,6 +930,7 @@ CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId) if (mDeviceBeingCommissioned == device) { + CancelCommissioningInteractions(); CommissioningStageComplete(CHIP_ERROR_CANCELLED); } else @@ -938,6 +940,21 @@ CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId) return CHIP_NO_ERROR; } +void DeviceCommissioner::CancelCommissioningInteractions() +{ + if (mReadClient) + { + ChipLogDetail(Controller, "Cancelling read request for step '%s'", StageToString(mCommissioningStage)); + mReadClient.reset(); // destructor cancels + } + if (mInvokeCancelFn) + { + ChipLogDetail(Controller, "Cancelling command invocation for step '%s'", StageToString(mCommissioningStage)); + mInvokeCancelFn(); + mInvokeCancelFn = nullptr; + } +} + CHIP_ERROR DeviceCommissioner::UnpairDevice(NodeId remoteDeviceId) { MATTER_TRACE_SCOPE("UnpairDevice", "DeviceCommissioner"); @@ -1014,7 +1031,8 @@ CHIP_ERROR DeviceCommissioner::SendCertificateChainRequestCommand(DeviceProxy * OperationalCredentials::Commands::CertificateChainRequest::Type request; request.certificateType = static_cast(certificateType); - return SendCommand(device, request, OnCertificateChainResponse, OnCertificateChainFailureResponse, timeout); + return SendCommissioningCommand(device, request, OnCertificateChainResponse, OnCertificateChainFailureResponse, kRootEndpointId, + timeout); } void DeviceCommissioner::OnCertificateChainFailureResponse(void * context, CHIP_ERROR error) @@ -1048,7 +1066,8 @@ CHIP_ERROR DeviceCommissioner::SendAttestationRequestCommand(DeviceProxy * devic OperationalCredentials::Commands::AttestationRequest::Type request; request.attestationNonce = attestationNonce; - ReturnErrorOnFailure(SendCommand(device, request, OnAttestationResponse, OnAttestationFailureResponse, timeout)); + ReturnErrorOnFailure( + SendCommissioningCommand(device, request, OnAttestationResponse, OnAttestationFailureResponse, kRootEndpointId, timeout)); ChipLogDetail(Controller, "Sent Attestation request, waiting for the Attestation Information"); return CHIP_NO_ERROR; } @@ -1218,7 +1237,7 @@ bool DeviceCommissioner::ExtendArmFailSafe(DeviceProxy * proxy, CommissioningSta request.expiryLengthSeconds = armFailSafeTimeout; request.breadcrumb = breadcrumb; ChipLogProgress(Controller, "Arming failsafe (%u seconds)", request.expiryLengthSeconds); - CHIP_ERROR err = SendCommand(proxy, request, onSuccess, onFailure, kRootEndpointId, commandTimeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, onSuccess, onFailure, kRootEndpointId, commandTimeout); if (err != CHIP_NO_ERROR) { onFailure(this, err); @@ -1309,7 +1328,8 @@ CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(D OperationalCredentials::Commands::CSRRequest::Type request; request.CSRNonce = csrNonce; - ReturnErrorOnFailure(SendCommand(device, request, OnOperationalCertificateSigningRequest, OnCSRFailureResponse, timeout)); + ReturnErrorOnFailure(SendCommissioningCommand(device, request, OnOperationalCertificateSigningRequest, OnCSRFailureResponse, + kRootEndpointId, timeout)); ChipLogDetail(Controller, "Sent CSR request, waiting for the CSR"); return CHIP_NO_ERROR; } @@ -1431,7 +1451,8 @@ CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(DeviceProxy * device, request.caseAdminSubject = adminSubject; request.adminVendorId = mVendorId; - ReturnErrorOnFailure(SendCommand(device, request, OnOperationalCertificateAddResponse, OnAddNOCFailureResponse, timeout)); + ReturnErrorOnFailure(SendCommissioningCommand(device, request, OnOperationalCertificateAddResponse, OnAddNOCFailureResponse, + kRootEndpointId, timeout)); ChipLogProgress(Controller, "Sent operational certificate to the device"); @@ -1515,7 +1536,8 @@ CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(DeviceProxy * device, OperationalCredentials::Commands::AddTrustedRootCertificate::Type request; request.rootCACertificate = rcac; - ReturnErrorOnFailure(SendCommand(device, request, OnRootCertSuccessResponse, OnRootCertFailureResponse, timeout)); + ReturnErrorOnFailure( + SendCommissioningCommand(device, request, OnRootCertSuccessResponse, OnRootCertFailureResponse, kRootEndpointId, timeout)); ChipLogProgress(Controller, "Sent root certificate to the device"); @@ -1623,13 +1645,13 @@ void DeviceCommissioner::OnNodeDiscovered(const chip::Dnssd::DiscoveredNodeData mSetUpCodePairer.NotifyCommissionableDeviceDiscovered(nodeData); } -void OnBasicSuccess(void * context, const chip::app::DataModel::NullObjectType &) +void DeviceCommissioner::OnBasicSuccess(void * context, const chip::app::DataModel::NullObjectType &) { DeviceCommissioner * commissioner = static_cast(context); commissioner->CommissioningStageComplete(CHIP_NO_ERROR); } -void OnBasicFailure(void * context, CHIP_ERROR error) +void DeviceCommissioner::OnBasicFailure(void * context, CHIP_ERROR error) { ChipLogProgress(Controller, "Received failure response %s\n", chip::ErrorStr(error)); DeviceCommissioner * commissioner = static_cast(context); @@ -1638,7 +1660,7 @@ void OnBasicFailure(void * context, CHIP_ERROR error) void DeviceCommissioner::CleanupCommissioning(DeviceProxy * proxy, NodeId nodeId, const CompletionStatus & completionStatus) { - commissioningCompletionStatus = completionStatus; + mCommissioningCompletionStatus = completionStatus; if (completionStatus.err == CHIP_NO_ERROR) { @@ -1650,15 +1672,17 @@ void DeviceCommissioner::CleanupCommissioning(DeviceProxy * proxy, NodeId nodeId } // Send the callbacks, we're done. CommissioningStageComplete(CHIP_NO_ERROR); - SendCommissioningCompleteCallbacks(nodeId, commissioningCompletionStatus); + SendCommissioningCompleteCallbacks(nodeId, mCommissioningCompletionStatus); } - else if (completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup) + else if (completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup && + completionStatus.err != CHIP_ERROR_CANCELLED) { // If we were already doing network setup, we need to retain the pase session and start again from network setup stage. // We do not need to reset the failsafe here because we want to keep everything on the device up to this point, so just - // send the completion callbacks. + // send the completion callbacks (see "Commissioning Flows Error Handling" in the spec). This does not apply if + // we're cleaning up because cancellation has been requested via StopPairing(). CommissioningStageComplete(CHIP_NO_ERROR); - SendCommissioningCompleteCallbacks(nodeId, commissioningCompletionStatus); + SendCommissioningCompleteCallbacks(nodeId, mCommissioningCompletionStatus); } else { @@ -1671,8 +1695,8 @@ void DeviceCommissioner::CleanupCommissioning(DeviceProxy * proxy, NodeId nodeId ChipLogProgress(Controller, "Expiring failsafe on proxy %p", proxy); mDeviceBeingCommissioned = proxy; // We actually want to do the same thing on success or failure because we're already in a failure state - CHIP_ERROR err = SendCommand(proxy, request, OnDisarmFailsafe, OnDisarmFailsafeFailure, - /* timeout = */ NullOptional); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnDisarmFailsafe, OnDisarmFailsafeFailure, kRootEndpointId, + /* timeout = */ NullOptional); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just pretend like the @@ -1694,7 +1718,7 @@ void DeviceCommissioner::OnDisarmFailsafe(void * context, void DeviceCommissioner::OnDisarmFailsafeFailure(void * context, CHIP_ERROR error) { - ChipLogProgress(Controller, "Received failure response when disarming failsafe%s\n", chip::ErrorStr(error)); + ChipLogProgress(Controller, "Ignoring failure to disarm failsafe: %" CHIP_ERROR_FORMAT, error.Format()); DeviceCommissioner * commissioner = static_cast(context); commissioner->DisarmDone(); } @@ -1711,7 +1735,7 @@ void DeviceCommissioner::DisarmDone() // Signal completion - this will reset mDeviceBeingCommissioned. CommissioningStageComplete(CHIP_NO_ERROR); - SendCommissioningCompleteCallbacks(nodeId, commissioningCompletionStatus); + SendCommissioningCompleteCallbacks(nodeId, mCommissioningCompletionStatus); // If we've disarmed the failsafe, it's because we're starting again, so kill the pase connection. if (commissionee != nullptr) @@ -1722,7 +1746,10 @@ void DeviceCommissioner::DisarmDone() void DeviceCommissioner::SendCommissioningCompleteCallbacks(NodeId nodeId, const CompletionStatus & completionStatus) { + ChipLogProgress(Controller, "Commissioning complete for node ID 0x" ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId), + (completionStatus.err == CHIP_NO_ERROR ? "success" : completionStatus.err.AsString())); mCommissioningStage = CommissioningStage::kSecurePairing; + if (mPairingDelegate == nullptr) { return; @@ -1746,17 +1773,12 @@ void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err, Commissionin { // Once this stage is complete, reset mDeviceBeingCommissioned - this will be reset when the delegate calls the next step. MATTER_TRACE_SCOPE("CommissioningStageComplete", "DeviceCommissioner"); - if (mDeviceBeingCommissioned == nullptr) - { - // We are getting a stray callback (e.g. due to un-cancellable - // operations) when we are not in fact commissioning anything. Just - // ignore it. - return; - } + VerifyOrDie(mDeviceBeingCommissioned); NodeId nodeId = mDeviceBeingCommissioned->GetDeviceId(); DeviceProxy * proxy = mDeviceBeingCommissioned; mDeviceBeingCommissioned = nullptr; + mInvokeCancelFn = nullptr; if (mPairingDelegate != nullptr) { @@ -1769,7 +1791,7 @@ void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err, Commissionin } report.stageCompleted = mCommissioningStage; CHIP_ERROR status = mCommissioningDelegate->CommissioningStepFinished(err, report); - if (status != CHIP_NO_ERROR) + if (status != CHIP_NO_ERROR && mCommissioningStage != CommissioningStage::kCleanup) { // Commissioning delegate will only return error if it failed to perform the appropriate commissioning step. // In this case, we should complete the commissioning for it. @@ -1898,9 +1920,10 @@ void DeviceCommissioner::OnDeviceConnectionRetryFn(void * context, const ScopedN #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES // ClusterStateCache::Callback impl -void DeviceCommissioner::OnDone(app::ReadClient *) +void DeviceCommissioner::OnDone(app::ReadClient * readClient) { - mReadClient = nullptr; + VerifyOrDie(readClient != nullptr && readClient == mReadClient.get()); + mReadClient.reset(); switch (mCommissioningStage) { case CommissioningStage::kReadCommissioningInfo: @@ -1913,6 +1936,7 @@ void DeviceCommissioner::OnDone(app::ReadClient *) ParseCommissioningInfo(); break; default: + VerifyOrDie(false); break; } } @@ -2484,9 +2508,31 @@ void DeviceCommissioner::OnCommissioningCompleteResponse( commissioner->CommissioningStageComplete(err, report); } +template +CHIP_ERROR +DeviceCommissioner::SendCommissioningCommand(DeviceProxy * device, const RequestObjectT & request, + CommandResponseSuccessCallback successCb, + CommandResponseFailureCallback failureCb, EndpointId endpoint, + Optional timeout) + +{ + VerifyOrDie(!mInvokeCancelFn); // we don't make parallel calls + + auto onSuccessCb = [context = this, successCb](const app::ConcreteCommandPath & aPath, const app::StatusIB & aStatus, + const typename RequestObjectT::ResponseType & responseData) { + successCb(context, responseData); + }; + auto onFailureCb = [context = this, failureCb](CHIP_ERROR aError) { failureCb(context, aError); }; + + return InvokeCommandRequest(device->GetExchangeManager(), device->GetSecureSession().Value(), endpoint, request, onSuccessCb, + onFailureCb, NullOptional, timeout, &mInvokeCancelFn); +} + void DeviceCommissioner::SendCommissioningReadRequest(DeviceProxy * proxy, Optional timeout, app::AttributePathParams * readPaths, size_t readPathsSize) { + VerifyOrDie(!mReadClient); // we don't perform parallel reads + app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); app::ReadPrepareParams readParams(proxy->GetSecureSession().Value()); readParams.mIsFabricFiltered = false; @@ -2635,7 +2681,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio request.UTCTime = utcTime.count() - kChipEpochUsSinceUnixEpoch; // For now, we assume a seconds granularity request.granularity = TimeSynchronization::GranularityEnum::kSecondsGranularity; - CHIP_ERROR err = SendCommand(proxy, request, OnBasicSuccess, OnSetUTCError, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnBasicSuccess, OnSetUTCError, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2654,7 +2700,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } TimeSynchronization::Commands::SetTimeZone::Type request; request.timeZone = params.GetTimeZone().Value(); - CHIP_ERROR err = SendCommand(proxy, request, OnSetTimeZoneResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnSetTimeZoneResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2673,7 +2719,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } TimeSynchronization::Commands::SetDSTOffset::Type request; request.DSTOffset = params.GetDSTOffsets().Value(); - CHIP_ERROR err = SendCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2692,7 +2738,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } TimeSynchronization::Commands::SetDefaultNTP::Type request; request.defaultNTP = params.GetDefaultNTP().Value(); - CHIP_ERROR err = SendCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2709,7 +2755,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio request.ssid.Emplace(params.GetWiFiCredentials().Value().ssid); } request.breadcrumb.Emplace(breadcrumb); - CHIP_ERROR err = SendCommand(proxy, request, OnScanNetworksResponse, OnScanNetworksFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnScanNetworksResponse, OnScanNetworksFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2776,7 +2822,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio request.newRegulatoryConfig = regulatoryConfig; request.countryCode = countryCode; request.breadcrumb = breadcrumb; - CHIP_ERROR err = SendCommand(proxy, request, OnSetRegulatoryConfigResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnSetRegulatoryConfigResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2968,7 +3014,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } TimeSynchronization::Commands::SetTrustedTimeSource::Type request; request.trustedTimeSource = params.GetTrustedTimeSource().Value(); - CHIP_ERROR err = SendCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnBasicSuccess, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -2990,7 +3036,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio request.ssid = params.GetWiFiCredentials().Value().ssid; request.credentials = params.GetWiFiCredentials().Value().credentials; request.breadcrumb.Emplace(breadcrumb); - CHIP_ERROR err = SendCommand(proxy, request, OnNetworkConfigResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnNetworkConfigResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -3010,7 +3056,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio NetworkCommissioning::Commands::AddOrUpdateThreadNetwork::Type request; request.operationalDataset = params.GetThreadOperationalDataset().Value(); request.breadcrumb.Emplace(breadcrumb); - CHIP_ERROR err = SendCommand(proxy, request, OnNetworkConfigResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnNetworkConfigResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -3044,7 +3090,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio params.GetSupportsConcurrentConnection().HasValue() ? (params.GetSupportsConcurrentConnection().Value() ? "true" : "false") : "missing"); - err = SendCommand(proxy, request, OnConnectNetworkResponse, OnBasicFailure, endpoint, timeout); + err = SendCommissioningCommand(proxy, request, OnConnectNetworkResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { @@ -3069,7 +3115,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio NetworkCommissioning::Commands::ConnectNetwork::Type request; request.networkID = extendedPanId; request.breadcrumb.Emplace(breadcrumb); - CHIP_ERROR err = SendCommand(proxy, request, OnConnectNetworkResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = SendCommissioningCommand(proxy, request, OnConnectNetworkResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -3099,7 +3145,8 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio request.monitoredSubject = params.GetICDMonitoredSubject().Value(); request.key = params.GetICDSymmetricKey().Value(); - CHIP_ERROR err = SendCommand(proxy, request, OnICDManagementRegisterClientResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = + SendCommissioningCommand(proxy, request, OnICDManagementRegisterClientResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. @@ -3137,7 +3184,8 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio break; case CommissioningStage::kSendComplete: { GeneralCommissioning::Commands::CommissioningComplete::Type request; - CHIP_ERROR err = SendCommand(proxy, request, OnCommissioningCompleteResponse, OnBasicFailure, endpoint, timeout); + CHIP_ERROR err = + SendCommissioningCommand(proxy, request, OnCommissioningCompleteResponse, OnBasicFailure, endpoint, timeout); if (err != CHIP_NO_ERROR) { // We won't get any async callbacks here, so just complete our stage. diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index a7e6f6dac4bcb9..024421dd8a1a6e 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -84,10 +84,6 @@ using namespace chip::Protocols::UserDirectedCommissioning; inline constexpr uint16_t kNumMaxActiveDevices = CHIP_CONFIG_CONTROLLER_MAX_ACTIVE_DEVICES; -// Raw functions for cluster callbacks -void OnBasicFailure(void * context, CHIP_ERROR err); -void OnBasicSuccess(void * context, const chip::app::DataModel::NullObjectType &); - struct ControllerInitParams { DeviceControllerSystemState * systemState = nullptr; @@ -569,8 +565,12 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, /** * @brief - * This function stops a pairing process that's in progress. It does not delete the pairing of a previously - * paired device. + * This function stops a pairing or commissioning process that is in progress. + * It does not delete the pairing of a previously paired device. + * + * Note that cancelling an ongoing commissioning process is an asynchronous operation. + * The pairing delegate (if any) will receive OnCommissioningComplete and OnCommissioningFailure + * failure callbacks with a status code of CHIP_ERROR_CANCELLED once cancellation is complete. * * @param[in] remoteDeviceId The remote device Id. * @@ -769,13 +769,14 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, OnExtendFailsafeFailure onFailure); private: - DevicePairingDelegate * mPairingDelegate; + DevicePairingDelegate * mPairingDelegate = nullptr; DeviceProxy * mDeviceBeingCommissioned = nullptr; CommissioneeDeviceProxy * mDeviceInPASEEstablishment = nullptr; CommissioningStage mCommissioningStage = CommissioningStage::kSecurePairing; bool mRunCommissioningAfterConnection = false; + Internal::InvokeCancelFn mInvokeCancelFn; ObjectPool mCommissioneeDevicePool; @@ -794,6 +795,9 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CHIP_ERROR LoadKeyId(PersistentStorageDelegate * delegate, uint16_t & out); + static void OnBasicFailure(void * context, CHIP_ERROR err); + static void OnBasicSuccess(void * context, const chip::app::DataModel::NullObjectType &); + /* This function sends a Device Attestation Certificate chain request to the device. The function does not hold a reference to the device object. */ @@ -955,26 +959,14 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, void ReleaseCommissioneeDevice(CommissioneeDeviceProxy * device); template - CHIP_ERROR SendCommand(DeviceProxy * device, const RequestObjectT & request, - CommandResponseSuccessCallback successCb, - CommandResponseFailureCallback failureCb, Optional timeout) - { - return SendCommand(device, request, successCb, failureCb, 0, timeout); - } - - template - CHIP_ERROR SendCommand(DeviceProxy * device, const RequestObjectT & request, - CommandResponseSuccessCallback successCb, - CommandResponseFailureCallback failureCb, EndpointId endpoint, Optional timeout) - { - ClusterBase cluster(*device->GetExchangeManager(), device->GetSecureSession().Value(), endpoint); - cluster.SetCommandTimeout(timeout); - - return cluster.InvokeCommand(request, this, successCb, failureCb); - } - + CHIP_ERROR SendCommissioningCommand(DeviceProxy * device, const RequestObjectT & request, + CommandResponseSuccessCallback successCb, + CommandResponseFailureCallback failureCb, EndpointId endpoint, + Optional timeout); void SendCommissioningReadRequest(DeviceProxy * proxy, Optional timeout, app::AttributePathParams * readPaths, size_t readPathsSize); + void CancelCommissioningInteractions(); + #if CHIP_CONFIG_ENABLE_READ_CLIENT void ParseCommissioningInfo(); // Parsing attributes read in kReadCommissioningInfo stage. @@ -1024,7 +1016,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, nullptr; // Commissioning delegate to call when PairDevice / Commission functions are used CommissioningDelegate * mCommissioningDelegate = nullptr; // Commissioning delegate that issued the PerformCommissioningStep command - CompletionStatus commissioningCompletionStatus; + CompletionStatus mCommissioningCompletionStatus; #if CHIP_CONFIG_ENABLE_READ_CLIENT Platform::UniquePtr mAttributeCache; diff --git a/src/controller/InvokeInteraction.h b/src/controller/InvokeInteraction.h index d167cde140db23..7576f8aac2e78b 100644 --- a/src/controller/InvokeInteraction.h +++ b/src/controller/InvokeInteraction.h @@ -18,13 +18,19 @@ #pragma once -#include #include #include +#include + namespace chip { namespace Controller { +namespace Internal { +// Cancellation functions on InvokeCommandRequest() are for internal use only. +typedef std::function InvokeCancelFn; +} // namespace Internal + /* * A typed command invocation function that takes as input a cluster-object representation of a command request and * callbacks for success and failure and either returns a decoded cluster-object representation of the response through @@ -49,7 +55,8 @@ InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHan typename TypedCommandCallback::OnSuccessCallbackType onSuccessCb, typename TypedCommandCallback::OnErrorCallbackType onErrorCb, const Optional & timedInvokeTimeoutMs, - const Optional & responseTimeout = NullOptional) + const Optional & responseTimeout = NullOptional, + Internal::InvokeCancelFn * outCancelFn = nullptr) { // InvokeCommandRequest expects responses, so cannot happen over a group session. VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT); @@ -81,6 +88,15 @@ InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHan ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs)); ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout)); + // If requested by the caller, provide a way to cancel the invoke interaction. + if (outCancelFn != nullptr) + { + *outCancelFn = [rawDecoderPtr = decoder.get(), rawCommandSender = commandSender.get()]() { + chip::Platform::Delete(rawCommandSender); + chip::Platform::Delete(rawDecoderPtr); + }; + } + // // We've effectively transferred ownership of the above allocated objects to CommandSender, and we need to wait for it to call // us back when processing is completed (through OnDone) to eventually free up resources. diff --git a/src/darwin/Framework/CHIP/MTRError.h b/src/darwin/Framework/CHIP/MTRError.h index 8eaefbd50df643..3068a1b2405779 100644 --- a/src/darwin/Framework/CHIP/MTRError.h +++ b/src/darwin/Framework/CHIP/MTRError.h @@ -56,21 +56,25 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ MTRErrorCodeIntegrityCheckFailed = 8, MTRErrorCodeTimeout = 9, MTRErrorCodeBufferTooSmall = 10, + /** * MTRErrorCodeFabricExists is returned when trying to commission a device * into a fabric when it's already part of that fabric. */ MTRErrorCodeFabricExists = 11, + /** * MTRErrorCodeUnknownSchema means the schema for the given cluster/attribute, * cluster/event, or cluster/command combination is not known. */ MTRErrorCodeUnknownSchema MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) = 12, + /** * MTRErrorCodeSchemaMismatch means that provided data did not match the * expected schema. */ MTRErrorCodeSchemaMismatch MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) = 13, + /** * MTRErrorCodeTLVDecodeFailed means that the TLV being decoded was malformed in * some way. This can include things like lengths running past the end of @@ -87,6 +91,11 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ * application's Info.plist. */ MTRErrorCodeDNSSDUnauthorized MTR_AVAILABLE(ios(17.2), macos(14.2), watchos(10.2), tvos(17.2)) = 15, + + /** + * The operation was cancelled. + */ + MTRErrorCodeCancelled MTR_NEWLY_AVAILABLE = 16, }; // clang-format on diff --git a/src/darwin/Framework/CHIP/MTRError.mm b/src/darwin/Framework/CHIP/MTRError.mm index 0cb40006ac3293..d607f00400d158 100644 --- a/src/darwin/Framework/CHIP/MTRError.mm +++ b/src/darwin/Framework/CHIP/MTRError.mm @@ -61,52 +61,72 @@ + (NSError *)errorForCHIPErrorCode:(CHIP_ERROR)errorCode logContext:(id)contextT return [MTRError errorForIMStatus:status]; } - NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init]; - MTRErrorCode code = MTRErrorCodeGeneralError; - - if (errorCode == CHIP_ERROR_INVALID_STRING_LENGTH) { + MTRErrorCode code; + NSString * description; + NSDictionary * additionalUserInfo; + switch (errorCode.AsInteger()) { + case CHIP_ERROR_INVALID_STRING_LENGTH.AsInteger(): code = MTRErrorCodeInvalidStringLength; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"A list length is invalid.", nil) }]; - } else if (errorCode == CHIP_ERROR_INVALID_INTEGER_VALUE) { + description = NSLocalizedString(@"A list length is invalid.", nil); + break; + case CHIP_ERROR_INVALID_INTEGER_VALUE.AsInteger(): code = MTRErrorCodeInvalidIntegerValue; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Unexpected integer value.", nil) }]; - } else if (errorCode == CHIP_ERROR_INVALID_ARGUMENT) { + description = NSLocalizedString(@"Unexpected integer value.", nil); + break; + case CHIP_ERROR_INVALID_ARGUMENT.AsInteger(): code = MTRErrorCodeInvalidArgument; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"An argument is invalid.", nil) }]; - } else if (errorCode == CHIP_ERROR_INVALID_MESSAGE_LENGTH) { + description = NSLocalizedString(@"An argument is invalid.", nil); + break; + case CHIP_ERROR_INVALID_MESSAGE_LENGTH.AsInteger(): code = MTRErrorCodeInvalidMessageLength; - [userInfo - addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"A message length is invalid.", nil) }]; - } else if (errorCode == CHIP_ERROR_INCORRECT_STATE) { + description = NSLocalizedString(@"A message length is invalid.", nil); + break; + case CHIP_ERROR_INCORRECT_STATE.AsInteger(): code = MTRErrorCodeInvalidState; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Invalid object state.", nil) }]; - } else if (errorCode == CHIP_ERROR_INTEGRITY_CHECK_FAILED) { + description = NSLocalizedString(@"Invalid object state.", nil); + break; + case CHIP_ERROR_INTEGRITY_CHECK_FAILED.AsInteger(): code = MTRErrorCodeIntegrityCheckFailed; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Integrity check failed.", nil) }]; - } else if (errorCode == CHIP_ERROR_TIMEOUT) { + description = NSLocalizedString(@"Integrity check failed.", nil); + break; + case CHIP_ERROR_TIMEOUT.AsInteger(): code = MTRErrorCodeTimeout; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Transaction timed out.", nil) }]; - } else if (errorCode == CHIP_ERROR_BUFFER_TOO_SMALL) { + description = NSLocalizedString(@"Transaction timed out.", nil); + break; + case CHIP_ERROR_BUFFER_TOO_SMALL.AsInteger(): code = MTRErrorCodeBufferTooSmall; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"A buffer is too small.", nil) }]; - } else if (errorCode == CHIP_ERROR_FABRIC_EXISTS) { + description = NSLocalizedString(@"A buffer is too small.", nil); + break; + case CHIP_ERROR_FABRIC_EXISTS.AsInteger(): code = MTRErrorCodeFabricExists; - [userInfo addEntriesFromDictionary:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"The device is already a member of this fabric.", nil) - }]; - } else if (errorCode == CHIP_ERROR_DECODE_FAILED) { + description = NSLocalizedString(@"The device is already a member of this fabric.", nil); + break; + case CHIP_ERROR_DECODE_FAILED.AsInteger(): code = MTRErrorCodeTLVDecodeFailed; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"TLV decoding failed.", nil) }]; - } else if (errorCode == CHIP_ERROR_DNS_SD_UNAUTHORIZED) { + description = NSLocalizedString(@"TLV decoding failed.", nil); + break; + case CHIP_ERROR_DNS_SD_UNAUTHORIZED.AsInteger(): code = MTRErrorCodeDNSSDUnauthorized; - [userInfo addEntriesFromDictionary:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Access denied to perform DNS-SD lookups. Check that \"_matter._tcp\" and/or \"_matterc._udp\" are listed under the NSBonjourServices key in Info.plist", nil) }]; - } else { + description = NSLocalizedString(@"Access denied to perform DNS-SD lookups. " + "Check that \"_matter._tcp\" and/or \"_matterc._udp\" " + "are listed under the NSBonjourServices key in Info.plist", + nil); + break; + case CHIP_ERROR_CANCELLED.AsInteger(): + code = MTRErrorCodeCancelled; + description = NSLocalizedString(@"The operation was cancelled.", nil); + break; + default: code = MTRErrorCodeGeneralError; - [userInfo addEntriesFromDictionary:@{ - NSLocalizedDescriptionKey : - [NSString stringWithFormat:NSLocalizedString(@"Undefined error:%u.", nil), errorCode.AsInteger()], - @"errorCode" : @(errorCode.AsInteger()), - }]; + description = [NSString stringWithFormat:NSLocalizedString(@"General error: %u", nil), errorCode.AsInteger()]; + additionalUserInfo = @{ @"errorCode" : @(errorCode.AsInteger()) }; + } + + NSDictionary * userInfo = @{ NSLocalizedDescriptionKey : description }; + if (additionalUserInfo) { + NSMutableDictionary * combined = [userInfo mutableCopy]; + [combined addEntriesFromDictionary:additionalUserInfo]; + userInfo = combined; } auto * error = [NSError errorWithDomain:MTRErrorDomain code:code userInfo:userInfo]; @@ -125,105 +145,87 @@ + (NSError *)errorForIMStatus:(const chip::app::StatusIB &)status using chip::Protocols::InteractionModel::Status; switch (status.mStatus) { case Status::Failure: - default: { + default: description = NSLocalizedString(@"Operation was not successful.", nil); break; - } - case Status::InvalidSubscription: { + case Status::InvalidSubscription: description = NSLocalizedString(@"Subscription ID is not active.", nil); break; - } - case Status::UnsupportedAccess: { + case Status::UnsupportedAccess: description = NSLocalizedString(@"The sender of the action or command does not have authorization or access.", nil); break; - } - case Status::UnsupportedEndpoint: { + case Status::UnsupportedEndpoint: description = NSLocalizedString(@"The endpoint indicated is unsupported on the node.", nil); break; - } - case Status::InvalidAction: { - description = NSLocalizedString( - @"The action is malformed, has missing fields, or fields with invalid values. Action not carried out.", nil); + case Status::InvalidAction: + description = NSLocalizedString(@"The action is malformed, has missing fields, or fields with invalid values. " + "Action not carried out.", + nil); break; - } - case Status::UnsupportedCommand: { - description = NSLocalizedString( - @"The specified action or command indicated is not supported on the device. Command or action not carried out.", nil); + case Status::UnsupportedCommand: + description = NSLocalizedString(@"The specified action or command indicated is not supported on the device." + "Command or action not carried out.", + nil); break; - } - case Status::InvalidCommand: { - description = NSLocalizedString( - @"The cluster command is malformed, has missing fields, or fields with invalid values. Command not carried out.", nil); + case Status::InvalidCommand: + description = NSLocalizedString(@"The cluster command is malformed, has missing fields, or fields with invalid values." + "Command not carried out.", + nil); break; - } - case Status::UnsupportedAttribute: { - description - = NSLocalizedString(@"The specified attribute or attribute data field or entry does not exist on the device.", nil); + case Status::UnsupportedAttribute: + description = NSLocalizedString(@"The specified attribute or attribute data field or entry does not exist on the device.", nil); break; - } - case Status::ConstraintError: { + case Status::ConstraintError: description = NSLocalizedString(@"Out of range error or set to a reserved value.", nil); break; - } - case Status::UnsupportedWrite: { + case Status::UnsupportedWrite: description = NSLocalizedString(@"Attempt to write a read-only attribute.", nil); break; - } - case Status::ResourceExhausted: { + case Status::ResourceExhausted: description = NSLocalizedString(@"An action or operation failed due to insufficient available resources. ", nil); break; - } - case Status::NotFound: { + case Status::NotFound: description = NSLocalizedString(@"The indicated data field or entry could not be found.", nil); break; - } - case Status::UnreportableAttribute: { + case Status::UnreportableAttribute: description = NSLocalizedString(@"Reports cannot be issued for this attribute.", nil); break; - } - case Status::InvalidDataType: { - description = NSLocalizedString( - @"The data type indicated is undefined or invalid for the indicated data field. Command or action not carried out.", + case Status::InvalidDataType: + description = NSLocalizedString(@"The data type indicated is undefined or invalid for the indicated data field. " + "Command or action not carried out.", nil); break; - } - case Status::UnsupportedRead: { + case Status::UnsupportedRead: description = NSLocalizedString(@"Attempt to read a write-only attribute.", nil); break; - } - case Status::DataVersionMismatch: { + case Status::DataVersionMismatch: description = NSLocalizedString(@"Cluster instance data version did not match request path.", nil); break; - } - case Status::Timeout: { + case Status::Timeout: description = NSLocalizedString(@"The transaction was aborted due to time being exceeded.", nil); break; - } case Status::Busy: { - description = NSLocalizedString( - @"The receiver is busy processing another action that prevents the execution of the incoming action.", nil); + description = NSLocalizedString(@"The receiver is busy processing another action " + "that prevents the execution of the incoming action.", + nil); break; - } - case Status::UnsupportedCluster: { + case Status::UnsupportedCluster: description = NSLocalizedString(@"The cluster indicated is not supported", nil); break; - } // Gap in values is intentional. - case Status::NoUpstreamSubscription: { + case Status::NoUpstreamSubscription: description = NSLocalizedString(@"Proxy does not have a subscription to the source.", nil); break; } - case Status::NeedsTimedInteraction: { - description = NSLocalizedString(@"An Untimed Write or Untimed Invoke interaction was used for an attribute or command that " - @"requires a Timed Write or Timed Invoke.", + case Status::NeedsTimedInteraction: + description = NSLocalizedString(@"An Untimed Write or Untimed Invoke interaction was used " + "for an attribute or command that requires a Timed Write or Timed Invoke.", nil); break; - } - case Status::UnsupportedEvent: { + case Status::UnsupportedEvent: description = NSLocalizedString(@"The event indicated is unsupported on the cluster.", nil); break; } - } NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init]; userInfo[NSLocalizedDescriptionKey] = description; @@ -301,17 +303,21 @@ + (CHIP_ERROR)errorToCHIPErrorCode:(NSError * _Nullable)error case MTRErrorCodeDNSSDUnauthorized: code = CHIP_ERROR_DNS_SD_UNAUTHORIZED.AsInteger(); break; + case MTRErrorCodeCancelled: + code = CHIP_ERROR_CANCELLED.AsInteger(); + break; case MTRErrorCodeGeneralError: { - if (error.userInfo != nil && error.userInfo[@"errorCode"] != nil) { - code = static_cast([error.userInfo[@"errorCode"] unsignedLongValue]); + id userInfoErrorCode = error.userInfo[@"errorCode"]; + if ([userInfoErrorCode isKindOfClass:NSNumber.class]) { + code = static_cast([userInfoErrorCode unsignedLongValue]); break; } // Weird error we did not create. Fall through. + } default: code = CHIP_ERROR_INTERNAL.AsInteger(); break; } - } return chip::ChipError(code); } diff --git a/src/darwin/Framework/CHIPTests/MTRPairingTests.m b/src/darwin/Framework/CHIPTests/MTRPairingTests.m index 6757dea7ceb541..54b8a4991325b7 100644 --- a/src/darwin/Framework/CHIPTests/MTRPairingTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPairingTests.m @@ -32,7 +32,7 @@ static const uint16_t kPairingTimeoutInSeconds = 10; static const uint16_t kTimeoutInSeconds = 3; -static uint64_t sDeviceId = 0x12344321; +static uint64_t sDeviceId = 100000000; static NSString * kOnboardingPayload = @"MT:Y.K90SO527JA0648G00"; static const uint16_t kLocalPort = 5541; static const uint16_t kTestVendorId = 0xFFF1u; @@ -82,6 +82,7 @@ @interface MTRPairingTestControllerDelegate : NSObject attestationDelegate; @property (nonatomic, nullable) NSNumber * failSafeExtension; +@property (nullable) NSError * commissioningCompleteError; @end @implementation MTRPairingTestControllerDelegate @@ -100,22 +101,22 @@ - (id)initWithExpectation:(XCTestExpectation *)expectation - (void)controller:(MTRDeviceController *)controller commissioningSessionEstablishmentDone:(NSError * _Nullable)error { - XCTAssertEqual(error.code, 0); + XCTAssertNil(error); __auto_type * params = [[MTRCommissioningParameters alloc] init]; params.deviceAttestationDelegate = self.attestationDelegate; params.failSafeTimeout = self.failSafeExtension; NSError * commissionError = nil; - [controller commissionNodeWithID:@(sDeviceId) commissioningParams:params error:&commissionError]; - XCTAssertNil(commissionError); + XCTAssertTrue([controller commissionNodeWithID:@(sDeviceId) commissioningParams:params error:&commissionError], + @"Failed to start commissioning for node ID %" PRIu64 ": %@", sDeviceId, commissionError); // Keep waiting for onCommissioningComplete } - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSError * _Nullable)error { - XCTAssertEqual(error.code, 0); + self.commissioningCompleteError = error; [_expectation fulfill]; _expectation = nil; } @@ -123,6 +124,7 @@ - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSEr @end @interface MTRPairingTests : XCTestCase +@property (nullable) MTRPairingTestControllerDelegate * controllerDelegate; @end @implementation MTRPairingTests @@ -148,11 +150,9 @@ + (void)setUp + (void)tearDown { - MTRDeviceController * controller = sController; - XCTAssertNotNil(controller); - - [controller shutdown]; - XCTAssertFalse([controller isRunning]); + [sController shutdown]; + XCTAssertFalse([sController isRunning]); + sController = nil; [[MTRDeviceControllerFactory sharedInstance] stopControllerFactory]; } @@ -163,6 +163,12 @@ - (void)setUp [self setContinueAfterFailure:NO]; } +- (void)tearDown +{ + [sController setDeviceControllerDelegate:(id _Nonnull) nil queue:dispatch_get_main_queue()]; // TODO: do we need a clearDeviceControllerDelegate API? + self.controllerDelegate = nil; +} + // attestationDelegate and failSafeExtension can both be nil - (void)doPairingTestWithAttestationDelegate:(id)attestationDelegate failSafeExtension:(NSNumber *)failSafeExtension { @@ -176,6 +182,7 @@ - (void)doPairingTestWithAttestationDelegate:(id)a dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); [sController setDeviceControllerDelegate:controllerDelegate queue:callbackQueue]; + self.controllerDelegate = controllerDelegate; NSError * error; __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:&error]; @@ -186,6 +193,7 @@ - (void)doPairingTestWithAttestationDelegate:(id)a XCTAssertNil(error); [self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds]; + XCTAssertNil(controllerDelegate.commissioningCompleteError); ResetCommissionee([MTRBaseDevice deviceWithNodeID:@(sDeviceId) controller:sController], dispatch_get_main_queue(), self, kTimeoutInSeconds); @@ -232,4 +240,62 @@ - (void)test004_PairWithAttestationDelegateFailsafeExtensionLong [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds]; } +- (void)doPairingAndWaitForProgress:(NSString *)trigger +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Trigger message seen"]; + MTRSetLogCallback(MTRLogTypeDetail, ^(MTRLogType type, NSString * moduleName, NSString * message) { + if ([message containsString:trigger]) { + [expectation fulfill]; + } + }); + + __auto_type * controllerDelegate = [[MTRPairingTestControllerDelegate alloc] initWithExpectation:nil + attestationDelegate:nil + failSafeExtension:nil]; + [sController setDeviceControllerDelegate:controllerDelegate queue:dispatch_get_main_queue()]; + self.controllerDelegate = controllerDelegate; + + __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:NULL]; + XCTAssertNotNil(payload); + NSError * error; + XCTAssertTrue([sController setupCommissioningSessionWithPayload:payload newNodeID:@(++sDeviceId) error:&error]); + XCTAssertNil(error); + + [self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds]; + MTRSetLogCallback(0, nil); +} + +- (void)doPairingTestAfterCancellationAtProgress:(NSString *)trigger +{ + // Run pairing up and wait for the trigger + [self doPairingAndWaitForProgress:trigger]; + + // Call StopPairing and wait for the commissioningComplete callback + XCTestExpectation * expectation = [self expectationWithDescription:@"commissioningComplete delegate method called"]; + self.controllerDelegate.expectation = expectation; + + NSError * error; + XCTAssertTrue([sController stopDevicePairing:sDeviceId error:&error], @"stopDevicePairing failed: %@", error); + [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds]; + + // Validate that the completion correctly indicated cancellation + error = self.controllerDelegate.commissioningCompleteError; + XCTAssertEqualObjects(error.domain, MTRErrorDomain); + XCTAssertEqual(error.code, MTRErrorCodeCancelled); + + // Now pair again. If the previous attempt was cancelled correctly this should work fine. + [self doPairingTestWithAttestationDelegate:nil failSafeExtension:nil]; +} + +- (void)test005_pairingAfterCancellation_ReadCommissioningInfo +{ + // @"Sending read request for commissioning information" + [self doPairingTestAfterCancellationAtProgress:@"Performing next commissioning step 'ReadCommissioningInfo'"]; +} + +- (void)test006_pairingAfterCancellation_ConfigRegulatoryCommand +{ + [self doPairingTestAfterCancellationAtProgress:@"Performing next commissioning step 'ConfigRegulatory'"]; +} + @end diff --git a/src/lib/support/CodeUtils.h b/src/lib/support/CodeUtils.h index 2b47538b8faebf..500804bc94912d 100644 --- a/src/lib/support/CodeUtils.h +++ b/src/lib/support/CodeUtils.h @@ -529,7 +529,7 @@ inline void chipDie(void) */ #if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE #define VerifyOrDie(aCondition) \ - nlABORT_ACTION(aCondition, ChipLogDetail(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition)) + nlABORT_ACTION(aCondition, ChipLogError(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition)) #else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE #define VerifyOrDie(aCondition) VerifyOrDieWithoutLogging(aCondition) #endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE @@ -569,7 +569,7 @@ inline void chipDie(void) * */ #define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \ - nlABORT_ACTION(aCondition, ChipLogDetail(aModule, aMessage, ##__VA_ARGS__)) + nlABORT_ACTION(aCondition, ChipLogError(aModule, aMessage, ##__VA_ARGS__)) /** * @def LogErrorOnFailure(expr) diff --git a/src/lib/support/logging/TextOnlyLogging.cpp b/src/lib/support/logging/TextOnlyLogging.cpp index 1e573942f85e17..eb7065f2d938d4 100644 --- a/src/lib/support/logging/TextOnlyLogging.cpp +++ b/src/lib/support/logging/TextOnlyLogging.cpp @@ -209,21 +209,21 @@ void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) } #if CHIP_LOG_FILTERING -uint8_t gLogFilter = kLogCategory_Max; +std::atomic gLogFilter(kLogCategory_Max); uint8_t GetLogFilter() { - return gLogFilter; + return gLogFilter.load(); } void SetLogFilter(uint8_t category) { - gLogFilter = category; + gLogFilter.store(category); } bool IsCategoryEnabled(uint8_t category) { - return (category <= gLogFilter); + return (category <= GetLogFilter()); } #endif // CHIP_LOG_FILTERING From ed8f37dbf58e188144cdba0ba7dd7ddd1b133c2c Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:41:18 -0800 Subject: [PATCH 36/52] [Darwin] MTRDevice delegate should be notified when cache is primed with basic info (#32251) * [Darwin] MTRDevice delegate should be notified when cache is primed with basic info * Changed logic to only report once, and also improve documentation in comments * Update src/darwin/Framework/CHIP/MTRDevice.mm Co-authored-by: Boris Zbarsky --------- Co-authored-by: Boris Zbarsky --- src/darwin/Framework/CHIP/MTRDevice.h | 9 ++ src/darwin/Framework/CHIP/MTRDevice.mm | 84 ++++++++++++++++++- .../Framework/CHIPTests/MTRDeviceTests.m | 15 +++- .../TestHelpers/MTRDeviceTestDelegate.h | 1 + .../TestHelpers/MTRDeviceTestDelegate.m | 7 ++ 5 files changed, 111 insertions(+), 5 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h index 23a1ecf51ad56c..ee7b1608d8246c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.h +++ b/src/darwin/Framework/CHIP/MTRDevice.h @@ -381,6 +381,15 @@ MTR_EXTERN NSString * const MTRDataVersionKey MTR_NEWLY_AVAILABLE; */ - (void)deviceBecameActive:(MTRDevice *)device MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +/** + * Notifies delegate when the device attribute cache has been primed with initial configuration data of the device + * + * This is called when the MTRDevice object goes from not knowing the device to having cached the first attribute reports that include basic mandatory information, e.g. Descriptor clusters. + * + * The intention is that after this is called, the client should be able to call read for mandatory attributes and likely expect non-nil values. + */ +- (void)deviceCachePrimed:(MTRDevice *)device MTR_NEWLY_AVAILABLE; + @end @interface MTRDevice (Deprecated) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index dbf1785c9bb6ba..0f304f42b7a741 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -225,6 +225,7 @@ @implementation MTRDevice { #ifdef DEBUG NSUInteger _unitTestAttributesReportedSinceLastCheck; #endif + BOOL _delegateDeviceCachePrimedCalled; } - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller @@ -502,6 +503,11 @@ - (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queu _weakDelegate = [MTRWeakReference weakReferenceWithObject:delegate]; _delegateQueue = queue; + // If Check if cache is already primed and client hasn't been informed yet, call the -deviceCachePrimed: callback + if (!_delegateDeviceCachePrimedCalled && [self _isCachePrimedWithInitialConfigurationData]) { + [self _callDelegateDeviceCachePrimed]; + } + if (setUpSubscription) { [self _setupSubscription]; } @@ -574,6 +580,29 @@ - (BOOL)_subscriptionAbleToReport return (delegate != nil) && (state == MTRDeviceStateReachable); } +- (BOOL)_callDelegateWithBlock:(void (^)(id))block +{ + os_unfair_lock_assert_owner(&self->_lock); + id delegate = _weakDelegate.strongObject; + if (delegate) { + dispatch_async(_delegateQueue, ^{ + block(delegate); + }); + return YES; + } + return NO; +} + +- (void)_callDelegateDeviceCachePrimed +{ + os_unfair_lock_assert_owner(&self->_lock); + _delegateDeviceCachePrimedCalled = [self _callDelegateWithBlock:^(id delegate) { + if ([delegate respondsToSelector:@selector(deviceCachePrimed:)]) { + [delegate deviceCachePrimed:self]; + } + }]; +} + // assume lock is held - (void)_changeState:(MTRDeviceState)state { @@ -611,6 +640,11 @@ - (void)_handleSubscriptionEstablished // reset subscription attempt wait time when subscription succeeds _lastSubscriptionAttemptWait = 0; + // As subscription is established, check if the delegate needs to be informed + if (!_delegateDeviceCachePrimedCalled) { + [self _callDelegateDeviceCachePrimed]; + } + [self _changeState:MTRDeviceStateReachable]; os_unfair_lock_unlock(&self->_lock); @@ -741,6 +775,7 @@ - (void)_handleReportEnd _receivingReport = NO; _receivingPrimingReport = NO; _estimatedStartTimeFromGeneralDiagnosticsUpTime = nil; + // For unit testing only #ifdef DEBUG id delegate = _weakDelegate.strongObject; @@ -1948,17 +1983,24 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray *)attributeValues reportChanges:(BOOL)reportChanges { + os_unfair_lock_lock(&self->_lock); + if (reportChanges) { - [self _handleAttributeReport:attributeValues]; + [self _reportAttributes:[self _getAttributesToReportWithReportedValues:attributeValues]]; } else { - os_unfair_lock_lock(&self->_lock); for (NSDictionary * responseValue in attributeValues) { MTRAttributePath * path = responseValue[MTRAttributePathKey]; NSDictionary * dataValue = responseValue[MTRDataKey]; _readCache[path] = dataValue; } - os_unfair_lock_unlock(&self->_lock); } + + // If cache is set from storage and is primed with initial configuration data, then assume the client had beeen informed in the past, and mark that the callback has been called + if ([self _isCachePrimedWithInitialConfigurationData]) { + _delegateDeviceCachePrimedCalled = YES; + } + + os_unfair_lock_unlock(&self->_lock); } // If value is non-nil, associate with expectedValueID @@ -2135,6 +2177,42 @@ - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath e } } +// This method checks if there is a need to inform delegate that the attribute cache has been "primed" +- (BOOL)_isCachePrimedWithInitialConfigurationData +{ + os_unfair_lock_assert_owner(&self->_lock); + + // Check if root node descriptor exists + NSDictionary * rootDescriptorPartsListDataValue = _readCache[[MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId) clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)]]; + if (!rootDescriptorPartsListDataValue || ![MTRArrayValueType isEqualToString:rootDescriptorPartsListDataValue[MTRTypeKey]]) { + return NO; + } + NSArray * partsList = rootDescriptorPartsListDataValue[MTRValueKey]; + if (![partsList isKindOfClass:[NSArray class]] || !partsList.count) { + MTR_LOG_ERROR("%@ unexpected type %@ for parts list %@", self, [partsList class], partsList); + return NO; + } + + // Check if we have cached descriptor clusters for each listed endpoint + for (NSDictionary * endpointDataValue in partsList) { + if (![MTRUnsignedIntegerValueType isEqual:endpointDataValue[MTRTypeKey]]) { + MTR_LOG_ERROR("%@ unexpected type for parts list item %@", self, endpointDataValue); + continue; + } + NSNumber * endpoint = endpointDataValue[MTRValueKey]; + if (![endpoint isKindOfClass:[NSNumber class]]) { + MTR_LOG_ERROR("%@ unexpected type for parts list item %@", self, endpointDataValue); + continue; + } + NSDictionary * descriptorDeviceTypeListDataValue = _readCache[[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID)]]; + if (![MTRArrayValueType isEqualToString:descriptorDeviceTypeListDataValue[MTRTypeKey]] || !descriptorDeviceTypeListDataValue[MTRValueKey]) { + return NO; + } + } + + return YES; +} + - (MTRBaseDevice *)newBaseDevice { return [MTRBaseDevice deviceWithNodeID:self.nodeID controller:self.deviceController]; diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index e4628bd475b5ae..e5539f4f75d993 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -2843,7 +2843,7 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage { dispatch_queue_t queue = dispatch_get_main_queue(); - // First start with clean slate and + // First start with clean slate by removing the MTRDevice and clearing the persisted cache __auto_type * device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; [sController removeDevice:device]; [sController.controllerDataStore clearAllStoredAttributes]; @@ -2853,6 +2853,7 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage // Now recreate device and get subscription primed device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; XCTestExpectation * gotReportsExpectation = [self expectationWithDescription:@"Attribute and Event reports have been received"]; + XCTestExpectation * gotDeviceCachePrimed = [self expectationWithDescription:@"Device cache primed for the first time"]; __auto_type * delegate = [[MTRDeviceTestDelegate alloc] init]; __weak __auto_type weakDelegate = delegate; delegate.onReportEnd = ^{ @@ -2860,9 +2861,12 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage __strong __auto_type strongDelegate = weakDelegate; strongDelegate.onReportEnd = nil; }; + delegate.onDeviceCachePrimed = ^{ + [gotDeviceCachePrimed fulfill]; + }; [device setDelegate:delegate queue:queue]; - [self waitForExpectations:@[ gotReportsExpectation ] timeout:60]; + [self waitForExpectations:@[ gotReportsExpectation, gotDeviceCachePrimed ] timeout:60]; NSUInteger attributesReportedWithFirstSubscription = [device unitTestAttributesReportedSinceLastCheck]; @@ -2879,10 +2883,17 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage __strong __auto_type strongDelegate = weakDelegate; strongDelegate.onReportEnd = nil; }; + __block BOOL onDeviceCachePrimedCalled = NO; + delegate.onDeviceCachePrimed = ^{ + onDeviceCachePrimedCalled = YES; + }; [device setDelegate:delegate queue:queue]; [self waitForExpectations:@[ resubGotReportsExpectation ] timeout:60]; + // Make sure that the new callback is only ever called once, the first time subscription was primed + XCTAssertFalse(onDeviceCachePrimedCalled); + NSUInteger attributesReportedWithSecondSubscription = [device unitTestAttributesReportedSinceLastCheck]; XCTAssertTrue(attributesReportedWithSecondSubscription < attributesReportedWithFirstSubscription); diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h index 0f7fce14226525..e8fd8f969b2aeb 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h @@ -27,6 +27,7 @@ typedef void (^MTRDeviceTestDelegateDataHandler)(NSArray Date: Tue, 27 Feb 2024 20:10:07 -0800 Subject: [PATCH 37/52] [Android] Add initial batch command support (#32326) --- .github/workflows/java-tests.yaml | 11 + examples/java-matter-controller/BUILD.gn | 1 + .../java/src/com/matter/controller/Main.kt | 1 + ...rOnNetworkLongImExtendableInvokeCommand.kt | 183 ++++++++++++++ kotlin-detect-config.yaml | 1 + scripts/tests/java/im_test.py | 13 + src/controller/java/AndroidCallbacks-JNI.cpp | 11 + src/controller/java/AndroidCallbacks.cpp | 180 +++++++++++++- src/controller/java/AndroidCallbacks.h | 17 ++ .../java/AndroidInteractionClient.cpp | 187 ++++++++++++++- .../java/AndroidInteractionClient.h | 2 + src/controller/java/BUILD.gn | 4 + .../java/CHIPDeviceController-JNI.cpp | 12 + src/controller/java/MatterCallbacks-JNI.cpp | 11 + .../java/MatterInteractionClient-JNI.cpp | 12 + .../devicecontroller/BatchInvokeCallback.java | 54 +++++ .../BatchInvokeCallbackJni.java | 94 ++++++++ .../ChipDeviceController.java | 34 +++ .../ExtendableInvokeCallback.java | 54 +++++ .../ExtendableInvokeCallbackJni.java | 94 ++++++++ .../model/InvokeResponseData.java | 224 ++++++++++++++++++ .../model/NoInvokeResponseData.java | 38 +++ .../chip/devicecontroller/model/Status.java | 4 + src/lib/core/core.gni | 3 +- 24 files changed, 1242 insertions(+), 3 deletions(-) create mode 100644 examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt create mode 100644 src/controller/java/src/chip/devicecontroller/BatchInvokeCallback.java create mode 100644 src/controller/java/src/chip/devicecontroller/BatchInvokeCallbackJni.java create mode 100644 src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallback.java create mode 100644 src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallbackJni.java create mode 100644 src/controller/java/src/chip/devicecontroller/model/InvokeResponseData.java create mode 100644 src/controller/java/src/chip/devicecontroller/model/NoInvokeResponseData.java diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml index f19d451fbc2df6..3aa6935d1735c4 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -132,6 +132,17 @@ jobs: --tool-cluster "im" \ --tool-args "onnetwork-long-im-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ --factoryreset \ + ' + - name: Run IM Batch Invoke Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_java_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-java-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-batch-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ ' - name: Run IM Read Test run: | diff --git a/examples/java-matter-controller/BUILD.gn b/examples/java-matter-controller/BUILD.gn index 34de1ed5585c12..76acd190eade16 100644 --- a/examples/java-matter-controller/BUILD.gn +++ b/examples/java-matter-controller/BUILD.gn @@ -57,6 +57,7 @@ kotlin_binary("java-matter-controller") { "java/src/com/matter/controller/commands/pairing/PairOnNetworkFabricCommand.kt", "java/src/com/matter/controller/commands/pairing/PairOnNetworkInstanceNameCommand.kt", "java/src/com/matter/controller/commands/pairing/PairOnNetworkLongCommand.kt", + "java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt", "java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt", "java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImReadCommand.kt", "java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImSubscribeCommand.kt", diff --git a/examples/java-matter-controller/java/src/com/matter/controller/Main.kt b/examples/java-matter-controller/java/src/com/matter/controller/Main.kt index d0e2ef892b0e95..a1a66a8420b196 100644 --- a/examples/java-matter-controller/java/src/com/matter/controller/Main.kt +++ b/examples/java-matter-controller/java/src/com/matter/controller/Main.kt @@ -67,6 +67,7 @@ private fun getImCommands( PairOnNetworkLongImSubscribeCommand(controller, credentialsIssuer), PairOnNetworkLongImWriteCommand(controller, credentialsIssuer), PairOnNetworkLongImInvokeCommand(controller, credentialsIssuer), + PairOnNetworkLongImExtendableInvokeCommand(controller, credentialsIssuer), ) } diff --git a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt new file mode 100644 index 00000000000000..d80e71acb53d73 --- /dev/null +++ b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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.matter.controller.commands.pairing + +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ExtendableInvokeCallback +import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback +import chip.devicecontroller.model.InvokeElement +import chip.devicecontroller.model.InvokeResponseData +import chip.devicecontroller.model.NoInvokeResponseData +import chip.devicecontroller.model.Status +import com.matter.controller.commands.common.CredentialsIssuer +import java.util.logging.Level +import java.util.logging.Logger +import kotlin.UShort +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.TlvWriter + +class PairOnNetworkLongImExtendableInvokeCommand( + controller: ChipDeviceController, + credsIssue: CredentialsIssuer? +) : + PairingCommand( + controller, + "onnetwork-long-im-batch-invoke", + credsIssue, + PairingModeType.ON_NETWORK, + PairingNetworkType.NONE, + DiscoveryFilterType.LONG_DISCRIMINATOR + ) { + private var devicePointer: Long = 0 + + private fun setDevicePointer(devicePointer: Long) { + this.devicePointer = devicePointer + } + + private inner class InternalInvokeCallback : ExtendableInvokeCallback { + private var responseCount = 0 + + override fun onError(e: Exception) { + logger.log(Level.INFO, "Batch Invoke receive onError" + e.message) + setFailure("invoke failure") + } + + override fun onResponse(invokeResponseData: InvokeResponseData) { + logger.log(Level.INFO, "Batch Invoke receive OnResponse on $invokeResponseData") + val clusterId = invokeResponseData.getClusterId().getId() + val commandId = invokeResponseData.getCommandId().getId() + val tlvData = invokeResponseData.getTlvByteArray() + val jsonData = invokeResponseData.getJsonString() + val status = invokeResponseData.getStatus() + + if (clusterId == CLUSTER_ID_IDENTIFY && commandId == IDENTIFY_COMMAND) { + if (tlvData != null || jsonData != null) { + setFailure("invoke failure with problematic payload") + } + if ( + status != null && status.status != Status.Code.Success && status.clusterStatus.isPresent() + ) { + setFailure("invoke failure with incorrect status") + } + } + + if (clusterId == CLUSTER_ID_TEST && commandId == TEST_ADD_ARGUMENT_RSP_COMMAND) { + if (tlvData == null || jsonData == null) { + setFailure("invoke failure with problematic payload") + } + + if (!jsonData.equals("""{"0:UINT":2}""")) { + setFailure("invoke failure with problematic json") + } + + if (status != null) { + setFailure("invoke failure with incorrect status") + } + } + responseCount++ + } + + override fun onNoResponse(noInvokeResponseData: NoInvokeResponseData) { + logger.log(Level.INFO, "Batch Invoke receive onNoResponse on $noInvokeResponseData") + } + + override fun onDone() { + if (responseCount == TEST_COMMONDS_NUM) { + setSuccess() + } else { + setFailure("invoke failure") + } + } + } + + private inner class InternalGetConnectedDeviceCallback : GetConnectedDeviceCallback { + override fun onDeviceConnected(devicePointer: Long) { + setDevicePointer(devicePointer) + logger.log(Level.INFO, "onDeviceConnected") + } + + override fun onConnectionFailure(nodeId: Long, error: Exception) { + logger.log(Level.INFO, "onConnectionFailure") + } + } + + override fun runCommand() { + val number: UShort = 1u + val tlvWriter1 = TlvWriter() + tlvWriter1.startStructure(AnonymousTag) + tlvWriter1.put(ContextSpecificTag(0), number) + tlvWriter1.endStructure() + + val element1: InvokeElement = + InvokeElement.newInstance( + /* endpointId= */ 0, + CLUSTER_ID_IDENTIFY, + IDENTIFY_COMMAND, + tlvWriter1.getEncoded(), + null + ) + + val tlvWriter2 = TlvWriter() + tlvWriter2.startStructure(AnonymousTag) + tlvWriter2.put(ContextSpecificTag(0), number) + tlvWriter2.put(ContextSpecificTag(1), number) + tlvWriter2.endStructure() + + val element2: InvokeElement = + InvokeElement.newInstance( + /* endpointId= */ 1, + CLUSTER_ID_TEST, + TEST_ADD_ARGUMENT_COMMAND, + tlvWriter2.getEncoded(), + null + ) + + val invokeList = listOf(element1, element2) + currentCommissioner() + .pairDeviceWithAddress( + getNodeId(), + getRemoteAddr().address.hostAddress, + MATTER_PORT, + getDiscriminator(), + getSetupPINCode(), + null + ) + currentCommissioner().setCompletionListener(this) + waitCompleteMs(getTimeoutMillis()) + currentCommissioner() + .getConnectedDevicePointer(getNodeId(), InternalGetConnectedDeviceCallback()) + clear() + currentCommissioner() + .extendableInvoke(InternalInvokeCallback(), devicePointer, invokeList, 0, 0) + waitCompleteMs(getTimeoutMillis()) + } + + companion object { + private val logger = + Logger.getLogger(PairOnNetworkLongImExtendableInvokeCommand::class.java.name) + + private const val MATTER_PORT = 5540 + private const val CLUSTER_ID_IDENTIFY = 0x0003L + private const val IDENTIFY_COMMAND = 0L + private const val CLUSTER_ID_TEST = 0xFFF1FC05L + private const val TEST_ADD_ARGUMENT_COMMAND = 0X04L + private const val TEST_ADD_ARGUMENT_RSP_COMMAND = 0X01L + private const val TEST_COMMONDS_NUM = 2 + } +} diff --git a/kotlin-detect-config.yaml b/kotlin-detect-config.yaml index 2ad54a05d6c483..c8398293d93e8b 100644 --- a/kotlin-detect-config.yaml +++ b/kotlin-detect-config.yaml @@ -104,6 +104,7 @@ style: - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkInstanceNameCommand.kt" - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongCommand.kt" - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt" + - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt" - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt" - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkShortCommand.kt" - "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkVendorCommand.kt" diff --git a/scripts/tests/java/im_test.py b/scripts/tests/java/im_test.py index d8acc6362e000d..6ac393629fbee8 100755 --- a/scripts/tests/java/im_test.py +++ b/scripts/tests/java/im_test.py @@ -71,6 +71,14 @@ def TestCmdOnnetworkLongImInvoke(self, nodeid, setuppin, discriminator, timeout) DumpProgramOutputToQueue(self.thread_list, Fore.GREEN + "JAVA " + Style.RESET_ALL, java_process, self.queue) return java_process.wait() + def TestCmdOnnetworkLongImExtendableInvoke(self, nodeid, setuppin, discriminator, timeout): + java_command = self.command + ['im', 'onnetwork-long-im-batch-invoke', nodeid, setuppin, discriminator, timeout] + logging.info(f"Execute: {java_command}") + java_process = subprocess.Popen( + java_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + DumpProgramOutputToQueue(self.thread_list, Fore.GREEN + "JAVA " + Style.RESET_ALL, java_process, self.queue) + return java_process.wait() + def TestCmdOnnetworkLongImWrite(self, nodeid, setuppin, discriminator, timeout): java_command = self.command + ['im', 'onnetwork-long-im-write', nodeid, setuppin, discriminator, timeout] logging.info(f"Execute: {java_command}") @@ -101,6 +109,11 @@ def RunTest(self): code = self.TestCmdOnnetworkLongImInvoke(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout) if code != 0: raise Exception(f"Testing pairing onnetwork-long-im-invoke failed with error {code}") + elif self.command_name == 'onnetwork-long-im-batch-invoke': + logging.info("Testing pairing onnetwork-long-im-batch-invoke") + code = self.TestCmdOnnetworkLongImExtendableInvoke(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout) + if code != 0: + raise Exception(f"Testing pairing onnetwork-long-im-batch-invoke failed with error {code}") elif self.command_name == 'onnetwork-long-im-write': logging.info("Testing pairing onnetwork-long-im-write") code = self.TestCmdOnnetworkLongImWrite(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout) diff --git a/src/controller/java/AndroidCallbacks-JNI.cpp b/src/controller/java/AndroidCallbacks-JNI.cpp index c87b669f9b521f..fff1226bdb37d2 100644 --- a/src/controller/java/AndroidCallbacks-JNI.cpp +++ b/src/controller/java/AndroidCallbacks-JNI.cpp @@ -69,3 +69,14 @@ JNI_METHOD(void, InvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self, { deleteInvokeCallback(env, self, callbackHandle); } + +JNI_METHOD(jlong, ExtendableInvokeCallbackJni, newCallback) +(JNIEnv * env, jobject self) +{ + return newExtendableInvokeCallback(env, self); +} + +JNI_METHOD(void, ExtendableInvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self, jlong callbackHandle) +{ + deleteExtendableInvokeCallback(env, self, callbackHandle); +} diff --git a/src/controller/java/AndroidCallbacks.cpp b/src/controller/java/AndroidCallbacks.cpp index fb7975262dff57..fc9fbe02f12292 100644 --- a/src/controller/java/AndroidCallbacks.cpp +++ b/src/controller/java/AndroidCallbacks.cpp @@ -791,6 +791,7 @@ InvokeCallback::~InvokeCallback() if (mCommandSender != nullptr) { Platform::Delete(mCommandSender); + mCommandSender = nullptr; } } @@ -802,7 +803,6 @@ void InvokeCallback::OnResponse(app::CommandSender * apCommandSender, const app: VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); jmethodID onResponseMethod; JniLocalReferenceScope scope(env); - VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to create Java InvokeElement: %s", ErrorStr(err))); VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(), ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__)); jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef(); @@ -1023,6 +1023,170 @@ jobject DecodeGeneralTLVValue(JNIEnv * env, TLV::TLVReader & readerForGeneralVal } } +ExtendableInvokeCallback::ExtendableInvokeCallback(jobject wrapperCallback) +{ + VerifyOrReturn(mWrapperCallbackRef.Init(wrapperCallback) == CHIP_NO_ERROR, + ChipLogError(Controller, "Could not init mWrapperCallbackRef for ExtendableInvokeCallback")); +} + +ExtendableInvokeCallback::~ExtendableInvokeCallback() +{ + if (mCommandSender != nullptr) + { + Platform::Delete(mCommandSender); + mCommandSender = nullptr; + } +} + +void ExtendableInvokeCallback::OnResponse(app::CommandSender * apCommandSender, + const app::CommandSender::ResponseData & aResponseData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); + jmethodID onResponseMethod; + JniLocalReferenceScope scope(env); + VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(), + ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__)); + jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef(); + DeviceLayer::StackUnlock unlock; + + jobject jCommandRef = nullptr; + if (aResponseData.commandRef.HasValue()) + { + err = JniReferences::GetInstance().CreateBoxedObject( + "java/lang/Integer", "(I)V", static_cast(aResponseData.commandRef.Value()), jCommandRef); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format())); + } + + if (aResponseData.data != nullptr) + { + TLV::TLVReader readerForJavaTLV; + TLV::TLVReader readerForJson; + readerForJavaTLV.Init(*(aResponseData.data)); + + // Create TLV byte array to pass to Java layer + size_t bufferLen = readerForJavaTLV.GetRemainingLength() + readerForJavaTLV.GetLengthRead(); + std::unique_ptr buffer = std::unique_ptr(new uint8_t[bufferLen]); + uint32_t size = 0; + + TLV::TLVWriter writer; + writer.Init(buffer.get(), bufferLen); + err = writer.CopyElement(TLV::AnonymousTag(), readerForJavaTLV); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed CopyElement: %" CHIP_ERROR_FORMAT, err.Format())); + size = writer.GetLengthWritten(); + + chip::ByteArray jniByteArray(env, reinterpret_cast(buffer.get()), static_cast(size)); + + // Convert TLV to JSON + std::string json; + readerForJson.Init(buffer.get(), size); + err = readerForJson.Next(); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(Controller, "Failed readerForJson next: %" CHIP_ERROR_FORMAT, err.Format())); + err = TlvToJson(readerForJson, json); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Failed TlvToJson: %" CHIP_ERROR_FORMAT, err.Format())); + UtfString jsonString(env, json.c_str()); + + err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onResponse", + "(IJJLjava/lang/Integer;[BLjava/lang/String;)V", &onResponseMethod); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onResponse method: %s", ErrorStr(err))); + + env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast(aResponseData.path.mEndpointId), + static_cast(aResponseData.path.mClusterId), static_cast(aResponseData.path.mCommandId), + jCommandRef, jniByteArray.jniValue(), jsonString.jniValue()); + } + else + { + err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onResponse", + "(IJJLjava/lang/Integer;ILjava/lang/Integer;)V", &onResponseMethod); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Unable to find onResponse method: %s", ErrorStr(err))); + + jobject jClusterState = nullptr; + if (aResponseData.statusIB.mClusterStatus.HasValue()) + { + err = JniReferences::GetInstance().CreateBoxedObject( + "java/lang/Integer", "(I)V", static_cast(aResponseData.statusIB.mClusterStatus.Value()), jClusterState); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(Controller, "Could not CreateBoxedObject with error %" CHIP_ERROR_FORMAT, err.Format())); + } + + env->CallVoidMethod(wrapperCallbackRef, onResponseMethod, static_cast(aResponseData.path.mEndpointId), + static_cast(aResponseData.path.mClusterId), static_cast(aResponseData.path.mCommandId), + jCommandRef, aResponseData.statusIB.mStatus, jClusterState); + } + + VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +} + +void ExtendableInvokeCallback::OnNoResponse(app::CommandSender * commandSender, + const app::CommandSender::NoResponseData & aNoResponseData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); + jmethodID onNoResponseMethod; + JniLocalReferenceScope scope(env); + VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(), + ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__)); + jobject wrapperCallbackRef = mWrapperCallbackRef.ObjectRef(); + DeviceLayer::StackUnlock unlock; + + err = JniReferences::GetInstance().FindMethod(env, wrapperCallbackRef, "onNoResponse", "(I)V", &onNoResponseMethod); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(Controller, "Unable to find onNoResponse method: %" CHIP_ERROR_FORMAT, err.Format())); + env->CallVoidMethod(wrapperCallbackRef, onNoResponseMethod, static_cast(aNoResponseData.commandRef)); + VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +} + +void ExtendableInvokeCallback::OnError(const app::CommandSender * apCommandSender, const app::CommandSender::ErrorData & aErrorData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); + JniLocalReferenceScope scope(env); + ChipLogError(Controller, "ExtendableInvokeCallback::OnError is called with %u", aErrorData.error.AsInteger()); + jthrowable exception; + err = AndroidControllerExceptions::GetInstance().CreateAndroidControllerException(env, ErrorStr(aErrorData.error), + aErrorData.error.AsInteger(), exception); + VerifyOrReturn( + err == CHIP_NO_ERROR, + ChipLogError(Controller, "Unable to create AndroidControllerException with error: %" CHIP_ERROR_FORMAT, err.Format())); + + jmethodID onErrorMethod; + VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(), + ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__)); + jobject wrapperCallback = mWrapperCallbackRef.ObjectRef(); + err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onError", "(Ljava/lang/Exception;)V", &onErrorMethod); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(Controller, "Unable to find onError method: %" CHIP_ERROR_FORMAT, err.Format())); + + DeviceLayer::StackUnlock unlock; + env->CallVoidMethod(wrapperCallback, onErrorMethod, exception); + VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +} + +void ExtendableInvokeCallback::OnDone(app::CommandSender * apCommandSender) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread")); + JniLocalReferenceScope scope(env); + jmethodID onDoneMethod; + VerifyOrReturn(mWrapperCallbackRef.HasValidObjectRef(), + ChipLogError(Controller, "mWrapperCallbackRef is not valid in %s", __func__)); + jobject wrapperCallback = mWrapperCallbackRef.ObjectRef(); + JniGlobalReference globalRef(std::move(mWrapperCallbackRef)); + + err = JniReferences::GetInstance().FindMethod(env, wrapperCallback, "onDone", "()V", &onDoneMethod); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onDone method")); + + DeviceLayer::StackUnlock unlock; + env->CallVoidMethod(wrapperCallback, onDoneMethod); + VerifyOrReturn(!env->ExceptionCheck(), env->ExceptionDescribe()); +} + jlong newConnectedDeviceCallback(JNIEnv * env, jobject self, jobject callback) { chip::DeviceLayer::StackLock lock; @@ -1085,5 +1249,19 @@ void deleteInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle) chip::Platform::Delete(invokeCallback); } +jlong newExtendableInvokeCallback(JNIEnv * env, jobject self) +{ + chip::DeviceLayer::StackLock lock; + ExtendableInvokeCallback * invokeCallback = chip::Platform::New(self); + return reinterpret_cast(invokeCallback); +} + +void deleteExtendableInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle) +{ + chip::DeviceLayer::StackLock lock; + ExtendableInvokeCallback * invokeCallback = reinterpret_cast(callbackHandle); + VerifyOrReturn(invokeCallback != nullptr, ChipLogError(Controller, "ExtendableInvokeCallback handle is nullptr")); + chip::Platform::Delete(invokeCallback); +} } // namespace Controller } // namespace chip diff --git a/src/controller/java/AndroidCallbacks.h b/src/controller/java/AndroidCallbacks.h index 700a1fb467f13f..0c49daf1b21a84 100644 --- a/src/controller/java/AndroidCallbacks.h +++ b/src/controller/java/AndroidCallbacks.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace chip { @@ -138,6 +139,20 @@ struct InvokeCallback : public app::CommandSender::Callback JniGlobalReference mWrapperCallbackRef; }; +struct ExtendableInvokeCallback : public app::CommandSender::ExtendableCallback +{ + ExtendableInvokeCallback(jobject wrapperCallback); + ~ExtendableInvokeCallback(); + + void OnResponse(app::CommandSender * commandSender, const app::CommandSender::ResponseData & aResponseData) override; + void OnNoResponse(app::CommandSender * commandSender, const app::CommandSender::NoResponseData & aNoResponseData) override; + void OnError(const app::CommandSender * apCommandSender, const app::CommandSender::ErrorData & aErrorData) override; + void OnDone(app::CommandSender * apCommandSender) override; + + app::CommandSender * mCommandSender = nullptr; + JniGlobalReference mWrapperCallbackRef; +}; + jlong newConnectedDeviceCallback(JNIEnv * env, jobject self, jobject callback); void deleteConnectedDeviceCallback(JNIEnv * env, jobject self, jlong callbackHandle); jlong newReportCallback(JNIEnv * env, jobject self, jobject subscriptionEstablishedCallbackJava, @@ -147,6 +162,8 @@ jlong newWriteAttributesCallback(JNIEnv * env, jobject self); void deleteWriteAttributesCallback(JNIEnv * env, jobject self, jlong callbackHandle); jlong newInvokeCallback(JNIEnv * env, jobject self); void deleteInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle); +jlong newExtendableInvokeCallback(JNIEnv * env, jobject self); +void deleteExtendableInvokeCallback(JNIEnv * env, jobject self, jlong callbackHandle); } // namespace Controller } // namespace chip diff --git a/src/controller/java/AndroidInteractionClient.cpp b/src/controller/java/AndroidInteractionClient.cpp index a0b22bfde530af..ec809570bcb1c7 100644 --- a/src/controller/java/AndroidInteractionClient.cpp +++ b/src/controller/java/AndroidInteractionClient.cpp @@ -431,7 +431,7 @@ CHIP_ERROR write(JNIEnv * env, jlong handle, jlong callbackHandle, jlong deviceP CHIP_ERROR PutPreencodedInvokeRequest(app::CommandSender & commandSender, app::CommandPathParams & path, const ByteSpan & data) { - // PrepareCommand does nott create the struct container with kFields and copycontainer below sets the + // PrepareCommand does not create the struct container with kFields and copycontainer below sets the // kFields container already ReturnErrorOnFailure(commandSender.PrepareCommand(path, false /* aStartDataStruct */)); TLV::TLVWriter * writer = commandSender.GetCommandDataIBTLVWriter(); @@ -442,6 +442,191 @@ CHIP_ERROR PutPreencodedInvokeRequest(app::CommandSender & commandSender, app::C return writer->CopyContainer(TLV::ContextTag(app::CommandDataIB::Tag::kFields), reader); } +CHIP_ERROR PutPreencodedInvokeRequest(app::CommandSender & commandSender, app::CommandPathParams & path, const ByteSpan & data, + app::CommandSender::PrepareCommandParameters & prepareCommandParams) +{ + // PrepareCommand does not create the struct container with kFields and copycontainer below sets the + // kFields container already + ReturnErrorOnFailure(commandSender.PrepareCommand(path, prepareCommandParams)); + TLV::TLVWriter * writer = commandSender.GetCommandDataIBTLVWriter(); + VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE); + TLV::TLVReader reader; + reader.Init(data); + ReturnErrorOnFailure(reader.Next()); + return writer->CopyContainer(TLV::ContextTag(app::CommandDataIB::Tag::kFields), reader); +} + +CHIP_ERROR extendableInvoke(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList, + jint timedRequestTimeoutMs, jint imTimeoutMs) +{ + chip::DeviceLayer::StackLock lock; + CHIP_ERROR err = CHIP_NO_ERROR; + auto callback = reinterpret_cast(callbackHandle); + app::CommandSender * commandSender = nullptr; + uint16_t groupId = 0; + bool isEndpointIdValid = false; + bool isGroupIdValid = false; + jint listSize = 0; + uint16_t convertedTimedRequestTimeoutMs = static_cast(timedRequestTimeoutMs); + app::CommandSender::ConfigParameters config; + + ChipLogDetail(Controller, "IM extendableInvoke() called"); + + DeviceProxy * device = reinterpret_cast(devicePtr); + VerifyOrExit(device != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(device->GetSecureSession().HasValue(), err = CHIP_ERROR_MISSING_SECURE_SESSION); + + VerifyOrExit(invokeElementList != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + SuccessOrExit(err = JniReferences::GetInstance().GetListSize(invokeElementList, listSize)); + + if ((listSize > 1) && (device->GetSecureSession().Value()->IsGroupSession())) + { + ChipLogError(Controller, "Not allow group session for InvokeRequests that has more than 1 CommandDataIB)"); + err = CHIP_ERROR_INVALID_ARGUMENT; + goto exit; + } + + commandSender = Platform::New(callback, device->GetExchangeManager(), timedRequestTimeoutMs != 0); + config.SetRemoteMaxPathsPerInvoke(device->GetSecureSession().Value()->GetRemoteSessionParameters().GetMaxPathsPerInvoke()); + SuccessOrExit(err = commandSender->SetCommandSenderConfig(config)); + + for (uint8_t i = 0; i < listSize; i++) + { + jmethodID getEndpointIdMethod = nullptr; + jmethodID getClusterIdMethod = nullptr; + jmethodID getCommandIdMethod = nullptr; + jmethodID getGroupIdMethod = nullptr; + jmethodID getTlvByteArrayMethod = nullptr; + jmethodID getJsonStringMethod = nullptr; + jmethodID isEndpointIdValidMethod = nullptr; + jmethodID isGroupIdValidMethod = nullptr; + jlong endpointIdObj = 0; + jlong clusterIdObj = 0; + jlong commandIdObj = 0; + jobject groupIdObj = nullptr; + jbyteArray tlvBytesObj = nullptr; + jobject invokeElement = nullptr; + SuccessOrExit(err = JniReferences::GetInstance().GetListItem(invokeElementList, i, invokeElement)); + SuccessOrExit( + err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getEndpointId", "(J)J", &getEndpointIdMethod)); + SuccessOrExit(err = + JniReferences::GetInstance().FindMethod(env, invokeElement, "getClusterId", "(J)J", &getClusterIdMethod)); + SuccessOrExit(err = + JniReferences::GetInstance().FindMethod(env, invokeElement, "getCommandId", "(J)J", &getCommandIdMethod)); + SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getGroupId", "()Ljava/util/Optional;", + &getGroupIdMethod)); + SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "isEndpointIdValid", "()Z", + &isEndpointIdValidMethod)); + SuccessOrExit( + err = JniReferences::GetInstance().FindMethod(env, invokeElement, "isGroupIdValid", "()Z", &isGroupIdValidMethod)); + SuccessOrExit( + err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getTlvByteArray", "()[B", &getTlvByteArrayMethod)); + + isEndpointIdValid = (env->CallBooleanMethod(invokeElement, isEndpointIdValidMethod) == JNI_TRUE); + isGroupIdValid = (env->CallBooleanMethod(invokeElement, isGroupIdValidMethod) == JNI_TRUE); + + if (isEndpointIdValid) + { + endpointIdObj = env->CallLongMethod(invokeElement, getEndpointIdMethod, static_cast(kInvalidEndpointId)); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + } + + if (isGroupIdValid) + { + VerifyOrExit(device->GetSecureSession().Value()->IsGroupSession(), err = CHIP_ERROR_INVALID_ARGUMENT); + groupIdObj = env->CallObjectMethod(invokeElement, getGroupIdMethod); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + VerifyOrExit(groupIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + + jobject boxedGroupId = nullptr; + + SuccessOrExit(err = JniReferences::GetInstance().GetOptionalValue(groupIdObj, boxedGroupId)); + VerifyOrExit(boxedGroupId != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + groupId = static_cast(JniReferences::GetInstance().IntegerToPrimitive(boxedGroupId)); + } + + clusterIdObj = env->CallLongMethod(invokeElement, getClusterIdMethod, static_cast(kInvalidClusterId)); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + + commandIdObj = env->CallLongMethod(invokeElement, getCommandIdMethod, static_cast(kInvalidCommandId)); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + + tlvBytesObj = static_cast(env->CallObjectMethod(invokeElement, getTlvByteArrayMethod)); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + + app::CommandSender::PrepareCommandParameters prepareCommandParams; + prepareCommandParams.commandRef.SetValue(static_cast(i)); + + { + uint16_t id = isEndpointIdValid ? static_cast(endpointIdObj) : groupId; + app::CommandPathFlags flag = + isEndpointIdValid ? app::CommandPathFlags::kEndpointIdValid : app::CommandPathFlags::kGroupIdValid; + app::CommandPathParams path(id, static_cast(clusterIdObj), static_cast(commandIdObj), flag); + + if (tlvBytesObj != nullptr) + { + JniByteArray tlvBytesObjBytes(env, tlvBytesObj); + SuccessOrExit( + err = PutPreencodedInvokeRequest(*commandSender, path, tlvBytesObjBytes.byteSpan(), prepareCommandParams)); + } + else + { + SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getJsonString", + "()Ljava/lang/String;", &getJsonStringMethod)); + jstring jsonJniString = static_cast(env->CallObjectMethod(invokeElement, getJsonStringMethod)); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + VerifyOrExit(jsonJniString != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + JniUtfString jsonUtfJniString(env, jsonJniString); + // The invoke does not support chunk, kMaxSecureSduLengthBytes should be enough for command json blob + uint8_t tlvBytes[chip::app::kMaxSecureSduLengthBytes] = { 0 }; + MutableByteSpan tlvEncodingLocal{ tlvBytes }; + SuccessOrExit(err = JsonToTlv(std::string(jsonUtfJniString.c_str(), static_cast(jsonUtfJniString.size())), + tlvEncodingLocal)); + SuccessOrExit(err = PutPreencodedInvokeRequest(*commandSender, path, tlvEncodingLocal, prepareCommandParams)); + } + } + + app::CommandSender::FinishCommandParameters finishCommandParams(convertedTimedRequestTimeoutMs != 0 + ? Optional(convertedTimedRequestTimeoutMs) + : Optional::Missing()); + + finishCommandParams.commandRef = prepareCommandParams.commandRef; + SuccessOrExit(err = commandSender->FinishCommand(finishCommandParams)); + } + SuccessOrExit(err = device->GetSecureSession().Value()->IsGroupSession() + ? commandSender->SendGroupCommandRequest(device->GetSecureSession().Value()) + : commandSender->SendCommandRequest(device->GetSecureSession().Value(), + imTimeoutMs != 0 + ? MakeOptional(System::Clock::Milliseconds32(imTimeoutMs)) + : Optional::Missing())); + + callback->mCommandSender = commandSender; +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "JNI IM Invoke Error: %s", err.AsString()); + if (err == CHIP_JNI_ERROR_EXCEPTION_THROWN) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + app::CommandSender::ErrorData errorData; + errorData.error = err; + callback->OnError(nullptr, errorData); + if (commandSender != nullptr) + { + Platform::Delete(commandSender); + commandSender = nullptr; + } + if (callback != nullptr) + { + Platform::Delete(callback); + callback = nullptr; + } + } + return err; +} + CHIP_ERROR invoke(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElement, jint timedRequestTimeoutMs, jint imTimeoutMs) { diff --git a/src/controller/java/AndroidInteractionClient.h b/src/controller/java/AndroidInteractionClient.h index 095061f0c98172..f38af6f4eb2a96 100644 --- a/src/controller/java/AndroidInteractionClient.h +++ b/src/controller/java/AndroidInteractionClient.h @@ -29,3 +29,5 @@ CHIP_ERROR write(JNIEnv * env, jlong handle, jlong callbackHandle, jlong deviceP jint timedRequestTimeoutMs, jint imTimeoutMs); CHIP_ERROR invoke(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElement, jint timedRequestTimeoutMs, jint imTimeoutMs); +CHIP_ERROR extendableInvoke(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList, + jint timedRequestTimeoutMs, jint imTimeoutMs); diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn index 31dbaafc9fb21f..a2f3c866bdaa76 100644 --- a/src/controller/java/BUILD.gn +++ b/src/controller/java/BUILD.gn @@ -459,6 +459,8 @@ android_library("java") { "src/chip/devicecontroller/ControllerParams.java", "src/chip/devicecontroller/DeviceAttestationDelegate.java", "src/chip/devicecontroller/DiscoveredDevice.java", + "src/chip/devicecontroller/ExtendableInvokeCallback.java", + "src/chip/devicecontroller/ExtendableInvokeCallbackJni.java", "src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java", "src/chip/devicecontroller/GroupKeySecurityPolicy.java", "src/chip/devicecontroller/ICDClientInfo.java", @@ -493,6 +495,8 @@ android_library("java") { "src/chip/devicecontroller/model/EndpointState.java", "src/chip/devicecontroller/model/EventState.java", "src/chip/devicecontroller/model/InvokeElement.java", + "src/chip/devicecontroller/model/InvokeResponseData.java", + "src/chip/devicecontroller/model/NoInvokeResponseData.java", "src/chip/devicecontroller/model/NodeState.java", "src/chip/devicecontroller/model/Status.java", ] diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index 206103ca82a311..fc2c0694b29849 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -2285,6 +2285,18 @@ JNI_METHOD(void, invoke) } } +JNI_METHOD(void, extendableInvoke) +(JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList, + jint timedRequestTimeoutMs, jint imTimeoutMs) +{ + CHIP_ERROR err = + extendableInvoke(env, handle, callbackHandle, devicePtr, invokeElementList, timedRequestTimeoutMs, imTimeoutMs); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "JNI IM Batch Invoke Error: %" CHIP_ERROR_FORMAT, err.Format()); + } +} + void * IOThreadMain(void * arg) { JNIEnv * env; diff --git a/src/controller/java/MatterCallbacks-JNI.cpp b/src/controller/java/MatterCallbacks-JNI.cpp index f20d9e47f093ff..93101df18066c4 100644 --- a/src/controller/java/MatterCallbacks-JNI.cpp +++ b/src/controller/java/MatterCallbacks-JNI.cpp @@ -69,3 +69,14 @@ JNI_METHOD(void, InvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self, { deleteInvokeCallback(env, self, callbackHandle); } + +JNI_METHOD(jlong, ExtendableInvokeCallbackJni, newCallback) +(JNIEnv * env, jobject self) +{ + return newExtendableInvokeCallback(env, self); +} + +JNI_METHOD(void, ExtendableInvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self, jlong callbackHandle) +{ + deleteExtendableInvokeCallback(env, self, callbackHandle); +} diff --git a/src/controller/java/MatterInteractionClient-JNI.cpp b/src/controller/java/MatterInteractionClient-JNI.cpp index 6a541723d041ed..152e52aedd65d7 100644 --- a/src/controller/java/MatterInteractionClient-JNI.cpp +++ b/src/controller/java/MatterInteractionClient-JNI.cpp @@ -65,3 +65,15 @@ JNI_METHOD(void, invoke) ChipLogError(Controller, "JNI IM Invoke Error: %" CHIP_ERROR_FORMAT, err.Format()); } } + +JNI_METHOD(void, extendableInvoke) +(JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList, + jint timedRequestTimeoutMs, jint imTimeoutMs) +{ + CHIP_ERROR err = + extendableInvoke(env, handle, callbackHandle, devicePtr, invokeElementList, timedRequestTimeoutMs, imTimeoutMs); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "JNI IM Batch Invoke Error: %" CHIP_ERROR_FORMAT, err.Format()); + } +} diff --git a/src/controller/java/src/chip/devicecontroller/BatchInvokeCallback.java b/src/controller/java/src/chip/devicecontroller/BatchInvokeCallback.java new file mode 100644 index 00000000000000..1b07e38de0615e --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/BatchInvokeCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller; + +import chip.devicecontroller.model.InvokeResponseData; +import chip.devicecontroller.model.NoInvokeResponseData; + +/** An interface for receiving invoke response. */ +public interface ExtendableInvokeCallback { + + /** + * OnError will be called when an error occurs after failing to call + * + * @param Exception The IllegalStateException which encapsulated the error message, the possible + * chip error could be - CHIP_ERROR_TIMEOUT: A response was not received within the expected + * response timeout. - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from + * the server. - CHIP_ERROR encapsulating the converted error from the StatusIB: If we got a + * non-path-specific status response from the server. - CHIP_ERROR*: All other cases. + */ + void onError(Exception e); + + /** + * OnResponse will be called when a write response has been received and processed for the given + * path. + * + * @param invokeResponseData invoke response that has either payload or status + */ + void onResponse(InvokeResponseData invokeResponseData); + + /** + * onNoResponse will be called for each request that failed to receive a response after the server + * indicates completion of all requests. + * + * @param noInvokeResponseData failed response data + */ + void onNoResponse(NoInvokeResponseData noInvokeResponseData); + + void onDone(); +} diff --git a/src/controller/java/src/chip/devicecontroller/BatchInvokeCallbackJni.java b/src/controller/java/src/chip/devicecontroller/BatchInvokeCallbackJni.java new file mode 100644 index 00000000000000..6982f58429a353 --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/BatchInvokeCallbackJni.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller; + +import chip.devicecontroller.model.InvokeResponseData; +import chip.devicecontroller.model.NoInvokeResponseData; +import java.util.Optional; +import javax.annotation.Nullable; + +/** JNI wrapper callback class for {@link InvokeCallback}. */ +public final class ExtendableInvokeCallbackJni { + private final ExtendableInvokeCallback wrappedExtendableInvokeCallback; + private long callbackHandle; + + public ExtendableInvokeCallbackJni(ExtendableInvokeCallback wrappedExtendableInvokeCallback) { + this.wrappedExtendableInvokeCallback = wrappedExtendableInvokeCallback; + this.callbackHandle = newCallback(); + } + + long getCallbackHandle() { + return callbackHandle; + } + + private native long newCallback(); + + private native void deleteCallback(long callbackHandle); + + private void onError(Exception e) { + wrappedExtendableInvokeCallback.onError(e); + } + + private void onResponse( + int endpointId, + long clusterId, + long commandId, + @Nullable Integer commandRef, + byte[] tlv, + String jsonString) { + wrappedExtendableInvokeCallback.onResponse( + InvokeResponseData.newInstance( + endpointId, clusterId, commandId, Optional.ofNullable(commandRef), tlv, jsonString)); + } + + private void onResponse( + int endpointId, + long clusterId, + long commandId, + @Nullable Integer commandRef, + int status, + @Nullable Integer clusterStatus) { + wrappedExtendableInvokeCallback.onResponse( + InvokeResponseData.newInstance( + endpointId, + clusterId, + commandId, + Optional.ofNullable(commandRef), + status, + Optional.ofNullable(clusterStatus))); + } + + private void onNoResponse(int commandRef) { + wrappedExtendableInvokeCallback.onNoResponse(NoInvokeResponseData.newInstance(commandRef)); + } + + private void onDone() { + wrappedExtendableInvokeCallback.onDone(); + } + + // TODO(#8578): Replace finalizer with PhantomReference. + @SuppressWarnings("deprecation") + protected void finalize() throws Throwable { + super.finalize(); + + if (callbackHandle != 0) { + deleteCallback(callbackHandle); + callbackHandle = 0; + } + } +} diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java index 9e5c6e12f42520..25d300ec1cadc1 100644 --- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java +++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java @@ -1205,6 +1205,32 @@ public void invoke( imTimeoutMs); } + /** + * @brief ExtendableInvoke command to target device + * @param ExtendableInvokeCallback Callback when invoke responses have been received and processed + * for the given batched invoke commands. + * @param devicePtr connected device pointer + * @param invokeElementList invoke element list + * @param timedRequestTimeoutMs this is timed request if this value is larger than 0 + * @param imTimeoutMs im interaction time out value, it would override the default value in c++ im + * layer if this value is non-zero. + */ + public void extendableInvoke( + ExtendableInvokeCallback callback, + long devicePtr, + List invokeElementList, + int timedRequestTimeoutMs, + int imTimeoutMs) { + ExtendableInvokeCallbackJni jniCallback = new ExtendableInvokeCallbackJni(callback); + extendableInvoke( + deviceControllerPtr, + jniCallback.getCallbackHandle(), + devicePtr, + invokeElementList, + timedRequestTimeoutMs, + imTimeoutMs); + } + /** Create a root (self-signed) X.509 DER encoded certificate */ public static byte[] createRootCertificate( KeypairDelegate keypair, long issuerId, @Nullable Long fabricId) { @@ -1377,6 +1403,14 @@ static native void invoke( int timedRequestTimeoutMs, int imTimeoutMs); + static native void extendableInvoke( + long deviceControllerPtr, + long callbackHandle, + long devicePtr, + List invokeElementList, + int timedRequestTimeoutMs, + int imTimeoutMs); + private native long newDeviceController(ControllerParams params); private native void setDeviceAttestationDelegate( diff --git a/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallback.java b/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallback.java new file mode 100644 index 00000000000000..1b07e38de0615e --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller; + +import chip.devicecontroller.model.InvokeResponseData; +import chip.devicecontroller.model.NoInvokeResponseData; + +/** An interface for receiving invoke response. */ +public interface ExtendableInvokeCallback { + + /** + * OnError will be called when an error occurs after failing to call + * + * @param Exception The IllegalStateException which encapsulated the error message, the possible + * chip error could be - CHIP_ERROR_TIMEOUT: A response was not received within the expected + * response timeout. - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from + * the server. - CHIP_ERROR encapsulating the converted error from the StatusIB: If we got a + * non-path-specific status response from the server. - CHIP_ERROR*: All other cases. + */ + void onError(Exception e); + + /** + * OnResponse will be called when a write response has been received and processed for the given + * path. + * + * @param invokeResponseData invoke response that has either payload or status + */ + void onResponse(InvokeResponseData invokeResponseData); + + /** + * onNoResponse will be called for each request that failed to receive a response after the server + * indicates completion of all requests. + * + * @param noInvokeResponseData failed response data + */ + void onNoResponse(NoInvokeResponseData noInvokeResponseData); + + void onDone(); +} diff --git a/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallbackJni.java b/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallbackJni.java new file mode 100644 index 00000000000000..6982f58429a353 --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/ExtendableInvokeCallbackJni.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller; + +import chip.devicecontroller.model.InvokeResponseData; +import chip.devicecontroller.model.NoInvokeResponseData; +import java.util.Optional; +import javax.annotation.Nullable; + +/** JNI wrapper callback class for {@link InvokeCallback}. */ +public final class ExtendableInvokeCallbackJni { + private final ExtendableInvokeCallback wrappedExtendableInvokeCallback; + private long callbackHandle; + + public ExtendableInvokeCallbackJni(ExtendableInvokeCallback wrappedExtendableInvokeCallback) { + this.wrappedExtendableInvokeCallback = wrappedExtendableInvokeCallback; + this.callbackHandle = newCallback(); + } + + long getCallbackHandle() { + return callbackHandle; + } + + private native long newCallback(); + + private native void deleteCallback(long callbackHandle); + + private void onError(Exception e) { + wrappedExtendableInvokeCallback.onError(e); + } + + private void onResponse( + int endpointId, + long clusterId, + long commandId, + @Nullable Integer commandRef, + byte[] tlv, + String jsonString) { + wrappedExtendableInvokeCallback.onResponse( + InvokeResponseData.newInstance( + endpointId, clusterId, commandId, Optional.ofNullable(commandRef), tlv, jsonString)); + } + + private void onResponse( + int endpointId, + long clusterId, + long commandId, + @Nullable Integer commandRef, + int status, + @Nullable Integer clusterStatus) { + wrappedExtendableInvokeCallback.onResponse( + InvokeResponseData.newInstance( + endpointId, + clusterId, + commandId, + Optional.ofNullable(commandRef), + status, + Optional.ofNullable(clusterStatus))); + } + + private void onNoResponse(int commandRef) { + wrappedExtendableInvokeCallback.onNoResponse(NoInvokeResponseData.newInstance(commandRef)); + } + + private void onDone() { + wrappedExtendableInvokeCallback.onDone(); + } + + // TODO(#8578): Replace finalizer with PhantomReference. + @SuppressWarnings("deprecation") + protected void finalize() throws Throwable { + super.finalize(); + + if (callbackHandle != 0) { + deleteCallback(callbackHandle); + callbackHandle = 0; + } + } +} diff --git a/src/controller/java/src/chip/devicecontroller/model/InvokeResponseData.java b/src/controller/java/src/chip/devicecontroller/model/InvokeResponseData.java new file mode 100644 index 00000000000000..263051580f09e5 --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/model/InvokeResponseData.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller.model; + +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import org.json.JSONException; +import org.json.JSONObject; + +/** Class for tracking invoke response data with either data or status */ +public final class InvokeResponseData { + private static final Logger logger = Logger.getLogger(InvokeResponseData.class.getName()); + @Nullable private final ChipPathId endpointId; + private final ChipPathId clusterId, commandId; + private final Optional commandRef; + @Nullable private final byte[] tlv; + @Nullable private final JSONObject json; + @Nullable private final Status status; + + private InvokeResponseData( + ChipPathId endpointId, + ChipPathId clusterId, + ChipPathId commandId, + Optional commandRef, + @Nullable byte[] tlv, + @Nullable String jsonString) { + this.endpointId = endpointId; + this.clusterId = clusterId; + this.commandId = commandId; + this.commandRef = commandRef; + + if (tlv != null) { + this.tlv = tlv.clone(); + } else { + this.tlv = null; + } + + JSONObject jsonObject = null; + if (jsonString != null) { + try { + jsonObject = new JSONObject(jsonString); + } catch (JSONException ex) { + logger.log(Level.SEVERE, "Error parsing JSON string", ex); + } + } + + this.json = jsonObject; + this.status = null; + } + + private InvokeResponseData( + ChipPathId endpointId, + ChipPathId clusterId, + ChipPathId commandId, + Optional commandRef, + int status, + Optional clusterStatus) { + this.endpointId = endpointId; + this.clusterId = clusterId; + this.commandId = commandId; + this.commandRef = commandRef; + this.status = Status.newInstance(status, clusterStatus); + this.tlv = null; + this.json = null; + } + + public ChipPathId getEndpointId() { + return endpointId; + } + + public ChipPathId getClusterId() { + return clusterId; + } + + public ChipPathId getCommandId() { + return commandId; + } + + public Optional getCommandRef() { + return commandRef; + } + + @Nullable + public Status getStatus() { + return status; + } + + // For use in JNI. + private long getEndpointId(long wildcardValue) { + return endpointId.getId(wildcardValue); + } + + private long getClusterId(long wildcardValue) { + return clusterId.getId(wildcardValue); + } + + private long getCommandId(long wildcardValue) { + return commandId.getId(wildcardValue); + } + + public boolean isEndpointIdValid() { + return endpointId != null; + } + + @Nullable + public byte[] getTlvByteArray() { + if (tlv != null) { + return tlv.clone(); + } + return null; + } + + @Nullable + public JSONObject getJsonObject() { + return json; + } + + @Nullable + public String getJsonString() { + if (json == null) return null; + return json.toString(); + } + + // check whether the current InvokeResponseData has same path as others. + @Override + public boolean equals(Object object) { + if (object instanceof InvokeResponseData) { + InvokeResponseData that = (InvokeResponseData) object; + return Objects.equals(this.endpointId, that.endpointId) + && Objects.equals(this.clusterId, that.clusterId) + && Objects.equals(this.commandId, that.commandId); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(endpointId, clusterId, commandId); + } + + @Override + public String toString() { + return String.format( + Locale.ENGLISH, + "Endpoint %s, cluster %s, command %s, payload: %s, status: %s", + endpointId, + clusterId, + commandId, + json == null ? "null" : getJsonString(), + status == null ? "null" : status.toString()); + } + + public static InvokeResponseData newInstance( + ChipPathId endpointId, + ChipPathId clusterId, + ChipPathId commandId, + Optional commandRef, + @Nullable byte[] tlv, + @Nullable String jsonString) { + return new InvokeResponseData(endpointId, clusterId, commandId, commandRef, tlv, jsonString); + } + + public static InvokeResponseData newInstance( + int endpointId, + long clusterId, + long commandId, + Optional commandRef, + @Nullable byte[] tlv, + @Nullable String jsonString) { + return new InvokeResponseData( + ChipPathId.forId(endpointId), + ChipPathId.forId(clusterId), + ChipPathId.forId(commandId), + commandRef, + tlv, + jsonString); + } + + public static InvokeResponseData newInstance( + ChipPathId endpointId, + ChipPathId clusterId, + ChipPathId commandId, + Optional commandRef, + int status, + Optional clusterStatus) { + return new InvokeResponseData( + endpointId, clusterId, commandId, commandRef, status, clusterStatus); + } + + public static InvokeResponseData newInstance( + int endpointId, + long clusterId, + long commandId, + Optional commandRef, + int status, + Optional clusterStatus) { + return new InvokeResponseData( + ChipPathId.forId(endpointId), + ChipPathId.forId(clusterId), + ChipPathId.forId(commandId), + commandRef, + status, + clusterStatus); + } +} diff --git a/src/controller/java/src/chip/devicecontroller/model/NoInvokeResponseData.java b/src/controller/java/src/chip/devicecontroller/model/NoInvokeResponseData.java new file mode 100644 index 00000000000000..03e930c62a978f --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/model/NoInvokeResponseData.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 chip.devicecontroller.model; + +import java.util.logging.Logger; + +/** Class for tracking failed invoke response data. */ +public final class NoInvokeResponseData { + private static final Logger logger = Logger.getLogger(NoInvokeResponseData.class.getName()); + private final Integer commandRef; + + private NoInvokeResponseData(int commandRef) { + this.commandRef = commandRef; + } + + public Integer getCommandRef() { + return commandRef; + } + + public static NoInvokeResponseData newInstance(int commandRef) { + return new NoInvokeResponseData(commandRef); + } +} diff --git a/src/controller/java/src/chip/devicecontroller/model/Status.java b/src/controller/java/src/chip/devicecontroller/model/Status.java index d859fae9f189e0..28abd36465689b 100644 --- a/src/controller/java/src/chip/devicecontroller/model/Status.java +++ b/src/controller/java/src/chip/devicecontroller/model/Status.java @@ -124,4 +124,8 @@ public static Status newInstance(int status) { public static Status newInstance(int status, Integer clusterStatus) { return new Status(status, Optional.ofNullable(clusterStatus)); } + + public static Status newInstance(int status, Optional clusterStatus) { + return new Status(status, clusterStatus); + } } diff --git a/src/lib/core/core.gni b/src/lib/core/core.gni index 8c7032ebac675a..02f98e3557f948 100644 --- a/src/lib/core/core.gni +++ b/src/lib/core/core.gni @@ -100,7 +100,8 @@ declare_args() { chip_tlv_validate_char_string_on_read = false chip_enable_sending_batch_commands = - current_os == "linux" || current_os == "mac" || current_os == "ios" + current_os == "linux" || current_os == "mac" || current_os == "ios" || + current_os == "android" } if (chip_target_style == "") { From 6f621f2fc1041e4c0f8cac03fde8a4d989907f4d Mon Sep 17 00:00:00 2001 From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:30:02 +1300 Subject: [PATCH 38/52] CI: Free up space on the github runner by deleting some tools we don't use (#32350) Currently this deletes .NET tooling and parts of the Android SDK. Note that our Android CI builds bring their own Android SDK via the chip-build-android container, and do not rely on the copy that is installed in the GitHub Ubuntu image. --- .../action.yaml | 2 + .../actions/maximize-runner-disk/action.yaml | 50 +++++++++++++++++++ .github/workflows/build.yaml | 5 ++ .github/workflows/smoketest-android.yaml | 1 + .github/workflows/unit_integration_test.yaml | 1 + 5 files changed, 59 insertions(+) create mode 100644 .github/actions/maximize-runner-disk/action.yaml diff --git a/.github/actions/checkout-submodules-and-bootstrap/action.yaml b/.github/actions/checkout-submodules-and-bootstrap/action.yaml index 8ddaec14e7bc47..b514b8dd795509 100644 --- a/.github/actions/checkout-submodules-and-bootstrap/action.yaml +++ b/.github/actions/checkout-submodules-and-bootstrap/action.yaml @@ -15,6 +15,8 @@ inputs: runs: using: "composite" steps: + - name: Maximize runner disk + uses: ./.github/actions/maximize-runner-disk - name: Dump disk info uses: ./.github/actions/dump-disk-info - name: Set git safe directory for local act runs diff --git a/.github/actions/maximize-runner-disk/action.yaml b/.github/actions/maximize-runner-disk/action.yaml new file mode 100644 index 00000000000000..fe5f95352aa53e --- /dev/null +++ b/.github/actions/maximize-runner-disk/action.yaml @@ -0,0 +1,50 @@ +name: Maximize runner disk +description: Free up disk space on the github runner +runs: + using: "composite" + steps: + - name: Free up disk space on the github runner + if: ${{ !env.ACT }} + shell: bash + run: | + # maximize-runner-disk + if [[ "$RUNNER_OS" == Linux ]]; then + # Directories to prune to free up space. Candidates: + # 1.6G /usr/share/dotnet + # 1.1G /usr/local/lib/android/sdk/platforms + # 1000M /usr/local/lib/android/sdk/build-tools + # 8.9G /usr/local/lib/android/sdk + # This list can be amended later to change the trade-off between the amount of + # disk space freed up, and how long it takes to do so (deleting many files is slow). + prune=(/usr/share/dotnet /usr/local/lib/android/sdk/platforms /usr/local/lib/android/sdk/build-tools) + + if [[ "$UID" -eq 0 && -d /__w ]]; then + root=/runner-root-volume + if [[ ! -d "$root" ]]; then + echo "Unable to maximize disk space, job is running inside a container and $root is not mounted" + exit 0 + fi + function sudo() { "$@"; } # we're already root (and sudo is probably unavailable) + elif [[ "$UID" -ne 0 && "$RUNNER_ENVIRONMENT" == github-hosted ]]; then + root= + else + echo "Unable to maximize disk space, unknown runner environment" + exit 0 + fi + + echo "Freeing up runner disk space on ${root:-/}" + function avail() { df -k --output=avail "${root:-/}" | grep '^[0-9]*$'; } + function now() { date '+%s'; } + before="$(avail)" start="$(now)" + for dir in "${prune[@]}"; do + if [[ -d "${root}${dir}" ]]; then + echo "- $dir" + # du -sh -- "${root}${dir}" + sudo rm -rf -- "${root}${dir}" + else + echo "- $dir (not found)" + fi + done + after="$(avail)" end="$(now)" + echo "Done, freed up $(( (after - before) / 1024 ))M of disk space in $(( end - start )) seconds." + fi diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 23dd3d3eb0f309..effec464b45aee 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,6 +42,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -137,6 +138,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -285,6 +287,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -345,6 +348,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" @@ -453,6 +457,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" diff --git a/.github/workflows/smoketest-android.yaml b/.github/workflows/smoketest-android.yaml index 396fb3ae153a46..64efaeb82ff20f 100644 --- a/.github/workflows/smoketest-android.yaml +++ b/.github/workflows/smoketest-android.yaml @@ -39,6 +39,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build-android:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" steps: diff --git a/.github/workflows/unit_integration_test.yaml b/.github/workflows/unit_integration_test.yaml index 62231116bb92c1..15199d4d6532e3 100644 --- a/.github/workflows/unit_integration_test.yaml +++ b/.github/workflows/unit_integration_test.yaml @@ -39,6 +39,7 @@ jobs: container: image: ghcr.io/project-chip/chip-build:35 volumes: + - "/:/runner-root-volume" - "/tmp/log_output:/tmp/test_logs" options: --privileged --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" From 355e2b478676271f72f7c1071865f16114f4a371 Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:10:30 +0900 Subject: [PATCH 39/52] [Android] Implement multiple write invoke UI (#32352) * Implement multiple write,invoke UI * fix reset btn --- .../clusterclient/WildcardFragment.kt | 240 +++++++++++------- .../app/src/main/res/layout/invoke_dialog.xml | 28 +- .../src/main/res/layout/wildcard_fragment.xml | 104 +++++++- .../app/src/main/res/layout/write_dialog.xml | 55 +--- .../app/src/main/res/values/strings.xml | 14 +- 5 files changed, 264 insertions(+), 177 deletions(-) diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt index 5b49a6d9ab05ab..2ffbec50c746a2 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt @@ -16,7 +16,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.ChipIdLookup -import chip.devicecontroller.InvokeCallback +import chip.devicecontroller.ExtendableInvokeCallback import chip.devicecontroller.ReportCallback import chip.devicecontroller.ResubscriptionAttemptCallback import chip.devicecontroller.SubscriptionEstablishedCallback @@ -26,6 +26,8 @@ import chip.devicecontroller.model.ChipAttributePath import chip.devicecontroller.model.ChipEventPath import chip.devicecontroller.model.ChipPathId import chip.devicecontroller.model.InvokeElement +import chip.devicecontroller.model.InvokeResponseData +import chip.devicecontroller.model.NoInvokeResponseData import chip.devicecontroller.model.NodeState import chip.devicecontroller.model.Status import com.google.chip.chiptool.ChipClient @@ -57,6 +59,8 @@ class WildcardFragment : Fragment() { private val attributePath = ArrayList() private val eventPath = ArrayList() + private val writePath = ArrayList() + private val invokePath = ArrayList() private val subscribeIdList = ArrayList() private val reportCallback = @@ -89,29 +93,47 @@ class WildcardFragment : Fragment() { private val writeAttributeCallback = object : WriteAttributesCallback { + var viewText = "" + override fun onError(attributePath: ChipAttributePath?, ex: Exception?) { Log.e(TAG, "Report error for $attributePath: $ex") + viewText += "onError : $attributePath - $ex\n" } override fun onResponse(attributePath: ChipAttributePath, status: Status) { - val text = "$attributePath : Write response: $status" - requireActivity().runOnUiThread { binding.outputTv.text = text } + viewText += "$attributePath : Write response: $status\n" } override fun onDone() { - Log.i(TAG, "write attribute Done") + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } - private val invokeCallback = - object : InvokeCallback { + private val extendableInvokeCallback = + object : ExtendableInvokeCallback { + var viewText = "" + override fun onError(e: java.lang.Exception?) { + viewText += "Invoke onError : $e\n" Log.e(TAG, "Report error", e) } - override fun onResponse(invokeElement: InvokeElement?, successCode: Long) { - val text = "Invoke Response : $invokeElement, $successCode" - requireActivity().runOnUiThread { binding.outputTv.text = text } + override fun onResponse(invokeResponseData: InvokeResponseData?) { + viewText += "Invoke Response : $invokeResponseData\n" + } + + override fun onNoResponse(noInvokeResponseData: NoInvokeResponseData?) { + viewText += "Invoke onNoResponse : $noInvokeResponseData\n" + } + + override fun onDone() { + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } @@ -123,41 +145,27 @@ class WildcardFragment : Fragment() { _binding = WildcardFragmentBinding.inflate(inflater, container, false) scope = viewLifecycleOwner.lifecycleScope - binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, i -> - val readBtnOn = (i == R.id.readRadioBtn) - val subscribeBtnOn = (i == R.id.subscribeRadioBtn) - val writeBtnOn = (i == R.id.writeRadioBtn) - val invokeBtnOn = (i == R.id.invokeRadioBtn) - - binding.addLayout.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) - binding.commandIdEd.visibility = getVisibility(invokeBtnOn) - binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) - binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) - binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) - } + val writeTypeSpinnerAdapter = + ArrayAdapter( + requireActivity(), + android.R.layout.simple_spinner_dropdown_item, + TLV_MAP.keys.toList() + ) + binding.writeValueTypeSp.adapter = writeTypeSpinnerAdapter - binding.sendBtn.setOnClickListener { - if (binding.readRadioBtn.isChecked) { - showReadDialog() - } else if (binding.subscribeRadioBtn.isChecked) { - showSubscribeDialog() - } else if (binding.writeRadioBtn.isChecked) { - showWriteDialog() - } else if (binding.invokeRadioBtn.isChecked) { - showInvokeDialog() - } + binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, radioBtnId -> + setVisibilityEachView(radioBtnId) } + binding.sendBtn.setOnClickListener { showDialog() } + binding.shutdownSubscriptionBtn.setOnClickListener { showShutdownSubscriptionDialog() } - binding.addAttributeBtn.setOnClickListener { addPathList(ATTRIBUTE) } - binding.addEventBtn.setOnClickListener { addPathList(EVENT) } + binding.addAttributeBtn.setOnClickListener { addPathList(SendType.ATTRIBUTE) } + binding.addEventBtn.setOnClickListener { addPathList(SendType.EVENT) } + binding.addListBtn.setOnClickListener { addRequest() } binding.resetBtn.setOnClickListener { resetPath() } + binding.writeInvokeresetBtn.setOnClickListener { resetPath() } addressUpdateFragment = childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment @@ -165,6 +173,58 @@ class WildcardFragment : Fragment() { return binding.root } + private fun setVisibilityEachView(radioBtnId: Int) { + val readBtnOn = (radioBtnId == R.id.readRadioBtn) + val subscribeBtnOn = (radioBtnId == R.id.subscribeRadioBtn) + val writeBtnOn = (radioBtnId == R.id.writeRadioBtn) + val invokeBtnOn = (radioBtnId == R.id.invokeRadioBtn) + + binding.addAttributeBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addEventBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.resetBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addListBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) + binding.commandIdEd.visibility = getVisibility(invokeBtnOn) + binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) + binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) + binding.writeValueLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueEd.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeSp.visibility = getVisibility(writeBtnOn) + binding.dataVersionLabel.visibility = getVisibility(writeBtnOn) + binding.dataVersionEd.visibility = getVisibility(writeBtnOn) + binding.invokeValueLabel.visibility = getVisibility(invokeBtnOn) + binding.invokeValueEd.visibility = getVisibility(invokeBtnOn) + binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) + binding.writeInvokeresetBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + + resetPath() + } + + private fun showDialog() { + if (binding.readRadioBtn.isChecked) { + showReadDialog() + } else if (binding.subscribeRadioBtn.isChecked) { + showSubscribeDialog() + } else if (binding.writeRadioBtn.isChecked) { + showWriteDialog() + } else if (binding.invokeRadioBtn.isChecked) { + showInvokeDialog() + } + } + + private fun addRequest() { + if (binding.writeRadioBtn.isChecked) { + addWriteRequest() + } else { + addInvokeRequest() + } + } + private fun getVisibility(isShowing: Boolean): Int { return if (isShowing) { View.VISIBLE @@ -173,7 +233,7 @@ class WildcardFragment : Fragment() { } } - private fun addPathList(type: Int) { + private fun addPathList(type: SendType) { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) @@ -183,9 +243,9 @@ class WildcardFragment : Fragment() { (binding.subscribeRadioBtn.isChecked) && (binding.isUrgentSp.selectedItem.toString().toBoolean()) - if (type == ATTRIBUTE) { + if (type == SendType.ATTRIBUTE) { attributePath.add(ChipAttributePath.newInstance(endpointId, clusterId, attributeId)) - } else if (type == EVENT) { + } else if (type == SendType.EVENT) { eventPath.add(ChipEventPath.newInstance(endpointId, clusterId, eventId, isUrgent)) } updateAddListView() @@ -194,6 +254,8 @@ class WildcardFragment : Fragment() { private fun resetPath() { attributePath.clear() eventPath.clear() + writePath.clear() + invokePath.clear() updateAddListView() } @@ -205,6 +267,12 @@ class WildcardFragment : Fragment() { for (event in eventPath) { builder.append("event($event)\n") } + for (write in writePath) { + builder.append("WritePath($write)\n") + } + for (invoke in invokePath) { + builder.append("InvokePath($invoke)\n") + } binding.sendListView.text = builder.toString() } @@ -312,13 +380,11 @@ class WildcardFragment : Fragment() { ) } - private suspend fun write( - writeValueType: String, - writeValue: String, - dataVersion: Int?, - timedRequestTimeoutMs: Int, - imTimeoutMs: Int - ) { + private fun addWriteRequest() { + val writeValue = binding.writeValueEd.text.toString() + val writeValueType = binding.writeValueTypeSp.selectedItem.toString() + val dataVersion = binding.dataVersionEd.text.toString() + val endpointId = if (!addressUpdateFragment.isGroupId()) { getChipPathIdForText(binding.endpointIdEd.text.toString()) @@ -329,10 +395,10 @@ class WildcardFragment : Fragment() { val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) val version = - if (dataVersion == null) { + if (dataVersion.isEmpty()) { Optional.empty() } else { - Optional.of(dataVersion) + Optional.of(dataVersion.toInt()) } lateinit var writeRequest: AttributeWriteRequest @@ -364,26 +430,15 @@ class WildcardFragment : Fragment() { version ) } - val devicePtr = - try { - addressUpdateFragment.getDevicePointer(requireContext()) - } catch (e: IllegalStateException) { - Log.d(TAG, "getDevicePointer exception", e) - return - } - deviceController.write( - writeAttributeCallback, - devicePtr, - listOf(writeRequest), - timedRequestTimeoutMs, - imTimeoutMs - ) + writePath.add(writeRequest) + updateAddListView() } - private suspend fun invoke(invokeJson: String, timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + private fun addInvokeRequest() { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val commandId = getChipPathIdForText(binding.commandIdEd.text.toString()) + val invokeJson = binding.invokeValueEd.text.toString() val jsonString = invokeJson.ifEmpty { "{}" } val invokeElement = @@ -398,6 +453,28 @@ class WildcardFragment : Fragment() { } else { InvokeElement.newInstance(endpointId, clusterId, commandId, null, jsonString) } + invokePath.add(invokeElement) + updateAddListView() + } + + private suspend fun write(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + val devicePtr = + try { + addressUpdateFragment.getDevicePointer(requireContext()) + } catch (e: IllegalStateException) { + Log.d(TAG, "getDevicePointer exception", e) + return + } + deviceController.write( + writeAttributeCallback, + devicePtr, + writePath, + timedRequestTimeoutMs, + imTimeoutMs + ) + } + + private suspend fun invoke(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { val devicePtr = try { addressUpdateFragment.getDevicePointer(requireContext()) @@ -405,10 +482,10 @@ class WildcardFragment : Fragment() { Log.d(TAG, "getDevicePointer exception", e) return } - deviceController.invoke( - invokeCallback, + deviceController.extendableInvoke( + extendableInvokeCallback, devicePtr, - invokeElement, + invokePath, timedRequestTimeoutMs, imTimeoutMs ) @@ -453,31 +530,14 @@ class WildcardFragment : Fragment() { private fun showWriteDialog() { binding.outputTv.text = "" val dialogView = requireActivity().layoutInflater.inflate(R.layout.write_dialog, null) - val writeValueTypeSp = dialogView.findViewById(R.id.writeValueTypeSp) - val spinnerAdapter = - ArrayAdapter( - requireActivity(), - android.R.layout.simple_spinner_dropdown_item, - TLV_MAP.keys.toList() - ) - writeValueTypeSp.adapter = spinnerAdapter val dialog = AlertDialog.Builder(requireContext()).apply { setView(dialogView) }.create() dialogView.findViewById