Merge pull request #600 from google/embedded

Add embedded SDK
diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD
index 6d5fb3f..577cab9 100644
--- a/internal/platform/implementation/windows/BUILD
+++ b/internal/platform/implementation/windows/BUILD
@@ -64,13 +64,22 @@
         "thread_pool.h",
         "webrtc.h",
         "wifi.h",
+        "wifi_hotspot.h",
         "wifi_lan.h",
     ],
     visibility = ["//visibility:private"],
     deps = [
         "//internal/platform:base",
+        "//internal/platform:types",
         "//internal/platform/implementation:comm",
+        "//internal/platform/implementation:types",
         "//internal/platform/implementation/windows/generated:types",
+        "@com_google_absl//absl/base:core_headers",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/memory",
+        "@com_google_absl//absl/synchronization",
+        "@com_google_absl//absl/types:optional",
     ],
 )
 
@@ -103,6 +112,9 @@
         "system_clock.cc",
         "thread_pool.cc",
         "utils.cc",
+        "wifi_hotspot_medium.cc",
+        "wifi_hotspot_server_socket.cc",
+        "wifi_hotspot_socket.cc",
         "wifi_lan_medium.cc",
         "wifi_lan_server_socket.cc",
         "wifi_lan_socket.cc",
@@ -117,12 +129,16 @@
         ":comm",
         ":crypto",  # build_cleaner: keep
         ":types",
+        "//internal/platform:base",
+        "//internal/platform:cancellation_flag",
         "//internal/platform:types",
-        "//internal/platform/implementation:comm",
         "//internal/platform/implementation:platform",
+        "//internal/platform/implementation:types",
         "//internal/platform/implementation/shared:count_down_latch",
         "//internal/platform/implementation/shared:file",
         "//internal/platform/implementation/windows/generated:types",
+        "@com_google_absl//absl/strings",
+        "@com_google_absl//absl/strings:str_format",
     ],
 )
 
diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc
index dfa48ac..82b933b 100644
--- a/internal/platform/implementation/windows/platform.cc
+++ b/internal/platform/implementation/windows/platform.cc
@@ -19,6 +19,7 @@
 #include <windows.h>
 
 #include <xstring>
+#include <string>
 #include <sstream>
 
 #include "internal/platform/implementation/shared/count_down_latch.h"
@@ -42,6 +43,7 @@
 #include "internal/platform/implementation/windows/webrtc.h"
 #include "internal/platform/implementation/windows/wifi.h"
 #include "internal/platform/implementation/windows/wifi_lan.h"
+#include "internal/platform/implementation/windows/wifi_hotspot.h"
 
 namespace location {
 namespace nearby {
@@ -234,7 +236,7 @@
 
 std::unique_ptr<WifiHotspotMedium>
 ImplementationPlatform::CreateWifiHotspotMedium() {
-  return nullptr;
+  return std::make_unique<windows::WifiHotspotMedium>();
 }
 
 // TODO(b/184975123): replace with real implementation.
diff --git a/internal/platform/implementation/windows/utils.cc b/internal/platform/implementation/windows/utils.cc
index 961f37a..37f9b38 100644
--- a/internal/platform/implementation/windows/utils.cc
+++ b/internal/platform/implementation/windows/utils.cc
@@ -15,7 +15,9 @@
 #include "internal/platform/implementation/windows/utils.h"
 
 // Windows headers
+#include <inaddr.h>
 #include <strsafe.h>
+#include <winsock.h>
 
 // Standard C/C++ headers
 #include <codecvt>
@@ -43,6 +45,34 @@
   return absl::AsciiStrToUpper(buffer);
 }
 
+std::string ipaddr_4bytes_to_dotdecimal_string(
+    absl::string_view ipaddr_4bytes) {
+  in_addr address;
+  address.S_un.S_un_b.s_b1 = ipaddr_4bytes[0];
+  address.S_un.S_un_b.s_b2 = ipaddr_4bytes[1];
+  address.S_un.S_un_b.s_b3 = ipaddr_4bytes[2];
+  address.S_un.S_un_b.s_b4 = ipaddr_4bytes[3];
+  char* ipv4_address = inet_ntoa(address);
+  if (ipv4_address == nullptr) {
+    return {};
+  }
+
+  return std::string(ipv4_address);
+}
+
+std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s) {
+  in_addr address;
+  address.S_un.S_addr = inet_addr(ipv4_s.c_str());
+  char ipv4_b[5];
+  ipv4_b[0] = address.S_un.S_un_b.s_b1;
+  ipv4_b[1] = address.S_un.S_un_b.s_b2;
+  ipv4_b[2] = address.S_un.S_un_b.s_b3;
+  ipv4_b[3] = address.S_un.S_un_b.s_b4;
+  ipv4_b[4] = 0;
+
+  return std::string(ipv4_b, 4);
+}
+
 std::wstring string_to_wstring(std::string str) {
   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
   return converter.from_bytes(str);
diff --git a/internal/platform/implementation/windows/utils.h b/internal/platform/implementation/windows/utils.h
index ffffde7..ceee544 100644
--- a/internal/platform/implementation/windows/utils.h
+++ b/internal/platform/implementation/windows/utils.h
@@ -33,6 +33,9 @@
 
 std::string uint64_to_mac_address_string(uint64_t bluetoothAddress);
 
+std::string ipaddr_4bytes_to_dotdecimal_string(absl::string_view ipaddr_4bytes);
+std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s);
+
 // Helpers to windows platform
 std::wstring string_to_wstring(std::string str);
 std::string wstring_to_string(std::wstring wstr);
diff --git a/internal/platform/implementation/windows/utils_test.cc b/internal/platform/implementation/windows/utils_test.cc
index 524a096..e345cb3 100644
--- a/internal/platform/implementation/windows/utils_test.cc
+++ b/internal/platform/implementation/windows/utils_test.cc
@@ -14,9 +14,15 @@
 
 #include "internal/platform/implementation/windows/utils.h"
 
+#include <windows.h>
+#include <string>
+
 #include "gtest/gtest.h"
 
-using location::nearby::windows::uint64_to_mac_address_string;
+// using location::nearby::windows::uint64_to_mac_address_string;
+namespace location {
+namespace nearby {
+namespace windows {
 
 TEST(UtilsTests, MacAddressToString) {
   // Arrange
@@ -29,3 +35,24 @@
   // Assert
   EXPECT_EQ(result, expected);
 }
+
+constexpr absl::string_view kIpDotdecimal{"192.168.1.37"};
+constexpr char kIp4Bytes[] = {192, 168, 1, 37};
+
+TEST(UtilsTests, Ip4BytesToDotdecimal) {
+  std::string result =
+      ipaddr_4bytes_to_dotdecimal_string(absl::string_view(kIp4Bytes));
+
+  EXPECT_EQ(result, kIpDotdecimal);
+}
+
+TEST(UtilsTests, IpDotdecimalTo4Bytes) {
+  std::string result =
+      ipaddr_dotdecimal_to_4bytes_string(std::string(kIpDotdecimal));
+
+  EXPECT_EQ(result, std::string(kIp4Bytes));
+}
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/implementation/windows/wifi_hotspot.h b/internal/platform/implementation/windows/wifi_hotspot.h
new file mode 100644
index 0000000..137be68
--- /dev/null
+++ b/internal/platform/implementation/windows/wifi_hotspot.h
@@ -0,0 +1,285 @@
+// Copyright 2020 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
+//
+//     https://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.
+
+#ifndef PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
+#define PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
+
+// Nearby connections headers
+#include "internal/platform/implementation/wifi_hotspot.h"
+
+// WinRT headers
+#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"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Credentials.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Cryptography.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Storage.Streams.h"
+#include "internal/platform/implementation/windows/generated/winrt/base.h"
+
+namespace location {
+namespace nearby {
+namespace windows {
+
+using ::winrt::fire_and_forget;
+using ::winrt::Windows::Devices::WiFiDirect::
+    WiFiDirectAdvertisementListenStateDiscoverability;
+using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectAdvertisementPublisher;
+using ::winrt::Windows::Devices::WiFiDirect::
+    WiFiDirectAdvertisementPublisherStatus;
+using ::winrt::Windows::Devices::WiFiDirect::
+    WiFiDirectAdvertisementPublisherStatusChangedEventArgs;
+using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionListener;
+using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionRequest;
+using ::winrt::Windows::Devices::WiFiDirect::
+    WiFiDirectConnectionRequestedEventArgs;
+
+using ::winrt::Windows::Devices::WiFi::WiFiAccessStatus;
+using ::winrt::Windows::Devices::WiFi::WiFiAdapter;
+using ::winrt::Windows::Devices::WiFi::WiFiAvailableNetwork;
+using ::winrt::Windows::Devices::WiFi::WiFiConnectionStatus;
+using ::winrt::Windows::Devices::WiFi::WiFiReconnectionKind;
+
+using ::winrt::Windows::Devices::Enumeration::DeviceInformation;
+using ::winrt::Windows::Devices::Enumeration::DeviceInformationPairing;
+
+using ::winrt::Windows::Storage::Streams::Buffer;
+using ::winrt::Windows::Storage::Streams::IInputStream;
+using ::winrt::Windows::Storage::Streams::InputStreamOptions;
+using ::winrt::Windows::Storage::Streams::IOutputStream;
+
+using ::winrt::Windows::Security::Credentials::PasswordCredential;
+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::Sockets::StreamSocket;
+using ::winrt::Windows::Networking::Sockets::StreamSocketListener;
+using ::winrt::Windows::Networking::Sockets::
+    StreamSocketListenerConnectionReceivedEventArgs;
+
+// WifiHotspotSocket wraps the socket functions to read and write stream.
+// In WiFi HOTSPOT, A WifiHotspotSocket will be passed to
+// StartAcceptingConnections's call back when StreamSocketListener got connect.
+// When call API to connect to remote WiFi Hotspot service, also will return a
+// WifiHotspotSocket to caller.
+class WifiHotspotSocket : public api::WifiHotspotSocket {
+ public:
+  explicit WifiHotspotSocket(StreamSocket socket);
+  WifiHotspotSocket(const WifiHotspotSocket&) = default;
+  WifiHotspotSocket(WifiHotspotSocket&&) = default;
+  ~WifiHotspotSocket() override;
+  WifiHotspotSocket& operator=(const WifiHotspotSocket&) = default;
+  WifiHotspotSocket& operator=(WifiHotspotSocket&&) = default;
+
+  // Returns the InputStream of the WifiHotspotSocket.
+  // On error, returned stream will report Exception::kIo on any operation.
+  //
+  // The returned object is not owned by the caller, and can be invalidated once
+  // the WifiHotspotSocket object is destroyed.
+  InputStream& GetInputStream() override;
+
+  // Returns the OutputStream of the WifiHotspotSocket.
+  // On error, returned stream will report Exception::kIo on any operation.
+  //
+  // The returned object is not owned by the caller, and can be invalidated once
+  // the WifiHotspotSocket object is destroyed.
+  OutputStream& GetOutputStream() override;
+
+  // Returns Exception::kIo on error, Exception::kSuccess otherwise.
+  Exception Close() override;
+
+ private:
+  // A simple wrapper to handle input stream of socket
+  class SocketInputStream : public InputStream {
+   public:
+    SocketInputStream(IInputStream input_stream);
+    ~SocketInputStream() override = default;
+
+    ExceptionOr<ByteArray> Read(std::int64_t size) override;
+    ExceptionOr<size_t> Skip(size_t offset) override;
+    Exception Close() override;
+
+   private:
+    IInputStream input_stream_{nullptr};
+  };
+
+  // A simple wrapper to handle output stream of socket
+  class SocketOutputStream : public OutputStream {
+   public:
+    SocketOutputStream(IOutputStream output_stream);
+    ~SocketOutputStream() override = default;
+
+    Exception Write(const ByteArray& data) override;
+    Exception Flush() override;
+    Exception Close() override;
+
+   private:
+    IOutputStream output_stream_{nullptr};
+  };
+
+  // Internal properties
+  StreamSocket stream_soket_{nullptr};
+  SocketInputStream input_stream_{nullptr};
+  SocketOutputStream output_stream_{nullptr};
+};
+
+// WifiHotspotServerSocket provides the support to server socket, this server
+// socket accepts connection from clients.
+class WifiHotspotServerSocket : public api::WifiHotspotServerSocket {
+ public:
+  explicit WifiHotspotServerSocket(int port = 0);
+  WifiHotspotServerSocket(const WifiHotspotServerSocket&) = default;
+  WifiHotspotServerSocket(WifiHotspotServerSocket&&) = default;
+  ~WifiHotspotServerSocket() override;
+  WifiHotspotServerSocket& operator=(const WifiHotspotServerSocket&) = default;
+  WifiHotspotServerSocket& operator=(WifiHotspotServerSocket&&) = default;
+
+  std::string GetIPAddress() const override;
+  int GetPort() const override;
+  void SetPort(int port) { port_ = port; }
+
+  StreamSocketListener GetSocketListener() const {
+    return stream_socket_listener_;
+  }
+
+  // Blocks until either:
+  // - at least one incoming connection request is available, or
+  // - ServerSocket is closed.
+  // On success, returns connected socket, ready to exchange data.
+  // Returns nullptr on error.
+  // Once error is reported, it is permanent, and ServerSocket has to be closed.
+  std::unique_ptr<api::WifiHotspotSocket> Accept() override;
+
+  // Called by the server side of a connection before passing ownership of
+  // WifiHotspotServerSocker to user, to track validity of a pointer to this
+  // server socket.
+  void SetCloseNotifier(std::function<void()> notifier);
+
+  // Returns Exception::kIo on error, Exception::kSuccess otherwise.
+  Exception Close() override;
+
+  // Binds to local port
+  bool listen();
+
+ private:
+  // The listener is accepting incoming connections
+  fire_and_forget Listener_ConnectionReceived(
+      StreamSocketListener listener,
+      StreamSocketListenerConnectionReceivedEventArgs const& args);
+
+  // Retrieves IP addresses from local machine
+  std::vector<std::string> GetIpAddresses() const;
+  std::string GetHotspotIpAddresses() const;
+
+  mutable absl::Mutex mutex_;
+  absl::CondVar cond_;
+
+  std::deque<StreamSocket> pending_sockets_ ABSL_GUARDED_BY(mutex_);
+  StreamSocketListener stream_socket_listener_{nullptr};
+  winrt::event_token listener_event_token_{};
+
+  // Close notifier
+  std::function<void()> close_notifier_ = nullptr;
+
+  // IP addresses of the computer. mDNS uses them to advertise.
+  std::vector<std::string> ip_addresses_{};
+
+  // Cache socket not be picked by upper layer
+  std::string hotspot_ipaddr_ = {};
+  int port_ = 0;
+  bool closed_ = false;
+};
+
+// Container of operations that can be performed over the WifiHotspot medium.
+class WifiHotspotMedium : public api::WifiHotspotMedium {
+ public:
+  ~WifiHotspotMedium() override;
+
+  // Discoverer connects to server socket
+  std::unique_ptr<api::WifiHotspotSocket> ConnectToService(
+      absl::string_view ip_address, int port,
+      CancellationFlag* cancellation_flag) override;
+
+  // Advertiser starts to listen on server socket
+  std::unique_ptr<api::WifiHotspotServerSocket> ListenForService(
+      int port) override;
+
+  // Advertiser start WIFI Hotspot with specific Crendentials
+  bool StartWifiHotspot(HotspotCredentials* hotspot_credentials_) override;
+  // Advertiser stop the current WIFI Hotspot
+  bool StopWifiHotspot() override;
+  // Discoverer connects to the Hotspot
+  bool ConnectWifiHotspot(HotspotCredentials* hotspot_credentials_) override;
+  // Discoverer disconnects from the Hotspot
+  bool DisconnectWifiHotspot() override;
+
+  absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
+      override {
+    return absl::nullopt;
+  }
+
+ private:
+  enum Value:char {
+    kMediumStatusIdle = 0,
+    kMediumStatusAccepting = (1 << 0),
+    kMediumStatusBeaconing = (1 << 1),
+    kMediumStatusConnected = (1 << 2),
+  };
+
+  bool IsIdle() { return medium_status_ == kMediumStatusIdle; }
+  // Advertiser is accepting connection on server socket
+  bool IsAccepting() { return (medium_status_ & kMediumStatusAccepting) != 0; }
+  // Advertiser started Hotspot and sending beacon
+  bool IsBeaconing() { return (medium_status_ & kMediumStatusBeaconing) != 0; }
+  // Discoverer is connected with the Hotspot
+  bool IsConnected() { return (medium_status_ & kMediumStatusConnected) != 0; }
+
+  WiFiDirectAdvertisementPublisher publisher_{nullptr};
+  WiFiDirectConnectionListener listener_{nullptr};
+
+  fire_and_forget OnStatusChanged(
+      WiFiDirectAdvertisementPublisher sender,
+      WiFiDirectAdvertisementPublisherStatusChangedEventArgs event);
+  winrt::event_token publisher_status_changed_token_;
+
+  fire_and_forget OnConnectionRequested(
+      WiFiDirectConnectionListener const& sender,
+      WiFiDirectConnectionRequestedEventArgs const& event);
+  winrt::event_token connection_requested_token_;
+
+  WiFiAdapter wifi_adapter_{nullptr};
+
+  // Gets error message from exception pointer
+  std::string GetErrorMessage(std::exception_ptr eptr);
+
+  // Protects to access some members
+  absl::Mutex mutex_;
+
+  // Medium Status
+  int medium_status_ = kMediumStatusIdle;
+
+  // Keep the server socket listener pointer
+  WifiHotspotServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr;
+};
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
+
+#endif  // PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
diff --git a/internal/platform/implementation/windows/wifi_hotspot_medium.cc b/internal/platform/implementation/windows/wifi_hotspot_medium.cc
new file mode 100644
index 0000000..fd4d23a
--- /dev/null
+++ b/internal/platform/implementation/windows/wifi_hotspot_medium.cc
@@ -0,0 +1,342 @@
+// Copyright 2021 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
+//
+//     https://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 "internal/platform/implementation/windows/wifi_hotspot.h"
+
+// Nearby connections headers
+#include "internal/platform/cancellation_flag_listener.h"
+#include "internal/platform/logging.h"
+#include "internal/platform/implementation/windows/utils.h"
+
+namespace location {
+namespace nearby {
+namespace windows {
+
+namespace {
+  constexpr int kMaxRetries = 3;
+  constexpr int kRetryIntervalMilliSeconds = 300;
+  constexpr int kMaxScans = 3;
+}  // namespace
+
+WifiHotspotMedium::~WifiHotspotMedium() {
+  StopWifiHotspot();
+  DisconnectWifiHotspot();
+}
+
+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;
+    return nullptr;
+  }
+
+  std::string ipv4_address;
+  if (ip_address.length() == 4) {
+    ipv4_address = ipaddr_4bytes_to_dotdecimal_string(ip_address);
+  } else {
+    ipv4_address = std::string(ip_address);
+  }
+  if (ipv4_address.empty()) {
+    NEARBY_LOGS(ERROR) << "Invalid IP address parameter.";
+    return nullptr;
+  }
+
+  HostName host_name{winrt::to_hstring(ipv4_address)};
+  winrt::hstring service_name{winrt::to_hstring(port)};
+
+  StreamSocket socket{};
+
+  // setup cancel listener
+  if (cancellation_flag != nullptr) {
+    if (cancellation_flag->Cancelled()) {
+      NEARBY_LOGS(INFO) << "connect has been cancelled to service "
+                        << ipv4_address << ":" << port;
+      return nullptr;
+    }
+
+    location::nearby::CancellationFlagListener cancellationFlagListener(
+        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.
+  for (int i = 0; i < kMaxRetries; i++) {
+    try {
+      socket.ConnectAsync(host_name, service_name).get();
+
+      auto wifi_hotspot_socket = std::make_unique<WifiHotspotSocket>(socket);
+
+      NEARBY_LOGS(INFO) << "connected to remote service " << ipv4_address << ":"
+                        << port;
+      return wifi_hotspot_socket;
+    } catch (...) {
+      NEARBY_LOGS(ERROR) << "failed to connect remote service " << ipv4_address
+                         << ":" << port << " for the " << i+1 << " time";
+      Sleep(kRetryIntervalMilliSeconds);
+    }
+  }
+  return nullptr;
+}
+
+std::unique_ptr<api::WifiHotspotServerSocket>
+WifiHotspotMedium::ListenForService(int port) {
+  absl::MutexLock lock(&mutex_);
+
+  // check current status
+  if (IsAccepting()) {
+    NEARBY_LOGS(WARNING) << "accepting connections already started on port "
+                         << server_socket_ptr_->GetPort();
+    return nullptr;
+  }
+
+  auto server_socket = std::make_unique<WifiHotspotServerSocket>(port);
+  server_socket_ptr_ = server_socket.get();
+
+  server_socket->SetCloseNotifier([this]() {
+    absl::MutexLock lock(&mutex_);
+    NEARBY_LOGS(INFO) << "server socket was closed on port "
+                      << server_socket_ptr_->GetPort();
+    medium_status_ &= (~kMediumStatusAccepting);
+    server_socket_ptr_ = nullptr;
+  });
+
+  if (server_socket->listen()) {
+    medium_status_ |= kMediumStatusAccepting;
+    NEARBY_LOGS(INFO) << "started to listen serive on port "
+                      << server_socket_ptr_->GetPort();
+    return server_socket;
+  }
+
+  NEARBY_LOGS(ERROR) << "Failed to listen service on port " << port;
+
+  return nullptr;
+}
+
+bool WifiHotspotMedium::StartWifiHotspot(
+    HotspotCredentials* hotspot_credentials_) {
+  absl::MutexLock lock(&mutex_);
+
+  if (IsBeaconing()) {
+    NEARBY_LOGS(WARNING) << "cannot create SoftAP again when it is running.";
+    return true;
+  }
+
+  publisher_ = WiFiDirectAdvertisementPublisher();
+  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());
+  }
+
+  // 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
+  publisher_.Advertisement().IsAutonomousGroupOwnerEnabled(true);
+
+  // Using WIFIDirect legacy mode to create a softAP. AP means "access point".
+  publisher_.Advertisement().LegacySettings().IsEnabled(true);
+  std::string password = absl::StrFormat("%08x", Prng().NextUint32());
+  hotspot_credentials_->SetPassword(password);
+  PasswordCredential creds;
+  creds.Password(winrt::to_hstring(password));
+  publisher_.Advertisement().LegacySettings().Passphrase(creds);
+
+  std::string ssid = "DIRECT-" + std::to_string(Prng().NextUint32());
+  hotspot_credentials_->SetSSID(ssid);
+  publisher_.Advertisement().LegacySettings().Ssid(winrt::to_hstring(ssid));
+
+  publisher_.Start();
+  if (publisher_.Status() == WiFiDirectAdvertisementPublisherStatus::Started) {
+    NEARBY_LOGS(INFO) << "Windows WIFI Hotspot started";
+    medium_status_ |= kMediumStatusBeaconing;
+
+    return true;
+  }
+
+  // Clean up when fail
+  NEARBY_LOGS(ERROR) << "Windows WIFI Hotspot fails to start";
+  listener_ = nullptr;
+  publisher_ = nullptr;
+  return false;
+}
+
+bool WifiHotspotMedium::StopWifiHotspot() {
+  // Need to use Win32 API to deregister the Dnssd instance
+  absl::MutexLock lock(&mutex_);
+
+  if (!IsBeaconing()) {
+    NEARBY_LOGS(WARNING) << "Cannot stop SoftAP because no SoftAP is started.";
+    return true;
+  }
+
+  if (publisher_) {
+    publisher_.Stop();
+    publisher_.StatusChanged(publisher_status_changed_token_);
+    listener_.ConnectionRequested(connection_requested_token_);
+    NEARBY_LOGS(INFO) << "succeeded to stop WIFI Hotspot";
+  }
+  medium_status_ &= (~kMediumStatusBeaconing);
+  return true;
+}
+
+fire_and_forget WifiHotspotMedium::OnStatusChanged(
+    WiFiDirectAdvertisementPublisher sender,
+    WiFiDirectAdvertisementPublisherStatusChangedEventArgs event) {
+  if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Started) {
+    if (sender.Advertisement().LegacySettings().IsEnabled()) {
+      NEARBY_LOGS(INFO)
+          << "WIFI SoftAP SSID: "
+          << winrt::to_string(
+                 publisher_.Advertisement().LegacySettings().Ssid());
+      NEARBY_LOGS(INFO) << "WIFI SoftAP PW: "
+                        << winrt::to_string(publisher_.Advertisement()
+                                                .LegacySettings()
+                                                .Passphrase()
+                                                .Password());
+    }
+  }
+  if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Stopped) {
+    NEARBY_LOGS(INFO) << "Receive WIFI direct/SoftAP stop event";
+  }
+  return winrt::fire_and_forget();
+}
+
+fire_and_forget WifiHotspotMedium::OnConnectionRequested(
+    WiFiDirectConnectionListener const& sender,
+    WiFiDirectConnectionRequestedEventArgs const& event) {
+  WiFiDirectConnectionRequest connection_request = event.GetConnectionRequest();
+  winrt::hstring device_name = connection_request.DeviceInformation().Name();
+  NEARBY_LOGS(INFO) << "Receive connection request from: "
+                    << winrt::to_string(device_name);
+
+  DeviceInformationPairing pairing =
+      connection_request.DeviceInformation().Pairing();
+  if (pairing.IsPaired())
+    NEARBY_LOGS(INFO) << "Paired";
+  else
+    NEARBY_LOGS(INFO) << "Not Paired";
+
+  return winrt::fire_and_forget();
+}
+
+bool WifiHotspotMedium::ConnectWifiHotspot(
+    HotspotCredentials* hotspot_credentials_) {
+  absl::MutexLock lock(&mutex_);
+
+  if (IsConnected()) {
+    NEARBY_LOGS(WARNING) << "Already connected to AP, disconnect first.";
+    DisconnectWifiHotspot();
+  }
+
+  auto access = WiFiAdapter::RequestAccessAsync().get();
+  if (access != WiFiAccessStatus::Allowed) {
+    NEARBY_LOGS(WARNING) << "Access Denied with reason: "
+                         << static_cast<int>(access);
+    return false;
+  }
+
+  auto adaptors = WiFiAdapter::FindAllAdaptersAsync().get();
+  if (adaptors.Size() < 1) {
+    NEARBY_LOGS(WARNING) << "No WIFI Adaptor found.";
+    return false;
+  }
+  wifi_adapter_ = adaptors.GetAt(0);
+
+  // SoftAP is an abbreviation for "software enabled access point".
+  WiFiAvailableNetwork nearby_softap{nullptr};
+  NEARBY_LOGS(INFO) << "Scanning for Nearby Hotspot SSID: "
+                    << hotspot_credentials_->GetSSID();
+
+  // First time scan may not find our target hotspot, try 3 times can almost
+  // guarantee to find the Hotspot
+  for (int i = 0; i < kMaxScans; i++) {
+    for (const auto& network :
+         wifi_adapter_.NetworkReport().AvailableNetworks()) {
+      if (winrt::to_string(network.Ssid()) == hotspot_credentials_->GetSSID()) {
+        NEARBY_LOGS(INFO) << "Found Nearby SSID: "
+                          << winrt::to_string(network.Ssid());
+        nearby_softap = network;
+        break;
+      }
+    }
+    if (nearby_softap) break;
+    NEARBY_LOGS(INFO) << "Scan ... ";
+    wifi_adapter_.ScanAsync().get();
+  }
+
+  if (!nearby_softap) {
+    NEARBY_LOGS(INFO) << "Hotspot is not found";
+    return false;
+  }
+
+  PasswordCredential creds;
+  creds.Password(winrt::to_hstring(hotspot_credentials_->GetPassword()));
+
+  auto connect_result =
+      wifi_adapter_
+          .ConnectAsync(nearby_softap, WiFiReconnectionKind::Automatic, creds)
+          .get();
+
+  if (connect_result.ConnectionStatus() != WiFiConnectionStatus::Success) {
+    NEARBY_LOGS(INFO) << "Connectiong failed with reason: "
+                      << static_cast<int>(connect_result.ConnectionStatus());
+    return false;
+  }
+
+  NEARBY_LOGS(INFO) << "Connected to: " << hotspot_credentials_->GetSSID();
+  medium_status_ |= kMediumStatusConnected;
+  return true;
+}
+
+bool WifiHotspotMedium::DisconnectWifiHotspot() {
+  absl::MutexLock lock(&mutex_);
+
+  if (!IsConnected()) {
+    NEARBY_LOGS(WARNING)
+        << "Cannot diconnect SoftAP because it is not connected.";
+  }
+  if (wifi_adapter_) {
+    wifi_adapter_.Disconnect();
+    NEARBY_LOGS(INFO) << "Disconnect softAP.";
+    wifi_adapter_ = {nullptr};
+  }
+  medium_status_ &= (~kMediumStatusConnected);
+  return true;
+}
+
+std::string WifiHotspotMedium::GetErrorMessage(std::exception_ptr eptr) {
+  try {
+    if (eptr) {
+      std::rethrow_exception(eptr);
+    } else {
+      return "";
+    }
+  } catch (const std::exception& e) {
+    return e.what();
+  }
+}
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc
new file mode 100644
index 0000000..095ccd4
--- /dev/null
+++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc
@@ -0,0 +1,207 @@
+// Copyright 2021 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
+//
+//     https://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 "internal/platform/implementation/windows/utils.h"
+#include "internal/platform/implementation/windows/wifi_hotspot.h"
+#include "internal/platform/logging.h"
+
+namespace location {
+namespace nearby {
+namespace windows {
+
+WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {}
+
+WifiHotspotServerSocket::~WifiHotspotServerSocket() { Close(); }
+
+std::string WifiHotspotServerSocket::GetIPAddress() const {
+  if (stream_socket_listener_ == nullptr) {
+    return {};
+  }
+
+  if (ip_addresses_.empty()) {
+    auto ip_addr = GetIpAddresses();
+    if (ip_addr.empty()) {
+      return {};
+    }
+    return ip_addr.front();
+  }
+  return ip_addresses_.front();
+}
+
+int WifiHotspotServerSocket::GetPort() const {
+  if (stream_socket_listener_ == nullptr) {
+    return 0;
+  }
+
+  return std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
+}
+
+std::unique_ptr<api::WifiHotspotSocket> WifiHotspotServerSocket::Accept() {
+  absl::MutexLock lock(&mutex_);
+  while (!closed_ && pending_sockets_.empty()) {
+    cond_.Wait(&mutex_);
+  }
+  if (closed_) return {};
+
+  StreamSocket wifi_hotspot_socket = pending_sockets_.front();
+  pending_sockets_.pop_front();
+  return std::make_unique<WifiHotspotSocket>(wifi_hotspot_socket);
+}
+
+void WifiHotspotServerSocket::SetCloseNotifier(std::function<void()> notifier) {
+  close_notifier_ = std::move(notifier);
+}
+
+Exception WifiHotspotServerSocket::Close() {
+  try {
+    absl::MutexLock lock(&mutex_);
+    if (closed_) {
+      return {Exception::kSuccess};
+    }
+    if (stream_socket_listener_ != nullptr) {
+      stream_socket_listener_.ConnectionReceived(listener_event_token_);
+      stream_socket_listener_.Close();
+      stream_socket_listener_ = nullptr;
+
+      if (!pending_sockets_.empty()) {
+        auto it = pending_sockets_.begin();
+        while (it != pending_sockets_.end()) {
+          it->Close();
+        }
+      }
+
+      cond_.SignalAll();
+    }
+
+    closed_ = true;
+    if (close_notifier_ != nullptr) {
+      close_notifier_();
+    }
+    return {Exception::kSuccess};
+  } catch (...) {
+    return {Exception::kIo};
+  }
+}
+
+bool WifiHotspotServerSocket::listen() {
+  // Check IP address
+  hotspot_ipaddr_ = GetHotspotIpAddresses();
+
+  if (hotspot_ipaddr_.empty()) {
+    NEARBY_LOGS(WARNING) << "failed to start accepting connection without IP "
+                            "addresses configured on computer.";
+    return false;
+  }
+
+  // Save connection callback
+  stream_socket_listener_ = StreamSocketListener();
+
+  // Setup callback
+  listener_event_token_ = stream_socket_listener_.ConnectionReceived(
+      {this, &WifiHotspotServerSocket::Listener_ConnectionReceived});
+
+  try {
+    HostName host_name{winrt::to_hstring(hotspot_ipaddr_)};
+    stream_socket_listener_
+        .BindEndpointAsync(host_name, winrt::to_hstring(port_))
+        .get();
+    if (port_ == 0) {
+      port_ =
+          std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
+    }
+
+    return true;
+  } catch (...) {
+    // Cannot bind to the preferred port, will let system to assign port.
+    NEARBY_LOGS(WARNING) << "cannot accept connection on preferred port.";
+  }
+
+  try {
+    stream_socket_listener_.BindServiceNameAsync({}).get();
+    // need to save the port information
+    port_ =
+        std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
+    NEARBY_LOGS(INFO) << "Server Socket port: " << port_;
+    return true;
+  } catch (...) {
+    // Cannot bind to the preferred port, will let system to assign port.
+    NEARBY_LOGS(ERROR) << "cannot bind to any port.";
+  }
+
+  return false;
+}
+
+fire_and_forget WifiHotspotServerSocket::Listener_ConnectionReceived(
+    StreamSocketListener listener,
+    StreamSocketListenerConnectionReceivedEventArgs const &args) {
+  absl::MutexLock lock(&mutex_);
+
+  if (closed_) {
+    return fire_and_forget{};
+  }
+
+  pending_sockets_.push_back(args.Socket());
+  cond_.SignalAll();
+  return fire_and_forget{};
+}
+
+bool HasEnding(std::string const &full_string, std::string const &ending) {
+  if (full_string.length() >= ending.length()) {
+    return (0 == full_string.compare(full_string.length() - ending.length(),
+                                     ending.length(), ending));
+  } else {
+    return false;
+  }
+}
+
+
+std::vector<std::string> WifiHotspotServerSocket::GetIpAddresses() const {
+  std::vector<std::string> result{};
+  auto host_names = NetworkInformation::GetHostNames();
+  for (auto host_name : host_names) {
+    if (host_name.IPInformation() != nullptr &&
+        host_name.IPInformation().NetworkAdapter() != nullptr &&
+        host_name.Type() == HostNameType::Ipv4) {
+      std::string ipv4_s = winrt::to_string(host_name.ToString());
+
+      if (HasEnding(ipv4_s, ".1")) {
+        // std::string ipv4_b_s = ipaddr_dotdecimal_to_4bytes_string(ipv4_s);
+        result.push_back(ipv4_s);
+      }
+    }
+  }
+  return result;
+}
+
+std::string WifiHotspotServerSocket::GetHotspotIpAddresses() const {
+  auto host_names = NetworkInformation::GetHostNames();
+  for (auto host_name : host_names) {
+    if (host_name.IPInformation() != nullptr &&
+        host_name.IPInformation().NetworkAdapter() != nullptr &&
+        host_name.Type() == HostNameType::Ipv4) {
+      std::string ipv4_s = winrt::to_string(host_name.ToString());
+      if (HasEnding(ipv4_s, ".1")) {
+        // TODO(b/228541380): replace when we find a better way to identifying
+        // the hotspot address
+        NEARBY_LOGS(INFO) << "Found Hotspot IP: " << ipv4_s;
+        return ipv4_s;
+      }
+    }
+  }
+  return {};
+}
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/implementation/windows/wifi_hotspot_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_socket.cc
new file mode 100644
index 0000000..b6dc1c7
--- /dev/null
+++ b/internal/platform/implementation/windows/wifi_hotspot_socket.cc
@@ -0,0 +1,142 @@
+// Copyright 2021 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
+//
+//     https://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 "internal/platform/implementation/windows/wifi_hotspot.h"
+#include "internal/platform/logging.h"
+
+namespace location {
+namespace nearby {
+namespace windows {
+
+WifiHotspotSocket::WifiHotspotSocket(StreamSocket socket) {
+  stream_soket_ = socket;
+  input_stream_ = SocketInputStream(socket.InputStream());
+  output_stream_ = SocketOutputStream(socket.OutputStream());
+}
+
+WifiHotspotSocket::~WifiHotspotSocket() {
+  if (stream_soket_ != nullptr) {
+    try {
+      Close();
+    } catch (...) {
+      NEARBY_LOGS(ERROR) << "Failed to destructor class WifiHotspotSocket.";
+    }
+  }
+}
+
+InputStream& WifiHotspotSocket::GetInputStream() { return input_stream_; }
+
+OutputStream& WifiHotspotSocket::GetOutputStream() { return output_stream_; }
+
+Exception WifiHotspotSocket::Close() {
+  try {
+    if (stream_soket_ != nullptr) {
+      stream_soket_.Close();
+    }
+    return {Exception::kSuccess};
+  } catch (...) {
+    return {Exception::kIo};
+  }
+}
+
+WifiHotspotSocket::SocketInputStream::SocketInputStream(
+    IInputStream input_stream) {
+  input_stream_ = input_stream;
+}
+
+ExceptionOr<ByteArray> WifiHotspotSocket::SocketInputStream::Read(
+    std::int64_t size) {
+  try {
+    Buffer buffer = Buffer(size);
+
+    auto ibuffer =
+        input_stream_.ReadAsync(buffer, size, InputStreamOptions::None).get();
+
+    if (ibuffer.Length() != size) {
+      NEARBY_LOGS(WARNING) << "Only got part of data of needed.";
+    }
+
+    ByteArray data((char*)ibuffer.data(), ibuffer.Length());
+
+    return ExceptionOr(data);
+  } catch (...) {
+    return Exception{Exception::kIo};
+  }
+}
+
+ExceptionOr<size_t> WifiHotspotSocket::SocketInputStream::Skip(size_t offset) {
+  try {
+    Buffer buffer = Buffer(offset);
+
+    auto ibuffer =
+        input_stream_.ReadAsync(buffer, offset, InputStreamOptions::None).get();
+    return ExceptionOr((size_t)ibuffer.Length());
+  } catch (...) {
+    return Exception{Exception::kIo};
+  }
+}
+
+Exception WifiHotspotSocket::SocketInputStream::Close() {
+  try {
+    input_stream_.Close();
+  } catch (...) {
+    return {Exception::kIo};
+  }
+
+  return {Exception::kSuccess};
+}
+
+// SocketOutputStream
+WifiHotspotSocket::SocketOutputStream::SocketOutputStream(
+    IOutputStream output_stream) {
+  output_stream_ = output_stream;
+}
+
+Exception WifiHotspotSocket::SocketOutputStream::Write(const ByteArray& data) {
+  Buffer buffer = Buffer(data.size());
+  std::memcpy(buffer.data(), data.data(), data.size());
+  buffer.Length(data.size());
+
+  try {
+    output_stream_.WriteAsync(buffer).get();
+  } catch (...) {
+    return {Exception::kIo};
+  }
+
+  return {Exception::kSuccess};
+}
+
+Exception WifiHotspotSocket::SocketOutputStream::Flush() {
+  try {
+    output_stream_.FlushAsync().get();
+  } catch (...) {
+    return {Exception::kIo};
+  }
+
+  return {Exception::kSuccess};
+}
+
+Exception WifiHotspotSocket::SocketOutputStream::Close() {
+  try {
+    output_stream_.Close();
+  } catch (...) {
+    return {Exception::kIo};
+  }
+
+  return {Exception::kSuccess};
+}
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/implementation/windows/wifi_lan_medium.cc b/internal/platform/implementation/windows/wifi_lan_medium.cc
index 371a9b0..26ccadb 100644
--- a/internal/platform/implementation/windows/wifi_lan_medium.cc
+++ b/internal/platform/implementation/windows/wifi_lan_medium.cc
@@ -344,7 +344,8 @@
 
   if (server_socket->listen()) {
     medium_status_ |= MEDIUM_STATUS_ACCEPTING;
-    NEARBY_LOGS(INFO) << "started to listen serive on port " << port;
+    NEARBY_LOGS(INFO) << "started to listen serive on port "
+                      << server_socket_ptr_->GetPort();
     return server_socket;
   }