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(); } }