/* * Copyright (C) 2014 BlueKitchen GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS * RINGWALD 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. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "CppUTest/TestHarness.h" #include "CppUTest/CommandLineTestRunner.h" #include "hci.h" #include "ble/att_db.h" #include "ble/att_db_util.h" #include "btstack_util.h" #include "bluetooth.h" #include "btstack_crypto.h" #include "bluetooth_gatt.h" typedef enum { READ_CALLBACK_MODE_RETURN_DEFAULT = 0, READ_CALLBACK_MODE_RETURN_ONE_BYTE, READ_CALLBACK_MODE_RETURN_PENDING } read_callback_mode_t; typedef enum { WRITE_CALLBACK_MODE_RETURN_DEFAULT = 0, WRITE_CALLBACK_MODE_RETURN_ERROR_WRITE_RESPONSE_PENDING, WRITE_CALLBACK_MODE_RETURN_INVALID_ATTRIBUTE_VALUE_LENGTH } write_callback_mode_t; static uint8_t battery_level = 100; static uint8_t cgm_status = 0; static uint8_t att_request[200]; static uint8_t att_response[1000]; static read_callback_mode_t read_callback_mode = READ_CALLBACK_MODE_RETURN_DEFAULT; static write_callback_mode_t write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; // these can be tweaked to report errors or some data as needed by test case static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ switch (read_callback_mode){ case READ_CALLBACK_MODE_RETURN_ONE_BYTE: return att_read_callback_handle_byte(0x55, offset, buffer, buffer_size); case READ_CALLBACK_MODE_RETURN_PENDING: return ATT_READ_RESPONSE_PENDING; default: return 0; } } static int att_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ switch (write_callback_mode){ case WRITE_CALLBACK_MODE_RETURN_ERROR_WRITE_RESPONSE_PENDING: return ATT_ERROR_WRITE_RESPONSE_PENDING; case WRITE_CALLBACK_MODE_RETURN_INVALID_ATTRIBUTE_VALUE_LENGTH: return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH; default: return 0; } } static uint16_t att_read_multiple_request(uint16_t num_value_handles, uint16_t * value_handles){ att_request[0] = ATT_READ_MULTIPLE_REQUEST; int i; int offset = 1; for (i=0;i<num_value_handles;i++){ little_endian_store_16(att_request, offset, value_handles[i]); offset += 2; } return offset; } static uint16_t att_write_request(uint16_t request_type, uint16_t attribute_handle, uint16_t value_length, const uint8_t * value){ att_request[0] = request_type; little_endian_store_16(att_request, 1, attribute_handle); (void)memcpy(&att_request[3], value, value_length); return 3 + value_length; } static uint16_t att_prepare_write_request(uint16_t request_type, uint16_t attribute_handle, uint16_t value_offset){ att_request[0] = request_type; little_endian_store_16(att_request, 1, attribute_handle); little_endian_store_16(att_request, 3, value_offset); return 5; } // ignore for now extern "C" void btstack_crypto_aes128_cmac_generator(btstack_crypto_aes128_cmac_t * request, const uint8_t * key, uint16_t size, uint8_t (*get_byte_callback)(uint16_t pos), uint8_t * hash, void (* callback)(void * arg), void * callback_arg){ } TEST_GROUP(AttDb){ att_connection_t att_connection; uint16_t att_request_len; uint16_t att_response_len; uint8_t callback_buffer[10]; void setup(void){ memset(&callback_buffer, 0, sizeof(callback_buffer)); read_callback_mode = READ_CALLBACK_MODE_RETURN_ONE_BYTE; memset(&att_connection, 0, sizeof(att_connection)); att_connection.max_mtu = 150; att_connection.mtu = ATT_DEFAULT_MTU; write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; read_callback_mode = READ_CALLBACK_MODE_RETURN_DEFAULT; // init att db util and add a service and characteristic att_db_util_init(); // 0x180F att_db_util_add_service_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE); // 0x2A19 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, ATT_PROPERTY_WRITE | ATT_PROPERTY_READ | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); // 0x2A1B att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATE, ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); // 0x2A1A att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_POWER_STATE, ATT_PROPERTY_READ | ATT_PROPERTY_NOTIFY, ATT_SECURITY_AUTHENTICATED, ATT_SECURITY_AUTHENTICATED, &battery_level, 1); // 0x2A49 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BLOOD_PRESSURE_FEATURE, ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_READ | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); // 0x2A35 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BLOOD_PRESSURE_MEASUREMENT, ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_AUTHENTICATED, ATT_SECURITY_AUTHENTICATED, &battery_level, 1); // 0x2A38 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_BODY_SENSOR_LOCATION, ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); const uint8_t uuid128[] = {0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; att_db_util_add_characteristic_uuid128(uuid128, ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); // 0x2AA7 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_CGM_MEASUREMENT, ATT_PROPERTY_WRITE_WITHOUT_RESPONSE | ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_NOTIFY, ATT_SECURITY_AUTHENTICATED, ATT_SECURITY_AUTHENTICATED, &battery_level, 1); // 0x2AAB att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_CGM_SESSION_RUN_TIME, ATT_PROPERTY_WRITE_WITHOUT_RESPONSE | ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); // 0x2A5C att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE, ATT_PROPERTY_AUTHENTICATED_SIGNED_WRITE | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); const uint8_t uuid128_service[] = {0xAA, 0xBB, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; att_db_util_add_service_uuid128(uuid128_service); // 0x2AA9 att_db_util_add_characteristic_uuid16(ORG_BLUETOOTH_CHARACTERISTIC_CGM_STATUS, ATT_PROPERTY_WRITE_WITHOUT_RESPONSE | ATT_PROPERTY_DYNAMIC | ATT_PROPERTY_NOTIFY, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &cgm_status, 1); const uint8_t uuid128_chr_no_notify[] = {0xAA, 0xCC, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; att_db_util_add_characteristic_uuid128(uuid128_chr_no_notify, ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, &battery_level, 1); att_db_util_add_included_service_uuid16(0x50, 0x51, 0xAACC); // const uint8_t uuid128_incl_service[] = {0xAA, 0xEE, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; // att_db_util_add_included_service_uuid128(0x50, 0x51, uuid128_incl_service); // set callbacks att_set_db(att_db_util_get_address()); att_set_read_callback(&att_read_callback); att_set_write_callback(&att_write_callback); } }; TEST(AttDb, SetDB_NullAddress){ // test some function att_set_db(NULL); } TEST(AttDb, MtuExchange){ // test some function att_request_len = 3; const uint8_t att_request[3] = { ATT_EXCHANGE_MTU_REQUEST, 0, att_connection.max_mtu}; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(att_request_len, att_response_len); const uint8_t expected_response[] = { ATT_EXCHANGE_MTU_RESPONSE, att_connection.max_mtu, 0}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); } TEST(AttDb, handle_read_multiple_request){ uint16_t value_handles[2]; uint16_t num_value_handles; // less then two values num_value_handles = 0; memset(&value_handles, 0, sizeof(value_handles)); { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, 0, 0, ATT_ERROR_INVALID_PDU}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } num_value_handles = 1; value_handles[0] = 0x1; { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, 0, 0, ATT_ERROR_INVALID_PDU}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } // invalid handle num_value_handles = 2; value_handles[0] = 0x0; { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, 0, 0, ATT_ERROR_INVALID_HANDLE}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } num_value_handles = 2; value_handles[0] = 0xF1; value_handles[1] = 0xF2; { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, value_handles[0], 0, ATT_ERROR_INVALID_HANDLE}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } // handle read not permitted num_value_handles = 2; value_handles[0] = 0x05; value_handles[1] = 0x06; { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, value_handles[1], 0, ATT_ERROR_READ_NOT_PERMITTED}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } // security validation (single case) num_value_handles = 2; value_handles[0] = 0x05; value_handles[1] = 0x09; { att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, ATT_READ_MULTIPLE_REQUEST, value_handles[1], 0, ATT_ERROR_INSUFFICIENT_AUTHENTICATION}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); } // static read num_value_handles = 2; value_handles[0] = 0x03; value_handles[1] = 0x05; { read_callback_mode = READ_CALLBACK_MODE_RETURN_ONE_BYTE; att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_READ_MULTIPLE_RESPONSE, 0x64, 0x10, 0x06, 0x00, 0x1B, 0x2A}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); read_callback_mode = READ_CALLBACK_MODE_RETURN_DEFAULT; } #ifdef ENABLE_ATT_DELAYED_RESPONSE // dynamic read num_value_handles = 2; value_handles[0] = 0x03; value_handles[1] = 0x0c; { read_callback_mode = READ_CALLBACK_MODE_RETURN_PENDING; att_request_len = att_read_multiple_request(num_value_handles, value_handles); CHECK_EQUAL(1 + 2 * num_value_handles, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_READ_MULTIPLE_RESPONSE, 0x64}; CHECK_EQUAL(sizeof(expected_response), att_response_len); MEMCMP_EQUAL(expected_response, att_response, att_response_len); read_callback_mode = READ_CALLBACK_MODE_RETURN_DEFAULT; } #endif } TEST(AttDb, handle_write_request){ uint16_t attribute_handle = 0x03; // not sufficient request length { att_request[0] = ATT_WRITE_REQUEST; att_request_len = 1; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], 0x00, 0x00, ATT_ERROR_INVALID_PDU}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } { att_request[0] = ATT_WRITE_REQUEST; att_request[1] = 0x03; att_request_len = 2; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], 0x00, 0x00, ATT_ERROR_INVALID_PDU}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // invalid handle { att_request[0] = ATT_WRITE_REQUEST; att_request[1] = 0; att_request[2] = 0; att_request_len = 3; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INVALID_HANDLE}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // write not permited: invalid write callback { att_set_write_callback(NULL); const uint8_t value[] = {0x50}; att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); att_set_write_callback(&att_write_callback); } // write not permited: no ATT_PROPERTY_WRITE { const uint8_t value[] = {0x50}; attribute_handle = 0x000c; // 0x2A49 att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // write not permited: no ATT_PROPERTY_DYNAMIC { const uint8_t value[] = {0x50}; attribute_handle = 0x0003; att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // security validation { const uint8_t value[] = {0x50}; attribute_handle = 0x000f; // 0x2A35 att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INSUFFICIENT_AUTHENTICATION}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // att_persistent_ccc_cache: ATT_PROPERTY_UUID16 // att_persistent_ccc_cache: ATT_PROPERTY_UUID128 // some callback error other then ATT_INTERNAL_WRITE_RESPONSE_PENDING { write_callback_mode = WRITE_CALLBACK_MODE_RETURN_INVALID_ATTRIBUTE_VALUE_LENGTH; const uint8_t value[] = {0x50}; attribute_handle = 0x0011; // 0x2A38 att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; } #ifdef ENABLE_ATT_DELAYED_RESPONSE // delayed response { write_callback_mode = WRITE_CALLBACK_MODE_RETURN_ERROR_WRITE_RESPONSE_PENDING; const uint8_t value[] = {0x50}; attribute_handle = 0x0011; // 0x2A38 att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(ATT_INTERNAL_WRITE_RESPONSE_PENDING, att_response_len); write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; } #endif // correct write { const uint8_t value[] = {0x50}; attribute_handle = 0x0011; att_request_len = att_write_request(ATT_WRITE_REQUEST, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_WRITE_RESPONSE}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } } TEST(AttDb, handle_prepare_write_request){ uint16_t attribute_handle = 0x0011; uint16_t value_offset = 0x10; // not sufficient request length { att_request[0] = ATT_PREPARE_WRITE_REQUEST; att_request_len = 1; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], 0x00, 0x00, ATT_ERROR_INVALID_PDU}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // write not permited: invalid write callback { att_set_write_callback(NULL); att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); att_set_write_callback(&att_write_callback); } // invalid handle { att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, 0, 0); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INVALID_HANDLE}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // write not permited: no ATT_PROPERTY_WRITE { attribute_handle = 0x000c; // 0x2A49 att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // write not permited: no ATT_PROPERTY_DYNAMIC { attribute_handle = 0x0003; att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_WRITE_NOT_PERMITTED}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // security validation { attribute_handle = 0x000f; // 0x2A35 att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INSUFFICIENT_AUTHENTICATION}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } // some callback error other then ATT_INTERNAL_WRITE_RESPONSE_PENDING { write_callback_mode = WRITE_CALLBACK_MODE_RETURN_INVALID_ATTRIBUTE_VALUE_LENGTH; attribute_handle = 0x0011; // 0x2A38 // prepare write att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response_prepare_write[] = {ATT_PREPARE_WRITE_RESPONSE, 0x11, 0x00, 0x10, 0x00}; MEMCMP_EQUAL(expected_response_prepare_write, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response_prepare_write), att_response_len); // execute write att_request_len = att_prepare_write_request(ATT_EXECUTE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response_write_execute[] = {ATT_ERROR_RESPONSE, att_request[0], att_request[1], att_request[2], ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH}; MEMCMP_EQUAL(expected_response_write_execute, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response_write_execute), att_response_len); write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; } #ifdef ENABLE_ATT_DELAYED_RESPONSE // delayed response { write_callback_mode = WRITE_CALLBACK_MODE_RETURN_ERROR_WRITE_RESPONSE_PENDING; attribute_handle = 0x0011; // 0x2A38 att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(ATT_INTERNAL_WRITE_RESPONSE_PENDING, att_response_len); write_callback_mode = WRITE_CALLBACK_MODE_RETURN_DEFAULT; } #endif // correct write { attribute_handle = 0x0011; att_request_len = att_prepare_write_request(ATT_PREPARE_WRITE_REQUEST, attribute_handle, value_offset); CHECK_EQUAL(5, att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); const uint8_t expected_response[] = {ATT_PREPARE_WRITE_RESPONSE, 0x11, 0x00, 0x10, 0x00}; MEMCMP_EQUAL(expected_response, att_response, att_response_len); CHECK_EQUAL(sizeof(expected_response), att_response_len); } } TEST(AttDb, att_uuid_for_handle){ // existing attribute handle uint16_t uuid = att_uuid_for_handle(0x0011); uint16_t expected_response = 0x2A38; CHECK_EQUAL(expected_response, uuid); // unknown attribute handle uuid = att_uuid_for_handle(0xFF00); expected_response = 0; CHECK_EQUAL(expected_response, uuid); // attribute handle for uuid128 uuid = att_uuid_for_handle(0x0014); expected_response = 0; CHECK_EQUAL(expected_response, uuid); } TEST(AttDb, gatt_server_get_handle_range){ uint16_t start_handle; uint16_t end_handle; bool service_exists = gatt_server_get_handle_range_for_service_with_uuid16(0x00, &start_handle, &end_handle); CHECK_EQUAL(false, service_exists); uint16_t attribute_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(0, 0xffff, 0x00); CHECK_EQUAL(0x00, attribute_handle); attribute_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); uint16_t configuration_handle = gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(0, 0xffff, attribute_handle); CHECK_EQUAL(0x00, configuration_handle); } TEST(AttDb, gatt_server_get_handle_range_for_service){ uint16_t start_handle; uint16_t end_handle; const uint8_t uuid128_1[] = {0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; const uint8_t uuid128_2[] = {0xAA, 0xBB, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; const uint8_t uuid128_3[] = {0xAA, 0xDD, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; bool service_exists = gatt_server_get_handle_range_for_service_with_uuid128(uuid128_1, &start_handle, &end_handle); CHECK_EQUAL(false, service_exists); service_exists = gatt_server_get_handle_range_for_service_with_uuid128(uuid128_2, &start_handle, &end_handle); CHECK_EQUAL(true, service_exists); uint16_t out_included_service_handle; uint16_t out_included_service_start_handle; uint16_t out_included_service_end_handle; service_exists = gatt_server_get_included_service_with_uuid16(0, 0xffff, 0xAA, &out_included_service_handle, &out_included_service_start_handle, &out_included_service_end_handle); CHECK_EQUAL(false, service_exists); service_exists = gatt_server_get_included_service_with_uuid16(0, 0, 0xAA, &out_included_service_handle, &out_included_service_start_handle, &out_included_service_end_handle); CHECK_EQUAL(false, service_exists); service_exists = gatt_server_get_included_service_with_uuid16(0, 0xffff, 0xAACC, &out_included_service_handle, &out_included_service_start_handle, &out_included_service_end_handle); CHECK_EQUAL(true, service_exists); } TEST(AttDb, gatt_server_get_value_handle_for_characteristic_with_uuid128){ const uint8_t uuid128_1[] = {0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; const uint8_t uuid128_2[] = {0xAA, 0xBB, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; uint16_t value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(0, 0xffff, uuid128_1); CHECK_EQUAL(20, value_handle); CHECK_EQUAL(false, att_is_persistent_ccc(value_handle)); CHECK_EQUAL(false, att_is_persistent_ccc(0x60)); value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(0, 0xffff, uuid128_2); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(0, 0, uuid128_2); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(0xffff, 0, uuid128_2); CHECK_EQUAL(0, value_handle); } TEST(AttDb, gatt_server_get_client_configuration_handle_for_characteristic){ const uint8_t uuid128_1[] = {0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; const uint8_t uuid128_2[] = {0xAA, 0xBB, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; const uint8_t uuid128_3[] = {0xAA, 0xCC, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; uint16_t value_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(0, 0xffff, uuid128_1); CHECK_EQUAL(21, value_handle); value_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(0, 0xffff, uuid128_2); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(0, 0, uuid128_2); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(0xffff, 0, uuid128_2); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(0xffff, 0, uuid128_3); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_BLOOD_PRESSURE_MEASUREMENT, GATT_SERVER_CHARACTERISTICS_CONFIGURATION); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, GATT_SERVER_CHARACTERISTICS_CONFIGURATION); CHECK_EQUAL(0, value_handle); value_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(0, 0, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, GATT_SERVER_CHARACTERISTICS_CONFIGURATION); CHECK_EQUAL(0, value_handle); } TEST(AttDb, handle_signed_write_command){ uint16_t attribute_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE); att_request[0] = ATT_SIGNED_WRITE_COMMAND; att_request_len = 1; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } TEST(AttDb, handle_write_command){ uint16_t attribute_handle = 0x03; att_dump_attributes(); // not sufficient request length { att_request[0] = ATT_WRITE_COMMAND; att_request_len = 1; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } { att_request[0] = ATT_WRITE_COMMAND; att_request[1] = 0x03; att_request_len = 2; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } // invalid write callback { att_set_write_callback(NULL); const uint8_t value[] = {0x50}; att_request_len = att_write_request(ATT_WRITE_COMMAND, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); att_set_write_callback(&att_write_callback); } // invalid handle { att_request[0] = ATT_WRITE_COMMAND; att_request[1] = 0; att_request[2] = 0; att_request_len = 3; att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } // write not permited: no ATT_PROPERTY_DYNAMIC { const uint8_t value[] = {0x50}; attribute_handle = 0x0003; att_request_len = att_write_request(ATT_WRITE_COMMAND, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } // write not permited: no ATT_PROPERTY_WRITE_WITHOUT_RESPONSE { const uint8_t value[] = {0x50}; attribute_handle = 0x000c; // 0x2A49 att_request_len = att_write_request(ATT_WRITE_COMMAND, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } // security validation { const uint8_t value[] = {0x50}; attribute_handle = 0x0017; // 0x2AA7 att_request_len = att_write_request(ATT_WRITE_COMMAND, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } // correct write { const uint8_t value[] = {0x50}; attribute_handle = 0x001A; // 0x2AAB att_request_len = att_write_request(ATT_WRITE_COMMAND, attribute_handle, sizeof(value), value); CHECK_EQUAL(3 + sizeof(value), att_request_len); att_response_len = att_handle_request(&att_connection, (uint8_t *) att_request, att_request_len, att_response); CHECK_EQUAL(0, att_response_len); } } TEST(AttDb, att_read_callback_handle_blob){ { const uint8_t blob[] = {0x44, 0x55}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 0, NULL, 0); CHECK_EQUAL(2, blob_size); } { const uint8_t blob[] = {}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { const uint8_t blob[] = {}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { const uint8_t blob[] = {0x55}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(1, blob_size); } { const uint8_t blob[] = {0x55}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { const uint8_t blob[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(10, blob_size); } { const uint8_t blob[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 1, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(9, blob_size); } { const uint8_t blob[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA}; uint16_t blob_size = att_read_callback_handle_blob(blob, sizeof(blob), 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } } TEST(AttDb, att_read_callback_handle_byte){ { uint16_t blob_size = att_read_callback_handle_byte(0x55, 0, NULL, 0); CHECK_EQUAL(1, blob_size); } { uint16_t blob_size = att_read_callback_handle_byte(0x55, 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { uint16_t blob_size = att_read_callback_handle_byte(0x55, 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(1, blob_size); } } TEST(AttDb, att_read_callback_handle_little_endian_16){ { uint16_t blob_size = att_read_callback_handle_little_endian_16(0x1122, 0, NULL, 0); CHECK_EQUAL(2, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_16(0x1122, 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_16(0x1122, 1, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(1, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_16(0x1122, 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(2, blob_size); } } TEST(AttDb, att_read_callback_handle_little_endian_32){ { uint16_t blob_size = att_read_callback_handle_little_endian_32(0x11223344, 0, NULL, 0); CHECK_EQUAL(4, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_32(0x11223344, 10, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(0, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_32(0x11223344, 3, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(1, blob_size); } { uint16_t blob_size = att_read_callback_handle_little_endian_32(0x11223344, 0, callback_buffer, sizeof(callback_buffer)); CHECK_EQUAL(4, blob_size); } } int main (int argc, const char * argv[]){ return CommandLineTestRunner::RunAllTests(argc, argv); }