mirror of
https://github.com/google/pebble.git
synced 2025-03-21 03:11:21 +00:00
871 lines
34 KiB
C
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);
|
|
|
|
}
|