blob: 3c0931b54031b9b04d4717c52b26c74f8a0535c3 [file] [log] [blame]
Boris Zbarskyc1afafe2021-09-17 10:20:56 -04001/*
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#pragma once
20
Yufeng Wang0cbff2c2022-01-27 09:17:20 -080021#include <access/SubjectDescriptor.h>
Boris Zbarsky7fd90f62021-10-15 11:59:25 -040022#include <app/ConcreteAttributePath.h>
Song GUObfdbd452021-12-06 15:53:34 +080023#include <app/MessageDef/AttributeReportIBs.h>
Carol Yangb3f7dd02021-12-20 08:27:44 -080024#include <app/data-model/DecodableList.h>
Yufeng Wang2d0eb942021-11-11 17:36:37 -080025#include <app/data-model/Decode.h>
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -040026#include <app/data-model/Encode.h>
Song GUOf2e7df82021-12-09 23:20:47 +080027#include <app/data-model/FabricScoped.h>
Boris Zbarskyc95449a2021-10-07 02:40:27 -040028#include <app/data-model/List.h> // So we can encode lists
29#include <app/data-model/TagBoundEncoder.h>
Boris Zbarskyc1afafe2021-09-17 10:20:56 -040030#include <app/util/basic-types.h>
31#include <lib/core/CHIPTLV.h>
32#include <lib/core/Optional.h>
Song GUOf2e7df82021-12-09 23:20:47 +080033#include <lib/support/logging/CHIPLogging.h>
Boris Zbarskyc1afafe2021-09-17 10:20:56 -040034
35/**
36 * Callback class that clusters can implement in order to interpose custom
37 * attribute-handling logic. An AttributeAccessInterface instance is associated
38 * with some specific cluster. A single instance may be used for a specific
39 * endpoint or for all endpoints.
40 *
41 * Instances of AttributeAccessInterface that are registered via
42 * registerAttributeAccessOverride will be consulted before taking the normal
43 * attribute access codepath and can use that codepath as a fallback if desired.
44 */
45namespace chip {
46namespace app {
47
Song GUObfdbd452021-12-06 15:53:34 +080048/**
49 * The AttributeReportBuilder is a helper class for filling a single report in AttributeReportIBs.
50 *
51 * Possible usage of AttributeReportBuilder might be:
52 *
53 * AttributeReportBuilder builder;
54 * ReturnErrorOnFailure(builder.PrepareAttribute(...));
55 * ReturnErrorOnFailure(builder.Encode(...));
56 * ReturnErrorOnFailure(builder.FinishAttribute());
57 */
58class AttributeReportBuilder
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -040059{
60public:
Song GUObfdbd452021-12-06 15:53:34 +080061 /**
62 * PrepareAttribute encodes the "header" part of an attribute report including the path and data version.
63 * Path will be encoded according to section 10.5.4.3.1 in the spec.
64 * Note: Only append is supported currently (encode a null list index), other operations won't encode a list index in the
65 * attribute path field.
66 * TODO: Add support for encoding a single element in the list (path with a valid list index).
67 */
68 CHIP_ERROR PrepareAttribute(AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteDataAttributePath & aPath,
69 DataVersion aDataVersion);
70
71 /**
72 * FinishAttribute encodes the "footer" part of an attribute report (it closes the containers opened in PrepareAttribute)
73 */
yunhanw-google299393b2021-12-06 09:59:34 -080074 CHIP_ERROR FinishAttribute(AttributeReportIBs::Builder & aAttributeReportIBs);
Song GUObfdbd452021-12-06 15:53:34 +080075
76 /**
77 * EncodeValue encodes the value field of the report, it should be called exactly once.
78 */
Jerry Johns11a5b142022-02-17 15:21:39 -080079 template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true, typename... Ts>
80 CHIP_ERROR EncodeValue(AttributeReportIBs::Builder & aAttributeReportIBs, T && item, Ts &&... aArgs)
Song GUObfdbd452021-12-06 15:53:34 +080081 {
yunhanw-google299393b2021-12-06 09:59:34 -080082 return DataModel::Encode(*(aAttributeReportIBs.GetAttributeReport().GetAttributeData().GetWriter()),
Jerry Johns11a5b142022-02-17 15:21:39 -080083 TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), item, std::forward<Ts>(aArgs)...);
84 }
85
86 template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true, typename... Ts>
87 CHIP_ERROR EncodeValue(AttributeReportIBs::Builder & aAttributeReportIBs, FabricIndex accessingFabricIndex, T && item,
88 Ts &&... aArgs)
89 {
90 return DataModel::EncodeForRead(*(aAttributeReportIBs.GetAttributeReport().GetAttributeData().GetWriter()),
91 TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), accessingFabricIndex, item);
Song GUObfdbd452021-12-06 15:53:34 +080092 }
Song GUObfdbd452021-12-06 15:53:34 +080093};
94
95/**
96 * The AttributeValueEncoder is a helper class for filling report payloads into AttributeReportIBs.
97 * The attribute value encoder can be initialized with a AttributeEncodeState for saving and recovering its state between encode
98 * sessions (chunkings).
99 *
100 * When Encode returns recoverable errors (e.g. CHIP_ERROR_NO_MEMORY) the state can be used to initialize the AttributeValueEncoder
101 * for future use on the same attribute path.
102 */
103class AttributeValueEncoder
104{
105public:
106 class ListEncodeHelper
107 {
108 public:
109 ListEncodeHelper(AttributeValueEncoder & encoder) : mAttributeValueEncoder(encoder) {}
110
Song GUOf2e7df82021-12-09 23:20:47 +0800111 template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
112 CHIP_ERROR Encode(T && aArg) const
Song GUObfdbd452021-12-06 15:53:34 +0800113 {
Jerry Johns11a5b142022-02-17 15:21:39 -0800114 VerifyOrReturnError(aArg.GetFabricIndex() != kUndefinedFabricIndex, CHIP_ERROR_INVALID_FABRIC_ID);
115
Song GUOe17d7672022-01-22 10:11:27 +0800116 // If we are encoding for a fabric filtered attribute read and the fabric index does not match that present in the
117 // request, skip encoding this list item.
118 VerifyOrReturnError(!mAttributeValueEncoder.mIsFabricFiltered ||
Song GUOede13882022-01-26 06:14:21 +0800119 aArg.GetFabricIndex() == mAttributeValueEncoder.mAccessingFabricIndex,
Song GUOe17d7672022-01-22 10:11:27 +0800120 CHIP_NO_ERROR);
Jerry Johns11a5b142022-02-17 15:21:39 -0800121 return mAttributeValueEncoder.EncodeListItem(mAttributeValueEncoder.mAccessingFabricIndex, std::forward<T>(aArg));
Song GUOf2e7df82021-12-09 23:20:47 +0800122 }
123
124 template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
125 CHIP_ERROR Encode(T && aArg) const
126 {
127 return mAttributeValueEncoder.EncodeListItem(std::forward<T>(aArg));
Song GUObfdbd452021-12-06 15:53:34 +0800128 }
129
130 private:
131 AttributeValueEncoder & mAttributeValueEncoder;
132 };
133
134 class AttributeEncodeState
135 {
136 public:
137 AttributeEncodeState() : mAllowPartialData(false), mCurrentEncodingListIndex(kInvalidListIndex) {}
138 bool AllowPartialData() const { return mAllowPartialData; }
139
140 private:
141 friend class AttributeValueEncoder;
142 /**
143 * When an attempt to encode an attribute returns an error, the buffer may contain tailing dirty data
144 * (since the put was aborted). The report engine normally rolls back the buffer to right before encoding
145 * of the attribute started on errors.
146 *
147 * When chunking a list, EncodeListItem will atomically encode list items, ensuring that the
148 * state of the buffer is valid to send (i.e. contains no trailing garbage), and return an error
149 * if the list doesn't entirely fit. In this situation, mAllowPartialData is set to communicate to the
150 * report engine that it should not roll back the list items.
151 *
152 * TODO: There might be a better name for this variable.
153 */
154 bool mAllowPartialData = false;
155 /**
156 * If set to kInvalidListIndex, indicates that we have not encoded any data for the list yet and
157 * need to start by encoding an empty list before we start encoding any list items.
158 *
159 * When set to a valid ListIndex value, indicates the index of the next list item that needs to be
160 * encoded (i.e. the count of items encoded so far).
161 */
162 ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
163 };
164
165 AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, FabricIndex aAccessingFabricIndex,
Song GUOe17d7672022-01-22 10:11:27 +0800166 const ConcreteAttributePath & aPath, DataVersion aDataVersion, bool aIsFabricFiltered = false,
Song GUObfdbd452021-12-06 15:53:34 +0800167 const AttributeEncodeState & aState = AttributeEncodeState()) :
168 mAttributeReportIBsBuilder(aAttributeReportIBsBuilder),
169 mAccessingFabricIndex(aAccessingFabricIndex), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId),
Song GUOe17d7672022-01-22 10:11:27 +0800170 mDataVersion(aDataVersion), mIsFabricFiltered(aIsFabricFiltered), mEncodeState(aState)
Boris Zbarskyc95449a2021-10-07 02:40:27 -0400171 {}
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400172
Song GUObfdbd452021-12-06 15:53:34 +0800173 /**
Boris Zbarskyc71b4662021-12-07 09:34:58 -0500174 * Encode a single value. This value will not be chunked; it will either be
175 * entirely encoded or fail to be encoded. Consumers are allowed to make
176 * either one call to Encode or one call to EncodeList to handle a read.
Song GUObfdbd452021-12-06 15:53:34 +0800177 */
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400178 template <typename... Ts>
Marc Lepage78719532021-12-06 23:01:50 -0500179 CHIP_ERROR Encode(Ts &&... aArgs)
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400180 {
Boris Zbarsky8af88482021-10-05 09:58:45 -0400181 mTriedEncode = true;
Song GUObfdbd452021-12-06 15:53:34 +0800182 return EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
Boris Zbarskyc95449a2021-10-07 02:40:27 -0400183 }
184
185 /**
Boris Zbarskyc71b4662021-12-07 09:34:58 -0500186 * Encode an explicit null value.
187 */
188 CHIP_ERROR EncodeNull()
189 {
190 // Doesn't matter what type Nullable we use here.
191 return Encode(DataModel::Nullable<uint8_t>());
192 }
193
194 /**
Boris Zbarsky27488482021-12-19 21:17:58 -0500195 * Encode an explicit empty list.
196 */
197 CHIP_ERROR EncodeEmptyList()
198 {
199 // Doesn't matter what type List we use here.
200 return Encode(DataModel::List<uint8_t>());
201 }
202
203 /**
Song GUObfdbd452021-12-06 15:53:34 +0800204 * aCallback is expected to take a const auto & argument and Encode() on it as many times as needed to encode all the list
205 * elements one by one. If any of those Encode() calls returns failure, aCallback must stop encoding and return failure. When
206 * all items are encoded aCallback is expected to return success.
Boris Zbarskyc95449a2021-10-07 02:40:27 -0400207 *
Song GUObfdbd452021-12-06 15:53:34 +0800208 * aCallback may not be called. Consumers must not assume it will be called.
209 *
210 * When EncodeList returns an error, the consumers must abort the encoding, and return the exact error to the caller.
211 *
212 * TODO: Can we hold a error state in the AttributeValueEncoder itself so functions in ember-compatibility-functions don't have
213 * to rely on the above assumption?
214 *
215 * Consumers are allowed to make either one call to EncodeList or one call to Encode to handle a read.
216 *
Boris Zbarskyc95449a2021-10-07 02:40:27 -0400217 */
218 template <typename ListGenerator>
219 CHIP_ERROR EncodeList(ListGenerator aCallback)
220 {
221 mTriedEncode = true;
Song GUObfdbd452021-12-06 15:53:34 +0800222 // Spec 10.5.4.3.1, 10.5.4.6 (Replace a list w/ Multiple IBs)
223 // EmptyList acts as the beginning of the whole array type attribute report.
224 // An empty list is encoded iff both mCurrentEncodingListIndex and mEncodeState.mCurrentEncodingListIndex are invalid
225 // values. After encoding the empty list, mEncodeState.mCurrentEncodingListIndex and mCurrentEncodingListIndex are set to 0.
Boris Zbarsky27488482021-12-19 21:17:58 -0500226 ReturnErrorOnFailure(EnsureListStarted());
Song GUObfdbd452021-12-06 15:53:34 +0800227 ReturnErrorOnFailure(aCallback(ListEncodeHelper(*this)));
228 // The Encode procedure finished without any error, clear the state.
229 mEncodeState = AttributeEncodeState();
230 return CHIP_NO_ERROR;
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400231 }
232
Boris Zbarsky8af88482021-10-05 09:58:45 -0400233 bool TriedEncode() const { return mTriedEncode; }
234
Boris Zbarsky2475f102021-10-29 13:01:22 -0400235 /**
236 * The accessing fabric index for this read or subscribe interaction.
237 */
238 FabricIndex AccessingFabricIndex() const { return mAccessingFabricIndex; }
239
Song GUObfdbd452021-12-06 15:53:34 +0800240 /**
241 * AttributeValueEncoder is a short lived object, and the state is persisted by mEncodeState and restored by constructor.
242 */
243 const AttributeEncodeState & GetState() const { return mEncodeState; }
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400244
245private:
Song GUObfdbd452021-12-06 15:53:34 +0800246 // We made EncodeListItem() private, and ListEncoderHelper will expose it by Encode()
247 friend class ListEncodeHelper;
248
249 template <typename... Ts>
Marc Lepage78719532021-12-06 23:01:50 -0500250 CHIP_ERROR EncodeListItem(Ts &&... aArgs)
Song GUObfdbd452021-12-06 15:53:34 +0800251 {
Boris Zbarsky27488482021-12-19 21:17:58 -0500252 // EncodeListItem must be called after EnsureListStarted(), thus mCurrentEncodingListIndex and
Song GUObfdbd452021-12-06 15:53:34 +0800253 // mEncodeState.mCurrentEncodingListIndex are not invalid values.
254 if (mCurrentEncodingListIndex < mEncodeState.mCurrentEncodingListIndex)
255 {
256 // We have encoded this element in previous chunks, skip it.
257 mCurrentEncodingListIndex++;
258 return CHIP_NO_ERROR;
259 }
260
261 TLV::TLVWriter backup;
262 mAttributeReportIBsBuilder.Checkpoint(backup);
263
264 CHIP_ERROR err = EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
265 if (err != CHIP_NO_ERROR)
266 {
267 // For list chunking, ReportEngine should not rollback the buffer when CHIP_NO_MEMORY or similar error occurred.
268 // However, the error might be raised in the middle of encoding procedure, then the buffer may contain partial data,
269 // unclosed containers etc. This line clears all possible partial data and makes EncodeListItem is atomic.
270 mAttributeReportIBsBuilder.Rollback(backup);
271 return err;
272 }
273
274 mCurrentEncodingListIndex++;
275 mEncodeState.mCurrentEncodingListIndex++;
276 return CHIP_NO_ERROR;
277 }
278
279 /**
Boris Zbarskyc71b4662021-12-07 09:34:58 -0500280 * Builds a single AttributeReportIB in AttributeReportIBs. The caller is
281 * responsible for setting up mPath correctly.
282 *
283 * In particular, when we are encoding a single element in the list, mPath
284 * must indicate a null list index to represent an "append" operation.
285 * operation.
Song GUObfdbd452021-12-06 15:53:34 +0800286 */
287 template <typename... Ts>
Marc Lepage78719532021-12-06 23:01:50 -0500288 CHIP_ERROR EncodeAttributeReportIB(Ts &&... aArgs)
Song GUObfdbd452021-12-06 15:53:34 +0800289 {
Song GUObfdbd452021-12-06 15:53:34 +0800290 AttributeReportBuilder builder;
291
292 ReturnErrorOnFailure(builder.PrepareAttribute(mAttributeReportIBsBuilder, mPath, mDataVersion));
yunhanw-google299393b2021-12-06 09:59:34 -0800293 ReturnErrorOnFailure(builder.EncodeValue(mAttributeReportIBsBuilder, std::forward<Ts>(aArgs)...));
Song GUObfdbd452021-12-06 15:53:34 +0800294
yunhanw-google299393b2021-12-06 09:59:34 -0800295 return builder.FinishAttribute(mAttributeReportIBsBuilder);
Song GUObfdbd452021-12-06 15:53:34 +0800296 }
297
298 /**
Boris Zbarsky27488482021-12-19 21:17:58 -0500299 * EnsureListStarted encodes the first item of one report with lists (an
300 * empty list), as needed.
Song GUObfdbd452021-12-06 15:53:34 +0800301 *
302 * If internal state indicates we have already encoded the empty list, this function will encode nothing, set
303 * mCurrentEncodingListIndex to 0 and return CHIP_NO_ERROR.
Boris Zbarskyc71b4662021-12-07 09:34:58 -0500304 *
305 * In all cases this function guarantees that mPath.mListOp is AppendItem
306 * after it returns, because at that point we will be encoding the list
307 * items.
Song GUObfdbd452021-12-06 15:53:34 +0800308 */
Boris Zbarsky27488482021-12-19 21:17:58 -0500309 CHIP_ERROR EnsureListStarted();
Song GUObfdbd452021-12-06 15:53:34 +0800310
Boris Zbarsky8af88482021-10-05 09:58:45 -0400311 bool mTriedEncode = false;
Song GUObfdbd452021-12-06 15:53:34 +0800312 AttributeReportIBs::Builder & mAttributeReportIBsBuilder;
Boris Zbarsky2475f102021-10-29 13:01:22 -0400313 const FabricIndex mAccessingFabricIndex;
Song GUObfdbd452021-12-06 15:53:34 +0800314 ConcreteDataAttributePath mPath;
315 DataVersion mDataVersion;
Song GUOe17d7672022-01-22 10:11:27 +0800316 bool mIsFabricFiltered = false;
Song GUObfdbd452021-12-06 15:53:34 +0800317 AttributeEncodeState mEncodeState;
318 ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
Boris Zbarsky3e86fbb2021-09-27 18:11:24 -0400319};
320
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800321class AttributeValueDecoder
322{
323public:
Yufeng Wang0cbff2c2022-01-27 09:17:20 -0800324 AttributeValueDecoder(TLV::TLVReader & aReader, const Access::SubjectDescriptor & aSubjectDescriptor) :
325 mReader(aReader), mSubjectDescriptor(aSubjectDescriptor)
Boris Zbarskyb0a79732021-11-22 09:35:47 -0500326 {}
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800327
Song GUO723671f2022-02-01 09:14:59 +0800328 template <typename T, typename std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800329 CHIP_ERROR Decode(T & aArg)
330 {
331 mTriedDecode = true;
332 return DataModel::Decode(mReader, aArg);
333 }
334
Song GUO723671f2022-02-01 09:14:59 +0800335 template <typename T, typename std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
336 CHIP_ERROR Decode(T & aArg)
337 {
338 mTriedDecode = true;
Song GUO08d8b9b2022-02-24 10:33:04 +0800339 // The WriteRequest comes with no fabric index, this will happen when receiving a write request on a PASE session before
340 // AddNOC.
341 VerifyOrReturnError(AccessingFabricIndex() != kUndefinedFabricIndex, CHIP_IM_GLOBAL_STATUS(UnsupportedAccess));
Song GUO723671f2022-02-01 09:14:59 +0800342 ReturnErrorOnFailure(DataModel::Decode(mReader, aArg));
343 aArg.SetFabricIndex(AccessingFabricIndex());
344 return CHIP_NO_ERROR;
345 }
346
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800347 bool TriedDecode() const { return mTriedDecode; }
348
Boris Zbarskyb0a79732021-11-22 09:35:47 -0500349 /**
350 * The accessing fabric index for this write interaction.
351 */
Yufeng Wang0cbff2c2022-01-27 09:17:20 -0800352 FabricIndex AccessingFabricIndex() const { return mSubjectDescriptor.fabricIndex; }
353
354 /**
355 * The accessing subject descriptor for this write interaction.
356 */
357 const Access::SubjectDescriptor & GetSubjectDescriptor() const { return mSubjectDescriptor; }
Boris Zbarskyb0a79732021-11-22 09:35:47 -0500358
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800359private:
360 TLV::TLVReader & mReader;
361 bool mTriedDecode = false;
Yufeng Wang0cbff2c2022-01-27 09:17:20 -0800362 const Access::SubjectDescriptor mSubjectDescriptor;
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800363};
364
Boris Zbarskyc1afafe2021-09-17 10:20:56 -0400365class AttributeAccessInterface
366{
367public:
368 /**
369 * aEndpointId can be Missing to indicate that this object is meant to be
370 * used with all endpoints.
371 */
372 AttributeAccessInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) :
373 mEndpointId(aEndpointId), mClusterId(aClusterId)
374 {}
375 virtual ~AttributeAccessInterface() {}
376
377 /**
378 * Callback for reading attributes.
379 *
Boris Zbarsky7fd90f62021-10-15 11:59:25 -0400380 * @param [in] aPath indicates which exact data is being read.
Boris Zbarsky8af88482021-10-05 09:58:45 -0400381 * @param [in] aEncoder the AttributeValueEncoder to use for encoding the
Boris Zbarskye2fe13a2022-01-25 12:38:14 -0500382 * data.
383 *
384 * The implementation can do one of three things:
385 *
386 * 1) Return a failure. This is treated as a failed read and the error is
387 * returned to the client, by converting it to a StatusIB.
388 * 2) Return success and attempt to encode data using aEncoder. The data is
389 * returned to the client.
390 * 3) Return success and not attempt to encode any data using aEncoder. In
391 * this case, Ember attribute access will happen for the read. This may
392 * involve reading from the attribute store or external attribute
393 * callbacks.
Boris Zbarskyc1afafe2021-09-17 10:20:56 -0400394 */
Boris Zbarsky51580952022-02-09 00:10:12 -0500395 virtual CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) = 0;
Boris Zbarskyc1afafe2021-09-17 10:20:56 -0400396
397 /**
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800398 * Callback for writing attributes.
399 *
400 * @param [in] aPath indicates which exact data is being written.
401 * @param [in] aDecoder the AttributeValueDecoder to use for decoding the
Boris Zbarskye2fe13a2022-01-25 12:38:14 -0500402 * data.
403 *
404 * The implementation can do one of three things:
405 *
406 * 1) Return a failure. This is treated as a failed write and the error is
407 * sent to the client, by converting it to a StatusIB.
408 * 2) Return success and attempt to decode from aDecoder. This is
409 * treated as a successful write.
410 * 3) Return success and not attempt to decode from aDecoder. In
411 * this case, Ember attribute access will happen for the write. This may
412 * involve writing to the attribute store or external attribute
413 * callbacks.
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800414 */
Boris Zbarsky51580952022-02-09 00:10:12 -0500415 virtual CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) { return CHIP_NO_ERROR; }
Yufeng Wang2d0eb942021-11-11 17:36:37 -0800416
417 /**
Song GUO01b51372022-04-08 16:57:04 +0800418 * Indicates the start of a series of list operations. This function will be called before the first Write operation of a series
419 * of consequence attribute data of the same attribute.
420 *
421 * 1) This function will be called if the client tries to set a nullable list attribute to null.
422 * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
423 * of the same attribute.
424 *
425 * @param [in] aPath indicates the path of the modified list.
426 */
427 virtual void OnListWriteBegin(const ConcreteAttributePath & aPath) {}
428
429 /**
430 * Indicates the end of a series of list operations. This function will be called after the last Write operation of a series
431 * of consequence attribute data of the same attribute.
432 *
433 * 1) This function will be called if the client tries to set a nullable list attribute to null.
434 * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
435 * of the same attribute.
436 * 3) When aWriteWasSuccessful is true, the data written must be consistent or the list is untouched.
437 *
438 * @param [in] aPath indicates the path of the modified list
439 * @param [in] aWriteWasSuccessful indicates whether the delivered list is complete.
440 *
441 */
442 virtual void OnListWriteEnd(const ConcreteAttributePath & aPath, bool aWriteWasSuccessful) {}
443
444 /**
Boris Zbarskyc1afafe2021-09-17 10:20:56 -0400445 * Mechanism for keeping track of a chain of AttributeAccessInterfaces.
446 */
447 void SetNext(AttributeAccessInterface * aNext) { mNext = aNext; }
448 AttributeAccessInterface * GetNext() const { return mNext; }
449
450 /**
451 * Check whether a this AttributeAccessInterface is relevant for a
452 * particular endpoint+cluster. An AttributeAccessInterface will be used
453 * for a read from a particular cluster only when this function returns
454 * true.
455 */
456 bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
457 {
458 return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId;
459 }
460
461 /**
462 * Check whether an AttributeAccessInterface is relevant for a particular
463 * specific endpoint. This is used to clean up overrides registered for an
464 * endpoint that becomes disabled.
465 */
Jerry Johns3a7feaa2021-11-09 21:50:37 -0800466 bool MatchesEndpoint(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; }
Boris Zbarskyc1afafe2021-09-17 10:20:56 -0400467
468 /**
469 * Check whether another AttributeAccessInterface wants to handle the same set of
470 * attributes as we do.
471 */
472 bool Matches(const AttributeAccessInterface & aOther) const
473 {
474 return mClusterId == aOther.mClusterId &&
475 (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value());
476 }
477
478private:
479 Optional<EndpointId> mEndpointId;
480 ClusterId mClusterId;
481 AttributeAccessInterface * mNext = nullptr;
482};
483
484} // namespace app
485} // namespace chip