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