pebble/tests/fw/comm/test_gap_le_connect.c
2025-01-27 11:38:16 -08:00

871 lines
34 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bluetooth/gap_le_connect.h"
#include "comm/ble/gap_le_connect.h"
#include "comm/ble/gap_le_connection.h"
#include "comm/ble/gap_le_task.h"
#include "kernel/events.h"
#include "services/common/analytics/analytics.h"
#include "clar.h"
#include <bluetooth/bonding_sync.h>
#include <bluetooth/sm_types.h>
#include <btutil/bt_device.h>
// Fakes
///////////////////////////////////////////////////////////
#include "fake_events.h"
#include "fake_GAPAPI.h"
#include "fake_bluetooth_persistent_storage.h"
#include "fake_HCIAPI.h"
#include "fake_new_timer.h"
#include "fake_pbl_malloc.h"
#include "fake_system_task.h"
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_bluetopia_interface.h"
#include "stubs_bt_lock.h"
#include "stubs_gap_le_advert.h"
#include "stubs_bluetooth_analytics.h"
#include "stubs_gatt_client_discovery.h"
#include "stubs_gatt_client_subscriptions.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pebble_pairing_service.h"
#include "stubs_regular_timer.h"
#include "stubs_shared_prf_storage.h"
// Note:
// The unit tests for "Pebble as Master" are disabled because role switching is not implemented yet,
// and the FW is currently "hard-wired" to be slave as a precautionary measure to prevent it from
// trying to connect as master. See PBL-20368.
void bt_driver_cb_handle_create_bonding(const BleBonding *bonding,
const BTDeviceAddress *addr) {
}
void cc2564A_bad_le_connection_complete_handle(unsigned int stack_id,
const GAP_LE_Current_Connection_Parameters_t *params) {
}
const GAP_LE_Pairing_Capabilities_t* gap_le_pairing_capabilities(void) {
return NULL;
}
void gap_le_device_name_request(uintptr_t stack_id, GAPLEConnection *connection) {
}
void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connection) {
}
void bt_driver_handle_le_conn_params_update_event(
const BleConnectionUpdateCompleteEvent *event) {
}
typedef struct PairingUserConfirmationCtx PairingUserConfirmationCtx;
void bt_driver_pebble_pairing_service_handle_status_change(const GAPLEConnection *connection) {
}
void bt_driver_cb_pairing_confirm_handle_request(const PairingUserConfirmationCtx *ctx,
const char *device_name,
const char *confirmation_token) {
}
void bt_driver_cb_pairing_confirm_handle_completed(const PairingUserConfirmationCtx *ctx,
bool success) {
}
void launcher_task_add_callback(void (*callback)(void *data), void *data) {
callback(data);
}
void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) {
}
void bluetooth_analytics_handle_connection_disconnection_event(
AnalyticsEvent type, uint8_t reason, const BleRemoteVersionInfo *vers_info) {
}
void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) {
}
// Helpers
///////////////////////////////////////////////////////////
static BTDeviceInternal prv_dummy_device(uint8_t octet) {
BTDeviceAddress address = {
.octets = {
[0] = octet,
[1] = octet,
[2] = octet,
[3] = octet,
[4] = octet,
[5] = octet,
},
};
BTDevice device = bt_device_init_with_address(address, true /* is_random */);
return *(BTDeviceInternal *)(&device);
}
static BTBondingID prv_add_bonding_for_fake_resolvable_device(void) {
BTDeviceInternal identity_device = {};
const SMIdentityResolvingKey *irk = (const SMIdentityResolvingKey *) fake_GAPAPI_get_fake_irk();
BleBonding bonding = {
.pairing_info = {
.identity = identity_device,
.irk = *irk,
.is_remote_identity_info_valid = true,
},
.is_gateway = true,
};
bt_driver_handle_host_added_bonding(&bonding);
return fake_bt_persistent_storage_add(irk, &identity_device, "Dummy", true /* is_gateway */);
}
static void prv_assert_no_event(void) {
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_NULL_EVENT);
}
static void prv_fake_connect(const BTDeviceInternal *device, bool is_master) {
// Simulate getting a Connection Complete event for the device from Bluetopia:
fake_gap_put_connection_event(HCI_ERROR_CODE_SUCCESS,
is_master, device);
cl_assert_equal_b(gap_le_connection_is_connected(device), true);
}
static void prv_fake_disconnect(const BTDeviceInternal *device, bool is_master) {
fake_gap_put_disconnection_event(HCI_ERROR_CODE_SUCCESS,
HCI_ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST,
is_master,
device);
cl_assert_equal_b(gap_le_connection_is_connected(device), false);
}
static void prv_assert_client_event(const BTDeviceInternal *device,
bool connected,
PebbleTaskBitset client_tasks,
uint8_t hci_reason) {
// Verify the Pebble event:
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_BLE_CONNECTION_EVENT);
// Event should only go to app:
cl_assert_equal_i(event.task_mask, (PebbleTaskBitset) ~(client_tasks));
const PebbleBLEConnectionEvent *conn_event = &event.bluetooth.le.connection;
const BTDeviceInternal event_device = PebbleEventToBTDeviceInternal(conn_event);
const bool is_same_device = bt_device_equal(&event_device.opaque,
&device->opaque);
cl_assert_equal_b(is_same_device, true);
cl_assert_equal_b(conn_event->connected, connected);
cl_assert_equal_i(conn_event->hci_reason, hci_reason);
}
// Tests
///////////////////////////////////////////////////////////
extern void gap_le_connect_bluetopia_connection_callback(
unsigned int stack_id, GAP_LE_Event_Data_t* event_data, unsigned long CallbackParameter);
void test_gap_le_connect__initialize(void) {
fake_GAPAPI_init();
// Register slave connection event callback for tests involving Pebble as slave:
// This normally happens in gap_le_advert.c. Taking a shortcut to avoid dragging in more code.
GAP_LE_Advertising_Enable(1, TRUE, NULL, NULL, gap_le_connect_bluetopia_connection_callback, 0);
fake_event_init();
fake_bt_persistent_storage_reset();
gap_le_connection_init();
gap_le_connect_init();
}
void test_gap_le_connect__cleanup(void) {
// Cancel all connection intents:
for (GAPLEClient c = 0; c < GAPLEClientNum; ++c) {
gap_le_connect_cancel_all(c);
}
gap_le_connect_deinit();
cl_assert_equal_b(gap_le_connect_has_pending_create_connection(), false);
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
gap_le_connection_deinit();
cl_assert_equal_b(fake_HCIAPI_whitelist_error_count(), 0);
fake_HCIAPI_deinit();
}
// -----------------------------------------------------------------------------
// Parameter / Bounds checking
void test_gap_le_connect__register_max_intents(void) {
for (int i = 0; i < GAP_LE_CONNECT_MASTER_MAX_CONNECTION_INTENTS + 1; ++i) {
BTDeviceInternal device = prv_dummy_device(i);
BTErrno e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
if (i == GAP_LE_CONNECT_MASTER_MAX_CONNECTION_INTENTS) {
// When the limit is reached, expect "not enough resources" error:
cl_assert_equal_i(e, BTErrnoNotEnoughResources);
} else {
cl_assert_equal_i(e, BTErrnoOK);
const bool registered = gap_le_connect_has_connection_intent(&device,
GAPLEClientApp);
cl_assert_equal_b(registered, true);
}
}
}
void test_gap_le_connect__register_null_device(void) {
BTErrno e = gap_le_connect_connect(NULL,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__unregister_null_device(void) {
BTErrno e = gap_le_connect_cancel(NULL, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__register_invalid_bonding(void) {
BTErrno e = gap_le_connect_connect_by_bonding(BT_BONDING_ID_INVALID,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__register_non_existing_bonding(void) {
BTErrno e = gap_le_connect_connect_by_bonding(~0,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__unregister_invalid_bonding(void) {
BTErrno e = gap_le_connect_cancel_by_bonding(BT_BONDING_ID_INVALID, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__unregister_non_existing_bonding(void) {
BTErrno e = gap_le_connect_cancel_by_bonding(~0, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__register_is_already_registered_for_same_client(void) {
BTErrno e;
bool registered;
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientApp);
cl_assert_equal_b(registered, true);
// Try registering the device again as same client:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidState);
// Should still be registered from the first call:
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientApp);
cl_assert_equal_b(registered, true);
}
void test_gap_le_connect__register_same_device_and_bonding(void) {
// Test that it is possible to have 2 intents for the same device, when registering one intent
// using the resolvable address and one with a bonding. Pebble will not try to collate these,
// because there are many addresses that resolve to the same bonding. The current implementation
// uses one address or one bonding per intent.
BTErrno e;
bool registered;
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientApp);
cl_assert_equal_b(registered, true);
// Register another connection intent using the bonding:
e = gap_le_connect_connect_by_bonding(bonding_id,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
registered = gap_le_connect_has_connection_intent_for_bonding(bonding_id, GAPLEClientApp);
cl_assert_equal_b(registered, true);
}
void test_gap_le_connect__register_two_clients_same_device(void) {
BTErrno e;
bool registered;
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientApp);
cl_assert_equal_b(registered, true);
// Try registering the device again for different client:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
cl_assert_equal_i(e, BTErrnoOK);
// Assert registrations:
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientApp);
cl_assert_equal_b(registered, true);
registered = gap_le_connect_has_connection_intent(&device, GAPLEClientKernel);
cl_assert_equal_b(registered, true);
// Only one registration (co-owned by the 2 clients):
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 1);
}
void test_gap_le_connect__unregister_unknown_device(void) {
BTDeviceInternal device = prv_dummy_device(1);
BTErrno e = gap_le_connect_cancel(&device, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
void test_gap_le_connect__unregister_unowned_intent(void) {
BTDeviceInternal device = prv_dummy_device(1);
BTErrno e;
// Register connection intent owned by kernel:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
cl_assert_equal_i(e, BTErrnoOK);
// Unregister connection intent owned by app:
e = gap_le_connect_cancel(&device, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoInvalidParameter);
}
// -----------------------------------------------------------------------------
// Virtual (dis)connection events
void __disabled_test_gap_le_connect__connection_event_for_registered_client(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent:
BTErrno e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// Device isn't connected. Verify no event was caused as a result of the
// registration.
prv_assert_no_event();
// Connect & verify the client task gets the event:
prv_fake_connect(&device, true /* is_master*/);
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
// Disconnect & verify the client task gets the event:
prv_fake_disconnect(&device, true /* is_master */);
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST);
}
void test_gap_le_connect__connection_event_for_registered_client_by_bonding(void) {
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent:
BTErrno e = gap_le_connect_connect_by_bonding(bonding_id,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// Device isn't connected. Verify no event was caused as a result of the
// registration.
prv_assert_no_event();
// Connect & verify the client task gets the event:
prv_fake_connect(&device, false /* is_master*/);
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
// Disconnect & verify the client task gets the event:
prv_fake_disconnect(&device, false /* is_master */);
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST);
}
void __disabled_test_gap_le_connect__register_for_already_connected_device(void) {
BTErrno e;
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for kernel:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
cl_assert_equal_i(e, BTErrnoOK);
// Simulate getting a Connection Complete event for the device from Bluetopia:
prv_fake_connect(&device, true /* is_master*/);
// Verify the kernel task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_KernelMain),
HCI_ERROR_CODE_SUCCESS);
// Register connection intent for app:
e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// Verify (only) the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
}
void test_gap_le_connect__register_for_already_connected_bonding(void) {
BTErrno e;
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent for kernel:
e = gap_le_connect_connect_by_bonding(bonding_id,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
cl_assert_equal_i(e, BTErrnoOK);
// Simulate getting a Connection Complete event for the device from Bluetopia:
prv_fake_connect(&device, false /* is_master*/);
// Verify the kernel task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_KernelMain),
HCI_ERROR_CODE_SUCCESS);
// Register connection intent for app:
e = gap_le_connect_connect_by_bonding(bonding_id,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// Verify (only) the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
}
void __disabled_test_gap_le_connect__disconnection_event_upon_airplane_mode(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent:
BTErrno e = gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
prv_fake_connect(&device, true /* is_master*/);
// Air-plane mode:
gap_le_connect_deinit();
// Verify (only) the app task got a (virtual) connection event:
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
GAPLEConnectHCIReasonExtensionAirPlaneMode);
}
// -----------------------------------------------------------------------------
// Auto-reconnect Tests
void __disabled_test_gap_le_connect__single_client_no_autoreconnect(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
prv_fake_connect(&device, true /* is_master*/);
// Verify the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
prv_fake_disconnect(&device, true /* is_master */);
// Verify the app task got a (virtual) disconnection event:
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST);
// Verify that the connection intent has been removed after disconnection:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
}
void __disabled_test_gap_le_connect__two_clients_one_without_autoreconnect(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register auto-reconnecting connection intent for kernel:
gap_le_connect_connect(&device,
true /* auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
prv_fake_connect(&device, true /* is_master*/);
// Register one-shot connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
prv_fake_disconnect(&device, true /* is_master */);
// Verify both app task and kernel got the (virtual) disconnection event:
prv_assert_client_event(&device, false /* connected */,
(1 << PebbleTask_App) | (1 << PebbleTask_KernelMain),
HCI_ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST);
// Verify that the connection intent is still there for the Kernel:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 1);
cl_assert_equal_b(gap_le_connect_has_connection_intent(&device, GAPLEClientKernel), true);
cl_assert_equal_b(gap_le_connect_has_connection_intent(&device, GAPLEClientApp), false);
cl_assert_equal_b(gap_le_connect_has_pending_create_connection(), true);
}
// -----------------------------------------------------------------------------
// Cancel Connect (as Master)
void __disabled_test_gap_le_connect__cancel_connect(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
BTErrno e = gap_le_connect_cancel(&device, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// The LE Cancel Create Connection command is always followed by an event that
// is sent by the BT Controller. Simulate this event:
fake_gap_le_put_cancel_create_event(&device, true /* is_master */);
prv_assert_no_event();
// Verify there are no more intents:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
cl_assert_equal_b(gap_le_connect_has_pending_create_connection(), false);
}
void __disabled_test_gap_le_connect__disconnection_event_upon_cancel_connect(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
prv_fake_connect(&device, false /* is_master*/);
BTErrno e = gap_le_connect_cancel(&device, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// The LE Cancel Create Connection command is always followed by an event that
// is sent by the BT Controller. Simulate this event:
fake_gap_le_put_cancel_create_event(&device, true /* is_master */);
// Verify the app task got a (virtual) disconnection event:
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
GAPLEConnectHCIReasonExtensionCancelConnect);
// Verify there are no more intents:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
}
// -----------------------------------------------------------------------------
// Cancel Connect by Bonding (as Slave)
void test_gap_le_connect__slave_cancel_connect_by_bonding(void) {
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent for app:
gap_le_connect_connect_by_bonding(bonding_id,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
BTErrno e = gap_le_connect_cancel_by_bonding(bonding_id, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
prv_assert_no_event();
// Verify there are no more intents:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
cl_assert_equal_b(gap_le_connect_has_pending_create_connection(), false);
}
void test_gap_le_connect__slave_disconnection_event_upon_cancel_connect_by_bonding(void) {
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
prv_fake_connect(&device, false /* is_master*/);
// Register connection intent for app:
gap_le_connect_connect_by_bonding(bonding_id,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
BTErrno e = gap_le_connect_cancel_by_bonding(bonding_id, GAPLEClientApp);
cl_assert_equal_i(e, BTErrnoOK);
// Verify the app task got a (virtual) disconnection event:
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
GAPLEConnectHCIReasonExtensionCancelConnect);
// Verify there are no more intents:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
}
// -------------------------------------------------------------------------------------------------
// Pairing
void __disabled_test_gap_le_connect__one_shot_intent_removed_when_disconnected_before_encrpt(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection one-shot intent, with pairing required:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
true /* is_pairing_required */,
GAPLEClientKernel);
// Expect intent:
cl_assert_equal_b(gap_le_connect_has_connection_intent(&device, GAPLEClientKernel), true);
prv_fake_connect(&device, true /* is_master*/);
prv_fake_disconnect(&device, true /* is_master */);
// Expect intent to be removed:
cl_assert_equal_b(gap_le_connect_has_connection_intent(&device, GAPLEClientKernel), false);
}
void test_gap_le_connect__connection_event_only_after_encrypted_if_encryption_required(void) {
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent for app:
gap_le_connect_connect_by_bonding(bonding_id,
true /* no auto_reconnect */,
true /* is_pairing_required */,
GAPLEClientApp);
prv_fake_connect(&device, false /* is_master*/);
// Verify the app task got NO (virtual) connection event, the link is not encrypted yet:
// TODO: legacy PEBBLE_BT_CONNECTION_EVENT is still emitted, see gap_le_connect.c
// prv_put_legacy_connection_event.
//
// prv_assert_no_event();
fake_event_clear_last();
fake_GAPAPI_set_encrypted_for_device(&device);
fake_GAPAPI_put_encryption_change_event(true /* encrypted */, GAP_LE_PAIRING_STATUS_NO_ERROR,
false /* is_master */, &device);
// Verify the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
}
void test_gap_le_connect__add_intent_requiring_pairing_after_connected_and_encrypted(void) {
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
prv_fake_connect(&device, false /* is_master*/);
fake_GAPAPI_set_encrypted_for_device(&device);
fake_GAPAPI_put_encryption_change_event(true /* encrypted */, GAP_LE_PAIRING_STATUS_NO_ERROR,
false /* is_master */, &device);
fake_event_clear_last();
const BleAddressAndIRKChange e = {
.device = device,
.is_address_updated = true,
.new_device = device,
.is_resolved = true,
.irk = *(const SMIdentityResolvingKey *) fake_GAPAPI_get_fake_irk(),
};
bt_driver_handle_le_connection_handle_update_address_and_irk(&e);
gap_le_connection_by_device(&device);
// Register connection intent for app:
gap_le_connect_connect_by_bonding(bonding_id,
true /* no auto_reconnect */,
true /* is_pairing_required */,
GAPLEClientApp);
// Verify the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
}
// -----------------------------------------------------------------------------
// Handling Bonding Changes
void test_gap_le_connect__removed_bonding_while_connected(void) {
BTDeviceInternal device = *fake_GAPAPI_get_device_resolving_to_fake_irk();
BTBondingID bonding_id = prv_add_bonding_for_fake_resolvable_device();
// Register connection intent for app:
gap_le_connect_connect_by_bonding(bonding_id,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
prv_fake_connect(&device, false /* is_master*/);
// Verify the app task got a (virtual) connection event:
prv_assert_client_event(&device, true /* connected */, (1 << PebbleTask_App),
HCI_ERROR_CODE_SUCCESS);
// Simulate "bonding will delete" callback:
gap_le_connect_handle_bonding_change(bonding_id, BtPersistBondingOpWillDelete);
// Verify the app task got a (virtual) disconnection event:
prv_assert_client_event(&device, false /* connected */, (1 << PebbleTask_App),
GAPLEConnectHCIReasonExtensionUserRemovedBonding);
// Verify there are no more intents:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 0);
}
// -----------------------------------------------------------------------------
// BT Controller White-list management
void __disabled_test_gap_le_connect__whitelist_add_when_disconnected(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
// Not connected yet, so expect to be added to white-list:
cl_assert_equal_b(fake_HCIAPI_whitelist_contains(&device), true);
prv_fake_connect(&device, true /* is_master*/);
// Connected, so expect to be removed from white-list:
cl_assert_equal_b(fake_HCIAPI_whitelist_contains(&device), false);
}
void __disabled_test_gap_le_connect__whitelist_add_when_connected(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientKernel);
prv_fake_connect(&device, true /* is_master*/);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
// Connected, so expect to be removed from white-list:
cl_assert_equal_b(fake_HCIAPI_whitelist_contains(&device), false);
}
void __disabled_test_gap_le_connect__whitelist_remove_when_connected(void) {
BTDeviceInternal device = prv_dummy_device(1);
// Register connection intent for app:
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
prv_fake_connect(&device, true /* is_master*/);
gap_le_connect_cancel(&device, GAPLEClientApp);
// Connected, so expect to be removed from white-list:
cl_assert_equal_b(fake_HCIAPI_whitelist_contains(&device), false);
}
void __disabled_test_gap_le_connect__whitelist_repopulated_on_init(void) {
BTDeviceInternal device = prv_dummy_device(1);
gap_le_connect_connect(&device,
false /* no auto_reconnect */,
false /* is_pairing_required */,
GAPLEClientApp);
gap_le_connect_deinit();
// Connection intents survive air-plane mode:
cl_assert_equal_i(gap_le_connect_connection_intents_count(), 1);
// "Reset" BT Controller:
fake_HCIAPI_deinit();
cl_assert_equal_i(fake_HCIAPI_whitelist_count(), 0);
gap_le_connect_init();
// Not connected yet, so expect to be added to white-list:
cl_assert_equal_b(fake_HCIAPI_whitelist_contains(&device), true);
cl_assert_equal_i(fake_HCIAPI_whitelist_count(), 1);
}