/****************************************************************************** * * Copyright 2014 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #define LOG_TAG "bt_btif_sock_sdp" #include "btif/include/btif_sock_sdp.h" #include #include "bta/include/bta_jv_api.h" #include "bta/include/bta_op_api.h" #include "bta/include/utl.h" #include "bta/pb/bta_pbs_int.h" #include "bta/sys/bta_sys.h" #include "internal_include/bt_target.h" #include "stack/include/bt_types.h" #include "stack/include/bt_uuid16.h" #include "stack/include/sdp_api.h" #include "stack/include/sdpdefs.h" #include "types/bluetooth/uuid.h" using namespace bluetooth::legacy::stack::sdp; using namespace bluetooth; using bluetooth::Uuid; // This module provides an abstraction on top of the lower-level SDP database // code for registration and discovery of various bluetooth sockets. // // This code also provides for on-demand registration of "pre-registered" // services as a backwards compatibility function to third-party applications // expecting a bluez stack. // Realm Character Set -- 0 is ASCII #define BTA_PBS_REALM_CHARSET 0 // Specifies whether or not client's user id is required during obex // authentication #define BTA_PBS_USERID_REQ FALSE static const tBTA_PBS_CFG bta_pbs_cfg = { BTA_PBS_REALM_CHARSET, // realm_charset: Server only BTA_PBS_USERID_REQ, // userid_req: Server only (BTA_PBS_SUPF_DOWNLOAD | BTA_PBS_SURF_BROWSE), // supported_features BTA_PBS_REPOSIT_LOCAL, // supported_repositories }; // object format lookup table #define OBEX_PUSH_NUM_FORMATS 7 static const tBTA_OP_FMT bta_ops_obj_fmt[OBEX_PUSH_NUM_FORMATS] = { BTA_OP_VCARD21_FMT, BTA_OP_VCARD30_FMT, BTA_OP_VCAL_FMT, BTA_OP_ICAL_FMT, BTA_OP_VNOTE_FMT, BTA_OP_VMSG_FMT, BTA_OP_OTHER_FMT}; // TODO(jtgans): Figure out if we actually need this define. This is ifndef // defined in bt_target.h, but nowhere else, so right now, unless something // overrides this before bt_target.h sets it, it will always be bt_target.h's // version. #ifndef BTUI_OPS_FORMATS #define BTUI_OPS_FORMATS \ (BTA_OP_VCARD21_MASK | BTA_OP_VCARD30_MASK | BTA_OP_VCAL_MASK | \ BTA_OP_ICAL_MASK | BTA_OP_VNOTE_MASK | BTA_OP_VMSG_MASK | BTA_OP_ANY_MASK) #endif #define RESERVED_SCN_PBS 19 #define RESERVED_SCN_OPS 12 #define UUID_MAX_LENGTH 16 #define SPP_PROFILE_VERSION 0x0102 // Adds a protocol list and service name (if provided) to an SDP record given by // |sdp_handle|, and marks it as browseable. This is a shortcut for defining a // set of protocols that includes L2CAP, RFCOMM, and optionally OBEX. If // |with_obex| is |true|, then an additional OBEX protocol UUID will be included // at the end of the protocol list. // // Returns true if successful, otherwise false. static bool create_base_record(const uint32_t sdp_handle, const char* name, const uint16_t channel, const bool with_obex) { log::verbose("create_base_record: scn: {}, name: {}, with_obex: {}", channel, name, with_obex); // Setup the protocol list and add it. tSDP_PROTOCOL_ELEM proto_list[SDP_MAX_LIST_ELEMS]; int num_proto_elements = with_obex ? 3 : 2; memset(proto_list, 0, num_proto_elements * sizeof(tSDP_PROTOCOL_ELEM)); proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; proto_list[0].num_params = 0; proto_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; proto_list[1].num_params = 1; proto_list[1].params[0] = channel; if (with_obex) { proto_list[2].protocol_uuid = UUID_PROTOCOL_OBEX; proto_list[2].num_params = 0; } // Mark the service as browseable. uint16_t list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; const char* stage = "protocol_list"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddProtocolList( sdp_handle, num_proto_elements, proto_list)) goto error; // Add the name to the SDP record. if (name[0] != '\0') { stage = "service_name"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddAttribute( sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, (uint32_t)strlen(name), (uint8_t*)name)) goto error; } stage = "browseable"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddUuidSequence( sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &list)) goto error; log::verbose( "create_base_record: successfully created base service record, handle: " "0x{:08x}, scn: {}, name: {}, with_obex: {}", sdp_handle, channel, name, with_obex); return true; error: log::error( "create_base_record: failed to create base service record, stage: {}, " "scn: {}, name: {}, with_obex: {}", stage, channel, name, with_obex); return false; } // Registers a service with the given |name|, |uuid|, and |channel| in the SDP // database as a generic L2CAP RFCOMM protocol, storing its |uuid| as a service // class sequence. static int add_sdp_by_uuid(const char* name, const Uuid& uuid, const uint16_t channel) { log::verbose("uuid: {}, scn: {}, service_name: {}", uuid.ToString(), channel, name); uint32_t handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); if (handle == 0) { log::error("failed to create sdp record, scn: {}, service_name: {}", channel, name); return 0; } // Convert the |uuid| into a big-endian representation and add it as a // sequence. uint8_t type = UUID_DESC_TYPE; uint8_t type_len = UUID_MAX_LENGTH; uint8_t type_buf[48]; // Store the address of type buf in a pointer on the stack, so we can pass // a double pointer to SDP_AddSequence uint8_t* type_buf_ptr = type_buf; uint8_t* tmp = type_buf; // Create the base SDP record. const char* stage = "create_base_record"; if (!create_base_record(handle, name, channel, false /* with_obex */)) goto error; // Do the conversion to big-endian -- tmp is only used to iterate through the // UUID array in the macro and serves no other purpose as the conversion // macros are not hygenic. { ARRAY_TO_BE_STREAM(tmp, uuid.To128BitBE().data(), UUID_MAX_LENGTH); } stage = "service_class_sequence"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddSequence( handle, (uint16_t)ATTR_ID_SERVICE_CLASS_ID_LIST, 1, &type, &type_len, &type_buf_ptr)) goto error; log::verbose( "service registered successfully, service_name: {}, handle: 0x{:08x}", name, handle); { // Write the custom 128-bit UUID to EIR tBTA_CUSTOM_UUID curr = {uuid, handle}; bta_sys_add_cust_uuid(curr); } return handle; error: if (get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) { log::warn("Unable to delete SDP record handle:{}", handle); } log::error("failed to register service stage: {}, service_name: {}", stage, name); return 0; } // Registers a service with the given |name| and |channel| in the SDP // database as a PBAP protocol. static int add_pbap_sdp(const char* name, const int channel) { log::verbose("add_pbap_sdp: scn {}, service_name {}", channel, name); uint32_t handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); if (handle == 0) { log::error("add_pbap_sdp: failed to create sdp record, service_name: {}", name); return 0; } uint16_t service = UUID_SERVCLASS_PBAP_PSE; // Create the base SDP record. const char* stage = "create_base_record"; if (!create_base_record(handle, name, channel, true /* with_obex */)) goto error; // Add service class stage = "service_class"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(handle, 1, &service)) goto error; // Add in the phone access descriptor stage = "profile_descriptor_list"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList( handle, UUID_SERVCLASS_PHONE_ACCESS, BTA_PBS_DEFAULT_VERSION)) goto error; // Set up our supported repositories stage = "supported_repositories"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddAttribute( handle, ATTR_ID_SUPPORTED_REPOSITORIES, UINT_DESC_TYPE, 1, (uint8_t*)&bta_pbs_cfg.supported_repositories)) goto error; // Notify the system that we've got a new service class UUID. bta_sys_add_uuid(UUID_SERVCLASS_PBAP_PSE); log::verbose( "add_pbap_sdp: service registered successfully, service_name: {}, " "handle: 0x{:08x}", name, handle); return handle; error: if (get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) { log::warn("Unable to delete SDP record handle:{}", handle); } log::error( "add_pbap_sdp: failed to register PBAP service, stage: {}, service_name: " "{}", stage, name); return 0; } // Registers a service with the given |name| and |channel| as an OBEX Push // protocol. static int add_ops_sdp(const char* name, const int channel) { log::verbose("add_ops_sdp: scn {}, service_name {}", channel, name); uint32_t handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); if (handle == 0) { log::error("add_ops_sdp: failed to create sdp record, service_name: {}", name); return 0; } // Add sequence for supported types. uint8_t desc_type[OBEX_PUSH_NUM_FORMATS]; uint8_t type_len[OBEX_PUSH_NUM_FORMATS]; uint8_t* type_value[OBEX_PUSH_NUM_FORMATS]; uint8_t j = 0; uint16_t service = UUID_SERVCLASS_OBEX_OBJECT_PUSH; tBTA_UTL_COD cod; // Create the base SDP record. const char* stage = "create_base_record"; if (!create_base_record(handle, name, channel, true /* with_obex */)) goto error; // Add service class. stage = "service_class"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(handle, 1, &service)) goto error; // Add the OBEX push profile descriptor. stage = "profile_descriptor_list"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList( handle, UUID_SERVCLASS_OBEX_OBJECT_PUSH, 0x0100)) goto error; for (int i = 0; i < OBEX_PUSH_NUM_FORMATS; i++) { if ((BTUI_OPS_FORMATS >> i) & 1) { type_value[j] = (uint8_t*)(&bta_ops_obj_fmt[i]); desc_type[j] = UINT_DESC_TYPE; type_len[j++] = 1; } } stage = "supported_types"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddSequence( handle, (uint16_t)ATTR_ID_SUPPORTED_FORMATS_LIST, j, desc_type, type_len, type_value)) goto error; // Set class of device. cod.service = BTM_COD_SERVICE_OBJ_TRANSFER; stage = "class_of_device"; if (!utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS)) goto error; // Notify the system that we've got a new service class UUID. bta_sys_add_uuid(UUID_SERVCLASS_OBEX_OBJECT_PUSH); log::verbose( "ad_maps_sdp: service registered successfully, service_name: {}, handle " "0x{:08x})", name, handle); return handle; error: if (get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) { log::warn("Unable to delete SDP record handle:{}", handle); } log::error( "add_ops_sdp: failed to register OPS service, stage: {}, service_name: " "{}", stage, name); return 0; } // Registers a service with the given |name| and |channel| as a serial port // profile protocol. static int add_spp_sdp(const char* name, const int channel) { log::verbose("add_spp_sdp: scn {}, service_name {}", channel, name); int handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); if (handle == 0) { log::error("add_spp_sdp: failed to create sdp record, service_name: {}", name); return 0; } // Create the base SDP record. const char* stage = "create_base_record"; uint16_t service = UUID_SERVCLASS_SERIAL_PORT; if (!create_base_record(handle, name, channel, false /* with_obex */)) goto error; stage = "service_class"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(handle, 1, &service)) goto error; stage = "profile_descriptor_list"; if (!get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList( handle, UUID_SERVCLASS_SERIAL_PORT, SPP_PROFILE_VERSION)) goto error; log::verbose( "add_spp_sdp: service registered successfully, service_name: {}, handle " "0x{:08x})", name, handle); return handle; error: if (get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) { log::warn("Unable to delete SDP record handle:{}", handle); } log::error( "add_spp_sdp: failed to register SPP service, stage: {}, service_name: " "{}", stage, name); return 0; } // Adds an RFCOMM SDP record for a service with the given |name|, |uuid|, and // |channel|. This function attempts to identify the type of the service based // upon its |uuid|, and will override the |channel| with a reserved channel // number if the |uuid| matches one of the preregistered bluez SDP records. static int add_rfc_sdp_by_uuid(const char* name, const Uuid& uuid, const int channel) { log::verbose("uuid: {}, service_name: {}, channel: {}", uuid.ToString(), name, channel); /* * Bluetooth Socket API relies on having preregistered bluez sdp records for * HSAG, HFAG, OPP & PBAP that are mapped to rc chan 10, 11,12 & 19. Today * HSAG and HFAG is routed to BRCM AG and are not using BT socket API so for * now we will need to support OPP and PBAP to enable 3rd party developer apps * running on BRCM Android. * * To do this we will check the UUID for the requested service and mimic the * SDP records of bluez upon reception. See functions add_opush() and * add_pbap() in sdptool.c for actual records. */ int final_channel = get_reserved_rfc_channel(uuid); if (final_channel == -1) { final_channel = channel; } int handle = 0; if (uuid == UUID_OBEX_OBJECT_PUSH) { handle = add_ops_sdp(name, final_channel); } else if (uuid == UUID_PBAP_PSE) { // PBAP Server is always channel 19 handle = add_pbap_sdp(name, final_channel); } else if (uuid == UUID_SPP) { handle = add_spp_sdp(name, final_channel); } else if (uuid == UUID_MAP_MAS) { // Record created by new SDP create record interface handle = 0xff; } else { handle = add_sdp_by_uuid(name, uuid, final_channel); } return handle; } bool is_reserved_rfc_channel(const int channel) { switch (channel) { case RESERVED_SCN_PBS: case RESERVED_SCN_OPS: return true; } return false; } int get_reserved_rfc_channel(const bluetooth::Uuid& uuid) { if (uuid == UUID_PBAP_PSE) { return RESERVED_SCN_PBS; } else if (uuid == UUID_OBEX_OBJECT_PUSH) { return RESERVED_SCN_OPS; } return -1; } // Adds an SDP record to the SDP database using the given |name|, |uuid|, and // |channel|. Note that if the |uuid| is empty, the |uuid| will be set based // upon the |channel| passed in. int add_rfc_sdp_rec(const char* name, Uuid uuid, const int channel) { if (uuid.IsEmpty()) { switch (channel) { case RESERVED_SCN_PBS: // PBAP Reserved port uuid = UUID_PBAP_PSE; break; case RESERVED_SCN_OPS: uuid = UUID_OBEX_OBJECT_PUSH; break; default: uuid = UUID_SPP; break; } } return add_rfc_sdp_by_uuid(name, uuid, channel); } // Deletes an SDP record with the given |handle|. void del_rfc_sdp_rec(int handle) { log::verbose("del_rfc_sdp_rec: handle:0x{:x}", handle); if ((handle != -1) && (handle != 0)) { // Remove the custom 128-bit UUID from EIR const tBTA_CUSTOM_UUID curr = {Uuid::kEmpty, (uint32_t)handle}; bta_sys_remove_cust_uuid(curr); BTA_JvDeleteRecord(handle); } }