diff --git a/Package.swift b/Package.swift index ffa83959..e504174b 100644 --- a/Package.swift +++ b/Package.swift @@ -507,6 +507,7 @@ let package = Package( "internal/platform/wifi_lan_test.cc", "internal/platform/wifi_test.cc", "internal/platform/wifi_utils_test.cc", + "internal/platform/connection_info_test.cc", "internal/platform/condition_variable_test.cc", "internal/platform/thread_check_nocompile_test.py", "internal/platform/bluetooth_classic_test.cc", diff --git a/internal/device.h b/internal/device.h index c8958f06..82f2f9b7 100644 --- a/internal/device.h +++ b/internal/device.h @@ -26,8 +26,10 @@ namespace nearby { +// We need absl::monostate here to represent the case that we get an invalid +// connection info data element. using ConnectionInfoVariant = - absl::variant; class NearbyDevice { @@ -43,8 +45,7 @@ class NearbyDevice { NearbyDevice& operator=(NearbyDevice&&) = default; NearbyDevice(const NearbyDevice&) = delete; NearbyDevice& operator=(const NearbyDevice&) = delete; - virtual absl::string_view GetEndpointId() const = 0; - virtual absl::string_view GetEndpointInfo() const = 0; + virtual std::string GetEndpointId() const = 0; // We will be adding more ConnectionInfo types to this variant as they are // implemented. virtual std::vector GetConnectionInfos() const = 0; diff --git a/internal/platform/BUILD b/internal/platform/BUILD index b3479b25..7de29f48 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -139,6 +139,7 @@ cc_library( srcs = [ "ble_connection_info.cc", "bluetooth_connection_info.cc", + "connection_info.cc", "wifi_lan_connection_info.cc", ], hdrs = [ @@ -152,11 +153,13 @@ cc_library( "//presence:__subpackages__", ], deps = [ - ":base", ":logging", + "//proto:connections_enums_cc_proto", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:variant", ], ) @@ -442,6 +445,7 @@ cc_test( "borrowable_test.cc", "cancelable_alarm_test.cc", "condition_variable_test.cc", + "connection_info_test.cc", "count_down_latch_test.cc", "credential_storage_impl_test.cc", "crypto_test.cc", @@ -476,11 +480,13 @@ cc_test( "//internal/platform/implementation:comm", "//internal/platform/implementation/g3", # build_cleaner: keep "//internal/proto:credential_cc_proto", + "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:variant", "@com_google_googletest//:gtest_main", ], ) diff --git a/internal/platform/ble_connection_info.cc b/internal/platform/ble_connection_info.cc index eee88721..1f4b573e 100644 --- a/internal/platform/ble_connection_info.cc +++ b/internal/platform/ble_connection_info.cc @@ -14,15 +14,109 @@ #include "internal/platform/ble_connection_info.h" -#include #include +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "internal/platform/connection_info.h" + namespace nearby { +namespace { +constexpr int kBleMacAddressMask = 0b01000000; +constexpr int kGattCharacteristicMask = 0b00100000; +constexpr int kPsmMask = 0b00010000; +constexpr int kPsmSize = 2; +} // namespace -ByteArray BleConnectionInfo::ToBytes() const { return ByteArray(mac_address_); } +std::string BleConnectionInfo::ToDataElementBytes() const { + std::string payload_data; + payload_data.push_back(kBleGattMediumType); + bool has_mac = mac_address_.size() == kMacAddressLength; + bool has_gatt = !gatt_characteristic_.empty(); + bool has_psm = psm_.size() == kPsmSize; + char mask = has_mac ? kBleMacAddressMask : 0; + mask |= has_gatt ? kGattCharacteristicMask : 0; + mask |= has_psm ? kPsmMask : 0; + payload_data.push_back(mask); + if (has_mac) { + payload_data.insert(payload_data.end(), mac_address_.begin(), + mac_address_.end()); + } + if (has_gatt) { + payload_data.push_back(gatt_characteristic_.length()); + payload_data.insert(payload_data.end(), gatt_characteristic_.begin(), + gatt_characteristic_.end()); + } + if (has_psm) { + payload_data.insert(payload_data.end(), psm_.begin(), psm_.end()); + } + payload_data.push_back(actions_); + std::string ret; + ret.push_back(kDataElementFieldType); + ret.push_back(payload_data.size()); + ret.insert(ret.end(), payload_data.begin(), payload_data.end()); + return ret; +} -BleConnectionInfo BleConnectionInfo::FromBytes(ByteArray bytes) { - std::string serial(bytes.AsStringView()); - return BleConnectionInfo(serial.substr(0, kMacAddressLength)); +absl::StatusOr BleConnectionInfo::FromDataElementBytes( + absl::string_view bytes) { + if (bytes.size() < kConnectionInfoMinimumLength) { + return absl::InvalidArgumentError("Insufficient length of data element"); + } + if (bytes[0] != kDataElementFieldType) { + return absl::InvalidArgumentError("Not a data element type"); + } + int position = 1; + int length = static_cast(bytes[position]); + if (length != bytes.size() - 2) { + return absl::InvalidArgumentError("Bad data element length"); + } + if (bytes[++position] != kBleGattMediumType) { + return absl::InvalidArgumentError("Not a BLE data element"); + } + char mask = bytes[++position]; + std::string address; + ++position; + if ((mask & kBleMacAddressMask) == kBleMacAddressMask) { + if (bytes.size() - position < kMacAddressLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read MAC address."); + } + address = std::string(bytes.substr(position, kMacAddressLength)); + position += kMacAddressLength; + } + std::string characteristic; + if ((mask & kGattCharacteristicMask) == kGattCharacteristicMask) { + char characteristic_length = bytes[position]; + if (bytes.size() - (position + 1) < characteristic_length) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read GATT characteristic."); + } + characteristic = + std::string(bytes.substr(position + 1, characteristic_length)); + position += characteristic_length + 1; + } + std::string psm; + if ((mask & kPsmMask) == kPsmMask) { + if (bytes.size() - position < kPsmSize) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read PSM."); + } + psm = std::string(bytes.substr(position, kPsmSize)); + position += kPsmSize; + } + if (bytes.size() == position) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read action."); + } + char action = bytes[position]; + // Check that we don't have any remaining bytes. + if (bytes.size() != ++position) { + return absl::InvalidArgumentError(absl::StrFormat( + "Nonzero remaining bytes: %d.", bytes.size() - position)); + } + return BleConnectionInfo(address, characteristic, psm, action); } } // namespace nearby diff --git a/internal/platform/ble_connection_info.h b/internal/platform/ble_connection_info.h index 46c303d7..fee467fe 100644 --- a/internal/platform/ble_connection_info.h +++ b/internal/platform/ble_connection_info.h @@ -17,42 +17,47 @@ #include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" #include "internal/platform/connection_info.h" -#include "internal/platform/logging.h" +#include "proto/connections_enums.pb.h" namespace nearby { -// 6 bytes that spell "BADMAC" -constexpr absl::string_view kDefunctMacAddr = "\x42\x41\x44\x4D\x41\x43"; - class BleConnectionInfo : public ConnectionInfo { public: - explicit BleConnectionInfo(absl::string_view mac_address) - : mac_address_(std::string(mac_address)) { - if (mac_address_.size() != kMacAddressLength) { - NEARBY_LOGS(WARNING) - << "MAC address is not of the expected length! Trying to " - "connect to this MAC address will not work!"; - mac_address_ = std::string(kDefunctMacAddr); - } - } + static absl::StatusOr FromDataElementBytes( + absl::string_view bytes); - BleConnectionInfo(BleConnectionInfo const& info) { - mac_address_ = info.mac_address_; + BleConnectionInfo(absl::string_view mac_address, + absl::string_view gatt_characteristic, + absl::string_view psm, char actions) + : mac_address_(std::string(mac_address)), + gatt_characteristic_(std::string(gatt_characteristic)), + psm_(std::string(psm)), + actions_(actions) {} + + ::location::nearby::proto::connections::Medium GetMediumType() + const override { + return ::location::nearby::proto::connections::Medium::BLE; } - MediumType GetMediumType() const override { return MediumType::kBle; } - ByteArray ToBytes() const override; - static BleConnectionInfo FromBytes(ByteArray bytes); - ByteArray GetMacAddress() const { return ByteArray(mac_address_); } + std::string ToDataElementBytes() const override; + std::string GetMacAddress() const { return mac_address_; } + std::string GetGattCharacteristic() const { return gatt_characteristic_; } + std::string GetPsm() const { return psm_; } + char GetActions() const override { return actions_; } private: std::string mac_address_; + std::string gatt_characteristic_; + std::string psm_; + char actions_ = 0; }; inline bool operator==(const BleConnectionInfo& a, const BleConnectionInfo& b) { - return a.GetMacAddress() == b.GetMacAddress(); + return a.GetMacAddress() == b.GetMacAddress() && + a.GetActions() == b.GetActions() && + a.GetGattCharacteristic() == b.GetGattCharacteristic(); } inline bool operator!=(const BleConnectionInfo& a, const BleConnectionInfo& b) { diff --git a/internal/platform/ble_connection_info_test.cc b/internal/platform/ble_connection_info_test.cc index 2a572bc6..99a98f56 100644 --- a/internal/platform/ble_connection_info_test.cc +++ b/internal/platform/ble_connection_info_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,64 +19,312 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" +#include "internal/platform/connection_info.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace { +using Medium = ::location::nearby::proto::connections::Medium; +using ::testing::status::StatusIs; + constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; +constexpr absl::string_view kGattCharacteristic = + "\x03\x0a\x13\x56\x67\x21\x12\x45"; +constexpr absl::string_view kPsm = "\x45\x56"; +constexpr char kAction = 0x0F; -TEST(BleConnectionInfoTest, TestMediumType) { - BleConnectionInfo info(kMacAddr); - EXPECT_EQ(info.GetMediumType(), BleConnectionInfo::MediumType::kBle); +TEST(BleConnectionInfoTest, TestGetFields) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + EXPECT_EQ(info.GetMediumType(), Medium::BLE); + EXPECT_EQ(info.GetGattCharacteristic(), kGattCharacteristic); + EXPECT_EQ(info.GetActions(), kAction); + EXPECT_EQ(info.GetMacAddress(), kMacAddr); + EXPECT_EQ(info.GetPsm(), kPsm); } -TEST(BleConnectionInfoTest, TestToBytes) { - ByteArray mac_addr_bytes = ByteArray(std::string(kMacAddr)); - BleConnectionInfo info(kMacAddr); - EXPECT_EQ(info.ToBytes(), mac_addr_bytes); +TEST(BleConnectionInfoTest, TestFromEmptyBytes) { + auto result = BleConnectionInfo::FromDataElementBytes(""); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(BleConnectionInfoTest, TestFromBytes) { - ByteArray mac_addr_bytes = ByteArray(std::string(kMacAddr)); - BleConnectionInfo info = BleConnectionInfo::FromBytes(mac_addr_bytes); - EXPECT_EQ(info.GetMacAddress(), mac_addr_bytes); +TEST(BleConnectionInfoTest, TestFromInvalidBytes) { + auto result = BleConnectionInfo::FromDataElementBytes(kMacAddr); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(BleConnectionInfoTest, TestGetMacAddress) { - ByteArray mac_addr_bytes = ByteArray(std::string(kMacAddr)); - BleConnectionInfo info(kMacAddr); - EXPECT_EQ(info.GetMacAddress(), mac_addr_bytes); -} - -TEST(BleConnectionInfoTest, TestGetLongMacAddr) { - BleConnectionInfo info(absl::StrCat(kMacAddr, "\x56\x70\x89")); - EXPECT_EQ(info.GetMacAddress().AsStringView(), kDefunctMacAddr); -} - -TEST(BleConnectionInfoTest, TestGetShortMacAddr) { - BleConnectionInfo info("\x56\x70\x89"); - EXPECT_EQ(info.GetMacAddress().AsStringView(), kDefunctMacAddr); +TEST(BleConnectionInfoTest, TestFromNoAction) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[1] -= 1; + auto result = BleConnectionInfo::FromDataElementBytes( + serialized.substr(0, serialized.length() - 1)); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } TEST(BleConnectionInfoTest, TestToFromBytes) { - BleConnectionInfo info(kMacAddr); - ByteArray serialized = info.ToBytes(); - BleConnectionInfo result = BleConnectionInfo::FromBytes(serialized); - EXPECT_EQ(result, info); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), info.GetMacAddress()); + EXPECT_EQ(info2.GetGattCharacteristic(), info.GetGattCharacteristic()); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), info.GetPsm()); +} + +TEST(BleConnectionInfoTest, TestToFromBytesNoGattCharacteristic) { + BleConnectionInfo info(kMacAddr, "", kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), info.GetMacAddress()); + EXPECT_EQ(info2.GetGattCharacteristic(), ""); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), info.GetPsm()); +} + +TEST(BleConnectionInfoTest, TestToFromBytesNoMac) { + BleConnectionInfo info("", kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), ""); + EXPECT_EQ(info2.GetGattCharacteristic(), info.GetGattCharacteristic()); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), info.GetPsm()); +} + +TEST(BleConnectionInfoTest, TestToFromBytesLongMac) { + BleConnectionInfo info(absl::StrCat(kMacAddr, kMacAddr), kGattCharacteristic, + kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), ""); + EXPECT_EQ(info2.GetGattCharacteristic(), info.GetGattCharacteristic()); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), info.GetPsm()); +} + +TEST(BleConnectionInfoTest, TestToFromBytesNoPsm) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, "", kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), info.GetMacAddress()); + EXPECT_EQ(info2.GetGattCharacteristic(), info.GetGattCharacteristic()); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), ""); +} + +TEST(BleConnectionInfoTest, TestToFromBytesLongPsm) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, + absl::StrCat(kPsm, kPsm), kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + auto info2 = result.value(); + EXPECT_EQ(info2.GetMacAddress(), info.GetMacAddress()); + EXPECT_EQ(info2.GetGattCharacteristic(), info.GetGattCharacteristic()); + EXPECT_EQ(info2.GetActions(), info.GetActions()); + EXPECT_EQ(info2.GetPsm(), ""); +} + +TEST(BleConnectionInfoTest, TestFromEmpty) { + auto result = BleConnectionInfo::FromDataElementBytes(""); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BleConnectionInfoTest, TestFromBadElementType) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[0] = 0x56; + auto result = BleConnectionInfo::FromDataElementBytes(serialized); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BleConnectionInfoTest, TestFromBadMask) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for MAC address only. + serialized[3] = 0x40; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic only. + serialized[3] = 0x20; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for PSM only. + serialized[3] = 0x10; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address + GATT characteristic. + serialized[3] = 0x60; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic + PSM. + serialized[3] = 0x30; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address and PSM. + serialized[3] = 0x50; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x70; + EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BleConnectionInfoTest, TestFromBadMaskNoMac) { + BleConnectionInfo info("", kGattCharacteristic, kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for MAC address only. + serialized[3] = 0x40; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic only. + serialized[3] = 0x20; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for PSM only. + serialized[3] = 0x10; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address + GATT characteristic. + serialized[3] = 0x60; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address + PSM. + serialized[3] = 0x50; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x30; + EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BleConnectionInfoTest, TestFromBadMaskNoGattCharacteristic) { + BleConnectionInfo info(kMacAddr, "", kPsm, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for MAC address only. + serialized[3] = 0x40; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic only. + serialized[3] = 0x20; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for PSM only. + serialized[3] = 0x10; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields. + serialized[3] = 0x70; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address + GATT characteristic. + serialized[3] = 0x60; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic + PSM. + serialized[3] = 0x30; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x50; + EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BleConnectionInfoTest, TestFromBadMaskNoPsm) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, "", kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for MAC address only. + serialized[3] = 0x40; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic only. + serialized[3] = 0x20; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for PSM only. + serialized[3] = 0x10; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields. + serialized[3] = 0x70; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic + PSM. + serialized[3] = 0x30; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for MAC address + PSM. + serialized[3] = 0x50; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x60; + EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BleConnectionInfoTest, TestFromBadMaskEmpty) { + BleConnectionInfo info("", "", "", kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for MAC address only. + serialized[3] = 0x40; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for GATT characteristic only. + serialized[3] = 0x20; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for PSM only. + serialized[3] = 0x10; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields. + serialized[3] = 0x70; + EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with correct mask. + serialized[3] = 0x00; + EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); } TEST(BleConnectionInfoTest, TestCopy) { - BleConnectionInfo info(kMacAddr); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); BleConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(BleConnectionInfoTest, TestEquals) { - BleConnectionInfo info(kMacAddr); - BleConnectionInfo info2(kMacAddr); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info2(kMacAddr, kGattCharacteristic, kPsm, kAction); EXPECT_EQ(info, info2); } diff --git a/internal/platform/bluetooth_connection_info.cc b/internal/platform/bluetooth_connection_info.cc index db0f24a0..ac4512a4 100644 --- a/internal/platform/bluetooth_connection_info.cc +++ b/internal/platform/bluetooth_connection_info.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,19 +14,90 @@ #include "internal/platform/bluetooth_connection_info.h" -#include #include -namespace nearby { +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "internal/platform/connection_info.h" -ByteArray BluetoothConnectionInfo::ToBytes() const { - return ByteArray(absl::StrCat(mac_address_, service_id_)); +namespace nearby { +namespace { +constexpr int kMacAddressMask = 0b01000000; +constexpr int kBluetoothUuidMask = 0b00100000; +} // namespace + +std::string BluetoothConnectionInfo::ToDataElementBytes() const { + std::string payload_data; + payload_data.push_back(kBluetoothMediumType); + bool has_mac = mac_address_.size() == kMacAddressLength; + bool has_bluetooth_uuid = !bluetooth_uuid_.empty(); + char mask = has_mac ? kMacAddressMask : 0; + mask |= has_bluetooth_uuid ? kBluetoothUuidMask : 0; + payload_data.push_back(mask); + if (has_mac) { + payload_data.insert(payload_data.end(), mac_address_.begin(), + mac_address_.end()); + } + if (has_bluetooth_uuid) { + payload_data.insert(payload_data.end(), bluetooth_uuid_.begin(), + bluetooth_uuid_.end()); + } + payload_data.push_back(actions_); + std::string ret; + ret.push_back(kDataElementFieldType); + ret.push_back(payload_data.size()); + ret.insert(ret.end(), payload_data.begin(), payload_data.end()); + return std::string(ret.data(), ret.size()); } -BluetoothConnectionInfo BluetoothConnectionInfo::FromBytes(ByteArray bytes) { - std::string serial(bytes.AsStringView()); - ByteArray mac_address = ByteArray(serial.substr(0, kMacAddressLength)); - std::string service_id = serial.substr(kMacAddressLength); - return BluetoothConnectionInfo(mac_address, service_id); +absl::StatusOr +BluetoothConnectionInfo::FromDataElementBytes(absl::string_view bytes) { + if (bytes.size() < kConnectionInfoMinimumLength) { + return absl::InvalidArgumentError("Insufficient length of data element"); + } + if (bytes[0] != kDataElementFieldType) { + return absl::InvalidArgumentError("Not a data element type"); + } + int position = 1; + char length = bytes[position]; + if (length != bytes.size() - 2) { + return absl::InvalidArgumentError("Bad data element length"); + } + if (bytes[++position] != kBluetoothMediumType) { + return absl::InvalidArgumentError("Not a Bluetooth data element"); + } + char mask = bytes[++position]; + std::string address = ""; + ++position; + if ((mask & kMacAddressMask) == kMacAddressMask) { + if (bytes.size() - position < kMacAddressLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read MAC address."); + } + address = std::string(bytes.substr(position, kMacAddressLength)); + position += kMacAddressLength; + } + std::string uuid = ""; + if ((mask & kBluetoothUuidMask) == kBluetoothUuidMask) { + if (bytes.size() - position < 4) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read bluetooth UUID."); + } + uuid = std::string(bytes.substr(position, 4)); + position += 4; + } + if (bytes.size() == position) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read action."); + } + char action = bytes[position]; + // Check that we don't have any remaining bytes. + if (bytes.size() != ++position) { + return absl::InvalidArgumentError(absl::StrFormat( + "Nonzero remaining bytes: %d.", bytes.size() - position)); + } + return BluetoothConnectionInfo(address, uuid, action); } } // namespace nearby diff --git a/internal/platform/bluetooth_connection_info.h b/internal/platform/bluetooth_connection_info.h index 78f924a7..82afe528 100644 --- a/internal/platform/bluetooth_connection_info.h +++ b/internal/platform/bluetooth_connection_info.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,40 +17,44 @@ #include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" #include "internal/platform/connection_info.h" +#include "proto/connections_enums.pb.h" namespace nearby { class BluetoothConnectionInfo : public ConnectionInfo { public: - MediumType GetMediumType() const override { return MediumType::kBluetooth; } - ByteArray ToBytes() const override; - BluetoothConnectionInfo() = delete; - explicit BluetoothConnectionInfo(const ByteArray& mac_address, - absl::string_view service_id) - : mac_address_(mac_address.data()), service_id_(std::string(service_id)) { - mac_address_.resize(kMacAddressLength); - } + static absl::StatusOr FromDataElementBytes( + absl::string_view bytes); - BluetoothConnectionInfo(BluetoothConnectionInfo const& info) { - mac_address_ = info.mac_address_; - service_id_ = info.service_id_; + BluetoothConnectionInfo(absl::string_view mac_address, + absl::string_view bluetooth_uuid, char actions) + : mac_address_(std::string(mac_address)), + bluetooth_uuid_(std::string(bluetooth_uuid)), + actions_(actions) {} + + ::location::nearby::proto::connections::Medium GetMediumType() + const override { + return ::location::nearby::proto::connections::Medium::BLUETOOTH; } - static BluetoothConnectionInfo FromBytes(ByteArray bytes); - ByteArray GetMacAddress() const { return ByteArray(mac_address_); } - absl::string_view GetServiceId() const { return service_id_; } + std::string ToDataElementBytes() const override; + std::string GetMacAddress() const { return mac_address_; } + std::string GetBluetoothUuid() const { return bluetooth_uuid_; } + char GetActions() const override { return actions_; } private: std::string mac_address_; - std::string service_id_; + std::string bluetooth_uuid_; + char actions_; }; inline bool operator==(const BluetoothConnectionInfo& a, const BluetoothConnectionInfo& b) { return a.GetMacAddress() == b.GetMacAddress() && - a.GetServiceId() == b.GetServiceId(); + a.GetBluetoothUuid() == b.GetBluetoothUuid() && + a.GetActions() == b.GetActions(); } inline bool operator!=(const BluetoothConnectionInfo& a, diff --git a/internal/platform/bluetooth_connection_info_test.cc b/internal/platform/bluetooth_connection_info_test.cc index 03518445..592b7a2e 100644 --- a/internal/platform/bluetooth_connection_info_test.cc +++ b/internal/platform/bluetooth_connection_info_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,77 +19,190 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace { +using Medium = ::location::nearby::proto::connections::Medium; +using ::testing::status::StatusIs; + constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; -constexpr absl::string_view kServiceId{"test"}; +constexpr absl::string_view kBluetoothUuid{"test"}; +constexpr char kAction = 0x0F; -TEST(BluetoothConnectionInfoTest, TestMediumType) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - EXPECT_EQ(info.GetMediumType(), - BluetoothConnectionInfo::MediumType::kBluetooth); -} - -TEST(BluetoothConnectionInfoTest, TestToBytes) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - ByteArray serialized_expected = - ByteArray(absl::StrCat(kMacAddr, kServiceId)); - EXPECT_EQ(info.ToBytes(), serialized_expected); -} - -TEST(BluetoothConnectionInfoTest, TestFromBytes) { - std::string macAddr(kMacAddr); - ByteArray serialized = - ByteArray(absl::StrCat(macAddr, kServiceId)); - BluetoothConnectionInfo info = BluetoothConnectionInfo::FromBytes(serialized); - EXPECT_EQ(info.GetMacAddress(), ByteArray(macAddr)); - EXPECT_EQ(info.GetServiceId(), kServiceId); -} - -TEST(BluetoothConnectionInfoTest, TestGetMacAddress) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - EXPECT_EQ(info.GetMacAddress(), ByteArray(macAddr)); +TEST(BluetoothConnectionInfoTest, TestGetFields) { + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + EXPECT_EQ(info.GetMediumType(), Medium::BLUETOOTH); + EXPECT_EQ(info.GetMacAddress(), kMacAddr); + EXPECT_EQ(info.GetActions(), kAction); + EXPECT_EQ(info.GetBluetoothUuid(), kBluetoothUuid); } TEST(BluetoothConnectionInfoTest, TestGetLongMacAddr) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr + "\x56\x70\x89"), kServiceId); - EXPECT_EQ(info.GetMacAddress().AsStringView(), kMacAddr); -} - -TEST(BluetoothConnectionInfoTest, TestGetServiceId) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - EXPECT_EQ(info.GetServiceId(), kServiceId); + BluetoothConnectionInfo info(absl::StrCat(kMacAddr, "\x56\x70\x89"), + kBluetoothUuid, kAction); + EXPECT_NE(info.GetMacAddress(), kMacAddr); } TEST(BluetoothConnectionInfoTest, TestToFromBytes) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - ByteArray serialized = info.ToBytes(); - BluetoothConnectionInfo result = - BluetoothConnectionInfo::FromBytes(serialized); - EXPECT_EQ(result, info); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + EXPECT_EQ(result.value(), info); +} + +TEST(BluetoothConnectionInfoTest, TestToFromNoMacAddress) { + BluetoothConnectionInfo info("", kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + info = result.value(); + EXPECT_EQ(info.GetMacAddress(), ""); + EXPECT_EQ(info.GetBluetoothUuid(), kBluetoothUuid); + EXPECT_EQ(info.GetActions(), kAction); +} + +TEST(BluetoothConnectionInfoTest, TestToFromWrongLength) { + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + ++serialized[1]; + auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BluetoothConnectionInfoTest, TestToFromNoBluetoothUuid) { + BluetoothConnectionInfo info(kMacAddr, "", kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + EXPECT_EQ(result.value(), info); +} + +TEST(BluetoothConnectionInfoTest, TestFromEmptyBytes) { + auto result = BluetoothConnectionInfo::FromDataElementBytes(""); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BluetoothConnectionInfoTest, TestFromNoAction) { + BluetoothConnectionInfo info("", "", kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[1] -= 1; + auto result = BluetoothConnectionInfo::FromDataElementBytes( + serialized.substr(0, serialized.length() - 1)); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BluetoothConnectionInfoTest, TestFromInvalidBytes) { + auto result = BluetoothConnectionInfo::FromDataElementBytes(kMacAddr); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BluetoothConnectionInfoTest, TestFromBadElementType) { + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[0] = 0x56; + auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(BluetoothConnectionInfoTest, TestFromBadMask) { + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + // Remove the mask for UUID. + serialized[3] = 0x40; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove the mask for MAC address and add back UUID. + serialized[3] = 0x20; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set empty mask. + serialized[3] = 0x00; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x60; + EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BluetoothConnectionInfoTest, TestFromBadMaskNoUuid) { + BluetoothConnectionInfo info(kMacAddr, "", kAction); + std::string serialized = info.ToDataElementBytes(); + // Set the mask for UUID and MAC address. + serialized[3] = 0x60; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set the mask for only UUID. + serialized[3] = 0x20; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set empty mask. + serialized[3] = 0x00; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x40; + EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BluetoothConnectionInfoTest, TestFromBadMaskNoMac) { + BluetoothConnectionInfo info("", kBluetoothUuid, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set the mask for UUID and MAC address. + serialized[3] = 0x60; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set the mask for only MAC address. + serialized[3] = 0x40; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set empty mask. + serialized[3] = 0x00; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x20; + EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(BluetoothConnectionInfoTest, TestFromBadMaskEmpty) { + BluetoothConnectionInfo info("", "", kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for UUID and MAC address. + serialized[3] = 0x60; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for only UUID. + serialized[3] = 0x20; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for only MAC address. + serialized[3] = 0x40; + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with correct mask. + serialized[3] = 0x00; + EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); } TEST(BluetoothConnectionInfoTest, TestCopy) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); BluetoothConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(BluetoothConnectionInfoTest, TestEquals) { - std::string macAddr(kMacAddr); - BluetoothConnectionInfo info(ByteArray(macAddr), kServiceId); - BluetoothConnectionInfo info2(ByteArray(macAddr), kServiceId); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info2(kMacAddr, kBluetoothUuid, kAction); EXPECT_EQ(info, info2); } diff --git a/internal/platform/connection_info.cc b/internal/platform/connection_info.cc new file mode 100644 index 00000000..eaa745cc --- /dev/null +++ b/internal/platform/connection_info.cc @@ -0,0 +1,47 @@ +// Copyright 2023 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/connection_info.h" + +#include "absl/strings/string_view.h" +#include "absl/types/variant.h" +#include "internal/platform/ble_connection_info.h" +#include "internal/platform/bluetooth_connection_info.h" +#include "internal/platform/wifi_lan_connection_info.h" + +namespace nearby { +ConnectionInfoVariant ConnectionInfo::FromDataElementBytes( + absl::string_view data_element_bytes) { + uint8_t type = data_element_bytes[2]; + if (type == kBluetoothMediumType) { + auto result = + BluetoothConnectionInfo::FromDataElementBytes(data_element_bytes); + if (result.ok()) { + return result.value(); + } + } else if (type == kBleGattMediumType) { + auto result = BleConnectionInfo::FromDataElementBytes(data_element_bytes); + if (result.ok()) { + return result.value(); + } + } else if (type == kWifiLanMediumType) { + auto result = + WifiLanConnectionInfo::FromDataElementBytes(data_element_bytes); + if (result.ok()) { + return result.value(); + } + } + return absl::monostate(); +} +} // namespace nearby diff --git a/internal/platform/connection_info.h b/internal/platform/connection_info.h index fb3a1733..804de0ad 100644 --- a/internal/platform/connection_info.h +++ b/internal/platform/connection_info.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,23 +15,37 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_CONNECTION_INFO_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_CONNECTION_INFO_H_ -#include "internal/platform/byte_array.h" +#include + +#include "absl/strings/string_view.h" +#include "absl/types/variant.h" +#include "proto/connections_enums.pb.h" namespace nearby { +inline constexpr uint8_t kDataElementFieldType = 0x14; +inline constexpr uint8_t kBluetoothMediumType = 0x00; +inline constexpr uint8_t kBleGattMediumType = 0x01; +inline constexpr uint8_t kWifiLanMediumType = 0x03; +inline constexpr int kMacAddressLength = 6; +inline constexpr int kConnectionInfoMinimumLength = 2; -constexpr int kMacAddressLength = 6; +class BleConnectionInfo; +class BluetoothConnectionInfo; +class WifiLanConnectionInfo; + +using ConnectionInfoVariant = + absl::variant; class ConnectionInfo { public: - enum class MediumType { - kUnknown = 0, - kBluetooth = 1, - kWifiLan = 2, - kBle = 3, - }; virtual ~ConnectionInfo() = default; - virtual MediumType GetMediumType() const = 0; - virtual ByteArray ToBytes() const = 0; + virtual ::location::nearby::proto::connections::Medium GetMediumType() + const = 0; + virtual std::string ToDataElementBytes() const = 0; + virtual char GetActions() const = 0; + static ConnectionInfoVariant FromDataElementBytes( + absl::string_view data_element_bytes); }; } // namespace nearby diff --git a/internal/platform/connection_info_test.cc b/internal/platform/connection_info_test.cc new file mode 100644 index 00000000..ed8f9bab --- /dev/null +++ b/internal/platform/connection_info_test.cc @@ -0,0 +1,108 @@ +// Copyright 2023 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/connection_info.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "internal/platform/ble_connection_info.h" +#include "internal/platform/bluetooth_connection_info.h" +#include "internal/platform/wifi_lan_connection_info.h" + +namespace nearby { +namespace { + +// BLE +constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; +constexpr absl::string_view kGattCharacteristic = + "\x03\x0a\x13\x56\x67\x21\x12\x45"; +constexpr absl::string_view kPsm = "\x45\x56"; +constexpr char kAction = 0x0F; +// Bluetooth +constexpr absl::string_view kBluetoothUuid{"test"}; +// WLAN +constexpr absl::string_view kIpv4Addr = "\x4C\x8B\x1D\xCE"; +constexpr absl::string_view kPort = "\x12\x34\x56\x78"; +constexpr absl::string_view kBssid = "\x0A\x1B\x2C\x34\x58\x7E"; + +TEST(ConnectionInfoTest, TestRestoreBle) { + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + auto serialized = info.ToDataElementBytes(); + auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); + ASSERT_TRUE(absl::holds_alternative(connection_info)); + auto ble_connection_info = absl::get(connection_info); + EXPECT_EQ(ble_connection_info, info); +} + +TEST(ConnectionInfoTest, TestRestoreBluetooth) { + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + auto serialized = info.ToDataElementBytes(); + auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); + ASSERT_TRUE( + absl::holds_alternative(connection_info)); + auto bt_connection_info = absl::get(connection_info); + EXPECT_EQ(bt_connection_info, info); +} + +TEST(ConnectionInfoTest, TestRestoreMdns) { + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + auto serialized = info.ToDataElementBytes(); + auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); + ASSERT_TRUE(absl::holds_alternative(connection_info)); + auto wlan_connection_info = absl::get(connection_info); + EXPECT_EQ(wlan_connection_info, info); +} + +TEST(ConnectionInfoTest, TestMonostate) { + WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, kAction); + BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, kAction); + BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, kAction); + std::vector infos = {&bt_info, &ble_info, &wifi_info}; + for (auto info : infos) { + auto serialized = info->ToDataElementBytes(); + auto connection_info = + ConnectionInfo::FromDataElementBytes(serialized.substr(0, 10)); + EXPECT_TRUE(absl::holds_alternative(connection_info)); + } +} + +TEST(ConnectionInfoTest, TestCannotRestoreAsOtherInfos) { + WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, kAction); + BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, kAction); + BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, kAction); + EXPECT_THAT( + BleConnectionInfo::FromDataElementBytes(wifi_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT( + BleConnectionInfo::FromDataElementBytes(bt_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes( + ble_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes( + wifi_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes( + ble_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT( + WifiLanConnectionInfo::FromDataElementBytes(bt_info.ToDataElementBytes()), + testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace nearby diff --git a/internal/platform/wifi_lan_connection_info.cc b/internal/platform/wifi_lan_connection_info.cc index f20ef5e1..361ddabe 100644 --- a/internal/platform/wifi_lan_connection_info.cc +++ b/internal/platform/wifi_lan_connection_info.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,36 +18,116 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" +#include "internal/platform/connection_info.h" namespace nearby { +namespace { +constexpr char kIpv4Mask = 0b01000000; +constexpr char kIpv6Mask = 0b00100000; +constexpr char kPortMask = 0b00010000; +constexpr char kBssidMask = 0b00001000; +} // namespace -ByteArray WifiLanConnectionInfo::ToBytes() const { - return ByteArray(absl::StrCat(std::to_string(GetWifiLanConnectionInfoType()), - ip_address_, port_, bssid_)); +std::string WifiLanConnectionInfo::ToDataElementBytes() const { + std::string payload_data; + payload_data.push_back(kWifiLanMediumType); + bool has_ipv4 = ip_address_.size() == kIpv4AddressLength; + bool has_ipv6 = ip_address_.size() == kIpv6AddressLength; + bool has_bssid = bssid_.size() == kBssidLength; + char mask = kPortMask; + mask |= has_ipv4 ? kIpv4Mask : 0; + mask |= has_ipv6 ? kIpv6Mask : 0; + mask |= has_bssid ? kBssidMask : 0; + payload_data.push_back(mask); + if (has_ipv4 || has_ipv6) { + payload_data.insert(payload_data.end(), ip_address_.begin(), + ip_address_.end()); + } + payload_data.insert(payload_data.end(), port_.begin(), port_.end()); + if (has_bssid) { + payload_data.insert(payload_data.end(), bssid_.begin(), bssid_.end()); + } + payload_data.push_back(actions_); + std::string ret; + ret.push_back(kDataElementFieldType); + ret.push_back(payload_data.size()); + ret.insert(ret.end(), payload_data.begin(), payload_data.end()); + return std::string(ret.data(), ret.size()); } -absl::StatusOr WifiLanConnectionInfo::FromBytes( - ByteArray bytes) { - // IPV4/IPV6 indicator size are both 1 - int ip_size = 1; - absl::string_view serial = bytes.AsStringView(); - if (serial.length() != - ip_size + kIpv4AddressLength + kPortLength + kBssidLength && - serial.length() != - ip_size + kIpv6AddressLength + kPortLength + kBssidLength) { - return absl::InvalidArgumentError("Bad byte array length"); +absl::StatusOr +WifiLanConnectionInfo::FromDataElementBytes(absl::string_view bytes) { + if (bytes.size() < kConnectionInfoMinimumLength) { + return absl::InvalidArgumentError("Insufficient length of data element"); } - size_t proc; - int type = std::stoi(std::string(serial.substr(0, ip_size)), &proc); - if (proc != ip_size) { - return absl::InvalidArgumentError("Could not determine IPV4 or IPV6"); + if (bytes[0] != kDataElementFieldType) { + return absl::InvalidArgumentError("Not a data element type"); } - int ip_length = type == kIpv4 ? kIpv4AddressLength : kIpv6AddressLength; - absl::string_view ip = serial.substr(ip_size, ip_length); - absl::string_view port = serial.substr(ip_size + ip_length, kPortLength); - absl::string_view bssid = serial.substr(ip_size + ip_length + kPortLength); - return WifiLanConnectionInfo(ip, port, bssid); + int position = 1; + char length = bytes[position]; + if (length != bytes.size() - 2) { + return absl::InvalidArgumentError("Bad data element length"); + } + if (bytes[++position] != kWifiLanMediumType) { + return absl::InvalidArgumentError("Not a WiFi LAN data element"); + } + char mask = bytes[++position]; + std::string address; + ++position; + // Sanity check to make sure that both are not present. + if ((mask & kIpv4Mask) == kIpv4Mask && (mask & kIpv6Mask) == kIpv6Mask) { + return absl::InvalidArgumentError( + "Both IPV4 and IPV6 addresses cannot be present."); + } + if ((mask & kIpv4Mask) == kIpv4Mask) { + if (bytes.size() - position < kIpv4AddressLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read IPV4 address."); + } + address = std::string(bytes.substr(position, kIpv4AddressLength)); + position += kIpv4AddressLength; + } else if ((mask & kIpv6Mask) == kIpv6Mask) { + if (bytes.size() - position < kIpv6AddressLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read IPV6 address."); + } + address = std::string(bytes.substr(position, kIpv6AddressLength)); + position += kIpv6AddressLength; + } + + std::string port; + if ((mask & kPortMask) == kPortMask) { + if (bytes.size() - position < kPortLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read port."); + } + port = std::string(bytes.substr(position, kPortLength)); + position += kPortLength; + } + + std::string bssid; + if ((mask & kBssidMask) == kBssidMask) { + if (bytes.size() - position < kBssidLength) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read port."); + } + bssid = std::string(bytes.substr(position, kBssidLength)); + position += kBssidLength; + } + + if (bytes.size() == position) { + return absl::InvalidArgumentError( + "Insufficient remaining bytes to read action."); + } + char action = bytes[position]; + // Check that we don't have any remaining bytes. + if (bytes.size() != ++position) { + return absl::InvalidArgumentError(absl::StrFormat( + "Nonzero remaining bytes: %d.", bytes.size() - position)); + } + return WifiLanConnectionInfo(address, port, bssid, action); } } // namespace nearby diff --git a/internal/platform/wifi_lan_connection_info.h b/internal/platform/wifi_lan_connection_info.h index 64f04580..a31bf6b2 100644 --- a/internal/platform/wifi_lan_connection_info.h +++ b/internal/platform/wifi_lan_connection_info.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,53 +20,46 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" #include "internal/platform/connection_info.h" +#include "proto/connections_enums.pb.h" namespace nearby { constexpr int kIpv4AddressLength = 4; constexpr int kIpv6AddressLength = 16; -constexpr int kPortLength = 2; +constexpr int kPortLength = 4; constexpr int kBssidLength = 6; class WifiLanConnectionInfo : public ConnectionInfo { public: - enum WifiLanConnectionInfoType { - kUnknown = 0, - kIpv4 = 1, - kIpv6 = 2, - }; - WifiLanConnectionInfo(absl::string_view ip_address, absl::string_view port) - : ip_address_(ip_address), port_(port), bssid_("") { - port_.resize(kPortLength); - bssid_.resize(kBssidLength); - } + static absl::StatusOr FromDataElementBytes( + absl::string_view bytes); + WifiLanConnectionInfo(absl::string_view ip_address, absl::string_view port, - absl::string_view bssid) - : ip_address_(ip_address), port_(port), bssid_(std::string(bssid)) { - port_.resize(kPortLength); - bssid_.resize(kBssidLength); - } - static absl::StatusOr FromBytes(ByteArray bytes); - MediumType GetMediumType() const override { return MediumType::kWifiLan; } - ByteArray ToBytes() const override; - ByteArray GetIpAddress() const { return ByteArray(ip_address_); } - ByteArray GetPort() const { return ByteArray(port_); } - ByteArray GetBssid() const { return ByteArray(bssid_); } - WifiLanConnectionInfoType GetWifiLanConnectionInfoType() const { - if (ip_address_.size() == kIpv6AddressLength) { - return kIpv6; - } else if (ip_address_.size() == kIpv4AddressLength) { - return kIpv4; - } - return kUnknown; + char actions) + : ip_address_(ip_address), port_(port), bssid_(""), actions_(actions) {} + WifiLanConnectionInfo(absl::string_view ip_address, absl::string_view port, + absl::string_view bssid, char actions) + : ip_address_(ip_address), + port_(port), + bssid_(std::string(bssid)), + actions_(actions) {} + + ::location::nearby::proto::connections::Medium GetMediumType() + const override { + return ::location::nearby::proto::connections::Medium::WIFI_LAN; } + std::string ToDataElementBytes() const override; + std::string GetIpAddress() const { return ip_address_; } + std::string GetPort() const { return port_; } + std::string GetBssid() const { return bssid_; } + char GetActions() const override { return actions_; } private: std::string ip_address_; std::string port_; std::string bssid_; + char actions_; }; inline bool operator==(const WifiLanConnectionInfo& a, diff --git a/internal/platform/wifi_lan_connection_info_test.cc b/internal/platform/wifi_lan_connection_info_test.cc index 52cce10a..817e47ff 100644 --- a/internal/platform/wifi_lan_connection_info_test.cc +++ b/internal/platform/wifi_lan_connection_info_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace { @@ -29,110 +30,242 @@ namespace { constexpr absl::string_view kIpv4Addr = "\x4C\x8B\x1D\xCE"; constexpr absl::string_view kIpv6Addr = "\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE"; -constexpr absl::string_view kPort = "\x56\x78"; +constexpr absl::string_view kPort = "\x12\x34\x56\x78"; constexpr absl::string_view kBssid = "\x0A\x1B\x2C\x34\x58\x7E"; +constexpr char kAction = 0x0F; using ::testing::status::StatusIs; +using Medium = ::location::nearby::proto::connections::Medium; TEST(WifiLanConnectionInfoTest, TestMediumType) { - WifiLanConnectionInfo info(kIpv4Addr, kPort); - EXPECT_EQ(info.GetMediumType(), WifiLanConnectionInfo::MediumType::kWifiLan); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kAction); + EXPECT_EQ(info.GetMediumType(), Medium::WIFI_LAN); } TEST(WifiLanConnectionInfoTest, TestGetMembers) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid); - EXPECT_EQ(info.GetIpAddress().AsStringView(), kIpv4Addr); - EXPECT_EQ(info.GetPort().AsStringView(), kPort); - EXPECT_EQ(info.GetBssid().AsStringView(), kBssid); -} - -TEST(WifiLanConnectionInfoTest, TestIpv4ToBytes) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid); - ByteArray expected( - absl::StrCat(std::to_string(info.GetWifiLanConnectionInfoType()), - kIpv4Addr, kPort, kBssid)); - EXPECT_EQ(info.ToBytes(), expected); -} - -TEST(WifiLanConnectionInfoTest, TestFromBytesIpv4) { - ByteArray expected = ByteArray(absl::StrCat( - std::to_string(WifiLanConnectionInfo::kIpv4), kIpv4Addr, kPort, kBssid)); - ASSERT_OK(WifiLanConnectionInfo::FromBytes(expected)); - WifiLanConnectionInfo info = - WifiLanConnectionInfo::FromBytes(expected).value(); - EXPECT_EQ(info.GetIpAddress().AsStringView(), kIpv4Addr); - EXPECT_EQ(info.GetPort().AsStringView(), kPort); - EXPECT_EQ(info.GetBssid().AsStringView(), kBssid); -} - -TEST(WifiLanConnectionInfoTest, TestFromBytesIpv6) { - ByteArray expected = ByteArray(absl::StrCat( - std::to_string(WifiLanConnectionInfo::kIpv6), kIpv6Addr, kPort, kBssid)); - ASSERT_OK(WifiLanConnectionInfo::FromBytes(expected)); - WifiLanConnectionInfo info = - WifiLanConnectionInfo::FromBytes(expected).value(); - EXPECT_EQ(info.GetIpAddress().AsStringView(), kIpv6Addr); - EXPECT_EQ(info.GetPort().AsStringView(), kPort); - EXPECT_EQ(info.GetBssid().AsStringView(), kBssid); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + EXPECT_EQ(info.GetIpAddress(), kIpv4Addr); + EXPECT_EQ(info.GetPort(), kPort); + EXPECT_EQ(info.GetBssid(), kBssid); + EXPECT_EQ(info.GetActions(), kAction); } TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv4) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid); - ByteArray serialized = info.ToBytes(); - WifiLanConnectionInfo result = - WifiLanConnectionInfo::FromBytes(serialized).value(); - EXPECT_EQ(result, info); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + EXPECT_EQ(result.value(), info); } TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv6) { - WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid); - ByteArray serialized = info.ToBytes(); - WifiLanConnectionInfo result = - WifiLanConnectionInfo::FromBytes(serialized).value(); - EXPECT_EQ(result, info); + WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); + ASSERT_OK(result); + EXPECT_EQ(result.value(), info); } TEST(WifiLanConnectionInfoTest, TestCopy) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); WifiLanConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(WifiLanConnectionInfoTest, TestEquals) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid); - WifiLanConnectionInfo info2(kIpv4Addr, kPort, kBssid); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info2(kIpv4Addr, kPort, kBssid, kAction); EXPECT_EQ(info, info2); } -TEST(WifiLanConnectionInfoTest, TestShortBssid) { - std::string shortBssid = "\x0A\x1B\x2C"; - std::string extended("\x0A\x1B\x2C\0\0\0", kBssidLength); - WifiLanConnectionInfo info(kIpv4Addr, kPort, shortBssid); - EXPECT_EQ(info.GetBssid().size(), kBssidLength); - // Value-initialized so there will be 3 0x0 characters - EXPECT_EQ(info.GetBssid().AsStringView(), extended); +TEST(WifiLanConnectionInfoTest, TestFromNoAction) { + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[1] -= 1; + auto result = WifiLanConnectionInfo::FromDataElementBytes( + serialized.substr(0, serialized.length() - 1)); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(WifiLanConnectionInfoTest, TestLongBssid) { - std::string longBssid = absl::StrCat(kBssid, "\x68\x42\x35"); - WifiLanConnectionInfo info(kIpv4Addr, kPort, longBssid); - EXPECT_EQ(info.GetBssid().size(), kBssidLength); - EXPECT_EQ(info.GetBssid().AsStringView(), kBssid); +TEST(WifiLanConnectionInfoTest, TestToFromShortBssid) { + std::string shortBssid = "\x0A\x1B\x2C"; + WifiLanConnectionInfo info(kIpv4Addr, kPort, shortBssid, kAction); + auto bytes = info.ToDataElementBytes(); + auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); + ASSERT_OK(result); + EXPECT_EQ(result->GetIpAddress(), kIpv4Addr); + EXPECT_EQ(result->GetPort(), kPort); + EXPECT_TRUE(result->GetBssid().empty()); + EXPECT_EQ(result->GetActions(), kAction); +} + +TEST(WifiLanConnectionInfoTest, TestToFromNoIp) { + WifiLanConnectionInfo info("", kPort, kBssid, kAction); + auto bytes = info.ToDataElementBytes(); + auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); + ASSERT_OK(result); + EXPECT_TRUE(result->GetIpAddress().empty()); + EXPECT_EQ(result->GetPort(), kPort); + EXPECT_EQ(result->GetBssid(), kBssid); + EXPECT_EQ(result->GetActions(), kAction); +} + +TEST(WifiLanConnectionInfoTest, TestToFromLongIp) { + WifiLanConnectionInfo info(absl::StrCat(kIpv4Addr, kIpv6Addr), kPort, kBssid, + kAction); + auto bytes = info.ToDataElementBytes(); + auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); + ASSERT_OK(result); + EXPECT_TRUE(result->GetIpAddress().empty()); + EXPECT_EQ(result->GetPort(), kPort); + EXPECT_EQ(result->GetBssid(), kBssid); + EXPECT_EQ(result->GetActions(), kAction); +} + +TEST(WifiLanConnectionInfoTest, TestFromIp) { + auto result = WifiLanConnectionInfo::FromDataElementBytes(kIpv4Addr); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); + result = WifiLanConnectionInfo::FromDataElementBytes(kIpv6Addr); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } TEST(WifiLanConnectionInfoTest, TestBadBytesLength) { - WifiLanConnectionInfo info(kIpv4Addr, kPort); - ByteArray serialized = info.ToBytes(); - ByteArray modified_short(std::string( - serialized.AsStringView().substr(0, kIpv4AddressLength + kPortLength))); - ByteArray modified_long(absl::StrCat(serialized.AsStringView(), kPort)); - EXPECT_THAT(WifiLanConnectionInfo::FromBytes(modified_short), + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + std::string modified_short( + serialized.substr(0, kIpv4AddressLength + kPortLength)); + std::string modified_long(absl::StrCat(serialized, kPort)); + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(modified_short), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(WifiLanConnectionInfo::FromBytes(ByteArray()), + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(""), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(WifiLanConnectionInfo::FromBytes(modified_long), + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(modified_long), StatusIs(absl::StatusCode::kInvalidArgument)); } +TEST(WifiLanConnectionInfoTest, TestFromBadElementType) { + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + serialized[0] = 0x56; + auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); + EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(WifiLanConnectionInfoTest, TestFromBadMaskIpv4) { + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for IPV4 address only. + serialized[3] = 0x40; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV6 address only. + serialized[3] = 0x20; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for port only. + serialized[3] = 0x10; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for BSSID only. + serialized[3] = 0x08; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV4 + IPV6 addresses. + serialized[3] = 0x60; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV4 + port. + serialized[3] = 0x50; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV4 + BSSID. + serialized[3] = 0x48; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields present. + serialized[3] = 0x78; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x58; + EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(WifiLanConnectionInfoTest, TestFromBadMaskIpv6) { + WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid, kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for IPV4 address only. + serialized[3] = 0x40; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV6 address only. + serialized[3] = 0x20; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for port only. + serialized[3] = 0x10; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for BSSID only. + serialized[3] = 0x08; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Remove all masks. + serialized[3] = 0x00; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV4 + IPV6 addresses. + serialized[3] = 0x60; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV6 + port. + serialized[3] = 0x30; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV6 + BSSID. + serialized[3] = 0x28; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields present. + serialized[3] = 0x78; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with the correct mask. + serialized[3] = 0x38; + EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); +} + +TEST(WifiLanConnectionInfoTest, TestFromBadMaskEmpty) { + WifiLanConnectionInfo info("", "", "", kAction); + std::string serialized = info.ToDataElementBytes(); + // Set mask for IPV4 address only. + serialized[3] = 0x40; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for IPV6 address only. + serialized[3] = 0x20; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for port only. + serialized[3] = 0x10; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for BSSID only. + serialized[3] = 0x08; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Set mask for all fields. + serialized[3] = 0x78; + EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Verify OK with correct mask. + serialized[3] = 0x00; + EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); +} + } // namespace } // namespace nearby diff --git a/presence/presence_device.cc b/presence/presence_device.cc index 5aa6f62f..474a9d20 100644 --- a/presence/presence_device.cc +++ b/presence/presence_device.cc @@ -18,6 +18,7 @@ #include #include "internal/crypto/random.h" +#include "internal/device.h" #include "internal/platform/ble_connection_info.h" #include "internal/platform/implementation/system_clock.h" #include "presence/device_motion.h" @@ -47,7 +48,8 @@ PresenceDevice::PresenceDevice(DeviceMotion device_motion, std::vector PresenceDevice::GetConnectionInfos() const { - return {nearby::BleConnectionInfo(metadata_.bluetooth_mac_address())}; + return {nearby::BleConnectionInfo(metadata_.bluetooth_mac_address(), + /*gatt_characteristic=*/"", /*psm=*/"", 0)}; } } // namespace presence } // namespace nearby diff --git a/presence/presence_device.h b/presence/presence_device.h index 7ba84094..94fd14eb 100644 --- a/presence/presence_device.h +++ b/presence/presence_device.h @@ -39,10 +39,7 @@ class PresenceDevice : public nearby::NearbyDevice { explicit PresenceDevice(Metadata metadata) noexcept; explicit PresenceDevice(DeviceMotion device_motion, Metadata metadata) noexcept; - absl::string_view GetEndpointId() const override { return endpoint_id_; } - void SetEndpointInfo(absl::string_view endpoint_info) { - endpoint_info_ = std::string(endpoint_info); - } + std::string GetEndpointId() const override { return endpoint_id_; } void AddExtendedProperty(const DataElement& data_element) { extended_properties_.push_back(data_element); } @@ -55,7 +52,6 @@ class PresenceDevice : public nearby::NearbyDevice { } void AddAction(const PresenceAction& action) { actions_.push_back(action); } std::vector GetActions() const { return actions_; } - absl::string_view GetEndpointInfo() const override { return endpoint_info_; } NearbyDevice::Type GetType() const override { return NearbyDevice::Type::kPresenceDevice; } @@ -73,7 +69,6 @@ class PresenceDevice : public nearby::NearbyDevice { std::vector extended_properties_; std::vector actions_; std::string endpoint_id_; - std::string endpoint_info_; }; // Timestamp is not used for equality since if the same device is discovered diff --git a/presence/presence_device_test.cc b/presence/presence_device_test.cc index a0f5f6ba..b6c1ff13 100644 --- a/presence/presence_device_test.cc +++ b/presence/presence_device_test.cc @@ -21,7 +21,6 @@ #include "gtest/gtest.h" #include "absl/types/variant.h" #include "internal/platform/ble_connection_info.h" -#include "internal/platform/logging.h" #include "presence/data_element.h" #include "presence/presence_action.h" @@ -78,9 +77,8 @@ TEST(PresenceDeviceTest, TestGetBluetoothAddress) { PresenceDevice device = PresenceDevice({kDefaultMotionType}, metadata); auto info = (device.GetConnectionInfos().at(0)); ASSERT_TRUE(absl::holds_alternative(info)); - EXPECT_EQ( - absl::get(info).GetMacAddress().AsStringView(), - kMacAddr); + EXPECT_EQ(absl::get(info).GetMacAddress(), + kMacAddr); } TEST(PresenceDevicetest, TestGetAddExtendedProperties) {