diff options
Diffstat (limited to 'src/components/transport_manager/src/bluetooth/bluetooth_device_scanner_posix.cc')
-rw-r--r-- | src/components/transport_manager/src/bluetooth/bluetooth_device_scanner_posix.cc | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner_posix.cc b/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner_posix.cc new file mode 100644 index 0000000000..6b662fbe1b --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner_posix.cc @@ -0,0 +1,511 @@ +/* + * \file bluetooth_device_scanner.cc + * \brief BluetoothDeviceScanner class header file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "transport_manager/bluetooth/bluetooth_device_scanner.h" + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/rfcomm.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> +#include <vector> +#include <sstream> +#include "transport_manager/bluetooth/bluetooth_transport_adapter.h" +#include "transport_manager/bluetooth/bluetooth_device.h" + +#include "utils/logger.h" +#include "utils/threads/thread.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +namespace { +char* SplitToAddr(char* dev_list_entry) { + char* bl_address = strtok(dev_list_entry, "()"); + if (bl_address != NULL) { + bl_address = strtok(NULL, "()"); + return bl_address; + } else { + return NULL; + } +} + +int FindPairedDevs(std::vector<bdaddr_t>* result) { + LOGGER_TRACE(logger_, "enter. result adress: " << result); + DCHECK(result != NULL); + + const char* cmd = "bt-device -l"; + + FILE* pipe = popen(cmd, "r"); + if (!pipe) { + LOGGER_TRACE(logger_, "exit -1. Condition: !pipe"); + return -1; + } + char* buffer = new char[1028]; + size_t counter = 0; + while (fgets(buffer, 1028, pipe) != NULL) { + if (0 < counter++) { // skip first line + char* bt_address = SplitToAddr(buffer); + if (bt_address) { + bdaddr_t address; + str2ba(bt_address, &address); + result->push_back(address); + } + } + delete[] buffer; + buffer = new char[1028]; + } + pclose(pipe); + LOGGER_TRACE(logger_, "exit with 0"); + return 0; +} +} // namespace + +BluetoothDeviceScanner::BluetoothDeviceScanner( + TransportAdapterController* controller, + bool auto_repeat_search, + int auto_repeat_pause_sec) + : controller_(controller) + , thread_(NULL) + , shutdown_requested_(false) + , ready_(true) + , device_scan_requested_(false) + , device_scan_requested_lock_() + , device_scan_requested_cv_() + , auto_repeat_search_(auto_repeat_search) + , auto_repeat_pause_sec_(auto_repeat_pause_sec) { + uint8_t smart_device_link_service_uuid_data[] = {0x93, + 0x6D, + 0xA0, + 0x1F, + 0x9A, + 0xBD, + 0x4D, + 0x9D, + 0x80, + 0xC7, + 0x02, + 0xAF, + 0x85, + 0xC8, + 0x22, + 0xA8}; + sdp_uuid128_create(&smart_device_link_service_uuid_, + smart_device_link_service_uuid_data); + thread_ = threads::CreateThread("BT Device Scaner", + new BluetoothDeviceScannerDelegate(this)); +} + +BluetoothDeviceScanner::~BluetoothDeviceScanner() { + thread_->join(); + delete thread_->delegate(); + threads::DeleteThread(thread_); +} + +bool BluetoothDeviceScanner::IsInitialised() const { + return thread_ && thread_->is_running(); +} + +void BluetoothDeviceScanner::UpdateTotalDeviceList() { + LOGGER_AUTO_TRACE(logger_); + DeviceVector devices; + devices.insert(devices.end(), + paired_devices_with_sdl_.begin(), + paired_devices_with_sdl_.end()); + devices.insert(devices.end(), + found_devices_with_sdl_.begin(), + found_devices_with_sdl_.end()); + controller_->SearchDeviceDone(devices); +} + +void BluetoothDeviceScanner::DoInquiry() { + LOGGER_AUTO_TRACE(logger_); + + const int device_id = hci_get_route(0); + if (device_id < 0) { + LOGGER_INFO(logger_, "HCI device is not available"); + shutdown_requested_ = true; + controller_->SearchDeviceFailed(SearchDeviceError()); + return; + } + + int device_handle = hci_open_dev(device_id); + if (device_handle < 0) { + controller_->SearchDeviceFailed(SearchDeviceError()); + return; + } + + if (paired_devices_.empty()) { + LOGGER_INFO(logger_, "Searching for paired devices."); + if (-1 == FindPairedDevs(&paired_devices_)) { + LOGGER_ERROR(logger_, "Failed to retrieve list of paired devices."); + controller_->SearchDeviceFailed(SearchDeviceError()); + } + } + + LOGGER_INFO(logger_, + "Check rfcomm channel on " << paired_devices_.size() + << " paired devices."); + + paired_devices_with_sdl_.clear(); + CheckSDLServiceOnDevices( + paired_devices_, device_handle, &paired_devices_with_sdl_); + UpdateTotalDeviceList(); + + LOGGER_INFO(logger_, "Starting hci_inquiry on device " << device_id); + const uint8_t inquiry_time = 8u; // Time unit is 1.28 seconds + const size_t max_devices = 256u; + inquiry_info* inquiry_info_list = new inquiry_info[max_devices]; + + const int number_of_devices = hci_inquiry(device_id, + inquiry_time, + max_devices, + 0, + &inquiry_info_list, + IREQ_CACHE_FLUSH); + + if (number_of_devices >= 0) { + LOGGER_INFO(logger_, + "hci_inquiry: found " << number_of_devices << " devices"); + std::vector<bdaddr_t> found_devices(number_of_devices); + for (int i = 0; i < number_of_devices; ++i) { + found_devices[i] = inquiry_info_list[i].bdaddr; + } + found_devices_with_sdl_.clear(); + CheckSDLServiceOnDevices( + found_devices, device_handle, &found_devices_with_sdl_); + } + UpdateTotalDeviceList(); + controller_->FindNewApplicationsRequest(); + + close(device_handle); + delete[] inquiry_info_list; + + if (number_of_devices < 0) { + LOGGER_DEBUG(logger_, "number_of_devices < 0"); + controller_->SearchDeviceFailed(SearchDeviceError()); + } +} + +void BluetoothDeviceScanner::CheckSDLServiceOnDevices( + const std::vector<bdaddr_t>& bd_addresses, + int device_handle, + DeviceVector* discovered_devices) { + LOGGER_TRACE(logger_, + "enter. bd_addresses: " + << &bd_addresses << ", device_handle: " << device_handle + << ", discovered_devices: " << discovered_devices); + std::vector<RfcommChannelVector> sdl_rfcomm_channels = + DiscoverSmartDeviceLinkRFCOMMChannels(bd_addresses); + + for (size_t i = 0; i < bd_addresses.size(); ++i) { + if (sdl_rfcomm_channels[i].empty()) { + continue; + } + + const bdaddr_t& bd_address = bd_addresses[i]; + char deviceName[256]; + int hci_read_remote_name_ret = + hci_read_remote_name(device_handle, + &bd_address, + sizeof(deviceName) / sizeof(deviceName[0]), + deviceName, + 0); + + if (hci_read_remote_name_ret != 0) { + LOGGER_ERROR_WITH_ERRNO(logger_, "hci_read_remote_name failed"); + strncpy(deviceName, + BluetoothDevice::GetUniqueDeviceId(bd_address).c_str(), + sizeof(deviceName) / sizeof(deviceName[0])); + } + + Device* bluetooth_device = + new BluetoothDevice(bd_address, deviceName, sdl_rfcomm_channels[i]); + if (bluetooth_device) { + LOGGER_INFO(logger_, "Bluetooth device created successfully"); + discovered_devices->push_back(bluetooth_device); + } else { + LOGGER_WARN(logger_, "Can't create bluetooth device " << deviceName); + } + } + LOGGER_TRACE(logger_, "exit"); +} + +std::vector<BluetoothDeviceScanner::RfcommChannelVector> +BluetoothDeviceScanner::DiscoverSmartDeviceLinkRFCOMMChannels( + const std::vector<bdaddr_t>& device_addresses) { + LOGGER_TRACE(logger_, "enter device_addresses: " << &device_addresses); + const size_t size = device_addresses.size(); + std::vector<RfcommChannelVector> result(size); + + static const int attempts = 4; + static const int attempt_timeout = 5; + std::vector<bool> processed(size, false); + unsigned processed_count = 0; + for (int nattempt = 0; nattempt < attempts; ++nattempt) { + for (size_t i = 0; i < size; ++i) { + if (processed[i]) { + continue; + } + const bool final = DiscoverSmartDeviceLinkRFCOMMChannels( + device_addresses[i], &result[i]); + if (final) { + processed[i] = true; + ++processed_count; + } + } + if (++processed_count >= size) { + break; + } + sleep(attempt_timeout); + } + LOGGER_TRACE( + logger_, + "exit with vector<RfcommChannelVector>: size = " << result.size()); + return result; +} + +bool BluetoothDeviceScanner::DiscoverSmartDeviceLinkRFCOMMChannels( + const bdaddr_t& device_address, RfcommChannelVector* channels) { + LOGGER_TRACE(logger_, + "enter. device_address: " << &device_address + << ", channels: " << channels); + static bdaddr_t any_address = {{0, 0, 0, 0, 0, 0}}; + + sdp_session_t* sdp_session = sdp_connect( + &any_address, &device_address, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE); + if (sdp_session == 0) { + bool result = !(errno == 31 || errno == 16 || errno == 117 || errno == 114); + if (result) { + LOGGER_TRACE(logger_, "exit with TRUE. Condition: sdp_session == 0"); + } else { + LOGGER_TRACE(logger_, "exit with FALSE. Condition: sdp_session == 0"); + } + return result; + } + + sdp_list_t* search_list = + sdp_list_append(0, &smart_device_link_service_uuid_); + uint32_t range = 0x0000ffff; + sdp_list_t* attr_list = sdp_list_append(0, &range); + sdp_list_t* response_list = 0; + + if (0 == sdp_service_search_attr_req(sdp_session, + search_list, + SDP_ATTR_REQ_RANGE, + attr_list, + &response_list)) { + for (sdp_list_t* r = response_list; 0 != r; r = r->next) { + sdp_record_t* sdp_record = static_cast<sdp_record_t*>(r->data); + sdp_list_t* proto_list = 0; + + if (0 == sdp_get_access_protos(sdp_record, &proto_list)) { + for (sdp_list_t* p = proto_list; 0 != p; p = p->next) { + sdp_list_t* pdsList = static_cast<sdp_list_t*>(p->data); + + for (sdp_list_t* pds = pdsList; 0 != pds; pds = pds->next) { + sdp_data_t* sdpData = static_cast<sdp_data_t*>(pds->data); + int proto = 0; + + for (sdp_data_t* d = sdpData; 0 != d; d = d->next) { + switch (d->dtd) { + case SDP_UUID16: + case SDP_UUID32: + case SDP_UUID128: + proto = sdp_uuid_to_proto(&d->val.uuid); + break; + + case SDP_UINT8: + if (RFCOMM_UUID == proto) { + channels->push_back(d->val.uint8); + } + break; + } + } + } + + sdp_list_free(pdsList, 0); + } + + sdp_list_free(proto_list, 0); + } + } + } + sdp_list_free(search_list, 0); + sdp_list_free(attr_list, 0); + sdp_list_free(response_list, 0); + sdp_close(sdp_session); + + if (!channels->empty()) { + LOGGER_INFO(logger_, "channels not empty"); + std::stringstream rfcomm_channels_string; + + for (RfcommChannelVector::const_iterator it = channels->begin(); + it != channels->end(); + ++it) { + if (it != channels->begin()) { + rfcomm_channels_string << ", "; + } + rfcomm_channels_string << static_cast<uint32_t>(*it); + } + + LOGGER_INFO( + logger_, + "SmartDeviceLink service was discovered on device " + << BluetoothDevice::GetUniqueDeviceId(device_address) + << " at channel(s): " << rfcomm_channels_string.str().c_str()); + } else { + LOGGER_INFO(logger_, + "SmartDeviceLink service was not discovered on device " + << BluetoothDevice::GetUniqueDeviceId(device_address)); + } + LOGGER_TRACE(logger_, "exit with TRUE"); + return true; +} + +void BluetoothDeviceScanner::Thread() { + LOGGER_AUTO_TRACE(logger_); + ready_ = true; + if (auto_repeat_search_) { + while (!shutdown_requested_) { + DoInquiry(); + device_scan_requested_ = false; + TimedWaitForDeviceScanRequest(); + } + } else { // search only on demand + while (!shutdown_requested_) { + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + while (!(device_scan_requested_ || shutdown_requested_)) { + device_scan_requested_cv_.Wait(auto_lock); + } + } + DoInquiry(); + device_scan_requested_ = false; + } + } +} + +void BluetoothDeviceScanner::TimedWaitForDeviceScanRequest() { + LOGGER_AUTO_TRACE(logger_); + + if (auto_repeat_pause_sec_ == 0) { + LOGGER_TRACE(logger_, "exit. Condition: auto_repeat_pause_sec_ == 0"); + return; + } + + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + while (!(device_scan_requested_ || shutdown_requested_)) { + const sync_primitives::ConditionalVariable::WaitStatus wait_status = + device_scan_requested_cv_.WaitFor(auto_lock, + auto_repeat_pause_sec_ * 1000); + if (wait_status == sync_primitives::ConditionalVariable::kTimeout) { + LOGGER_INFO(logger_, "Bluetooth scanner timeout, performing scan"); + device_scan_requested_ = true; + } + } + } +} + +TransportAdapter::Error BluetoothDeviceScanner::Init() { + LOGGER_AUTO_TRACE(logger_); + if (!thread_->start()) { + LOGGER_ERROR(logger_, "Bluetooth device scanner thread start failed"); + return TransportAdapter::FAIL; + } + LOGGER_INFO(logger_, "Bluetooth device scanner thread started"); + return TransportAdapter::OK; +} + +void BluetoothDeviceScanner::Terminate() { + LOGGER_AUTO_TRACE(logger_); + shutdown_requested_ = true; + if (thread_) { + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + device_scan_requested_ = false; + device_scan_requested_cv_.NotifyOne(); + } + LOGGER_INFO(logger_, + "Waiting for bluetooth device scanner thread termination"); + thread_->stop(); + LOGGER_INFO(logger_, "Bluetooth device scanner thread stopped"); + } +} + +TransportAdapter::Error BluetoothDeviceScanner::Scan() { + LOGGER_AUTO_TRACE(logger_); + if ((!IsInitialised()) || shutdown_requested_) { + LOGGER_WARN(logger_, "BAD_STATE"); + return TransportAdapter::BAD_STATE; + } + if (auto_repeat_pause_sec_ == 0) { + return TransportAdapter::OK; + } + TransportAdapter::Error ret = TransportAdapter::OK; + + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + if (!device_scan_requested_) { + LOGGER_TRACE(logger_, "Requesting device Scan"); + device_scan_requested_ = true; + device_scan_requested_cv_.NotifyOne(); + } else { + ret = TransportAdapter::BAD_STATE; + LOGGER_WARN(logger_, "BAD_STATE"); + } + return ret; +} + +BluetoothDeviceScanner::BluetoothDeviceScannerDelegate:: + BluetoothDeviceScannerDelegate(BluetoothDeviceScanner* scanner) + : scanner_(scanner) {} + +void BluetoothDeviceScanner::BluetoothDeviceScannerDelegate::threadMain() { + LOGGER_AUTO_TRACE(logger_); + DCHECK(scanner_); + scanner_->Thread(); +} + +} // namespace transport_adapter +} // namespace transport_manager |