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 : #include "messaging/ExchangeContext.h"
20 : #include <app/AppConfig.h>
21 : #include <app/InteractionModelEngine.h>
22 : #include <app/MessageDef/EventPathIB.h>
23 : #include <app/StatusResponse.h>
24 : #include <app/WriteHandler.h>
25 : #include <app/reporting/Engine.h>
26 : #include <app/util/MatterCallbacks.h>
27 : #include <credentials/GroupDataProvider.h>
28 : #include <lib/support/TypeTraits.h>
29 :
30 : namespace chip {
31 : namespace app {
32 :
33 : using namespace Protocols::InteractionModel;
34 : using Status = Protocols::InteractionModel::Status;
35 : constexpr uint8_t kListAttributeType = 0x48;
36 :
37 427 : CHIP_ERROR WriteHandler::Init()
38 : {
39 427 : VerifyOrReturnError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
40 :
41 427 : MoveToState(State::Initialized);
42 :
43 427 : mACLCheckCache.ClearValue();
44 427 : mProcessingAttributePath.ClearValue();
45 :
46 427 : return CHIP_NO_ERROR;
47 : }
48 :
49 427 : void WriteHandler::Close()
50 : {
51 427 : VerifyOrReturn(mState != State::Uninitialized);
52 :
53 : // DeliverFinalListWriteEnd will be a no-op if we have called
54 : // DeliverFinalListWriteEnd in success conditions, so passing false for
55 : // wasSuccessful here is safe: if it does anything, we were in fact not
56 : // successful.
57 427 : DeliverFinalListWriteEnd(false /* wasSuccessful */);
58 427 : mExchangeCtx.Release();
59 427 : mSuppressResponse = false;
60 427 : MoveToState(State::Uninitialized);
61 : }
62 :
63 1735 : Status WriteHandler::HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext,
64 : System::PacketBufferHandle && aPayload, bool aIsTimedWrite)
65 : {
66 1735 : System::PacketBufferHandle packet = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
67 1735 : VerifyOrReturnError(!packet.IsNull(), Status::Failure);
68 :
69 1735 : System::PacketBufferTLVWriter messageWriter;
70 1735 : messageWriter.Init(std::move(packet));
71 1735 : VerifyOrReturnError(mWriteResponseBuilder.Init(&messageWriter) == CHIP_NO_ERROR, Status::Failure);
72 :
73 1735 : mWriteResponseBuilder.CreateWriteResponses();
74 1735 : VerifyOrReturnError(mWriteResponseBuilder.GetError() == CHIP_NO_ERROR, Status::Failure);
75 :
76 1735 : Status status = ProcessWriteRequest(std::move(aPayload), aIsTimedWrite);
77 :
78 : // Do not send response on Group Write
79 1735 : if (status == Status::Success && !apExchangeContext->IsGroupExchangeContext())
80 : {
81 1733 : CHIP_ERROR err = SendWriteResponse(std::move(messageWriter));
82 1733 : if (err != CHIP_NO_ERROR)
83 : {
84 0 : status = Status::Failure;
85 : }
86 : }
87 :
88 1735 : return status;
89 1735 : }
90 :
91 427 : Status WriteHandler::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
92 : bool aIsTimedWrite)
93 : {
94 : //
95 : // Let's take over further message processing on this exchange from the IM.
96 : // This is only relevant during chunked requests.
97 : //
98 427 : mExchangeCtx.Grab(apExchangeContext);
99 :
100 427 : Status status = HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), aIsTimedWrite);
101 :
102 : // The write transaction will be alive only when the message was handled successfully and there are more chunks.
103 427 : if (!(status == Status::Success && mHasMoreChunks))
104 : {
105 36 : Close();
106 : }
107 :
108 427 : return status;
109 : }
110 :
111 1309 : CHIP_ERROR WriteHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
112 : System::PacketBufferHandle && aPayload)
113 : {
114 1309 : CHIP_ERROR err = CHIP_NO_ERROR;
115 :
116 1309 : VerifyOrDieWithMsg(apExchangeContext == mExchangeCtx.Get(), DataManagement,
117 : "Incoming exchange context should be same as the initial request.");
118 1309 : VerifyOrDieWithMsg(!apExchangeContext->IsGroupExchangeContext(), DataManagement,
119 : "OnMessageReceived should not be called on GroupExchangeContext");
120 1309 : if (!aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteRequest))
121 : {
122 1 : if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
123 : {
124 0 : CHIP_ERROR statusError = CHIP_NO_ERROR;
125 : // Parse the status response so we can log it properly.
126 0 : StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError);
127 : }
128 1 : ChipLogDetail(DataManagement, "Unexpected message type %d", aPayloadHeader.GetMessageType());
129 1 : StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
130 1 : Close();
131 1 : return CHIP_ERROR_INVALID_MESSAGE_TYPE;
132 : }
133 :
134 : Status status =
135 1308 : HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), false /* chunked write should not be timed write */);
136 1308 : if (status == Status::Success)
137 : {
138 : // We have no more chunks, the write response has been sent in HandleWriteRequestMessage, so close directly.
139 1308 : if (!mHasMoreChunks)
140 : {
141 387 : Close();
142 : }
143 : }
144 : else
145 : {
146 0 : err = StatusResponse::Send(status, apExchangeContext, false /*aExpectResponse*/);
147 0 : Close();
148 : }
149 1308 : return CHIP_NO_ERROR;
150 : }
151 :
152 2 : void WriteHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
153 : {
154 2 : ChipLogError(DataManagement, "Time out! failed to receive status response from Exchange: " ChipLogFormatExchange,
155 : ChipLogValueExchange(apExchangeContext));
156 2 : Close();
157 2 : }
158 :
159 1733 : CHIP_ERROR WriteHandler::FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet)
160 : {
161 1733 : VerifyOrReturnError(mState == State::AddStatus, CHIP_ERROR_INCORRECT_STATE);
162 1733 : ReturnErrorOnFailure(mWriteResponseBuilder.GetWriteResponses().EndOfAttributeStatuses());
163 1733 : ReturnErrorOnFailure(mWriteResponseBuilder.EndOfWriteResponseMessage());
164 1733 : ReturnErrorOnFailure(aMessageWriter.Finalize(&packet));
165 1733 : return CHIP_NO_ERROR;
166 : }
167 :
168 1733 : CHIP_ERROR WriteHandler::SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter)
169 : {
170 1733 : CHIP_ERROR err = CHIP_NO_ERROR;
171 1733 : System::PacketBufferHandle packet;
172 :
173 1733 : VerifyOrExit(mState == State::AddStatus, err = CHIP_ERROR_INCORRECT_STATE);
174 :
175 1733 : err = FinalizeMessage(std::move(aMessageWriter), packet);
176 1733 : SuccessOrExit(err);
177 :
178 1733 : VerifyOrExit(mExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
179 1733 : mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
180 3466 : err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::WriteResponse, std::move(packet),
181 1733 : mHasMoreChunks ? Messaging::SendMessageFlags::kExpectResponse
182 : : Messaging::SendMessageFlags::kNone);
183 1733 : SuccessOrExit(err);
184 :
185 1733 : MoveToState(State::Sending);
186 :
187 1733 : exit:
188 1733 : return err;
189 1733 : }
190 :
191 406 : void WriteHandler::DeliverListWriteBegin(const ConcreteAttributePath & aPath)
192 : {
193 406 : if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
194 : {
195 406 : attrOverride->OnListWriteBegin(aPath);
196 : }
197 406 : }
198 :
199 413 : void WriteHandler::DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful)
200 : {
201 413 : if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
202 : {
203 406 : attrOverride->OnListWriteEnd(aPath, writeWasSuccessful);
204 : }
205 413 : }
206 :
207 848 : void WriteHandler::DeliverFinalListWriteEnd(bool writeWasSuccessful)
208 : {
209 848 : if (mProcessingAttributePath.HasValue() && mProcessingAttributeIsList)
210 : {
211 411 : DeliverListWriteEnd(mProcessingAttributePath.Value(), writeWasSuccessful);
212 : }
213 848 : mProcessingAttributePath.ClearValue();
214 848 : }
215 :
216 0 : CHIP_ERROR WriteHandler::DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful)
217 : {
218 0 : VerifyOrReturnError(mProcessingAttributePath.HasValue() && mProcessingAttributeIsList, CHIP_NO_ERROR);
219 :
220 0 : Credentials::GroupDataProvider::GroupEndpoint mapping;
221 0 : Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
222 : Credentials::GroupDataProvider::EndpointIterator * iterator;
223 :
224 0 : GroupId groupId = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
225 0 : FabricIndex fabricIndex = GetAccessingFabricIndex();
226 :
227 0 : auto processingConcreteAttributePath = mProcessingAttributePath.Value();
228 0 : mProcessingAttributePath.ClearValue();
229 :
230 0 : iterator = groupDataProvider->IterateEndpoints(fabricIndex);
231 0 : VerifyOrReturnError(iterator != nullptr, CHIP_ERROR_NO_MEMORY);
232 :
233 0 : while (iterator->Next(mapping))
234 : {
235 0 : if (groupId != mapping.group_id)
236 : {
237 0 : continue;
238 : }
239 :
240 0 : processingConcreteAttributePath.mEndpointId = mapping.endpoint_id;
241 :
242 0 : if (!InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, processingConcreteAttributePath))
243 : {
244 0 : DeliverListWriteEnd(processingConcreteAttributePath, writeWasSuccessful);
245 : }
246 : }
247 0 : iterator->Release();
248 0 : return CHIP_NO_ERROR;
249 : }
250 : namespace {
251 :
252 : // To reduce the various use of previousProcessed.HasValue() && previousProcessed.Value() == nextAttribute to save code size.
253 6996 : bool IsSameAttribute(const Optional<ConcreteAttributePath> & previousProcessed, const ConcreteDataAttributePath & nextAttribute)
254 : {
255 6996 : return previousProcessed.HasValue() && previousProcessed.Value() == nextAttribute;
256 : }
257 :
258 2480 : bool ShouldReportListWriteEnd(const Optional<ConcreteAttributePath> & previousProcessed, bool previousProcessedAttributeIsList,
259 : const ConcreteDataAttributePath & nextAttribute)
260 : {
261 2480 : return previousProcessedAttributeIsList && !IsSameAttribute(previousProcessed, nextAttribute) && previousProcessed.HasValue();
262 : }
263 :
264 2480 : bool ShouldReportListWriteBegin(const Optional<ConcreteAttributePath> & previousProcessed, bool previousProcessedAttributeIsList,
265 : const ConcreteDataAttributePath & nextAttribute)
266 : {
267 2480 : return !IsSameAttribute(previousProcessed, nextAttribute) && nextAttribute.IsListOperation();
268 : }
269 :
270 : } // namespace
271 :
272 1733 : CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader)
273 : {
274 1733 : CHIP_ERROR err = CHIP_NO_ERROR;
275 :
276 1733 : ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
277 1733 : const Access::SubjectDescriptor subjectDescriptor = mExchangeCtx->GetSessionHandle()->GetSubjectDescriptor();
278 :
279 4221 : while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
280 : {
281 : chip::TLV::TLVReader dataReader;
282 2488 : AttributeDataIB::Parser element;
283 2488 : AttributePathIB::Parser attributePath;
284 2488 : ConcreteDataAttributePath dataAttributePath;
285 2488 : TLV::TLVReader reader = aAttributeDataIBsReader;
286 :
287 2488 : err = element.Init(reader);
288 2488 : SuccessOrExit(err);
289 :
290 2488 : err = element.GetPath(&attributePath);
291 2488 : SuccessOrExit(err);
292 :
293 2488 : err = attributePath.GetConcreteAttributePath(dataAttributePath);
294 2488 : SuccessOrExit(err);
295 :
296 2488 : err = element.GetData(&dataReader);
297 2488 : SuccessOrExit(err);
298 :
299 2488 : const auto attributeMetadata = GetAttributeMetadata(dataAttributePath);
300 2488 : bool currentAttributeIsList = (attributeMetadata != nullptr && attributeMetadata->attributeType == kListAttributeType);
301 :
302 2488 : if (!dataAttributePath.IsListOperation() && currentAttributeIsList)
303 : {
304 411 : dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
305 : }
306 :
307 7028 : if (InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, dataAttributePath) ||
308 : // Per chunking protocol, we are processing the list entries, but the initial empty list is not processed, so we reject
309 : // it with Busy status code.
310 4540 : (dataAttributePath.IsListItemOperation() && !IsSameAttribute(mProcessingAttributePath, dataAttributePath)))
311 : {
312 8 : err = AddStatus(dataAttributePath, StatusIB(Status::Busy));
313 8 : continue;
314 : }
315 :
316 2480 : if (ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
317 : {
318 2 : DeliverListWriteEnd(mProcessingAttributePath.Value(), mAttributeWriteSuccessful);
319 : }
320 :
321 2480 : if (ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
322 : {
323 406 : DeliverListWriteBegin(dataAttributePath);
324 406 : mAttributeWriteSuccessful = true;
325 : }
326 :
327 2480 : mProcessingAttributeIsList = dataAttributePath.IsListOperation();
328 2480 : mProcessingAttributePath.SetValue(dataAttributePath);
329 :
330 2480 : MatterPreAttributeWriteCallback(dataAttributePath);
331 2480 : TLV::TLVWriter backup;
332 2480 : DataVersion version = 0;
333 2480 : mWriteResponseBuilder.GetWriteResponses().Checkpoint(backup);
334 2480 : err = element.GetDataVersion(&version);
335 2480 : if (CHIP_NO_ERROR == err)
336 : {
337 12 : dataAttributePath.mDataVersion.SetValue(version);
338 : }
339 2468 : else if (CHIP_END_OF_TLV == err)
340 : {
341 2468 : err = CHIP_NO_ERROR;
342 : }
343 2480 : SuccessOrExit(err);
344 2480 : err = WriteSingleClusterData(subjectDescriptor, dataAttributePath, dataReader, this);
345 2480 : if (err != CHIP_NO_ERROR)
346 : {
347 10 : mWriteResponseBuilder.GetWriteResponses().Rollback(backup);
348 10 : err = AddStatus(dataAttributePath, StatusIB(err));
349 : }
350 2480 : MatterPostAttributeWriteCallback(dataAttributePath);
351 2480 : SuccessOrExit(err);
352 2488 : }
353 :
354 1733 : if (CHIP_END_OF_TLV == err)
355 : {
356 1733 : err = CHIP_NO_ERROR;
357 : }
358 :
359 1733 : SuccessOrExit(err);
360 :
361 1733 : if (!mHasMoreChunks)
362 : {
363 421 : DeliverFinalListWriteEnd(mAttributeWriteSuccessful);
364 : }
365 :
366 1312 : exit:
367 1733 : return err;
368 : }
369 :
370 0 : CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader)
371 : {
372 0 : CHIP_ERROR err = CHIP_NO_ERROR;
373 :
374 0 : ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
375 : const Access::SubjectDescriptor subjectDescriptor =
376 0 : mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetSubjectDescriptor();
377 :
378 0 : GroupId groupId = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
379 0 : FabricIndex fabric = GetAccessingFabricIndex();
380 :
381 0 : while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
382 : {
383 : chip::TLV::TLVReader dataReader;
384 0 : AttributeDataIB::Parser element;
385 0 : AttributePathIB::Parser attributePath;
386 0 : ConcreteDataAttributePath dataAttributePath;
387 0 : TLV::TLVReader reader = aAttributeDataIBsReader;
388 :
389 0 : Credentials::GroupDataProvider::GroupEndpoint mapping;
390 0 : Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
391 : Credentials::GroupDataProvider::EndpointIterator * iterator;
392 :
393 0 : err = element.Init(reader);
394 0 : SuccessOrExit(err);
395 :
396 0 : err = element.GetPath(&attributePath);
397 0 : SuccessOrExit(err);
398 :
399 0 : err = attributePath.GetGroupAttributePath(dataAttributePath);
400 0 : SuccessOrExit(err);
401 :
402 0 : err = element.GetData(&dataReader);
403 0 : SuccessOrExit(err);
404 :
405 0 : if (!dataAttributePath.IsListOperation() && dataReader.GetType() == TLV::TLVType::kTLVType_Array)
406 : {
407 0 : dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
408 : }
409 :
410 0 : ChipLogDetail(DataManagement,
411 : "Received group attribute write for Group=%u Cluster=" ChipLogFormatMEI " attribute=" ChipLogFormatMEI,
412 : groupId, ChipLogValueMEI(dataAttributePath.mClusterId), ChipLogValueMEI(dataAttributePath.mAttributeId));
413 :
414 0 : iterator = groupDataProvider->IterateEndpoints(fabric);
415 0 : VerifyOrExit(iterator != nullptr, err = CHIP_ERROR_NO_MEMORY);
416 :
417 : bool shouldReportListWriteEnd =
418 0 : ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath);
419 0 : bool shouldReportListWriteBegin = false; // This will be set below.
420 :
421 0 : const EmberAfAttributeMetadata * attributeMetadata = nullptr;
422 :
423 0 : while (iterator->Next(mapping))
424 : {
425 0 : if (groupId != mapping.group_id)
426 : {
427 0 : continue;
428 : }
429 :
430 0 : dataAttributePath.mEndpointId = mapping.endpoint_id;
431 :
432 : // Try to get the metadata from for the attribute from one of the expanded endpoints (it doesn't really matter which
433 : // endpoint we pick, as long as it's valid) and update the path info according to it and recheck if we need to report
434 : // list write begin.
435 0 : if (attributeMetadata == nullptr)
436 : {
437 0 : attributeMetadata = GetAttributeMetadata(dataAttributePath);
438 0 : bool currentAttributeIsList =
439 0 : (attributeMetadata != nullptr && attributeMetadata->attributeType == kListAttributeType);
440 0 : if (!dataAttributePath.IsListOperation() && currentAttributeIsList)
441 : {
442 0 : dataAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
443 : }
444 : ConcreteDataAttributePath pathForCheckingListWriteBegin(kInvalidEndpointId, dataAttributePath.mClusterId,
445 0 : dataAttributePath.mEndpointId, dataAttributePath.mListOp,
446 0 : dataAttributePath.mListIndex);
447 : shouldReportListWriteBegin =
448 0 : ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, pathForCheckingListWriteBegin);
449 0 : }
450 :
451 0 : if (shouldReportListWriteEnd)
452 : {
453 0 : auto processingConcreteAttributePath = mProcessingAttributePath.Value();
454 0 : processingConcreteAttributePath.mEndpointId = mapping.endpoint_id;
455 0 : if (!InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, processingConcreteAttributePath))
456 : {
457 0 : DeliverListWriteEnd(processingConcreteAttributePath, true /* writeWasSuccessful */);
458 : }
459 : }
460 :
461 0 : if (InteractionModelEngine::GetInstance()->HasConflictWriteRequests(this, dataAttributePath))
462 : {
463 0 : ChipLogDetail(DataManagement,
464 : "Writing attribute endpoint=%u Cluster=" ChipLogFormatMEI " attribute=" ChipLogFormatMEI
465 : " is conflict with other write transactions.",
466 : mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
467 : ChipLogValueMEI(dataAttributePath.mAttributeId));
468 0 : continue;
469 : }
470 :
471 0 : if (shouldReportListWriteBegin)
472 : {
473 0 : DeliverListWriteBegin(dataAttributePath);
474 : }
475 :
476 0 : ChipLogDetail(DataManagement,
477 : "Processing group attribute write for endpoint=%u Cluster=" ChipLogFormatMEI
478 : " attribute=" ChipLogFormatMEI,
479 : mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
480 : ChipLogValueMEI(dataAttributePath.mAttributeId));
481 :
482 0 : chip::TLV::TLVReader tmpDataReader(dataReader);
483 :
484 0 : MatterPreAttributeWriteCallback(dataAttributePath);
485 0 : err = WriteSingleClusterData(subjectDescriptor, dataAttributePath, tmpDataReader, this);
486 :
487 0 : if (err != CHIP_NO_ERROR)
488 : {
489 0 : ChipLogError(DataManagement,
490 : "WriteSingleClusterData Endpoint=%u Cluster=" ChipLogFormatMEI " Attribute =" ChipLogFormatMEI
491 : " failed: %" CHIP_ERROR_FORMAT,
492 : mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
493 : ChipLogValueMEI(dataAttributePath.mAttributeId), err.Format());
494 : }
495 0 : MatterPostAttributeWriteCallback(dataAttributePath);
496 : }
497 :
498 0 : dataAttributePath.mEndpointId = kInvalidEndpointId;
499 0 : mProcessingAttributeIsList = dataAttributePath.IsListOperation();
500 0 : mProcessingAttributePath.SetValue(dataAttributePath);
501 0 : iterator->Release();
502 0 : }
503 :
504 0 : if (CHIP_END_OF_TLV == err)
505 : {
506 0 : err = CHIP_NO_ERROR;
507 : }
508 :
509 0 : err = DeliverFinalListWriteEndForGroupWrite(true);
510 :
511 0 : exit:
512 : // The DeliverFinalListWriteEndForGroupWrite above will deliver the successful state of the list write and clear the
513 : // mProcessingAttributePath making the following call no-op. So we call it again after the exit label to deliver a failure state
514 : // to the clusters. Ignore the error code since we need to deliver other more important failures.
515 0 : DeliverFinalListWriteEndForGroupWrite(false);
516 0 : return err;
517 : }
518 :
519 1735 : Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite)
520 : {
521 1735 : CHIP_ERROR err = CHIP_NO_ERROR;
522 1735 : System::PacketBufferTLVReader reader;
523 :
524 1735 : WriteRequestMessage::Parser writeRequestParser;
525 1735 : AttributeDataIBs::Parser AttributeDataIBsParser;
526 : TLV::TLVReader AttributeDataIBsReader;
527 : // Default to InvalidAction for our status; that's what we want if any of
528 : // the parsing of our overall structure or paths fails. Once we have a
529 : // successfully parsed path, the only way we will get a failure return is if
530 : // our path handling fails to AddStatus on us.
531 : //
532 : // TODO: That's not technically InvalidAction, and we should probably make
533 : // our callees hand out Status as well.
534 1735 : Status status = Status::InvalidAction;
535 :
536 1735 : reader.Init(std::move(aPayload));
537 :
538 1735 : err = writeRequestParser.Init(reader);
539 1735 : SuccessOrExit(err);
540 :
541 : #if CHIP_CONFIG_IM_PRETTY_PRINT
542 1735 : writeRequestParser.PrettyPrint();
543 : #endif
544 1735 : err = writeRequestParser.GetSuppressResponse(&mSuppressResponse);
545 1735 : if (err == CHIP_END_OF_TLV)
546 : {
547 4 : err = CHIP_NO_ERROR;
548 : }
549 1735 : SuccessOrExit(err);
550 :
551 1735 : err = writeRequestParser.GetTimedRequest(&mIsTimedRequest);
552 1735 : SuccessOrExit(err);
553 :
554 1735 : err = writeRequestParser.GetMoreChunkedMessages(&mHasMoreChunks);
555 1735 : if (err == CHIP_ERROR_END_OF_TLV)
556 : {
557 4 : err = CHIP_NO_ERROR;
558 : }
559 1735 : SuccessOrExit(err);
560 :
561 1735 : if (mHasMoreChunks && (mExchangeCtx->IsGroupExchangeContext() || mIsTimedRequest))
562 : {
563 : // Sanity check: group exchange context should only have one chunk.
564 : // Also, timed requests should not have more than one chunk.
565 0 : ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_TYPE);
566 : }
567 :
568 1735 : err = writeRequestParser.GetWriteRequests(&AttributeDataIBsParser);
569 1735 : SuccessOrExit(err);
570 :
571 1735 : if (mIsTimedRequest != aIsTimedWrite)
572 : {
573 : // The message thinks it should be part of a timed interaction but it's
574 : // not, or vice versa. Spec says to Respond with UNSUPPORTED_ACCESS.
575 2 : status = Status::UnsupportedAccess;
576 2 : goto exit;
577 : }
578 :
579 1733 : AttributeDataIBsParser.GetReader(&AttributeDataIBsReader);
580 :
581 1733 : if (mExchangeCtx->IsGroupExchangeContext())
582 : {
583 0 : err = ProcessGroupAttributeDataIBs(AttributeDataIBsReader);
584 : }
585 : else
586 : {
587 1733 : err = ProcessAttributeDataIBs(AttributeDataIBsReader);
588 : }
589 1733 : SuccessOrExit(err);
590 1733 : SuccessOrExit(err = writeRequestParser.ExitContainer());
591 :
592 1733 : if (err == CHIP_NO_ERROR)
593 : {
594 1733 : status = Status::Success;
595 : }
596 :
597 0 : exit:
598 1735 : if (err != CHIP_NO_ERROR)
599 : {
600 0 : ChipLogError(DataManagement, "Failed to process write request: %" CHIP_ERROR_FORMAT, err.Format());
601 : }
602 3470 : return status;
603 1735 : }
604 :
605 2476 : CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
606 : {
607 2476 : return AddStatus(aPath, StatusIB(aStatus));
608 : }
609 :
610 0 : CHIP_ERROR WriteHandler::AddClusterSpecificSuccess(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
611 : {
612 0 : return AddStatus(aPath, StatusIB(Status::Success, aClusterStatus));
613 : }
614 :
615 0 : CHIP_ERROR WriteHandler::AddClusterSpecificFailure(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
616 : {
617 0 : return AddStatus(aPath, StatusIB(Status::Failure, aClusterStatus));
618 : }
619 :
620 2494 : CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus)
621 : {
622 2494 : AttributeStatusIBs::Builder & writeResponses = mWriteResponseBuilder.GetWriteResponses();
623 2494 : AttributeStatusIB::Builder & attributeStatusIB = writeResponses.CreateAttributeStatus();
624 :
625 2494 : if (!aStatus.IsSuccess())
626 : {
627 33 : mAttributeWriteSuccessful = false;
628 : }
629 :
630 2494 : ReturnErrorOnFailure(writeResponses.GetError());
631 :
632 2494 : AttributePathIB::Builder & path = attributeStatusIB.CreatePath();
633 2494 : ReturnErrorOnFailure(attributeStatusIB.GetError());
634 2494 : ReturnErrorOnFailure(path.Encode(aPath));
635 :
636 2494 : StatusIB::Builder & statusIBBuilder = attributeStatusIB.CreateErrorStatus();
637 2494 : ReturnErrorOnFailure(attributeStatusIB.GetError());
638 2494 : statusIBBuilder.EncodeStatusIB(aStatus);
639 2494 : ReturnErrorOnFailure(statusIBBuilder.GetError());
640 2494 : ReturnErrorOnFailure(attributeStatusIB.EndOfAttributeStatusIB());
641 :
642 2494 : MoveToState(State::AddStatus);
643 2494 : return CHIP_NO_ERROR;
644 : }
645 :
646 1 : FabricIndex WriteHandler::GetAccessingFabricIndex() const
647 : {
648 1 : return mExchangeCtx->GetSessionHandle()->GetFabricIndex();
649 : }
650 :
651 5081 : const char * WriteHandler::GetStateStr() const
652 : {
653 : #if CHIP_DETAIL_LOGGING
654 5081 : switch (mState)
655 : {
656 427 : case State::Uninitialized:
657 427 : return "Uninitialized";
658 :
659 427 : case State::Initialized:
660 427 : return "Initialized";
661 :
662 2494 : case State::AddStatus:
663 2494 : return "AddStatus";
664 1733 : case State::Sending:
665 1733 : return "Sending";
666 : }
667 : #endif // CHIP_DETAIL_LOGGING
668 0 : return "N/A";
669 : }
670 :
671 5081 : void WriteHandler::MoveToState(const State aTargetState)
672 : {
673 5081 : mState = aTargetState;
674 5081 : ChipLogDetail(DataManagement, "IM WH moving to [%s]", GetStateStr());
675 5081 : }
676 :
677 : } // namespace app
678 : } // namespace chip
679 :
680 2480 : void __attribute__((weak)) MatterPreAttributeWriteCallback(const chip::app::ConcreteAttributePath & attributePath) {}
681 2480 : void __attribute__((weak)) MatterPostAttributeWriteCallback(const chip::app::ConcreteAttributePath & attributePath) {}
|