diff --git a/lib/ble/ble_transport.c b/lib/ble/ble_transport.c index d541b14f..77c43a7c 100644 --- a/lib/ble/ble_transport.c +++ b/lib/ble/ble_transport.c @@ -329,6 +329,7 @@ int ble_advertise(struct ble_transport* t, const void* data, size_t len) { return ble_backend_advertise(t->backend, data, len); } ble_link_id ble_connect(struct ble_transport* t, const struct ble_endpoint* ep, uint64_t peer) { + if (!peer) { DEBUG_ERROR(CAT, "BLE connect requires expected peer service identity"); return 0; } if (!t || !t->ready) { DEBUG_WARN(CAT, "BLE connect before ready"); return 0; } struct ble_connection* c = new_link(t, ep, 1, peer); if (!c) return 0; diff --git a/lib/ble/ble_transport.h b/lib/ble/ble_transport.h index af73c7e3..439f693e 100644 --- a/lib/ble/ble_transport.h +++ b/lib/ble/ble_transport.h @@ -39,7 +39,7 @@ struct ble_advertising_power { /* Копия возможностей активного радио, без I/O; NULL/не готово возвращает неизвестные значения. Только uasync. */ struct ble_advertising_power ble_get_advertising_power(const struct ble_transport* transport); struct ble_callbacks { - /* Объявление нужного company_id; буфер заимствован до возврата. RSSI=127 означает неизвестный. */ + /* Node UUID из объявления сервиса: общий ble_identity bytes; буфер заимствован. RSSI=127 — неизвестный. */ void (*broadcast)(void* arg, const struct ble_endpoint* endpoint, const uint8_t* data, size_t len, int rssi); void (*status)(void* arg, enum ble_state state, const char* reason); /* Состояние радио, reason заимствован. */ void (*advertising)(void* arg, enum ble_state state, const char* reason); /* Публикация bytes: READY не подтверждает приём. */ @@ -52,14 +52,14 @@ struct ble_callbacks { }; struct ble_config { uint64_t node_id; /* Ненулевой ID для HELLO; не криптографическое доказательство. */ - uint16_t company_id; /* Manufacturer ID, одинаковый у участников. 0xffff только для разработки. */ + uint16_t company_id; /* Необязательные метаданные имени Android; адресация определяется UUID, а не manufacturer. */ const char* adapter; /* Linux: hci0 либо NULL (первый адаптер); Windows: NULL. Копируется. */ struct ble_callbacks callbacks; void* arg; /* Заимствованный контекст callbacks, живёт до destroy. */ unsigned tx_limit; /* Целых TX-пакетов, включая фрагментируемый: 0=64, 1..64. */ unsigned tx_window; /* DATA в очереди/отправке/ожидании удалённого ACK: 0=BLE_TX_WINDOW_FRAMES, 1..64. */ uint8_t initial_advertisement[BLE_BROADCAST_MAX]; /* Начальное объявление; копируется вместе с config. */ - size_t initial_advertisement_size; /* 0: выключено; 1..24: Windows запускает publisher до GATT. */ + size_t initial_advertisement_size; /* 0: скрыть идентичность в advertising; 1..24: объявлять node UUID сервиса. */ }; /* Создать остановленный transport. ua/config заимствованы только ua; возвращает handle или NULL с логом. */ struct ble_transport* ble_create(struct UASYNC* ua, const struct ble_config* config); @@ -67,13 +67,14 @@ struct ble_transport* ble_create(struct UASYNC* ua, const struct ble_config* con int ble_start(struct ble_transport* transport); /* Остановить свои объявления/scan/сессии, завершить pending и освободить очереди. Идемпотентно, вне callbacks. */ void ble_stop(struct ble_transport* transport); -/* Разрешить/запретить новые входящие; запрет закрывает текущие входящие. По умолчанию разрешены. */ +/* Разрешить/запретить наши новые входящие сессии; запрет закрывает текущие входящие. Общий ACL ОС этим не управляется. */ void ble_set_incoming(struct ble_transport* transport, int allowed); void ble_destroy(struct ble_transport* transport); /* stop + free; NULL допустим. */ -/* Заменить повторяемое manufacturer-объявление (0..24 байта); копирует данные. NULL,0 выключает его. - * Регистрация не гарантирует выход в эфир/получение. Собственное connectable-объявление сервиса независимо. */ +/* Задать метаданные ble_identity и публиковать node UUID сервиса. NULL,0 скрывает UUID/метаданные в advertising. + * Сервис остаётся зарегистрированным для известных адресов; допуск определяет ble_set_incoming, не видимость. + * Регистрация не гарантирует выход в эфир/получение. Собственное имя Windows выбирается ОС. */ int ble_advertise(struct ble_transport* transport, const void* data, size_t len); -/* Самостоятельно выполнить connect/discovery/subscribe/HELLO. expected_node=0 не проверяет ID ответа. +/* Самостоятельно выполнить connect/discovery/subscribe/HELLO. expected_node ненулевой, задаёт UUID сервиса и ID ответа. * Handle либо 0 при отказе. Готовность/ошибка — connection; физическое ACL может обслуживать другие сервисы. */ ble_link_id ble_connect(struct ble_transport* transport, const struct ble_endpoint* endpoint, uint64_t expected_node); /* Копировать направление существующего GATT в outgoing (0/1). 0 — успех, -1 — неверный handle/аргументы с логом. */ diff --git a/lib/ble/ble_winrt.cpp b/lib/ble/ble_winrt.cpp index ba4e7c64..a88aacb0 100644 --- a/lib/ble/ble_winrt.cpp +++ b/lib/ble/ble_winrt.cpp @@ -285,7 +285,8 @@ struct WinState : std::enable_shared_from_this { parameters.IsConnectable(true); parameters.IsDiscoverable(broadcast_enabled); DEBUG_INFO(CAT, "BLE WinRT start single GATT advertisement UUID=%s connectable=1 discoverable=%d", service_uuid.c_str(), broadcast_enabled); - service_requested = true; publication_deadline = get_time_us() + 20000000; + service_requested = true; + if (!publication_deadline) publication_deadline = get_time_us() + 20000000; step("GattServiceProvider.StartAdvertising"); provider.StartAdvertising(parameters); } /* Один GATT provider обслуживает и обнаружение, и подключения; отдельного publisher нет. */ @@ -304,11 +305,14 @@ struct WinState : std::enable_shared_from_this { } return; } - if (status == GattServiceProviderAdvertisementStatus::Started) { + if (status == GattServiceProviderAdvertisementStatus::Started || + status == GattServiceProviderAdvertisementStatus::StartedWithoutAllAdvertisementData) { + if (status != GattServiceProviderAdvertisementStatus::Started) + DEBUG_WARN(CAT, "BLE WinRT advertising omitted some fields UUID=%s; confirm service UUID on scanner", service_uuid.c_str()); publication_deadline = 0; service_ready = true; ble_backend_advertising(owner, broadcast_enabled ? BLE_READY : BLE_CLOSED, "GATT discovery publication applied"); ready(); } else if (status == GattServiceProviderAdvertisementStatus::Aborted) radio_error("GATT advertising aborted", error); - else if (service_requested) fail("GATT advertisement stopped unexpectedly"); + else if (service_requested && service_ready) fail("GATT advertisement stopped unexpectedly"); } /* Только доступные Win11 API: фактические параметры отделены от запрошенного профиля. */ void log_connection(ble_link_id id) { @@ -792,6 +796,7 @@ extern "C" struct ble_backend* ble_backend_create(struct ble_transport* owner, s auto state = std::make_shared(); state->owner = owner; state->ua = ua; state->company = cfg->company_id; state->broadcast_size = cfg->initial_advertisement_size; state->broadcast_enabled = state->broadcast_size != 0; + state->publication_deadline = get_time_us() + 20000000; char uuid[37]; if (ble_service_uuid_encode(uuid, cfg->node_id)) return nullptr; state->service_uuid = uuid; if (state->broadcast_size > state->broadcast.size()) { DEBUG_ERROR(CAT, "BLE WinRT initial advertisement too large"); return nullptr; } if (state->broadcast_size) std::memcpy(state->broadcast.data(), cfg->initial_advertisement, state->broadcast_size); @@ -827,9 +832,10 @@ extern "C" struct ble_backend* ble_backend_create(struct ble_transport* owner, s if (!adapter.IsLowEnergySupported() || !adapter.IsCentralRoleSupported() || !adapter.IsPeripheralRoleSupported()) { s.fail("adapter lacks BLE central/peripheral support"); return; } - s.adapter_ready = true; WinState::diagnostics_tick(&s); + s.adapter_ready = true; s.ensure_service(); }); + WinState::diagnostics_tick(state.get()); void* memory = u_malloc(sizeof(ble_backend)); if (!memory) { DEBUG_ERROR(CAT, "BLE WinRT allocation failed"); state->shutdown(); return nullptr; } return new(memory) ble_backend{state};