Add AdvertisementCapabilities to Nearby Share advertisements.

PiperOrigin-RevId: 874144304
This commit is contained in:
Francis Tsui
2026-02-23 10:32:11 -08:00
committed by Copybara-Service
parent 7034015127
commit 295f480dc1
10 changed files with 273 additions and 28 deletions
+12
View File
@@ -74,10 +74,12 @@ cc_library(
name = "types",
srcs = [
"advertisement.cc",
"advertisement_capabilities.cc",
"share_target.cc",
],
hdrs = [
"advertisement.h",
"advertisement_capabilities.h",
"constants.h",
"nearby_connection.h",
"nearby_connections_manager.h",
@@ -1000,3 +1002,13 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "advertisement_capabilities_test",
srcs = ["advertisement_capabilities_test.cc"],
deps = [
":types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
+33 -12
View File
@@ -23,11 +23,11 @@
#include <vector>
#include "absl/types/span.h"
#include "sharing/advertisement_capabilities.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace sharing {
namespace nearby::sharing {
namespace {
// v1 advertisements:
@@ -57,6 +57,7 @@ enum class TlvTypes : uint8_t {
kUnknown = 0,
kQrCode = 1,
kVendorId = 2,
kCapabilities = 3,
};
// The length in bytes of the vendor ID in the TLV advertisement.
constexpr uint8_t kVendorIdLength = 1;
@@ -126,7 +127,7 @@ bool ParseHasDeviceName(uint8_t b) {
std::unique_ptr<Advertisement> Advertisement::NewInstance(
std::vector<uint8_t> salt, std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type, std::optional<std::string> device_name,
uint8_t vendor_id) {
uint8_t vendor_id, AdvertisementCapabilities capabilities) {
if (salt.size() != Advertisement::kSaltSize) {
LOG(ERROR) << "Failed to create advertisement because the salt did "
"not match the expected length "
@@ -153,17 +154,21 @@ std::unique_ptr<Advertisement> Advertisement::NewInstance(
// Using `new` to access a non-public constructor.
return std::make_unique<Advertisement>(
/* version= */ 0, std::move(salt), std::move(encrypted_metadata_key),
device_type, std::move(device_name), vendor_id);
device_type, std::move(device_name), vendor_id, std::move(capabilities));
}
std::vector<uint8_t> Advertisement::ToEndpointInfo() const {
std::vector<uint8_t> capabilities_data = capabilities_.ToBytes();
// 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) +
(vendor_id_ != static_cast<uint8_t>(BlockedVendorId::kNone)
? (kTlvMinimumLength + kVendorIdLength)
: 0);
: 0) +
(capabilities_.IsEmpty() || capabilities_data.empty()
? 0
: (kTlvMinimumLength + capabilities_data.size()));
std::vector<uint8_t> endpoint_info;
endpoint_info.reserve(size);
@@ -190,6 +195,14 @@ std::vector<uint8_t> Advertisement::ToEndpointInfo() const {
// The vendor ID itself.
endpoint_info.push_back(vendor_id_);
}
// Add capabilities TLV
if (!capabilities_.IsEmpty() && !capabilities_data.empty()) {
VLOG(1) << "Adding capabilities to advertisement";
endpoint_info.push_back(static_cast<uint8_t>(TlvTypes::kCapabilities));
endpoint_info.push_back(static_cast<uint8_t>(capabilities_data.size()));
endpoint_info.insert(endpoint_info.end(), capabilities_data.begin(),
capabilities_data.end());
}
return endpoint_info;
}
@@ -204,7 +217,7 @@ std::unique_ptr<Advertisement> Advertisement::FromEndpointInfo(
return nullptr;
}
auto iter = endpoint_info.begin();
auto iter = endpoint_info.cbegin();
uint8_t first_byte = *iter++;
int version = ParseVersion(first_byte);
@@ -242,6 +255,7 @@ std::unique_ptr<Advertisement> Advertisement::FromEndpointInfo(
}
uint8_t vendor_id = static_cast<uint8_t>(BlockedVendorId::kNone);
AdvertisementCapabilities capabilities{};
while (endpoint_info.end() - iter >= kTlvMinimumLength) {
// We will parse a TLV element now.
TlvTypes type = static_cast<TlvTypes>(*iter++);
@@ -263,6 +277,11 @@ std::unique_ptr<Advertisement> Advertisement::FromEndpointInfo(
// TODO: b/341984671 - Implement handling for this TLV type.
iter += value_len;
break;
case TlvTypes::kCapabilities:
capabilities = AdvertisementCapabilities::Parse(
absl::MakeConstSpan(iter, value_len));
iter += value_len;
break;
default:
LOG(ERROR) << "Unknown TLV type: " << static_cast<uint8_t>(type);
iter += value_len;
@@ -272,7 +291,7 @@ std::unique_ptr<Advertisement> Advertisement::FromEndpointInfo(
return Advertisement::NewInstance(
std::move(salt), std::move(encrypted_metadata_key), device_type,
std::move(optional_device_name), vendor_id);
std::move(optional_device_name), vendor_id, std::move(capabilities));
// LINT.ThenChange(//depot/google3/third_party/nearby/connections/implementation/mediums/advertisements/advertisement_util.cc)
}
@@ -280,7 +299,8 @@ bool Advertisement::operator==(const Advertisement& other) const {
return version_ == other.version_ && salt_ == other.salt_ &&
encrypted_metadata_key_ == other.encrypted_metadata_key_ &&
device_type_ == other.device_type_ &&
device_name_ == other.device_name_ && vendor_id_ == other.vendor_id_;
device_name_ == other.device_name_ && vendor_id_ == other.vendor_id_ &&
capabilities_.ToBytes() == other.capabilities_.ToBytes();
}
// private
@@ -288,13 +308,14 @@ Advertisement::Advertisement(int version, std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type,
std::optional<std::string> device_name,
uint8_t vendor_id)
uint8_t vendor_id,
AdvertisementCapabilities capabilities)
: version_(version),
salt_(std::move(salt)),
encrypted_metadata_key_(std::move(encrypted_metadata_key)),
device_type_(device_type),
device_name_(std::move(device_name)),
vendor_id_(vendor_id) {}
vendor_id_(vendor_id),
capabilities_(std::move(capabilities)) {}
} // namespace sharing
} // namespace nearby
} // namespace nearby::sharing
+10 -9
View File
@@ -23,10 +23,10 @@
#include <vector>
#include "absl/types/span.h"
#include "sharing/advertisement_capabilities.h"
#include "sharing/common/nearby_share_enums.h"
namespace nearby {
namespace sharing {
namespace nearby::sharing {
// An advertisement in the form of
// [VERSION|VISIBILITY][SALT][ACCOUNT_IDENTIFIER][LEN][DEVICE_NAME].
@@ -44,17 +44,17 @@ class Advertisement {
};
// LINT.ThenChange(//depot/google3/java/com/google/android/gmscore/integ/client/nearby/src/com/google/android/gms/nearby/sharing/SharingOptions.java:VendorId)
static std::unique_ptr<Advertisement> NewInstance(
std::vector<uint8_t> salt, std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type, std::optional<std::string> device_name,
uint8_t vendor_id);
uint8_t vendor_id, AdvertisementCapabilities capabilities);
// TODO: b/341967036 - Remove uses of std::optional for device name. Empty
// string should be enough.
Advertisement(int version, std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type,
std::optional<std::string> device_name, uint8_t vendor_id);
std::optional<std::string> device_name, uint8_t vendor_id,
AdvertisementCapabilities capabilities);
~Advertisement() = default;
Advertisement(const Advertisement&) = default;
Advertisement& operator=(const Advertisement&) = default;
@@ -96,14 +96,15 @@ class Advertisement {
ShareTargetType device_type_ = ShareTargetType::kUnknown;
// The human-readable name of the remote device.
std::optional<std::string> device_name_ = std::nullopt;
const std::optional<std::string> device_name_;
// 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 uint8_t vendor_id_;
const AdvertisementCapabilities capabilities_;
};
} // namespace sharing
} // namespace nearby
} // namespace nearby::sharing
#endif // THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_H_
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2025 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 "sharing/advertisement_capabilities.h"
#include <cstdint>
#include <vector>
#include "absl/types/span.h"
#include "sharing/internal/public/logging.h"
namespace nearby::sharing {
constexpr uint8_t kFileSyncMask = 0b00000001;
AdvertisementCapabilities AdvertisementCapabilities::Parse(
absl::Span<const uint8_t> data) {
AdvertisementCapabilities capabilities{};
for (const uint8_t byte : data) {
switch (byte) {
case static_cast<uint8_t>(Capability::kFileSync):
capabilities.Add(Capability::kFileSync);
break;
default:
continue;
}
}
return capabilities;
}
std::vector<uint8_t> AdvertisementCapabilities::ToBytes() const {
std::vector<uint8_t> bytes;
for (const Capability capability : capabilities_) {
switch (capability) {
case Capability::kFileSync:
if (bytes.empty()) {
bytes.resize(1);
}
bytes[0] |= kFileSyncMask;
break;
default:
LOG(DFATAL) << "Unhandled capability: "
<< static_cast<uint8_t>(capability);
break;
}
}
return bytes;
}
} // namespace nearby::sharing
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2025 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 THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_CAPABILITIES_H_
#define THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_CAPABILITIES_H_
#include <cstdint>
#include <initializer_list>
#include <vector>
#include "absl/types/span.h"
namespace nearby::sharing {
// A container class for storing capabilities to be added to QuickShare
// advertisements.
class AdvertisementCapabilities {
public:
enum class Capability {
kInvalid = 0,
kFileSync = 1, // File sync extension support.
};
// Parses serialized capabilities from an advertisement.
static AdvertisementCapabilities Parse(absl::Span<const uint8_t> data);
AdvertisementCapabilities(std::initializer_list<Capability> capabilities)
: capabilities_(capabilities) {}
void Add(Capability capability) { capabilities_.push_back(capability); }
// Returns true if there are no capabilities in this object.
bool IsEmpty() const { return capabilities_.empty(); }
// Serializes the capabilities into a byte array for inclusion in an
// advertisement.
std::vector<uint8_t> ToBytes() const;
private:
std::vector<Capability> capabilities_;
};
} // namespace nearby::sharing
#endif // THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_CAPABILITIES_H_
@@ -0,0 +1,67 @@
// Copyright 2025 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 "sharing/advertisement_capabilities.h"
#include <cstdint>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby::sharing {
namespace {
TEST(AdvertisementCapabilitiesTest, ParseEmpty) {
EXPECT_TRUE(AdvertisementCapabilities::Parse({}).IsEmpty());
}
TEST(AdvertisementCapabilitiesTest, ParseFileSync) {
uint8_t data[] = {
static_cast<uint8_t>(AdvertisementCapabilities::Capability::kFileSync)};
AdvertisementCapabilities capabilities =
AdvertisementCapabilities::Parse(data);
EXPECT_FALSE(capabilities.IsEmpty());
EXPECT_THAT(capabilities.ToBytes(), testing::ElementsAre(0x01));
}
TEST(AdvertisementCapabilitiesTest, ToBytesEmpty) {
AdvertisementCapabilities capabilities({});
EXPECT_TRUE(capabilities.ToBytes().empty());
}
TEST(AdvertisementCapabilitiesTest, ToBytesFileSync) {
AdvertisementCapabilities capabilities(
{AdvertisementCapabilities::Capability::kFileSync});
EXPECT_THAT(capabilities.ToBytes(), testing::ElementsAre(0x01));
}
TEST(AdvertisementCapabilitiesTest, AddCapability) {
AdvertisementCapabilities capabilities({});
EXPECT_TRUE(capabilities.IsEmpty());
capabilities.Add(AdvertisementCapabilities::Capability::kFileSync);
EXPECT_FALSE(capabilities.IsEmpty());
EXPECT_THAT(capabilities.ToBytes(), testing::ElementsAre(0x01));
}
TEST(AdvertisementCapabilitiesTest, MultipleAdds) {
// Currently only kFileSync is supported.
AdvertisementCapabilities capabilities({});
capabilities.Add(AdvertisementCapabilities::Capability::kFileSync);
capabilities.Add(AdvertisementCapabilities::Capability::kFileSync);
// Duplicate adds should still result in bit 0 being set.
EXPECT_THAT(capabilities.ToBytes(), testing::ElementsAre(0x01));
}
} // namespace
} // namespace nearby::sharing
+23 -2
View File
@@ -21,6 +21,7 @@
#include "gtest/gtest.h"
#include "absl/types/span.h"
#include "sharing/advertisement_capabilities.h"
#include "sharing/common/nearby_share_enums.h"
namespace nearby {
@@ -43,6 +44,7 @@ struct TestParameters {
ShareTargetType target_type;
std::optional<std::string> target_name;
int vendor_id;
AdvertisementCapabilities capabilities;
};
class AdvertisementTest : public testing::TestWithParam<TestParameters> {};
@@ -51,17 +53,18 @@ TEST_P(AdvertisementTest, TestAdvertisementRoundTrip) {
auto params = GetParam();
auto advertisement = Advertisement::NewInstance(
params.salt, params.encrypted_metadata_key, params.target_type,
params.target_name, params.vendor_id);
params.target_name, params.vendor_id, params.capabilities);
auto bytes = advertisement->ToEndpointInfo();
auto advertisement_from_bytes = Advertisement::FromEndpointInfo(bytes);
EXPECT_EQ(*advertisement_from_bytes, *advertisement);
}
TEST(BadAdvertisementTest, TestTlvParsingOnAdvertisement) {
AdvertisementCapabilities capabilities{};
auto advertisement = Advertisement::NewInstance(
std::vector<uint8_t>(Advertisement::kSaltSize),
std::vector<uint8_t>(Advertisement::kMetadataEncryptionKeyHashByteSize),
ShareTargetType::kLaptop, std::nullopt, /*vendor_id=*/1);
ShareTargetType::kLaptop, std::nullopt, /*vendor_id=*/1, capabilities);
auto bytes = advertisement->ToEndpointInfo();
// Add a TLV field for QR code.
bytes.insert(bytes.end(), kQrCodeTlvBytes.begin(), kQrCodeTlvBytes.end());
@@ -149,6 +152,24 @@ INSTANTIATE_TEST_SUITE_P(
.target_type = ShareTargetType::kLaptop,
.target_name = std::nullopt,
.vendor_id = 0}));
INSTANTIATE_TEST_SUITE_P(
Capabilities, AdvertisementTest,
testing::Values(
TestParameters{.salt = std::vector<uint8_t>(Advertisement::kSaltSize),
.encrypted_metadata_key = std::vector<uint8_t>(
Advertisement::kMetadataEncryptionKeyHashByteSize),
.target_type = ShareTargetType::kPhone,
.target_name = std::nullopt,
.vendor_id = 0,
.capabilities = AdvertisementCapabilities{}},
TestParameters{.salt = std::vector<uint8_t>(Advertisement::kSaltSize),
.encrypted_metadata_key = std::vector<uint8_t>(
Advertisement::kMetadataEncryptionKeyHashByteSize),
.target_type = ShareTargetType::kPhone,
.target_name = std::nullopt,
.vendor_id = 0,
.capabilities = AdvertisementCapabilities{
AdvertisementCapabilities::Capability::kFileSync}}));
} // namespace
} // namespace sharing
+7 -1
View File
@@ -51,6 +51,7 @@
#include "internal/platform/task_runner.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/advertisement.h"
#include "sharing/advertisement_capabilities.h"
#include "sharing/analytics/analytics_information.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/attachment_container.h"
@@ -1503,11 +1504,16 @@ NearbySharingServiceImpl::CreateEndpointInfo(
ShareTargetType device_type =
static_cast<ShareTargetType>(device_info_.GetDeviceType());
AdvertisementCapabilities capabilities{};
if (supports_file_sync_) {
capabilities.Add(AdvertisementCapabilities::Capability::kFileSync);
}
std::unique_ptr<Advertisement> advertisement = Advertisement::NewInstance(
std::move(salt), std::move(encrypted_key), device_type, device_name,
visibility == DeviceVisibility::DEVICE_VISIBILITY_EVERYONE
? static_cast<uint8_t>(GetReceivingVendorId())
: static_cast<uint8_t>(BlockedVendorId::kNone));
: static_cast<uint8_t>(BlockedVendorId::kNone),
std::move(capabilities));
if (advertisement) {
return advertisement->ToEndpointInfo();
} else {
+1
View File
@@ -524,6 +524,7 @@ class NearbySharingServiceImpl
// If true, a new endpoint id will be generated at the next advertisement.
bool force_new_endpoint_id_ = false;
OutgoingTargetsManager outgoing_targets_manager_;
bool supports_file_sync_ = false;
};
} // namespace nearby::sharing
+4 -4
View File
@@ -30,7 +30,6 @@
#include <utility>
#include <vector>
#include "base/casts.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
@@ -54,6 +53,7 @@
#include "internal/test/fake_task_runner.h"
#include "internal/test/mock_account_observer.h"
#include "sharing/advertisement.h"
#include "sharing/advertisement_capabilities.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/attachment_container.h"
#include "sharing/certificates/fake_nearby_share_certificate_manager.h"
@@ -739,7 +739,7 @@ class NearbySharingServiceImplTest : public testing::Test {
std::unique_ptr<Advertisement> advertisement = Advertisement::NewInstance(
GetNearbyShareTestEncryptedMetadataKey().salt(),
GetNearbyShareTestEncryptedMetadataKey().encrypted_key(), kDeviceType,
kDeviceName, vendor_id);
kDeviceName, vendor_id, AdvertisementCapabilities{});
return advertisement->ToEndpointInfo();
}
@@ -1079,7 +1079,7 @@ class NearbySharingServiceImplTest : public testing::Test {
std::unique_ptr<Advertisement> advertisement = Advertisement::NewInstance(
GetNearbyShareTestEncryptedMetadataKey().salt(),
GetNearbyShareTestEncryptedMetadataKey().encrypted_key(), kDeviceType,
std::nullopt, kVendorId);
std::nullopt, kVendorId, AdvertisementCapabilities{});
return advertisement->ToEndpointInfo();
}
@@ -4517,7 +4517,7 @@ TEST_F(NearbySharingServiceImplTest, CreateShareTarget) {
std::unique_ptr<Advertisement> advertisement = Advertisement::NewInstance(
GetNearbyShareTestEncryptedMetadataKey().salt(),
GetNearbyShareTestEncryptedMetadataKey().encrypted_key(), kDeviceType,
kDeviceName, kVendorId);
kDeviceName, kVendorId, AdvertisementCapabilities{});
// Flip |for_self_share| to true to ensure the resulting ShareTarget picks
// this up.