mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 07:36:10 -04:00
Decode the action in Base NP advertisements back into a list of Data Elements
PiperOrigin-RevId: 466204445
This commit is contained in:
committed by
Copybara-Service
parent
076b00ed61
commit
6e6ae3ebd7
@@ -133,6 +133,7 @@ cc_library(
|
||||
":broadcast_request",
|
||||
":types",
|
||||
"//internal/platform:logging",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
@@ -51,9 +52,26 @@ int GetActionMask(int action) {
|
||||
return kEmptyMask;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
// The reverse of `GetActionMask()`
|
||||
absl::optional<uint8_t> GetActionFromBitMask(int mask) {
|
||||
switch (mask) {
|
||||
case kTapToTransferMask:
|
||||
return action::kTapToTransferAction;
|
||||
case kActiveUnlockMask:
|
||||
return action::kActiveUnlockAction;
|
||||
case kNearbyShareMask:
|
||||
return action::kNearbyShareAction;
|
||||
case kFastPairMask:
|
||||
return action::kFastPairAction;
|
||||
case kFitCastMask:
|
||||
return action::kFitCastAction;
|
||||
default:
|
||||
NEARBY_LOG(WARNING, "Unsupported action for bit mask 0x%x", mask);
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
int ActionFactory::GetMask(const DataElement& element) {
|
||||
int GetMask(const DataElement& element) {
|
||||
int type = element.GetType();
|
||||
switch (type) {
|
||||
case DataElement::kContextTimestampFieldType: {
|
||||
@@ -78,7 +96,9 @@ int ActionFactory::GetMask(const DataElement& element) {
|
||||
return kEmptyMask;
|
||||
}
|
||||
|
||||
Action ActionFactory::createAction(
|
||||
} // namespace
|
||||
|
||||
Action ActionFactory::CreateAction(
|
||||
const std::vector<DataElement>& data_elements) {
|
||||
Action action = {.action = 0};
|
||||
std::for_each(data_elements.begin(), data_elements.end(),
|
||||
@@ -89,5 +109,24 @@ Action ActionFactory::createAction(
|
||||
return action;
|
||||
}
|
||||
|
||||
void ActionFactory::DecodeAction(const Action& action,
|
||||
std::vector<DataElement>& output) {
|
||||
uint8_t context_timestamp =
|
||||
(action.action >> kContentTimestampShift) & kContentTimestampMask;
|
||||
if (context_timestamp) {
|
||||
output.emplace_back(DataElement::kContextTimestampFieldType,
|
||||
context_timestamp);
|
||||
}
|
||||
for (int i = 0; i < kContentTimestampShift; i++) {
|
||||
int bit_mask = 1 << i;
|
||||
if (action.action & bit_mask) {
|
||||
absl::optional<uint8_t> action_value = GetActionFromBitMask(bit_mask);
|
||||
if (action_value) {
|
||||
output.emplace_back(DataElement::kActionFieldType, *action_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace presence
|
||||
} // namespace nearby
|
||||
|
||||
@@ -23,18 +23,21 @@
|
||||
namespace nearby {
|
||||
namespace presence {
|
||||
|
||||
/** Defines the mapping between Data Elements and Actions in the Base NP
|
||||
* advertisement. */
|
||||
// Defines the mapping between Data Elements and Actions in the Base NP
|
||||
// advertisement.
|
||||
class ActionFactory {
|
||||
public:
|
||||
/** Returns an Action for Base NP advertisement from a collection of Data
|
||||
* Elements. Data Elements unsupported in the Base NP advertisement are
|
||||
* ignored.
|
||||
*/
|
||||
static Action createAction(const std::vector<DataElement>& data_elements);
|
||||
// Returns an Action for Base NP advertisement from a collection of Data
|
||||
// Elements. Data Elements unsupported in the Base NP advertisement are
|
||||
// ignored.
|
||||
static Action CreateAction(const std::vector<DataElement>& data_elements);
|
||||
|
||||
private:
|
||||
static int GetMask(const DataElement& element);
|
||||
// Decodes a Base NP Action into a list of Data Elements. The Data Elements
|
||||
// are appended to the `output` list.
|
||||
//
|
||||
// DecodeAction is effectively a reverse operation of CreateAction.
|
||||
static void DecodeAction(const Action& action,
|
||||
std::vector<DataElement>& output);
|
||||
};
|
||||
} // namespace presence
|
||||
} // namespace nearby
|
||||
|
||||
@@ -28,12 +28,14 @@ namespace nearby {
|
||||
namespace presence {
|
||||
namespace {
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
|
||||
TEST(ActionFactory, CreateActiveUnlockAction) {
|
||||
std::vector<DataElement> data_elements;
|
||||
data_elements.emplace_back(DataElement::kActionFieldType,
|
||||
action::kActiveUnlockAction);
|
||||
|
||||
Action action = ActionFactory::createAction(data_elements);
|
||||
Action action = ActionFactory::CreateAction(data_elements);
|
||||
|
||||
EXPECT_EQ(action.action, 1 << 7);
|
||||
}
|
||||
@@ -45,7 +47,7 @@ TEST(ActionFactory, CreateContextTimestamp) {
|
||||
data_elements.emplace_back(DataElement::kContextTimestampFieldType,
|
||||
kTimestamp);
|
||||
|
||||
Action action = ActionFactory::createAction(data_elements);
|
||||
Action action = ActionFactory::CreateAction(data_elements);
|
||||
|
||||
EXPECT_EQ(action.action, 0x0B << 12);
|
||||
}
|
||||
@@ -59,11 +61,35 @@ TEST(ActionFactory, CreateContextTimestampAndFastPair) {
|
||||
data_elements.emplace_back(DataElement::kActionFieldType,
|
||||
action::kFastPairAction);
|
||||
|
||||
Action action = ActionFactory::createAction(data_elements);
|
||||
Action action = ActionFactory::CreateAction(data_elements);
|
||||
|
||||
EXPECT_EQ(action.action, (0x0B << 12) | 0x20);
|
||||
}
|
||||
|
||||
TEST(ActionFactory, DecodeActiveUnlockAction) {
|
||||
constexpr Action kAction = {.action = 1 << 7};
|
||||
std::vector<DataElement> data_elements;
|
||||
|
||||
ActionFactory::DecodeAction(kAction, data_elements);
|
||||
|
||||
EXPECT_THAT(data_elements,
|
||||
ElementsAre(DataElement(DataElement::kActionFieldType,
|
||||
action::kActiveUnlockAction)));
|
||||
}
|
||||
|
||||
TEST(ActionFactory, DecodeContextTimestampAndFastPair) {
|
||||
constexpr Action kAction = {.action = (0x0B << 12) | 0x20};
|
||||
std::vector<DataElement> data_elements;
|
||||
|
||||
ActionFactory::DecodeAction(kAction, data_elements);
|
||||
|
||||
EXPECT_THAT(data_elements,
|
||||
ElementsAre(DataElement(DataElement::kContextTimestampFieldType,
|
||||
absl::HexStringToBytes("0B")),
|
||||
DataElement(DataElement::kActionFieldType,
|
||||
action::kFastPairAction)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace presence
|
||||
} // namespace nearby
|
||||
|
||||
@@ -52,7 +52,7 @@ TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) {
|
||||
std::vector<DataElement> data_elements;
|
||||
data_elements.emplace_back(DataElement::kActionFieldType,
|
||||
action::kActiveUnlockAction);
|
||||
Action action = ActionFactory::createAction(data_elements);
|
||||
Action action = ActionFactory::CreateAction(data_elements);
|
||||
BroadcastRequest request =
|
||||
BroadcastRequest(BasePresenceRequestBuilder(identity)
|
||||
.SetSalt(salt)
|
||||
@@ -87,7 +87,7 @@ TEST(AdvertisementFactory,
|
||||
std::vector<DataElement> data_elements;
|
||||
data_elements.emplace_back(DataElement::kActionFieldType,
|
||||
action::kActiveUnlockAction);
|
||||
Action action = ActionFactory::createAction(data_elements);
|
||||
Action action = ActionFactory::CreateAction(data_elements);
|
||||
BroadcastRequest request =
|
||||
BroadcastRequest(BasePresenceRequestBuilder(identity)
|
||||
.SetSalt("AB")
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
namespace nearby {
|
||||
namespace presence {
|
||||
@@ -77,6 +79,11 @@ inline bool operator==(const DataElement& i1, const DataElement& i2) {
|
||||
return i1.GetType() == i2.GetType() && i1.GetValue() == i2.GetValue();
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const DataElement& elem) {
|
||||
return os << "DataElement(" << elem.GetType() << ", "
|
||||
<< absl::BytesToHexString(elem.GetValue()) << ")";
|
||||
}
|
||||
|
||||
} // namespace presence
|
||||
} // namespace nearby
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ cc_library(
|
||||
"//internal/platform:comm",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//third_party/nearby/presence:action_factory",
|
||||
"//third_party/nearby/presence:advertisement_factory",
|
||||
"//third_party/nearby/presence:credential",
|
||||
"//third_party/nearby/presence:types",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "third_party/nearby/presence/implementation/advertisement_decoder.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "third_party/nearby/presence/action_factory.h"
|
||||
#include "third_party/nearby/presence/data_element.h"
|
||||
|
||||
namespace nearby {
|
||||
@@ -65,6 +67,23 @@ bool IsIdentity(int data_type) {
|
||||
data_type <= DataElement::kProvisionedIdentityFieldType;
|
||||
}
|
||||
|
||||
void DecodeBaseAction(absl::string_view serialized_action,
|
||||
std::vector<DataElement>& output) {
|
||||
if (serialized_action.size() != sizeof(uint16_t)) {
|
||||
NEARBY_LOGS(WARNING) << "Base NP action \'"
|
||||
<< absl::BytesToHexString(serialized_action)
|
||||
<< "\' has wrong length " << serialized_action.size()
|
||||
<< " , expected " << sizeof(uint16_t);
|
||||
return;
|
||||
}
|
||||
// Two bytes in Big Endian order
|
||||
uint8_t high = serialized_action[0];
|
||||
uint8_t low = serialized_action[1];
|
||||
Action action = {.action = static_cast<uint16_t>((high << 8) | low)};
|
||||
|
||||
ActionFactory::DecodeAction(action, output);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::StatusOr<std::vector<DataElement>>
|
||||
@@ -114,7 +133,11 @@ AdvertisementDecoder::DecodeAdvertisement(absl::string_view advertisement) {
|
||||
advertisement = *decrypted;
|
||||
index = 0;
|
||||
}
|
||||
result.push_back(*std::move(elem));
|
||||
if (elem->GetType() == DataElement::kActionFieldType) {
|
||||
DecodeBaseAction(elem->GetValue(), result);
|
||||
} else {
|
||||
result.push_back(*std::move(elem));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace presence {
|
||||
namespace {
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Return;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
using ::testing::status::StatusIs;
|
||||
|
||||
class MockCredentialManager : public CredentialManagerImpl {
|
||||
@@ -83,6 +84,27 @@ TEST(AdvertisementDecoder, DecodeBaseNpPublicAdvertisement) {
|
||||
DataElement(9, absl::HexStringToBytes("EEFF"))));
|
||||
}
|
||||
|
||||
TEST(AdvertisementDecoder, DecodeBaseNpPWithActionField) {
|
||||
std::string salt = "AB";
|
||||
MockCredentialManager credential_manager;
|
||||
AdvertisementDecoder decoder(&credential_manager);
|
||||
|
||||
auto result =
|
||||
decoder.DecodeAdvertisement(absl::HexStringToBytes("002041420326B840"));
|
||||
|
||||
EXPECT_OK(result);
|
||||
EXPECT_THAT(*result,
|
||||
UnorderedElementsAre(
|
||||
DataElement(DataElement::kSaltFieldType, salt),
|
||||
DataElement(DataElement::kPublicIdentityFieldType, ""),
|
||||
DataElement(DataElement::kContextTimestampFieldType,
|
||||
absl::HexStringToBytes("0B")),
|
||||
DataElement(DataElement::kActionFieldType,
|
||||
action::kTapToTransferAction),
|
||||
DataElement(DataElement::kActionFieldType,
|
||||
action::kNearbyShareAction)));
|
||||
}
|
||||
|
||||
TEST(AdvertisementDecoder, InvalidAdvertisementFieldTooShort) {
|
||||
MockCredentialManager credential_manager;
|
||||
AdvertisementDecoder decoder(&credential_manager);
|
||||
|
||||
Reference in New Issue
Block a user