[BLE Refactor] Implements BleExtraField in mediums::BleAdvertisement for Extended advertisement.

PiperOrigin-RevId: 441665698
This commit is contained in:
edwinwu
2022-04-13 22:06:57 -07:00
committed by Copybara-Service
parent f8dbc648b5
commit d1b2437b0d
3 changed files with 115 additions and 14 deletions
@@ -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(),