From 90cf35a38a88d5aef110c21884f1c21bd7076a54 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 13 Jan 2016 00:20:37 +0000 Subject: [PATCH 1/6] WIP: compiling but untested first cut at whitelisting code --- source/ble-services/MicroBitBLEManager.cpp | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 5534ae5..55d4a95 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -139,7 +139,17 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); ble->securityManager().init(MICROBIT_BLE_ENABLE_BONDING, MICROBIT_BLE_REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY); - // Bring up any configured auxiliary services. + // Configure a whitelist to filter all traffic from unbonded devices. Most BLE stacks only permit one connection at a time, + // so this prevents denial of service attacks. + BLEProtocol::Address_t bondedAddresses[4]; + Gap::Whitelist_t whitelist; + whitelist.addresses = bondedAddresses; + whitelist.capacity = 4; + + ble->securityManager().getAddressesFromBondTable(whitelist); + ble->gap().setWhitelist(whitelist); + + // Bring up any configured auxiliary services. #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) new MicroBitDFUService(*ble); #endif @@ -189,7 +199,9 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble->setAdvertisingInterval(200); - ble->startAdvertising(); + + if (whitelist.size > 0) + ble->startAdvertising(); } /** @@ -230,6 +242,14 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) int brightness = 255; int fadeDirection = 0; + // Clear the whitelist, so we're discoverable by all BLE devices. + BLEProtocol::Address_t addresses[4]; + Gap::Whitelist_t whitelist; + whitelist.addresses = addresses; + whitelist.capacity = 4; + whitelist.size = 0; + ble->gap().setWhitelist(whitelist); + // Update the advertised name of this micro:bit to include the device name ble->clearAdvertisingPayload(); From f17151a0bf66b45cabc63a7f8e73c86466beca3d Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 16 Jan 2016 16:54:58 +0000 Subject: [PATCH 2/6] microbit: Added optional support for whitelisting and limited advertising Added MicroBitConfig.h option to enable/disable whitelisting Added MicroBitConfig.h option to enable/disable limited period advertising Added support for limited period advertising Minor code cleanups --- inc/MicroBitBLEManager.h | 36 ++++++++------- inc/MicroBitConfig.h | 19 ++++++-- source/ble-services/MicroBitBLEManager.cpp | 54 ++++++++++++++++------ 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/inc/MicroBitBLEManager.h b/inc/MicroBitBLEManager.h index 0c53e22..c4169e7 100644 --- a/inc/MicroBitBLEManager.h +++ b/inc/MicroBitBLEManager.h @@ -33,10 +33,12 @@ #include "MicroBitTemperatureService.h" #include "ExternalEvents.h" -#define MICROBIT_BLE_PAIR_REQUEST 0x01 -#define MICROBIT_BLE_PAIR_COMPLETE 0x02 -#define MICROBIT_BLE_PAIR_PASSCODE 0x04 -#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08 +#define MICROBIT_BLE_PAIR_REQUEST 0x01 +#define MICROBIT_BLE_PAIR_COMPLETE 0x02 +#define MICROBIT_BLE_PAIR_PASSCODE 0x04 +#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08 + +#define MICROBIT_BLE_PAIRING_TIMEOUT 90 /** * Class definition for the MicroBitBLEManager. @@ -81,36 +83,36 @@ class MicroBitBLEManager void pairingMode(MicroBitDisplay &display); /** - * Method that is called whenever a BLE device disconnects from us. - * The nordic stack stops dvertising whenever a device connects, so we use - * this callback to restart advertising. + * Makes the micro:bit discoverable via BLE, such that bonded devices can connect + * When called, the micro:bit will begin advertising for a predefined period, + * (MICROBIT_BLE_ADVERTISING_TIMEOUT seconds) thereby allowing bonded devices to connect. */ - void onDisconnectionCallback(); - - /** - * Displays the device's ID code as a histogram on the LED matrix display. - */ - void showNameHistogram(MicroBitDisplay &display); + void advertise(); /** * A request to pair has been received from a BLE device. * If we're in pairing mode, display the passkey to the user. */ - void pairingRequested(ManagedString passKey); + void pairingRequested(ManagedString passKey); /** * A pairing request has been sucesfully completed. * If we're in pairing mode, display feedback to the user. */ - void pairingComplete(bool success); + void pairingComplete(bool success); + + private: + + /** + * Displays the device's ID code as a histogram on the LED matrix display. + */ + void showNameHistogram(MicroBitDisplay &display); - private: int pairingStatus; ManagedString passKey; ManagedString deviceName; }; - #endif diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 68567dc..6755986 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -129,13 +129,13 @@ // Enable/Disable BLE during normal operation. // Set '1' to enable. #ifndef MICROBIT_BLE_ENABLED -#define MICROBIT_BLE_ENABLED 1 +#define MICROBIT_BLE_ENABLED 1 #endif // Enable/Disable BLE pairing mode mode at power up. // Set '1' to enable. #ifndef MICROBIT_BLE_PAIRING_MODE -#define MICROBIT_BLE_PAIRING_MODE 1 +#define MICROBIT_BLE_PAIRING_MODE 1 #endif // Enable/Disable the use of private resolvable addresses. @@ -144,11 +144,24 @@ #define MICROBIT_BLE_PRIVATE_ADDRESSES 0 #endif +// Enable/Disbale the use of BLE whitelisting. +// If enabled, the micro:bit will only respond to conneciton requests from +// known, bonded devices. +#ifndef MICROBIT_BLE_WHITELIST +#define MICROBIT_BLE_WHITELIST 1 +#endif + +// Define the period of time for which the BLE stack will advertise (seconds) +// Afer this period, advertising will cease. Set to '0' for no timeout (always advertise). +#ifndef MICROBIT_BLE_ADVERTISING_TIMEOUT +#define MICROBIT_BLE_ADVERTISING_TIMEOUT 0 +#endif + // Enable/Disable BLE Service: MicroBitDFU // This allows over the air programming during normal operation. // Set '1' to enable. #ifndef MICROBIT_BLE_DFU_SERVICE -#define MICROBIT_BLE_DFU_SERVICE 1 +#define MICROBIT_BLE_DFU_SERVICE 1 #endif // Enable/Disable BLE Service: MicroBitEventService diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 55d4a95..9596cd8 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -24,8 +24,6 @@ #define MICROBIT_BLE_ENABLE_BONDING true #define MICROBIT_BLE_REQUIRE_MITM true - -#define MICROBIT_PAIRING_MODE_TIMEOUT 90 #define MICROBIT_PAIRING_FADE_SPEED 4 @@ -35,6 +33,7 @@ const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0"; const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION; const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL; + /* * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods. * So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit @@ -50,7 +49,7 @@ static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *r (void) reason; /* -Wunused-param */ if (manager) - manager->onDisconnectionCallback(); + manager->advertise(); } @@ -88,14 +87,14 @@ MicroBitBLEManager::MicroBitBLEManager() } /** - * Method that is called whenever a BLE device disconnects from us. - * The nordic stack stops dvertising whenever a device connects, so we use - * this callback to restart advertising. + * Makes the micro:bit discoverable via BLE, such that bonded devices can connect + * When called, the micro:bit will begin advertising for a predefined period, thereby allowing + * bonded devices to connect. */ -void MicroBitBLEManager::onDisconnectionCallback() +void MicroBitBLEManager::advertise() { - if(ble) - ble->startAdvertising(); + if(ble) + ble->gap().startAdvertising(); } /** @@ -131,7 +130,7 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb #if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES) // Configure for private addresses, so kids' behaviour can't be easily tracked. - ble->setAddress(Gap::ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE, NULL); + ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0}); #endif // Setup our security requirements. @@ -139,8 +138,9 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); ble->securityManager().init(MICROBIT_BLE_ENABLE_BONDING, MICROBIT_BLE_REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY); - // Configure a whitelist to filter all traffic from unbonded devices. Most BLE stacks only permit one connection at a time, - // so this prevents denial of service attacks. +#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) + // Configure a whitelist to filter all connection requetss from unbonded devices. + // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks. BLEProtocol::Address_t bondedAddresses[4]; Gap::Whitelist_t whitelist; whitelist.addresses = bondedAddresses; @@ -149,6 +149,10 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->securityManager().getAddressesFromBondTable(whitelist); ble->gap().setWhitelist(whitelist); + ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST); + ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS); +#endif + // Bring up any configured auxiliary services. #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) new MicroBitDFUService(*ble); @@ -195,13 +199,28 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->setPreferredConnectionParams(&fast); // Setup advertising. +#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) + ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); +#else ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); +#endif + ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble->setAdvertisingInterval(200); +#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0) + ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT); +#endif + + // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices... + // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable. + // If whiltelisting is disabled, then we always advertise. +#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) if (whitelist.size > 0) +#endif ble->startAdvertising(); + } /** @@ -242,13 +261,16 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) int brightness = 255; int fadeDirection = 0; - // Clear the whitelist, so we're discoverable by all BLE devices. + // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices. +#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) BLEProtocol::Address_t addresses[4]; Gap::Whitelist_t whitelist; whitelist.addresses = addresses; whitelist.capacity = 4; whitelist.size = 0; ble->gap().setWhitelist(whitelist); + ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); +#endif // Update the advertised name of this micro:bit to include the device name ble->clearAdvertisingPayload(); @@ -257,7 +279,9 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble->setAdvertisingInterval(200); - ble->startAdvertising(); + + ble->gap().setAdvertisingTimeout(0); + ble->gap().startAdvertising(); // Stop any running animations on the display display.stopAnimation(); @@ -330,7 +354,7 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) uBit.sleep(30); timeInPairingMode++; - if (timeInPairingMode >= MICROBIT_PAIRING_MODE_TIMEOUT * 30) + if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30) microbit_reset(); } } From 07852b0cd66ba0b54abdbabb9b1eda902884c115 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 16 Jan 2016 17:07:20 +0000 Subject: [PATCH 3/6] microbit: Added support for BLE transmission power control Added new method to MicroBitBLEManager to allow the radio transmission power to be controlled. Provides a linear power level scale in the range 0..7. --- inc/MicroBitBLEManager.h | 9 +++++++++ source/ble-services/MicroBitBLEManager.cpp | 23 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/inc/MicroBitBLEManager.h b/inc/MicroBitBLEManager.h index c4169e7..baab621 100644 --- a/inc/MicroBitBLEManager.h +++ b/inc/MicroBitBLEManager.h @@ -74,6 +74,15 @@ class MicroBitBLEManager */ void init(ManagedString deviceName, ManagedString serialNumber); + /** + * Change the output power level of the transmitter to the given value. + * + * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest. + * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range. + * + */ + int setTransmitPower(int power); + /** * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming * of the micro:bit in cases where BLE is disabled during normal operation. diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 9596cd8..8356994 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -25,6 +25,7 @@ #define MICROBIT_BLE_REQUIRE_MITM true #define MICROBIT_PAIRING_FADE_SPEED 4 +#define MICROBIT_BLE_POWER_LEVELS 8 const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A"; @@ -32,7 +33,7 @@ const char* MICROBIT_BLE_MODEL = "BBC micro:bit"; const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0"; const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION; const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL; - +const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4}; /* * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods. @@ -220,9 +221,27 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb if (whitelist.size > 0) #endif ble->startAdvertising(); - } +/** + * Change the output power level of the transmitter to the given value. + * + * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest. + * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range. + * + */ +int MicroBitBLEManager::setTransmitPower(int power) +{ + if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS) + return MICROBIT_INVALID_PARAMETER; + + if (ble->gap().setTxPower(MICROBIT_BLE_POWER_LEVEL[power]) != NRF_SUCCESS) + return MICROBIT_NOT_SUPPORTED; + + return MICROBIT_OK; +} + + /** * A request to pair has been received from a BLE device. * If we're in pairing mode, display the passkey to the user. From e754ecf3aacfa3e6270dea1c09b5cb9131793041 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 16 Jan 2016 17:29:46 +0000 Subject: [PATCH 4/6] microbit: Setting of default BLE transmission power level Added configuration option in MicroBitConfig to allow the default power level to be defined. Based on experiments undertaken by the BBC and subsequent policies defined by the BBC's child protection panel, to is set by default it's lowest level (0), in order to protect children's privacy. It should be noted that this is 30dB below the device's default setting, so will inevitably leave the device more susceptible to interference, and therefore significantly reduce the reliability of the device. This will likely present itself through increased packet loss, reduced thorughout, lost data and connection drop outs. The normal BLE power level on commercial devices translates to power level of 6. The author has witnessed an inability to connect to a micro:bit from a distance of 40cm under domestic conditions using a Samsung S5 mini running Android 4.4.2. --- inc/MicroBitConfig.h | 8 ++++++++ source/ble-services/MicroBitBLEManager.cpp | 3 +++ 2 files changed, 11 insertions(+) diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 6755986..5e27682 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -157,6 +157,14 @@ #define MICROBIT_BLE_ADVERTISING_TIMEOUT 0 #endif +// Defines default power level of the BLE radio transmitter. +// Valid values are in the range 0..7 inclusive, with 0 being the lowest power and 7 the highest power. +// Based on trials undertaken by the BBC, the radio is normally set to its lowest power level +// to best protect children's privacy. +#ifndef MICROBIT_BLE_DEFAULT_TX_POWER +#define MICROBIT_BLE_DEFAULT_TX_POWER 0 +#endif + // Enable/Disable BLE Service: MicroBitDFU // This allows over the air programming during normal operation. // Set '1' to enable. diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 8356994..6ec9130 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -154,6 +154,9 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS); #endif + // Configure the radio at our default power level + setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER); + // Bring up any configured auxiliary services. #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) new MicroBitDFUService(*ble); From 1c9ae30d1c70509dd54f3895a05b6de2ed158e17 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 16 Jan 2016 21:05:46 +0000 Subject: [PATCH 5/6] microbit: Added basic bond table management The Nordic BLE stack contains a fixed size bond table - a list of devices that have paired with our device (in the case the micro:bit). The default mbed wrapper around the nordic stack does not account for the case where the bond table is full. This patch implements basic bounds checking within the MicroBitBLEManager so that this table cannot overrun. If a new device attempts to pair when the bond table is full, the table is emptied prior to completing bonding. --- inc/MicroBitBLEManager.h | 6 +++ source/ble-services/MicroBitBLEManager.cpp | 52 ++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/inc/MicroBitBLEManager.h b/inc/MicroBitBLEManager.h index baab621..86a38df 100644 --- a/inc/MicroBitBLEManager.h +++ b/inc/MicroBitBLEManager.h @@ -98,6 +98,12 @@ class MicroBitBLEManager */ void advertise(); + /** + * Determines the number of devices currently bonded with this micro:bit + * @return The number of active bonds. + */ + int getBondCount(); + /** * A request to pair has been received from a BLE device. * If we're in pairing mode, display the passkey to the user. diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 6ec9130..1956ca5 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -26,6 +26,7 @@ #define MICROBIT_PAIRING_FADE_SPEED 4 #define MICROBIT_BLE_POWER_LEVELS 8 +#define MICROBIT_BLE_MAXIMUM_BONDS 4 const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A"; @@ -142,10 +143,10 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) // Configure a whitelist to filter all connection requetss from unbonded devices. // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks. - BLEProtocol::Address_t bondedAddresses[4]; + BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS]; Gap::Whitelist_t whitelist; whitelist.addresses = bondedAddresses; - whitelist.capacity = 4; + whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; ble->securityManager().getAddressesFromBondTable(whitelist); ble->gap().setWhitelist(whitelist); @@ -244,13 +245,39 @@ int MicroBitBLEManager::setTransmitPower(int power) return MICROBIT_OK; } +/** + * Determines the number of devices currently bonded with this micro:bit + * @return The number of active bonds. + */ +int MicroBitBLEManager::getBondCount() +{ + BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS]; + Gap::Whitelist_t whitelist; + whitelist.addresses = bondedAddresses; + whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; + ble->securityManager().getAddressesFromBondTable(whitelist); + + return whitelist.size; +} /** * A request to pair has been received from a BLE device. * If we're in pairing mode, display the passkey to the user. + * Also, purge the binding table if it has reached capacity. */ void MicroBitBLEManager::pairingRequested(ManagedString passKey) { + // Firstly, determine if there is free space in the bonding table. + // If not, clear it out to make room. + + // TODO: It would be much better to implement some sort of LRU/NFU policy here, + // but this isn't currently supported in mbed, so we'd need to layer break... + + // If we're full, empty the bond table. + if (getBondCount() >= MICROBIT_BLE_MAXIMUM_BONDS) + ble->securityManager().purgeAllBondingState(); + + // Update our mode to display the passkey. this->passKey = passKey; this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST; } @@ -283,12 +310,14 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) int brightness = 255; int fadeDirection = 0; + ble->gap().stopAdvertising(); + // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices. #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) - BLEProtocol::Address_t addresses[4]; + BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS]; Gap::Whitelist_t whitelist; whitelist.addresses = addresses; - whitelist.capacity = 4; + whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; whitelist.size = 0; ble->gap().setWhitelist(whitelist); ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); @@ -365,6 +394,21 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) { MicroBitImage tick("0,0,0,0,0\n0,0,0,0,255\n0,0,0,255,0\n255,0,255,0,0\n0,255,0,0,0\n"); display.print(tick,0,0,0); + uBit.sleep(5000); + + /* + * Disabled, as the API to return the number of active bonds is not reliable at present... + * + display.clear(); + ManagedString c(getBondCount()); + ManagedString c2("/"); + ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS); + ManagedString c4("USED"); + + display.scroll(c+c2+c3+c4); + * + * + */ } else { From 13eb3c07e7d0ec024117803028eecd8daed8e157 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 17 Jan 2016 00:23:00 +0000 Subject: [PATCH 6/6] microbit: Add explicit service changed notification BLE central devices (such as mobile phones/tablets) are permitted to cache the list of services and characteristics provided by a BLE peripheral such as the micro:bit. This was causing compatibility problems with IOS during FOTA flashing, as the the list of BLE services may change after flashing. --- source/ble-services/MicroBitBLEManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/ble-services/MicroBitBLEManager.cpp b/source/ble-services/MicroBitBLEManager.cpp index 1956ca5..725619a 100644 --- a/source/ble-services/MicroBitBLEManager.cpp +++ b/source/ble-services/MicroBitBLEManager.cpp @@ -55,6 +55,15 @@ static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *r } +/** + * Callback when a BLE GATT connect occurs. + */ +static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t *reason) +{ + // Ensure that there's no stale, cached information in the client... invalidate all characteristics. + sd_ble_gatts_service_changed(reason->handle, 0x000c, 0xffff); +} + static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) { (void) handle; /* -Wunused-param */ @@ -122,6 +131,7 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb // automatically restart advertising after a device disconnects. ble->onDisconnection(bleDisconnectionCallback); + ble->onConnection(bleConnectionCallback); // configure the stack to hold on to CPU during critical timing events. // mbed-classic performs __disabe_irq calls in its timers, which can cause MIC failures