diff --git a/sharing/advertisement.cc b/sharing/advertisement.cc index 9ec19464..3fde440e 100644 --- a/sharing/advertisement.cc +++ b/sharing/advertisement.cc @@ -49,9 +49,36 @@ constexpr uint8_t kVisibilityBitmask = 0b1; // The bit mask for parsing and writing Device Type. constexpr uint8_t kDeviceTypeBitmask = 0b111; +// The minimum length of a TLV element. +// 1 byte for type and 1 byte for length, which can be 0. +constexpr uint8_t kTlvMinimumLength = 2; +// LINT.IfChange() +enum class TlvTypes : uint8_t { + kUnknown = 0, + kQrCode = 1, + kVendorId = 2, +}; + +enum class VendorId : uint8_t { + kNone = 0, + kSamsung = 1, +}; +// The length in bytes of the vendor ID in the TLV advertisement. +constexpr uint8_t kVendorIdLength = 1; + +// Turns |vendor_id| into a supported vendor ID. +uint8_t ConvertVendorId(uint8_t vendor_id) { + switch (vendor_id) { + case 1: + return 1; + default: + return 0; + } +} +// LINT.ThenChange(//depot/google3/java/com/google/android/gmscore/integ/modules/nearby/src/com/google/android/gms/nearby/sharing/provider/connections/certificatemanager/Advertisement.java) + const uint8_t kMinimumSize = - /* Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)= - */ + /* Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits) */ 1 + sharing::Advertisement::kSaltSize + sharing::Advertisement::kMetadataEncryptionKeyHashByteSize; @@ -94,14 +121,13 @@ ShareTargetType ParseDeviceType(uint8_t b) { bool ParseHasDeviceName(uint8_t b) { return ((b >> 4) & kVisibilityBitmask) == 0; } - } // namespace // static std::unique_ptr Advertisement::NewInstance( std::vector salt, std::vector encrypted_metadata_key, ShareTargetType device_type, std::optional device_name, - int32_t vendor_id) { + uint8_t vendor_id) { if (salt.size() != Advertisement::kSaltSize) { NL_LOG(ERROR) << "Failed to create advertisement because the salt did " "not match the expected length " @@ -132,8 +158,13 @@ std::unique_ptr Advertisement::NewInstance( } std::vector Advertisement::ToEndpointInfo() { + // We add 3 bytes for vendor ID because of type (1 byte), len (1 byte), and + // the ID itself (1 byte). int size = kMinimumSize + (device_name_.has_value() ? 1 : 0) + - (device_name_.has_value() ? device_name_->size() : 0); + (device_name_.has_value() ? device_name_->size() : 0) + + (vendor_id_ != static_cast(VendorId::kNone) + ? (kTlvMinimumLength + kVendorIdLength) + : 0); std::vector endpoint_info; endpoint_info.reserve(size); @@ -151,6 +182,16 @@ std::vector Advertisement::ToEndpointInfo() { device_name_->end()); } + // Add vendor ID if it is not the default |VendorId::kNone|. + if (vendor_id_ != static_cast(VendorId::kNone)) { + // Add vendor ID in TLV format. + endpoint_info.push_back(static_cast(TlvTypes::kVendorId)); + // Length is 1 byte. + endpoint_info.push_back(kVendorIdLength); + // The vendor ID itself. + endpoint_info.push_back(vendor_id_); + } + return endpoint_info; } @@ -182,27 +223,54 @@ std::unique_ptr Advertisement::FromEndpointInfo( iter, iter + Advertisement::kMetadataEncryptionKeyHashByteSize); iter += Advertisement::kMetadataEncryptionKeyHashByteSize; - int device_name_length = 0; - if (iter != endpoint_info.end()) device_name_length = *iter++ & 0xff; - - if (endpoint_info.end() - iter < device_name_length || - (device_name_length == 0 && has_device_name)) { - NL_LOG(ERROR) - << "Failed to parse advertisement because the device name did " - "not match the expected length " - << device_name_length; - return nullptr; - } - std::optional optional_device_name; - if (device_name_length > 0) { + if (has_device_name) { + int device_name_length = 0; + if (iter != endpoint_info.end()) device_name_length = *iter++ & 0xff; + + if (device_name_length == 0 || + (endpoint_info.end() - iter < device_name_length)) { + NL_LOG(ERROR) + << "Failed to parse advertisement because the device name did " + "not match the expected length " + << device_name_length; + return nullptr; + } + optional_device_name = std::string(iter, iter + device_name_length); iter += device_name_length; } + uint8_t vendor_id = static_cast(VendorId::kNone); + while (endpoint_info.end() - iter >= kTlvMinimumLength) { + // We will parse a TLV element now. + TlvTypes type = static_cast(*iter++); + uint8_t value_len = *iter++; + if (endpoint_info.end() - iter < value_len) { + NL_LOG(ERROR) << "Invalid length when parsing TLV element: " << value_len; + return nullptr; + } + switch (type) { + case TlvTypes::kVendorId: + if (value_len != kVendorIdLength) { + NL_LOG(ERROR) << "Invalid vendor_id_len: " << value_len; + return nullptr; + } + vendor_id = ConvertVendorId(*iter++); + break; + case TlvTypes::kQrCode: + // TODO: b/341984671 - Implement handling for this TLV type. + break; + default: + NL_LOG(ERROR) << "Unknown TLV type: " << static_cast(type); + break; + } + iter += value_len; + } + return Advertisement::NewInstance( std::move(salt), std::move(encrypted_metadata_key), device_type, - std::move(optional_device_name), /*vendor_id=*/0); + std::move(optional_device_name), vendor_id); } bool Advertisement::operator==(const Advertisement& other) const { @@ -217,7 +285,7 @@ Advertisement::Advertisement(int version, std::vector salt, std::vector encrypted_metadata_key, ShareTargetType device_type, std::optional device_name, - int32_t vendor_id) + uint8_t vendor_id) : version_(version), salt_(std::move(salt)), encrypted_metadata_key_(std::move(encrypted_metadata_key)), diff --git a/sharing/advertisement.h b/sharing/advertisement.h index bd496082..6570a1bd 100644 --- a/sharing/advertisement.h +++ b/sharing/advertisement.h @@ -40,12 +40,14 @@ class Advertisement { static std::unique_ptr NewInstance( std::vector salt, std::vector encrypted_metadata_key, ShareTargetType device_type, std::optional device_name, - int32_t vendor_id); + uint8_t vendor_id); + // TODO: b/341967036 - Remove uses of std::optional for device name. Empty + // string should be enough. Advertisement(int version, std::vector salt, std::vector encrypted_metadata_key, ShareTargetType device_type, - std::optional device_name, int32_t vendor_id); + std::optional device_name, uint8_t vendor_id); ~Advertisement() = default; Advertisement(const Advertisement&) = default; Advertisement& operator=(const Advertisement&) = default; @@ -63,7 +65,7 @@ class Advertisement { ShareTargetType device_type() const { return device_type_; } const std::optional& device_name() const { return device_name_; } bool HasDeviceName() const { return device_name_.has_value(); } - int32_t vendor_id() const { return vendor_id_; } + uint8_t vendor_id() const { return vendor_id_; } static std::unique_ptr FromEndpointInfo( absl::Span endpoint_info); @@ -91,7 +93,7 @@ class Advertisement { // The vendor identifier of the remote device. Reference for vendor ID: // google3/java/com/google/android/gmscore/integ/client/nearby/src/com/google/android/gms/nearby/sharing/SharingOptions.java - const int32_t vendor_id_; + const uint8_t vendor_id_; }; } // namespace sharing diff --git a/sharing/advertisement_test.cc b/sharing/advertisement_test.cc index f370d36c..577aa15c 100644 --- a/sharing/advertisement_test.cc +++ b/sharing/advertisement_test.cc @@ -67,6 +67,21 @@ INSTANTIATE_TEST_SUITE_P( .target_type = ShareTargetType::kTablet, .target_name = std::nullopt, .vendor_id = 0})); +INSTANTIATE_TEST_SUITE_P( + VendorIds, AdvertisementTest, + testing::Values( + TestParameters{.salt = std::vector(Advertisement::kSaltSize), + .encrypted_metadata_key = std::vector( + Advertisement::kMetadataEncryptionKeyHashByteSize), + .target_type = ShareTargetType::kPhone, + .target_name = std::nullopt, + .vendor_id = 0}, + TestParameters{.salt = std::vector(Advertisement::kSaltSize), + .encrypted_metadata_key = std::vector( + Advertisement::kMetadataEncryptionKeyHashByteSize), + .target_type = ShareTargetType::kPhone, + .target_name = std::nullopt, + .vendor_id = 1})); INSTANTIATE_TEST_SUITE_P( TargetNames, AdvertisementTest, testing::Values(