[BLE Refactor] Implement CreateAdvertisementHeader in BleV2 medium.

PiperOrigin-RevId: 429550598
diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD
index 3784a44..95c21cb 100644
--- a/connections/implementation/mediums/BUILD
+++ b/connections/implementation/mediums/BUILD
@@ -17,6 +17,7 @@
     name = "mediums",
     srcs = [
         "ble.cc",
+        "ble_v2.cc",
         "bluetooth_classic.cc",
         "bluetooth_radio.cc",
         "mediums.cc",
@@ -26,6 +27,7 @@
     ],
     hdrs = [
         "ble.h",
+        "ble_v2.h",
         "bluetooth_classic.h",
         "bluetooth_radio.h",
         "lost_entity_tracker.h",
diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc
new file mode 100644
index 0000000..f22a93f
--- /dev/null
+++ b/connections/implementation/mediums/ble_v2.cc
@@ -0,0 +1,84 @@
+// 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/ble_v2.h"
+
+#include <string>
+#include <string_view>
+
+#include "absl/strings/str_cat.h"
+#include "connections/implementation/mediums/ble_v2/ble_advertisement.h"
+#include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h"
+#include "connections/implementation/mediums/ble_v2/bloom_filter.h"
+#include "connections/implementation/mediums/utils.h"
+#include "internal/platform/prng.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+namespace {
+
+constexpr int kDummyServiceIdLength = 128;
+
+ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) {
+  return Utils::Sha256Hash(
+      advertisement_bytes,
+      mediums::BleAdvertisementHeader::kAdvertisementHashLength);
+}
+
+}  // namespace
+
+BleV2::BleV2(BluetoothRadio& radio)
+    : radio_(radio), adapter_(radio_.GetBluetoothAdapter()) {}
+
+ByteArray BleV2::CreateAdvertisementHeader() {
+  // Create a randomized dummy service id to anonymize the header with.
+  ByteArray dummy_service_id_bytes =
+      Utils::GenerateRandomBytes(kDummyServiceIdLength);
+  std::string dummy_service_id{dummy_service_id_bytes};
+
+  mediums::BloomFilter<
+      mediums::BleAdvertisementHeader::kServiceIdBloomFilterLength>
+      bloom_filter;
+  bloom_filter.Add(dummy_service_id);
+
+  ByteArray advertisement_hash =
+      GenerateAdvertisementHash(dummy_service_id_bytes);
+  for (const auto& item : gatt_advertisements_) {
+    const std::string& service_id = item.second.first;
+    const ByteArray& gatt_advertisement = item.second.second;
+    bloom_filter.Add(service_id);
+
+    // Compute the next hash according to the algorithm in
+    // https://source.corp.google.com/piper///depot/google3/java/com/google/android/gmscore/integ/modules/nearby/src/com/google/android/gms/nearby/mediums/bluetooth/BluetoothLowEnergy.java;rcl=428397891;l=1043
+    ByteArray previous_advertisement_hash = advertisement_hash;
+    std::string advertisement_bodies =
+        absl::StrCat(previous_advertisement_hash.AsStringView(),
+                     gatt_advertisement.AsStringView());
+
+    advertisement_hash =
+        GenerateAdvertisementHash(ByteArray(std::move(advertisement_bodies)));
+  }
+
+  return ByteArray(mediums::BleAdvertisementHeader(
+      mediums::BleAdvertisementHeader::Version::kV2,
+      /*extended_advertisement=*/false,
+      /*num_slots=*/gatt_advertisements_.size(), ByteArray(bloom_filter),
+      advertisement_hash, /*psm=*/0));
+}
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h
new file mode 100644
index 0000000..2edc01b
--- /dev/null
+++ b/connections/implementation/mediums/ble_v2.h
@@ -0,0 +1,52 @@
+// 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_BLE_V2_H_
+#define CORE_INTERNAL_MEDIUMS_BLE_V2_H_
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "absl/container/flat_hash_map.h"
+#include "connections/implementation/mediums/bluetooth_radio.h"
+#include "internal/platform/byte_array.h"
+#include "internal/platform/mutex.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+// TODO(b/213691253): Add BleV2 medium tests after more functions are ready.
+//  Provides the operations that can be performed on the Bluetooth Low Energy
+//  (BLE) medium.
+class BleV2 {
+ public:
+  explicit BleV2(BluetoothRadio& bluetooth_radio);
+
+ private:
+  ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_);
+
+  mutable Mutex mutex_;
+  BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_);
+  BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_);
+  absl::flat_hash_map<int, std::pair<std::string, ByteArray>>
+      gatt_advertisements_ ABSL_GUARDED_BY(mutex_);
+};
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CORE_INTERNAL_MEDIUMS_BLE_V2_H_
diff --git a/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc b/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc
index a1379c1..30b440c 100644
--- a/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc
+++ b/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc
@@ -26,6 +26,10 @@
 namespace connections {
 namespace mediums {
 
+// These definitions are necessary before C++17.
+constexpr int BleAdvertisementHeader::kAdvertisementHashLength;
+constexpr int BleAdvertisementHeader::kServiceIdBloomFilterLength;
+
 BleAdvertisementHeader::BleAdvertisementHeader(
     Version version, bool extended_advertisement, int num_slots,
     const ByteArray &service_id_bloom_filter,
diff --git a/connections/implementation/mediums/ble_v2/ble_advertisement_header.h b/connections/implementation/mediums/ble_v2/ble_advertisement_header.h
index 24dd358..b3f8355 100644
--- a/connections/implementation/mediums/ble_v2/ble_advertisement_header.h
+++ b/connections/implementation/mediums/ble_v2/ble_advertisement_header.h
@@ -51,6 +51,9 @@
     // characteristic so the two are not compatible.
   };
 
+  static constexpr int kAdvertisementHashLength = 4;
+  static constexpr int kServiceIdBloomFilterLength = 10;
+
   BleAdvertisementHeader() = default;
   BleAdvertisementHeader(Version version, bool extended_advertisement,
                          int num_slots,
@@ -76,8 +79,6 @@
 
  private:
   static constexpr int kVersionAndNumSlotsLength = 1;
-  static constexpr int kServiceIdBloomFilterLength = 10;
-  static constexpr int kAdvertisementHashLength = 4;
   static constexpr int kMinAdvertisementHeaderLength =
       kVersionAndNumSlotsLength + kServiceIdBloomFilterLength +
       kAdvertisementHashLength;
diff --git a/cpp/core/internal/mediums/BUILD b/cpp/core/internal/mediums/BUILD
new file mode 100644
index 0000000..9167825
--- /dev/null
+++ b/cpp/core/internal/mediums/BUILD
@@ -0,0 +1,14 @@
+# 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.
+licenses(["notice"])
diff --git a/cpp/core/internal/mediums/ble_v2.cc b/cpp/core/internal/mediums/ble_v2.cc
new file mode 100644
index 0000000..a1e0f5a
--- /dev/null
+++ b/cpp/core/internal/mediums/ble_v2.cc
@@ -0,0 +1,90 @@
+// 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 "third_party/nearby/cpp/core/internal/mediums/ble_v2.h"
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement.h"
+#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h"
+#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/bloom_filter.h"
+#include "third_party/nearby/cpp/core/internal/mediums/utils.h"
+#include "third_party/nearby/cpp/platform/base/prng.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+ByteArray BleV2::GenerateAdvertisementHash(
+    const ByteArray& advertisement_bytes) {
+  return Utils::Sha256Hash(
+      advertisement_bytes,
+      mediums::BleAdvertisementHeader::kAdvertisementHashLength);
+}
+
+ByteArray BleV2::GenerateHash(const std::string& source, size_t size) {
+  return Utils::Sha256Hash(source, size);
+}
+
+ByteArray BleV2::GenerateDeviceToken() {
+  return Utils::Sha256Hash(std::to_string(Prng().NextUint32()),
+                           mediums::BleAdvertisement::kDeviceTokenLength);
+}
+
+BleV2::BleV2(BluetoothRadio& radio) : radio_(radio) {}
+
+ByteArray BleV2::CreateAdvertisementHeader() {
+  // Create a randomized dummy service id to anonymize the header with.
+  ByteArray dummy_service_id_bytes =
+      Utils::GenerateRandomBytes(kDummyServiceIdLength);
+  std::string dummy_service_id{dummy_service_id_bytes};
+
+  // Create a bloom filter with the dummy service id.
+  mediums::BloomFilter<
+      mediums::BleAdvertisementHeader::kServiceIdBloomFilterLength>
+      bloom_filter;
+  bloom_filter.Add(dummy_service_id);
+
+  // Add the service id for each GATT advertisement into the bloom filter, while
+  // also creating a hash of dummyServiceIdBytes + all GATT advertisements.
+  ByteArray advertisement_hash =
+      GenerateAdvertisementHash(dummy_service_id_bytes);
+  int num_slots = 0;
+  for (const auto& item : gatt_advertisement_info.gatt_advertisements) {
+    std::string service_id = item.second.first;
+    ByteArray gatt_advertisment = item.second.second;
+    bloom_filter.Add(service_id);
+
+    // Compute the next hash by taking the hash of [previous hash] + [next
+    // advertisement data].
+    ByteArray previous_advertisement_hash = advertisement_hash;
+    std::string advertisement_bodies =
+        absl::StrCat(std::string(previous_advertisement_hash),
+                     std::string(gatt_advertisment));
+
+    advertisement_hash =
+        GenerateAdvertisementHash(ByteArray{std::move(advertisement_bodies)});
+    num_slots++;
+  }
+
+  return ByteArray(mediums::BleAdvertisementHeader(
+      mediums::BleAdvertisementHeader::Version::kV2,
+      /*extended_advertisement=*/false, num_slots, ByteArray(bloom_filter),
+      advertisement_hash, /*psm=*/0));
+}
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/cpp/core/internal/mediums/ble_v2.h b/cpp/core/internal/mediums/ble_v2.h
new file mode 100644
index 0000000..ac40080
--- /dev/null
+++ b/cpp/core/internal/mediums/ble_v2.h
@@ -0,0 +1,82 @@
+// 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_BLE_V2_H_
+#define CORE_INTERNAL_MEDIUMS_BLE_V2_H_
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "absl/container/flat_hash_map.h"
+#include "third_party/nearby/cpp/core/internal/mediums/bluetooth_radio.h"
+#include "third_party/nearby/cpp/platform/base/byte_array.h"
+#include "third_party/nearby/cpp/platform/public/mutex.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+class BleV2 {
+ public:
+  explicit BleV2(BluetoothRadio& bluetooth_radio);
+  ~BleV2() = default;
+
+ private:
+  // Container that stores info about a GATT advertisement.
+  struct GattAdvertisementInfo {
+    bool Empty() const { return gatt_advertisements.empty(); }
+    void Clear() { gatt_advertisements.clear(); }
+    void Add(int slot, std::pair<std::string, ByteArray> gatt_advertisement) {
+      gatt_advertisements.insert({slot, gatt_advertisement});
+    }
+    void Remove(int slot) { gatt_advertisements.erase(slot); }
+    bool Existed(int slot) const {
+      return gatt_advertisements.contains(slot);
+    }
+    std::pair<std::string, ByteArray> GetAdvertisement(int slot) {
+      const auto& it = gatt_advertisements.find(slot);
+      if (it == gatt_advertisements.end()) {
+        return {};
+      }
+      return it->second;
+    }
+
+    // A map of slot -> pair of {service_id, gatt_advertisement}
+    absl::flat_hash_map<int, std::pair<std::string, ByteArray>>
+        gatt_advertisements;
+  };
+
+  static constexpr int kMaxAdvertisementLength = 512;
+  static constexpr int kDummyServiceIdLength = 128;
+
+  static ByteArray GenerateAdvertisementHash(
+      const ByteArray& advertisement_bytes);
+  static ByteArray GenerateHash(const std::string& source, size_t size);
+  static ByteArray GenerateDeviceToken();
+
+  ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_);
+
+  mutable Mutex mutex_;
+  BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_);
+  BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){
+      radio_.GetBluetoothAdapter()};
+  GattAdvertisementInfo gatt_advertisement_info ABSL_GUARDED_BY(mutex_);
+};
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CORE_INTERNAL_MEDIUMS_BLE_V2_H_
diff --git a/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc
new file mode 100644
index 0000000..3f12355
--- /dev/null
+++ b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc
@@ -0,0 +1,142 @@
+// 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.
+
+#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h"
+
+#include <inttypes.h>
+
+#include "absl/strings/str_cat.h"
+#include "third_party/nearby/cpp/platform/base/base64_utils.h"
+#include "third_party/nearby/cpp/platform/base/base_input_stream.h"
+#include "third_party/nearby/cpp/platform/public/logging.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+namespace mediums {
+
+// These definitions are necessary before C++17.
+constexpr int BleAdvertisementHeader::kAdvertisementHashLength;
+constexpr int BleAdvertisementHeader::kServiceIdBloomFilterLength;
+
+BleAdvertisementHeader::BleAdvertisementHeader(
+    Version version, bool extended_advertisement, int num_slots,
+    const ByteArray &service_id_bloom_filter,
+    const ByteArray &advertisement_hash, int psm) {
+  if (version != Version::kV2 || num_slots <= 0 ||
+      service_id_bloom_filter.size() != kServiceIdBloomFilterLength ||
+      advertisement_hash.size() != kAdvertisementHashLength) {
+    return;
+  }
+
+  version_ = version;
+  extended_advertisement_ = extended_advertisement;
+  num_slots_ = num_slots;
+  service_id_bloom_filter_ = service_id_bloom_filter;
+  advertisement_hash_ = advertisement_hash;
+  psm_ = psm;
+}
+
+BleAdvertisementHeader::BleAdvertisementHeader(
+    const ByteArray &ble_advertisement_header_bytes) {
+  if (ble_advertisement_header_bytes.Empty()) {
+    NEARBY_LOG(
+        ERROR,
+        "Cannot deserialize BLEAdvertisementHeader: failed Base64 decoding");
+    return;
+  }
+
+  if (ble_advertisement_header_bytes.size() < kMinAdvertisementHeaderLength) {
+    NEARBY_LOG(ERROR,
+               "Cannot deserialize BleAdvertisementHeader: expecting min %u "
+               "raw bytes, got %" PRIu64 " instead",
+               kMinAdvertisementHeaderLength,
+               ble_advertisement_header_bytes.size());
+    return;
+  }
+
+  ByteArray advertisement_header_bytes{ble_advertisement_header_bytes};
+  BaseInputStream base_input_stream{advertisement_header_bytes};
+  // The first 1 byte is supposed to be the version and number of slots.
+  auto version_and_num_slots_byte =
+      static_cast<char>(base_input_stream.ReadUint8());
+  // The upper 3 bits are supposed to be the version.
+  version_ =
+      static_cast<Version>((version_and_num_slots_byte & kVersionBitmask) >> 5);
+  if (version_ != Version::kV2) {
+    NEARBY_LOG(
+        ERROR,
+        "Cannot deserialize BleAdvertisementHeader: unsupported Version %d",
+        version_);
+    return;
+  }
+  // The next 1 bit is supposed to be the extended advertisement flag.
+  extended_advertisement_ =
+      ((version_and_num_slots_byte & kExtendedAdvertismentBitMask) >> 4) == 1;
+  // The lower 4 bits are supposed to be the number of slots.
+  num_slots_ = static_cast<int>(version_and_num_slots_byte & kNumSlotsBitmask);
+  if (num_slots_ <= 0) {
+    version_ = Version::kUndefined;
+    return;
+  }
+
+  // The next 10 bytes are supposed to be the service_id_bloom_filter.
+  service_id_bloom_filter_ =
+      base_input_stream.ReadBytes(kServiceIdBloomFilterLength);
+
+  // The next 4 bytes are supposed to be the advertisement_hash.
+  advertisement_hash_ = base_input_stream.ReadBytes(kAdvertisementHashLength);
+
+  // The next 2 bytes are PSM value.
+  if (base_input_stream.IsAvailable(sizeof(std::uint16_t))) {
+    psm_ = static_cast<int>(base_input_stream.ReadUint16());
+  }
+}
+
+BleAdvertisementHeader::operator ByteArray() const {
+  if (!IsValid()) {
+    return ByteArray();
+  }
+
+  // The first 3 bits are the Version.
+  char version_and_num_slots_byte =
+      (static_cast<char>(version_) << 5) & kVersionBitmask;
+  // The next 1 bit is extended advertisement flag.
+  version_and_num_slots_byte |=
+      (static_cast<char>(extended_advertisement_) << 4) &
+      kExtendedAdvertismentBitMask;
+  // The next 5 bits are the number of slots.
+  version_and_num_slots_byte |=
+      static_cast<char>(num_slots_) & kNumSlotsBitmask;
+
+  // Convert psm_ value to 2-bytes.
+  ByteArray psm_byte{sizeof(std::uint16_t)};
+  char *data = psm_byte.data();
+  data[0] = psm_ & 0xFF00;
+  data[1] = psm_ & 0x00FF;
+
+  // clang-format off
+  std::string out = absl::StrCat(std::string(1, version_and_num_slots_byte),
+                                 std::string(service_id_bloom_filter_),
+                                 std::string(advertisement_hash_),
+                                 std::string(psm_byte));
+  // clang-format on
+
+  return ByteArray(std::move(out));
+}
+
+}  // namespace mediums
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h
new file mode 100644
index 0000000..15ba4aa
--- /dev/null
+++ b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h
@@ -0,0 +1,102 @@
+// 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 CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
+#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
+
+#include <string>
+
+#include "third_party/nearby/cpp/platform/base/byte_array.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+namespace mediums {
+
+// Represents the format of the Mediums BLE Advertisement Header used in
+// Advertising + Discovery.
+//
+// [VERSION][NUM_SLOTS][SERVICE_ID_BLOOM_FILTER][ADVERTISEMENT_HASH][L2_CAP_PSM]
+//
+// See go/nearby-ble-design for more information.
+//
+// Note. The object constructed by default constructor or the parameterized
+// constructor with invalid value(s) is treated as invalid instance. Caller
+// should be responsible to call IsValid() to check the instance is invalid in
+// advance before continue on.
+class BleAdvertisementHeader {
+ public:
+  // Versions of the BleAdvertisementHeader.
+  enum class Version {
+    kUndefined = 0,
+    kV1 = 1,
+    kV2 = 2,
+    // Version is only allocated 3 bits in the BleAdvertisementHeader, so this
+    // can never go beyond V7.
+    //
+    // V1 is not present because it's an old format used in Nearby Connections
+    // before this logic was pushed down into Nearby Mediums. V1 put
+    // everything in the service data, while V2 puts the data inside a GATT
+    // characteristic so the two are not compatible.
+  };
+
+  static constexpr int kAdvertisementHashLength = 4;
+  static constexpr int kServiceIdBloomFilterLength = 10;
+
+  BleAdvertisementHeader() = default;
+  BleAdvertisementHeader(Version version, bool extended_advertisement,
+                         int num_slots,
+                         const ByteArray &service_id_bloom_filter,
+                         const ByteArray &advertisement_hash, int psm);
+  explicit BleAdvertisementHeader(
+      const ByteArray &ble_advertisement_header_bytes);
+  BleAdvertisementHeader(const BleAdvertisementHeader &) = default;
+  BleAdvertisementHeader &operator=(const BleAdvertisementHeader &) = default;
+  BleAdvertisementHeader(BleAdvertisementHeader &&) = default;
+  BleAdvertisementHeader &operator=(BleAdvertisementHeader &&) = default;
+  ~BleAdvertisementHeader() = default;
+
+  explicit operator ByteArray() const;
+
+  bool IsValid() const { return version_ == Version::kV2; }
+  Version GetVersion() const { return version_; }
+  bool IsExtendedAdvertisement() const { return extended_advertisement_; }
+  int GetNumSlots() const { return num_slots_; }
+  ByteArray GetServiceIdBloomFilter() const { return service_id_bloom_filter_; }
+  ByteArray GetAdvertisementHash() const { return advertisement_hash_; }
+  int GetPsmValue() const { return psm_; }
+
+ private:
+  static constexpr int kVersionAndNumSlotsLength = 1;
+  static constexpr int kMinAdvertisementHeaderLength =
+      kVersionAndNumSlotsLength + kServiceIdBloomFilterLength +
+      kAdvertisementHashLength;
+  static constexpr int kVersionBitmask = 0x0E0;
+  static constexpr int kExtendedAdvertismentBitMask = 0x010;
+  static constexpr int kNumSlotsBitmask = 0x00F;
+
+  Version version_ = Version::kUndefined;
+  bool extended_advertisement_ = false;
+  int num_slots_ = 0;
+  ByteArray service_id_bloom_filter_;
+  ByteArray advertisement_hash_;
+  int psm_ = 0;
+};
+
+}  // namespace mediums
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
diff --git a/internal/platform/byte_array.h b/internal/platform/byte_array.h
index b9dba2d..221961b 100644
--- a/internal/platform/byte_array.h
+++ b/internal/platform/byte_array.h
@@ -23,6 +23,8 @@
 #include <type_traits>
 #include <utility>
 
+#include "absl/strings/str_cat.h"
+
 namespace location {
 namespace nearby {
 
@@ -92,6 +94,11 @@
   // operation.
   explicit operator std::string() && { return std::move(data_); }
 
+  // Returns the representation of the underlying data as a string view.
+  absl::string_view AsStringView() const {
+    return absl::string_view(data(), size());
+  }
+
  private:
   std::string data_;
 };
diff --git a/internal/platform/byte_array_test.cc b/internal/platform/byte_array_test.cc
index 0084f02..366a319 100644
--- a/internal/platform/byte_array_test.cc
+++ b/internal/platform/byte_array_test.cc
@@ -87,4 +87,12 @@
   EXPECT_EQ(std::string(bytes), std::string(data.data(), data.size()));
 }
 
+TEST(ByteArrayTest, CreateFromAbslStringReturnsTheSame) {
+  const absl::string_view kTestString = "Test String";
+  ByteArray bytes{std::string(kTestString)};
+
+  EXPECT_EQ(bytes.size(), kTestString.size());
+  EXPECT_EQ(bytes.AsStringView(), kTestString);
+}
+
 }  // namespace