Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file defines Reporting Engine for a CHIP Interaction Model
22 : *
23 : */
24 :
25 : #pragma once
26 :
27 : #include "app/data-model-provider/AttributeChangeListener.h"
28 : #include <access/AccessControl.h>
29 : #include <app/EventReporter.h>
30 : #include <app/MessageDef/ReportDataMessage.h>
31 : #include <app/ReadHandler.h>
32 : #include <app/reporting/Generations.h>
33 : #include <app/util/basic-types.h>
34 : #include <lib/core/CHIPCore.h>
35 : #include <lib/support/CodeUtils.h>
36 : #include <lib/support/logging/CHIPLogging.h>
37 : #include <messaging/ExchangeContext.h>
38 : #include <messaging/ExchangeMgr.h>
39 : #include <protocols/Protocols.h>
40 : #include <system/SystemPacketBuffer.h>
41 : #include <system/TLVPacketBufferBackingStore.h>
42 :
43 : namespace chip {
44 : namespace app {
45 :
46 : class InteractionModelEngine;
47 : class TestReadInteraction;
48 :
49 : namespace reporting {
50 :
51 : /*
52 : * @class Engine
53 : *
54 : * @brief The reporting engine is responsible for generating reports to reader. It is able to find the intersection
55 : * between the path interest set of each reader with what has changed in the publisher data store and generate tailored
56 : * reports for each reader.
57 : *
58 : * At its core, it tries to gather and pack as much relevant attributes changes and/or events as possible into a report
59 : * message before sending that to the reader. It continues to do so until it has no more work to do.
60 : */
61 : class Engine : public EventReporter, public DataModel::AttributeChangeListener
62 : {
63 : public:
64 : /**
65 : * Constructor Engine with a valid InteractionModelEngine pointer.
66 : */
67 : Engine(InteractionModelEngine * apImEngine);
68 :
69 : /**
70 : * Initializes the reporting engine. Should only be called once.
71 : *
72 : * @param[in] A pointer to EventManagement, should not be a nullptr.
73 : *
74 : * @retval #CHIP_NO_ERROR On success.
75 : * @retval other Was unable to retrieve data and write it into the writer.
76 : */
77 : CHIP_ERROR Init(EventManagement * apEventManagement);
78 :
79 : void Shutdown();
80 :
81 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
82 572 : void SetWriterReserved(uint32_t aReservedSize) { mReservedSize = aReservedSize; }
83 :
84 9 : void SetMaxAttributesPerChunk(uint32_t aMaxAttributesPerChunk) { mMaxAttributesPerChunk = aMaxAttributesPerChunk; }
85 : #endif
86 :
87 : /**
88 : * Should be invoked when the device receives a Status report, or when the Report data request times out.
89 : * This allows the engine to do some clean-up.
90 : *
91 : */
92 : void OnReportConfirm();
93 :
94 : /**
95 : * Main work-horse function that executes the run-loop asynchronously on the CHIP thread
96 : */
97 : CHIP_ERROR ScheduleRun();
98 :
99 : /**
100 : * Application marks mutated change path and would be sent out in later report.
101 : */
102 : CHIP_ERROR SetDirty(const AttributePathParams & aAttributePathParams);
103 :
104 : /*
105 : * Resets the tracker that tracks the currently serviced read handler.
106 : * apReadHandler can be non-null to indicate that the reset is due to a
107 : * specific ReadHandler being deallocated.
108 : */
109 867 : void ResetReadHandlerTracker(ReadHandler * apReadHandlerBeingDeleted)
110 : {
111 867 : if (apReadHandlerBeingDeleted == mRunningReadHandler)
112 : {
113 : // Just decrement, so our increment after we finish running it will
114 : // do the right thing.
115 738 : --mCurReadHandlerIdx;
116 : }
117 : else
118 : {
119 : // No idea what to do here to make the indexing sane. Just start at
120 : // the beginning. We need to do better here; see
121 : // https://github.com/project-chip/connectedhomeip/issues/13809
122 129 : mCurReadHandlerIdx = 0;
123 : }
124 867 : }
125 :
126 20 : uint32_t GetNumReportsInFlight() const { return mNumReportsInFlight; }
127 :
128 2856 : AttributeGeneration GetDirtySetGeneration() const { return mDirtyGeneration; }
129 :
130 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
131 : size_t GetGlobalDirtySetSize() { return mGlobalDirtySet.Allocated(); }
132 : #endif
133 :
134 : // DataModel::AttributeChangeListener implementation
135 : void OnAttributeChanged(const ConcreteAttributePath & path, DataModel::AttributeChangeType type) override;
136 : void OnEndpointChanged(EndpointId endpointId, DataModel::EndpointChangeType type) override;
137 :
138 : private:
139 : /**
140 : * Main work-horse function that executes the run-loop.
141 : */
142 : void Run();
143 :
144 : friend class TestReportingEngine;
145 : friend class ::chip::app::TestReadInteraction;
146 :
147 2169 : bool IsRunScheduled() const { return mRunScheduled; }
148 :
149 : struct AttributePathParamsWithGeneration : public AttributePathParams
150 : {
151 124 : AttributePathParamsWithGeneration() = default;
152 122 : AttributePathParamsWithGeneration(const AttributePathParams aPath) : AttributePathParams(aPath) {}
153 :
154 : AttributeGeneration mGeneration;
155 : };
156 :
157 : /**
158 : * Build Single Report Data including attribute changes and event data stream, and send out
159 : *
160 : */
161 : CHIP_ERROR BuildAndSendSingleReportData(ReadHandler * apReadHandler);
162 :
163 : CHIP_ERROR BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
164 : bool * apHasMoreChunks, bool * apHasEncodedData);
165 : CHIP_ERROR BuildSingleReportDataEventReports(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
166 : bool aBufferIsUsed, bool * apHasMoreChunks, bool * apHasEncodedData);
167 :
168 : /**
169 : * Encodes StatusIB event reports for non-wildcard paths that fail to be validated:
170 : * - invalid paths (invalid endpoint/cluster id)
171 : * - failure to validate ACL (cannot fetch ACL requirement or ACL failure)
172 : *
173 : * Returns CHIP_NO_ERROR if encoding succeeds, returns error code on a fatal error (generally failure to encode EventStatusIB
174 : * values).
175 : */
176 : CHIP_ERROR CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData, ReadHandler * apReadHandler);
177 :
178 : // If version match, it means don't send, if version mismatch, it means send.
179 : // If client sends the same path with multiple data versions, client will get the data back per the spec, because at least one
180 : // of those will fail to match. This function should return false if either nothing in the list matches the given
181 : // endpoint+cluster in the path or there is an entry in the list that matches the endpoint+cluster in the path but does not
182 : // match the current data version of that cluster.
183 : bool IsClusterDataVersionMatch(const SingleLinkedListNode<DataVersionFilter> * aDataVersionFilterList,
184 : const ConcreteReadAttributePath & aPath);
185 :
186 : /**
187 : * EventReporter implementation.
188 : */
189 : CHIP_ERROR NewEventGenerated(ConcreteEventPath & aPath, uint32_t aBytesConsumed) override;
190 : void ScheduleUrgentEventDeliverySync(Optional<FabricIndex> fabricIndex = NullOptional) override;
191 :
192 : /**
193 : * Send Report via ReadHandler
194 : *
195 : */
196 : CHIP_ERROR SendReport(ReadHandler * apReadHandler, System::PacketBufferHandle && aPayload, bool aHasMoreChunks);
197 :
198 : /**
199 : * Generate and send the report data request when there exists subscription or read request
200 : *
201 : */
202 : static void Run(System::Layer * aSystemLayer, void * apAppState);
203 :
204 : CHIP_ERROR ScheduleBufferPressureEventDelivery(uint32_t aBytesWritten);
205 : void GetMinEventLogPosition(uint32_t & aMinLogPosition);
206 :
207 : /**
208 : * If the provided path is a superset of our of our existing paths, update that existing path to match the
209 : * provided path.
210 : *
211 : * Return whether one of our paths is now a superset of the provided path.
212 : */
213 : bool MergeOverlappedAttributePath(const AttributePathParams & aAttributePath);
214 :
215 : /**
216 : * If we are running out of ObjectPool for the global dirty set, we will try to merge the existing items by clusters.
217 : *
218 : * Returns whether we have released any paths.
219 : */
220 : bool MergeDirtyPathsUnderSameCluster();
221 :
222 : /**
223 : * If we are running out of ObjectPool for the global dirty set and we cannot find a slot after merging the existing items by
224 : * clusters, we will try to merge the existing items by endpoints.
225 : *
226 : * Returns whether we have released any paths.
227 : */
228 : bool MergeDirtyPathsUnderSameEndpoint();
229 :
230 : /**
231 : * During the iterating of the paths, releasing the object in the inner loop will cause undefined behavior of the ObjectPool, so
232 : * we replace the items to be cleared by a tomb first, then clear all the tombs after the iteration.
233 : *
234 : * Returns whether we have released any paths.
235 : */
236 : bool ClearTombPaths();
237 :
238 : CHIP_ERROR InsertPathIntoDirtySet(const AttributePathParams & aAttributePath);
239 :
240 5555 : inline void BumpDirtySetGeneration() { mDirtyGeneration.Increment(); }
241 :
242 : /**
243 : * Boolean to indicate if ScheduleRun is pending. This flag is used to prevent calling ScheduleRun multiple times
244 : * within the same execution context to avoid applying too much pressure on platforms that use small, fixed size event queues.
245 : *
246 : */
247 : bool mRunScheduled = false;
248 :
249 : /**
250 : * The number of report date request in flight
251 : *
252 : */
253 : uint32_t mNumReportsInFlight = 0;
254 :
255 : /**
256 : * Current read handler index
257 : *
258 : */
259 : uint32_t mCurReadHandlerIdx = 0;
260 :
261 : /**
262 : * The read handler we're calling BuildAndSendSingleReportData on right now.
263 : */
264 : ReadHandler * mRunningReadHandler = nullptr;
265 :
266 : /**
267 : * mGlobalDirtySet is used to track the set of attribute/event paths marked dirty for reporting purposes.
268 : *
269 : */
270 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
271 : // For unit tests, always use inline allocation for code coverage.
272 : ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET, ObjectPoolMem::kInline> mGlobalDirtySet;
273 : #else
274 : ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET> mGlobalDirtySet;
275 : #endif
276 :
277 : /**
278 : * A generation counter for the dirty attrbute set.
279 : * ReadHandlers can save the generation value when generating reports.
280 : *
281 : * Then we can tell whether they might have missed reporting an attribute by
282 : * comparing its generation counter to the saved one.
283 : *
284 : * mDirtySetGeneration will increase by one when SetDirty is called.
285 : *
286 : * Count it from 1, so 0 can be used in ReadHandler to indicate "the read handler has never
287 : * completed a report".
288 : */
289 : AttributeGeneration mDirtyGeneration{ 1 };
290 :
291 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
292 : uint32_t mReservedSize = 0;
293 : uint32_t mMaxAttributesPerChunk = UINT32_MAX;
294 : #endif
295 :
296 : InteractionModelEngine * mpImEngine = nullptr;
297 :
298 : EventManagement * mpEventManagement = nullptr;
299 : };
300 :
301 : }; // namespace reporting
302 : }; // namespace app
303 : }; // namespace chip
|