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 : #include <app/AttributePathExpandIterator.h>
20 :
21 : #include <app/AttributePathParams.h>
22 : #include <app/ConcreteAttributePath.h>
23 : #include <app/EventManagement.h>
24 : #include <app/GlobalAttributes.h>
25 : #include <app/att-storage.h>
26 : #include <lib/core/CHIPCore.h>
27 : #include <lib/core/TLVDebug.h>
28 : #include <lib/support/CodeUtils.h>
29 : #include <lib/support/DLLUtil.h>
30 : #include <lib/support/logging/CHIPLogging.h>
31 :
32 : using namespace chip;
33 :
34 : // TODO: Need to make it so that declarations of things that don't depend on generated files are not intermixed in af.h with
35 : // dependencies on generated files, so we don't have to re-declare things here.
36 : // Note: Some of the generated files that depended by af.h are gen_config.h and gen_tokens.h
37 : typedef uint8_t EmberAfClusterMask;
38 :
39 : extern uint16_t emberAfEndpointCount();
40 : extern uint16_t emberAfIndexFromEndpoint(EndpointId endpoint);
41 : extern uint8_t emberAfClusterCount(EndpointId endpoint, bool server);
42 : extern uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster);
43 : extern uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster,
44 : chip::AttributeId attributeId);
45 : extern chip::EndpointId emberAfEndpointFromIndex(uint16_t index);
46 : extern Optional<ClusterId> emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server);
47 : extern Optional<AttributeId> emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster,
48 : uint16_t attributeIndex);
49 : extern uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask);
50 : extern bool emberAfEndpointIndexIsEnabled(uint16_t index);
51 :
52 : namespace chip {
53 : namespace app {
54 :
55 2847 : AttributePathExpandIterator::AttributePathExpandIterator(ObjectList<AttributePathParams> * aAttributePath)
56 : {
57 2847 : mpAttributePath = aAttributePath;
58 :
59 : // Reset iterator state
60 2847 : mEndpointIndex = UINT16_MAX;
61 2847 : mClusterIndex = UINT8_MAX;
62 2847 : mAttributeIndex = UINT16_MAX;
63 :
64 : static_assert(std::numeric_limits<decltype(mGlobalAttributeIndex)>::max() >= ArraySize(GlobalAttributesNotInMetadata),
65 : "Our index won't be able to hold the value we need to hold.");
66 : static_assert(std::is_same<decltype(mGlobalAttributeIndex), uint8_t>::value,
67 : "If this changes audit all uses where we set to UINT8_MAX");
68 2847 : mGlobalAttributeIndex = UINT8_MAX;
69 :
70 : // Make the iterator ready to emit the first valid path in the list.
71 2847 : Next();
72 2847 : }
73 :
74 488 : void AttributePathExpandIterator::PrepareEndpointIndexRange(const AttributePathParams & aAttributePath)
75 : {
76 488 : if (aAttributePath.HasWildcardEndpointId())
77 : {
78 60 : mEndpointIndex = 0;
79 60 : mEndEndpointIndex = emberAfEndpointCount();
80 : }
81 : else
82 : {
83 428 : mEndpointIndex = emberAfIndexFromEndpoint(aAttributePath.mEndpointId);
84 : // If the given cluster id does not exist on the given endpoint, it will return uint16(0xFFFF), then endEndpointIndex
85 : // will be 0, means we should iterate a null endpoint set (skip it).
86 428 : mEndEndpointIndex = static_cast<uint16_t>(mEndpointIndex + 1);
87 : }
88 488 : }
89 :
90 574 : void AttributePathExpandIterator::PrepareClusterIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId)
91 : {
92 574 : if (aAttributePath.HasWildcardClusterId())
93 : {
94 88 : mClusterIndex = 0;
95 88 : mEndClusterIndex = emberAfClusterCount(aEndpointId, true /* server */);
96 : }
97 : else
98 : {
99 486 : mClusterIndex = emberAfClusterIndex(aEndpointId, aAttributePath.mClusterId, CLUSTER_MASK_SERVER);
100 : // If the given cluster id does not exist on the given endpoint, it will return uint8(0xFF), then endClusterIndex
101 : // will be 0, means we should iterate a null cluster set (skip it).
102 486 : mEndClusterIndex = static_cast<uint8_t>(mClusterIndex + 1);
103 : }
104 574 : }
105 :
106 641 : void AttributePathExpandIterator::PrepareAttributeIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId,
107 : ClusterId aClusterId)
108 : {
109 641 : if (aAttributePath.HasWildcardAttributeId())
110 : {
111 594 : mAttributeIndex = 0;
112 594 : mEndAttributeIndex = emberAfGetServerAttributeCount(aEndpointId, aClusterId);
113 594 : mGlobalAttributeIndex = 0;
114 594 : mGlobalAttributeEndIndex = ArraySize(GlobalAttributesNotInMetadata);
115 : }
116 : else
117 : {
118 47 : mAttributeIndex = emberAfGetServerAttributeIndexByAttributeId(aEndpointId, aClusterId, aAttributePath.mAttributeId);
119 : // If the given attribute id does not exist on the given endpoint, it will return uint16(0xFFFF), then endAttributeIndex
120 : // will be 0, means we should iterate a null attribute set (skip it).
121 47 : mEndAttributeIndex = static_cast<uint16_t>(mAttributeIndex + 1);
122 47 : if (mAttributeIndex == UINT16_MAX)
123 : {
124 : // Check whether this is a non-metadata global attribute.
125 : //
126 : // Default to the max value, which will correspond (after we add 1
127 : // and overflow to 0 for the max index) to us not going through
128 : // non-metadata global attributes for this attribute.
129 17 : mGlobalAttributeIndex = UINT8_MAX;
130 :
131 : static_assert(ArraySize(GlobalAttributesNotInMetadata) <= UINT8_MAX, "Iterating over at most 256 array entries");
132 :
133 17 : const uint8_t arraySize = static_cast<uint8_t>(ArraySize(GlobalAttributesNotInMetadata));
134 64 : for (uint8_t idx = 0; idx < arraySize; ++idx)
135 : {
136 51 : if (GlobalAttributesNotInMetadata[idx] == aAttributePath.mAttributeId)
137 : {
138 4 : mGlobalAttributeIndex = idx;
139 4 : break;
140 : }
141 : }
142 17 : mGlobalAttributeEndIndex = static_cast<uint8_t>(mGlobalAttributeIndex + 1);
143 : }
144 : else
145 : {
146 30 : mGlobalAttributeIndex = UINT8_MAX;
147 30 : mGlobalAttributeEndIndex = 0;
148 : }
149 : }
150 641 : }
151 :
152 263 : void AttributePathExpandIterator::ResetCurrentCluster()
153 : {
154 : // If this is a null iterator, or the attribute id of current cluster info is not a wildcard attribute id, then this function
155 : // will do nothing, since we won't be expanding the wildcard attribute ids under a cluster.
156 263 : VerifyOrReturn(mpAttributePath != nullptr && mpAttributePath->mValue.HasWildcardAttributeId());
157 :
158 : // Otherwise, we will reset the index for iterating the attributes, so we report the attributes for this cluster again. This
159 : // will ensure that the client sees a coherent view of the cluster from the reports generated by a single (wildcard) attribute
160 : // path in the request.
161 : //
162 : // Note that when Next() returns, we must be in one of the following states:
163 : // - This is not a wildcard path
164 : // - We just expanded some attribute id field
165 : // - We have exhausted all paths
166 : // Only the second case will happen here since the above check will fail for 1 and 3, so the following Next() call must result
167 : // in a valid path, which is the first attribute id we will emit for the current cluster.
168 3 : mAttributeIndex = UINT16_MAX;
169 3 : mGlobalAttributeIndex = UINT8_MAX;
170 3 : Next();
171 : }
172 :
173 6754 : bool AttributePathExpandIterator::Next()
174 : {
175 7963 : for (; mpAttributePath != nullptr; (mpAttributePath = mpAttributePath->mpNext, mEndpointIndex = UINT16_MAX))
176 : {
177 6033 : mOutputPath.mExpanded = mpAttributePath->mValue.IsWildcardPath();
178 :
179 6033 : if (mEndpointIndex == UINT16_MAX)
180 : {
181 : // Special case: If this is a concrete path, we just return its value as-is.
182 2126 : if (!mpAttributePath->mValue.IsWildcardPath())
183 : {
184 1638 : mOutputPath.mEndpointId = mpAttributePath->mValue.mEndpointId;
185 1638 : mOutputPath.mClusterId = mpAttributePath->mValue.mClusterId;
186 1638 : mOutputPath.mAttributeId = mpAttributePath->mValue.mAttributeId;
187 :
188 : // Prepare for next iteration
189 1638 : mEndpointIndex = mEndEndpointIndex = 0;
190 1638 : return true;
191 : }
192 :
193 488 : PrepareEndpointIndexRange(mpAttributePath->mValue);
194 488 : mClusterIndex = UINT8_MAX;
195 : }
196 :
197 4766 : for (; mEndpointIndex < mEndEndpointIndex;
198 371 : (mEndpointIndex++, mClusterIndex = UINT8_MAX, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX))
199 : {
200 3557 : if (!emberAfEndpointIndexIsEnabled(mEndpointIndex))
201 : {
202 : // Not an enabled endpoint; skip it.
203 23 : continue;
204 : }
205 :
206 3534 : EndpointId endpointId = emberAfEndpointFromIndex(mEndpointIndex);
207 :
208 3534 : if (mClusterIndex == UINT8_MAX)
209 : {
210 574 : PrepareClusterIndexRange(mpAttributePath->mValue, endpointId);
211 574 : mAttributeIndex = UINT16_MAX;
212 574 : mGlobalAttributeIndex = UINT8_MAX;
213 : }
214 :
215 3946 : for (; mClusterIndex < mEndClusterIndex;
216 412 : (mClusterIndex++, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX))
217 : {
218 : // emberAfGetNthClusterId must return a valid cluster id here since we have verified the mClusterIndex does
219 : // not exceed the mEndClusterIndex.
220 3598 : ClusterId clusterId = emberAfGetNthClusterId(endpointId, mClusterIndex, true /* server */).Value();
221 3598 : if (mAttributeIndex == UINT16_MAX && mGlobalAttributeIndex == UINT8_MAX)
222 : {
223 641 : PrepareAttributeIndexRange(mpAttributePath->mValue, endpointId, clusterId);
224 : }
225 :
226 3598 : if (mAttributeIndex < mEndAttributeIndex)
227 : {
228 : // GetServerAttributeIdByIdex must return a valid attribute here since we have verified the mAttributeIndex does
229 : // not exceed the mEndAttributeIndex.
230 2060 : mOutputPath.mAttributeId = emberAfGetServerAttributeIdByIndex(endpointId, clusterId, mAttributeIndex).Value();
231 2060 : mOutputPath.mClusterId = clusterId;
232 2060 : mOutputPath.mEndpointId = endpointId;
233 2060 : mAttributeIndex++;
234 : // We found a valid attribute path, now return and increase the attribute index for next iteration.
235 : // Return true will skip the increment of mClusterIndex, mEndpointIndex and mpAttributePath.
236 2060 : return true;
237 : }
238 1538 : if (mGlobalAttributeIndex < mGlobalAttributeEndIndex)
239 : {
240 : // Return a path pointing to the next global attribute.
241 1126 : mOutputPath.mAttributeId = GlobalAttributesNotInMetadata[mGlobalAttributeIndex];
242 1126 : mOutputPath.mClusterId = clusterId;
243 1126 : mOutputPath.mEndpointId = endpointId;
244 1126 : mGlobalAttributeIndex++;
245 1126 : return true;
246 : }
247 : // We have exhausted all attributes of this cluster, continue iterating over attributes of next cluster.
248 : }
249 : // We have exhausted all clusters of this endpoint, continue iterating over clusters of next endpoint.
250 : }
251 : // We have exhausted all endpoints in this cluster info, continue iterating over next cluster info item.
252 : }
253 :
254 : // Reset to default, invalid value.
255 1930 : mOutputPath = ConcreteReadAttributePath();
256 1930 : return false;
257 : }
258 : } // namespace app
259 : } // namespace chip
|