Coverage Report

Created: 2024-12-17 06:44

/src/connectedhomeip/src/app/MessageDef/AttributePathIB.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 *
3
 *    Copyright (c) 2020-2021 Project CHIP Authors
4
 *    Copyright (c) 2018 Google LLC.
5
 *    Copyright (c) 2016-2017 Nest Labs, Inc.
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 "AttributePathIB.h"
20
#include "MessageDefHelper.h"
21
22
#include <inttypes.h>
23
#include <stdarg.h>
24
#include <stdio.h>
25
26
#include <app/AppConfig.h>
27
#include <app/data-model/Encode.h>
28
#include <app/data-model/Nullable.h>
29
30
namespace chip {
31
namespace app {
32
#if CHIP_CONFIG_IM_PRETTY_PRINT
33
CHIP_ERROR AttributePathIB::Parser::PrettyPrint() const
34
0
{
35
0
    CHIP_ERROR err = CHIP_NO_ERROR;
36
0
    TLV::TLVReader reader;
37
38
0
    PRETTY_PRINT("AttributePathIB =");
39
0
    PRETTY_PRINT("{");
40
41
    // make a copy of the Path reader
42
0
    reader.Init(mReader);
43
44
0
    while (CHIP_NO_ERROR == (err = reader.Next()))
45
0
    {
46
0
        if (!TLV::IsContextTag(reader.GetTag()))
47
0
        {
48
0
            continue;
49
0
        }
50
0
        uint32_t tagNum = TLV::TagNumFromTag(reader.GetTag());
51
0
        switch (tagNum)
52
0
        {
53
0
        case to_underlying(Tag::kEnableTagCompression):
54
0
#if CHIP_DETAIL_LOGGING
55
0
        {
56
0
            bool enableTagCompression;
57
0
            ReturnErrorOnFailure(reader.Get(enableTagCompression));
58
0
            PRETTY_PRINT("\tenableTagCompression = %s, ", enableTagCompression ? "true" : "false");
59
0
        }
60
0
#endif // CHIP_DETAIL_LOGGING
61
0
        break;
62
0
        case to_underlying(Tag::kNode):
63
0
            VerifyOrReturnError(TLV::kTLVType_UnsignedInteger == reader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE);
64
65
0
#if CHIP_DETAIL_LOGGING
66
0
            {
67
0
                NodeId node;
68
0
                reader.Get(node);
69
0
                PRETTY_PRINT("\tNode = 0x" ChipLogFormatX64 ",", ChipLogValueX64(node));
70
0
            }
71
0
#endif // CHIP_DETAIL_LOGGING
72
0
            break;
73
0
        case to_underlying(Tag::kEndpoint):
74
0
            VerifyOrReturnError(TLV::kTLVType_UnsignedInteger == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
75
0
#if CHIP_DETAIL_LOGGING
76
0
            {
77
0
                EndpointId endpoint;
78
0
                reader.Get(endpoint);
79
0
                PRETTY_PRINT("\tEndpoint = 0x%x,", endpoint);
80
0
            }
81
0
#endif // CHIP_DETAIL_LOGGING
82
0
            break;
83
0
        case to_underlying(Tag::kCluster):
84
0
            VerifyOrReturnError(TLV::kTLVType_UnsignedInteger == reader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE);
85
86
0
#if CHIP_DETAIL_LOGGING
87
0
            {
88
0
                ClusterId cluster;
89
0
                ReturnErrorOnFailure(reader.Get(cluster));
90
0
                PRETTY_PRINT("\tCluster = 0x%" PRIx32 ",", cluster);
91
0
            }
92
0
#endif // CHIP_DETAIL_LOGGING
93
0
            break;
94
0
        case to_underlying(Tag::kAttribute):
95
0
            VerifyOrReturnError(TLV::kTLVType_UnsignedInteger == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
96
0
#if CHIP_DETAIL_LOGGING
97
0
            {
98
0
                AttributeId attribute;
99
0
                ReturnErrorOnFailure(reader.Get(attribute));
100
0
                PRETTY_PRINT("\tAttribute = " ChipLogFormatMEI ",", ChipLogValueMEI(attribute));
101
0
            }
102
0
#endif // CHIP_DETAIL_LOGGING
103
0
            break;
104
0
        case to_underlying(Tag::kListIndex):
105
0
            VerifyOrReturnError(TLV::kTLVType_UnsignedInteger == reader.GetType() || TLV::kTLVType_Null == reader.GetType(),
106
0
                                CHIP_ERROR_WRONG_TLV_TYPE);
107
0
#if CHIP_DETAIL_LOGGING
108
            // We have checked the element is either uint or null
109
0
            if (TLV::kTLVType_UnsignedInteger == reader.GetType())
110
0
            {
111
0
                uint16_t listIndex;
112
0
                ReturnErrorOnFailure(reader.Get(listIndex));
113
0
                PRETTY_PRINT("\tListIndex = 0x%x,", listIndex);
114
0
            }
115
0
            else
116
0
            {
117
0
                PRETTY_PRINT("\tListIndex = Null,");
118
0
            }
119
0
#endif // CHIP_DETAIL_LOGGING
120
0
            break;
121
0
        default:
122
0
            PRETTY_PRINT("Unknown tag num %" PRIu32, tagNum);
123
0
            break;
124
0
        }
125
0
    }
126
127
0
    PRETTY_PRINT("}");
128
0
    PRETTY_PRINT("\t");
129
    // if we have exhausted this container
130
0
    if (CHIP_END_OF_TLV == err)
131
0
    {
132
0
        err = CHIP_NO_ERROR;
133
0
    }
134
0
    ReturnErrorOnFailure(err);
135
0
    return reader.ExitContainer(mOuterContainerType);
136
0
}
137
#endif // CHIP_CONFIG_IM_PRETTY_PRINT
138
139
CHIP_ERROR AttributePathIB::Parser::GetEnableTagCompression(bool * const apEnableTagCompression) const
140
0
{
141
0
    return GetSimpleValue(to_underlying(Tag::kEnableTagCompression), TLV::kTLVType_Boolean, apEnableTagCompression);
142
0
}
143
144
CHIP_ERROR AttributePathIB::Parser::GetNode(NodeId * const apNode) const
145
0
{
146
0
    return GetUnsignedInteger(to_underlying(Tag::kNode), apNode);
147
0
}
148
149
CHIP_ERROR AttributePathIB::Parser::GetEndpoint(EndpointId * const apEndpoint) const
150
0
{
151
0
    return GetUnsignedInteger(to_underlying(Tag::kEndpoint), apEndpoint);
152
0
}
153
154
CHIP_ERROR AttributePathIB::Parser::GetCluster(ClusterId * const apCluster) const
155
0
{
156
0
    return GetUnsignedInteger(to_underlying(Tag::kCluster), apCluster);
157
0
}
158
159
CHIP_ERROR AttributePathIB::Parser::GetAttribute(AttributeId * const apAttribute) const
160
0
{
161
0
    return GetUnsignedInteger(to_underlying(Tag::kAttribute), apAttribute);
162
0
}
163
164
CHIP_ERROR AttributePathIB::Parser::GetListIndex(ListIndex * const apListIndex) const
165
0
{
166
0
    return GetUnsignedInteger(to_underlying(Tag::kListIndex), apListIndex);
167
0
}
168
169
CHIP_ERROR AttributePathIB::Parser::GetListIndex(DataModel::Nullable<ListIndex> * const apListIndex) const
170
0
{
171
0
    return GetNullableUnsignedInteger(to_underlying(Tag::kListIndex), apListIndex);
172
0
}
173
174
CHIP_ERROR AttributePathIB::Parser::GetGroupAttributePath(ConcreteDataAttributePath & aAttributePath,
175
                                                          ValidateIdRanges aValidateRanges) const
176
0
{
177
0
    ReturnErrorOnFailure(GetCluster(&aAttributePath.mClusterId));
178
0
    ReturnErrorOnFailure(GetAttribute(&aAttributePath.mAttributeId));
179
180
0
    if (aValidateRanges == ValidateIdRanges::kYes)
181
0
    {
182
0
        VerifyOrReturnError(IsValidClusterId(aAttributePath.mClusterId), CHIP_IM_GLOBAL_STATUS(InvalidAction));
183
0
        VerifyOrReturnError(IsValidAttributeId(aAttributePath.mAttributeId), CHIP_IM_GLOBAL_STATUS(InvalidAction));
184
0
    }
185
186
0
    CHIP_ERROR err = CHIP_NO_ERROR;
187
0
    DataModel::Nullable<ListIndex> listIndex;
188
0
    err = GetListIndex(&(listIndex));
189
0
    if (err == CHIP_NO_ERROR)
190
0
    {
191
0
        if (listIndex.IsNull())
192
0
        {
193
0
            aAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::AppendItem;
194
0
        }
195
0
        else
196
0
        {
197
            // TODO: Add ListOperation::ReplaceItem support. (Attribute path with valid list index)
198
0
            err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH_IB;
199
0
        }
200
0
    }
201
0
    else if (CHIP_END_OF_TLV == err)
202
0
    {
203
        // We do not have the context for the actual data type here. We always set the list operation to not list and the users
204
        // should interpret it as ReplaceAll when the attribute type is a list.
205
0
        aAttributePath.mListOp = ConcreteDataAttributePath::ListOperation::NotList;
206
0
        err                    = CHIP_NO_ERROR;
207
0
    }
208
0
    return err;
209
0
}
210
211
CHIP_ERROR AttributePathIB::Parser::GetConcreteAttributePath(ConcreteDataAttributePath & aAttributePath,
212
                                                             ValidateIdRanges aValidateRanges) const
213
0
{
214
0
    ReturnErrorOnFailure(GetGroupAttributePath(aAttributePath, aValidateRanges));
215
216
    // And now read our endpoint.
217
0
    return GetEndpoint(&aAttributePath.mEndpointId);
218
0
}
219
220
CHIP_ERROR AttributePathIB::Parser::ParsePath(AttributePathParams & aAttribute) const
221
0
{
222
0
    CHIP_ERROR err = CHIP_NO_ERROR;
223
224
0
    err = GetEndpoint(&(aAttribute.mEndpointId));
225
0
    if (err == CHIP_NO_ERROR)
226
0
    {
227
0
        VerifyOrReturnError(!aAttribute.HasWildcardEndpointId(), CHIP_IM_GLOBAL_STATUS(InvalidAction));
228
0
    }
229
0
    else if (err == CHIP_END_OF_TLV)
230
0
    {
231
0
        err = CHIP_NO_ERROR;
232
0
    }
233
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_IM_GLOBAL_STATUS(InvalidAction));
234
235
0
    err = GetCluster(&aAttribute.mClusterId);
236
0
    if (err == CHIP_NO_ERROR)
237
0
    {
238
0
        VerifyOrReturnError(IsValidClusterId(aAttribute.mClusterId), CHIP_IM_GLOBAL_STATUS(InvalidAction));
239
0
    }
240
0
    else if (err == CHIP_END_OF_TLV)
241
0
    {
242
0
        err = CHIP_NO_ERROR;
243
0
    }
244
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_IM_GLOBAL_STATUS(InvalidAction));
245
246
0
    err = GetAttribute(&aAttribute.mAttributeId);
247
0
    if (err == CHIP_NO_ERROR)
248
0
    {
249
0
        VerifyOrReturnError(IsValidAttributeId(aAttribute.mAttributeId), CHIP_IM_GLOBAL_STATUS(InvalidAction));
250
0
    }
251
0
    else if (err == CHIP_END_OF_TLV)
252
0
    {
253
0
        err = CHIP_NO_ERROR;
254
0
    }
255
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_IM_GLOBAL_STATUS(InvalidAction));
256
257
    // A wildcard cluster requires that the attribute path either be
258
    // wildcard or a global attribute.
259
0
    VerifyOrReturnError(!aAttribute.HasWildcardClusterId() || aAttribute.HasWildcardAttributeId() ||
260
0
                            IsGlobalAttribute(aAttribute.mAttributeId),
261
0
                        CHIP_IM_GLOBAL_STATUS(InvalidAction));
262
263
0
    err = GetListIndex(&aAttribute.mListIndex);
264
0
    if (err == CHIP_NO_ERROR)
265
0
    {
266
0
        VerifyOrReturnError(!aAttribute.HasWildcardAttributeId() && !aAttribute.HasWildcardListIndex(),
267
0
                            CHIP_IM_GLOBAL_STATUS(InvalidAction));
268
0
    }
269
0
    else if (err == CHIP_END_OF_TLV)
270
0
    {
271
0
        err = CHIP_NO_ERROR;
272
0
    }
273
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_IM_GLOBAL_STATUS(InvalidAction));
274
0
    return CHIP_NO_ERROR;
275
0
}
276
277
AttributePathIB::Builder & AttributePathIB::Builder::EnableTagCompression(const bool aEnableTagCompression)
278
0
{
279
    // skip if error has already been set
280
0
    if (mError == CHIP_NO_ERROR)
281
0
    {
282
0
        mError = mpWriter->PutBoolean(TLV::ContextTag(Tag::kEnableTagCompression), aEnableTagCompression);
283
0
    }
284
0
    return *this;
285
0
}
286
287
AttributePathIB::Builder & AttributePathIB::Builder::Node(const NodeId aNode)
288
0
{
289
    // skip if error has already been set
290
0
    if (mError == CHIP_NO_ERROR)
291
0
    {
292
0
        mError = mpWriter->Put(TLV::ContextTag(Tag::kNode), aNode);
293
0
    }
294
0
    return *this;
295
0
}
296
297
AttributePathIB::Builder & AttributePathIB::Builder::Endpoint(const EndpointId aEndpoint)
298
0
{
299
    // skip if error has already been set
300
0
    if (mError == CHIP_NO_ERROR)
301
0
    {
302
0
        mError = mpWriter->Put(TLV::ContextTag(Tag::kEndpoint), aEndpoint);
303
0
    }
304
0
    return *this;
305
0
}
306
307
AttributePathIB::Builder & AttributePathIB::Builder::Cluster(const ClusterId aCluster)
308
0
{
309
    // skip if error has already been set
310
0
    if (mError == CHIP_NO_ERROR)
311
0
    {
312
0
        mError = mpWriter->Put(TLV::ContextTag(Tag::kCluster), aCluster);
313
0
    }
314
0
    return *this;
315
0
}
316
317
AttributePathIB::Builder & AttributePathIB::Builder::Attribute(const AttributeId aAttribute)
318
0
{
319
    // skip if error has already been set
320
0
    if (mError == CHIP_NO_ERROR)
321
0
    {
322
0
        mError = mpWriter->Put(TLV::ContextTag(Tag::kAttribute), aAttribute);
323
0
    }
324
0
    return *this;
325
0
}
326
327
AttributePathIB::Builder & AttributePathIB::Builder::ListIndex(const DataModel::Nullable<chip::ListIndex> & aListIndex)
328
0
{
329
    // skip if error has already been set
330
0
    if (mError == CHIP_NO_ERROR)
331
0
    {
332
0
        mError = DataModel::Encode(*mpWriter, TLV::ContextTag(Tag::kListIndex), aListIndex);
333
0
    }
334
0
    return *this;
335
0
}
336
337
AttributePathIB::Builder & AttributePathIB::Builder::ListIndex(const chip::ListIndex aListIndex)
338
0
{
339
    // skip if error has already been set
340
0
    if (mError == CHIP_NO_ERROR)
341
0
    {
342
0
        mError = mpWriter->Put(TLV::ContextTag(Tag::kListIndex), aListIndex);
343
0
    }
344
0
    return *this;
345
0
}
346
347
CHIP_ERROR AttributePathIB::Builder::EndOfAttributePathIB()
348
0
{
349
0
    EndOfContainer();
350
0
    return GetError();
351
0
}
352
353
CHIP_ERROR AttributePathIB::Builder::Encode(const AttributePathParams & aAttributePathParams)
354
0
{
355
0
    if (!(aAttributePathParams.HasWildcardEndpointId()))
356
0
    {
357
0
        Endpoint(aAttributePathParams.mEndpointId);
358
0
    }
359
360
0
    if (!(aAttributePathParams.HasWildcardClusterId()))
361
0
    {
362
0
        Cluster(aAttributePathParams.mClusterId);
363
0
    }
364
365
0
    if (!(aAttributePathParams.HasWildcardAttributeId()))
366
0
    {
367
0
        Attribute(aAttributePathParams.mAttributeId);
368
0
    }
369
370
0
    if (!(aAttributePathParams.HasWildcardListIndex()))
371
0
    {
372
0
        ListIndex(aAttributePathParams.mListIndex);
373
0
    }
374
375
0
    return EndOfAttributePathIB();
376
0
}
377
378
CHIP_ERROR AttributePathIB::Builder::Encode(const ConcreteDataAttributePath & aAttributePath)
379
0
{
380
0
    Endpoint(aAttributePath.mEndpointId);
381
0
    Cluster(aAttributePath.mClusterId);
382
0
    Attribute(aAttributePath.mAttributeId);
383
384
0
    if (!aAttributePath.IsListOperation() || aAttributePath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll)
385
0
    {
386
        /* noop */
387
0
    }
388
0
    else if (aAttributePath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
389
0
    {
390
0
        ListIndex(DataModel::NullNullable);
391
0
    }
392
0
    else
393
0
    {
394
        // TODO: Add ListOperation::ReplaceItem support. (Attribute path with valid list index)
395
0
        return CHIP_ERROR_INVALID_ARGUMENT;
396
0
    }
397
398
0
    return EndOfAttributePathIB();
399
0
}
400
401
} // namespace app
402
} // namespace chip