Decode the action in Base NP advertisements back into a list of Data Elements

PiperOrigin-RevId: 466204445
This commit is contained in:
Janusz Sobczak
2022-08-08 19:39:05 -07:00
committed by Copybara-Service
parent 076b00ed61
commit 6e6ae3ebd7
9 changed files with 140 additions and 18 deletions
+1
View File
@@ -133,6 +133,7 @@ cc_library(
":broadcast_request",
":types",
"//internal/platform:logging",
"@com_google_absl//absl/types:optional",
],
)
+42 -3
View File
@@ -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
+12 -9
View File
@@ -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
+29 -3
View File
@@ -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
+2 -2
View File
@@ -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")
+7
View File
@@ -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
+1
View File
@@ -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);