//********************************************************* // // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the MIT License (MIT). // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #include "stdafx.h" #include "WlanHostedNetworkWinRT.h" using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Devices::Enumeration; using namespace ABI::Windows::Devices::WiFiDirect; using namespace ABI::Windows::Security::Credentials; using namespace ABI::Windows::Networking; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectConnectionListener_Windows__CDevices__CWiFiDirect__CWiFiDirectConnectionRequestedEventArgs ConnectionRequestedHandler; typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectAdvertisementPublisher_Windows__CDevices__CWiFiDirect__CWiFiDirectAdvertisementPublisherStatusChangedEventArgs StatusChangedHandler; typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectDevice_IInspectable ConnectionStatusChangedHandler; typedef __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CWiFiDirect__CWiFiDirectDevice FromIdAsyncHandler; typedef __FIVectorView_1_Windows__CNetworking__CEndpointPair EndpointPairCollection; WlanHostedNetworkHelper::WlanHostedNetworkHelper() : _ssidProvided(false), _passphraseProvided(false), _listener(nullptr), _autoAccept(true) { } WlanHostedNetworkHelper::~WlanHostedNetworkHelper() { if (_publisher.Get() != nullptr) { _publisher->Stop(); } Reset(); } void WlanHostedNetworkHelper::Start() { HRESULT hr = S_OK; // Clean up old state Reset(); // Create WiFiDirectAdvertisementPublisher hr = Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectAdvertisementPublisher).Get(), &_publisher); if (FAILED(hr)) { throw WlanHostedNetworkException("ActivateInstance for WiFiDirectAdvertisementPublisher failed", hr); } // Add event handler for advertisement StatusChanged hr = _publisher->add_StatusChanged( Callback([this](IWiFiDirectAdvertisementPublisher* sender, IWiFiDirectAdvertisementPublisherStatusChangedEventArgs* args) -> HRESULT { HRESULT hr = S_OK; WiFiDirectAdvertisementPublisherStatus status; WiFiDirectError error; try { hr = args->get_Status(&status); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Status for AdvertisementPubliserStatusChangedEventArgs failed", hr); } switch (status) { case WiFiDirectAdvertisementPublisherStatus_Started: { // Begin listening for connections and notify listener that the advertisement started StartListener(); if (_listener != nullptr) { _listener->OnAdvertisementStarted(); } break; } case WiFiDirectAdvertisementPublisherStatus_Aborted: { // Check error and notify listener that the advertisement stopped hr = args->get_Error(&error); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Error for AdvertisementPubliserStatusChangedEventArgs failed", hr); } if (_listener != nullptr) { std::wstring message; switch (error) { case WiFiDirectError_RadioNotAvailable: message = L"Advertisement aborted, Wi-Fi radio is turned off"; break; case WiFiDirectError_ResourceInUse: message = L"Advertisement aborted, Resource In Use"; break; default: message = L"Advertisement aborted, unknown reason"; break; } _listener->OnAdvertisementAborted(message); } break; } case WiFiDirectAdvertisementPublisherStatus_Stopped: { // Notify listener that the advertisement is stopped if (_listener != nullptr) { _listener->OnAdvertisementStopped(L"Advertisement stopped"); } break; } } } catch (WlanHostedNetworkException& e) { if (_listener != nullptr) { std::wostringstream ss; ss << e.what() << ": " << e.GetErrorCode(); _listener->OnAsyncException(ss.str()); } return e.GetErrorCode(); } return hr; }).Get(), &_statusChangedToken); // Set Advertisement required settings hr = _publisher->get_Advertisement(_advertisement.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get advertisement for WiFiDirectAdvertisementPublisher failed", hr); } // Must set the autonomous group owner (GO) enabled flag // Legacy Wi-Fi Direct advertisement uses a Wi-Fi Direct GO to act as an access point to legacy settings hr = _advertisement->put_IsAutonomousGroupOwnerEnabled(true); if (FAILED(hr)) { throw WlanHostedNetworkException("Set is autonomous group owner for WiFiDirectAdvertisement failed", hr); } hr = _advertisement->get_LegacySettings(_legacySettings.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get legacy settings for WiFiDirectAdvertisement failed", hr); } // Must enable legacy settings so that non-Wi-Fi Direct peers can connect in legacy mode hr = _legacySettings->put_IsEnabled(true); if (FAILED(hr)) { throw WlanHostedNetworkException("Set is enabled for WiFiDirectLegacySettings failed", hr); } HString hstrSSID; HString hstrPassphrase; // Either specify an SSID, or read the randomly generated one if (_ssidProvided) { hr = hstrSSID.Set(_ssid.c_str()); if (FAILED(hr)) { throw WlanHostedNetworkException("Failed to create HSTRING representation for SSID", hr); } hr = _legacySettings->put_Ssid(hstrSSID.Get()); if (FAILED(hr)) { throw WlanHostedNetworkException("Set SSID for WiFiDirectLegacySettings failed", hr); } } else { hr = _legacySettings->get_Ssid(hstrSSID.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get SSID for WiFiDirectLegacySettings failed", hr); } _ssid = hstrSSID.GetRawBuffer(nullptr); } // Either specify a passphrase, or read the randomly generated one ComPtr passwordCredential; hr = _legacySettings->get_Passphrase(passwordCredential.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Passphrase for WiFiDirectLegacySettings failed", hr); } if (_passphraseProvided) { hr = hstrPassphrase.Set(_passphrase.c_str()); if (FAILED(hr)) { throw WlanHostedNetworkException("Failed to create HSTRING representation for Passphrase", hr); } hr = passwordCredential->put_Password(hstrPassphrase.Get()); if (FAILED(hr)) { throw WlanHostedNetworkException("Set Passphrase for WiFiDirectLegacySettings failed", hr); } } else { hr = passwordCredential->get_Password(hstrPassphrase.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Passphrase for WiFiDirectLegacySettings failed", hr); } _passphrase = hstrPassphrase.GetRawBuffer(nullptr); } // Start the advertisement, which will create an access point that other peers can connect to hr = _publisher->Start(); if (FAILED(hr)) { throw WlanHostedNetworkException("Start WiFiDirectAdvertisementPublisher failed", hr); } } void WlanHostedNetworkHelper::Stop() { HRESULT hr = S_OK; // Call stop on the publisher and expect the status changed callback if (_publisher.Get() != nullptr) { hr = _publisher->Stop(); if (FAILED(hr)) { throw WlanHostedNetworkException("Stop WiFiDirectAdvertisementPublisher failed", hr); } } else { throw WlanHostedNetworkException("WiFiDirectAdvertisementPublisher is not running", hr); } } void WlanHostedNetworkHelper::StartListener() { HRESULT hr = S_OK; // Create WiFiDirectConnectionListener hr = Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectConnectionListener).Get(), &_connectionListener); if (FAILED(hr)) { throw WlanHostedNetworkException("ActivateInstance for WiFiDirectConnectionListener failed", hr); } hr = _connectionListener->add_ConnectionRequested( Callback([this](IWiFiDirectConnectionListener* sender, IWiFiDirectConnectionRequestedEventArgs* args) -> HRESULT { HRESULT hr = S_OK; ComPtr request; ComPtr deviceInformation; HString deviceId; ComPtr wfdStatics; if (_listener != nullptr) { _listener->LogMessage(L"Connection Requested..."); } bool acceptConnection = true; if (!_autoAccept && _prompt != nullptr) { acceptConnection = _prompt->AcceptIncommingConnection(); } try { hr = args->GetConnectionRequest(request.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get connection request for ConnectionRequestedEventArgs failed", hr); } if (acceptConnection) { hr = request->get_DeviceInformation(deviceInformation.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get device information for ConnectionRequest failed", hr); } hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectDevice).Get(), &wfdStatics); if (FAILED(hr)) { throw WlanHostedNetworkException("GetActivationFactory for WiFiDirectDevice failed", hr); } hr = deviceInformation->get_Id(deviceId.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get ID for DeviceInformation failed", hr); } ComPtr> asyncAction; hr = wfdStatics->FromIdAsync(deviceId.Get(), &asyncAction); if (FAILED(hr)) { throw WlanHostedNetworkException("From ID Async for WiFiDirectDevice failed", hr); } hr = asyncAction->put_Completed(Callback([this](IAsyncOperation* pHandler, AsyncStatus status) -> HRESULT { HRESULT hr = S_OK; ComPtr wfdDevice; ComPtr endpointPairs; ComPtr endpointPair; ComPtr remoteHostName; HString remoteHostNameDisplay; HString deviceId; try { if (status == AsyncStatus::Completed) { // Get the WiFiDirectDevice object hr = pHandler->GetResults(wfdDevice.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Put Completed for FromIDAsync operation failed", hr); } // Now retrieve the endpoint pairs, which includes the IP address assigned to the peer hr = wfdDevice->GetConnectionEndpointPairs(endpointPairs.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get EndpointPairs for WiFiDirectDevice failed", hr); } hr = endpointPairs->GetAt(0, endpointPair.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get first EndpointPair in collection failed", hr); } hr = endpointPair->get_RemoteHostName(remoteHostName.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Remote HostName for EndpointPair failed", hr); } hr = remoteHostName->get_DisplayName(remoteHostNameDisplay.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Display Name for Remote HostName failed", hr); } // Add handler for connection status changed EventRegistrationToken statusChangedToken; hr = wfdDevice->add_ConnectionStatusChanged(Callback([this](IWiFiDirectDevice* sender, IInspectable*) -> HRESULT { WiFiDirectConnectionStatus status; HString deviceId; HRESULT hr = S_OK; try { hr = sender->get_ConnectionStatus(&status); if (FAILED(hr)) { throw WlanHostedNetworkException("Get connection status for peer failed", hr); } switch (status) { case WiFiDirectConnectionStatus_Connected: // NO-OP break; case WiFiDirectConnectionStatus_Disconnected: // Clean-up state hr = sender->get_DeviceId(deviceId.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Device ID failed", hr); } auto itDevice = _connectedDevices.find(deviceId.GetRawBuffer(nullptr)); auto itToken = _connectedDeviceStatusChangedTokens.find(deviceId.GetRawBuffer(nullptr)); if (itToken != _connectedDeviceStatusChangedTokens.end()) { if (itDevice != _connectedDevices.end()) { itDevice->second->remove_ConnectionStatusChanged(itToken->second); } _connectedDeviceStatusChangedTokens.erase(itToken); } if (itDevice != _connectedDevices.end()) { _connectedDevices.erase(itDevice); } // Notify listener of disconnect if (_listener != nullptr) { _listener->OnDeviceDisconnected(deviceId.GetRawBuffer(nullptr)); } break; } } catch (WlanHostedNetworkException& e) { if (_listener != nullptr) { std::wostringstream ss; ss << e.what() << ": " << e.GetErrorCode(); _listener->OnAsyncException(ss.str()); } return e.GetErrorCode(); } return hr; }).Get(), &statusChangedToken); // Store the connected peer hr = wfdDevice->get_DeviceId(deviceId.GetAddressOf()); if (FAILED(hr)) { throw WlanHostedNetworkException("Get Device ID failed", hr); } _connectedDevices.insert(std::make_pair(deviceId.GetRawBuffer(nullptr), wfdDevice)); _connectedDeviceStatusChangedTokens.insert(std::make_pair(deviceId.GetRawBuffer(nullptr), statusChangedToken)); // Notify Listener if (_listener != nullptr) { _listener->OnDeviceConnected(remoteHostNameDisplay.GetRawBuffer(nullptr)); } } else { if (_listener != nullptr) { switch (status) { case AsyncStatus::Started: _listener->LogMessage(L"Device connected, status=Started"); break; case AsyncStatus::Canceled: _listener->LogMessage(L"Device connected, status=Canceled"); break; case AsyncStatus::Error: _listener->LogMessage(L"Device connected, status=Error"); break; } } } } catch (WlanHostedNetworkException& e) { if (_listener != nullptr) { std::wostringstream ss; ss << e.what() << ": " << e.GetErrorCode(); _listener->OnAsyncException(ss.str()); } return e.GetErrorCode(); } return hr; }).Get()); if (FAILED(hr)) { throw WlanHostedNetworkException("Put Completed for FromIDAsync operation failed", hr); } } else { if (_listener != nullptr) { _listener->LogMessage(L"Declined"); } } } catch (WlanHostedNetworkException& e) { if (_listener != nullptr) { std::wostringstream ss; ss << e.what() << ": " << e.GetErrorCode(); _listener->OnAsyncException(ss.str()); } return e.GetErrorCode(); } return hr; }).Get(), &_connectionRequestedToken); if (FAILED(hr)) { throw WlanHostedNetworkException("Add ConnectionRequested handler for WiFiDirectConnectionListener failed", hr); } if (_listener != nullptr) { _listener->LogMessage(L"Connection Listener is ready"); } } void WlanHostedNetworkHelper::Reset() { if (_connectionListener.Get() != nullptr) { _connectionListener->remove_ConnectionRequested(_connectionRequestedToken); } if (_publisher.Get() != nullptr) { _publisher->remove_StatusChanged(_statusChangedToken); } _legacySettings.Reset(); _advertisement.Reset(); _publisher.Reset(); _connectionListener.Reset(); _connectedDevices.clear(); }