| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <app/ConcreteAttributePath.h> |
| #include <app/MessageDef/AttributeDataElement.h> |
| #include <app/data-model/Encode.h> |
| #include <app/data-model/List.h> // So we can encode lists |
| #include <app/data-model/TagBoundEncoder.h> |
| #include <app/util/basic-types.h> |
| #include <lib/core/CHIPTLV.h> |
| #include <lib/core/Optional.h> |
| |
| /** |
| * Callback class that clusters can implement in order to interpose custom |
| * attribute-handling logic. An AttributeAccessInterface instance is associated |
| * with some specific cluster. A single instance may be used for a specific |
| * endpoint or for all endpoints. |
| * |
| * Instances of AttributeAccessInterface that are registered via |
| * registerAttributeAccessOverride will be consulted before taking the normal |
| * attribute access codepath and can use that codepath as a fallback if desired. |
| */ |
| namespace chip { |
| namespace app { |
| |
| class AttributeValueEncoder : protected TagBoundEncoder |
| { |
| public: |
| AttributeValueEncoder(TLV::TLVWriter * aWriter, FabricIndex aAccessingFabricIndex) : |
| TagBoundEncoder(aWriter, TLV::ContextTag(AttributeDataElement::kCsTag_Data)), mAccessingFabricIndex(aAccessingFabricIndex) |
| {} |
| |
| template <typename... Ts> |
| CHIP_ERROR Encode(Ts... aArgs) |
| { |
| mTriedEncode = true; |
| if (mWriter == nullptr) |
| { |
| return CHIP_NO_ERROR; |
| } |
| return TagBoundEncoder::Encode(std::forward<Ts>(aArgs)...); |
| } |
| |
| /** |
| * aCallback is expected to take a const TagBoundEncoder& argument and |
| * Encode() on it as many times as needed to encode all the list elements |
| * one by one. If any of those Encode() calls returns failure, aCallback |
| * must stop encoding and return failure. When all items are encoded |
| * aCallback is expected to return success. |
| * |
| * aCallback may not be called. Consumers must not assume it will be |
| * called. |
| */ |
| template <typename ListGenerator> |
| CHIP_ERROR EncodeList(ListGenerator aCallback) |
| { |
| mTriedEncode = true; |
| if (mWriter == nullptr) |
| { |
| return CHIP_NO_ERROR; |
| } |
| return TagBoundEncoder::EncodeList(aCallback); |
| } |
| |
| bool TriedEncode() const { return mTriedEncode; } |
| |
| /** |
| * The accessing fabric index for this read or subscribe interaction. |
| */ |
| FabricIndex AccessingFabricIndex() const { return mAccessingFabricIndex; } |
| |
| // For consumers that can't just do a single Encode call for some reason |
| // (e.g. they're encoding a list a bit at a time). |
| TLV::TLVWriter * PrepareManualEncode() |
| { |
| // If this is called, the consumer is trying to encode a value. |
| mTriedEncode = true; |
| return mWriter; |
| } |
| |
| private: |
| bool mTriedEncode = false; |
| const FabricIndex mAccessingFabricIndex; |
| }; |
| |
| class AttributeAccessInterface |
| { |
| public: |
| /** |
| * aEndpointId can be Missing to indicate that this object is meant to be |
| * used with all endpoints. |
| */ |
| AttributeAccessInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) : |
| mEndpointId(aEndpointId), mClusterId(aClusterId) |
| {} |
| virtual ~AttributeAccessInterface() {} |
| |
| /** |
| * Callback for reading attributes. |
| * |
| * @param [in] aPath indicates which exact data is being read. |
| * @param [in] aEncoder the AttributeValueEncoder to use for encoding the |
| * data. If this function returns scucess and no attempt is |
| * made to encode data using aEncoder, the |
| * AttributeAccessInterface did not try to provide any data. In |
| * this case, normal attribute access will happen for the read. |
| * This may involve reading from the attribute store or external |
| * attribute callbacks. |
| */ |
| virtual CHIP_ERROR Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder) = 0; |
| |
| /** |
| * Mechanism for keeping track of a chain of AttributeAccessInterfaces. |
| */ |
| void SetNext(AttributeAccessInterface * aNext) { mNext = aNext; } |
| AttributeAccessInterface * GetNext() const { return mNext; } |
| |
| /** |
| * Check whether a this AttributeAccessInterface is relevant for a |
| * particular endpoint+cluster. An AttributeAccessInterface will be used |
| * for a read from a particular cluster only when this function returns |
| * true. |
| */ |
| bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const |
| { |
| return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId; |
| } |
| |
| /** |
| * Check whether an AttributeAccessInterface is relevant for a particular |
| * specific endpoint. This is used to clean up overrides registered for an |
| * endpoint that becomes disabled. |
| */ |
| bool MatchesExactly(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; } |
| |
| /** |
| * Check whether another AttributeAccessInterface wants to handle the same set of |
| * attributes as we do. |
| */ |
| bool Matches(const AttributeAccessInterface & aOther) const |
| { |
| return mClusterId == aOther.mClusterId && |
| (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value()); |
| } |
| |
| private: |
| Optional<EndpointId> mEndpointId; |
| ClusterId mClusterId; |
| AttributeAccessInterface * mNext = nullptr; |
| }; |
| |
| } // namespace app |
| } // namespace chip |