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