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 #include <algorithm>
18 #include <regex>
19 #include <type_traits>
20
21 #define LOG_TAG "AidlConversionNdkCpp"
22 #include <utils/Log.h>
23
24 #include <android-base/expected.h>
25 #include <android/binder_auto_utils.h>
26 #include <android/binder_enums.h>
27 #include <android/binder_parcel.h>
28 #include <binder/Enums.h>
29 #include <media/AidlConversionNdkCpp.h>
30 #include <media/AidlConversionUtil.h>
31
32 using aidl::android::aidl_utils::statusTFromBinderStatusT;
33
34 namespace android {
35
36 namespace {
37
isVendorExtension(const std::string & s)38 bool isVendorExtension(const std::string& s) {
39 // Per definition in AudioAttributes.aidl and {Playback|Record}TrackMetadata.aidl
40 static const std::regex vendorExtension("VX_[A-Z0-9]{3,}_[_A-Z0-9]+");
41 return std::regex_match(s.begin(), s.end(), vendorExtension);
42 }
43
isNotVendorExtension(const std::string & s)44 inline bool isNotVendorExtension(const std::string& s) { return !isVendorExtension(s); }
45
filterOutNonVendorTagsInPlace(std::vector<std::string> & tags)46 void filterOutNonVendorTagsInPlace(std::vector<std::string>& tags) {
47 if (std::find_if(tags.begin(), tags.end(), isNotVendorExtension) == tags.end()) {
48 return;
49 }
50 std::vector<std::string> temp;
51 temp.reserve(tags.size());
52 std::copy_if(tags.begin(), tags.end(), std::back_inserter(temp), isVendorExtension);
53 tags = std::move(temp);
54 }
55
56 // cpp2ndk and ndk2cpp are universal converters which work for any type,
57 // however they are not the most efficient way to convert due to extra
58 // marshaling / unmarshaling step.
59
60 template<typename NdkType, typename CppType>
cpp2ndk(const CppType & cpp)61 ConversionResult<NdkType> cpp2ndk(const CppType& cpp) {
62 Parcel cppParcel;
63 RETURN_IF_ERROR(cpp.writeToParcel(&cppParcel));
64 ::ndk::ScopedAParcel ndkParcel(AParcel_create());
65 const int32_t ndkParcelBegin = AParcel_getDataPosition(ndkParcel.get());
66 RETURN_IF_ERROR(statusTFromBinderStatusT(AParcel_unmarshal(
67 ndkParcel.get(), cppParcel.data(), cppParcel.dataSize())));
68 RETURN_IF_ERROR(statusTFromBinderStatusT(AParcel_setDataPosition(
69 ndkParcel.get(), ndkParcelBegin)));
70 NdkType ndk;
71 RETURN_IF_ERROR(statusTFromBinderStatusT(ndk.readFromParcel(ndkParcel.get())));
72 return ndk;
73 }
74
75 template<typename CppType, typename NdkType>
ndk2cpp(const NdkType & ndk)76 ConversionResult<CppType> ndk2cpp(const NdkType& ndk) {
77 ::ndk::ScopedAParcel ndkParcel(AParcel_create());
78 RETURN_IF_ERROR(statusTFromBinderStatusT(ndk.writeToParcel(ndkParcel.get())));
79 const int32_t ndkParcelDataSize = AParcel_getDataSize(ndkParcel.get());
80 if (ndkParcelDataSize < 0) {
81 return base::unexpected(BAD_VALUE);
82 }
83 // Parcel does not expose its data in a mutable form, we have to use an intermediate buffer.
84 std::vector<uint8_t> parcelData(static_cast<size_t>(ndkParcelDataSize));
85 RETURN_IF_ERROR(statusTFromBinderStatusT(AParcel_marshal(
86 ndkParcel.get(), parcelData.data(), 0, ndkParcelDataSize)));
87 Parcel cppParcel;
88 RETURN_IF_ERROR(cppParcel.setData(parcelData.data(), parcelData.size()));
89 CppType cpp;
90 RETURN_IF_ERROR(cpp.readFromParcel(&cppParcel));
91 return cpp;
92 }
93
94 // cpp2ndk_Enum and ndk2cpp_Enum are more efficient implementations specifically for enums.
95
96 template<typename OutEnum, typename OutEnumRange, typename InEnum>
convertEnum(const OutEnumRange & range,InEnum e)97 ConversionResult<OutEnum> convertEnum(const OutEnumRange& range, InEnum e) {
98 using InIntType = std::underlying_type_t<InEnum>;
99 static_assert(std::is_same_v<InIntType, std::underlying_type_t<OutEnum>>);
100
101 InIntType inEnumIndex = static_cast<InIntType>(e);
102 OutEnum outEnum = static_cast<OutEnum>(inEnumIndex);
103 if (std::find(range.begin(), range.end(), outEnum) == range.end()) {
104 return base::unexpected(BAD_VALUE);
105 }
106 return outEnum;
107 }
108
109 template<typename NdkEnum, typename CppEnum>
cpp2ndk_Enum(CppEnum cpp)110 ConversionResult<NdkEnum> cpp2ndk_Enum(CppEnum cpp) {
111 return convertEnum<NdkEnum>(::ndk::enum_range<NdkEnum>(), cpp);
112 }
113
114 template<typename CppEnum, typename NdkEnum>
ndk2cpp_Enum(NdkEnum ndk)115 ConversionResult<CppEnum> ndk2cpp_Enum(NdkEnum ndk) {
116 return convertEnum<CppEnum>(enum_range<CppEnum>(), ndk);
117 }
118
119 } // namespace
120
121 #define GENERATE_CONVERTERS(packageName, className) \
122 GENERATE_CONVERTERS_IMPL(packageName, _, className)
123
124 #define GENERATE_CONVERTERS_IMPL(packageName, prefix, className) \
125 ConversionResult<::aidl::packageName::className> cpp2ndk##prefix##className( \
126 const ::packageName::className& cpp) { \
127 return cpp2ndk<::aidl::packageName::className>(cpp); \
128 } \
129 ConversionResult<::packageName::className> ndk2cpp##prefix##className( \
130 const ::aidl::packageName::className& ndk) { \
131 return ndk2cpp<::packageName::className>(ndk); \
132 }
133
134 #define GENERATE_ENUM_CONVERTERS(packageName, className) \
135 ConversionResult<::aidl::packageName::className> cpp2ndk_##className( \
136 const ::packageName::className& cpp) { \
137 return cpp2ndk_Enum<::aidl::packageName::className>(cpp); \
138 } \
139 ConversionResult<::packageName::className> ndk2cpp_##className( \
140 const ::aidl::packageName::className& ndk) { \
141 return ndk2cpp_Enum<::packageName::className>(ndk); \
142 }
143
144 GENERATE_CONVERTERS(android::media::audio::common, AudioFormatDescription);
145 GENERATE_CONVERTERS_IMPL(android::media::audio::common, _Impl_, AudioHalEngineConfig);
146 GENERATE_CONVERTERS(android::media::audio::common, AudioMMapPolicyInfo);
147 GENERATE_ENUM_CONVERTERS(android::media::audio::common, AudioMMapPolicyType);
148 GENERATE_ENUM_CONVERTERS(android::media::audio::common, AudioMode);
149 GENERATE_CONVERTERS(android::media::audio::common, AudioPort);
150
151 namespace {
152
153 // Filter out all AudioAttributes tags that do not conform to the vendor extension pattern.
154 template<typename T>
filterOutNonVendorTags(T & audioHalEngineConfig)155 void filterOutNonVendorTags(T& audioHalEngineConfig) {
156 for (auto& strategy : audioHalEngineConfig.productStrategies) {
157 for (auto& group : strategy.attributesGroups) {
158 for (auto& attr : group.attributes) {
159 filterOutNonVendorTagsInPlace(attr.tags);
160 }
161 }
162 }
163 }
164
165 } // namespace
166
167 ConversionResult<::aidl::android::media::audio::common::AudioHalEngineConfig>
cpp2ndk_AudioHalEngineConfig(const::android::media::audio::common::AudioHalEngineConfig & cpp)168 cpp2ndk_AudioHalEngineConfig(const ::android::media::audio::common::AudioHalEngineConfig& cpp) {
169 auto conv = cpp2ndk_Impl_AudioHalEngineConfig(cpp);
170 if (conv.ok()) {
171 filterOutNonVendorTags(conv.value());
172 }
173 return conv;
174 }
175
176 ConversionResult<::android::media::audio::common::AudioHalEngineConfig>
ndk2cpp_AudioHalEngineConfig(const::aidl::android::media::audio::common::AudioHalEngineConfig & ndk)177 ndk2cpp_AudioHalEngineConfig(
178 const ::aidl::android::media::audio::common::AudioHalEngineConfig& ndk) {
179 auto conv = ndk2cpp_Impl_AudioHalEngineConfig(ndk);
180 if (conv.ok()) {
181 filterOutNonVendorTags(conv.value());
182 }
183 return conv;
184 }
185
186
187 } // namespace android
188