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