Line data Source code
1 : /*
2 : * Copyright (c) 2024 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #pragma once
18 :
19 : #include <lib/core/CHIPError.h>
20 : #include <lib/core/TLVReader.h>
21 : #include <lib/core/TLVWriter.h>
22 :
23 : #include <app/AttributeValueDecoder.h>
24 : #include <app/AttributeValueEncoder.h>
25 : #include <app/CommandHandler.h>
26 : #include <app/data-model-provider/AttributeChangeListener.h>
27 :
28 : #include <app/data-model-provider/ActionReturnStatus.h>
29 : #include <app/data-model-provider/Context.h>
30 : #include <app/data-model-provider/OperationTypes.h>
31 : #include <app/data-model-provider/ProviderMetadataTree.h>
32 :
33 : namespace chip {
34 : namespace app {
35 : namespace DataModel {
36 :
37 : /// Represents operations against a matter-defined data model.
38 : ///
39 : /// Class is SINGLE-THREADED:
40 : /// - operations are assumed to only be ever run in a single event-loop
41 : /// thread or equivalent
42 : /// - class is allowed to attempt to cache indexes/locations for faster
43 : /// lookups of things (e.g during iterations)
44 : class Provider : public ProviderMetadataTree
45 : {
46 : public:
47 1170 : ~Provider() override = default;
48 :
49 : // `context` references will be guaranteed valid until Shutdown is called()
50 129 : virtual CHIP_ERROR Startup(InteractionModelContext context) { return CHIP_NO_ERROR; }
51 111 : virtual CHIP_ERROR Shutdown() { return CHIP_NO_ERROR; }
52 :
53 : /// NOTE: this code is NOT required to handle `List` global attributes:
54 : /// AcceptedCommandsList, GeneratedCommandsList OR AttributeList
55 : ///
56 : /// Users of DataModel::Provider are expected to get these lists
57 : /// from ProviderMetadataTree (in particular IM Reads of these
58 : /// attributes will be automatically filled from metadata).
59 : ///
60 : /// When this is invoked, caller is expected to have already done some validations:
61 : /// - `request.path` is a valid path inside the ProviderMetadataTree (an AttributeEntry exists)
62 : /// - Attribute is readable according to the ProviderMetadataTree/AttributeEntry data
63 : /// - Appropriate ACL checks done according to the attribute's AttributeEntry
64 : ///
65 : /// Return value notes:
66 : /// ActionReturnStatus::IsOutOfSpaceEncodingResponse
67 : /// - Indicates that list encoding had insufficient buffer space to encode elements.
68 : /// - encoder::GetState().AllowPartialData() determines if these errors are permanent (no partial
69 : /// data allowed) or further encoding can be retried (AllowPartialData true for list encoding)
70 : virtual ActionReturnStatus ReadAttribute(const ReadAttributeRequest & request, AttributeValueEncoder & encoder) = 0;
71 :
72 : /// Requests a write of an attribute.
73 : ///
74 : /// When this is invoked, caller is expected to have already done some validations:
75 : /// - cluster `data version` has been checked for the incoming request if applicable
76 : /// - validation of ACL/timed interaction flags/writability, if those checks are desired.
77 : /// - `request.path` is a valid path inside the ProviderMetadataTree (an AttributeEntry exists)
78 : /// - Attribute is writable according to the ProviderMetadataTree/AttributeEntry data
79 : /// - Appropriate ACL checks done according to the attribute's AttributeEntry
80 : virtual ActionReturnStatus WriteAttribute(const WriteAttributeRequest & request, AttributeValueDecoder & decoder) = 0;
81 :
82 : /// Indicates the start/end of a series of list operations. This function will be called either before the first
83 : /// Write operation or after the last one of a series of consecutive attribute data of the same attribute.
84 : ///
85 : /// 1) This function will be called if the client tries to set a nullable list attribute to null.
86 : /// 2) This function will only be called at the beginning and end of a series of consecutive attribute data
87 : /// blocks for the same attribute, no matter what list operations those data blocks represent.
88 : /// 3) The opType argument indicates the type of notification (Start, Failure, Success).
89 : virtual void ListAttributeWriteNotification(const ConcreteAttributePath & aPath, ListWriteOperation opType,
90 : FabricIndex accessingFabric) = 0;
91 :
92 : /// `handler` is used to send back the reply.
93 : /// - returning `std::nullopt` means that return value was placed in handler directly.
94 : /// This includes cases where command handling and value return will be done asynchronously.
95 : /// - returning a value other than Success implies an error reply (error and data are mutually exclusive)
96 : ///
97 : /// Preconditions:
98 : /// - `request.path` MUST refer to a command that actually exists. This is because in practice
99 : /// callers must do ACL and flag checks (e.g. for timed invoke) before calling this function.
100 : ///
101 : /// Callers that do not care about those checks should use `ProviderMetadataTree::AcceptedCommands`
102 : /// to check for command existence.
103 : ///
104 : /// - TODO: as interfaces are updated, we may want to make the above requirement more
105 : /// relaxed, as it seems desirable for users of this interface to have guaranteed
106 : /// behavior (like error on invalid paths) whereas today this seems unclear as some
107 : /// command intercepts do not validate that the command is in fact accepted on the
108 : /// endpoint provided.
109 : ///
110 : /// Return value expectations:
111 : /// - if a response has been placed into `handler` then std::nullopt MUST be returned. In particular
112 : /// note that CHIP_NO_ERROR is NOT the same as std::nullopt:
113 : /// > CHIP_NO_ERROR means handler had no status set and we expect the caller to AddStatus(success)
114 : /// > std::nullopt means that handler has added an appropriate data/status response
115 : /// - if a value is returned (not nullopt) then the handler response MUST NOT be filled. The caller
116 : /// will then issue `handler->AddStatus(request.path, <return_value>->GetStatusCode())`. This is a
117 : /// convenience to make writing Invoke calls easier.
118 : virtual std::optional<ActionReturnStatus> InvokeCommand(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments,
119 : CommandHandler * handler) = 0;
120 :
121 : // Attribute Change Listener Management
122 : //
123 : // NOTE:
124 : // - Listeners MAY be unregistered at any time, however implementation assumes the processing
125 : // is single threaded (Register/Unregister/Notify MUST be called from the chip event loop).
126 : void RegisterAttributeChangeListener(AttributeChangeListener & listener);
127 : void UnregisterAttributeChangeListener(AttributeChangeListener & listener);
128 : void NotifyAttributeChanged(const ConcreteAttributePath & path, AttributeChangeType type);
129 : void NotifyEndpointChanged(EndpointId endpointId, EndpointChangeType type);
130 :
131 : private:
132 : /// Represents an active iteration over the listener list.
133 : /// Since listeners can be unregistered during notification, and notifications
134 : /// can be nested, we need to track all active iterators and update them
135 : /// if the element they are about to process is removed.
136 : ///
137 : /// Active iterators are allocated on the stack during Notify* calls and
138 : /// registered in the `mActiveIterators` linked list.
139 : struct ActiveIterator
140 : {
141 : AttributeChangeListener * expectedNext; // The next listener this iterator expects to process
142 : ActiveIterator * nextIterator; // Link to the next active iterator in the stack
143 : };
144 :
145 : AttributeChangeListener * mAttributeChangeListenersHead = nullptr;
146 : ActiveIterator * mActiveIterators = nullptr; // Head of the stack of active iterators
147 : };
148 :
149 : } // namespace DataModel
150 : } // namespace app
151 : } // namespace chip
|