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 : #pragma once
20 :
21 : #include <app/AppConfig.h>
22 : #include <app/AttributeAccessToken.h>
23 : #include <app/AttributePathParams.h>
24 : #include <app/ConcreteAttributePath.h>
25 : #include <app/InteractionModelDelegatePointers.h>
26 : #include <app/MessageDef/WriteResponseMessage.h>
27 : #include <app/data-model-provider/ActionReturnStatus.h>
28 : #include <app/data-model-provider/Provider.h>
29 : #include <lib/core/CHIPCore.h>
30 : #include <lib/core/TLVDebug.h>
31 : #include <lib/support/BitFlags.h>
32 : #include <lib/support/CodeUtils.h>
33 : #include <lib/support/DLLUtil.h>
34 : #include <lib/support/logging/CHIPLogging.h>
35 : #include <messaging/ExchangeHolder.h>
36 : #include <messaging/ExchangeMgr.h>
37 : #include <messaging/Flags.h>
38 : #include <protocols/Protocols.h>
39 : #include <protocols/interaction_model/Constants.h>
40 : #include <protocols/interaction_model/StatusCode.h>
41 : #include <system/SystemPacketBuffer.h>
42 : #include <system/TLVPacketBufferBackingStore.h>
43 :
44 : namespace chip {
45 : namespace app {
46 :
47 : class WriteHandler;
48 :
49 : class WriteHandlerDelegate
50 : {
51 : public:
52 61 : virtual ~WriteHandlerDelegate() = default;
53 :
54 : /**
55 : * Returns whether the write operation to the given path is in conflict with another write operation.
56 : * (i.e. another write transaction is in the middle of processing a chunked write to the given path.)
57 : */
58 : virtual bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & aPath) = 0;
59 : };
60 :
61 : /**
62 : * @brief The write handler is responsible for processing a write request and sending a write reply.
63 : */
64 : class WriteHandler : public Messaging::ExchangeDelegate
65 : {
66 : public:
67 240 : WriteHandler() : mExchangeCtx(*this) {}
68 :
69 : /**
70 : * Initialize the WriteHandler. Within the lifetime
71 : * of this instance, this method is invoked once after object
72 : * construction until a call to Close is made to terminate the
73 : * instance.
74 : *
75 : * @param[in] apProvider A valid pointer to the model used to forward writes towards
76 : * @param[in] apWriteHandlerDelegate A Valid pointer to the WriteHandlerDelegate.
77 : *
78 : * @retval #CHIP_ERROR_INVALID_ARGUMENT on invalid pointers
79 : * @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
80 : * kState_NotInitialized.
81 : * @retval #CHIP_NO_ERROR On success.
82 : *
83 : */
84 : CHIP_ERROR Init(DataModel::Provider * apProvider, WriteHandlerDelegate * apWriteHandlerDelegate);
85 :
86 : /**
87 : * Process a write request. Parts of the processing may end up being asynchronous, but the WriteHandler
88 : * guarantees that it will call Close on itself when processing is done (including if OnWriteRequest
89 : * returns an error).
90 : *
91 : * @param[in] apExchangeContext A pointer to the ExchangeContext.
92 : * @param[in] aPayload A payload that has read request data
93 : * @param[in] aIsTimedWrite Whether write is part of a timed interaction.
94 : *
95 : * @retval Status. Callers are expected to send a status response if the
96 : * return status is not Status::Success.
97 : */
98 : Protocols::InteractionModel::Status OnWriteRequest(Messaging::ExchangeContext * apExchangeContext,
99 : System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
100 :
101 : /**
102 : * Clean up state when we are done sending the write response.
103 : */
104 : void Close();
105 :
106 11594 : bool IsFree() const { return mState == State::Uninitialized; }
107 :
108 240 : ~WriteHandler() override = default;
109 :
110 : CHIP_ERROR ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
111 : CHIP_ERROR ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
112 :
113 : CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus);
114 : CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
115 : {
116 : return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus });
117 : }
118 :
119 : CHIP_ERROR AddClusterSpecificSuccess(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
120 : CHIP_ERROR AddClusterSpecificFailure(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
121 :
122 : FabricIndex GetAccessingFabricIndex() const;
123 :
124 : /**
125 : * Check whether the WriteRequest we are handling is a timed write.
126 : */
127 4964 : bool IsTimedWrite() const { return mStateFlags.Has(StateBits::kIsTimedRequest); }
128 :
129 : bool MatchesExchangeContext(Messaging::ExchangeContext * apExchangeContext) const
130 : {
131 : return !IsFree() && mExchangeCtx.Get() == apExchangeContext;
132 : }
133 :
134 : void CacheACLCheckResult(const AttributeAccessToken & aToken) { mACLCheckCache.SetValue(aToken); }
135 :
136 : bool ACLCheckCacheHit(const AttributeAccessToken & aToken)
137 : {
138 : return mACLCheckCache.HasValue() && mACLCheckCache.Value() == aToken;
139 : }
140 :
141 12 : bool IsCurrentlyProcessingWritePath(const ConcreteAttributePath & aPath)
142 : {
143 12 : return mProcessingAttributePath.HasValue() && mProcessingAttributePath.Value() == aPath;
144 : }
145 :
146 : private:
147 : friend class TestWriteInteraction;
148 : enum class State : uint8_t
149 : {
150 : Uninitialized = 0, // The handler has not been initialized
151 : Initialized, // The handler has been initialized and is ready
152 : AddStatus, // The handler has added status code
153 : Sending, // The handler has sent out the write response
154 : };
155 : using Status = Protocols::InteractionModel::Status;
156 : Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
157 : Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
158 : bool aIsTimedWrite);
159 :
160 : CHIP_ERROR FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet);
161 : CHIP_ERROR SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter);
162 :
163 : void MoveToState(const State aTargetState);
164 : const char * GetStateStr() const;
165 :
166 : void DeliverListWriteBegin(const ConcreteAttributePath & aPath);
167 : void DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful);
168 :
169 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
170 : // after handling the last chunk of a series of write requests. Or the write handler was shutdown (usually due to transport
171 : // timeout).
172 : // This function will become no-op on group writes, since DeliverFinalListWriteEndForGroupWrite will clear the
173 : // mProcessingAttributePath after processing the AttributeDataIBs from the request.
174 : void DeliverFinalListWriteEnd(bool writeWasSuccessful);
175 :
176 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
177 : // after handling the last attribute in a group write request (since group writes will never be chunked writes). Or we failed to
178 : // process the group write request (usually due to malformed messages). This function should only be called by
179 : // ProcessGroupAttributeDataIBs.
180 : CHIP_ERROR DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful);
181 :
182 : CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
183 :
184 : // ExchangeDelegate
185 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
186 : System::PacketBufferHandle && aPayload) override;
187 : void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
188 :
189 : /// Validate that a write is acceptable on the given path.
190 : ///
191 : /// Validates that ACL, writability and Timed interaction settings are ok.
192 : ///
193 : /// Returns a success status if all is ok, failure otherwise.
194 : DataModel::ActionReturnStatus CheckWriteAllowed(const Access::SubjectDescriptor & aSubject,
195 : const ConcreteAttributePath & aPath);
196 :
197 : // Write the given data to the given path
198 : CHIP_ERROR WriteClusterData(const Access::SubjectDescriptor & aSubject, const ConcreteDataAttributePath & aPath,
199 : TLV::TLVReader & aData);
200 :
201 : /// Checks whether the given path corresponds to a list attribute
202 : /// Return values:
203 : /// true/false: valid attribute path, known if list or not
204 : /// std::nulloptr - path not available/valid, unknown if attribute is a list or not
205 : std::optional<bool> IsListAttributePath(const ConcreteAttributePath & path);
206 :
207 : Messaging::ExchangeHolder mExchangeCtx;
208 : WriteResponseMessage::Builder mWriteResponseBuilder;
209 : Optional<ConcreteAttributePath> mProcessingAttributePath;
210 : Optional<AttributeAccessToken> mACLCheckCache = NullOptional;
211 :
212 : DataModel::Provider * mDataModelProvider = nullptr;
213 : std::optional<ConcreteAttributePath> mLastSuccessfullyWrittenPath;
214 :
215 : // This may be a "fake" pointer or a real delegate pointer, depending
216 : // on CHIP_CONFIG_STATIC_GLOBAL_INTERACTION_MODEL_ENGINE setting.
217 : //
218 : // When this is not a real pointer, it checks that the value is always
219 : // set to the global InteractionModelEngine and the size of this
220 : // member is 1 byte.
221 : InteractionModelDelegatePointer<WriteHandlerDelegate> mDelegate;
222 :
223 : // bit level enums to save storage for this object. InteractionModelEngine maintains
224 : // several of these objects, so every bit of storage multiplies storage usage.
225 : enum class StateBits : uint8_t
226 : {
227 : kIsTimedRequest = 0x01,
228 : kSuppressResponse = 0x02,
229 : kHasMoreChunks = 0x04,
230 : kProcessingAttributeIsList = 0x08,
231 : // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
232 : // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on
233 : // the fact that the errors that won't be delivered to AttributeAccessInterface are:
234 : // (1) Attribute not found
235 : // (2) Access control failed
236 : // (3) Write request to a read-only attribute
237 : // (4) Data version mismatch
238 : // (5) Not using timed write.
239 : // Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group
240 : // writes.
241 : kAttributeWriteSuccessful = 0x10,
242 : };
243 :
244 : BitFlags<StateBits> mStateFlags;
245 : State mState = State::Uninitialized;
246 : };
247 :
248 : } // namespace app
249 : } // namespace chip
|