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 : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
18 :
19 : #include <optional>
20 :
21 : #include <access/AccessControl.h>
22 : #include <access/Privilege.h>
23 : #include <access/RequestPath.h>
24 : #include <app-common/zap-generated/attribute-type.h>
25 : #include <app/AttributeAccessInterface.h>
26 : #include <app/AttributeAccessInterfaceRegistry.h>
27 : #include <app/AttributeValueEncoder.h>
28 : #include <app/RequiredPrivilege.h>
29 : #include <app/data-model/FabricScoped.h>
30 : #include <app/util/af-types.h>
31 : #include <app/util/attribute-metadata.h>
32 : #include <app/util/attribute-storage-detail.h>
33 : #include <app/util/attribute-storage.h>
34 : #include <app/util/ember-io-storage.h>
35 : #include <app/util/endpoint-config-api.h>
36 : #include <data-model-providers/codegen/EmberAttributeDataBuffer.h>
37 : #include <lib/core/CHIPError.h>
38 : #include <lib/support/CodeUtils.h>
39 : #include <lib/support/Span.h>
40 : #include <lib/support/odd-sized-integers.h>
41 :
42 : #include <zap-generated/endpoint_config.h>
43 :
44 : namespace chip {
45 : namespace app {
46 :
47 : namespace {
48 :
49 : using namespace chip::app::Compatibility::Internal;
50 : using Protocols::InteractionModel::Status;
51 :
52 : /// Attempts to read via an attribute access interface (AAI)
53 : ///
54 : /// If it returns a CHIP_ERROR, then this is a FINAL result (i.e. either failure or success).
55 : ///
56 : /// If it returns std::nullopt, then there is no AAI to handle the given path
57 : /// and processing should figure out the value otherwise (generally from other ember data)
58 1804 : std::optional<CHIP_ERROR> TryReadViaAccessInterface(const DataModel::ReadAttributeRequest & request, AttributeAccessInterface * aai,
59 : AttributeValueEncoder & encoder)
60 : {
61 : // Processing can happen only if an attribute access interface actually exists..
62 1804 : if (aai == nullptr)
63 : {
64 41 : return std::nullopt;
65 : }
66 :
67 1763 : CHIP_ERROR err = aai->Read(request, encoder);
68 :
69 3526 : if (err != CHIP_NO_ERROR)
70 : {
71 : // Implementation of 8.4.3.2 of the spec for path expansion
72 140 : if (request.path.mExpanded && (err == CHIP_IM_GLOBAL_STATUS(UnsupportedRead)))
73 : {
74 1 : return CHIP_NO_ERROR;
75 : }
76 :
77 138 : return err;
78 : }
79 :
80 : // If the encoder tried to encode, then a value should have been written.
81 : // - if encode, assume DONE (i.e. FINAL CHIP_NO_ERROR)
82 : // - if no encode, say that processing must continue
83 3248 : return encoder.TriedEncode() ? std::make_optional(CHIP_NO_ERROR) : std::nullopt;
84 : }
85 :
86 : } // namespace
87 :
88 : /// separated-out ReadAttribute implementation (given existing complexity)
89 : ///
90 : /// Generally will:
91 : /// - validate ACL (only for non-internal requests)
92 : /// - Try to read attribute via the AttributeAccessInterface
93 : /// - Try to read the value from ember RAM storage
94 1806 : DataModel::ActionReturnStatus CodegenDataModelProvider::ReadAttribute(const DataModel::ReadAttributeRequest & request,
95 : AttributeValueEncoder & encoder)
96 : {
97 1806 : ChipLogDetail(DataManagement,
98 : "Reading attribute: Cluster=" ChipLogFormatMEI " Endpoint=0x%x AttributeId=" ChipLogFormatMEI " (expanded=%d)",
99 : ChipLogValueMEI(request.path.mClusterId), request.path.mEndpointId, ChipLogValueMEI(request.path.mAttributeId),
100 : request.path.mExpanded);
101 :
102 : // Codegen logic specific: we accept AAI reads BEFORE server cluster interface, so that we are backwards compatible
103 : // in case some application installed AAI before Server Cluster Interfaces were supported
104 : const EmberAfAttributeMetadata * attributeMetadata =
105 1806 : emberAfLocateAttributeMetadata(request.path.mEndpointId, request.path.mClusterId, request.path.mAttributeId);
106 :
107 : // we only allow AAI on ember-registered clusters
108 1806 : if (attributeMetadata != nullptr)
109 : {
110 1804 : std::optional<CHIP_ERROR> aai_result = TryReadViaAccessInterface(
111 1804 : request, AttributeAccessInterfaceRegistry::Instance().Get(request.path.mEndpointId, request.path.mClusterId), encoder);
112 1804 : VerifyOrReturnError(!aai_result.has_value(), *aai_result);
113 : }
114 :
115 43 : if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
116 : {
117 4 : return cluster->ReadAttribute(request, encoder);
118 : }
119 :
120 : // ReadAttribute requirement is that request.path is a VALID path inside the provider
121 : // metadata tree. Clients are supposed to validate this (and data version and other flags)
122 : // This SHOULD NEVER HAPPEN hence the general return code (seemed preferable to VerifyOrDie)
123 39 : VerifyOrReturnError(attributeMetadata != nullptr, Status::Failure);
124 :
125 : // At this point, we have to use ember directly to read the data.
126 : EmberAfAttributeSearchRecord record;
127 39 : record.endpoint = request.path.mEndpointId;
128 39 : record.clusterId = request.path.mClusterId;
129 39 : record.attributeId = request.path.mAttributeId;
130 39 : Protocols::InteractionModel::Status status = emAfReadOrWriteAttribute(
131 39 : &record, &attributeMetadata, gEmberAttributeIOBufferSpan.data(), static_cast<uint16_t>(gEmberAttributeIOBufferSpan.size()),
132 : /* write = */ false);
133 :
134 39 : if (status != Protocols::InteractionModel::Status::Success)
135 : {
136 2 : return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status);
137 : }
138 :
139 37 : VerifyOrReturnError(attributeMetadata != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
140 :
141 37 : MutableByteSpan data = gEmberAttributeIOBufferSpan;
142 37 : Ember::EmberAttributeDataBuffer emberData(attributeMetadata, data);
143 37 : return encoder.Encode(emberData);
144 : }
145 :
146 : } // namespace app
147 : } // namespace chip
|