Add Wifi Medium to query WIFI interface capability and information

PiperOrigin-RevId: 451233512
diff --git a/Package.swift b/Package.swift
index 45fd27b..4f55fd1 100644
--- a/Package.swift
+++ b/Package.swift
@@ -437,6 +437,7 @@
         "connections/implementation/mediums/lost_entity_tracker_test.cc",
         "connections/implementation/mediums/bluetooth_radio_test.cc",
         "connections/implementation/mediums/wifi_hotspot_test.cc",
+        "connections/implementation/mediums/wifi_test.cc",
         "connections/implementation/endpoint_channel_manager_test.cc",
         "connections/implementation/bwu_manager_test.cc",
         "connections/implementation/base_bwu_handler_test.cc",
@@ -462,6 +463,8 @@
         "internal/platform/uuid_test.cc",
         "internal/platform/wifi_hotspot_test.cc",
         "internal/platform/wifi_lan_test.cc",
+        "internal/platform/wifi_test.cc",
+        "internal/platform/wifi_utils_test.cc",
         "internal/platform/condition_variable_test.cc",
         "internal/platform/thread_check_nocompile_test.py",
         "internal/platform/bluetooth_classic_test.cc",
diff --git a/connections/implementation/base_pcp_handler.cc b/connections/implementation/base_pcp_handler.cc
index 942f81e..187947a 100644
--- a/connections/implementation/base_pcp_handler.cc
+++ b/connections/implementation/base_pcp_handler.cc
@@ -548,11 +548,31 @@
         // Generate the nonce to use for this connection.
         std::int32_t nonce = prng_.NextInt32();
 
+        bool is_supports_5_ghz = false;
+        std::string bssid = "";
+        std::int32_t ap_frequency = -1;
+        std::string ip_address = "";
+        if (mediums_->GetWifi().IsAvailable()) {
+          is_supports_5_ghz =
+              mediums_->GetWifi().GetCapability().supports_5_ghz;
+
+          api::WifiInformation& wifi_info =
+              mediums_->GetWifi().GetInformation();
+          bssid = wifi_info.bssid;
+          ap_frequency = wifi_info.ap_frequency;
+          ip_address = wifi_info.ip_address_4_bytes;
+          NEARBY_LOGS(INFO) << "Query for WIFI information: is_supports_5_ghz="
+                            << is_supports_5_ghz << "; bssid=" << bssid
+                            << "; ap_frequency=" << ap_frequency
+                            << "Mhz; ip_address in bytes format=" << ip_address;
+        }
+
         // The first message we have to send, after connecting, is to tell the
         // endpoint about ourselves.
         Exception write_exception = WriteConnectionRequestFrame(
             channel.get(), client->GetLocalEndpointId(), info.endpoint_info,
-            nonce, GetSupportedConnectionMediumsByPriority(connection_options),
+            nonce, is_supports_5_ghz, bssid, ap_frequency, ip_address,
+            GetSupportedConnectionMediumsByPriority(connection_options),
             connection_options.keep_alive_interval_millis,
             connection_options.keep_alive_timeout_millis);
         if (!write_exception.Ok()) {
@@ -710,12 +730,14 @@
 Exception BasePcpHandler::WriteConnectionRequestFrame(
     EndpointChannel* endpoint_channel, const std::string& local_endpoint_id,
     const ByteArray& local_endpoint_info, std::int32_t nonce,
+    bool supports_5_ghz, const std::string& bssid, std::int32_t ap_frequency,
+    const std::string& ip_address,
     const std::vector<proto::connections::Medium>& supported_mediums,
     std::int32_t keep_alive_interval_millis,
     std::int32_t keep_alive_timeout_millis) {
   return endpoint_channel->Write(parser::ForConnectionRequest(
-      local_endpoint_id, local_endpoint_info, nonce, /*supports_5_ghz =*/false,
-      /*bssid=*/std::string{}, supported_mediums, keep_alive_interval_millis,
+      local_endpoint_id, local_endpoint_info, nonce, supports_5_ghz, bssid,
+      ap_frequency, ip_address, supported_mediums, keep_alive_interval_millis,
       keep_alive_timeout_millis));
 }
 
diff --git a/connections/implementation/base_pcp_handler.h b/connections/implementation/base_pcp_handler.h
index 7b9b47c..305a7c3 100644
--- a/connections/implementation/base_pcp_handler.h
+++ b/connections/implementation/base_pcp_handler.h
@@ -387,6 +387,8 @@
   static Exception WriteConnectionRequestFrame(
       EndpointChannel* endpoint_channel, const std::string& local_endpoint_id,
       const ByteArray& local_endpoint_info, std::int32_t nonce,
+      bool supports_5_ghz, const std::string& bssid, std::int32_t ap_frequency,
+      const std::string& ip_address,
       const std::vector<proto::connections::Medium>& supported_mediums,
       std::int32_t keep_alive_interval_millis,
       std::int32_t keep_alive_timeout_millis);
diff --git a/connections/implementation/endpoint_manager_test.cc b/connections/implementation/endpoint_manager_test.cc
index a60ca41..7c82700 100644
--- a/connections/implementation/endpoint_manager_test.cc
+++ b/connections/implementation/endpoint_manager_test.cc
@@ -195,9 +195,10 @@
   auto endpoint_channel = std::make_unique<MockEndpointChannel>();
   auto connect_request = std::make_unique<MockFrameProcessor>();
   ByteArray endpoint_info{"endpoint_name"};
-  auto read_data =
-      parser::ForConnectionRequest("endpoint_id", endpoint_info, 1234, false,
-                                   "", std::vector{Medium::BLE}, 0, 0);
+  auto read_data = parser::ForConnectionRequest(
+      "endpoint_id", endpoint_info, 1234, false, "", 2412, "8xqT",
+      std::vector<Medium>{Medium::BLE}, 0,
+      0);
   EXPECT_CALL(*connect_request, OnIncomingFrame);
   EXPECT_CALL(*connect_request, OnEndpointDisconnect);
   EXPECT_CALL(*endpoint_channel, Read())
diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD
index 71d3faa..afefd61 100644
--- a/connections/implementation/mediums/BUILD
+++ b/connections/implementation/mediums/BUILD
@@ -32,6 +32,7 @@
         "bluetooth_radio.h",
         "mediums.h",
         "webrtc_stub.h",
+        "wifi.h",
         "wifi_hotspot.h",
         "wifi_lan.h",
     ],
@@ -101,6 +102,7 @@
         "lost_entity_tracker_test.cc",
         "wifi_hotspot_test.cc",
         "wifi_lan_test.cc",
+        "wifi_test.cc",
     ],
     defines = ["NO_WEBRTC"],
     shard_count = 16,
diff --git a/connections/implementation/mediums/mediums.cc b/connections/implementation/mediums/mediums.cc
index 051562b..bc686c1 100644
--- a/connections/implementation/mediums/mediums.cc
+++ b/connections/implementation/mediums/mediums.cc
@@ -26,6 +26,8 @@
 
 BleV2& Mediums::GetBleV2() { return ble_v2_; }
 
+Wifi& Mediums::GetWifi() { return wifi_; }
+
 WifiLan& Mediums::GetWifiLan() { return wifi_lan_; }
 
 WifiHotspot& Mediums::GetWifiHotspot() { return wifi_hotspot_; }
diff --git a/connections/implementation/mediums/mediums.h b/connections/implementation/mediums/mediums.h
index 28a9c95..aa4743c 100644
--- a/connections/implementation/mediums/mediums.h
+++ b/connections/implementation/mediums/mediums.h
@@ -24,6 +24,7 @@
 #else
 #include "connections/implementation/mediums/webrtc.h"
 #endif
+#include "connections/implementation/mediums/wifi.h"
 #include "connections/implementation/mediums/wifi_hotspot.h"
 #include "connections/implementation/mediums/wifi_lan.h"
 
@@ -49,6 +50,9 @@
   // Returns a handle to the Ble medium.
   BleV2& GetBleV2();
 
+  // Returns a handle to the Wifi medium.
+  Wifi& GetWifi();
+
   // Returns a handle to the Wifi-Lan medium.
   WifiLan& GetWifiLan();
 
@@ -71,6 +75,7 @@
   BluetoothClassic bluetooth_classic_{bluetooth_radio_};
   Ble ble_{bluetooth_radio_};
   BleV2 ble_v2_{bluetooth_radio_};
+  Wifi wifi_;
   WifiLan wifi_lan_;
   WifiHotspot wifi_hotspot_;
   mediums::WebRtc webrtc_;
diff --git a/connections/implementation/mediums/wifi.h b/connections/implementation/mediums/wifi.h
new file mode 100644
index 0000000..34caa6d
--- /dev/null
+++ b/connections/implementation/mediums/wifi.h
@@ -0,0 +1,80 @@
+// Copyright 2022 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 CORE_INTERNAL_MEDIUMS_WIFI_H_
+#define CORE_INTERNAL_MEDIUMS_WIFI_H_
+
+// #include "internal/platform/mutex.h"
+#include <string>
+
+#include "internal/platform/mutex_lock.h"
+#include "internal/platform/wifi.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+class Wifi {
+ public:
+  Wifi() = default;
+  ~Wifi() = default;
+  // Not copyable or movable
+  Wifi(const Wifi&) = delete;
+  Wifi& operator=(const Wifi&) = delete;
+  Wifi(Wifi&&) = delete;
+  Wifi& operator=(Wifi&&) = delete;
+
+  // Returns true, if Wifi Medium are supported by a platform.
+  bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_) {
+    MutexLock lock(&mutex_);
+    return IsAvailableLocked();
+  }
+
+  api::WifiCapability& GetCapability() {
+    MutexLock lock(&mutex_);
+    return medium_.GetCapability();
+  }
+
+  api::WifiInformation& GetInformation() {
+    MutexLock lock(&mutex_);
+    return medium_.GetInformation();
+  }
+
+  bool VerifyInternetConnectivity() {
+    MutexLock lock(&mutex_);
+    return medium_.VerifyInternetConnectivity();
+  }
+
+  std::string GetIpAddress() const {
+    MutexLock lock(&mutex_);
+    return medium_.GetIpAddress();
+  }
+
+ private:
+  mutable Mutex mutex_;
+  WifiMedium medium_ ABSL_GUARDED_BY(mutex_);
+
+  // Same as IsAvailable(), but must be called with mutex_ held.
+  bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
+    if (medium_.IsValid()) return medium_.IsInterfaceValid();
+
+    return false;
+  }
+};
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CORE_INTERNAL_MEDIUMS_WIFI_H_
diff --git a/connections/implementation/mediums/wifi_test.cc b/connections/implementation/mediums/wifi_test.cc
new file mode 100644
index 0000000..2488a0f
--- /dev/null
+++ b/connections/implementation/mediums/wifi_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 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 "connections/implementation/mediums/wifi.h"
+
+#include "gmock/gmock.h"
+#include "protobuf-matchers/protocol-buffer-matchers.h"
+#include "gtest/gtest.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+namespace {
+
+TEST(WifiTest, ConstructorDestructorWorks) {
+  Wifi wifi_a;
+  Wifi wifi_b;
+
+  if (wifi_a.IsAvailable() && wifi_b.IsAvailable()) {
+    // Make sure we can create 2 distinct mediums.
+    EXPECT_NE(&wifi_a, &wifi_b);
+  }
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+TEST(WifiTest, CanGetCapability) {
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+TEST(WifiTest, CanInformation) {
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+
+}  // namespace
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/connections/implementation/offline_frames.cc b/connections/implementation/offline_frames.cc
index 4182930..5294a48 100644
--- a/connections/implementation/offline_frames.cc
+++ b/connections/implementation/offline_frames.cc
@@ -66,6 +66,8 @@
                                const ByteArray& endpoint_info,
                                std::int32_t nonce, bool supports_5_ghz,
                                const std::string& bssid,
+                               std::int32_t ap_frequency,
+                               const std::string& ip_address,
                                const std::vector<Medium>& mediums,
                                std::int32_t keep_alive_interval_millis,
                                std::int32_t keep_alive_timeout_millis) {
@@ -84,6 +86,8 @@
   auto* medium_metadata = connection_request->mutable_medium_metadata();
   medium_metadata->set_supports_5_ghz(supports_5_ghz);
   if (!bssid.empty()) medium_metadata->set_bssid(bssid);
+  medium_metadata->set_ap_frequency(ap_frequency);
+  if (!ip_address.empty()) medium_metadata->set_ip_address(ip_address);
   if (!mediums.empty()) {
     for (const auto& medium : mediums) {
       connection_request->add_mediums(MediumToConnectionRequestMedium(medium));
diff --git a/connections/implementation/offline_frames.h b/connections/implementation/offline_frames.h
index 3dc5229..462de6f 100644
--- a/connections/implementation/offline_frames.h
+++ b/connections/implementation/offline_frames.h
@@ -46,6 +46,8 @@
                                const ByteArray& endpoint_info,
                                std::int32_t nonce, bool supports_5_ghz,
                                const std::string& bssid,
+                               std::int32_t ap_frequency,
+                               const std::string& ip_address,
                                const std::vector<Medium>& mediums,
                                std::int32_t keep_alive_interval_millis,
                                std::int32_t keep_alive_timeout_millis);
diff --git a/connections/implementation/offline_frames_test.cc b/connections/implementation/offline_frames_test.cc
index d34e431..d434b6c 100644
--- a/connections/implementation/offline_frames_test.cc
+++ b/connections/implementation/offline_frames_test.cc
@@ -40,6 +40,8 @@
 constexpr int kNonce = 1234;
 constexpr bool kSupports5ghz = true;
 constexpr absl::string_view kBssid{"FF:FF:FF:FF:FF:FF"};
+constexpr int kApFrequency = 2412;
+constexpr absl::string_view kIp4Bytes = {"8xqT"};
 constexpr std::array<Medium, 9> kMediums = {
     Medium::MDNS, Medium::BLUETOOTH,   Medium::WIFI_HOTSPOT,
     Medium::BLE,  Medium::WIFI_LAN,    Medium::WIFI_AWARE,
@@ -95,7 +97,7 @@
         endpoint_name: "XYZ"
         endpoint_info: "XYZ"
         nonce: 1234
-        medium_metadata: < supports_5_ghz: true bssid: "FF:FF:FF:FF:FF:FF" >
+        medium_metadata: < supports_5_ghz: true bssid: "FF:FF:FF:FF:FF:FF" ip_address: "8xqT" ap_frequency: 2412 >
         mediums: MDNS
         mediums: BLUETOOTH
         mediums: WIFI_HOTSPOT
@@ -111,9 +113,10 @@
     >)pb";
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, std::string(kBssid),
-      std::vector(kMediums.begin(), kMediums.end()), kKeepAliveIntervalMillis,
-      kKeepAliveTimeoutMillis);
+      kSupports5ghz, std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
+      kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   auto response = FromBytes(bytes);
   ASSERT_TRUE(response.ok());
   OfflineFrame message = FromBytes(bytes).result();
diff --git a/connections/implementation/offline_frames_validator_test.cc b/connections/implementation/offline_frames_validator_test.cc
index e70e251..04cd73e 100644
--- a/connections/implementation/offline_frames_validator_test.cc
+++ b/connections/implementation/offline_frames_validator_test.cc
@@ -35,6 +35,8 @@
 constexpr int kNonce = 1234;
 constexpr bool kSupports5ghz = true;
 constexpr absl::string_view kBssid{"FF:FF:FF:FF:FF:FF"};
+constexpr int kApFrequency = 2412;
+constexpr absl::string_view kIp4Bytes = {"8xqT"};
 constexpr int kStatusAccepted = 0;
 constexpr absl::string_view kSsid = "ssid";
 constexpr absl::string_view kPassword = "password";
@@ -57,9 +59,10 @@
 
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, std::string(kBssid),
-      std::vector(kMediums.begin(), kMediums.end()), kKeepAliveIntervalMillis,
-      kKeepAliveTimeoutMillis);
+      kSupports5ghz, std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
+      kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
 
   auto ret_value = EnsureValidOfflineFrame(offline_frame);
@@ -73,9 +76,10 @@
 
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, std::string(kBssid),
-      std::vector(kMediums.begin(), kMediums.end()), kKeepAliveIntervalMillis,
-      kKeepAliveTimeoutMillis);
+      kSupports5ghz, std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
+      kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
   auto* v1_frame = offline_frame.mutable_v1();
 
@@ -93,9 +97,10 @@
   std::string empty_enpoint_id;
   ByteArray bytes = ForConnectionRequest(
       empty_enpoint_id, ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, std::string(kBssid),
-      std::vector(kMediums.begin(), kMediums.end()), kKeepAliveIntervalMillis,
-      kKeepAliveTimeoutMillis);
+      kSupports5ghz, std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
+      kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
 
   auto ret_value = EnsureValidOfflineFrame(offline_frame);
@@ -110,7 +115,9 @@
   ByteArray empty_endpoint_info;
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), empty_endpoint_info, kNonce, kSupports5ghz,
-      std::string(kBssid), std::vector(kMediums.begin(), kMediums.end()),
+      std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
       kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
 
@@ -126,7 +133,9 @@
   std::string empty_bssid;
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, empty_bssid, std::vector(kMediums.begin(), kMediums.end()),
+      kSupports5ghz, empty_bssid, kApFrequency, std::string(kIp4Bytes),
+      std::vector<Medium, std::allocator<Medium>>(kMediums.begin(),
+                                                  kMediums.end()),
       kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
 
@@ -142,8 +151,8 @@
   std::vector<Medium> empty_mediums;
   ByteArray bytes = ForConnectionRequest(
       std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce,
-      kSupports5ghz, std::string(kBssid), empty_mediums,
-      kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
+      kSupports5ghz, std::string(kBssid), kApFrequency, std::string(kIp4Bytes),
+      empty_mediums, kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis);
   offline_frame.ParseFromString(std::string(bytes));
 
   auto ret_value = EnsureValidOfflineFrame(offline_frame);
diff --git a/internal/platform/BUILD b/internal/platform/BUILD
index 02870d3..6ea26e6 100644
--- a/internal/platform/BUILD
+++ b/internal/platform/BUILD
@@ -334,14 +334,17 @@
         "file.cc",
         "wifi_hotspot.cc",
         "wifi_lan.cc",
+        "wifi_utils.cc",
     ],
     hdrs = [
         "ble.h",
         "ble_v2.h",
         "bluetooth_adapter.h",
         "bluetooth_classic.h",
+        "wifi.h",
         "wifi_hotspot.h",
         "wifi_lan.h",
+        "wifi_utils.h",
     ],
     copts = ["-DCORE_ADAPTER_DLL"],
     defines = ["NO_WEBRTC"],
@@ -350,6 +353,7 @@
         "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
         "//internal/analytics:__subpackages__",
         "//internal/platform:__pkg__",
+        "//internal/platform/implementation:__subpackages__",
     ],
     deps = [
         ":logging",
@@ -390,6 +394,8 @@
         "uuid_test.cc",
         "wifi_hotspot_test.cc",
         "wifi_lan_test.cc",
+        "wifi_test.cc",
+        "wifi_utils_test.cc",
     ],
     copts = ["-DCORE_ADAPTER_DLL"],
     defines = ["NO_WEBRTC"],
diff --git a/internal/platform/implementation/wifi.h b/internal/platform/implementation/wifi.h
index 1f0f5b7..21c85ed 100644
--- a/internal/platform/implementation/wifi.h
+++ b/internal/platform/implementation/wifi.h
@@ -46,6 +46,39 @@
   kAuthFailure = 3,
 };
 
+// WIFI Band type.
+enum class WifiBandType {
+  kUnknown = 0,
+  kBand24Ghz = 1,
+  kBand5Ghz = 2,
+  kBand6Ghz = 3,
+  kBand60Ghz = 4,
+};
+
+// Native WIFI's capablity parameters
+struct WifiCapability {
+  bool supports_5_ghz = false;
+  bool supports_6_ghz = false;
+};
+
+// Native WIFI's information parameters
+struct WifiInformation {
+  // Is this WIFI interface connected to AP or not
+  bool is_connected;
+  // AP's SSID if connected
+  std::string ssid;
+  // WiFi LAN BSSID, in the form of a six-byte MAC address: XX:XX:XX:XX:XX:XX
+  std::string bssid;
+  // The frequency of the WiFi LAN AP(in MHz, or -1 is not associated with an AP
+  // over WiFi).
+  std::int32_t ap_frequency;
+  // The interface's IP address in the form of "xx.xx.xx.xx"
+  std::string ip_address_dot_decimal;
+  // IP address, in network byte order: the highest order byte of the address is
+  // in byte[0].
+  std::string ip_address_4_bytes;
+};
+
 // Represents a WiFi network found during a call to WifiMedium#scan().
 class WifiScanResult {
  public:
@@ -66,6 +99,12 @@
  public:
   virtual ~WifiMedium() {}
 
+  virtual bool IsInterfaceValid() const = 0;
+
+  virtual WifiCapability& GetCapability() = 0;
+
+  virtual WifiInformation& GetInformation() = 0;
+
   class ScanResultCallback {
    public:
     virtual ~ScanResultCallback() = default;
diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD
index b6a958a..bc47bab 100644
--- a/internal/platform/implementation/windows/BUILD
+++ b/internal/platform/implementation/windows/BUILD
@@ -73,6 +73,7 @@
     visibility = ["//visibility:private"],
     deps = [
         "//internal/platform:base",
+        "//internal/platform:comm",
         "//internal/platform:types",
         "//internal/platform/implementation:comm",
         "//internal/platform/implementation:types",
@@ -124,6 +125,7 @@
         "wifi_lan_medium.cc",
         "wifi_lan_server_socket.cc",
         "wifi_lan_socket.cc",
+        "wifi_medium.cc",
     ],
     copts = ["-Ithird_party/nearby/internal/platform/implementation/windows/generated"],
     defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
diff --git a/internal/platform/implementation/windows/generated/BUILD b/internal/platform/implementation/windows/generated/BUILD
index 441241c..d7049d6 100644
--- a/internal/platform/implementation/windows/generated/BUILD
+++ b/internal/platform/implementation/windows/generated/BUILD
@@ -32,6 +32,7 @@
         "comsuppwd.lib",
         "setupapi.lib",
         "dnsapi.lib",
+        "wlanapi.lib",
     ],
     textual_hdrs = glob(["**/*.h"]),
     visibility = [
diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc
index a74bf4d..6cda5c0 100644
--- a/internal/platform/implementation/windows/platform.cc
+++ b/internal/platform/implementation/windows/platform.cc
@@ -227,7 +227,7 @@
 
 // TODO(b/184975123): replace with real implementation.
 std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() {
-  return std::unique_ptr<WifiMedium>();
+  return std::make_unique<windows::WifiMedium>();
 }
 
 std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
diff --git a/internal/platform/implementation/windows/wifi.h b/internal/platform/implementation/windows/wifi.h
index 2fdfa27..366847d 100644
--- a/internal/platform/implementation/windows/wifi.h
+++ b/internal/platform/implementation/windows/wifi.h
@@ -15,12 +15,29 @@
 #ifndef PLATFORM_IMPL_WINDOWS_WIFI_H_
 #define PLATFORM_IMPL_WINDOWS_WIFI_H_
 
+#include <windows.h>
+#include <wlanapi.h>
+#include <objbase.h>
+#include <wtypes.h>
+
+// Nearby connections headers
 #include "internal/platform/implementation/wifi.h"
+#include "internal/platform/wifi_utils.h"
+
+// WinRT headers
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
+#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h"
 
 namespace location {
 namespace nearby {
 namespace windows {
 
+using ::winrt::Windows::Networking::HostName;
+using ::winrt::Windows::Networking::HostNameType;
+using ::winrt::Windows::Networking::Connectivity::ConnectionProfile;
+using ::winrt::Windows::Networking::Connectivity::NetworkInformation;
+
 // Represents a WiFi network found during a call to WifiMedium#scan().
 class WifiScanResult : public api::WifiScanResult {
  public:
@@ -46,9 +63,15 @@
 // Container of operations that can be performed over the WiFi medium.
 class WifiMedium : public api::WifiMedium {
  public:
-  // TODO(b/184975123): replace with real implementation.
+  WifiMedium();
   ~WifiMedium() override = default;
 
+  bool IsInterfaceValid() const override;
+
+  api::WifiCapability& GetCapability() override { return wifi_capability_; }
+
+  api::WifiInformation& GetInformation() override;
+
   class ScanResultCallback : public api::WifiMedium::ScanResultCallback {
    public:
     // TODO(b/184975123): replace with real implementation.
@@ -88,7 +111,16 @@
 
   // Returns the local device's IP address in the IPv4 dotted-quad format.
   // TODO(b/184975123): replace with real implementation.
-  std::string GetIpAddress() override { return "Un-implemented"; }
+  std::string GetIpAddress() override;
+
+ private:
+  // Since the WIFI interface capability won't change in the connection session,
+  // we only need to querry it once at the beginning
+  void InitCapability();
+
+  bool wifi_interface_valid_;
+  api::WifiCapability wifi_capability_;
+  api::WifiInformation wifi_information_;
 };
 
 }  // namespace windows
diff --git a/internal/platform/implementation/windows/wifi_medium.cc b/internal/platform/implementation/windows/wifi_medium.cc
new file mode 100644
index 0000000..5950ed7
--- /dev/null
+++ b/internal/platform/implementation/windows/wifi_medium.cc
@@ -0,0 +1,228 @@
+// Copyright 2022 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 "absl/strings/str_format.h"
+#include "internal/platform/implementation/windows/wifi.h"
+#include "internal/platform/implementation/windows/utils.h"
+#include "internal/platform/logging.h"
+
+namespace location {
+namespace nearby {
+namespace windows {
+
+namespace {
+  constexpr int kMacAddrLen = 6;
+  constexpr int kDefaultApFreq = -1;
+}  // namespace
+
+
+WifiMedium::WifiMedium() {
+  InitCapability();
+}
+
+PWLAN_INTERFACE_INFO_LIST EnumInterface(PHANDLE client_handle) {
+  DWORD client_version = 2;
+  DWORD negotiated_version = 0;
+  DWORD result = 0;
+
+  /* variables used for WlanEnumInterfaces */
+  PWLAN_INTERFACE_INFO_LIST p_intf_list = NULL;
+
+  result =
+      WlanOpenHandle(client_version, NULL, &negotiated_version, client_handle);
+  if (result != ERROR_SUCCESS) {
+    NEARBY_LOGS(INFO) << "WlanOpenHandle failed with error: " << result;
+    return p_intf_list;
+  }
+
+  result = WlanEnumInterfaces(*client_handle, NULL, &p_intf_list);
+  if (result != ERROR_SUCCESS) {
+    NEARBY_LOGS(INFO) << "WlanEnumInterfaces failed with error: " << result;
+  }
+  return p_intf_list;
+}
+
+bool WifiMedium::IsInterfaceValid() const {
+  return wifi_interface_valid_;
+}
+
+void WifiMedium::InitCapability() {
+  HANDLE client_handle = NULL;
+
+  /* variables used for WlanEnumInterfaces */
+  PWLAN_INTERFACE_INFO_LIST p_intf_list = NULL;
+  PWLAN_INTERFACE_INFO p_intf_info = NULL;
+  PWLAN_INTERFACE_CAPABILITY p_intf_capability = NULL;
+
+  wifi_capability_.supports_5_ghz = false;
+  wifi_capability_.supports_6_ghz = false;
+  wifi_interface_valid_ = false;
+
+  p_intf_list = EnumInterface(&client_handle);
+  if (!client_handle) {
+    NEARBY_LOGS(INFO) << "Client Handle is NULL";
+    return;
+  }
+  if (!p_intf_list) {
+    NEARBY_LOGS(INFO) << "WlanEnumInterfaces failed with error: ";
+    return;
+  }
+  wifi_interface_valid_ = true;
+
+  for (int i = 0; i < (int)p_intf_list->dwNumberOfItems; i++) {
+    p_intf_info = (WLAN_INTERFACE_INFO*)&p_intf_list->InterfaceInfo[i];
+    if (WlanGetInterfaceCapability(client_handle, &p_intf_info->InterfaceGuid,
+                                   NULL, &p_intf_capability) != ERROR_SUCCESS) {
+      NEARBY_LOGS(INFO) << "Get Capability failed";
+      WlanFreeMemory(p_intf_list);
+      p_intf_list = NULL;
+      return;
+    }
+
+    for (int j = 0; j < p_intf_capability->dwNumberOfSupportedPhys; j++) {
+      if (p_intf_capability->dot11PhyTypes[j] == dot11_phy_type_ofdm)
+        wifi_capability_.supports_5_ghz = true;
+    }
+  }
+
+  WlanFreeMemory(p_intf_capability);
+  p_intf_capability = NULL;
+  WlanFreeMemory(p_intf_list);
+  p_intf_list = NULL;
+}
+
+api::WifiInformation& WifiMedium::GetInformation() {
+  HANDLE client_handle = NULL;
+  PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = NULL;
+  DWORD result = 0;
+  DWORD connect_info_size = sizeof(WLAN_CONNECTION_ATTRIBUTES);
+  WLAN_OPCODE_VALUE_TYPE op_code_value_type = wlan_opcode_value_type_invalid;
+
+  /* variables used for WlanEnumInterfaces */
+  PWLAN_INTERFACE_INFO_LIST p_intf_list = NULL;
+  PWLAN_INTERFACE_INFO p_intf_info = NULL;
+  PWLAN_CONNECTION_ATTRIBUTES p_connect_info = NULL;
+  ULONG* channel = NULL;
+
+  wifi_information_.is_connected = false;
+  wifi_information_.ap_frequency = kDefaultApFreq;
+
+  p_intf_list = EnumInterface(&client_handle);
+  if (!client_handle) {
+    NEARBY_LOGS(INFO) << "Client Handle is NULL";
+    return wifi_information_;
+  }
+  if (!p_intf_list) {
+    NEARBY_LOGS(INFO) << "WlanEnumInterfaces failed with error: ";
+    return wifi_information_;
+  }
+
+  for (int i = 0; i < (int)p_intf_list->dwNumberOfItems; i++) {
+    p_intf_info = (WLAN_INTERFACE_INFO*)&p_intf_list->InterfaceInfo[i];
+    if (p_intf_info->isState == wlan_interface_state_connected) {
+      NEARBY_LOGS(INFO) << "Found connected WIFI interface No: " << i;
+      wifi_information_.is_connected = true;
+
+      DWORD channel_size;
+      result = WlanQueryInterface(client_handle, &p_intf_info->InterfaceGuid,
+                                  wlan_intf_opcode_channel_number, NULL,
+                                  &channel_size, (PVOID*)&channel,
+                                  &op_code_value_type);
+      if (result != ERROR_SUCCESS) {
+        NEARBY_LOGS(INFO) << "WlanQueryInterface error = " << result;
+      } else {
+        wifi_information_.ap_frequency =
+            WifiUtils::ConvertChannelToFrequencyMhz(
+                *channel, api::WifiBandType::kUnknown);
+        NEARBY_LOGS(INFO) << "Frequency: " << *channel << "; ap_frequency: "
+                          << wifi_information_.ap_frequency;
+        WlanFreeMemory(channel);
+        channel = NULL;
+      }
+
+      result = WlanQueryInterface(client_handle, &p_intf_info->InterfaceGuid,
+                                  wlan_intf_opcode_current_connection, NULL,
+                                  &connect_info_size, (PVOID*)&p_connect_info,
+                                  &op_code_value_type);
+      if (result != ERROR_SUCCESS) {
+        NEARBY_LOGS(INFO) << "WlanQueryInterface error = " << result;
+        WlanFreeMemory(p_intf_list);
+        p_intf_list = NULL;
+        return wifi_information_;
+      }
+
+      wifi_information_.ssid.resize(
+          p_connect_info->wlanAssociationAttributes.dot11Ssid.uSSIDLength);
+      std::memcpy(
+          wifi_information_.ssid.data(),
+          reinterpret_cast<const char*>(
+              p_connect_info->wlanAssociationAttributes.dot11Ssid.ucSSID),
+          wifi_information_.ssid.size());
+      NEARBY_LOGS(INFO) << "wifi ssid is: " << wifi_information_.ssid
+                        << "; length is:" << wifi_information_.ssid.length();
+
+      char str_tmp[kMacAddrLen];
+      strncpy(str_tmp,
+              reinterpret_cast<const char*>(
+                  p_connect_info->wlanAssociationAttributes.dot11Bssid),
+              kMacAddrLen);
+      wifi_information_.bssid = absl::StrFormat(
+          "%2llx:%2llx:%2llx:%2llx:%2llx:%2llx", str_tmp[0], str_tmp[1],
+          str_tmp[2], str_tmp[3], str_tmp[4], str_tmp[5]);
+      NEARBY_LOGS(INFO) << "wifi bssid is: " << wifi_information_.bssid;
+    }
+  }
+
+  WlanFreeMemory(p_connect_info);
+  p_connect_info = NULL;
+  WlanFreeMemory(p_intf_list);
+  p_intf_list = NULL;
+
+  wifi_information_.ip_address_dot_decimal = GetIpAddress();
+  wifi_information_.ip_address_4_bytes = ipaddr_dotdecimal_to_4bytes_string(
+      wifi_information_.ip_address_dot_decimal);
+
+  return wifi_information_;
+}
+
+std::string WifiMedium::GetIpAddress() {
+  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());
+      NEARBY_LOGS(INFO) << "Found IP: " << ipv4_s;
+
+      auto profile = host_name.IPInformation()
+                         .NetworkAdapter()
+                         .GetConnectedProfileAsync()
+                         .get();
+      if (profile.IsWlanConnectionProfile()) {
+        if ( wifi_information_.ssid == winrt::to_string(
+                   profile.WlanConnectionProfileDetails().GetConnectedSsid())) {
+          NEARBY_LOGS(INFO)
+              << "SSID of this IP matches with this WIFI interface's SSID:"
+              << wifi_information_.ssid << ", return this IP";
+          return ipv4_s;
+        }
+      }
+    }
+  }
+  return {};
+}
+
+}  // namespace windows
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/wifi.h b/internal/platform/wifi.h
new file mode 100644
index 0000000..d1d0785
--- /dev/null
+++ b/internal/platform/wifi.h
@@ -0,0 +1,76 @@
+// Copyright 2022 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 <string>
+
+#include "internal/platform/implementation/platform.h"
+#include "internal/platform/implementation/wifi.h"
+
+
+#ifndef PLATFORM_PUBLIC_WIFI_H_
+#define PLATFORM_PUBLIC_WIFI_H_
+
+
+
+namespace location {
+namespace nearby {
+
+class WifiMedium {
+ public:
+  using Platform = api::ImplementationPlatform;
+
+  WifiMedium() : impl_(Platform::CreateWifiMedium()) {}
+  ~WifiMedium() = default;
+
+  api::WifiCapability& GetCapability() {
+    CHECK(impl_);
+    return impl_->GetCapability();
+  }
+
+  api::WifiInformation& GetInformation() {
+    CHECK(impl_);
+    return impl_->GetInformation();
+  }
+
+  bool IsInterfaceValid() const {
+    CHECK(impl_);
+    return impl_->IsInterfaceValid();
+  }
+
+  bool VerifyInternetConnectivity() {
+    CHECK(impl_);
+    return impl_->VerifyInternetConnectivity();
+  }
+
+  std::string GetIpAddress() const {
+    CHECK(impl_);
+    return impl_->GetIpAddress();
+  }
+
+  bool IsValid() const { return impl_ != nullptr; }
+
+  api::WifiMedium& GetImpl() {
+    CHECK(impl_);
+    return *impl_;
+  }
+
+ private:
+  // Mutex mutex_;
+  std::unique_ptr<api::WifiMedium> impl_;
+};
+
+}  // namespace nearby
+}  // namespace location
+
+#endif  // PLATFORM_PUBLIC_WIFI_H_
diff --git a/internal/platform/wifi_test.cc b/internal/platform/wifi_test.cc
new file mode 100644
index 0000000..2ed1107
--- /dev/null
+++ b/internal/platform/wifi_test.cc
@@ -0,0 +1,46 @@
+// Copyright 2022 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/wifi.h"
+
+#include "gmock/gmock.h"
+#include "protobuf-matchers/protocol-buffer-matchers.h"
+#include "gtest/gtest.h"
+
+namespace location {
+namespace nearby {
+namespace {
+
+TEST(WifiMediumTest, ConstructorDestructorWorks) {
+  WifiMedium wifi_a;
+  WifiMedium wifi_b;
+
+  if (wifi_a.IsValid() && wifi_b.IsValid()) {
+    // Make sure we can create 2 distinct mediums.
+    EXPECT_NE(&wifi_a.GetImpl(), &wifi_a.GetImpl());
+  }
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+TEST(WifiMediumTest, CanGetCapability) {
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+TEST(WifiMediumTest, CanInformation) {
+  // TODO(b/233324423): Add test coverage for wifi.h
+}
+
+}  // namespace
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/wifi_utils.cc b/internal/platform/wifi_utils.cc
new file mode 100644
index 0000000..2f99312
--- /dev/null
+++ b/internal/platform/wifi_utils.cc
@@ -0,0 +1,96 @@
+// Copyright 2022 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/wifi_utils.h"
+
+namespace location {
+namespace nearby {
+
+// Utility function to convert channel number to frequency in MHz
+// @param channel to convert
+// @return center frequency in Mhz of the channel, return "kUnspecified" if no
+// match
+// Add band support later
+int WifiUtils::ConvertChannelToFrequencyMhz(int channel, WifiBandType band) {
+  if (band == WifiBandType::kUnknown || band == WifiBandType::kBand24Ghz ||
+      band == WifiBandType::kBand5Ghz) {
+    if (channel == 14) {
+      return 2484;
+    } else if (channel >= kBand24GhzFirstChNum &&
+               channel <= kBand24GhzLastChNum) {
+      return ((channel - kBand24GhzFirstChNum) * 5) + kBand24GhzStartFreqMhz;
+    } else if (channel >= kBand5GhzFirstChNum &&
+               channel <= kBand5GhzLastChNum) {
+      return ((channel - kBand5GhzFirstChNum) * 5) + kBand5GhzStartFreqMhz;
+    } else {
+      return kUnspecified;
+    }
+  }
+
+  if (band == WifiBandType::kBand6Ghz) {
+    if (channel >= kBand6GhzFirstChNum &&
+        channel <= kBand6GhzLastChNum) {
+      if (channel == 2) {
+        return kBand6GhzOpClass136Ch2FreqMhz;
+      }
+      return ((channel - kBand6GhzFirstChNum) * 5) +
+             kBand6GhzStartFreqMhz;
+    } else {
+      return kUnspecified;
+    }
+  }
+
+  if (band == WifiBandType::kBand60Ghz) {
+    if (channel >= kBand60GhzFirstChNum &&
+        channel <= kBand60GhzLastChNum) {
+      return ((channel - kBand60GhzFirstChNum) * 2160) +
+             kBand60GhzStartFreqMhz;
+    } else {
+      return kUnspecified;
+    }
+  }
+
+  return kUnspecified;
+}
+
+// Utility function to convert frequency in MHz to channel number
+// @param freqMhz frequency in MHz
+// @return channel number associated with given frequency, return "kUnspecified"
+// if no match
+int WifiUtils::ConvertFrequencyMhzToChannel(int freq_mhz) {
+  // Special case
+  if (freq_mhz == kBand24GhzEndFreqMhz) {
+    return 14;
+  } else if (freq_mhz >= kBand24GhzStartFreqMhz &&
+             freq_mhz <= kBand24GhzEndFreqMhz) {
+    return (freq_mhz - kBand24GhzStartFreqMhz) / 5 + kBand24GhzFirstChNum;
+  } else if (freq_mhz >= kBand5GhzStartFreqMhz &&
+             freq_mhz <= kBand5GhzEndFreqMhz) {
+    return (freq_mhz - kBand5GhzStartFreqMhz) / 5 + kBand5GhzFirstChNum;
+  } else if (freq_mhz >= kBand6GhzStartFreqMhz &&
+             freq_mhz <= kBand6GhzEndFreqMhz) {
+    if (freq_mhz == kBand6GhzOpClass136Ch2FreqMhz) {
+      return 2;
+    }
+    return (freq_mhz - kBand6GhzStartFreqMhz) / 5 + kBand6GhzFirstChNum;
+  } else if (freq_mhz >= kBand60GhzStartFreqMhz &&
+             freq_mhz <= kBand60GhzEndFreqMhz) {
+    return (freq_mhz - kBand60GhzStartFreqMhz) / 2160 + kBand60GhzFirstChNum;
+  }
+
+  return kUnspecified;
+}
+
+}  // namespace nearby
+}  // namespace location
diff --git a/internal/platform/wifi_utils.h b/internal/platform/wifi_utils.h
new file mode 100644
index 0000000..a7b9045
--- /dev/null
+++ b/internal/platform/wifi_utils.h
@@ -0,0 +1,55 @@
+// Copyright 2022 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_BASE_WIFI_UTILS_H_
+#define PLATFORM_BASE_WIFI_UTILS_H_
+
+#include "internal/platform/implementation/wifi.h"
+
+namespace location {
+namespace nearby {
+
+using location::nearby::api::WifiBandType;
+
+class WifiUtils {
+ public:
+  static constexpr int kUnspecified = -1;
+  static constexpr int kBand24GhzFirstChNum = 1;
+  static constexpr int kBand24GhzLastChNum = 14;
+  static constexpr int kBand24GhzStartFreqMhz = 2412;
+  static constexpr int kBand24GhzEndFreqMhz = 2484;
+  static constexpr int kBand5GhzFirstChNum = 32;
+  static constexpr int kBand5GhzLastChNum = 177;
+  static constexpr int kBand5GhzStartFreqMhz = 5160;
+  static constexpr int kBand5GhzEndFreqMhz = 5885;
+  static constexpr int kBand6GhzFirstChNum = 1;
+  static constexpr int kBand6GhzLastChNum = 233;
+  static constexpr int kBand6GhzStartFreqMhz = 5955;
+  static constexpr int kBand6GhzEndFreqMhz = 7115;
+  static constexpr int kBand6GhzPscStartMhz = 5975;
+  static constexpr int kBand6GhzPscStepSizeMhz = 80;
+  static constexpr int kBand6GhzOpClass136Ch2FreqMhz = 5935;
+  static constexpr int kBand60GhzFirstChNum = 1;
+  static constexpr int kBand60GhzLastChNum = 6;
+  static constexpr int kBand60GhzStartFreqMhz = 58320;
+  static constexpr int kBand60GhzEndFreqMhz = 70200;
+
+  static int ConvertChannelToFrequencyMhz(int channel, WifiBandType band_type);
+  static int ConvertFrequencyMhzToChannel(int freq_mhz);
+};
+
+}  // namespace nearby
+}  // namespace location
+
+#endif  // PLATFORM_BASE_WIFI_UTILS_H_
diff --git a/internal/platform/wifi_utils_test.cc b/internal/platform/wifi_utils_test.cc
new file mode 100644
index 0000000..04cc392
--- /dev/null
+++ b/internal/platform/wifi_utils_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2022 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/wifi_utils.h"
+
+#include "gmock/gmock.h"
+#include "protobuf-matchers/protocol-buffer-matchers.h"
+#include "gtest/gtest.h"
+
+namespace location {
+namespace nearby {
+namespace {
+
+constexpr int kChan6Num_2G = 9;
+constexpr int kChan6NumFreq_2G = 2452;
+
+constexpr int kChan48Num_5G = 48;
+constexpr int kChan48NumFreq_5G = 5240;
+
+constexpr int kChan69Num_6G = 69;
+constexpr int kChan69NumFreq_6G = 6295;
+
+constexpr int kChan4Num_60G = 4;
+constexpr int kChan4NumFreq_60G = 64800;
+
+constexpr int kChan20Num_2G_NotExist = 20;
+constexpr int kChan180Num_5G_NotExist = 180;
+constexpr int kChan0Num_6G_NotExist = 0;
+constexpr int kChan30Num_60G_NotExist = 30;
+
+constexpr int kFreqNotExist = 1002;
+
+
+
+TEST(WifiUtilsTest, ConvertChannelToFrequency) {
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan6Num_2G,
+                                                    WifiBandType::kUnknown),
+            kChan6NumFreq_2G);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan6Num_2G,
+                                                    WifiBandType::kBand24Ghz),
+            kChan6NumFreq_2G);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan48Num_5G,
+                                                    WifiBandType::kBand5Ghz),
+            kChan48NumFreq_5G);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan69Num_6G,
+                                                    WifiBandType::kBand6Ghz),
+            kChan69NumFreq_6G);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan4Num_60G,
+                                                    WifiBandType::kBand60Ghz),
+            kChan4NumFreq_60G);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan20Num_2G_NotExist,
+                                                    WifiBandType::kBand24Ghz),
+            WifiUtils::kUnspecified);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan180Num_5G_NotExist,
+                                                    WifiBandType::kBand5Ghz),
+            WifiUtils::kUnspecified);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan0Num_6G_NotExist,
+                                                    WifiBandType::kBand6Ghz),
+            WifiUtils::kUnspecified);
+  EXPECT_EQ(WifiUtils::ConvertChannelToFrequencyMhz(kChan30Num_60G_NotExist,
+                                                    WifiBandType::kBand60Ghz),
+            WifiUtils::kUnspecified);}
+
+TEST(WifiUtilsTest, ConvertFrequencyToChannel) {
+  EXPECT_EQ(WifiUtils::ConvertFrequencyMhzToChannel(kChan6NumFreq_2G),
+            kChan6Num_2G);
+  EXPECT_EQ(WifiUtils::ConvertFrequencyMhzToChannel(kChan48NumFreq_5G),
+            kChan48Num_5G);
+  EXPECT_EQ(WifiUtils::ConvertFrequencyMhzToChannel(kChan69NumFreq_6G),
+            kChan69Num_6G);
+  EXPECT_EQ(WifiUtils::ConvertFrequencyMhzToChannel(kChan4NumFreq_60G),
+            kChan4Num_60G);
+  EXPECT_EQ(WifiUtils::ConvertFrequencyMhzToChannel(kFreqNotExist),
+            WifiUtils::kUnspecified);
+}
+
+}  // namespace
+}  // namespace nearby
+}  // namespace location
+