mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
[BLE Refactor] Implements BleExtraField in mediums::BleAdvertisement for Extended advertisement.
PiperOrigin-RevId: 441665698
This commit is contained in:
committed by
Copybara-Service
parent
f8dbc648b5
commit
d1b2437b0d
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -29,6 +30,16 @@ namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint8_t kPsmBitmask = 0x01;
|
||||
|
||||
bool HasField(uint8_t field_mask, uint8_t psm_bit) {
|
||||
return (field_mask & psm_bit) > 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BleAdvertisement::BleAdvertisement(Version version,
|
||||
SocketVersion socket_version,
|
||||
const ByteArray &service_id_hash,
|
||||
@@ -163,11 +174,12 @@ BleAdvertisement::BleAdvertisement(const ByteArray &ble_advertisement_bytes) {
|
||||
// this advertisement. That means it must support device token if there's any
|
||||
// extra field. E.g. If iOS or other platform wants to use extra fields, need
|
||||
// to put a random or empty device token in the advertisement.
|
||||
// TODO(b/219939733): Implement BleExtraField to read the PSM value. We fake
|
||||
// this extra field as uint16, 2 bytes.
|
||||
if (base_input_stream.IsAvailable(
|
||||
BleAdvertisementHeader::kPsmValueByteLength)) {
|
||||
psm_ = static_cast<int>(base_input_stream.ReadUint16());
|
||||
int extra_fields_byte_number =
|
||||
kExtraFieldsMaskLength + BleAdvertisementHeader::kPsmValueByteLength;
|
||||
if (base_input_stream.IsAvailable(extra_fields_byte_number)) {
|
||||
BleExtraFields extra_fields{
|
||||
base_input_stream.ReadBytes(extra_fields_byte_number)};
|
||||
psm_ = extra_fields.GetPsm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,15 +226,12 @@ BleAdvertisement::operator ByteArray() const {
|
||||
|
||||
ByteArray BleAdvertisement::ByteArrayWithExtraField() const {
|
||||
ByteArray advertisement_bytes = ByteArray(*this);
|
||||
// TODO(b/219939733): Implement BleExtraField for PSM value.
|
||||
ByteArray psm_byte{BleAdvertisementHeader::kPsmValueByteLength};
|
||||
char *data = psm_byte.data();
|
||||
data[0] = psm_ & 0xFF00;
|
||||
data[1] = psm_ & 0x00FF;
|
||||
std::string advertisement_with_extra_bytes =
|
||||
absl::StrCat(std::string(advertisement_bytes), std::string(psm_byte));
|
||||
ByteArray extra_fields_bytes = ByteArray(BleExtraFields(psm_));
|
||||
|
||||
return ByteArray(std::move(advertisement_with_extra_bytes));
|
||||
std::string advertisement_with_extra_fields_bytes = absl::StrCat(
|
||||
std::string(advertisement_bytes), std::string(extra_fields_bytes));
|
||||
|
||||
return ByteArray(std::move(advertisement_with_extra_fields_bytes));
|
||||
}
|
||||
|
||||
bool BleAdvertisement::operator==(const BleAdvertisement &rhs) const {
|
||||
@@ -261,6 +270,45 @@ void BleAdvertisement::SerializeDataSize(bool fast_advertisement,
|
||||
}
|
||||
}
|
||||
|
||||
BleAdvertisement::BleExtraFields::BleExtraFields(int psm) : psm_(psm) {}
|
||||
|
||||
BleAdvertisement::BleExtraFields::BleExtraFields(
|
||||
const ByteArray &ble_extra_fields_bytes) {
|
||||
if (ble_extra_fields_bytes.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArray mutated_extra_fields_bytes = {ble_extra_fields_bytes};
|
||||
BaseInputStream base_input_stream{mutated_extra_fields_bytes};
|
||||
// The first 1 byte is field mask.
|
||||
auto mask_byte = static_cast<uint8_t>(base_input_stream.ReadUint8());
|
||||
if (!mask_byte) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The next 2 bytes are supposed to be the psm value.
|
||||
if (HasField(mask_byte, kPsmBitmask) &&
|
||||
base_input_stream.IsAvailable(
|
||||
BleAdvertisementHeader::kPsmValueByteLength)) {
|
||||
psm_ = static_cast<int>(base_input_stream.ReadUint16());
|
||||
}
|
||||
}
|
||||
|
||||
BleAdvertisement::BleExtraFields::operator ByteArray() const {
|
||||
if (psm_ == BleAdvertisementHeader::kDefaultPsmValue) {
|
||||
return ByteArray{};
|
||||
}
|
||||
|
||||
ByteArray psm_byte{BleAdvertisementHeader::kPsmValueByteLength};
|
||||
char *data = psm_byte.data();
|
||||
data[0] = psm_ & 0xFF00;
|
||||
data[1] = psm_ & 0x00FF;
|
||||
|
||||
std::string out =
|
||||
absl::StrCat(std::string(1, kPsmBitmask), std::string(psm_byte));
|
||||
return ByteArray{std::move(out)};
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
|
||||
@@ -100,6 +100,30 @@ class BleAdvertisement {
|
||||
int GetPsm() const { return psm_; }
|
||||
|
||||
private:
|
||||
// Represents the extra fields of the `BleAdvertisement` used in Advertising +
|
||||
// Discovery. The maximum number of extra fields is 8. The format of the field
|
||||
// can be different, but the order of the fields should be fixed.
|
||||
//
|
||||
// e.g. [BIT_MASK][X_FIELD(2 Bytes)][LENGTH(2 Bytes) + Y_FIELD(n Bytes)]
|
||||
//
|
||||
// Below is the current fields
|
||||
// [BIT_MASK][PSM_VALUE(2 Bytes)]
|
||||
//
|
||||
// The PSM (protocol service multiplexer) value is used for create data
|
||||
// connection on L2CAP socket. It only exists when remote device supports
|
||||
// L2CAP socket feature.
|
||||
class BleExtraFields {
|
||||
public:
|
||||
explicit BleExtraFields(int psm);
|
||||
explicit BleExtraFields(const ByteArray &ble_extra_fields_bytes);
|
||||
explicit operator ByteArray() const;
|
||||
|
||||
int GetPsm() const { return psm_; }
|
||||
|
||||
private:
|
||||
int psm_ = BleAdvertisementHeader::kDefaultPsmValue;
|
||||
};
|
||||
|
||||
void DoInitialize(bool fast_advertisement, Version version,
|
||||
SocketVersion socket_version,
|
||||
const ByteArray &service_id_hash, const ByteArray &data,
|
||||
@@ -137,6 +161,7 @@ class BleAdvertisement {
|
||||
// required header that comes before the service data, this leaves the
|
||||
// advertiser with 27 leftover bytes.
|
||||
static constexpr int kMaxFastAdvertisementLength = 27;
|
||||
static constexpr int kExtraFieldsMaskLength = 1;
|
||||
|
||||
Version version_{Version::kUndefined};
|
||||
SocketVersion socket_version_{SocketVersion::kUndefined};
|
||||
|
||||
@@ -470,7 +470,8 @@ TEST(BleAdvertisementTest, ConstructionWorksWithPsmValue) {
|
||||
EXPECT_EQ(psm, ble_advertisement.GetPsm());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionFromSerializedBytesWithPsmValueWorks) {
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFromSerializedBytesWithExtraFieldsWitPsmValueWorks) {
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray data{std::string(kData)};
|
||||
ByteArray device_token{std::string(kDeviceToken)};
|
||||
@@ -493,6 +494,33 @@ TEST(BleAdvertisementTest, ConstructionFromSerializedBytesWithPsmValueWorks) {
|
||||
EXPECT_EQ(psm, ble_advertisement.GetPsm());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFromSerializedBytesWithExtraFieldsWithoutPsmValueFails) {
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray data{std::string(kData)};
|
||||
ByteArray device_token{std::string(kDeviceToken)};
|
||||
|
||||
// Construct BleAdvertisement without psm.
|
||||
BleAdvertisement original_ble_advertisement(
|
||||
kVersion, kSocketVersion, service_id_hash, data, device_token);
|
||||
// But use ByteArrayWithExtraField to restore back. It should fail.
|
||||
ByteArray ble_advertisement_bytes =
|
||||
original_ble_advertisement.ByteArrayWithExtraField();
|
||||
BleAdvertisement ble_advertisement(ble_advertisement_bytes);
|
||||
|
||||
ASSERT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_FALSE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kSocketVersion, ble_advertisement.GetSocketVersion());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(data.size(), ble_advertisement.GetData().size());
|
||||
EXPECT_EQ(data, ble_advertisement.GetData());
|
||||
EXPECT_EQ(device_token, ble_advertisement.GetDeviceToken());
|
||||
// The psm value should be default one.
|
||||
EXPECT_EQ(BleAdvertisementHeader::kDefaultPsmValue,
|
||||
ble_advertisement.GetPsm());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, Hash) {
|
||||
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
|
||||
BleAdvertisement(),
|
||||
|
||||
Reference in New Issue
Block a user