• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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