2019-04-12 15:43:53 +02:00
/*
* BLEScan . cpp
*
* Created on : Jul 1 , 2017
* Author : kolban
2021-04-22 12:01:20 +02:00
*
* Update : April , 2021
* add BLE5 support
2019-04-12 15:43:53 +02:00
*/
# include "sdkconfig.h"
2021-04-05 14:23:58 +03:00
# if defined(CONFIG_BLUEDROID_ENABLED)
2019-04-12 15:43:53 +02:00
# include <esp_err.h>
# include <map>
# include "BLEAdvertisedDevice.h"
# include "BLEScan.h"
# include "BLEUtils.h"
# include "GeneralUtils.h"
# include "esp32-hal-log.h"
/**
* Constructor
*/
BLEScan : : BLEScan ( ) {
Fix for missed scan response data (BLEScan). (#4358)
This is a fix for missing scan responses after a first successfull scan.
While running the BLE_scan.ino sketch with wantDuplicates=false, i got
only one result with correct advertising and scan response length (31,26):
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false);
pBLEScan->start(scanTime, false);
...
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 26, addr type: 1
All following calls to start() just returned the advertising data without
scan response data:
pBLEScan->start(scanTime, false);
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 0, addr type: 1
With "wantDuplicates=true" i got:
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
pBLEScan->start(scanTime, false);
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 26, addr type: 1
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 26, addr type: 1
[W][BLEScan.cpp:73] handleGAPEvent(): ESP_GAP_SEARCH_INQ_CMPL_EVT
Devices found: 1
Scan done!
pBLEScan->start(scanTime, false);
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 0, addr type: 1
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 0 + 26, addr type: 1
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 31 + 0, addr type: 1
[W][BLEScan.cpp:109] handleGAPEvent(): bytes length: 0 + 26, addr type: 1
Explicitly initializing m_scan_params.scan_duplicate of BLEScan solves
this issue (In my case the un-initialized value was
m_scan_params.scan_duplicate == 1073599044).
Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>
2020-10-01 14:43:30 +02:00
memset ( & m_scan_params , 0 , sizeof ( m_scan_params ) ) ; // Initialize all params
2019-04-12 15:43:53 +02:00
m_scan_params . scan_type = BLE_SCAN_TYPE_PASSIVE ; // Default is a passive scan.
m_scan_params . own_addr_type = BLE_ADDR_TYPE_PUBLIC ;
m_scan_params . scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL ;
2020-10-01 20:45:45 +10:00
m_scan_params . scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE ;
2019-04-12 15:43:53 +02:00
m_pAdvertisedDeviceCallbacks = nullptr ;
m_stopped = true ;
m_wantDuplicates = false ;
2020-11-03 01:41:50 +07:00
m_shouldParse = true ;
2019-04-12 15:43:53 +02:00
setInterval ( 100 ) ;
setWindow ( 100 ) ;
} // BLEScan
/**
* @ brief Handle GAP events related to scans .
* @ param [ in ] event The event type for this event .
* @ param [ in ] param Parameter data for this event .
*/
void BLEScan : : handleGAPEvent (
esp_gap_ble_cb_event_t event ,
esp_ble_gap_cb_param_t * param ) {
switch ( event ) {
// ---------------------------
// scan_rst:
// esp_gap_search_evt_t search_evt
// esp_bd_addr_t bda
// esp_bt_dev_type_t dev_type
// esp_ble_addr_type_t ble_addr_type
// esp_ble_evt_type_t ble_evt_type
// int rssi
// uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]
// int flag
// int num_resps
// uint8_t adv_data_len
// uint8_t scan_rsp_len
case ESP_GAP_BLE_SCAN_RESULT_EVT : {
switch ( param - > scan_rst . search_evt ) {
//
// ESP_GAP_SEARCH_INQ_CMPL_EVT
//
// Event that indicates that the duration allowed for the search has completed or that we have been
// asked to stop.
case ESP_GAP_SEARCH_INQ_CMPL_EVT : {
2019-04-15 17:26:35 +02:00
log_w ( " ESP_GAP_SEARCH_INQ_CMPL_EVT " ) ;
2019-04-12 15:43:53 +02:00
m_stopped = true ;
m_semaphoreScanEnd . give ( ) ;
if ( m_scanCompleteCB ! = nullptr ) {
m_scanCompleteCB ( m_scanResults ) ;
}
break ;
} // ESP_GAP_SEARCH_INQ_CMPL_EVT
//
// ESP_GAP_SEARCH_INQ_RES_EVT
//
// Result that has arrived back from a Scan inquiry.
case ESP_GAP_SEARCH_INQ_RES_EVT : {
if ( m_stopped ) { // If we are not scanning, nothing to do with the extra results.
break ;
}
// Examine our list of previously scanned addresses and, if we found this one already,
// ignore it.
BLEAddress advertisedAddress ( param - > scan_rst . bda ) ;
bool found = false ;
2020-11-03 01:41:50 +07:00
bool shouldDelete = true ;
if ( ! m_wantDuplicates ) {
if ( m_scanResults . m_vectorAdvertisedDevices . count ( advertisedAddress . toString ( ) ) ! = 0 ) {
found = true ;
}
if ( found ) { // If we found a previous entry AND we don't want duplicates, then we are done.
log_d ( " Ignoring %s, already seen it. " , advertisedAddress . toString ( ) . c_str ( ) ) ;
vTaskDelay ( 1 ) ; // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here
break ;
}
2019-04-12 15:43:53 +02:00
}
// We now construct a model of the advertised device that we have just found for the first
// time.
2019-04-15 17:26:35 +02:00
// ESP_LOG_BUFFER_HEXDUMP((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG);
// log_w("bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type);
2019-04-12 15:43:53 +02:00
BLEAdvertisedDevice * advertisedDevice = new BLEAdvertisedDevice ( ) ;
advertisedDevice - > setAddress ( advertisedAddress ) ;
advertisedDevice - > setRSSI ( param - > scan_rst . rssi ) ;
advertisedDevice - > setAdFlag ( param - > scan_rst . flag ) ;
2020-11-03 01:41:50 +07:00
if ( m_shouldParse ) {
advertisedDevice - > parseAdvertisement ( ( uint8_t * ) param - > scan_rst . ble_adv , param - > scan_rst . adv_data_len + param - > scan_rst . scan_rsp_len ) ;
} else {
advertisedDevice - > setPayload ( ( uint8_t * ) param - > scan_rst . ble_adv , param - > scan_rst . adv_data_len + param - > scan_rst . scan_rsp_len ) ;
}
2019-04-12 15:43:53 +02:00
advertisedDevice - > setScan ( this ) ;
advertisedDevice - > setAddressType ( param - > scan_rst . ble_addr_type ) ;
2020-11-03 01:41:50 +07:00
if ( m_pAdvertisedDeviceCallbacks ) { // if has callback, no need to record to vector
2019-04-12 15:43:53 +02:00
m_pAdvertisedDeviceCallbacks - > onResult ( * advertisedDevice ) ;
2020-11-03 01:41:50 +07:00
} else if ( ! m_wantDuplicates & & ! found ) { // if no callback and not want duplicate, and not already in vector, record it
m_scanResults . m_vectorAdvertisedDevices . insert ( std : : pair < std : : string , BLEAdvertisedDevice * > ( advertisedAddress . toString ( ) , advertisedDevice ) ) ;
shouldDelete = false ;
2019-04-12 15:43:53 +02:00
}
2020-11-03 01:41:50 +07:00
if ( shouldDelete ) {
2019-04-12 15:43:53 +02:00
delete advertisedDevice ;
2020-11-03 01:41:50 +07:00
}
2019-04-12 15:43:53 +02:00
break ;
} // ESP_GAP_SEARCH_INQ_RES_EVT
default : {
break ;
}
} // switch - search_evt
break ;
} // ESP_GAP_BLE_SCAN_RESULT_EVT
2021-04-22 12:01:20 +02:00
# ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
case ESP_GAP_BLE_EXT_ADV_REPORT_EVT : {
if ( param - > ext_adv_report . params . event_type & ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY ) {
log_v ( " legacy adv, adv type 0x%x data len %d " , param - > ext_adv_report . params . event_type , param - > ext_adv_report . params . adv_data_len ) ;
}
else {
log_v ( " extend adv, adv type 0x%x data len %d, data status: %d " , param - > ext_adv_report . params . event_type , param - > ext_adv_report . params . adv_data_len , param - > ext_adv_report . params . data_status ) ;
}
if ( m_pExtendedScanCb ! = nullptr )
{
m_pExtendedScanCb - > onResult ( param - > ext_adv_report . params ) ;
}
break ;
}
case ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT : {
if ( param - > set_ext_scan_params . status ! = ESP_BT_STATUS_SUCCESS ) {
log_e ( " extend scan parameters set failed, error status = %x " , param - > set_ext_scan_params . status ) ;
break ;
}
log_v ( " extend scan params set successfully " ) ;
break ;
}
case ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT :
if ( param - > ext_scan_start . status ! = ESP_BT_STATUS_SUCCESS ) {
log_e ( " scan start failed, error status = %x " , param - > scan_start_cmpl . status ) ;
break ;
}
log_v ( " Scan start success " ) ;
break ;
case ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onStop ( param - > ext_scan_stop . status ) ;
}
if ( param - > ext_scan_stop . status ! = ESP_BT_STATUS_SUCCESS ) {
log_e ( " extend Scan stop failed, error status = %x " , param - > ext_scan_stop . status ) ;
break ;
}
log_v ( " Stop extend scan successfully " ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onCreateSync ( param - > period_adv_create_sync . status ) ;
}
log_v ( " ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, status %d " , param - > period_adv_create_sync . status ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onCancelSync ( param - > period_adv_sync_cancel . status ) ;
}
log_v ( " ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT, status %d " , param - > period_adv_sync_cancel . status ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onTerminateSync ( param - > period_adv_sync_term . status ) ;
}
log_v ( " ESP_GAP_BLE_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT, status %d " , param - > period_adv_sync_term . status ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onLostSync ( param - > periodic_adv_sync_lost . sync_handle ) ;
}
log_v ( " ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT, sync handle %d " , param - > periodic_adv_sync_lost . sync_handle ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onSync ( * ( esp_ble_periodic_adv_sync_estab_param_t * ) & param - > periodic_adv_sync_estab ) ;
}
log_v ( " ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT, status %d " , param - > periodic_adv_sync_estab . status ) ;
break ;
case ESP_GAP_BLE_PERIODIC_ADV_REPORT_EVT :
if ( m_pPeriodicScanCb ! = nullptr )
{
m_pPeriodicScanCb - > onReport ( param - > period_adv_report . params ) ;
}
break ;
# endif // CONFIG_BT_BLE_50_FEATURES_SUPPORTED
2019-04-12 15:43:53 +02:00
default : {
break ;
} // default
} // End switch
} // gapEventHandler
/**
* @ brief Should we perform an active or passive scan ?
* The default is a passive scan . An active scan means that we will wish a scan response .
* @ param [ in ] active If true , we perform an active scan otherwise a passive scan .
* @ return N / A .
*/
void BLEScan : : setActiveScan ( bool active ) {
if ( active ) {
m_scan_params . scan_type = BLE_SCAN_TYPE_ACTIVE ;
} else {
m_scan_params . scan_type = BLE_SCAN_TYPE_PASSIVE ;
}
} // setActiveScan
/**
* @ brief Set the call backs to be invoked .
* @ param [ in ] pAdvertisedDeviceCallbacks Call backs to be invoked .
* @ param [ in ] wantDuplicates True if we wish to be called back with duplicates . Default is false .
2020-11-03 01:41:50 +07:00
* @ param [ in ] shouldParse True if we wish to parse advertised package or raw payload . Default is true .
2019-04-12 15:43:53 +02:00
*/
2020-11-03 01:41:50 +07:00
void BLEScan : : setAdvertisedDeviceCallbacks ( BLEAdvertisedDeviceCallbacks * pAdvertisedDeviceCallbacks , bool wantDuplicates , bool shouldParse ) {
2019-04-12 15:43:53 +02:00
m_wantDuplicates = wantDuplicates ;
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks ;
2020-11-03 01:41:50 +07:00
m_shouldParse = shouldParse ;
2019-04-12 15:43:53 +02:00
} // setAdvertisedDeviceCallbacks
2021-04-22 12:01:20 +02:00
# ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
void BLEScan : : setExtendedScanCallback ( BLEExtAdvertisingCallbacks * cb )
{
m_pExtendedScanCb = cb ;
}
/**
* @ brief This function is used to set the extended scan parameters to be used on the advertising channels .
*
*
* @ return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan : : setExtScanParams ( )
{
esp_ble_ext_scan_params_t ext_scan_params = {
. own_addr_type = BLE_ADDR_TYPE_PUBLIC ,
. filter_policy = BLE_SCAN_FILTER_ALLOW_ALL ,
. scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE ,
. cfg_mask = ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK | ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK ,
. uncoded_cfg = { BLE_SCAN_TYPE_ACTIVE , 40 , 40 } ,
. coded_cfg = { BLE_SCAN_TYPE_ACTIVE , 40 , 40 } ,
} ;
esp_err_t rc = esp_ble_gap_set_ext_scan_params ( & ext_scan_params ) ;
if ( rc ) {
log_e ( " set extend scan params error, error code = %x " , rc ) ;
}
return rc ;
}
/**
* @ brief This function is used to set the extended scan parameters to be used on the advertising channels .
*
* @ param [ in ] params : scan parameters
*
* @ return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan : : setExtScanParams ( esp_ble_ext_scan_params_t * ext_scan_params )
{
esp_err_t rc = esp_ble_gap_set_ext_scan_params ( ext_scan_params ) ;
if ( rc ) {
log_e ( " set extend scan params error, error code = %x " , rc ) ;
}
return rc ;
}
/**
* @ brief This function is used to enable scanning .
*
* @ param [ in ] duration : Scan duration
* @ param [ in ] period : Time interval from when the Controller started its last Scan Duration until it begins the subsequent Scan Duration .
*
* @ return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan : : startExtScan ( uint32_t duration , uint16_t period )
{
esp_err_t rc = esp_ble_gap_start_ext_scan ( duration , period ) ;
if ( rc ) log_e ( " extended scan start failed: %d " , rc ) ;
return rc ;
}
esp_err_t BLEScan : : stopExtScan ( )
{
esp_err_t rc ;
rc = esp_ble_gap_stop_ext_scan ( ) ;
return rc ;
}
void BLEScan : : setPeriodicScanCallback ( BLEPeriodicScanCallbacks * cb )
{
m_pPeriodicScanCb = cb ;
}
# endif // CONFIG_BT_BLE_50_FEATURES_SUPPORTED
2019-04-12 15:43:53 +02:00
/**
* @ brief Set the interval to scan .
* @ param [ in ] The interval in msecs .
*/
void BLEScan : : setInterval ( uint16_t intervalMSecs ) {
m_scan_params . scan_interval = intervalMSecs / 0.625 ;
} // setInterval
/**
* @ brief Set the window to actively scan .
* @ param [ in ] windowMSecs How long to actively scan .
*/
void BLEScan : : setWindow ( uint16_t windowMSecs ) {
m_scan_params . scan_window = windowMSecs / 0.625 ;
} // setWindow
/**
* @ brief Start scanning .
* @ param [ in ] duration The duration in seconds for which to scan .
* @ param [ in ] scanCompleteCB A function to be called when scanning has completed .
* @ param [ in ] are we continue scan ( true ) or we want to clear stored devices ( false )
* @ return True if scan started or false if there was an error .
*/
bool BLEScan : : start ( uint32_t duration , void ( * scanCompleteCB ) ( BLEScanResults ) , bool is_continue ) {
2019-04-15 17:26:35 +02:00
log_v ( " >> start(duration=%d) " , duration ) ;
2019-04-12 15:43:53 +02:00
m_semaphoreScanEnd . take ( std : : string ( " start " ) ) ;
m_scanCompleteCB = scanCompleteCB ; // Save the callback to be invoked when the scan completes.
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
// then we should not clear map or we will connect the same device few times
if ( ! is_continue ) {
for ( auto _dev : m_scanResults . m_vectorAdvertisedDevices ) {
delete _dev . second ;
}
m_scanResults . m_vectorAdvertisedDevices . clear ( ) ;
}
esp_err_t errRc = : : esp_ble_gap_set_scan_params ( & m_scan_params ) ;
if ( errRc ! = ESP_OK ) {
2019-04-15 17:26:35 +02:00
log_e ( " esp_ble_gap_set_scan_params: err: %d, text: %s " , errRc , GeneralUtils : : errorToString ( errRc ) ) ;
2019-04-12 15:43:53 +02:00
m_semaphoreScanEnd . give ( ) ;
return false ;
}
errRc = : : esp_ble_gap_start_scanning ( duration ) ;
if ( errRc ! = ESP_OK ) {
2019-04-15 17:26:35 +02:00
log_e ( " esp_ble_gap_start_scanning: err: %d, text: %s " , errRc , GeneralUtils : : errorToString ( errRc ) ) ;
2019-04-12 15:43:53 +02:00
m_semaphoreScanEnd . give ( ) ;
return false ;
}
m_stopped = false ;
2019-04-15 17:26:35 +02:00
log_v ( " << start() " ) ;
2019-04-12 15:43:53 +02:00
return true ;
} // start
/**
* @ brief Start scanning and block until scanning has been completed .
* @ param [ in ] duration The duration in seconds for which to scan .
* @ return The BLEScanResults .
*/
BLEScanResults BLEScan : : start ( uint32_t duration , bool is_continue ) {
if ( start ( duration , nullptr , is_continue ) ) {
m_semaphoreScanEnd . wait ( " start " ) ; // Wait for the semaphore to release.
}
return m_scanResults ;
} // start
/**
* @ brief Stop an in progress scan .
* @ return N / A .
*/
void BLEScan : : stop ( ) {
2019-04-15 17:26:35 +02:00
log_v ( " >> stop() " ) ;
2019-04-12 15:43:53 +02:00
esp_err_t errRc = : : esp_ble_gap_stop_scanning ( ) ;
m_stopped = true ;
m_semaphoreScanEnd . give ( ) ;
if ( errRc ! = ESP_OK ) {
2019-04-15 17:26:35 +02:00
log_e ( " esp_ble_gap_stop_scanning: err: %d, text: %s " , errRc , GeneralUtils : : errorToString ( errRc ) ) ;
2019-04-12 15:43:53 +02:00
return ;
}
2019-04-15 17:26:35 +02:00
log_v ( " << stop() " ) ;
2019-04-12 15:43:53 +02:00
} // stop
// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
void BLEScan : : erase ( BLEAddress address ) {
2019-04-15 17:26:35 +02:00
log_i ( " erase device: %s " , address . toString ( ) . c_str ( ) ) ;
2019-04-12 15:43:53 +02:00
BLEAdvertisedDevice * advertisedDevice = m_scanResults . m_vectorAdvertisedDevices . find ( address . toString ( ) ) - > second ;
m_scanResults . m_vectorAdvertisedDevices . erase ( address . toString ( ) ) ;
delete advertisedDevice ;
}
/**
* @ brief Dump the scan results to the log .
*/
void BLEScanResults : : dump ( ) {
2019-04-15 17:26:35 +02:00
log_v ( " >> Dump scan results: " ) ;
2019-04-12 15:43:53 +02:00
for ( int i = 0 ; i < getCount ( ) ; i + + ) {
2019-04-15 17:26:35 +02:00
log_d ( " - %s " , getDevice ( i ) . toString ( ) . c_str ( ) ) ;
2019-04-12 15:43:53 +02:00
}
} // dump
/**
* @ brief Return the count of devices found in the last scan .
* @ return The number of devices found in the last scan .
*/
int BLEScanResults : : getCount ( ) {
return m_vectorAdvertisedDevices . size ( ) ;
} // getCount
/**
* @ brief Return the specified device at the given index .
* The index should be between 0 and getCount ( ) - 1.
* @ param [ in ] i The index of the device .
* @ return The device at the specified index .
*/
BLEAdvertisedDevice BLEScanResults : : getDevice ( uint32_t i ) {
uint32_t x = 0 ;
BLEAdvertisedDevice dev = * m_vectorAdvertisedDevices . begin ( ) - > second ;
for ( auto it = m_vectorAdvertisedDevices . begin ( ) ; it ! = m_vectorAdvertisedDevices . end ( ) ; it + + ) {
dev = * it - > second ;
if ( x = = i ) break ;
x + + ;
}
return dev ;
}
BLEScanResults BLEScan : : getResults ( ) {
return m_scanResults ;
}
void BLEScan : : clearResults ( ) {
for ( auto _dev : m_scanResults . m_vectorAdvertisedDevices ) {
delete _dev . second ;
}
m_scanResults . m_vectorAdvertisedDevices . clear ( ) ;
}
2021-04-05 14:23:58 +03:00
# endif /* CONFIG_BLUEDROID_ENABLED */