1 /*
2  * Copyright (C) 2012 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 "BluetoothPanServiceJni"
18 
19 #include <string.h>
20 
21 #include "com_android_bluetooth.h"
22 #include "hardware/bt_pan.h"
23 
24 namespace android {
25 
26 static jmethodID method_onConnectStateChanged;
27 static jmethodID method_onControlStateChanged;
28 
29 static const btpan_interface_t* sPanIf = NULL;
30 static jobject mCallbacksObj = NULL;
31 
marshall_bda(const RawAddress * bd_addr)32 static jbyteArray marshall_bda(const RawAddress* bd_addr) {
33   CallbackEnv sCallbackEnv(__func__);
34   if (!sCallbackEnv.valid()) return NULL;
35 
36   jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
37   if (!addr) {
38     log::error("Fail to new jbyteArray bd addr");
39     return NULL;
40   }
41   sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress),
42                                    (jbyte*)bd_addr);
43   return addr;
44 }
45 
control_state_callback(btpan_control_state_t state,int local_role,bt_status_t error,const char * ifname)46 static void control_state_callback(btpan_control_state_t state, int local_role,
47                                    bt_status_t error, const char* ifname) {
48   log::debug("state:{}, local_role:{}, ifname:{}", state, local_role, ifname);
49   if (mCallbacksObj == NULL) {
50     log::error("Callbacks Obj is NULL");
51     return;
52   }
53   CallbackEnv sCallbackEnv(__func__);
54   if (!sCallbackEnv.valid()) return;
55   ScopedLocalRef<jstring> js_ifname(sCallbackEnv.get(),
56                                     sCallbackEnv->NewStringUTF(ifname));
57   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onControlStateChanged,
58                                (jint)local_role, (jint)state, (jint)error,
59                                js_ifname.get());
60 }
61 
connection_state_callback(btpan_connection_state_t state,bt_status_t error,const RawAddress * bd_addr,int local_role,int remote_role)62 static void connection_state_callback(btpan_connection_state_t state,
63                                       bt_status_t error,
64                                       const RawAddress* bd_addr, int local_role,
65                                       int remote_role) {
66   log::debug("state:{}, local_role:{}, remote_role:{}", state, local_role,
67              remote_role);
68   if (mCallbacksObj == NULL) {
69     log::error("Callbacks Obj is NULL");
70     return;
71   }
72   CallbackEnv sCallbackEnv(__func__);
73   if (!sCallbackEnv.valid()) return;
74   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
75   if (!addr.get()) {
76     log::error("Fail to new jbyteArray bd addr for PAN channel state");
77     return;
78   }
79   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged,
80                                addr.get(), (jint)state, (jint)error,
81                                (jint)local_role, (jint)remote_role);
82 }
83 
84 static btpan_callbacks_t sBluetoothPanCallbacks = {
85     sizeof(sBluetoothPanCallbacks), control_state_callback,
86     connection_state_callback};
87 
88 // Define native functions
89 static const bt_interface_t* btIf;
90 
initializeNative(JNIEnv * env,jobject object)91 static void initializeNative(JNIEnv* env, jobject object) {
92   log::debug("Initialize pan");
93   if (btIf) return;
94 
95   btIf = getBluetoothInterface();
96   if (btIf == NULL) {
97     log::error("Bluetooth module is not loaded");
98     return;
99   }
100 
101   if (sPanIf != NULL) {
102     log::warn("Cleaning up Bluetooth PAN Interface before initializing...");
103     sPanIf->cleanup();
104     sPanIf = NULL;
105   }
106 
107   if (mCallbacksObj != NULL) {
108     log::warn("Cleaning up Bluetooth PAN callback object");
109     env->DeleteGlobalRef(mCallbacksObj);
110     mCallbacksObj = NULL;
111   }
112 
113   sPanIf = (btpan_interface_t*)btIf->get_profile_interface(BT_PROFILE_PAN_ID);
114   if (sPanIf == NULL) {
115     log::error("Failed to get Bluetooth PAN Interface");
116     return;
117   }
118 
119   mCallbacksObj = env->NewGlobalRef(object);
120 
121   bt_status_t status = sPanIf->init(&sBluetoothPanCallbacks);
122   if (status != BT_STATUS_SUCCESS) {
123     log::error("Failed to initialize Bluetooth PAN, status: {}",
124                bt_status_text(status));
125     sPanIf = NULL;
126     if (mCallbacksObj != NULL) {
127       log::warn(
128           "initialization failed: Cleaning up Bluetooth PAN callback object");
129       env->DeleteGlobalRef(mCallbacksObj);
130       mCallbacksObj = NULL;
131     }
132     return;
133   }
134 }
135 
cleanupNative(JNIEnv * env,jobject)136 static void cleanupNative(JNIEnv* env, jobject /* object */) {
137   log::debug("Cleanup pan");
138   if (!btIf) return;
139 
140   if (sPanIf != NULL) {
141     log::warn("Cleaning up Bluetooth PAN Interface...");
142     sPanIf->cleanup();
143     sPanIf = NULL;
144   }
145 
146   if (mCallbacksObj != NULL) {
147     log::warn("Cleaning up Bluetooth PAN callback object");
148     env->DeleteGlobalRef(mCallbacksObj);
149     mCallbacksObj = NULL;
150   }
151   btIf = NULL;
152 }
153 
connectPanNative(JNIEnv * env,jobject,jbyteArray address,jint src_role,jint dest_role)154 static jboolean connectPanNative(JNIEnv* env, jobject /* object */,
155                                  jbyteArray address, jint src_role,
156                                  jint dest_role) {
157   log::debug("Connect pan");
158   if (!sPanIf) return JNI_FALSE;
159 
160   jbyte* addr = env->GetByteArrayElements(address, NULL);
161   if (!addr) {
162     log::error("Bluetooth device address null");
163     return JNI_FALSE;
164   }
165 
166   jboolean ret = JNI_TRUE;
167   bt_status_t status = sPanIf->connect((RawAddress*)addr, src_role, dest_role);
168   if (status != BT_STATUS_SUCCESS) {
169     log::error("Failed PAN channel connection, status: {}",
170                bt_status_text(status));
171     ret = JNI_FALSE;
172   }
173   env->ReleaseByteArrayElements(address, addr, 0);
174 
175   return ret;
176 }
177 
disconnectPanNative(JNIEnv * env,jobject,jbyteArray address)178 static jboolean disconnectPanNative(JNIEnv* env, jobject /* object */,
179                                     jbyteArray address) {
180   log::debug("Disconnects pan");
181   if (!sPanIf) return JNI_FALSE;
182 
183   jbyte* addr = env->GetByteArrayElements(address, NULL);
184   if (!addr) {
185     log::error("Bluetooth device address null");
186     return JNI_FALSE;
187   }
188 
189   jboolean ret = JNI_TRUE;
190   bt_status_t status = sPanIf->disconnect((RawAddress*)addr);
191   if (status != BT_STATUS_SUCCESS) {
192     log::error("Failed disconnect pan channel, status: {}",
193                bt_status_text(status));
194     ret = JNI_FALSE;
195   }
196   env->ReleaseByteArrayElements(address, addr, 0);
197 
198   return ret;
199 }
200 
register_com_android_bluetooth_pan(JNIEnv * env)201 int register_com_android_bluetooth_pan(JNIEnv* env) {
202   const JNINativeMethod methods[] = {
203       {"initializeNative", "()V", (void*)initializeNative},
204       {"cleanupNative", "()V", (void*)cleanupNative},
205       {"connectPanNative", "([BII)Z", (void*)connectPanNative},
206       {"disconnectPanNative", "([B)Z", (void*)disconnectPanNative},
207   };
208   const int result = REGISTER_NATIVE_METHODS(
209       env, "com/android/bluetooth/pan/PanNativeInterface", methods);
210   if (result != 0) {
211     return result;
212   }
213 
214   const JNIJavaMethod javaMethods[]{
215       {"onConnectStateChanged", "([BIIII)V", &method_onConnectStateChanged},
216       {"onControlStateChanged", "(IIILjava/lang/String;)V",
217        &method_onControlStateChanged},
218   };
219   GET_JAVA_METHODS(env, "com/android/bluetooth/pan/PanNativeInterface",
220                    javaMethods);
221 
222   return 0;
223 }
224 }
225