1 /*
2 * Copyright (C) 2024 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 #define LOG_TAG "BluetoothHciVendorSpecificJni"
18
19 #include <shared_mutex>
20
21 #include "btif/include/btif_hci_vs.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_hci_vs.h"
24
25 using bluetooth::hci_vs::BluetoothHciVendorSpecificInterface;
26 using bluetooth::hci_vs::Cookie;
27
28 namespace android {
29
30 static std::shared_timed_mutex interface_mutex;
31 static std::shared_timed_mutex callbacks_mutex;
32
33 static jmethodID method_onCommandStatus;
34 static jmethodID method_onCommandComplete;
35 static jmethodID method_onEvent;
36 static jobject mCallbacksObj = nullptr;
37
38 class BluetoothHciVendorSpecificCallbacksImpl
39 : public bluetooth::hci_vs::BluetoothHciVendorSpecificCallbacks {
40 public:
41 ~BluetoothHciVendorSpecificCallbacksImpl() = default;
42
onCommandStatus(uint16_t ocf,uint8_t status,Cookie cookie)43 void onCommandStatus(uint16_t ocf, uint8_t status, Cookie cookie) override {
44 log::info("");
45 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
46
47 CallbackEnv callbackEnv(__func__);
48 if (!callbackEnv.valid() || mCallbacksObj == nullptr) {
49 return;
50 }
51
52 auto j_cookie = toJByteArray(callbackEnv.get(), cookie);
53 if (!j_cookie.get()) {
54 log::error("Error while allocating byte array for cookie");
55 return;
56 }
57
58 callbackEnv->CallVoidMethod(mCallbacksObj, method_onCommandStatus, (jint)ocf, (jint)status,
59 j_cookie.get());
60 }
61
onCommandComplete(uint16_t ocf,std::vector<uint8_t> return_parameters,Cookie cookie)62 void onCommandComplete(uint16_t ocf, std::vector<uint8_t> return_parameters,
63 Cookie cookie) override {
64 log::info("");
65 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
66
67 CallbackEnv callbackEnv(__func__);
68 if (!callbackEnv.valid() || mCallbacksObj == nullptr) {
69 return;
70 }
71
72 auto j_return_parameters = toJByteArray(callbackEnv.get(), return_parameters);
73 auto j_cookie = toJByteArray(callbackEnv.get(), cookie);
74 if (!j_return_parameters.get() || !j_cookie.get()) {
75 log::error("Error while allocating byte array for return parameters or cookie");
76 return;
77 }
78
79 callbackEnv->CallVoidMethod(mCallbacksObj, method_onCommandComplete, (jint)ocf,
80 j_return_parameters.get(), j_cookie.get());
81 }
82
onEvent(uint8_t code,std::vector<uint8_t> data)83 void onEvent(uint8_t code, std::vector<uint8_t> data) override {
84 log::info("");
85 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
86
87 CallbackEnv callbackEnv(__func__);
88 if (!callbackEnv.valid() || mCallbacksObj == nullptr) {
89 return;
90 }
91
92 auto j_data = toJByteArray(callbackEnv.get(), data);
93 if (!j_data.get()) {
94 log::error("Error while allocating byte array for event data");
95 return;
96 }
97
98 callbackEnv->CallVoidMethod(mCallbacksObj, method_onEvent, (jint)code, j_data.get());
99 }
100
101 private:
102 template <typename T>
toJByteArray(JNIEnv * env,const T & src)103 static ScopedLocalRef<jbyteArray> toJByteArray(JNIEnv* env, const T& src) {
104 ScopedLocalRef<jbyteArray> dst(env, env->NewByteArray(src.size()));
105 if (dst.get()) {
106 env->SetByteArrayRegion(dst.get(), 0, src.size(), reinterpret_cast<const jbyte*>(src.data()));
107 }
108 return dst;
109 }
110 };
111
112 static BluetoothHciVendorSpecificInterface* sBluetoothHciVendorSpecificInterface = nullptr;
113 static BluetoothHciVendorSpecificCallbacksImpl sBluetoothHciVendorSpecificCallbacks;
114
initNative(JNIEnv * env,jobject object)115 static void initNative(JNIEnv* env, jobject object) {
116 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
117 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
118
119 if (sBluetoothHciVendorSpecificInterface != nullptr) {
120 log::info("Cleaning up BluetoothHciVendorSpecific Interface before initializing...");
121 sBluetoothHciVendorSpecificInterface = nullptr;
122 }
123
124 if (mCallbacksObj != nullptr) {
125 log::info("Cleaning up BluetoothHciVendorSpecific callback object");
126 env->DeleteGlobalRef(mCallbacksObj);
127 mCallbacksObj = nullptr;
128 }
129
130 if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
131 log::error("Failed to allocate Global Ref for BluetoothHciVendorSpecific Callbacks");
132 return;
133 }
134
135 sBluetoothHciVendorSpecificInterface =
136 bluetooth::hci_vs::getBluetoothHciVendorSpecificInterface();
137 if (sBluetoothHciVendorSpecificInterface == nullptr) {
138 log::error("Failed to get BluetoothHciVendorSpecific Interface");
139 return;
140 }
141
142 sBluetoothHciVendorSpecificInterface->init(&sBluetoothHciVendorSpecificCallbacks);
143 }
144
cleanupNative(JNIEnv * env,jobject)145 static void cleanupNative(JNIEnv* env, jobject /* object */) {
146 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
147 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
148
149 if (sBluetoothHciVendorSpecificInterface != nullptr) {
150 sBluetoothHciVendorSpecificInterface = nullptr;
151 }
152
153 if (mCallbacksObj != nullptr) {
154 env->DeleteGlobalRef(mCallbacksObj);
155 mCallbacksObj = nullptr;
156 }
157 }
158
sendCommandNative(JNIEnv * env,jobject,jint ocf,jbyteArray parametersArray,jbyteArray uuidArray)159 static void sendCommandNative(JNIEnv* env, jobject /* obj */, jint ocf, jbyteArray parametersArray,
160 jbyteArray uuidArray) {
161 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
162 log::verbose("");
163 if (!sBluetoothHciVendorSpecificInterface) {
164 return;
165 }
166
167 jbyte* pParameters = env->GetByteArrayElements(parametersArray, NULL);
168 jbyte* pUuid = env->GetByteArrayElements(uuidArray, NULL);
169 if (pParameters && pUuid && env->GetArrayLength(uuidArray) == 16) {
170 std::vector<uint8_t> parameters(
171 reinterpret_cast<uint8_t*>(pParameters),
172 reinterpret_cast<uint8_t*>(pParameters + env->GetArrayLength(parametersArray)));
173
174 Cookie cookie;
175 std::memcpy(cookie.data(), reinterpret_cast<const uint8_t*>(pUuid), 16);
176
177 if (!sBluetoothHciVendorSpecificInterface) {
178 return;
179 }
180 sBluetoothHciVendorSpecificInterface->sendCommand(ocf, parameters, cookie);
181
182 } else {
183 jniThrowIOException(env, EINVAL);
184 }
185
186 if (pParameters) {
187 env->ReleaseByteArrayElements(parametersArray, pParameters, 0);
188 }
189
190 if (pUuid) {
191 env->ReleaseByteArrayElements(uuidArray, pUuid, 0);
192 }
193 }
194
register_com_android_bluetooth_btservice_BluetoothHciVendorSpecific(JNIEnv * env)195 int register_com_android_bluetooth_btservice_BluetoothHciVendorSpecific(JNIEnv* env) {
196 const JNINativeMethod methods[] = {
197 {"initNative", "()V", reinterpret_cast<void*>(initNative)},
198 {"cleanupNative", "()V", reinterpret_cast<void*>(cleanupNative)},
199 {"sendCommandNative", "(I[B[B)V", reinterpret_cast<void*>(sendCommandNative)},
200 };
201 const int result = REGISTER_NATIVE_METHODS(
202 env, "com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface",
203 methods);
204 if (result != 0) {
205 return result;
206 }
207
208 const JNIJavaMethod javaMethods[] = {
209 {"onCommandStatus", "(II[B)V", &method_onCommandStatus},
210 {"onCommandComplete", "(I[B[B)V", &method_onCommandComplete},
211 {"onEvent", "(I[B)V", &method_onEvent},
212 };
213 GET_JAVA_METHODS(env, "com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface",
214 javaMethods);
215
216 return 0;
217 }
218
219 } // namespace android
220