1 /*
2  * Copyright (C) 2015 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 "BluetoothSdpJni"
18 
19 #include <string.h>
20 
21 #include "com_android_bluetooth.h"
22 #include "hardware/bt_sdp.h"
23 
24 using bluetooth::Uuid;
25 
26 static const Uuid UUID_OBEX_OBJECT_PUSH = Uuid::From16Bit(0x1105);
27 static const Uuid UUID_PBAP_PSE = Uuid::From16Bit(0x112F);
28 static const Uuid UUID_MAP_MAS = Uuid::From16Bit(0x1132);
29 static const Uuid UUID_MAP_MNS = Uuid::From16Bit(0x1133);
30 static const Uuid UUID_SAP = Uuid::From16Bit(0x112D);
31 static const Uuid UUID_DIP = Uuid::From16Bit(0x1200);
32 
33 namespace android {
34 static jmethodID method_sdpRecordFoundCallback;
35 static jmethodID method_sdpMasRecordFoundCallback;
36 static jmethodID method_sdpMnsRecordFoundCallback;
37 static jmethodID method_sdpPseRecordFoundCallback;
38 static jmethodID method_sdpOppOpsRecordFoundCallback;
39 static jmethodID method_sdpSapsRecordFoundCallback;
40 static jmethodID method_sdpDipRecordFoundCallback;
41 
42 static const btsdp_interface_t* sBluetoothSdpInterface = NULL;
43 
44 static void sdp_search_callback(bt_status_t status, const RawAddress& bd_addr,
45                                 const Uuid& uuid_in, int record_size,
46                                 bluetooth_sdp_record* record);
47 
48 btsdp_callbacks_t sBluetoothSdpCallbacks = {sizeof(sBluetoothSdpCallbacks),
49                                             sdp_search_callback};
50 
51 static jobject sCallbacksObj = NULL;
52 
initializeNative(JNIEnv * env,jobject object)53 static void initializeNative(JNIEnv* env, jobject object) {
54   const bt_interface_t* btInf = getBluetoothInterface();
55 
56   if (btInf == NULL) {
57     log::error("Bluetooth module is not loaded");
58     return;
59   }
60   if (sBluetoothSdpInterface != NULL) {
61     log::warn("Cleaning up Bluetooth SDP Interface before initializing...");
62     sBluetoothSdpInterface->deinit();
63     sBluetoothSdpInterface = NULL;
64   }
65 
66   sBluetoothSdpInterface = (btsdp_interface_t*)btInf->get_profile_interface(
67       BT_PROFILE_SDP_CLIENT_ID);
68   if (sBluetoothSdpInterface == NULL) {
69     log::error("Error getting SDP client interface");
70   } else {
71     sBluetoothSdpInterface->init(&sBluetoothSdpCallbacks);
72   }
73 
74   sCallbacksObj = env->NewGlobalRef(object);
75 }
76 
sdpSearchNative(JNIEnv * env,jobject,jbyteArray address,jbyteArray uuidObj)77 static jboolean sdpSearchNative(JNIEnv* env, jobject /* obj */,
78                                 jbyteArray address, jbyteArray uuidObj) {
79   log::debug("");
80 
81   if (!sBluetoothSdpInterface) return JNI_FALSE;
82 
83   jbyte* addr = env->GetByteArrayElements(address, NULL);
84   if (addr == NULL) {
85     jniThrowIOException(env, EINVAL);
86     return JNI_FALSE;
87   }
88 
89   jbyte* raw_uuid = env->GetByteArrayElements(uuidObj, NULL);
90   if (!raw_uuid) {
91     log::error("failed to get uuid");
92     env->ReleaseByteArrayElements(address, addr, 0);
93     return JNI_FALSE;
94   }
95   Uuid uuid = Uuid::From128BitBE((uint8_t*)raw_uuid);
96   log::debug("UUID {}", uuid);
97 
98   int ret = sBluetoothSdpInterface->sdp_search((RawAddress*)addr, uuid);
99   if (ret != BT_STATUS_SUCCESS) {
100     log::error("SDP Search initialization failed: {}", ret);
101   }
102 
103   if (addr) env->ReleaseByteArrayElements(address, addr, 0);
104   if (raw_uuid) env->ReleaseByteArrayElements(uuidObj, raw_uuid, 0);
105   return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
106 }
107 
sdp_search_callback(bt_status_t status,const RawAddress & bd_addr,const Uuid & uuid_in,int count,bluetooth_sdp_record * records)108 static void sdp_search_callback(bt_status_t status, const RawAddress& bd_addr,
109                                 const Uuid& uuid_in, int count,
110                                 bluetooth_sdp_record* records) {
111   CallbackEnv sCallbackEnv(__func__);
112   if (!sCallbackEnv.valid()) return;
113 
114   ScopedLocalRef<jbyteArray> addr(
115       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
116   if (!addr.get()) return;
117 
118   ScopedLocalRef<jbyteArray> uuid(sCallbackEnv.get(),
119                                   sCallbackEnv->NewByteArray(sizeof(Uuid)));
120   if (!uuid.get()) return;
121 
122   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
123                                    (const jbyte*)&bd_addr);
124   sCallbackEnv->SetByteArrayRegion(uuid.get(), 0, sizeof(Uuid),
125                                    (const jbyte*)uuid_in.To128BitBE().data());
126 
127   log::debug("Status is: {}, Record count: {}", bt_status_text(status), count);
128 
129   // Ensure we run the loop at least once, to also signal errors if they occur
130   for (int i = 0; i < count || i == 0; i++) {
131     bool more_results = (i < (count - 1)) ? true : false;
132     bluetooth_sdp_record* record = &records[i];
133     ScopedLocalRef<jstring> service_name(sCallbackEnv.get(), NULL);
134     if (record->hdr.service_name_length > 0) {
135       log::debug("ServiceName:  {}", record->mas.hdr.service_name);
136       service_name.reset(
137           (jstring)sCallbackEnv->NewStringUTF(record->mas.hdr.service_name));
138     }
139 
140     /* call the right callback according to the uuid*/
141     if (uuid_in == UUID_MAP_MAS) {
142       sCallbackEnv->CallVoidMethod(
143           sCallbacksObj, method_sdpMasRecordFoundCallback, (jint)status,
144           addr.get(), uuid.get(), (jint)record->mas.mas_instance_id,
145           (jint)record->mas.hdr.l2cap_psm,
146           (jint)record->mas.hdr.rfcomm_channel_number,
147           (jint)record->mas.hdr.profile_version,
148           (jint)record->mas.supported_features,
149           (jint)record->mas.supported_message_types, service_name.get(),
150           more_results);
151 
152     } else if (uuid_in == UUID_MAP_MNS) {
153       sCallbackEnv->CallVoidMethod(
154           sCallbacksObj, method_sdpMnsRecordFoundCallback, (jint)status,
155           addr.get(), uuid.get(), (jint)record->mns.hdr.l2cap_psm,
156           (jint)record->mns.hdr.rfcomm_channel_number,
157           (jint)record->mns.hdr.profile_version,
158           (jint)record->mns.supported_features, service_name.get(),
159           more_results);
160 
161     } else if (uuid_in == UUID_PBAP_PSE) {
162       sCallbackEnv->CallVoidMethod(
163           sCallbacksObj, method_sdpPseRecordFoundCallback, (jint)status,
164           addr.get(), uuid.get(), (jint)record->pse.hdr.l2cap_psm,
165           (jint)record->pse.hdr.rfcomm_channel_number,
166           (jint)record->pse.hdr.profile_version,
167           (jint)record->pse.supported_features,
168           (jint)record->pse.supported_repositories, service_name.get(),
169           more_results);
170 
171     } else if (uuid_in == UUID_OBEX_OBJECT_PUSH) {
172       jint formats_list_size = record->ops.supported_formats_list_len;
173       ScopedLocalRef<jbyteArray> formats_list(
174           sCallbackEnv.get(), sCallbackEnv->NewByteArray(formats_list_size));
175       if (!formats_list.get()) return;
176       sCallbackEnv->SetByteArrayRegion(
177           formats_list.get(), 0, formats_list_size,
178           (jbyte*)record->ops.supported_formats_list);
179 
180       sCallbackEnv->CallVoidMethod(
181           sCallbacksObj, method_sdpOppOpsRecordFoundCallback, (jint)status,
182           addr.get(), uuid.get(), (jint)record->ops.hdr.l2cap_psm,
183           (jint)record->ops.hdr.rfcomm_channel_number,
184           (jint)record->ops.hdr.profile_version, service_name.get(),
185           formats_list.get(), more_results);
186 
187     } else if (uuid_in == UUID_SAP) {
188       sCallbackEnv->CallVoidMethod(
189           sCallbacksObj, method_sdpSapsRecordFoundCallback, (jint)status,
190           addr.get(), uuid.get(), (jint)record->mas.hdr.rfcomm_channel_number,
191           (jint)record->mas.hdr.profile_version, service_name.get(),
192           more_results);
193     } else if (uuid_in == UUID_DIP) {
194       log::debug("Get UUID_DIP");
195       sCallbackEnv->CallVoidMethod(
196           sCallbacksObj, method_sdpDipRecordFoundCallback, (jint)status,
197           addr.get(), uuid.get(), (jint)record->dip.spec_id,
198           (jint)record->dip.vendor,
199           (jint)record->dip.vendor_id_source,
200           (jint)record->dip.product,
201           (jint)record->dip.version,
202           record->dip.primary_record,
203           more_results);
204     } else {
205       // we don't have a wrapper for this uuid, send as raw data
206       jint record_data_size = record->hdr.user1_ptr_len;
207       ScopedLocalRef<jbyteArray> record_data(
208           sCallbackEnv.get(), sCallbackEnv->NewByteArray(record_data_size));
209       if (!record_data.get()) return;
210 
211       sCallbackEnv->SetByteArrayRegion(record_data.get(), 0, record_data_size,
212                                        (jbyte*)record->hdr.user1_ptr);
213       sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpRecordFoundCallback,
214                                    (jint)status, addr.get(), uuid.get(),
215                                    record_data_size, record_data.get());
216     }
217   }  // End of for-loop
218 }
219 
sdpCreateMapMasRecordNative(JNIEnv * env,jobject,jstring name_str,jint mas_id,jint scn,jint l2cap_psm,jint version,jint msg_types,jint features)220 static jint sdpCreateMapMasRecordNative(JNIEnv* env, jobject /* obj */,
221                                         jstring name_str, jint mas_id, jint scn,
222                                         jint l2cap_psm, jint version,
223                                         jint msg_types, jint features) {
224   log::debug("");
225   if (!sBluetoothSdpInterface) return -1;
226 
227   bluetooth_sdp_record record = {};  // Must be zero initialized
228   record.mas.hdr.type = SDP_TYPE_MAP_MAS;
229 
230   const char* service_name = NULL;
231   if (name_str != NULL) {
232     service_name = env->GetStringUTFChars(name_str, NULL);
233     record.mas.hdr.service_name = (char*)service_name;
234     record.mas.hdr.service_name_length = strlen(service_name);
235   } else {
236     record.mas.hdr.service_name = NULL;
237     record.mas.hdr.service_name_length = 0;
238   }
239   record.mas.hdr.rfcomm_channel_number = scn;
240   record.mas.hdr.l2cap_psm = l2cap_psm;
241   record.mas.hdr.profile_version = version;
242 
243   record.mas.mas_instance_id = mas_id;
244   record.mas.supported_features = features;
245   record.mas.supported_message_types = msg_types;
246 
247   int handle = -1;
248   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
249   if (ret != BT_STATUS_SUCCESS) {
250     log::error("SDP Create record failed: {}", ret);
251   } else {
252     log::debug("SDP Create record success - handle: {}", handle);
253   }
254 
255   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
256   return handle;
257 }
258 
sdpCreateMapMnsRecordNative(JNIEnv * env,jobject,jstring name_str,jint scn,jint l2cap_psm,jint version,jint features)259 static jint sdpCreateMapMnsRecordNative(JNIEnv* env, jobject /* obj */,
260                                         jstring name_str, jint scn,
261                                         jint l2cap_psm, jint version,
262                                         jint features) {
263   log::debug("");
264   if (!sBluetoothSdpInterface) return -1;
265 
266   bluetooth_sdp_record record = {};  // Must be zero initialized
267   record.mns.hdr.type = SDP_TYPE_MAP_MNS;
268 
269   const char* service_name = NULL;
270   if (name_str != NULL) {
271     service_name = env->GetStringUTFChars(name_str, NULL);
272     record.mns.hdr.service_name = (char*)service_name;
273     record.mns.hdr.service_name_length = strlen(service_name);
274   } else {
275     record.mns.hdr.service_name = NULL;
276     record.mns.hdr.service_name_length = 0;
277   }
278   record.mns.hdr.rfcomm_channel_number = scn;
279   record.mns.hdr.l2cap_psm = l2cap_psm;
280   record.mns.hdr.profile_version = version;
281 
282   record.mns.supported_features = features;
283 
284   int handle = -1;
285   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
286   if (ret != BT_STATUS_SUCCESS) {
287     log::error("SDP Create record failed: {}", ret);
288   } else {
289     log::debug("SDP Create record success - handle: {}", handle);
290   }
291 
292   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
293   return handle;
294 }
295 
sdpCreatePbapPceRecordNative(JNIEnv * env,jobject,jstring name_str,jint version)296 static jint sdpCreatePbapPceRecordNative(JNIEnv* env, jobject /* obj */,
297                                          jstring name_str, jint version) {
298   log::debug("");
299   if (!sBluetoothSdpInterface) return -1;
300 
301   bluetooth_sdp_record record = {};  // Must be zero initialized
302   record.pce.hdr.type = SDP_TYPE_PBAP_PCE;
303 
304   const char* service_name = NULL;
305   if (name_str != NULL) {
306     service_name = env->GetStringUTFChars(name_str, NULL);
307     record.pce.hdr.service_name = (char*)service_name;
308     record.pce.hdr.service_name_length = strlen(service_name);
309   } else {
310     record.pce.hdr.service_name = NULL;
311     record.pce.hdr.service_name_length = 0;
312   }
313   record.pce.hdr.profile_version = version;
314 
315   int handle = -1;
316   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
317   if (ret != BT_STATUS_SUCCESS) {
318     log::error("SDP Create record failed: {}", ret);
319   } else {
320     log::debug("SDP Create record success - handle: {}", handle);
321   }
322 
323   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
324   return handle;
325 }
326 
sdpCreatePbapPseRecordNative(JNIEnv * env,jobject,jstring name_str,jint scn,jint l2cap_psm,jint version,jint supported_repositories,jint features)327 static jint sdpCreatePbapPseRecordNative(JNIEnv* env, jobject /* obj */,
328                                          jstring name_str, jint scn,
329                                          jint l2cap_psm, jint version,
330                                          jint supported_repositories,
331                                          jint features) {
332   log::debug("");
333   if (!sBluetoothSdpInterface) return -1;
334 
335   bluetooth_sdp_record record = {};  // Must be zero initialized
336   record.pse.hdr.type = SDP_TYPE_PBAP_PSE;
337 
338   const char* service_name = NULL;
339   if (name_str != NULL) {
340     service_name = env->GetStringUTFChars(name_str, NULL);
341     record.pse.hdr.service_name = (char*)service_name;
342     record.pse.hdr.service_name_length = strlen(service_name);
343   } else {
344     record.pse.hdr.service_name = NULL;
345     record.pse.hdr.service_name_length = 0;
346   }
347   record.pse.hdr.rfcomm_channel_number = scn;
348   record.pse.hdr.l2cap_psm = l2cap_psm;
349   record.pse.hdr.profile_version = version;
350 
351   record.pse.supported_features = features;
352   record.pse.supported_repositories = supported_repositories;
353 
354   int handle = -1;
355   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
356   if (ret != BT_STATUS_SUCCESS) {
357     log::error("SDP Create record failed: {}", ret);
358   } else {
359     log::debug("SDP Create record success - handle: {}", handle);
360   }
361 
362   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
363   return handle;
364 }
365 
sdpCreateOppOpsRecordNative(JNIEnv * env,jobject,jstring name_str,jint scn,jint l2cap_psm,jint version,jbyteArray supported_formats_list)366 static jint sdpCreateOppOpsRecordNative(JNIEnv* env, jobject /* obj */,
367                                         jstring name_str, jint scn,
368                                         jint l2cap_psm, jint version,
369                                         jbyteArray supported_formats_list) {
370   log::debug("");
371   if (!sBluetoothSdpInterface) return -1;
372 
373   bluetooth_sdp_record record = {};  // Must be zero initialized
374   record.ops.hdr.type = SDP_TYPE_OPP_SERVER;
375 
376   const char* service_name = NULL;
377   if (name_str != NULL) {
378     service_name = env->GetStringUTFChars(name_str, NULL);
379     record.ops.hdr.service_name = (char*)service_name;
380     record.ops.hdr.service_name_length = strlen(service_name);
381   } else {
382     record.ops.hdr.service_name = NULL;
383     record.ops.hdr.service_name_length = 0;
384   }
385   record.ops.hdr.rfcomm_channel_number = scn;
386   record.ops.hdr.l2cap_psm = l2cap_psm;
387   record.ops.hdr.profile_version = version;
388 
389   int formats_list_len = 0;
390   jbyte* formats_list = env->GetByteArrayElements(supported_formats_list, NULL);
391   if (formats_list != NULL) {
392     formats_list_len = env->GetArrayLength(supported_formats_list);
393     if (formats_list_len > SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH) {
394       formats_list_len = SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH;
395     }
396     memcpy(record.ops.supported_formats_list, formats_list, formats_list_len);
397   }
398 
399   record.ops.supported_formats_list_len = formats_list_len;
400 
401   int handle = -1;
402   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
403   if (ret != BT_STATUS_SUCCESS) {
404     log::error("SDP Create record failed: {}", ret);
405   } else {
406     log::debug("SDP Create record success - handle: {}", handle);
407   }
408 
409   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
410   if (formats_list)
411     env->ReleaseByteArrayElements(supported_formats_list, formats_list, 0);
412   return handle;
413 }
414 
sdpCreateSapsRecordNative(JNIEnv * env,jobject,jstring name_str,jint scn,jint version)415 static jint sdpCreateSapsRecordNative(JNIEnv* env, jobject /* obj */,
416                                       jstring name_str, jint scn,
417                                       jint version) {
418   log::debug("");
419   if (!sBluetoothSdpInterface) return -1;
420 
421   bluetooth_sdp_record record = {};  // Must be zero initialized
422   record.sap.hdr.type = SDP_TYPE_SAP_SERVER;
423 
424   const char* service_name = NULL;
425   if (name_str != NULL) {
426     service_name = env->GetStringUTFChars(name_str, NULL);
427     record.mas.hdr.service_name = (char*)service_name;
428     record.mas.hdr.service_name_length = strlen(service_name);
429   } else {
430     record.mas.hdr.service_name = NULL;
431     record.mas.hdr.service_name_length = 0;
432   }
433   record.mas.hdr.rfcomm_channel_number = scn;
434   record.mas.hdr.profile_version = version;
435 
436   int handle = -1;
437   int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
438   if (ret != BT_STATUS_SUCCESS) {
439     log::error("SDP Create record failed: {}", ret);
440   } else {
441     log::debug("SDP Create record success - handle: {}", handle);
442   }
443 
444   if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
445   return handle;
446 }
447 
sdpRemoveSdpRecordNative(JNIEnv *,jobject,jint record_id)448 static jboolean sdpRemoveSdpRecordNative(JNIEnv* /* env */, jobject /* obj */,
449                                          jint record_id) {
450   log::debug("");
451   if (!sBluetoothSdpInterface) return false;
452 
453   int ret = sBluetoothSdpInterface->remove_sdp_record(record_id);
454   if (ret != BT_STATUS_SUCCESS) {
455     log::error("SDP Remove record failed: {}", ret);
456     return false;
457   }
458 
459   log::debug("SDP Remove record success - handle: {}", record_id);
460   return true;
461 }
462 
cleanupNative(JNIEnv * env,jobject)463 static void cleanupNative(JNIEnv* env, jobject /* object */) {
464   const bt_interface_t* btInf = getBluetoothInterface();
465 
466   if (btInf == NULL) {
467     log::error("Bluetooth module is not loaded");
468     return;
469   }
470 
471   if (sBluetoothSdpInterface != NULL) {
472     log::warn("Cleaning up Bluetooth SDP Interface...");
473     sBluetoothSdpInterface->deinit();
474     sBluetoothSdpInterface = NULL;
475   }
476 
477   if (sCallbacksObj != NULL) {
478     log::warn("Cleaning up Bluetooth SDP object");
479     env->DeleteGlobalRef(sCallbacksObj);
480     sCallbacksObj = NULL;
481   }
482 }
483 
register_com_android_bluetooth_sdp(JNIEnv * env)484 int register_com_android_bluetooth_sdp(JNIEnv* env) {
485   const JNINativeMethod methods[] = {
486       {"initializeNative", "()V", (void*)initializeNative},
487       {"cleanupNative", "()V", (void*)cleanupNative},
488       {"sdpSearchNative", "([B[B)Z", (void*)sdpSearchNative},
489       {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I",
490        (void*)sdpCreateMapMasRecordNative},
491       {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I",
492        (void*)sdpCreateMapMnsRecordNative},
493       {"sdpCreatePbapPceRecordNative", "(Ljava/lang/String;I)I",
494        (void*)sdpCreatePbapPceRecordNative},
495       {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I",
496        (void*)sdpCreatePbapPseRecordNative},
497       {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I",
498        (void*)sdpCreateOppOpsRecordNative},
499       {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I",
500        (void*)sdpCreateSapsRecordNative},
501       {"sdpRemoveSdpRecordNative", "(I)Z", (void*)sdpRemoveSdpRecordNative},
502   };
503   const int result = REGISTER_NATIVE_METHODS(
504       env, "com/android/bluetooth/sdp/SdpManagerNativeInterface", methods);
505   if (result != 0) {
506     return result;
507   }
508 
509   const JNIJavaMethod javaMethods[] = {
510       {"sdpRecordFoundCallback", "(I[B[BI[B)V", &method_sdpRecordFoundCallback},
511       {"sdpMasRecordFoundCallback", "(I[B[BIIIIIILjava/lang/String;Z)V",
512        &method_sdpMasRecordFoundCallback},
513       {"sdpMnsRecordFoundCallback", "(I[B[BIIIILjava/lang/String;Z)V",
514        &method_sdpMnsRecordFoundCallback},
515       {"sdpPseRecordFoundCallback", "(I[B[BIIIIILjava/lang/String;Z)V",
516        &method_sdpPseRecordFoundCallback},
517       {"sdpOppOpsRecordFoundCallback", "(I[B[BIIILjava/lang/String;[BZ)V",
518        &method_sdpOppOpsRecordFoundCallback},
519       {"sdpSapsRecordFoundCallback", "(I[B[BIILjava/lang/String;Z)V",
520        &method_sdpSapsRecordFoundCallback},
521       {"sdpDipRecordFoundCallback", "(I[B[BIIIIIZZ)V",
522        &method_sdpDipRecordFoundCallback},
523   };
524   GET_JAVA_METHODS(env, "com/android/bluetooth/sdp/SdpManagerNativeInterface",
525                    javaMethods);
526 
527   return 0;
528 }
529 }
530