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 <access/AccessControl.h>
28 : #include <app/EventReporter.h>
29 : #include <app/MessageDef/ReportDataMessage.h>
30 : #include <app/ReadHandler.h>
31 : #include <app/data-model-provider/ProviderChangeListener.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 DataModel::ProviderChangeListener, public EventReporter
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 2850 : 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 : /* ProviderChangeListener implementation */
135 : void MarkDirty(const AttributePathParams & path) override;
136 :
137 : private:
138 : /**
139 : * Main work-horse function that executes the run-loop.
140 : */
141 : void Run();
142 :
143 : friend class TestReportingEngine;
144 : friend class ::chip::app::TestReadInteraction;
145 :
146 2166 : bool IsRunScheduled() const { return mRunScheduled; }
147 :
148 : struct AttributePathParamsWithGeneration : public AttributePathParams
149 : {
150 121 : AttributePathParamsWithGeneration() = default;
151 119 : AttributePathParamsWithGeneration(const AttributePathParams aPath) : AttributePathParams(aPath) {}
152 :
153 : AttributeGeneration mGeneration;
154 : };
155 :
156 : /**
157 : * Build Single Report Data including attribute changes and event data stream, and send out
158 : *
159 : */
160 : CHIP_ERROR BuildAndSendSingleReportData(ReadHandler * apReadHandler);
161 :
162 : CHIP_ERROR BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
163 : bool * apHasMoreChunks, bool * apHasEncodedData);
164 : CHIP_ERROR BuildSingleReportDataEventReports(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
165 : bool aBufferIsUsed, bool * apHasMoreChunks, bool * apHasEncodedData);
166 :
167 : /**
168 : * Encodes StatusIB event reports for non-wildcard paths that fail to be validated:
169 : * - invalid paths (invalid endpoint/cluster id)
170 : * - failure to validate ACL (cannot fetch ACL requirement or ACL failure)
171 : *
172 : * Returns CHIP_NO_ERROR if encoding succeeds, returns error code on a fatal error (generally failure to encode EventStatusIB
173 : * values).
174 : */
175 : CHIP_ERROR CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData, ReadHandler * apReadHandler);
176 :
177 : // If version match, it means don't send, if version mismatch, it means send.
178 : // If client sends the same path with multiple data versions, client will get the data back per the spec, because at least one
179 : // of those will fail to match. This function should return false if either nothing in the list matches the given
180 : // endpoint+cluster in the path or there is an entry in the list that matches the endpoint+cluster in the path but does not
181 : // match the current data version of that cluster.
182 : bool IsClusterDataVersionMatch(const SingleLinkedListNode<DataVersionFilter> * aDataVersionFilterList,
183 : const ConcreteReadAttributePath & aPath);
184 :
185 : /**
186 : * EventReporter implementation.
187 : */
188 : CHIP_ERROR NewEventGenerated(ConcreteEventPath & aPath, uint32_t aBytesConsumed) override;
189 : void ScheduleUrgentEventDeliverySync(Optional<FabricIndex> fabricIndex = NullOptional) override;
190 :
191 : /**
192 : * Send Report via ReadHandler
193 : *
194 : */
195 : CHIP_ERROR SendReport(ReadHandler * apReadHandler, System::PacketBufferHandle && aPayload, bool aHasMoreChunks);
196 :
197 : /**
198 : * Generate and send the report data request when there exists subscription or read request
199 : *
200 : */
201 : static void Run(System::Layer * aSystemLayer, void * apAppState);
202 :
203 : CHIP_ERROR ScheduleBufferPressureEventDelivery(uint32_t aBytesWritten);
204 : void GetMinEventLogPosition(uint32_t & aMinLogPosition);
205 :
206 : /**
207 : * If the provided path is a superset of our of our existing paths, update that existing path to match the
208 : * provided path.
209 : *
210 : * Return whether one of our paths is now a superset of the provided path.
211 : */
212 : bool MergeOverlappedAttributePath(const AttributePathParams & aAttributePath);
213 :
214 : /**
215 : * If we are running out of ObjectPool for the global dirty set, we will try to merge the existing items by clusters.
216 : *
217 : * Returns whether we have released any paths.
218 : */
219 : bool MergeDirtyPathsUnderSameCluster();
220 :
221 : /**
222 : * If we are running out of ObjectPool for the global dirty set and we cannot find a slot after merging the existing items by
223 : * clusters, we will try to merge the existing items by endpoints.
224 : *
225 : * Returns whether we have released any paths.
226 : */
227 : bool MergeDirtyPathsUnderSameEndpoint();
228 :
229 : /**
230 : * During the iterating of the paths, releasing the object in the inner loop will cause undefined behavior of the ObjectPool, so
231 : * we replace the items to be cleared by a tomb first, then clear all the tombs after the iteration.
232 : *
233 : * Returns whether we have released any paths.
234 : */
235 : bool ClearTombPaths();
236 :
237 : CHIP_ERROR InsertPathIntoDirtySet(const AttributePathParams & aAttributePath);
238 :
239 5413 : inline void BumpDirtySetGeneration() { mDirtyGeneration.Increment(); }
240 :
241 : /**
242 : * Boolean to indicate if ScheduleRun is pending. This flag is used to prevent calling ScheduleRun multiple times
243 : * within the same execution context to avoid applying too much pressure on platforms that use small, fixed size event queues.
244 : *
245 : */
246 : bool mRunScheduled = false;
247 :
248 : /**
249 : * The number of report date request in flight
250 : *
251 : */
252 : uint32_t mNumReportsInFlight = 0;
253 :
254 : /**
255 : * Current read handler index
256 : *
257 : */
258 : uint32_t mCurReadHandlerIdx = 0;
259 :
260 : /**
261 : * The read handler we're calling BuildAndSendSingleReportData on right now.
262 : */
263 : ReadHandler * mRunningReadHandler = nullptr;
264 :
265 : /**
266 : * mGlobalDirtySet is used to track the set of attribute/event paths marked dirty for reporting purposes.
267 : *
268 : */
269 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
270 : // For unit tests, always use inline allocation for code coverage.
271 : ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET, ObjectPoolMem::kInline> mGlobalDirtySet;
272 : #else
273 : ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET> mGlobalDirtySet;
274 : #endif
275 :
276 : /**
277 : * A generation counter for the dirty attrbute set.
278 : * ReadHandlers can save the generation value when generating reports.
279 : *
280 : * Then we can tell whether they might have missed reporting an attribute by
281 : * comparing its generation counter to the saved one.
282 : *
283 : * mDirtySetGeneration will increase by one when SetDirty is called.
284 : *
285 : * Count it from 1, so 0 can be used in ReadHandler to indicate "the read handler has never
286 : * completed a report".
287 : */
288 : AttributeGeneration mDirtyGeneration{ 1 };
289 :
290 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
291 : uint32_t mReservedSize = 0;
292 : uint32_t mMaxAttributesPerChunk = UINT32_MAX;
293 : #endif
294 :
295 : InteractionModelEngine * mpImEngine = nullptr;
296 :
297 : EventManagement * mpEventManagement = nullptr;
298 : };
299 :
300 : }; // namespace reporting
301 : }; // namespace app
302 : }; // namespace chip
|