1 /*
2  * Copyright (C) 2020 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 <aidl/android/hardware/common/fmq/MQDescriptor.h>
20 #include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h>
21 #include <aidl/android/hardware/common/fmq/UnsynchronizedWrite.h>
22 #include <cutils/native_handle.h>
23 #include <fmq/AidlMQDescriptorShim.h>
24 #include <fmq/MessageQueueBase.h>
25 #include <utils/Log.h>
26 #include <type_traits>
27 
28 namespace android {
29 
30 using aidl::android::hardware::common::fmq::MQDescriptor;
31 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
32 using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
33 using android::details::AidlMQDescriptorShim;
34 using android::hardware::MQFlavor;
35 
36 template <typename T>
37 struct FlavorTypeToValue;
38 
39 template <>
40 struct FlavorTypeToValue<SynchronizedReadWrite> {
41     static constexpr MQFlavor value = hardware::kSynchronizedReadWrite;
42 };
43 
44 template <>
45 struct FlavorTypeToValue<UnsynchronizedWrite> {
46     static constexpr MQFlavor value = hardware::kUnsynchronizedWrite;
47 };
48 
49 typedef uint64_t RingBufferPosition;
50 
51 /*
52  * AIDL parcelables will have the typedef fixed_size. It is std::true_type when the
53  * parcelable is annotated with @FixedSize, and std::false_type when not. Other types
54  * should not have the fixed_size typedef, so they will always resolve to std::false_type.
55  */
56 template <typename T, typename = void>
57 struct has_typedef_fixed_size : std::false_type {};
58 
59 template <typename T>
60 struct has_typedef_fixed_size<T, std::void_t<typename T::fixed_size>> : T::fixed_size {};
61 
62 #define STATIC_AIDL_TYPE_CHECK(T)                                                                  \
63     static_assert(has_typedef_fixed_size<T>::value == true || std::is_fundamental<T>::value ||     \
64                           std::is_enum<T>::value,                                                  \
65                   "Only fundamental types, enums, and AIDL parcelables annotated with @FixedSize " \
66                   "and built for the NDK backend are supported as payload types(T).");
67 
68 template <typename T, typename U>
69 struct AidlMessageQueue final
70     : public MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value> {
71     STATIC_AIDL_TYPE_CHECK(T);
72     typedef AidlMQDescriptorShim<T, FlavorTypeToValue<U>::value> Descriptor;
73     /**
74      * This constructor uses the external descriptor used with AIDL interfaces.
75      * It will create an FMQ based on the descriptor that was obtained from
76      * another FMQ instance for communication.
77      *
78      * @param desc Descriptor from another FMQ that contains all of the
79      * information required to create a new instance of that queue.
80      * @param resetPointers Boolean indicating whether the read/write pointers
81      * should be reset or not.
82      */
83     AidlMessageQueue(const MQDescriptor<T, U>& desc, bool resetPointers = true);
84     ~AidlMessageQueue() = default;
85 
86     /**
87      * This constructor uses Ashmem shared memory to create an FMQ
88      * that can contain a maximum of 'numElementsInQueue' elements of type T.
89      *
90      * @param numElementsInQueue Capacity of the AidlMessageQueue in terms of T.
91      * @param configureEventFlagWord Boolean that specifies if memory should
92      * also be allocated and mapped for an EventFlag word.
93      * @param bufferFd User-supplied file descriptor to map the memory for the ringbuffer
94      * By default, bufferFd=-1 means library will allocate ashmem region for ringbuffer.
95      * MessageQueue takes ownership of the file descriptor.
96      * @param bufferSize size of buffer in bytes that bufferFd represents. This
97      * size must be larger than or equal to (numElementsInQueue * sizeof(T)).
98      * Otherwise, operations will cause out-of-bounds memory access.
99      */
100     AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord,
101                      android::base::unique_fd bufferFd, size_t bufferSize);
102 
103     AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord = false)
104         : AidlMessageQueue(numElementsInQueue, configureEventFlagWord, android::base::unique_fd(),
105                            0) {}
106 
107     MQDescriptor<T, U> dupeDesc();
108 
109   private:
110     AidlMessageQueue(const AidlMessageQueue& other) = delete;
111     AidlMessageQueue& operator=(const AidlMessageQueue& other) = delete;
112     AidlMessageQueue() = delete;
113 };
114 
115 template <typename T, typename U>
116 AidlMessageQueue<T, U>::AidlMessageQueue(const MQDescriptor<T, U>& desc, bool resetPointers)
117     : MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>(Descriptor(desc),
118                                                                              resetPointers) {}
119 
120 template <typename T, typename U>
121 AidlMessageQueue<T, U>::AidlMessageQueue(size_t numElementsInQueue, bool configureEventFlagWord,
122                                          android::base::unique_fd bufferFd, size_t bufferSize)
123     : MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>(
124               numElementsInQueue, configureEventFlagWord, std::move(bufferFd), bufferSize) {}
125 
126 template <typename T, typename U>
127 MQDescriptor<T, U> AidlMessageQueue<T, U>::dupeDesc() {
128     auto* shim = MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value>::getDesc();
129     if (shim) {
130         std::vector<aidl::android::hardware::common::fmq::GrantorDescriptor> grantors;
131         for (const auto& grantor : shim->grantors()) {
132             grantors.push_back(aidl::android::hardware::common::fmq::GrantorDescriptor{
133                     .fdIndex = static_cast<int32_t>(grantor.fdIndex),
134                     .offset = static_cast<int32_t>(grantor.offset),
135                     .extent = static_cast<int64_t>(grantor.extent)});
136         }
137         std::vector<ndk::ScopedFileDescriptor> fds;
138         std::vector<int> ints;
139         int data_index = 0;
140         for (; data_index < shim->handle()->numFds; data_index++) {
141             fds.push_back(ndk::ScopedFileDescriptor(dup(shim->handle()->data[data_index])));
142         }
143         for (; data_index < shim->handle()->numFds + shim->handle()->numInts; data_index++) {
144             ints.push_back(shim->handle()->data[data_index]);
145         }
146         return MQDescriptor<T, U>{
147                 .quantum = static_cast<int32_t>(shim->getQuantum()),
148                 .grantors = grantors,
149                 .flags = static_cast<int32_t>(shim->getFlags()),
150                 .handle = {std::move(fds), std::move(ints)},
151         };
152     } else {
153         return MQDescriptor<T, U>();
154     }
155 }
156 
157 }  // namespace android
158