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