Skip to content

Commit 3b73411

Browse files
tehampsonrestyled-commitsbzbarsky-apple
authored andcommitted
Adding MRP analytics delegate (project-chip#37439)
This is a starting point for MRP events to be sent to some sort of delegate interested. The design is intentionally done in this way to reduce code size within the SDK and is meant for applications such as a controller to registers a delegate for MRP events allowing for it to construct analytics. --------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent b0d0100 commit 3b73411

8 files changed

+531
-0
lines changed

src/lib/core/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ buildconfig_header("chip_buildconfig") {
7171
"CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ=${chip_tlv_validate_char_string_on_read}",
7272
"CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS=${chip_enable_sending_batch_commands}",
7373
"CHIP_CONFIG_TEST_GOOGLETEST=${chip_build_tests_googletest}",
74+
"CHIP_CONFIG_MRP_ANALYTICS_ENABLED=${chip_enable_mrp_analytics}",
7475
]
7576

7677
visibility = [ ":chip_config_header" ]

src/lib/core/CHIPConfig.h

+14
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,20 @@ extern const char CHIP_NON_PRODUCTION_MARKER[];
18671867
#define CHIP_CONFIG_TEST_GOOGLETEST 0
18681868
#endif // CHIP_CONFIG_TEST_GOOGLETEST
18691869

1870+
/**
1871+
* @def CHIP_CONFIG_MRP_ANALYTICS_ENABLED
1872+
*
1873+
* @brief
1874+
* Enables code for collecting and sending analytic related events for MRP
1875+
*
1876+
* The purpose of this macro is to prevent compiling code related to MRP analytics
1877+
* for devices that are not interested interested to save on flash.
1878+
*/
1879+
1880+
#ifndef CHIP_CONFIG_MRP_ANALYTICS_ENABLED
1881+
#define CHIP_CONFIG_MRP_ANALYTICS_ENABLED 0
1882+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
1883+
18701884
/**
18711885
* @}
18721886
*/

src/lib/core/core.gni

+4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ declare_args() {
126126
chip_enable_sending_batch_commands =
127127
current_os == "linux" || current_os == "mac" || current_os == "ios" ||
128128
current_os == "android"
129+
130+
chip_enable_mrp_analytics =
131+
current_os == "linux" || current_os == "android" || current_os == "mac" ||
132+
current_os == "ios"
129133
}
130134

131135
if (chip_target_style == "") {

src/messaging/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ static_library("messaging") {
6363
"ExchangeMgr.cpp",
6464
"ExchangeMgr.h",
6565
"Flags.h",
66+
"ReliableMessageAnalyticsDelegate.h",
6667
"ReliableMessageContext.cpp",
6768
"ReliableMessageContext.h",
6869
"ReliableMessageMgr.cpp",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* @file
20+
* This file defines an interface for objects interested in MRP events for analytics
21+
*/
22+
23+
#pragma once
24+
25+
#include <lib/core/DataModelTypes.h>
26+
#include <lib/core/NodeId.h>
27+
28+
namespace chip {
29+
namespace Messaging {
30+
31+
class ReliableMessageAnalyticsDelegate
32+
{
33+
public:
34+
virtual ~ReliableMessageAnalyticsDelegate() = default;
35+
36+
enum class SessionType
37+
{
38+
kEstablishedCase,
39+
// Initially, we are starting with only one session type, but we are considering the future when we expand to allow
40+
// other session types, such as establishing a CASE session.
41+
};
42+
43+
enum class EventType
44+
{
45+
// Event associated with first time this specific message is sent.
46+
kInitialSend,
47+
// Event associated with re-transmitting a message that was previously sent but not acknowledged.
48+
kRetransmission,
49+
// Event associated with receiving an acknowledgement of a previously sent message.
50+
kAcknowledged,
51+
// Event associated with transmission of a message that failed to be acknowledged.
52+
kFailed,
53+
};
54+
55+
struct TransmitEvent
56+
{
57+
// When the session has a peer node ID, this will be a value other than kUndefinedNodeId.
58+
NodeId nodeId = kUndefinedNodeId;
59+
// When the session has a fabric index, this will be a value other than kUndefinedFabricIndex.
60+
FabricIndex fabricIndex = kUndefinedFabricIndex;
61+
// Session type of session the message involved is being sent on.
62+
SessionType sessionType = SessionType::kEstablishedCase;
63+
// The transmit event type.
64+
EventType eventType = EventType::kInitialSend;
65+
// The outgoing message counter associated with the event. If there is no outgoing message counter
66+
// this value will be 0.
67+
uint32_t messageCounter = 0;
68+
};
69+
70+
virtual void OnTransmitEvent(const TransmitEvent & event) = 0;
71+
};
72+
73+
} // namespace Messaging
74+
} // namespace chip

src/messaging/ReliableMessageMgr.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,35 @@ void ReliableMessageMgr::TicklessDebugDumpRetransTable(const char * log)
101101
#endif
102102
}
103103

104+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
105+
void ReliableMessageMgr::NotifyMessageSendAnalytics(const RetransTableEntry & entry, const SessionHandle & sessionHandle,
106+
const ReliableMessageAnalyticsDelegate::EventType & eventType)
107+
{
108+
// For now we only support sending analytics for messages being sent over an established CASE session.
109+
if (!mAnalyticsDelegate || !sessionHandle->IsSecureSession())
110+
{
111+
return;
112+
}
113+
114+
auto secureSession = sessionHandle->AsSecureSession();
115+
if (!secureSession->IsCASESession())
116+
{
117+
return;
118+
}
119+
120+
uint32_t messageCounter = entry.retainedBuf.GetMessageCounter();
121+
auto fabricIndex = sessionHandle->GetFabricIndex();
122+
auto destination = secureSession->GetPeerNodeId();
123+
ReliableMessageAnalyticsDelegate::TransmitEvent event = { .nodeId = destination,
124+
.fabricIndex = fabricIndex,
125+
.sessionType =
126+
ReliableMessageAnalyticsDelegate::SessionType::kEstablishedCase,
127+
.eventType = eventType,
128+
.messageCounter = messageCounter };
129+
mAnalyticsDelegate->OnTransmitEvent(event);
130+
}
131+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
132+
104133
void ReliableMessageMgr::ExecuteActions()
105134
{
106135
System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp();
@@ -155,6 +184,10 @@ void ReliableMessageMgr::ExecuteActions()
155184
Transport::GetSessionTypeString(session), fabricIndex, ChipLogValueX64(destination),
156185
CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS);
157186

187+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
188+
NotifyMessageSendAnalytics(*entry, session, ReliableMessageAnalyticsDelegate::EventType::kFailed);
189+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
190+
158191
// If the exchange is expecting a response, it will handle sending
159192
// this notification once it detects that it has not gotten a
160193
// response. Otherwise, we need to do it.
@@ -286,6 +319,9 @@ System::Clock::Timeout ReliableMessageMgr::GetBackoff(System::Clock::Timeout bas
286319
void ReliableMessageMgr::StartRetransmision(RetransTableEntry * entry)
287320
{
288321
CalculateNextRetransTime(*entry);
322+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
323+
NotifyMessageSendAnalytics(*entry, entry->ec->GetSessionHandle(), ReliableMessageAnalyticsDelegate::EventType::kInitialSend);
324+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
289325
StartTimer();
290326
}
291327

@@ -295,6 +331,11 @@ bool ReliableMessageMgr::CheckAndRemRetransTable(ReliableMessageContext * rc, ui
295331
mRetransTable.ForEachActiveObject([&](auto * entry) {
296332
if (entry->ec->GetReliableMessageContext() == rc && entry->retainedBuf.GetMessageCounter() == ackMessageCounter)
297333
{
334+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
335+
auto session = entry->ec->GetSessionHandle();
336+
NotifyMessageSendAnalytics(*entry, session, ReliableMessageAnalyticsDelegate::EventType::kAcknowledged);
337+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
338+
298339
// Clear the entry from the retransmision table.
299340
ClearRetransTable(*entry);
300341

@@ -334,6 +375,10 @@ CHIP_ERROR ReliableMessageMgr::SendFromRetransTable(RetransTableEntry * entry)
334375
#if CHIP_CONFIG_ENABLE_ICD_SERVER
335376
app::ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
336377
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
378+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
379+
NotifyMessageSendAnalytics(*entry, entry->ec->GetSessionHandle(),
380+
ReliableMessageAnalyticsDelegate::EventType::kRetransmission);
381+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
337382
#if CHIP_CONFIG_RESOLVE_PEER_ON_FIRST_TRANSMIT_FAILURE
338383
const ExchangeManager * exchangeMgr = entry->ec->GetExchangeMgr();
339384
// TODO: investigate why in ReliableMessageMgr::CheckResendApplicationMessageWithPeerExchange unit test released exchange
@@ -440,6 +485,13 @@ void ReliableMessageMgr::RegisterSessionUpdateDelegate(SessionUpdateDelegate * s
440485
mSessionUpdateDelegate = sessionUpdateDelegate;
441486
}
442487

488+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
489+
void ReliableMessageMgr::RegisterAnalyticsDelegate(ReliableMessageAnalyticsDelegate * analyticsDelegate)
490+
{
491+
mAnalyticsDelegate = analyticsDelegate;
492+
}
493+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
494+
443495
CHIP_ERROR ReliableMessageMgr::MapSendError(CHIP_ERROR error, uint16_t exchangeId, bool isInitiator)
444496
{
445497
if (

src/messaging/ReliableMessageMgr.h

+18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <lib/support/BitFlags.h>
3232
#include <lib/support/Pool.h>
3333
#include <messaging/ExchangeContext.h>
34+
#include <messaging/ReliableMessageAnalyticsDelegate.h>
3435
#include <messaging/ReliableMessageProtocolConfig.h>
3536
#include <system/SystemLayer.h>
3637
#include <system/SystemPacketBuffer.h>
@@ -185,6 +186,15 @@ class ReliableMessageMgr
185186
*/
186187
void RegisterSessionUpdateDelegate(SessionUpdateDelegate * sessionUpdateDelegate);
187188

189+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
190+
/**
191+
* Registers a delegate interested in analytic information
192+
*
193+
* @param[in] analyticsDelegate - Pointer to delegate for reporting analytic
194+
*/
195+
void RegisterAnalyticsDelegate(ReliableMessageAnalyticsDelegate * analyticsDelegate);
196+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
197+
188198
/**
189199
* Map a send error code to the error code we should actually use for
190200
* success checks. This maps some error codes to CHIP_NO_ERROR as
@@ -245,10 +255,18 @@ class ReliableMessageMgr
245255

246256
void TicklessDebugDumpRetransTable(const char * log);
247257

258+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
259+
void NotifyMessageSendAnalytics(const RetransTableEntry & entry, const SessionHandle & sessionHandle,
260+
const ReliableMessageAnalyticsDelegate::EventType & eventType);
261+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
262+
248263
// ReliableMessageProtocol Global tables for timer context
249264
ObjectPool<RetransTableEntry, CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE> mRetransTable;
250265

251266
SessionUpdateDelegate * mSessionUpdateDelegate = nullptr;
267+
#if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
268+
ReliableMessageAnalyticsDelegate * mAnalyticsDelegate = nullptr;
269+
#endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
252270

253271
static System::Clock::Timeout sAdditionalMRPBackoffTime;
254272
};

0 commit comments

Comments
 (0)