Line data Source code
1 : /**
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * Copyright (c) 2015-2017 Nest Labs, Inc.
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 : #include <access/AccessControl.h>
20 : #include <access/RequestPath.h>
21 : #include <access/SubjectDescriptor.h>
22 : #include <app/EventManagement.h>
23 : #include <app/InteractionModelEngine.h>
24 : #include <lib/core/TLVUtilities.h>
25 : #include <lib/support/CodeUtils.h>
26 : #include <lib/support/logging/CHIPLogging.h>
27 :
28 : #include <cassert>
29 : #include <cinttypes>
30 :
31 : using namespace chip::TLV;
32 :
33 : namespace chip {
34 : namespace app {
35 : EventManagement EventManagement::sInstance;
36 :
37 : /**
38 : * @brief
39 : * A TLVReader backed by CircularEventBuffer
40 : */
41 : class CircularEventReader : public TLV::TLVReader
42 : {
43 : public:
44 : /**
45 : * @brief
46 : * Initializes a TLVReader object backed by CircularEventBuffer
47 : *
48 : * Reading begins in the CircularTLVBuffer belonging to this
49 : * CircularEventBuffer. When the reader runs out of data, it begins
50 : * to read from the previous CircularEventBuffer.
51 : *
52 : * @param[in] apBuf A pointer to a fully initialized CircularEventBuffer
53 : *
54 : */
55 : void Init(CircularEventBufferWrapper * apBuf);
56 :
57 944 : virtual ~CircularEventReader() = default;
58 : };
59 :
60 870 : EventManagement & EventManagement::GetInstance()
61 : {
62 870 : return sInstance;
63 : }
64 :
65 : struct ReclaimEventCtx
66 : {
67 : CircularEventBuffer * mpEventBuffer = nullptr;
68 : size_t mSpaceNeededForMovedEvent = 0;
69 : };
70 :
71 : /**
72 : * @brief
73 : * Internal structure for traversing event list.
74 : */
75 : struct CopyAndAdjustDeltaTimeContext
76 : {
77 2167 : CopyAndAdjustDeltaTimeContext(TLVWriter * aWriter, EventLoadOutContext * inContext) : mpWriter(aWriter), mpContext(inContext) {}
78 :
79 : TLV::TLVWriter * mpWriter = nullptr;
80 : EventLoadOutContext * mpContext = nullptr;
81 : };
82 :
83 128 : CHIP_ERROR EventManagement::Init(Messaging::ExchangeManager * apExchangeManager, uint32_t aNumBuffers,
84 : CircularEventBuffer * apCircularEventBuffer,
85 : const LogStorageResources * const apLogStorageResources,
86 : MonotonicallyIncreasingCounter<EventNumber> * apEventNumberCounter,
87 : System::Clock::Milliseconds64 aMonotonicStartupTime, EventReporter * apEventReporter)
88 : {
89 128 : VerifyOrReturnError(apEventReporter != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
90 128 : VerifyOrReturnError(aNumBuffers != 0, CHIP_ERROR_INVALID_ARGUMENT);
91 128 : VerifyOrReturnError(mState == EventManagementStates::Shutdown, CHIP_ERROR_INCORRECT_STATE);
92 :
93 128 : CircularEventBuffer * current = nullptr;
94 128 : CircularEventBuffer * prev = nullptr;
95 128 : CircularEventBuffer * next = nullptr;
96 :
97 128 : mpExchangeMgr = apExchangeManager;
98 :
99 512 : for (uint32_t bufferIndex = 0; bufferIndex < aNumBuffers; bufferIndex++)
100 : {
101 384 : next = (bufferIndex < aNumBuffers - 1) ? &apCircularEventBuffer[bufferIndex + 1] : nullptr;
102 :
103 384 : current = &apCircularEventBuffer[bufferIndex];
104 384 : current->Init(apLogStorageResources[bufferIndex].mpBuffer, apLogStorageResources[bufferIndex].mBufferSize, prev, next,
105 384 : apLogStorageResources[bufferIndex].mPriority);
106 :
107 384 : prev = current;
108 :
109 384 : current->mProcessEvictedElement = nullptr;
110 384 : current->mAppData = nullptr;
111 : }
112 :
113 128 : mpEventNumberCounter = apEventNumberCounter;
114 128 : mLastEventNumber = mpEventNumberCounter->GetValue();
115 :
116 128 : mpEventBuffer = apCircularEventBuffer;
117 128 : mState = EventManagementStates::Idle;
118 128 : mBytesWritten = 0;
119 :
120 128 : mMonotonicStartupTime = aMonotonicStartupTime;
121 :
122 128 : mpEventReporter = apEventReporter;
123 :
124 128 : return CHIP_NO_ERROR;
125 : }
126 :
127 422 : CHIP_ERROR EventManagement::CopyToNextBuffer(CircularEventBuffer * apEventBuffer)
128 : {
129 422 : CircularTLVWriter writer;
130 422 : CircularTLVReader reader;
131 422 : CHIP_ERROR err = CHIP_NO_ERROR;
132 422 : CircularEventBuffer * nextBuffer = apEventBuffer->GetNextCircularEventBuffer();
133 422 : if (nextBuffer == nullptr)
134 : {
135 0 : return CHIP_ERROR_INVALID_ARGUMENT;
136 : }
137 422 : CircularEventBuffer backup = *nextBuffer;
138 :
139 : // Set up the next buffer s.t. it fails if needs to evict an element
140 422 : nextBuffer->mProcessEvictedElement = AlwaysFail;
141 :
142 422 : writer.Init(*nextBuffer);
143 :
144 : // Set up the reader s.t. it is positioned to read the head event
145 422 : reader.Init(*apEventBuffer);
146 :
147 422 : err = reader.Next();
148 422 : SuccessOrExit(err);
149 :
150 422 : err = writer.CopyElement(reader);
151 422 : SuccessOrExit(err);
152 :
153 422 : err = writer.Finalize();
154 422 : SuccessOrExit(err);
155 :
156 422 : ChipLogDetail(EventLogging, "Copy Event to next buffer with priority %u", static_cast<unsigned>(nextBuffer->GetPriority()));
157 0 : exit:
158 844 : if (err != CHIP_NO_ERROR)
159 : {
160 0 : *nextBuffer = backup;
161 : }
162 422 : return err;
163 422 : }
164 :
165 665 : CHIP_ERROR EventManagement::EnsureSpaceInCircularBuffer(size_t aRequiredSpace, PriorityLevel aPriority)
166 : {
167 665 : CHIP_ERROR err = CHIP_NO_ERROR;
168 665 : size_t requiredSpace = aRequiredSpace;
169 665 : CircularEventBuffer * eventBuffer = mpEventBuffer;
170 665 : ReclaimEventCtx ctx;
171 :
172 : // Check that we have this much space in all our event buffers that might
173 : // hold the event. If we do not, that will prevent the event from being
174 : // properly evicted into higher-priority buffers. We want to discover
175 : // this early, so that testing surfaces the need to make those buffers
176 : // larger.
177 1373 : for (auto * currentBuffer = mpEventBuffer; currentBuffer; currentBuffer = currentBuffer->GetNextCircularEventBuffer())
178 : {
179 1373 : VerifyOrExit(requiredSpace <= currentBuffer->GetTotalDataLength(), err = CHIP_ERROR_BUFFER_TOO_SMALL);
180 1373 : if (currentBuffer->IsFinalDestinationForPriority(aPriority))
181 : {
182 665 : break;
183 : }
184 : }
185 :
186 665 : VerifyOrExit(eventBuffer != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
187 :
188 : // check whether we actually need to do anything, exit if we don't
189 665 : VerifyOrExit(requiredSpace > eventBuffer->AvailableDataLength(), err = CHIP_NO_ERROR);
190 :
191 : while (true)
192 : {
193 1884 : if (requiredSpace > eventBuffer->AvailableDataLength())
194 : {
195 1100 : ctx.mpEventBuffer = eventBuffer;
196 1100 : ctx.mSpaceNeededForMovedEvent = 0;
197 :
198 1100 : eventBuffer->mProcessEvictedElement = EvictEvent;
199 1100 : eventBuffer->mAppData = &ctx;
200 1100 : err = eventBuffer->EvictHead();
201 :
202 : // one of two things happened: either the element was evicted immediately if the head's priority is same as current
203 : // buffer(final one), or we figured out how much space we need to evict it into the next buffer, the check happens in
204 : // EvictEvent function
205 :
206 2200 : if (err != CHIP_NO_ERROR)
207 : {
208 735 : VerifyOrExit(ctx.mSpaceNeededForMovedEvent != 0, /* no-op, return err */);
209 735 : VerifyOrExit(eventBuffer->GetNextCircularEventBuffer() != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
210 735 : if (ctx.mSpaceNeededForMovedEvent <= eventBuffer->GetNextCircularEventBuffer()->AvailableDataLength())
211 : {
212 : // we can copy the event outright. copy event and
213 : // subsequently evict head s.t. evicting the head
214 : // element always succeeds.
215 : // Since we're calling CopyElement and we've checked
216 : // that there is space in the next buffer, we don't expect
217 : // this to fail.
218 422 : err = CopyToNextBuffer(eventBuffer);
219 422 : SuccessOrExit(err);
220 : // success; evict head unconditionally
221 422 : eventBuffer->mProcessEvictedElement = nullptr;
222 422 : err = eventBuffer->EvictHead();
223 : // if unconditional eviction failed, this
224 : // means that we have no way of further
225 : // clearing the buffer. fail out and let the
226 : // caller know that we could not honor the
227 : // request
228 422 : SuccessOrExit(err);
229 422 : continue;
230 : }
231 : // we cannot copy event outright. We remember the
232 : // current required space in mRequiredSpaceForEvicted, we note the
233 : // space requirements for the event in the current
234 : // buffer and make that space in the next buffer.
235 313 : eventBuffer->SetRequiredSpaceforEvicted(requiredSpace);
236 313 : eventBuffer = eventBuffer->GetNextCircularEventBuffer();
237 :
238 : // Sanity check: return error here on null event buffer. If
239 : // eventBuffer->mpNext were null, then the `EvictBuffer`
240 : // would have succeeded -- the event was
241 : // already in the final buffer.
242 313 : VerifyOrExit(eventBuffer != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
243 313 : requiredSpace = ctx.mSpaceNeededForMovedEvent;
244 : }
245 : }
246 : else
247 : {
248 : // this branch is only taken when we go back in the buffer chain since we have free/spare enough space in next buffer,
249 : // and need to retry to copy event from current buffer to next buffer, and free space for current buffer
250 784 : if (eventBuffer == mpEventBuffer)
251 471 : break;
252 313 : eventBuffer = eventBuffer->GetPreviousCircularEventBuffer();
253 313 : requiredSpace = eventBuffer->GetRequiredSpaceforEvicted();
254 313 : err = CHIP_NO_ERROR;
255 : }
256 : }
257 :
258 471 : mpEventBuffer->mProcessEvictedElement = nullptr;
259 471 : mpEventBuffer->mAppData = nullptr;
260 :
261 665 : exit:
262 665 : return err;
263 : }
264 :
265 665 : CHIP_ERROR EventManagement::CalculateEventSize(EventLoggingDelegate * apDelegate, const InternalEventOptions * apOptions,
266 : uint32_t & requiredSize)
267 : {
268 665 : System::PacketBufferTLVWriter writer;
269 665 : EventLoadOutContext ctxt = EventLoadOutContext(writer, apOptions->mPriority, GetLastEventNumber());
270 665 : System::PacketBufferHandle buf = System::PacketBufferHandle::New(kMaxEventSizeReserve);
271 665 : if (buf.IsNull())
272 : {
273 0 : return CHIP_ERROR_NO_MEMORY;
274 : }
275 665 : writer.Init(std::move(buf));
276 :
277 665 : ctxt.mCurrentEventNumber = mLastEventNumber;
278 665 : ctxt.mCurrentTime = mLastEventTimestamp;
279 665 : CHIP_ERROR err = ConstructEvent(&ctxt, apDelegate, apOptions);
280 1330 : if (err == CHIP_NO_ERROR)
281 : {
282 665 : requiredSize = writer.GetLengthWritten();
283 : }
284 665 : return err;
285 665 : }
286 :
287 1330 : CHIP_ERROR EventManagement::ConstructEvent(EventLoadOutContext * apContext, EventLoggingDelegate * apDelegate,
288 : const InternalEventOptions * apOptions)
289 : {
290 1330 : VerifyOrReturnError(apContext->mCurrentEventNumber >= apContext->mStartingEventNumber, CHIP_NO_ERROR
291 : /* no-op: don't write event, but advance current event Number */);
292 :
293 1330 : VerifyOrReturnError(apOptions != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
294 :
295 1330 : EventReportIB::Builder eventReportBuilder;
296 1330 : ReturnErrorOnFailure(eventReportBuilder.Init(&(apContext->mWriter)));
297 1330 : EventDataIB::Builder & eventDataIBBuilder = eventReportBuilder.CreateEventData();
298 1330 : ReturnErrorOnFailure(eventReportBuilder.GetError());
299 1330 : EventPathIB::Builder & eventPathBuilder = eventDataIBBuilder.CreatePath();
300 1330 : ReturnErrorOnFailure(eventDataIBBuilder.GetError());
301 :
302 1330 : CHIP_ERROR err = eventPathBuilder.Endpoint(apOptions->mPath.mEndpointId)
303 1330 : .Cluster(apOptions->mPath.mClusterId)
304 1330 : .Event(apOptions->mPath.mEventId)
305 1330 : .EndOfEventPathIB();
306 1330 : ReturnErrorOnFailure(err);
307 1330 : eventDataIBBuilder.EventNumber(apContext->mCurrentEventNumber).Priority(chip::to_underlying(apContext->mPriority));
308 1330 : ReturnErrorOnFailure(eventDataIBBuilder.GetError());
309 :
310 1330 : if (apOptions->mTimestamp.IsSystem())
311 : {
312 24 : eventDataIBBuilder.SystemTimestamp(apOptions->mTimestamp.mValue);
313 : }
314 : else
315 : {
316 1306 : eventDataIBBuilder.EpochTimestamp(apOptions->mTimestamp.mValue);
317 : }
318 :
319 1330 : ReturnErrorOnFailure(eventDataIBBuilder.GetError());
320 :
321 : // Callback to write the EventData
322 1330 : ReturnErrorOnFailure(apDelegate->WriteEvent(apContext->mWriter));
323 :
324 : // The fabricIndex profile tag is internal use only for fabric filtering when retrieving event from circular event buffer,
325 : // and would not go on the wire.
326 : // Revisit FabricRemovedCB function should the encoding of fabricIndex change in the future.
327 1330 : if (apOptions->mFabricIndex != kUndefinedFabricIndex)
328 : {
329 8 : TEMPORARY_RETURN_IGNORED apContext->mWriter.Put(TLV::ProfileTag(kEventManagementProfile, kFabricIndexTag),
330 8 : apOptions->mFabricIndex);
331 : }
332 1330 : ReturnErrorOnFailure(eventDataIBBuilder.EndOfEventDataIB());
333 1330 : ReturnErrorOnFailure(eventReportBuilder.EndOfEventReportIB());
334 1330 : ReturnErrorOnFailure(apContext->mWriter.Finalize());
335 1330 : apContext->mFirst = false;
336 1330 : return CHIP_NO_ERROR;
337 : }
338 :
339 127 : void EventManagement::CreateEventManagement(Messaging::ExchangeManager * apExchangeManager, uint32_t aNumBuffers,
340 : CircularEventBuffer * apCircularEventBuffer,
341 : const LogStorageResources * const apLogStorageResources,
342 : MonotonicallyIncreasingCounter<EventNumber> * apEventNumberCounter,
343 : System::Clock::Milliseconds64 aMonotonicStartupTime)
344 : {
345 :
346 127 : TEMPORARY_RETURN_IGNORED sInstance.Init(apExchangeManager, aNumBuffers, apCircularEventBuffer, apLogStorageResources,
347 : apEventNumberCounter, aMonotonicStartupTime,
348 127 : &InteractionModelEngine::GetInstance()->GetReportingEngine());
349 127 : }
350 :
351 : /**
352 : * @brief Perform any actions we need to on shutdown.
353 : */
354 127 : void EventManagement::DestroyEventManagement()
355 : {
356 127 : sInstance.mState = EventManagementStates::Shutdown;
357 127 : sInstance.mpEventBuffer = nullptr;
358 127 : sInstance.mpExchangeMgr = nullptr;
359 127 : }
360 :
361 944 : CircularEventBuffer * EventManagement::GetPriorityBuffer(PriorityLevel aPriority) const
362 : {
363 944 : CircularEventBuffer * buf = mpEventBuffer;
364 2759 : while (!buf->IsFinalDestinationForPriority(aPriority))
365 : {
366 1815 : buf = buf->GetNextCircularEventBuffer();
367 1815 : assert(buf != nullptr);
368 : // code guarantees that every PriorityLevel has a buffer destination.
369 : }
370 944 : return buf;
371 : }
372 :
373 10117 : CHIP_ERROR EventManagement::CopyAndAdjustDeltaTime(const TLVReader & aReader, size_t aDepth, void * apContext)
374 : {
375 10117 : CopyAndAdjustDeltaTimeContext * ctx = static_cast<CopyAndAdjustDeltaTimeContext *>(apContext);
376 10117 : TLVReader reader(aReader);
377 :
378 10117 : if (aReader.GetTag() == TLV::ProfileTag(kEventManagementProfile, kFabricIndexTag))
379 : {
380 : // Does not go on the wire.
381 13 : return CHIP_NO_ERROR;
382 : }
383 10125 : if ((aReader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kSystemTimestamp)) && !(ctx->mpContext->mFirst) &&
384 21 : (ctx->mpContext->mCurrentTime.mType == ctx->mpContext->mPreviousTime.mType))
385 : {
386 21 : return ctx->mpWriter->Put(TLV::ContextTag(EventDataIB::Tag::kDeltaSystemTimestamp),
387 42 : ctx->mpContext->mCurrentTime.mValue - ctx->mpContext->mPreviousTime.mValue);
388 : }
389 11259 : if ((aReader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kEpochTimestamp)) && !(ctx->mpContext->mFirst) &&
390 1176 : (ctx->mpContext->mCurrentTime.mType == ctx->mpContext->mPreviousTime.mType))
391 : {
392 0 : return ctx->mpWriter->Put(TLV::ContextTag(EventDataIB::Tag::kDeltaEpochTimestamp),
393 0 : ctx->mpContext->mCurrentTime.mValue - ctx->mpContext->mPreviousTime.mValue);
394 : }
395 :
396 10083 : return ctx->mpWriter->CopyElement(reader);
397 : }
398 :
399 665 : void EventManagement::VendEventNumber()
400 : {
401 665 : CHIP_ERROR err = CHIP_NO_ERROR;
402 : // Now advance the counter.
403 665 : err = mpEventNumberCounter->Advance();
404 1330 : if (err != CHIP_NO_ERROR)
405 : {
406 0 : ChipLogError(EventLogging, "%s Advance() failed with %" CHIP_ERROR_FORMAT, __FUNCTION__, err.Format());
407 : }
408 :
409 : // Assign event Number to the buffer's counter's value.
410 665 : mLastEventNumber = mpEventNumberCounter->GetValue();
411 665 : }
412 :
413 679 : CHIP_ERROR EventManagement::LogEvent(EventLoggingDelegate * apDelegate, const EventOptions & aEventOptions,
414 : EventNumber & aEventNumber)
415 : {
416 679 : assertChipStackLockedByCurrentThread();
417 679 : VerifyOrReturnError(mState != EventManagementStates::Shutdown, CHIP_ERROR_INCORRECT_STATE);
418 665 : return LogEventPrivate(apDelegate, aEventOptions, aEventNumber);
419 : }
420 :
421 665 : CHIP_ERROR EventManagement::LogEventPrivate(EventLoggingDelegate * apDelegate, const EventOptions & aEventOptions,
422 : EventNumber & aEventNumber)
423 : {
424 665 : CircularTLVWriter writer;
425 665 : CHIP_ERROR err = CHIP_NO_ERROR;
426 665 : uint32_t requestSize = 0;
427 665 : aEventNumber = 0;
428 665 : CircularTLVWriter checkpoint = writer;
429 665 : EventLoadOutContext ctxt = EventLoadOutContext(writer, aEventOptions.mPriority, mLastEventNumber);
430 665 : InternalEventOptions opts;
431 :
432 665 : Timestamp timestamp;
433 : #if CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
434 : System::Clock::Milliseconds64 utc_time;
435 665 : err = System::SystemClock().GetClock_RealTimeMS(utc_time);
436 1330 : if (err == CHIP_NO_ERROR)
437 : {
438 653 : timestamp = Timestamp::Epoch(utc_time);
439 : }
440 : else
441 : #endif // CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
442 : {
443 12 : auto systemTimeMs = System::SystemClock().GetMonotonicMilliseconds64() - mMonotonicStartupTime;
444 12 : timestamp = Timestamp::System(systemTimeMs);
445 : }
446 :
447 665 : opts = InternalEventOptions(timestamp);
448 : // Start the event container (anonymous structure) in the circular buffer
449 665 : writer.Init(*mpEventBuffer);
450 :
451 665 : opts.mPriority = aEventOptions.mPriority;
452 : // Create all event specific data
453 : // Timestamp; encoded as a delta time
454 :
455 665 : opts.mPath = aEventOptions.mPath;
456 665 : opts.mFabricIndex = aEventOptions.mFabricIndex;
457 :
458 665 : ctxt.mCurrentEventNumber = mLastEventNumber;
459 665 : ctxt.mCurrentTime.mValue = mLastEventTimestamp.mValue;
460 :
461 665 : err = CalculateEventSize(apDelegate, &opts, requestSize);
462 665 : SuccessOrExit(err);
463 :
464 : // Ensure we have space in the in-memory logging queues
465 665 : err = EnsureSpaceInCircularBuffer(requestSize, aEventOptions.mPriority);
466 665 : SuccessOrExit(err);
467 :
468 665 : err = ConstructEvent(&ctxt, apDelegate, &opts);
469 665 : SuccessOrExit(err);
470 :
471 665 : mBytesWritten += writer.GetLengthWritten();
472 :
473 665 : exit:
474 1330 : if (err != CHIP_NO_ERROR)
475 : {
476 0 : ChipLogError(EventLogging, "Log event with error %" CHIP_ERROR_FORMAT, err.Format());
477 0 : writer = checkpoint;
478 : }
479 : else if (opts.mPriority >= CHIP_CONFIG_EVENT_GLOBAL_PRIORITY)
480 : {
481 665 : aEventNumber = mLastEventNumber;
482 665 : VendEventNumber();
483 665 : mLastEventTimestamp = timestamp;
484 : #if CHIP_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
485 665 : ChipLogDetail(EventLogging,
486 : "LogEvent event number: 0x" ChipLogFormatX64 " priority: %u, endpoint id: 0x%x"
487 : " cluster id: " ChipLogFormatMEI " event id: 0x%" PRIx32 " %s timestamp: 0x" ChipLogFormatX64,
488 : ChipLogValueX64(aEventNumber), static_cast<unsigned>(opts.mPriority), opts.mPath.mEndpointId,
489 : ChipLogValueMEI(opts.mPath.mClusterId), opts.mPath.mEventId,
490 : opts.mTimestamp.mType == Timestamp::Type::kSystem ? "Sys" : "Epoch", ChipLogValueX64(opts.mTimestamp.mValue));
491 : #endif // CHIP_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
492 :
493 665 : err = mpEventReporter->NewEventGenerated(opts.mPath, mBytesWritten);
494 : }
495 :
496 665 : return err;
497 : }
498 :
499 2167 : CHIP_ERROR EventManagement::CopyEvent(const TLVReader & aReader, TLVWriter & aWriter, EventLoadOutContext * apContext)
500 : {
501 2167 : TLVReader reader;
502 : TLVType containerType;
503 : TLVType containerType1;
504 2167 : CopyAndAdjustDeltaTimeContext context(&aWriter, apContext);
505 2167 : CHIP_ERROR err = CHIP_NO_ERROR;
506 :
507 2167 : reader.Init(aReader);
508 2167 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
509 2167 : ReturnErrorOnFailure(aWriter.StartContainer(AnonymousTag(), kTLVType_Structure, containerType));
510 :
511 2153 : ReturnErrorOnFailure(reader.Next());
512 2153 : ReturnErrorOnFailure(reader.EnterContainer(containerType1));
513 2153 : ReturnErrorOnFailure(
514 : aWriter.StartContainer(TLV::ContextTag(EventReportIB::Tag::kEventData), kTLVType_Structure, containerType1));
515 2123 : err = TLV::Utilities::Iterate(reader, CopyAndAdjustDeltaTime, &context, false /*recurse*/);
516 4246 : if (err == CHIP_END_OF_TLV)
517 : {
518 1661 : err = CHIP_NO_ERROR;
519 : }
520 2123 : ReturnErrorOnFailure(err);
521 1661 : ReturnErrorOnFailure(aWriter.EndContainer(containerType1));
522 1656 : ReturnErrorOnFailure(aWriter.EndContainer(containerType));
523 1652 : ReturnErrorOnFailure(aWriter.Finalize());
524 1652 : return CHIP_NO_ERROR;
525 : }
526 :
527 3250 : bool EventManagement::IncludeEventInReport(EventLoadOutContext * eventLoadOutContext,
528 : const EventManagement::EventEnvelopeContext & event)
529 : {
530 3250 : if (eventLoadOutContext->mCurrentEventNumber < eventLoadOutContext->mStartingEventNumber)
531 : {
532 1033 : return false;
533 : }
534 :
535 2252 : if (event.mFabricIndex.HasValue() &&
536 35 : (event.mFabricIndex.Value() == kUndefinedFabricIndex ||
537 28 : eventLoadOutContext->mSubjectDescriptor.fabricIndex != event.mFabricIndex.Value()))
538 : {
539 22 : return false;
540 : }
541 :
542 2195 : bool isPathInterested = false;
543 2195 : ConcreteEventPath path(event.mEndpointId, event.mClusterId, event.mEventId);
544 : // Check whether the event path is in the interested paths
545 2243 : for (auto * interestedPath = eventLoadOutContext->mpInterestedEventPaths; interestedPath != nullptr;
546 48 : interestedPath = interestedPath->mpNext)
547 : {
548 2221 : if (interestedPath->mValue.IsEventPathSupersetOf(path))
549 : {
550 2173 : isPathInterested = true;
551 2173 : break;
552 : }
553 : }
554 2195 : if (!isPathInterested)
555 : {
556 22 : return false;
557 : }
558 :
559 : DataModel::EventEntry eventInfo;
560 4346 : if (InteractionModelEngine::GetInstance()->GetDataModelProvider()->EventInfo(path, eventInfo) != CHIP_NO_ERROR)
561 : {
562 : // If we cannot get event info here, that means the data model doesn't support the
563 : // event (eg. the endpoint is removed). Return false, because we can't even perform ACL checks here,
564 : // since we don't know what permissions to check for. This is not an error condition
565 : // (other than omitting the event), because the only way we can end up in this situation
566 : // is with a wildcard event path.
567 : //
568 : // TODO: We should not record the event in the log buffer when the event is not supported by the data model, see #41448.
569 : // TODO: We should remove the event from the log buffer when the event path is no longer valid (eg. when the endpoint of the
570 : // event path is removed), see #41448
571 0 : return false;
572 : }
573 :
574 2173 : Access::RequestPath requestPath{ .cluster = event.mClusterId,
575 2173 : .endpoint = event.mEndpointId,
576 : .requestType = Access::RequestType::kEventReadRequest,
577 2173 : .entityId = event.mEventId };
578 : CHIP_ERROR accessControlError =
579 2173 : Access::GetAccessControl().Check(eventLoadOutContext->mSubjectDescriptor, requestPath, eventInfo.readPrivilege);
580 4346 : if (accessControlError != CHIP_NO_ERROR)
581 : {
582 : // If we don't have the access for the event, the only way we could have gotten here
583 : // is due to a wildcard event path that includes the event. Return false so that
584 : // the event will be excluded in the generated event report.
585 6 : return false;
586 : }
587 :
588 : // If all the checks pass, the event will be included in the generated event report.
589 2167 : return true;
590 : }
591 :
592 3250 : CHIP_ERROR EventManagement::EventIterator(const TLVReader & aReader, size_t aDepth, EventLoadOutContext * apEventLoadOutContext,
593 : EventEnvelopeContext * event, bool & encodeEvent)
594 : {
595 3250 : CHIP_ERROR err = CHIP_NO_ERROR;
596 3250 : TLVReader innerReader;
597 : TLVType tlvType;
598 : TLVType tlvType1;
599 :
600 3250 : innerReader.Init(aReader);
601 3250 : VerifyOrDie(event != nullptr);
602 3250 : ReturnErrorOnFailure(innerReader.EnterContainer(tlvType));
603 3250 : ReturnErrorOnFailure(innerReader.Next());
604 :
605 3250 : ReturnErrorOnFailure(innerReader.EnterContainer(tlvType1));
606 3250 : err = TLV::Utilities::Iterate(innerReader, FetchEventParameters, event, false /*recurse*/);
607 :
608 3250 : if (event->mFieldsToRead != kRequiredEventField)
609 : {
610 0 : return CHIP_ERROR_INVALID_ARGUMENT;
611 : }
612 :
613 6500 : if (err == CHIP_END_OF_TLV)
614 : {
615 3250 : err = CHIP_NO_ERROR;
616 : }
617 3250 : ReturnErrorOnFailure(err);
618 :
619 3250 : apEventLoadOutContext->mCurrentTime = event->mCurrentTime;
620 3250 : apEventLoadOutContext->mCurrentEventNumber = event->mEventNumber;
621 :
622 3250 : encodeEvent = IncludeEventInReport(apEventLoadOutContext, *event);
623 3250 : return CHIP_NO_ERROR;
624 : }
625 :
626 3250 : CHIP_ERROR EventManagement::CopyEventsSince(const TLVReader & aReader, size_t aDepth, void * apContext)
627 : {
628 3250 : EventLoadOutContext * const loadOutContext = static_cast<EventLoadOutContext *>(apContext);
629 3250 : EventEnvelopeContext event;
630 3250 : bool encodeEvent = false;
631 3250 : CHIP_ERROR err = EventIterator(aReader, aDepth, loadOutContext, &event, encodeEvent);
632 6500 : if ((err == CHIP_NO_ERROR) && encodeEvent)
633 : {
634 : // checkpoint the writer
635 2167 : TLV::TLVWriter checkpoint = loadOutContext->mWriter;
636 :
637 2167 : err = CopyEvent(aReader, loadOutContext->mWriter, loadOutContext);
638 :
639 : // CHIP_NO_ERROR and CHIP_END_OF_TLV signify a
640 : // successful copy. In all other cases, roll back the
641 : // writer state back to the checkpoint, i.e., the state
642 : // before we began the copy operation.
643 4849 : if ((err != CHIP_NO_ERROR) && (err != CHIP_END_OF_TLV))
644 : {
645 515 : loadOutContext->mWriter = checkpoint;
646 515 : return err;
647 : }
648 :
649 1652 : loadOutContext->mPreviousTime.mValue = loadOutContext->mCurrentTime.mValue;
650 1652 : loadOutContext->mFirst = false;
651 1652 : loadOutContext->mEventCount++;
652 : }
653 2735 : return err;
654 : }
655 :
656 893 : CHIP_ERROR EventManagement::FetchEventsSince(TLVWriter & aWriter, const SingleLinkedListNode<EventPathParams> * apEventPathList,
657 : EventNumber & aEventMin, size_t & aEventCount,
658 : const Access::SubjectDescriptor & aSubjectDescriptor)
659 : {
660 : // TODO: Add particular set of event Paths in FetchEventsSince so that we can filter the interested paths
661 893 : CHIP_ERROR err = CHIP_NO_ERROR;
662 893 : const bool recurse = false;
663 893 : TLVReader reader;
664 893 : CircularEventBufferWrapper bufWrapper;
665 893 : EventLoadOutContext context(aWriter, PriorityLevel::Invalid, aEventMin);
666 :
667 893 : context.mSubjectDescriptor = aSubjectDescriptor;
668 893 : context.mpInterestedEventPaths = apEventPathList;
669 893 : err = GetEventReader(reader, PriorityLevel::Critical, &bufWrapper);
670 893 : SuccessOrExit(err);
671 :
672 893 : err = TLV::Utilities::Iterate(reader, CopyEventsSince, &context, recurse);
673 1786 : if (err == CHIP_END_OF_TLV)
674 : {
675 378 : err = CHIP_NO_ERROR;
676 : }
677 :
678 515 : exit:
679 2679 : if (err == CHIP_ERROR_BUFFER_TOO_SMALL || err == CHIP_ERROR_NO_MEMORY)
680 : {
681 : // We failed to fetch the current event because the buffer is too small, we will start from this one the next time.
682 515 : aEventMin = context.mCurrentEventNumber;
683 : }
684 : else
685 : {
686 : // For all other cases, continue from the next event.
687 378 : aEventMin = context.mCurrentEventNumber + 1;
688 : }
689 893 : aEventCount += context.mEventCount;
690 1786 : return err;
691 893 : }
692 :
693 8 : CHIP_ERROR EventManagement::FabricRemovedCB(const TLV::TLVReader & aReader, size_t aDepth, void * apContext)
694 : {
695 : // the function does not actually remove the event, instead, it sets the fabric index to an invalid value.
696 8 : FabricIndex * invalidFabricIndex = static_cast<FabricIndex *>(apContext);
697 :
698 8 : TLVReader event;
699 : TLVType tlvType;
700 : TLVType tlvType1;
701 8 : event.Init(aReader);
702 16 : VerifyOrReturnError(event.EnterContainer(tlvType) == CHIP_NO_ERROR, CHIP_NO_ERROR);
703 16 : VerifyOrReturnError(event.Next(TLV::ContextTag(EventReportIB::Tag::kEventData)) == CHIP_NO_ERROR, CHIP_NO_ERROR);
704 16 : VerifyOrReturnError(event.EnterContainer(tlvType1) == CHIP_NO_ERROR, CHIP_NO_ERROR);
705 :
706 96 : while (CHIP_NO_ERROR == event.Next())
707 : {
708 48 : if (event.GetTag() == TLV::ProfileTag(kEventManagementProfile, kFabricIndexTag))
709 : {
710 8 : uint8_t fabricIndex = 0;
711 16 : VerifyOrReturnError(event.Get(fabricIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
712 8 : if (fabricIndex == *invalidFabricIndex)
713 : {
714 4 : TLVCircularBuffer * readBuffer = static_cast<TLVCircularBuffer *>(event.GetBackingStore());
715 : // fabricIndex is encoded as an integer; the dataPtr will point to a location immediately after its encoding
716 : // shift the dataPtr to point to the encoding of the fabric index, accounting for wraparound in backing storage
717 : // we cannot get the actual encoding size from current container beginning to the fabric index because of several
718 : // optional parameters, so we are assuming minimal encoding is used and the fabric index is 1 byte.
719 : uint8_t * dataPtr;
720 4 : if (event.GetReadPoint() != readBuffer->GetQueue())
721 : {
722 4 : dataPtr = readBuffer->GetQueue() + (event.GetReadPoint() - readBuffer->GetQueue() - 1);
723 : }
724 : else
725 : {
726 0 : dataPtr = readBuffer->GetQueue() + readBuffer->GetTotalDataLength() - 1;
727 : }
728 :
729 4 : *dataPtr = kUndefinedFabricIndex;
730 : }
731 8 : return CHIP_NO_ERROR;
732 : }
733 : }
734 0 : return CHIP_NO_ERROR;
735 : }
736 :
737 2 : CHIP_ERROR EventManagement::FabricRemoved(FabricIndex aFabricIndex)
738 : {
739 2 : const bool recurse = false;
740 2 : TLVReader reader;
741 2 : CircularEventBufferWrapper bufWrapper;
742 :
743 2 : ReturnErrorOnFailure(GetEventReader(reader, PriorityLevel::Critical, &bufWrapper));
744 2 : CHIP_ERROR err = TLV::Utilities::Iterate(reader, FabricRemovedCB, &aFabricIndex, recurse);
745 4 : if (err == CHIP_END_OF_TLV)
746 : {
747 2 : err = CHIP_NO_ERROR;
748 : }
749 2 : return err;
750 2 : }
751 :
752 944 : CHIP_ERROR EventManagement::GetEventReader(TLVReader & aReader, PriorityLevel aPriority, CircularEventBufferWrapper * apBufWrapper)
753 : {
754 944 : CircularEventBuffer * buffer = GetPriorityBuffer(aPriority);
755 944 : VerifyOrReturnError(buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
756 944 : apBufWrapper->mpCurrent = buffer;
757 :
758 944 : CircularEventReader reader;
759 944 : reader.Init(apBufWrapper);
760 944 : aReader.Init(reader);
761 :
762 944 : return CHIP_NO_ERROR;
763 944 : }
764 :
765 21796 : CHIP_ERROR EventManagement::FetchEventParameters(const TLVReader & aReader, size_t, void * apContext)
766 : {
767 21796 : EventEnvelopeContext * const envelope = static_cast<EventEnvelopeContext *>(apContext);
768 21796 : TLVReader reader;
769 21796 : reader.Init(aReader);
770 :
771 21796 : if (reader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kPath))
772 : {
773 4350 : EventPathIB::Parser path;
774 4350 : ReturnErrorOnFailure(path.Init(aReader));
775 4350 : ReturnErrorOnFailure(path.GetEndpoint(&(envelope->mEndpointId)));
776 4350 : ReturnErrorOnFailure(path.GetCluster(&(envelope->mClusterId)));
777 4350 : ReturnErrorOnFailure(path.GetEvent(&(envelope->mEventId)));
778 4350 : envelope->mFieldsToRead |= 1 << to_underlying(EventDataIB::Tag::kPath);
779 : }
780 :
781 21796 : if (reader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kPriority))
782 : {
783 : uint16_t extPriority; // Note: the type here matches the type case in EventManagement::LogEvent, priority section
784 4350 : ReturnErrorOnFailure(reader.Get(extPriority));
785 4350 : envelope->mPriority = static_cast<PriorityLevel>(extPriority);
786 4350 : envelope->mFieldsToRead |= 1 << to_underlying(EventDataIB::Tag::kPriority);
787 : }
788 :
789 21796 : if (reader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kEventNumber))
790 : {
791 4350 : ReturnErrorOnFailure(reader.Get(envelope->mEventNumber));
792 : }
793 :
794 21796 : if (reader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kSystemTimestamp))
795 : {
796 : uint64_t systemTime;
797 58 : ReturnErrorOnFailure(reader.Get(systemTime));
798 58 : envelope->mCurrentTime.mType = Timestamp::Type::kSystem;
799 58 : envelope->mCurrentTime.mValue = systemTime;
800 : }
801 :
802 21796 : if (reader.GetTag() == TLV::ContextTag(EventDataIB::Tag::kEpochTimestamp))
803 : {
804 : uint64_t epochTime;
805 4292 : ReturnErrorOnFailure(reader.Get(epochTime));
806 4292 : envelope->mCurrentTime.mType = Timestamp::Type::kEpoch;
807 4292 : envelope->mCurrentTime.mValue = epochTime;
808 : }
809 :
810 21796 : if (reader.GetTag() == TLV::ProfileTag(kEventManagementProfile, kFabricIndexTag))
811 : {
812 46 : uint8_t fabricIndex = kUndefinedFabricIndex;
813 46 : ReturnErrorOnFailure(reader.Get(fabricIndex));
814 46 : envelope->mFabricIndex.SetValue(fabricIndex);
815 : }
816 21796 : return CHIP_NO_ERROR;
817 : }
818 :
819 1100 : CHIP_ERROR EventManagement::EvictEvent(TLVCircularBuffer & apBuffer, void * apAppData, TLVReader & aReader)
820 : {
821 : // pull out the delta time, pull out the priority
822 1100 : ReturnErrorOnFailure(aReader.Next());
823 :
824 : TLVType containerType;
825 : TLVType containerType1;
826 1100 : ReturnErrorOnFailure(aReader.EnterContainer(containerType));
827 1100 : ReturnErrorOnFailure(aReader.Next());
828 :
829 1100 : ReturnErrorOnFailure(aReader.EnterContainer(containerType1));
830 1100 : EventEnvelopeContext context;
831 1100 : constexpr bool recurse = false;
832 1100 : CHIP_ERROR err = TLV::Utilities::Iterate(aReader, FetchEventParameters, &context, recurse);
833 2200 : if (err == CHIP_END_OF_TLV)
834 : {
835 1100 : err = CHIP_NO_ERROR;
836 : }
837 1100 : ReturnErrorOnFailure(err);
838 :
839 1100 : ReturnErrorOnFailure(aReader.ExitContainer(containerType1));
840 1100 : ReturnErrorOnFailure(aReader.ExitContainer(containerType));
841 1100 : const PriorityLevel imp = static_cast<PriorityLevel>(context.mPriority);
842 :
843 1100 : ReclaimEventCtx * const ctx = static_cast<ReclaimEventCtx *>(apAppData);
844 1100 : CircularEventBuffer * const eventBuffer = ctx->mpEventBuffer;
845 1100 : if (eventBuffer->IsFinalDestinationForPriority(imp))
846 : {
847 365 : ChipLogProgress(EventLogging,
848 : "Dropped 1 event from buffer with priority %u and event number 0x" ChipLogFormatX64
849 : " due to overflow: event priority_level: %u",
850 : static_cast<unsigned>(eventBuffer->GetPriority()), ChipLogValueX64(context.mEventNumber),
851 : static_cast<unsigned>(imp));
852 365 : ctx->mSpaceNeededForMovedEvent = 0;
853 365 : return CHIP_NO_ERROR;
854 : }
855 :
856 : // event is not getting dropped. Note how much space it requires, and return.
857 735 : ctx->mSpaceNeededForMovedEvent = aReader.GetLengthRead();
858 735 : return CHIP_END_OF_TLV;
859 : }
860 :
861 345 : void EventManagement::SetScheduledEventInfo(EventNumber & aEventNumber, uint32_t & aInitialWrittenEventBytes) const
862 : {
863 345 : aEventNumber = mLastEventNumber;
864 345 : aInitialWrittenEventBytes = mBytesWritten;
865 345 : }
866 :
867 0 : CHIP_ERROR EventManagement::GenerateEvent(EventLoggingDelegate * eventPayloadWriter, const EventOptions & options,
868 : EventNumber & generatedEventNumber)
869 : {
870 0 : return LogEvent(eventPayloadWriter, options, generatedEventNumber);
871 : }
872 :
873 0 : void EventManagement::ScheduleUrgentEventDeliverySync(std::optional<FabricIndex> fabricIndex)
874 : {
875 0 : mpEventReporter->ScheduleUrgentEventDeliverySync(FromStdOptional(fabricIndex));
876 0 : }
877 :
878 384 : void CircularEventBuffer::Init(uint8_t * apBuffer, uint32_t aBufferLength, CircularEventBuffer * apPrev,
879 : CircularEventBuffer * apNext, PriorityLevel aPriorityLevel)
880 : {
881 384 : TLVCircularBuffer::Init(apBuffer, aBufferLength);
882 384 : mpPrev = apPrev;
883 384 : mpNext = apNext;
884 384 : mPriority = aPriorityLevel;
885 384 : }
886 :
887 5232 : bool CircularEventBuffer::IsFinalDestinationForPriority(PriorityLevel aPriority) const
888 : {
889 5232 : return !((mpNext != nullptr) && (mpNext->mPriority <= aPriority));
890 : }
891 :
892 : /**
893 : * @brief
894 : * TLVCircularBuffer::OnInit can modify the state of the buffer, but we don't want that behavior here.
895 : * We want to make sure we don't change our state, and just report the currently-available space.
896 : */
897 1087 : CHIP_ERROR CircularEventBuffer::OnInit(TLV::TLVWriter & writer, uint8_t *& bufStart, uint32_t & bufLen)
898 : {
899 1087 : GetCurrentWritableBuffer(bufStart, bufLen);
900 1087 : return CHIP_NO_ERROR;
901 : }
902 :
903 944 : void CircularEventReader::Init(CircularEventBufferWrapper * apBufWrapper)
904 : {
905 : CircularEventBuffer * prev;
906 :
907 944 : if (apBufWrapper->mpCurrent == nullptr)
908 0 : return;
909 :
910 944 : TEMPORARY_RETURN_IGNORED TLVReader::Init(*apBufWrapper, apBufWrapper->mpCurrent->DataLength());
911 944 : mMaxLen = apBufWrapper->mpCurrent->DataLength();
912 991 : for (prev = apBufWrapper->mpCurrent->GetPreviousCircularEventBuffer(); prev != nullptr;
913 47 : prev = prev->GetPreviousCircularEventBuffer())
914 : {
915 47 : CircularEventBufferWrapper bufWrapper;
916 47 : bufWrapper.mpCurrent = prev;
917 47 : mMaxLen += prev->DataLength();
918 47 : }
919 : }
920 :
921 2936 : CHIP_ERROR CircularEventBufferWrapper::GetNextBuffer(TLVReader & aReader, const uint8_t *& aBufStart, uint32_t & aBufLen)
922 : {
923 2936 : CHIP_ERROR err = CHIP_NO_ERROR;
924 2936 : TEMPORARY_RETURN_IGNORED mpCurrent->GetNextBuffer(aReader, aBufStart, aBufLen);
925 2936 : SuccessOrExit(err);
926 :
927 2936 : if ((aBufLen == 0) && (mpCurrent->GetPreviousCircularEventBuffer() != nullptr))
928 : {
929 1815 : mpCurrent = mpCurrent->GetPreviousCircularEventBuffer();
930 1815 : aBufStart = nullptr;
931 1815 : err = GetNextBuffer(aReader, aBufStart, aBufLen);
932 : }
933 :
934 1121 : exit:
935 2936 : return err;
936 : }
937 :
938 : } // namespace app
939 : } // namespace chip
|