1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <sstream>
24 #include <string>
25 #include <sys/types.h>
26 #include <type_traits>
27 #include <utils/Errors.h>
28
29 #include <system/audio_effect.h>
30
31 namespace android {
32 namespace effect {
33 namespace utils {
34
35 /**
36 * A wrapper class of legacy effect_param_t to avoid raw pointer read/write everywhere.
37 * The wrapper use the reference of underlying effect_param_t structure, and does not manage
38 * ownership of the structure.
39 * Thread safety is not in consideration in this class for now.
40 */
41 class EffectParamWrapper {
42 public:
EffectParamWrapper(const effect_param_t & param)43 explicit EffectParamWrapper(const effect_param_t& param) : mParam(param) {}
44
45 // validate command size to be at least parameterSize + valueSize after effect_param_t
validateCmdSize(size_t cmdSize)46 bool validateCmdSize(size_t cmdSize) const {
47 return (uint64_t)getPaddedParameterSize() + mParam.vsize + sizeof(effect_param_t) <=
48 cmdSize;
49 }
50
51 /**
52 * validate parameter and value size to be at least equals to the target size.
53 */
validateParamValueSize(size_t paramSize,size_t valueSize)54 bool validateParamValueSize(size_t paramSize, size_t valueSize) const {
55 return mParam.psize >= paramSize && mParam.vsize >= valueSize;
56 }
57
toString()58 std::string toString() const {
59 std::ostringstream os;
60 os << "effect_param_t: { ";
61 os << "status: " << mParam.status << ", p: " << mParam.psize
62 << " (padded: " << getPaddedParameterSize() << "), v: " << mParam.vsize
63 << ", dataAddr: " << &mParam.data;
64 os << "}";
65 return os.str();
66 }
67
copyDataWithCheck(void * dst,const void * src,size_t len,size_t offset,size_t max)68 status_t copyDataWithCheck(void* dst, const void* src, size_t len,
69 size_t offset, size_t max) {
70 if (!dst || !src || len + offset > max) {
71 return BAD_VALUE;
72 }
73 std::memcpy(dst, src, len);
74 return OK;
75 }
76
readFromData(void * buf,size_t len,size_t offset,size_t max)77 status_t readFromData(void* buf, size_t len, size_t offset, size_t max) {
78 return copyDataWithCheck(buf, mParam.data + offset, len, offset, max);
79 }
80
getStatus()81 status_t getStatus() const { return mParam.status; }
getPaddedParameterSize()82 size_t getPaddedParameterSize() const { return padding(mParam.psize); }
getParameterSize()83 size_t getParameterSize() const { return mParam.psize; }
getValueSize()84 size_t getValueSize() const { return mParam.vsize; }
getValueAddress()85 const uint8_t* getValueAddress() const {
86 return (uint8_t*)mParam.data + getPaddedParameterSize();
87 }
88
getTotalSize()89 uint64_t getTotalSize() const {
90 return (uint64_t)sizeof(effect_param_t) + getPaddedParameterSize() + getValueSize();
91 }
92
93 /**
94 * Get reference to effect_param_t.
95 */
getEffectParam()96 const effect_param_t& getEffectParam() const { return mParam; }
97
98 bool operator==(const EffectParamWrapper& other) const {
99 return (&other == this) || 0 == std::memcmp(&mParam, &other.mParam, sizeof(effect_param_t));
100 }
101
102 /**
103 * Padding psize to 32 bits aligned, because "start of value field inside
104 * the data field is always on a 32 bits boundary".
105 */
padding(size_t size)106 static constexpr inline size_t padding(size_t size) {
107 return ((size - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
108 }
109
110 private:
111 /* member with variable sized type at end of class */
112 const effect_param_t& mParam;
113 };
114
115 /**
116 * Reader class of effect_param_t data buffer.
117 */
118 class EffectParamReader : public EffectParamWrapper {
119 public:
EffectParamReader(const effect_param_t & param)120 explicit EffectParamReader(const effect_param_t& param)
121 : EffectParamWrapper(param), mValueROffset(getPaddedParameterSize()) {}
122
123 /**
124 * Read n value of type T from data to buf, mParamROffset will advance n * sizeof(T) if success,
125 * no advance if failure.
126 */
127 template <typename T>
128 status_t readFromParameter(T* buf, size_t n = 1) {
129 size_t len = n * sizeof(T);
130 status_t ret = readFromData(buf, len, mParamROffset /* param offset */,
131 getParameterSize() /* max offset */);
132 if (OK == ret) {
133 mParamROffset += len;
134 }
135 return ret;
136 }
137
138 /**
139 * Read number of value in type T from data to buf, mValueROffset will advance n * sizeof(T) if
140 * success, no advance if failure.
141 */
142 template <typename T>
143 status_t readFromValue(T* buf, size_t n = 1) {
144 size_t len = n * sizeof(T);
145 status_t ret = readFromData(buf, len, mValueROffset /* data offset */,
146 getPaddedParameterSize() + getValueSize() /* max offset */);
147 if (OK == ret) {
148 mValueROffset += len;
149 }
150 return ret;
151 }
152
toString()153 std::string toString() const {
154 std::ostringstream os;
155 os << EffectParamWrapper::toString();
156 os << ", paramROffset: " << mParamROffset;
157 os << ", valueROffset: " << mValueROffset;
158 return os.str();
159 }
160
reset()161 void reset() {
162 mParamROffset = 0;
163 mValueROffset = getPaddedParameterSize();
164 }
165
166 private:
167 size_t mParamROffset = 0;
168 size_t mValueROffset = 0;
169 };
170
171 /**
172 * Writer class of effect_param_t data buffer.
173 */
174 class EffectParamWriter : public EffectParamReader {
175 public:
EffectParamWriter(effect_param_t & param)176 explicit EffectParamWriter(effect_param_t& param)
177 : EffectParamReader(param), mParam(param), mValueWOffset(getPaddedParameterSize()) {
178 }
179
180 /**
181 * Write n number of param in type T from buf to data, mParamWOffset will advance n * sizeof(T)
182 * if success, no advance if failure.
183 */
184 template <typename T>
185 status_t writeToParameter(const T* buf, size_t n = 1) {
186 size_t len = n * sizeof(T);
187 status_t ret = writeToData(buf, len, mParamWOffset /* data offset */,
188 getParameterSize() /* max */);
189 if (OK == ret) {
190 mParamWOffset += len;
191 }
192 return ret;
193 }
194
writeToData(const void * buf,size_t len,size_t offset,size_t max)195 status_t writeToData(const void* buf, size_t len, size_t offset, size_t max) {
196 return copyDataWithCheck(mParam.data + offset, buf, len, offset, max);
197 }
198 /**
199 * Write n number of value in type T from buf to data, mValueWOffset will advance n * sizeof(T)
200 * if success, no advance if failure.
201 */
202 template <typename T>
203 status_t writeToValue(const T* buf, size_t n = 1) {
204 size_t len = n * sizeof(T);
205 status_t ret = writeToData(buf, len, mValueWOffset /* data offset */,
206 getPaddedParameterSize() + getValueSize() /* max */);
207 if (OK == ret) {
208 mValueWOffset += len;
209 }
210 return ret;
211 }
212
213 /**
214 * Set the current value write offset to vsize.
215 * Together with getTotalSize(), can be used by getParameter to set replySize.
216 */
finishValueWrite()217 void finishValueWrite() { mParam.vsize = mValueWOffset - getPaddedParameterSize(); }
218
setStatus(status_t status)219 void setStatus(status_t status) { mParam.status = status; }
220
221 /**
222 * Overwrite the entire effect_param_t with input.
223 */
overwrite(const effect_param_t & param)224 status_t overwrite(const effect_param_t& param) {
225 EffectParamReader reader(param);
226 const auto size = reader.getTotalSize();
227 if (size > getTotalSize()) {
228 return BAD_VALUE;
229 }
230 std::memcpy((void *)&mParam, (void *)&reader.getEffectParam(), size);
231 reset();
232 mValueWOffset += reader.getValueSize();
233 return OK;
234 }
235
236 /**
237 * Reset the offsets with underlying effect_param_t.
238 */
reset()239 void reset() {
240 EffectParamReader::reset();
241 mParamWOffset = 0;
242 mValueWOffset = getPaddedParameterSize();
243 }
244
toString()245 std::string toString() const {
246 std::ostringstream os;
247 os << EffectParamReader::toString();
248 os << ", paramWOffset: " << mParamWOffset;
249 os << ", valueWOffset: " << mValueWOffset;
250 return os.str();
251 }
252
253 private:
254 effect_param_t& mParam;
255 size_t mParamWOffset = 0;
256 size_t mValueWOffset = 0;
257 };
258
ToString(const audio_uuid_t & uuid)259 inline std::string ToString(const audio_uuid_t& uuid) {
260 char str[64];
261 snprintf(str, sizeof(str), "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
262 uuid.timeLow, uuid.timeMid, uuid.timeHiAndVersion, uuid.clockSeq,
263 uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3],
264 uuid.node[4], uuid.node[5]);
265 return str;
266 }
267
268 inline bool operator==(const audio_uuid_t& lhs, const audio_uuid_t& rhs) {
269 return lhs.timeLow == rhs.timeLow && lhs.timeMid == rhs.timeMid &&
270 lhs.timeHiAndVersion == rhs.timeHiAndVersion &&
271 lhs.clockSeq == rhs.clockSeq &&
272 std::memcmp(lhs.node, rhs.node, sizeof(lhs.node)) == 0;
273 }
274
275 inline bool operator!=(const audio_uuid_t& lhs, const audio_uuid_t& rhs) {
276 return !(lhs == rhs);
277 }
278
279 /**
280 * @brief Helper function to write a single parameter (type P) and value (type
281 * V) to effect_param_t, with optional buffer size check.
282 *
283 * Type P and V must be trivially copyable type to ensure safe copying to the
284 * effect_param_t structure.
285 *
286 * Usage:
287 * effect_param_t *param = (effect_param_t *)buf;
288 * if (OK != android::effect::utils::writeToEffectParam(param, p, v)) {
289 * // error handling
290 * }
291 *
292 * @param param The pointer to effect_param_t buffer.
293 * @param p The parameter to write into effect_param_t, 32 bit padded.
294 * @param v The value to write into effect_param_t, start of value field inside
295 * the data field is always on a 32 bits boundary.
296 * @param bufSize OPTIONAL: The size of the buffer pointer to effect_param_t. If
297 * a valid bufSize provide, it will be used to verify if it's big enough to
298 * write both param and value.
299 * @return status_t OK on success, BAD_VALUE on any failure.
300 * Specifically, BAD_VALUE is returned if:
301 * - The `param` pointer is null.
302 * - The `bufSize` is provided and is insufficient to hold the data.
303 */
304 template <typename P, typename V>
requires(std::is_trivially_copyable_v<P> && std::is_trivially_copyable_v<V>)305 requires(std::is_trivially_copyable_v<P> && std::is_trivially_copyable_v<V>)
306 status_t writeToEffectParam(effect_param_t* param, const P p, const V v,
307 size_t bufSize = 0) {
308 const size_t pSize = EffectParamWrapper::padding(sizeof(P)),
309 vSize = sizeof(V);
310 if (!param ||
311 (bufSize != 0 && bufSize < sizeof(effect_param_t) + pSize + vSize)) {
312 return BAD_VALUE;
313 }
314
315 param->psize = pSize;
316 param->vsize = vSize;
317 EffectParamWriter writer(*param);
318
319 status_t ret = writer.writeToParameter(&p);
320 return ret == OK ? writer.writeToValue(&v) : ret;
321 }
322
323 /**
324 * @brief Helper function to read a single parameter (type P) and value (type V)
325 * from effect_param_t.
326 *
327 * Type P and V must be trivially copyable type to ensure safe copying from the
328 * effect_param_t structure.
329 *
330 * Usage:
331 * effect_param_t *param = (effect_param_t *)buf;
332 * if (OK != android::effect::utils::readFromEffectParam(param, &p, &v)) {
333 * // error handling
334 * }
335 *
336 * @param param The pointer to effect_param_t buffer.
337 * @param p The pointer to the return parameter read from effect_param_t.
338 * @param v The pointer to the return value read from effect_param_t.
339 * @return status_t OK on success, BAD_VALUE on any failure.
340 * Specifically, BAD_VALUE is returned if:
341 * - Any of `param`, `p`, or `v` pointers is null.
342 * - The `psize` or `vsize` is smaller than the size of `P` and `V`.
343 *
344 * **Important:** Even in case of an error (return value `BAD_VALUE`), the
345 * memory location pointed to by `p` might be updated.
346 */
347 template <typename P, typename V>
requires(std::is_trivially_copyable_v<P> && std::is_trivially_copyable_v<V>)348 requires(std::is_trivially_copyable_v<P> && std::is_trivially_copyable_v<V>)
349 status_t readFromEffectParam(const effect_param_t* param, P* p, V* v) {
350 if (!param || !p || !v) return BAD_VALUE;
351
352 const size_t pSize = sizeof(P), vSize = sizeof(V);
353 EffectParamReader reader(*param);
354 if (!reader.validateParamValueSize(pSize, vSize)) return BAD_VALUE;
355
356 status_t ret = reader.readFromParameter(p);
357 return ret == OK ? reader.readFromValue(v) : ret;
358 }
359
360 } // namespace utils
361 } // namespace effect
362 } // namespace android
363