sdpd-service.c revision da8ebafe5f67103ed8a1d4fd21bbd6b02b62221e
1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2001-2002 Nokia Corporation 6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> 7 * Copyright (C) 2002-2009 Marcel Holtmann <marcel@holtmann.org> 8 * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com> 9 * 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 */ 26 27#ifdef HAVE_CONFIG_H 28#include <config.h> 29#endif 30 31#include <stdio.h> 32#include <errno.h> 33#include <stdlib.h> 34#include <sys/time.h> 35#include <sys/socket.h> 36 37#include <bluetooth/bluetooth.h> 38#include <bluetooth/sdp.h> 39#include <bluetooth/sdp_lib.h> 40 41#include <netinet/in.h> 42 43#include "sdpd.h" 44#include "logging.h" 45 46static sdp_record_t *server = NULL; 47 48static uint8_t service_classes = 0x00; 49static service_classes_callback_t service_classes_callback = NULL; 50 51static uint16_t did_vendor = 0x0000; 52static uint16_t did_product = 0x0000; 53static uint16_t did_version = 0x0000; 54 55/* 56 * List of version numbers supported by the SDP server. 57 * Add to this list when newer versions are supported. 58 */ 59static sdp_version_t sdpVnumArray[1] = { 60 { 1, 0 } 61}; 62static const int sdpServerVnumEntries = 1; 63 64/* 65 * A simple function which returns the time of day in 66 * seconds. Used for updating the service db state 67 * attribute of the service record of the SDP server 68 */ 69uint32_t sdp_get_time() 70{ 71 /* 72 * To handle failure in gettimeofday, so an old 73 * value is returned and service does not fail 74 */ 75 static struct timeval tm; 76 77 gettimeofday(&tm, NULL); 78 return (uint32_t) tm.tv_sec; 79} 80 81/* 82 * The service database state is an attribute of the service record 83 * of the SDP server itself. This attribute is guaranteed to 84 * change if any of the contents of the service repository 85 * changes. This function updates the timestamp of value of 86 * the svcDBState attribute 87 * Set the SDP server DB. Simply a timestamp which is the marker 88 * when the DB was modified. 89 */ 90static void update_db_timestamp(void) 91{ 92 uint32_t dbts = sdp_get_time(); 93 sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts); 94 sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d); 95} 96 97static void update_svclass_list(const bdaddr_t *src) 98{ 99 sdp_list_t *list = sdp_get_record_list(); 100 uint8_t val = 0; 101 102 for (; list; list = list->next) { 103 sdp_record_t *rec = (sdp_record_t *) list->data; 104 105 if (rec->svclass.type != SDP_UUID16) 106 continue; 107 108 switch (rec->svclass.value.uuid16) { 109 case DIALUP_NET_SVCLASS_ID: 110 case CIP_SVCLASS_ID: 111 val |= 0x42; /* Telephony & Networking */ 112 break; 113 case IRMC_SYNC_SVCLASS_ID: 114 case OBEX_OBJPUSH_SVCLASS_ID: 115 case OBEX_FILETRANS_SVCLASS_ID: 116 case IRMC_SYNC_CMD_SVCLASS_ID: 117 case PBAP_PSE_SVCLASS_ID: 118 val |= 0x10; /* Object Transfer */ 119 break; 120 case HEADSET_SVCLASS_ID: 121 case HANDSFREE_SVCLASS_ID: 122 val |= 0x20; /* Audio */ 123 break; 124 case CORDLESS_TELEPHONY_SVCLASS_ID: 125 case INTERCOM_SVCLASS_ID: 126 case FAX_SVCLASS_ID: 127 case SAP_SVCLASS_ID: 128 /* 129 * Setting the telephony bit for the handsfree audio gateway 130 * role is not required by the HFP specification, but the 131 * Nokia 616 carkit is just plain broken! It will refuse 132 * pairing without this bit set. 133 */ 134 case HANDSFREE_AGW_SVCLASS_ID: 135 val |= 0x40; /* Telephony */ 136 break; 137 case AUDIO_SOURCE_SVCLASS_ID: 138 case VIDEO_SOURCE_SVCLASS_ID: 139 val |= 0x08; /* Capturing */ 140 break; 141 case AUDIO_SINK_SVCLASS_ID: 142 case VIDEO_SINK_SVCLASS_ID: 143 val |= 0x04; /* Rendering */ 144 break; 145 case PANU_SVCLASS_ID: 146 case NAP_SVCLASS_ID: 147 case GN_SVCLASS_ID: 148 val |= 0x02; /* Networking */ 149 break; 150 } 151 } 152 153 SDPDBG("Service classes 0x%02x", val); 154 155 service_classes = val; 156 157 if (service_classes_callback) 158 service_classes_callback(src, val); 159} 160 161uint8_t get_service_classes(const bdaddr_t *bdaddr) 162{ 163 return service_classes; 164} 165 166void set_service_classes_callback(service_classes_callback_t callback) 167{ 168 service_classes_callback = callback; 169} 170 171void create_ext_inquiry_response(const char *name, uint8_t *data) 172{ 173 sdp_list_t *list = sdp_get_record_list(); 174 uint8_t *ptr = data; 175 uint16_t uuid[24]; 176 int i, index = 0; 177 178 if (name) { 179 int len = strlen(name); 180 181 if (len > 48) { 182 len = 48; 183 ptr[1] = 0x08; 184 } else 185 ptr[1] = 0x09; 186 187 ptr[0] = len + 1; 188 189 memcpy(ptr + 2, name, len); 190 191 ptr += len + 2; 192 } 193 194 if (did_vendor != 0x0000) { 195 uint16_t source = 0x0002; 196 *ptr++ = 9; 197 *ptr++ = 11; 198 *ptr++ = (source & 0x00ff); 199 *ptr++ = (source & 0xff00) >> 8; 200 *ptr++ = (did_vendor & 0x00ff); 201 *ptr++ = (did_vendor & 0xff00) >> 8; 202 *ptr++ = (did_product & 0x00ff); 203 *ptr++ = (did_product & 0xff00) >> 8; 204 *ptr++ = (did_version & 0x00ff); 205 *ptr++ = (did_version & 0xff00) >> 8; 206 } 207 208 ptr[1] = 0x03; 209 210 for (; list; list = list->next) { 211 sdp_record_t *rec = (sdp_record_t *) list->data; 212 213 if (rec->svclass.type != SDP_UUID16) 214 continue; 215 216 if (rec->svclass.value.uuid16 < 0x1100) 217 continue; 218 219 if (index > 23) { 220 ptr[1] = 0x02; 221 break; 222 } 223 224 for (i = 0; i < index; i++) 225 if (uuid[i] == rec->svclass.value.uuid16) 226 break; 227 228 if (i == index - 1) 229 continue; 230 231 uuid[index++] = rec->svclass.value.uuid16; 232 } 233 234 if (index > 0) { 235 ptr[0] = (index * 2) + 1; 236 ptr += 2; 237 238 for (i = 0; i < index; i++) { 239 *ptr++ = (uuid[i] & 0x00ff); 240 *ptr++ = (uuid[i] & 0xff00) >> 8; 241 } 242 } 243} 244 245void register_public_browse_group(void) 246{ 247 sdp_list_t *browselist; 248 uuid_t bgscid, pbgid; 249 sdp_data_t *sdpdata; 250 sdp_record_t *browse = sdp_record_alloc(); 251 252 browse->handle = SDP_SERVER_RECORD_HANDLE + 1; 253 254 sdp_record_add(BDADDR_ANY, browse); 255 sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle); 256 sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata); 257 258 sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID); 259 browselist = sdp_list_append(0, &bgscid); 260 sdp_set_service_classes(browse, browselist); 261 sdp_list_free(browselist, 0); 262 263 sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP); 264 sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID, 265 SDP_UUID16, &pbgid.value.uuid16); 266} 267 268/* 269 * The SDP server must present its own service record to 270 * the service repository. This can be accessed by service 271 * discovery clients. This method constructs a service record 272 * and stores it in the repository 273 */ 274void register_server_service(void) 275{ 276 sdp_list_t *classIDList; 277 uuid_t classID; 278 void **versions, **versionDTDs; 279 uint8_t dtd; 280 sdp_data_t *pData; 281 int i; 282 283 server = sdp_record_alloc(); 284 server->pattern = NULL; 285 286 /* Force the record to be SDP_SERVER_RECORD_HANDLE */ 287 server->handle = SDP_SERVER_RECORD_HANDLE; 288 289 sdp_record_add(BDADDR_ANY, server); 290 sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE, 291 sdp_data_alloc(SDP_UINT32, &server->handle)); 292 293 sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID); 294 classIDList = sdp_list_append(0, &classID); 295 sdp_set_service_classes(server, classIDList); 296 sdp_list_free(classIDList, 0); 297 298 /* 299 * Set the version numbers supported, these are passed as arguments 300 * to the server on command line. Now defaults to 1.0 301 * Build the version number sequence first 302 */ 303 versions = (void **)malloc(sdpServerVnumEntries * sizeof(void *)); 304 versionDTDs = (void **)malloc(sdpServerVnumEntries * sizeof(void *)); 305 dtd = SDP_UINT16; 306 for (i = 0; i < sdpServerVnumEntries; i++) { 307 uint16_t *version = malloc(sizeof(uint16_t)); 308 *version = sdpVnumArray[i].major; 309 *version = (*version << 8); 310 *version |= sdpVnumArray[i].minor; 311 versions[i] = version; 312 versionDTDs[i] = &dtd; 313 } 314 pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries); 315 for (i = 0; i < sdpServerVnumEntries; i++) 316 free(versions[i]); 317 free(versions); 318 free(versionDTDs); 319 sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData); 320 321 update_db_timestamp(); 322 update_svclass_list(BDADDR_ANY); 323} 324 325void register_device_id(const uint16_t vendor, const uint16_t product, 326 const uint16_t version) 327{ 328 const uint16_t spec = 0x0102, source = 0x0002; 329 const uint8_t primary = 1; 330 sdp_list_t *class_list, *group_list, *profile_list; 331 uuid_t class_uuid, group_uuid; 332 sdp_data_t *sdp_data, *primary_data, *source_data; 333 sdp_data_t *spec_data, *vendor_data, *product_data, *version_data; 334 sdp_profile_desc_t profile; 335 sdp_record_t *record = sdp_record_alloc(); 336 337 info("Adding device id record for %04x:%04x", vendor, product); 338 339 did_vendor = vendor; 340 did_product = product; 341 did_version = version; 342 343 record->handle = sdp_next_handle(); 344 345 sdp_record_add(BDADDR_ANY, record); 346 sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle); 347 sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data); 348 349 sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID); 350 class_list = sdp_list_append(0, &class_uuid); 351 sdp_set_service_classes(record, class_list); 352 sdp_list_free(class_list, NULL); 353 354 sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP); 355 group_list = sdp_list_append(NULL, &group_uuid); 356 sdp_set_browse_groups(record, group_list); 357 sdp_list_free(group_list, NULL); 358 359 sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID); 360 profile.version = spec; 361 profile_list = sdp_list_append(NULL, &profile); 362 sdp_set_profile_descs(record, profile_list); 363 sdp_list_free(profile_list, NULL); 364 365 spec_data = sdp_data_alloc(SDP_UINT16, &spec); 366 sdp_attr_add(record, 0x0200, spec_data); 367 368 vendor_data = sdp_data_alloc(SDP_UINT16, &vendor); 369 sdp_attr_add(record, 0x0201, vendor_data); 370 371 product_data = sdp_data_alloc(SDP_UINT16, &product); 372 sdp_attr_add(record, 0x0202, product_data); 373 374 version_data = sdp_data_alloc(SDP_UINT16, &version); 375 sdp_attr_add(record, 0x0203, version_data); 376 377 primary_data = sdp_data_alloc(SDP_BOOL, &primary); 378 sdp_attr_add(record, 0x0204, primary_data); 379 380 source_data = sdp_data_alloc(SDP_UINT16, &source); 381 sdp_attr_add(record, 0x0205, source_data); 382 383 update_db_timestamp(); 384 update_svclass_list(BDADDR_ANY); 385} 386 387int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec) 388{ 389 sdp_data_t *data; 390 sdp_list_t *pattern; 391 392 if (rec->handle == 0xffffffff) { 393 rec->handle = sdp_next_handle(); 394 if (rec->handle < 0x10000) 395 return -1; 396 } else { 397 if (sdp_record_find(rec->handle)) 398 return -1; 399 } 400 401 debug("Adding record with handle 0x%05x", rec->handle); 402 403 sdp_record_add(src, rec); 404 405 data = sdp_data_alloc(SDP_UINT32, &rec->handle); 406 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); 407 408 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 409 uuid_t uuid; 410 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 411 sdp_pattern_add_uuid(rec, &uuid); 412 } 413 414 for (pattern = rec->pattern; pattern; pattern = pattern->next) { 415 char uuid[32]; 416 417 if (pattern->data == NULL) 418 continue; 419 420 sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid)); 421 debug("Record pattern UUID %s", uuid); 422 } 423 424 update_db_timestamp(); 425 update_svclass_list(src); 426 427 return 0; 428} 429 430int remove_record_from_server(uint32_t handle) 431{ 432 sdp_record_t *rec; 433 434 debug("Removing record with handle 0x%05x", handle); 435 436 rec = sdp_record_find(handle); 437 if (!rec) 438 return -ENOENT; 439 440 if (sdp_record_remove(handle) == 0) { 441 update_db_timestamp(); 442 update_svclass_list(BDADDR_ANY); 443 } 444 445 sdp_record_free(rec); 446 447 return 0; 448} 449 450/* FIXME: refactor for server-side */ 451static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, 452 unsigned int bufsize, 453 uint32_t handleExpected, int *scanned) 454{ 455 int extractStatus = -1, localExtractedLength = 0; 456 uint8_t dtd; 457 int seqlen = 0; 458 sdp_record_t *rec = NULL; 459 uint16_t attrId, lookAheadAttrId; 460 sdp_data_t *pAttr = NULL; 461 uint32_t handle = 0xffffffff; 462 463 *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen); 464 p += *scanned; 465 bufsize -= *scanned; 466 467 if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) { 468 SDPDBG("Unexpected end of packet"); 469 return NULL; 470 } 471 472 lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t)))); 473 474 SDPDBG("Look ahead attr id : %d", lookAheadAttrId); 475 476 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 477 if (bufsize < (sizeof(uint8_t) * 2) + 478 sizeof(uint16_t) + sizeof(uint32_t)) { 479 SDPDBG("Unexpected end of packet"); 480 return NULL; 481 } 482 handle = ntohl(bt_get_unaligned((uint32_t *) (p + 483 sizeof(uint8_t) + sizeof(uint16_t) + 484 sizeof(uint8_t)))); 485 SDPDBG("SvcRecHandle : 0x%x", handle); 486 rec = sdp_record_find(handle); 487 } else if (handleExpected != 0xffffffff) 488 rec = sdp_record_find(handleExpected); 489 490 if (!rec) { 491 rec = sdp_record_alloc(); 492 rec->attrlist = NULL; 493 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 494 rec->handle = handle; 495 sdp_record_add(device, rec); 496 } else if (handleExpected != 0xffffffff) { 497 rec->handle = handleExpected; 498 sdp_record_add(device, rec); 499 } 500 } else { 501 sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free); 502 rec->attrlist = NULL; 503 } 504 505 while (localExtractedLength < seqlen) { 506 int attrSize = sizeof(uint8_t); 507 int attrValueLength = 0; 508 509 if (bufsize < attrSize + sizeof(uint16_t)) { 510 SDPDBG("Unexpected end of packet: Terminating extraction of attributes"); 511 break; 512 } 513 514 SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d", 515 seqlen, localExtractedLength); 516 dtd = *(uint8_t *) p; 517 518 attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize))); 519 attrSize += sizeof(uint16_t); 520 521 SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId); 522 523 pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize, 524 &attrValueLength, rec); 525 526 SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength); 527 528 attrSize += attrValueLength; 529 if (pAttr == NULL) { 530 SDPDBG("Terminating extraction of attributes"); 531 break; 532 } 533 localExtractedLength += attrSize; 534 p += attrSize; 535 bufsize -= attrSize; 536 sdp_attr_replace(rec, attrId, pAttr); 537 extractStatus = 0; 538 SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d", 539 seqlen, localExtractedLength); 540 } 541 542 if (extractStatus == 0) { 543 SDPDBG("Successful extracting of Svc Rec attributes"); 544#ifdef SDP_DEBUG 545 sdp_print_service_attr(rec->attrlist); 546#endif 547 *scanned += seqlen; 548 } 549 return rec; 550} 551 552/* 553 * Add the newly created service record to the service repository 554 */ 555int service_register_req(sdp_req_t *req, sdp_buf_t *rsp) 556{ 557 int scanned = 0; 558 sdp_data_t *handle; 559 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 560 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 561 sdp_record_t *rec; 562 563 req->flags = *p++; 564 if (req->flags & SDP_DEVICE_RECORD) { 565 bacpy(&req->device, (bdaddr_t *) p); 566 p += sizeof(bdaddr_t); 567 bufsize -= sizeof(bdaddr_t); 568 } 569 570 // save image of PDU: we need it when clients request this attribute 571 rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned); 572 if (!rec) 573 goto invalid; 574 575 if (rec->handle == 0xffffffff) { 576 rec->handle = sdp_next_handle(); 577 if (rec->handle < 0x10000) { 578 sdp_record_free(rec); 579 goto invalid; 580 } 581 } else { 582 if (sdp_record_find(rec->handle)) { 583 /* extract_pdu_server will add the record handle 584 * if it is missing. So instead of failing, skip 585 * the record adding to avoid duplication. */ 586 goto success; 587 } 588 } 589 590 sdp_record_add(&req->device, rec); 591 if (!(req->flags & SDP_RECORD_PERSIST)) 592 sdp_svcdb_set_collectable(rec, req->sock); 593 594 handle = sdp_data_alloc(SDP_UINT32, &rec->handle); 595 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle); 596 597success: 598 /* if the browse group descriptor is NULL, 599 * ensure that the record belongs to the ROOT group */ 600 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 601 uuid_t uuid; 602 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 603 sdp_pattern_add_uuid(rec, &uuid); 604 } 605 606 update_db_timestamp(); 607 update_svclass_list(BDADDR_ANY); 608 609 /* Build a rsp buffer */ 610 bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data); 611 rsp->data_size = sizeof(uint32_t); 612 613 return 0; 614 615invalid: 616 bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data); 617 rsp->data_size = sizeof(uint16_t); 618 619 return -1; 620} 621 622/* 623 * Update a service record 624 */ 625int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) 626{ 627 sdp_record_t *orec; 628 int status = 0, scanned = 0; 629 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 630 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 631 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 632 633 SDPDBG("Svc Rec Handle: 0x%x", handle); 634 635 p += sizeof(uint32_t); 636 bufsize -= sizeof(uint32_t); 637 638 orec = sdp_record_find(handle); 639 640 SDPDBG("SvcRecOld: %p", orec); 641 642 if (orec) { 643 sdp_record_t *nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, 644 handle, &scanned); 645 if (nrec && handle == nrec->handle) { 646 update_db_timestamp(); 647 update_svclass_list(BDADDR_ANY); 648 } else { 649 SDPDBG("SvcRecHandle : 0x%x", handle); 650 SDPDBG("SvcRecHandleNew : 0x%x", nrec->handle); 651 SDPDBG("SvcRecNew : %p", nrec); 652 SDPDBG("SvcRecOld : %p", orec); 653 SDPDBG("Failure to update, restore old value"); 654 655 status = SDP_INVALID_SYNTAX; 656 } 657 658 if (nrec) 659 sdp_record_free(nrec); 660 } else 661 status = SDP_INVALID_RECORD_HANDLE; 662 663 p = rsp->data; 664 bt_put_unaligned(htons(status), (uint16_t *) p); 665 rsp->data_size = sizeof(uint16_t); 666 return status; 667} 668 669/* 670 * Remove a registered service record 671 */ 672int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp) 673{ 674 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 675 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 676 sdp_record_t *rec; 677 int status = 0; 678 679 /* extract service record handle */ 680 p += sizeof(uint32_t); 681 682 rec = sdp_record_find(handle); 683 if (rec) { 684 sdp_svcdb_collect(rec); 685 status = sdp_record_remove(handle); 686 sdp_record_free(rec); 687 if (status == 0) { 688 update_db_timestamp(); 689 update_svclass_list(BDADDR_ANY); 690 } 691 } else { 692 status = SDP_INVALID_RECORD_HANDLE; 693 SDPDBG("Could not find record : 0x%x", handle); 694 } 695 696 p = rsp->data; 697 bt_put_unaligned(htons(status), (uint16_t *) p); 698 rsp->data_size = sizeof(uint16_t); 699 700 return status; 701} 702