Fixed a few issues found during test

PiperOrigin-RevId: 457604581
This commit is contained in:
guogang
2022-06-27 17:30:37 -07:00
committed by Copybara-Service
parent 87c76ea235
commit 698b04184a
5 changed files with 248 additions and 250 deletions
@@ -14,6 +14,7 @@
#include "internal/platform/implementation/windows/ble_medium.h"
#include <chrono> // NOLINT
#include <future> // NOLINT
#include <memory>
#include <string>
@@ -21,6 +22,8 @@
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/windows/ble_peripheral.h"
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
#include "internal/platform/implementation/windows/utils.h"
@@ -29,6 +32,7 @@
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.Storage.Streams.h"
#include "winrt/base.h"
namespace location {
namespace nearby {
@@ -110,6 +114,10 @@ using ::winrt::Windows::Storage::Streams::DataReader;
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.datawriter?view=winrt-22621
using ::winrt::Windows::Storage::Streams::DataWriter;
// Represents a time interval as a signed 64-bit integer value.
// https://docs.microsoft.com/en-us/uwp/api/windows.foundation.timespan?view=winrt-22621
using ::winrt::Windows::Foundation::TimeSpan;
template <typename T>
using IVector = winrt::Windows::Foundation::Collections::IVector<T>;
@@ -132,6 +140,12 @@ bool BleMedium::StartAdvertising(
<< " fast advertisement service uuid= 0x"
<< absl::BytesToHexString(fast_advertisement_service_uuid);
if (is_publisher_started_) {
NEARBY_LOGS(WARNING)
<< "BLE cannot start to advertise again when it is running.";
return false;
}
DataWriter data_writer;
// TODO(b/234229562): Add parsing logic for fast_advertisement_service_uuid
@@ -160,58 +174,47 @@ bool BleMedium::StartAdvertising(
if (fast_advertisement_service_uuid.empty()) {
publisher_ = BluetoothLEAdvertisementPublisher(advertisement_);
publisher_.UseExtendedAdvertisement(true);
publisher_token_ =
publisher_.StatusChanged({this, &BleMedium::PublisherHandler});
publisher_started_promise_ = std::promise<PublisherState>();
std::future<PublisherState> publisher_state_future =
publisher_started_promise_.get_future();
publisher_.Start();
return publisher_state_future.get() == PublisherState::kStarted;
} else {
// Extended Advertisement not supported, must make sure advertisement_bytes
// is less than 27 bytes
if (advertisement_bytes.size() <= 27) {
publisher_ = BluetoothLEAdvertisementPublisher(advertisement_);
publisher_.UseExtendedAdvertisement(false);
publisher_token_ =
publisher_.StatusChanged({this, &BleMedium::PublisherHandler});
publisher_started_promise_ = std::promise<PublisherState>();
std::future<PublisherState> publisher_state_future =
publisher_started_promise_.get_future();
publisher_.Start();
return publisher_state_future.get() == PublisherState::kStarted;
} else {
// otherwise no-op
NEARBY_LOGS(INFO) << "Everyone Mode unavailable for hardware that does "
"not support Extended Advertising.";
publisher_ = nullptr;
return false;
}
}
publisher_token_ =
publisher_.StatusChanged({this, &BleMedium::PublisherHandler});
publisher_.Start();
is_publisher_started_ = true;
NEARBY_LOGS(INFO) << "Windows Ble StartAdvertising started.";
return true;
}
bool BleMedium::StopAdvertising(const std::string& service_id) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "Windows Ble StopAdvertising: service_id=" << service_id;
publisher_stopped_promise_ = std::promise<PublisherState>();
std::future<PublisherState> publisher_state_future =
publisher_stopped_promise_.get_future();
if (!is_publisher_started_) {
NEARBY_LOGS(WARNING) << "BLE advertising is not running.";
return false;
}
publisher_.Stop();
return publisher_state_future.get() == PublisherState::kStopped;
// Don't need to wait for the status becomes to `Stopped`. If application
// starts to scanning immediately, the scanning still needs to wait the
// stopping to finish.
is_publisher_started_ = false;
return true;
}
bool BleMedium::StartScanning(
@@ -222,34 +225,34 @@ bool BleMedium::StartScanning(
NEARBY_LOGS(INFO) << "Windows Ble StartScanning: service_id=" << service_id;
if (is_watcher_started_) {
NEARBY_LOGS(WARNING)
<< "BLE cannot start to scan again when it is running.";
return false;
}
service_id_ = service_id;
advertisement_received_callback_ = std::move(callback);
watcher_ = BluetoothLEAdvertisementWatcher();
watcher_token_ = watcher_.Stopped({this, &BleMedium::WatcherHandler});
advertisement_received_token_ =
watcher_.Received({this, &BleMedium::AdvertisementReceivedHandler});
is_watcher_started_ = false;
watcher_started_promise_ = std::promise<WatcherState>();
std::future<WatcherState> watcher_state_future =
watcher_started_promise_.get_future();
if (adapter_->IsExtendedAdvertisingSupported()) {
watcher_.AllowExtendedAdvertisements(true);
}
// Active mode indicates that scan request packets will be sent to query for
// Scan Response
watcher_.ScanningMode(BluetoothLEScanningMode::Active);
::winrt::Windows::Devices::Bluetooth::BluetoothSignalStrengthFilter filter;
filter.SamplingInterval(TimeSpan(std::chrono::seconds(2)));
watcher_.SignalStrengthFilter(filter);
watcher_.Start();
while (!is_watcher_started_) {
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Created) {
watcher_started_promise_.set_value(WatcherState::kStarted);
return true;
}
}
is_watcher_started_ = true;
NEARBY_LOGS(INFO) << "Windows Ble StartScanning started.";
return true;
}
@@ -257,23 +260,21 @@ bool BleMedium::StopScanning(const std::string& service_id) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "Windows Ble StopScanning: service_id=" << service_id;
is_watcher_stopped_ = false;
watcher_stopped_promise_ = std::promise<WatcherState>();
std::future<WatcherState> watcher_state_future =
watcher_stopped_promise_.get_future();
if (!is_watcher_started_) {
NEARBY_LOGS(WARNING) << "BLE scanning is not running.";
return false;
}
watcher_.Stop();
while (!is_watcher_stopped_) {
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped) {
watcher_stopped_promise_.set_value(WatcherState::kStopped);
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
return true;
}
}
// Don't need to wait for the status becomes to `Stopped`. If application
// starts to scanning immediately, the scanning still needs to wait the
// stopping to finish.
is_watcher_started_ = false;
NEARBY_LOGS(ERROR)
<< "Windows Ble stoped scanning successfully for service_id="
<< service_id;
return true;
}
@@ -309,13 +310,22 @@ std::unique_ptr<api::BleSocket> BleMedium::Connect(
void BleMedium::PublisherHandler(
BluetoothLEAdvertisementPublisher publisher,
BluetoothLEAdvertisementPublisherStatusChangedEventArgs args) {
// This method is called when publisher's status is changed.
switch (args.Status()) {
case BluetoothLEAdvertisementPublisherStatus::Created:
NEARBY_LOGS(INFO) << "Nearby BLE Medium created to advertise.";
return;
case BluetoothLEAdvertisementPublisherStatus::Started:
publisher_started_promise_.set_value(PublisherState::kStarted);
break;
NEARBY_LOGS(INFO) << "Nearby BLE Medium started to advertise.";
return;
case BluetoothLEAdvertisementPublisherStatus::Stopping:
NEARBY_LOGS(INFO) << "Nearby BLE Medium is stopping.";
return;
case BluetoothLEAdvertisementPublisherStatus::Waiting:
NEARBY_LOGS(INFO) << "Nearby BLE Medium is waiting.";
return;
case BluetoothLEAdvertisementPublisherStatus::Stopped:
publisher_stopped_promise_.set_value(PublisherState::kStopped);
publisher_.StatusChanged(publisher_token_);
NEARBY_LOGS(INFO) << "Nearby BLE Medium stopped to advertise.";
break;
case BluetoothLEAdvertisementPublisherStatus::Aborted:
switch (args.Error()) {
@@ -325,181 +335,137 @@ void BleMedium::PublisherHandler(
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium start advertising operation was "
"successfully completed or serviced.";
publisher_started_promise_.set_value(PublisherState::kStarted);
}
if (publisher_.Status() ==
BluetoothLEAdvertisementPublisherStatus::Stopped) {
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium stop advertising operation was "
"successfully completed or serviced.";
publisher_stopped_promise_.set_value(PublisherState::kStopped);
publisher_.StatusChanged(publisher_token_);
} else {
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"unknown errors.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
}
break;
case BluetoothError::RadioNotAvailable:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"radio not available.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::ResourceInUse:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium advertising failed due to resource in use.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::DeviceNotConnected:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"remote device is not connected.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::DisabledByPolicy:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"disabled by policy.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::DisabledByUser:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"disabled by user.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::NotSupported:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"hardware not supported.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::TransportNotSupported:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"transport not supported.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::ConsentRequired:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to "
"consent required.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
case BluetoothError::OtherError:
default:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium advertising failed due to unknown errors.";
publisher_started_promise_.set_value(PublisherState::kError);
publisher_stopped_promise_.set_value(PublisherState::kError);
break;
}
break;
default:
break;
}
// The publisher is stopped. Clean up the running publisher
{
absl::MutexLock lock(&mutex_);
if (publisher_ != nullptr) {
NEARBY_LOGS(ERROR) << "Nearby BLE Medium cleaned the publisher.";
publisher_.StatusChanged(publisher_token_);
publisher_ = nullptr;
is_publisher_started_ = false;
}
}
}
void BleMedium::WatcherHandler(
BluetoothLEAdvertisementWatcher watcher,
BluetoothLEAdvertisementWatcherStoppedEventArgs args) {
// This method is called when watcher stopped. Args give more detailed
// information on the reason.
switch (args.Error()) {
case BluetoothError::Success:
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Started) {
NEARBY_LOGS(ERROR) << "Nearby BLE Medium start scanning operation was "
"successfully completed or serviced.";
watcher_started_promise_.set_value(WatcherState::kStarted);
is_watcher_started_ = true;
}
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped) {
NEARBY_LOGS(ERROR) << "Nearby BLE Medium stop scanning operation was "
"successfully completed or serviced.";
watcher_stopped_promise_.set_value(WatcherState::kStopped);
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
is_watcher_stopped_ = true;
} else {
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to unknown errors.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
}
NEARBY_LOGS(ERROR) << "Nearby BLE Medium stoped to scan successfully.";
break;
case BluetoothError::RadioNotAvailable:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to radio not available.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to radio not available.";
break;
case BluetoothError::ResourceInUse:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to resource in use.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to resource in use.";
break;
case BluetoothError::DeviceNotConnected:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium scanning failed due to "
NEARBY_LOGS(ERROR) << "Nearby BLE Medium stoped to scan due to "
"remote device is not connected.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
break;
case BluetoothError::DisabledByPolicy:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to disabled by policy.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to disabled by policy.";
break;
case BluetoothError::DisabledByUser:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to disabled by user.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to disabled by user.";
break;
case BluetoothError::NotSupported:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to hardware not supported.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to hardware not supported.";
break;
case BluetoothError::TransportNotSupported:
NEARBY_LOGS(ERROR) << "Nearby BLE Medium scanning failed due to "
NEARBY_LOGS(ERROR) << "Nearby BLE Medium stoped to scan due to "
"transport not supported.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
break;
case BluetoothError::ConsentRequired:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to consent required.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to consent required.";
break;
case BluetoothError::OtherError:
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to unknown errors.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
<< "Nearby BLE Medium stoped to scan due to unknown errors.";
break;
default:
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Started) {
watcher_started_promise_.set_value(WatcherState::kStarted);
is_watcher_started_ = true;
}
if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped) {
watcher_stopped_promise_.set_value(WatcherState::kStopped);
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
is_watcher_stopped_ = true;
} else {
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium scanning failed due to unknown errors.";
watcher_started_promise_.set_value(WatcherState::kError);
watcher_stopped_promise_.set_value(WatcherState::kError);
}
NEARBY_LOGS(ERROR)
<< "Nearby BLE Medium stoped to scan due to unknown errors.";
break;
}
// No matter the reason, should clean up the watcher if it is not empty.
// The BLE V1 interface doesn't have API to return the error to upper layer.
{
absl::MutexLock lock(&mutex_);
if (watcher_ != nullptr) {
NEARBY_LOGS(ERROR) << "Nearby BLE Medium cleaned the watcher.";
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
watcher_ = nullptr;
is_watcher_started_ = false;
}
}
}
void BleMedium::AdvertisementReceivedHandler(
@@ -508,7 +474,6 @@ void BleMedium::AdvertisementReceivedHandler(
// Handle all BLE advertisements and determine whether the BLE Medium
// Advertisement Scan Response packet (containing Copresence UUID 0xFEF3 in
// 0x16 Service Data) has been received in the handler
absl::MutexLock lock(&peripheral_map_mutex_);
BluetoothLEAdvertisement advertisement = args.Advertisement();
for (BluetoothLEAdvertisementDataSection service_data :
@@ -543,16 +508,12 @@ void BleMedium::AdvertisementReceivedHandler(
std::make_unique<BlePeripheral>();
peripheral->SetName(peripheral_name);
peripheral->SetAdvertisementBytes(advertisement_data);
if (peripheral_map_.contains(peripheral_name)) {
if (peripheral_map_[peripheral_name]->GetAdvertisementBytes(
service_id_) == advertisement_data) {
return;
}
}
BlePeripheral* peripheral_ptr = peripheral.get();
peripheral_map_.emplace(peripheral_name, std::move(peripheral));
{
absl::MutexLock lock(&peripheral_map_mutex_);
peripheral_map_[peripheral_name] = std::move(peripheral);
}
// Received Fast Advertisement packet
if (unconsumed_buffer_length <= 27) {
@@ -84,9 +84,20 @@ class BleMedium : public api::BleMedium {
CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_);
private:
enum class PublisherState { kStarted = 0, kStopped, kError };
enum class WatcherState { kStarted = 0, kStopped, kError };
void PublisherHandler(
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisher publisher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisherStatusChangedEventArgs args);
void AdvertisementReceivedHandler(
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs args);
void WatcherHandler(::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStoppedEventArgs args);
absl::Mutex mutex_;
BluetoothAdapter* adapter_;
@@ -103,37 +114,17 @@ class BleMedium : public api::BleMedium {
// WinRT objects
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisher publisher_;
BluetoothLEAdvertisementPublisher publisher_ = nullptr;
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher_;
BluetoothLEAdvertisementWatcher watcher_ = nullptr;
::winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement
advertisement_;
void PublisherHandler(
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisher publisher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisherStatusChangedEventArgs args);
void AdvertisementReceivedHandler(
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs args);
void WatcherHandler(::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStoppedEventArgs args);
bool is_publisher_started_ = false;
bool is_watcher_started_ = false;
::winrt::event_token publisher_token_;
std::promise<PublisherState> publisher_started_promise_;
std::promise<PublisherState> publisher_stopped_promise_;
::winrt::event_token watcher_token_;
std::promise<WatcherState> watcher_started_promise_;
std::promise<WatcherState> watcher_stopped_promise_;
bool is_watcher_started_ = false;
bool is_watcher_stopped_ = false;
::winrt::event_token advertisement_received_token_;
};
@@ -69,6 +69,7 @@ void BluetoothClassicMedium::OnScanModeChanged(
bool BluetoothClassicMedium::StartDiscovery(
BluetoothClassicMedium::DiscoveryCallback discovery_callback) {
NEARBY_LOGS(INFO) << "StartDiscovery is called.";
EnterCriticalSection(&critical_section_);
bool result = false;
@@ -84,6 +85,7 @@ bool BluetoothClassicMedium::StartDiscovery(
}
bool BluetoothClassicMedium::StopDiscovery() {
NEARBY_LOGS(INFO) << "StopDiscovery is called.";
EnterCriticalSection(&critical_section_);
bool result = false;
@@ -136,6 +138,7 @@ void BluetoothClassicMedium::InitializeDeviceWatcher() {
std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
api::BluetoothDevice& remote_device, const std::string& service_uuid,
CancellationFlag* cancellation_flag) {
NEARBY_LOGS(INFO) << "ConnectToService is called.";
if (service_uuid.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service_uuid not specified.";
return nullptr;
@@ -354,6 +357,8 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) {
std::unique_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
NEARBY_LOGS(INFO) << "ListenForService is called with service name: "
<< service_name << ".";
if (service_uuid.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service_uuid was empty.";
return nullptr;
@@ -17,12 +17,14 @@
// Standard C/C++ headers
#include <functional>
#include <optional>
#include <string>
// Nearby connections headers
#include "internal/platform/implementation/wifi_hotspot.h"
// WinRT headers
#include "absl/types/optional.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFi.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFiDirect.h"
@@ -71,8 +73,9 @@ using ::winrt::Windows::Security::Cryptography::CryptographicBuffer;
using ::winrt::Windows::Networking::HostName;
using ::winrt::Windows::Networking::HostNameType;
using ::winrt::Windows::Networking::Connectivity::NetworkInformation;
using ::winrt::Windows::Networking::Connectivity::ConnectionProfile;
using ::winrt::Windows::Networking::Connectivity::ConnectionProfileDeleteStatus;
using ::winrt::Windows::Networking::Connectivity::NetworkInformation;
using ::winrt::Windows::Networking::Sockets::StreamSocket;
using ::winrt::Windows::Networking::Sockets::StreamSocketListener;
using ::winrt::Windows::Networking::Sockets::
@@ -244,13 +247,16 @@ class WifiHotspotMedium : public api::WifiHotspotMedium {
}
private:
enum Value:char {
enum Value : char {
kMediumStatusIdle = 0,
kMediumStatusAccepting = (1 << 0),
kMediumStatusBeaconing = (1 << 1),
kMediumStatusConnected = (1 << 2),
};
// Implemented the disconnection to WiFi hotspot, and used to avoid deadlock.
bool InternalDisconnectWifiHotspot();
bool IsIdle() { return medium_status_ == kMediumStatusIdle; }
// Advertiser is accepting connection on server socket
bool IsAccepting() { return (medium_status_ & kMediumStatusAccepting) != 0; }
@@ -280,15 +286,9 @@ class WifiHotspotMedium : public api::WifiHotspotMedium {
// Protects to access some members
absl::Mutex mutex_;
// If the WiFi Adaptor supports to start a Hotspot interface.
bool hotspot_interface_valid_;
// Medium Status
int medium_status_ = kMediumStatusIdle;
// Hotspot profiles that Discoverer has connected in the whole session;
std::vector<ConnectionProfile> hotspot_profiles_ ABSL_GUARDED_BY(mutex_);
// Keep the server socket listener pointer
WifiHotspotServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr;
};
@@ -17,25 +17,20 @@
// Nearby connections headers
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/logging.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/logging.h"
namespace location {
namespace nearby {
namespace windows {
namespace {
constexpr int kMaxRetries = 3;
constexpr int kRetryIntervalMilliSeconds = 300;
constexpr int kMaxScans = 2;
constexpr int kMaxRetries = 3;
constexpr int kRetryIntervalMilliSeconds = 300;
constexpr int kMaxScans = 2;
} // namespace
WifiHotspotMedium::WifiHotspotMedium() {
HotspotCredentials hotspot_credentials_;
hotspot_interface_valid_ = StartWifiHotspot(&hotspot_credentials_);
if (hotspot_interface_valid_)
StopWifiHotspot();
}
WifiHotspotMedium::WifiHotspotMedium() {}
WifiHotspotMedium::~WifiHotspotMedium() {
StopWifiHotspot();
@@ -43,14 +38,15 @@ WifiHotspotMedium::~WifiHotspotMedium() {
}
bool WifiHotspotMedium::IsInterfaceValid() const {
return hotspot_interface_valid_;
// Windows 10 starts to support WiFi direct feature, so don't need to check
// feature by OS due to targeting OS version is at leat Windows 10.
NEARBY_LOGS(ERROR) << "WiFi hotspot: valid interface found.";
return true;
}
std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag* cancellation_flag) {
if (ip_address.empty() || port == 0) {
NEARBY_LOGS(ERROR) << "no valid service address and port to connect: "
<< "ip_address = " << ip_address << ", port = " << port;
@@ -85,10 +81,11 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
cancellation_flag, [socket]() { socket.CancelIOAsync().get(); });
}
// Try connecting to the service up to kMaxRetries. Because it may fail fisrt
// time if DHCP procedure is not finished yet.
// Try connecting to the service up to kMaxRetries, because it may fail
// first time if DHCP procedure is not finished yet.
for (int i = 0; i < kMaxRetries; i++) {
try {
Sleep(kRetryIntervalMilliSeconds);
socket.ConnectAsync(host_name, service_name).get();
auto wifi_hotspot_socket = std::make_unique<WifiHotspotSocket>(socket);
@@ -98,8 +95,7 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
return wifi_hotspot_socket;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to connect remote service " << ipv4_address
<< ":" << port << " for the " << i+1 << " time";
Sleep(kRetryIntervalMilliSeconds);
<< ":" << port << " for the " << i + 1 << " time";
}
}
return nullptr;
@@ -152,16 +148,11 @@ bool WifiHotspotMedium::StartWifiHotspot(
publisher_status_changed_token_ =
publisher_.StatusChanged({this, &WifiHotspotMedium::OnStatusChanged});
listener_ = WiFiDirectConnectionListener();
try {
listener_.ConnectionRequested(
{this, &WifiHotspotMedium::OnConnectionRequested});
} catch (winrt::hresult_error const& ex) {
NEARBY_LOGS(ERROR) << __func__ << ": winrt exception: " << ex.code() << ": "
<< winrt::to_string(ex.message());
}
connection_requested_token_ = listener_.ConnectionRequested(
{this, &WifiHotspotMedium::OnConnectionRequested});
// Normal mode: The device is highly discoverable so long as the app is in the
// foreground.
// Normal mode: The device is highly discoverable so long as the app is in
// the foreground.
publisher_.Advertisement().ListenStateDiscoverability(
WiFiDirectAdvertisementListenStateDiscoverability::Normal);
// Enbale Autonomous GO mode
@@ -189,6 +180,8 @@ bool WifiHotspotMedium::StartWifiHotspot(
// Clean up when fail
NEARBY_LOGS(ERROR) << "Windows WiFi Hotspot fails to start";
publisher_.StatusChanged(publisher_status_changed_token_);
listener_.ConnectionRequested(connection_requested_token_);
listener_ = nullptr;
publisher_ = nullptr;
return false;
@@ -207,8 +200,11 @@ bool WifiHotspotMedium::StopWifiHotspot() {
publisher_.Stop();
publisher_.StatusChanged(publisher_status_changed_token_);
listener_.ConnectionRequested(connection_requested_token_);
publisher_ = nullptr;
listener_ = nullptr;
NEARBY_LOGS(INFO) << "succeeded to stop WiFi Hotspot";
}
medium_status_ &= (~kMediumStatusBeaconing);
return true;
}
@@ -228,10 +224,31 @@ fire_and_forget WifiHotspotMedium::OnStatusChanged(
.Passphrase()
.Password());
}
return winrt::fire_and_forget();
} else if (event.Status() ==
WiFiDirectAdvertisementPublisherStatus::Created) {
NEARBY_LOGS(INFO) << "Receive WiFi direct/SoftAP Created event.";
return winrt::fire_and_forget();
} else if (event.Status() ==
WiFiDirectAdvertisementPublisherStatus::Stopped) {
NEARBY_LOGS(INFO) << "Receive WiFi direct/SoftAP Stopped event.";
} else if (event.Status() ==
WiFiDirectAdvertisementPublisherStatus::Aborted) {
NEARBY_LOGS(INFO) << "Receive WiFi direct/SoftAP Aborted event.";
}
if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Stopped) {
NEARBY_LOGS(INFO) << "Receive WiFi direct/SoftAP stop event";
// Publisher is stopped. Need to clean up the publisher.
{
absl::MutexLock lock(&mutex_);
if (publisher_ != nullptr) {
NEARBY_LOGS(ERROR) << "Windows WiFi Hotspot cleanup.";
publisher_.StatusChanged(publisher_status_changed_token_);
listener_.ConnectionRequested(connection_requested_token_);
listener_ = nullptr;
publisher_ = nullptr;
}
}
return winrt::fire_and_forget();
}
@@ -259,7 +276,7 @@ bool WifiHotspotMedium::ConnectWifiHotspot(
if (IsConnected()) {
NEARBY_LOGS(WARNING) << "Already connected to AP, disconnect first.";
DisconnectWifiHotspot();
InternalDisconnectWifiHotspot();
}
auto access = WiFiAdapter::RequestAccessAsync().get();
@@ -269,12 +286,12 @@ bool WifiHotspotMedium::ConnectWifiHotspot(
return false;
}
auto adaptors = WiFiAdapter::FindAllAdaptersAsync().get();
if (adaptors.Size() < 1) {
NEARBY_LOGS(WARNING) << "No WiFi Adaptor found.";
auto adapters = WiFiAdapter::FindAllAdaptersAsync().get();
if (adapters.Size() < 1) {
NEARBY_LOGS(WARNING) << "No WiFi Adapter found.";
return false;
}
wifi_adapter_ = adaptors.GetAt(0);
wifi_adapter_ = adapters.GetAt(0);
// SoftAP is an abbreviation for "software enabled access point".
WiFiAvailableNetwork nearby_softap{nullptr};
@@ -283,6 +300,8 @@ bool WifiHotspotMedium::ConnectWifiHotspot(
// First time scan may not find our target hotspot, try 2 more times can
// almost guarantee to find the Hotspot
wifi_adapter_.ScanAsync().get();
for (int i = 0; i < kMaxScans; i++) {
for (const auto& network :
wifi_adapter_.NetworkReport().AvailableNetworks()) {
@@ -311,54 +330,76 @@ bool WifiHotspotMedium::ConnectWifiHotspot(
.ConnectAsync(nearby_softap, WiFiReconnectionKind::Manual, creds)
.get();
if (connect_result.ConnectionStatus() != WiFiConnectionStatus::Success) {
if (connect_result == nullptr ||
connect_result.ConnectionStatus() != WiFiConnectionStatus::Success) {
NEARBY_LOGS(INFO) << "Connecting failed with reason: "
<< static_cast<int>(connect_result.ConnectionStatus());
return false;
}
std::string last_ssid = hotspot_credentials_->GetSSID();
NEARBY_LOGS(INFO) << "Connected to: " << last_ssid;
medium_status_ |= kMediumStatusConnected;
Sleep(50);
auto profile =
wifi_adapter_.NetworkAdapter().GetConnectedProfileAsync().get();
if (profile.IsWlanConnectionProfile()) {
if (winrt::to_string(
profile.WlanConnectionProfileDetails().GetConnectedSsid()) ==
last_ssid) {
hotspot_profiles_.push_back(profile);
NEARBY_LOGS(INFO) << "Save WiFi profile with SSID: " << last_ssid;
}
}
NEARBY_LOGS(INFO) << "Connected to hotspot: " << last_ssid;
return true;
}
bool WifiHotspotMedium::DisconnectWifiHotspot() {
absl::MutexLock lock(&mutex_);
return InternalDisconnectWifiHotspot();
}
if (!hotspot_profiles_.empty()) {
for (auto profile : hotspot_profiles_) {
NEARBY_LOGS(INFO)
<< "Delete WiFi profile with SSID: "
<< winrt::to_string(
profile.WlanConnectionProfileDetails().GetConnectedSsid())
<< ", result: " << static_cast<int>(profile.TryDeleteAsync().get());
}
hotspot_profiles_.clear();
}
bool WifiHotspotMedium::InternalDisconnectWifiHotspot() {
if (!IsConnected()) {
NEARBY_LOGS(WARNING)
<< "Cannot diconnect SoftAP because it is not connected.";
<< "Cannot disconnect SoftAP because it is not connected.";
return true;
}
if (wifi_adapter_) {
// Gets connected WiFi profile.
auto profile =
wifi_adapter_.NetworkAdapter().GetConnectedProfileAsync().get();
// Disconnect to the WiFi connection through the WiFi adapter.
wifi_adapter_.Disconnect();
NEARBY_LOGS(INFO) << "Disconnect softAP.";
wifi_adapter_ = {nullptr};
wifi_adapter_ = nullptr;
// Try to remove the WiFi profile
if (profile != nullptr && profile.CanDelete() &&
profile.IsWlanConnectionProfile()) {
std::string ssid = winrt::to_string(
profile.WlanConnectionProfileDetails().GetConnectedSsid());
auto profile_delete_status = profile.TryDeleteAsync().get();
switch (profile_delete_status) {
case ConnectionProfileDeleteStatus::Success:
NEARBY_LOGS(INFO)
<< "WiFi profile with SSID:" << ssid << " is deleted.";
break;
case ConnectionProfileDeleteStatus::DeniedBySystem:
NEARBY_LOGS(ERROR)
<< "Failed to delete WiFi profile with SSID:" << ssid
<< " due to denied by system.";
break;
case ConnectionProfileDeleteStatus::DeniedByUser:
NEARBY_LOGS(ERROR)
<< "Failed to delete WiFi profile with SSID:" << ssid
<< " due to denied by user.";
break;
case ConnectionProfileDeleteStatus::UnknownError:
NEARBY_LOGS(ERROR)
<< "Failed to delete WiFi profile with SSID:" << ssid
<< " due to unknonw error.";
break;
default:
break;
}
}
NEARBY_LOGS(INFO) << "Disconnected to SoftAP.";
}
medium_status_ &= (~kMediumStatusConnected);
return true;
}