remove unused code.

PiperOrigin-RevId: 911531448
This commit is contained in:
Francis Tsui
2026-05-06 13:54:19 -07:00
committed by Copybara-Service
parent 353f7d5a1d
commit 6b2f1b96eb
126 changed files with 13 additions and 15052 deletions
-12
View File
@@ -48,15 +48,3 @@ jobs:
- uses: actions/checkout@v6
- name: Build Connections
run: CC=clang-18 CXX=clang-18++ BAZEL_CXXOPTS="-std=c++20" bazel build --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true --copt='-DGITHUB_BUILD' //connections:core
# - name: Build Presence
# run: CC=clang-18 CXX=clang-18++ BAZEL_CXXOPTS="-std=c++20" bazel build --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true --copt='-DGITHUB_BUILD' //presence
build-rust-linux:
name: Build Rust on Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Build FPP
run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml
+3 -3
View File
@@ -35,7 +35,7 @@ cc_library(
],
visibility = [
"//connections:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":authentication_status",
@@ -54,8 +54,8 @@ cc_library(
],
visibility = [
"//connections:__subpackages__",
"//presence:__subpackages__",
"//sharing:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
)
@@ -70,7 +70,7 @@ cc_library(
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":authentication_status",
+5 -5
View File
@@ -164,7 +164,7 @@ cc_library(
visibility = [
"//connections/implementation:__subpackages__",
"//internal/platform/implementation:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":base",
@@ -191,7 +191,7 @@ cc_library(
"//connections/implementation:__pkg__",
"//connections/v3:__pkg__",
"//internal/interop:__pkg__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":logging",
@@ -293,7 +293,7 @@ cc_library(
"//connections:__subpackages__",
"//internal/platform/implementation:__subpackages__",
"//internal/test:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":base",
@@ -349,7 +349,7 @@ cc_library(
"//connections:__subpackages__",
"//internal/platform/implementation:__subpackages__",
"//internal/test:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":base",
@@ -404,7 +404,7 @@ cc_library(
visibility = [
"//connections:__subpackages__",
"//internal/platform/implementation:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":base",
+3 -3
View File
@@ -49,8 +49,8 @@ cc_library(
"//internal/test:__subpackages__",
"//location/nearby/analytics/cpp:__subpackages__",
"//location/nearby/cpp/sharing:__subpackages__",
"//presence:__subpackages__",
"//sharing:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
"//internal/base:file_path",
@@ -112,8 +112,8 @@ cc_library(
"//internal/network:__subpackages__",
"//internal/platform:__pkg__",
"//internal/platform/implementation:__subpackages__",
"//presence:__subpackages__",
"//presence/implementation:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
"//third_party/nearby/presence/implementation:__subpackages__",
],
deps = [
"//connections/implementation/proto:offline_wire_formats_cc_proto",
+1 -1
View File
@@ -193,8 +193,8 @@ cc_library(
"//internal/weave:__subpackages__",
"//location/nearby/cpp:__subpackages__",
"//location/nearby/sharing/sdk:__subpackages__",
"//presence:__subpackages__",
"//sharing:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":comm",
@@ -328,8 +328,8 @@ cc_library(
"//connections:partners",
"//internal/platform:__subpackages__",
"//location/nearby:__subpackages__",
"//presence:__subpackages__",
"//sharing:__subpackages__",
"//third_party/nearby/presence:__subpackages__",
],
deps = [
":crypto", # build_cleaner: keep
-223
View File
@@ -1,223 +0,0 @@
# Copyright 2020 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.
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(default_visibility = ["//:__subpackages__"])
licenses(["notice"])
cc_library(
name = "presence",
srcs = [
"presence_client_impl.cc",
"presence_device_provider.cc",
"presence_service_impl.cc",
],
hdrs = [
"presence_client.h",
"presence_client_impl.h",
"presence_device_provider.h",
"presence_service.h",
"presence_service_impl.h",
],
deps = [
":types",
"//internal/interop:authentication_status",
"//internal/interop:authentication_transport_interface",
"//internal/interop:device",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence/implementation:internal", # build_cleaner: keep
"//presence/implementation/mediums",
"//presence/proto:presence_frame_cc_proto",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:variant",
],
)
cc_library(
name = "test_support",
testonly = 1,
srcs = [
"fake_presence_client.cc",
"fake_presence_service.cc",
],
hdrs = [
"fake_presence_client.h",
"fake_presence_service.h",
],
deps = [
":presence",
":types",
"//internal/interop:device",
"//internal/interop:test_support",
"//internal/platform:types",
"//internal/proto:metadata_cc_proto",
"//presence/implementation:internal", # build_cleaner: keep
"@com_google_absl//absl/status:statusor",
],
)
cc_library(
name = "types",
srcs = [
"device_motion.cc",
"discovery_filter.cc",
"presence_action.cc",
"presence_device.cc",
"presence_zone.cc",
"scan_request_builder.cc",
],
hdrs = [
"broadcast_options.h",
"broadcast_request.h",
"data_element.h",
"data_types.h",
"device_motion.h",
"discovery_filter.h",
"discovery_options.h",
"power_mode.h",
"presence_action.h",
"presence_device.h",
"presence_zone.h",
"scan_request.h",
"scan_request_builder.h",
],
deps = [
"//connections/implementation/proto:offline_wire_formats_cc_proto",
"//internal/interop:device",
"//internal/platform:base",
"//internal/platform:connection_info",
"//internal/platform:logging",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:variant",
],
)
cc_test(
name = "types_test",
size = "small",
srcs = [
"broadcast_options_test.cc",
"device_motion_test.cc",
"discovery_filter_test.cc",
"discovery_options_test.cc",
"presence_action_test.cc",
"presence_device_test.cc",
"presence_identity_test.cc",
"presence_zone_test.cc",
"scan_request_builder_test.cc",
],
shard_count = 6,
deps = [
":types",
"//connections/implementation/proto:offline_wire_formats_cc_proto",
"//internal/platform:connection_info",
"//internal/platform:types",
"//internal/proto:credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:variant",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "credential_test",
size = "small",
srcs = [
"credential_test.cc",
"presence_identity_test.cc",
],
shard_count = 6,
deps = [
"//internal/platform:uuid",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "presence_test",
size = "small",
srcs = [
"presence_client_test.cc",
"presence_device_provider_test.cc",
"presence_service_test.cc",
],
shard_count = 6,
deps = [
":presence",
":types",
"//internal/crypto",
"//internal/interop:authentication_status",
"//internal/interop:authentication_transport_interface",
"//internal/interop:device",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence/implementation:internal",
"//presence/implementation:internal_test",
"//presence/proto:presence_frame_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
-36
View File
@@ -1,36 +0,0 @@
// Copyright 2020 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_PRESENCE_BROADCAST_OPTIONS_H_
#define THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_OPTIONS_H_
#include <cstdint>
namespace nearby {
namespace presence {
struct BroadcastOptions {
const std::int64_t reporting_interval_millis;
};
inline bool operator==(const BroadcastOptions& o1, const BroadcastOptions& o2) {
return o1.reporting_interval_millis == o2.reporting_interval_millis;
}
inline bool operator!=(const BroadcastOptions& o1, const BroadcastOptions& o2) {
return !(o1 == o2);
}
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_OPTIONS_H_
-53
View File
@@ -1,53 +0,0 @@
// Copyright 2020 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 "presence/broadcast_options.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby {
namespace presence {
namespace {
constexpr std::int64_t kReportingIntervalMillis1 = 1000;
constexpr std::int64_t kReportingIntervalMillis2 = 2000;
TEST(BroadcastOptionsTest, NoDefaultConstructor) {
EXPECT_FALSE(std::is_trivially_constructible<BroadcastOptions>::value);
}
TEST(BroadcastOptionsTest, ExplicitInitEquals) {
BroadcastOptions option1 = {kReportingIntervalMillis1};
BroadcastOptions option2 = {kReportingIntervalMillis1};
EXPECT_EQ(option1, option2);
EXPECT_EQ(option1.reporting_interval_millis, kReportingIntervalMillis1);
}
TEST(BroadcastOptionsTest, ExplicitInitNotEquals) {
BroadcastOptions option1 = {kReportingIntervalMillis1};
BroadcastOptions option2 = {kReportingIntervalMillis2};
EXPECT_NE(option1, option2);
}
TEST(BroadcastOptionsTest, CopyInitEquals) {
BroadcastOptions option1 = {kReportingIntervalMillis1};
BroadcastOptions option2 = {option1};
EXPECT_EQ(option1, option2);
}
} // namespace
} // namespace presence
} // namespace nearby
-77
View File
@@ -1,77 +0,0 @@
// Copyright 2022 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_PRESENCE_BROADCAST_REQUEST_H_
#define THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_REQUEST_H_
#include <string>
#include <vector>
#include "absl/types/variant.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/power_mode.h"
namespace nearby {
namespace presence {
// Broadcast parameter for presence features.
struct PresenceBroadcast {
struct BroadcastSection {
// Presence identity type.
::nearby::internal::IdentityType identity =
::nearby::internal::IdentityType::IDENTITY_TYPE_UNSPECIFIED;
// Additional Data Elements.
// The Presence SDK generates:
// - Salt,
// - (Private/Trusted/Public/Provisioned) Identity,
// - TX power,
// - Advertisement signature
// Data Elements when they are required in the advertisement. Other Data
// Elements are provided by the client application.
// Nearby SDK encrypts Data ELements before broadcasting if a non-public
// `PresenceIdentity` is provided.
std::vector<DataElement> extended_properties;
// Account name used to select private credentials.
std::string account_name;
// Manager app id, used to select private credentials.
std::string manager_app_id;
};
std::vector<BroadcastSection> sections;
};
// Broadcast request for legacy Android T, which needs to provide credential
// and salt in the broadcast parameters.
// TODO(b/243443813) - Support Legacy Broadcast Request
struct LegacyPresenceBroadcast {};
// Nearby Presence advertisement request options.
struct BroadcastRequest {
// Calibrated TX power. The broadcast recipient uses it to calculate the
// distance between both devices.
int tx_power;
// The broadcast frequency hint.
PowerMode power_mode;
absl::variant<PresenceBroadcast, LegacyPresenceBroadcast> variant;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_REQUEST_H_
-79
View File
@@ -1,79 +0,0 @@
// Copyright 2020 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 <cstdlib>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/platform/uuid.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::internal::LocalCredential;
using ::nearby::internal::SharedCredential;
using ::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
using ::protobuf_matchers::EqualsProto;
TEST(CredentialsTest, NoDefaultConstructor) {
EXPECT_FALSE(std::is_trivially_constructible<LocalCredential>::value);
EXPECT_FALSE(std::is_trivially_constructible<SharedCredential>::value);
}
TEST(CredentialsTest, InitSharedCredential) {
SharedCredential pc1 = {};
SharedCredential pc2 = {};
EXPECT_THAT(pc1, EqualsProto(pc2));
pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_THAT(pc1, ::testing::Not(EqualsProto(pc2)));
pc2.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_THAT(pc1, EqualsProto(pc2));
}
TEST(CredentialsTest, InitLocalCredential) {
LocalCredential pc1 = {};
LocalCredential pc2 = {};
EXPECT_THAT(pc1, EqualsProto(pc2));
pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_THAT(pc1, ::testing::Not(EqualsProto(pc2)));
pc2.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_THAT(pc1, EqualsProto(pc2));
}
TEST(CredentialsTest, CopyLocalCredential) {
LocalCredential pc1 = {};
pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
auto salts = pc1.mutable_consumed_salts();
salts->insert(std::pair<int32_t, bool>(15, true));
LocalCredential pc1_copy = {pc1};
EXPECT_THAT(pc1, EqualsProto(pc1_copy));
}
TEST(CredentialsTest, CopySharedCredential) {
SharedCredential pc1 = {};
pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP);
for (const uint8_t byte : nearby::Uuid().data()) {
pc1.mutable_secret_id()->push_back(byte);
}
SharedCredential pc1_copy = {pc1};
EXPECT_THAT(pc1, EqualsProto(pc1_copy));
}
} // namespace
} // namespace presence
} // namespace nearby
-111
View File
@@ -1,111 +0,0 @@
// Copyright 2022 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_PRESENCE_DATA_ELEMENT_H_
#define THIRD_PARTY_NEARBY_PRESENCE_DATA_ELEMENT_H_
#include <stdint.h>
#include <initializer_list>
#include <ostream>
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
namespace nearby {
namespace presence {
// Reserved Action types when the field type is kActionFieldType.
// The values are bit numbers in BE ordering.
// TODO(b/338107166): these are out of date, need to be updated to latest spec
enum class ActionBit {
kCallTransferAction = 4,
kActiveUnlockAction = 8,
kNearbyShareAction = 9,
kInstantTetheringAction = 10,
kPhoneHubAction = 11,
kPresenceManagerAction = 12,
kFinderAction = 13,
kFastPairSassAction = 14,
kTapToTransferAction = 15,
kLastAction
};
// helpful for enumerating overall all possible action bit types, this must be
// kept in sync with the above enum
constexpr std::initializer_list<ActionBit> kAllActionBits = {
ActionBit::kCallTransferAction, ActionBit::kActiveUnlockAction,
ActionBit::kNearbyShareAction, ActionBit::kInstantTetheringAction,
ActionBit::kPhoneHubAction, ActionBit::kPresenceManagerAction,
ActionBit::kFinderAction, ActionBit::kFastPairSassAction,
ActionBit::kTapToTransferAction};
/** Describes a custom Data element in NP advertisement. */
class DataElement {
public:
// The field types listed below require special processing when generating and
// parsing NP advertisements.
static constexpr int kSaltFieldType = 0;
static constexpr int kPrivateGroupIdentityFieldType = 1;
static constexpr int kContactsGroupIdentityFieldType = 2;
static constexpr int kPublicIdentityFieldType = 3;
static constexpr int kTxPowerFieldType = 5;
static constexpr int kActionFieldType = 6;
static constexpr int kModelIdFieldType = 7;
static constexpr int kEddystoneIdFieldType = 8;
static constexpr int kAccountKeyDataFieldType = 9;
static constexpr int kConnectionStatusFieldType = 10;
static constexpr int kBatteryFieldType = 11;
static constexpr int kAdvertisementSignature = 12;
static constexpr int kContextTimestampFieldType = 13;
// Maximum allowed Data Element's value length
static constexpr int kMaxDataElementLength = 15;
// Maximum allowed Data Element's type
static constexpr int kMaxDataElementType = 15;
// The DE header is (length << kDataElementLengthShift | type)
static constexpr int kDataElementLengthShift = 4;
DataElement(uint16_t type, absl::string_view value)
: type_(type), value_(value) {}
DataElement(uint16_t type, uint8_t value)
: type_(type),
value_(reinterpret_cast<const char*>(&value), sizeof(value)) {}
explicit DataElement(ActionBit action)
: DataElement(kActionFieldType, static_cast<uint8_t>(action)) {}
~DataElement() = default;
uint16_t GetType() const { return type_; }
absl::string_view GetValue() const { return value_; }
private:
uint16_t type_;
std::string value_;
};
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
#endif // THIRD_PARTY_NEARBY_PRESENCE_DATA_ELEMENT_H_
-64
View File
@@ -1,64 +0,0 @@
// Copyright 2020 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_PRESENCE_SCAN_CALLBACK_H_
#define THIRD_PARTY_NEARBY_PRESENCE_SCAN_CALLBACK_H_
#include <cstdint>
#include "absl/functional/any_invocable.h"
#include "internal/platform/logging.h"
#include "presence/presence_device.h"
namespace nearby {
namespace presence {
// Unique Scan Session Identifier.
using ScanSessionId = uint64_t;
// Callers would provide the implementation of these callbacks. If callers
// don't need these signal updates, they can skip with the provided default
// empty functions.
struct ScanCallback {
// Updates client with the result of start scanning.
absl::AnyInvocable<void(absl::Status)> start_scan_cb = [](absl::Status) {};
// Reports a {@link PresenceDevice} being discovered.
absl::AnyInvocable<void(PresenceDevice)> on_discovered_cb =
[](PresenceDevice) {};
// Reports a {@link PresenceDevice} information(distance, and etc)
// changed.
absl::AnyInvocable<void(PresenceDevice)> on_updated_cb = [](PresenceDevice) {
};
// Reports a {@link PresenceDevice} is no longer within range.
absl::AnyInvocable<void(PresenceDevice)> on_lost_cb = [](PresenceDevice) {};
};
// Unique Broadcast Session Identifier.
using BroadcastSessionId = uint64_t;
// Callers would provide the implementation of these callbacks. If callers
// don't need these signal updates, they can skip with the provided default
// empty functions.
struct BroadcastCallback {
absl::AnyInvocable<void(absl::Status)> start_broadcast_cb = [](absl::Status) {
};
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_SCAN_CALLBACK_H_
-28
View File
@@ -1,28 +0,0 @@
// Copyright 2020 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 "presence/device_motion.h"
namespace nearby {
namespace presence {
DeviceMotion::DeviceMotion(MotionType motion_type, float confidence) noexcept
: motion_type_(motion_type), confidence_(confidence) {}
DeviceMotion::MotionType DeviceMotion::GetMotionType() const {
return motion_type_;
}
float DeviceMotion::GetConfidence() const { return confidence_; }
} // namespace presence
} // namespace nearby
-47
View File
@@ -1,47 +0,0 @@
// Copyright 2020 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_PRESENCE_DEVICE_MOTION_H_
#define THIRD_PARTY_NEARBY_PRESENCE_DEVICE_MOTION_H_
namespace nearby {
namespace presence {
class DeviceMotion {
public:
enum class MotionType {
kPointAndHold = 0,
kStationaryAndHold = 1,
};
DeviceMotion(MotionType motion_type = MotionType::kPointAndHold,
float confidence = 0) noexcept;
MotionType GetMotionType() const;
float GetConfidence() const;
private:
const MotionType motion_type_;
const float confidence_;
};
inline bool operator==(const DeviceMotion& m1, const DeviceMotion& m2) {
return m1.GetMotionType() == m2.GetMotionType() &&
m1.GetConfidence() == m2.GetConfidence();
}
inline bool operator!=(const DeviceMotion& m1, const DeviceMotion& m2) {
return !(m1 == m2);
}
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_DEVICE_MOTION_H_
-61
View File
@@ -1,61 +0,0 @@
// Copyright 2020 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 "presence/device_motion.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby {
namespace presence {
namespace {
static const DeviceMotion::MotionType kDefaultMotionType =
DeviceMotion::MotionType::kPointAndHold;
static const float kDefaultConfidence = 0;
static const float kConfidenceForTest = 0.1;
TEST(DeviceMotionTest, DefaultConstructorWorks) {
DeviceMotion motion;
EXPECT_EQ(motion.GetMotionType(), kDefaultMotionType);
EXPECT_EQ(motion.GetConfidence(), kDefaultConfidence);
}
TEST(DeviceMotionTest, DefaultEquals) {
DeviceMotion motion1;
DeviceMotion motion2;
EXPECT_EQ(motion1, motion2);
}
TEST(DeviceMotionTest, ExplicitInitEquals) {
DeviceMotion motion1 = {kDefaultMotionType, kConfidenceForTest};
DeviceMotion motion2 = {kDefaultMotionType, kConfidenceForTest};
EXPECT_EQ(motion1, motion2);
EXPECT_EQ(motion1.GetConfidence(), kConfidenceForTest);
}
TEST(DeviceMotionTest, ExplicitInitNotEquals) {
DeviceMotion motion1 = {kDefaultMotionType, kConfidenceForTest};
DeviceMotion motion2 = {kDefaultMotionType};
EXPECT_NE(motion1, motion2);
}
TEST(DeviceMotionTest, CopyInitEquals) {
DeviceMotion motion1;
DeviceMotion motion2 = {motion1};
EXPECT_EQ(motion1, motion2);
}
} // namespace
} // namespace presence
} // namespace nearby
-36
View File
@@ -1,36 +0,0 @@
// Copyright 2020 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 "presence/discovery_filter.h"
namespace nearby {
namespace presence {
using ::nearby::internal::IdentityType;
DiscoveryFilter::DiscoveryFilter(
const std::vector<PresenceAction>& actions,
const std::vector<IdentityType>& identities,
const std::vector<PresenceZone>& zones) noexcept
: actions_(actions), identities_(identities), zones_(zones) {}
std::vector<PresenceAction> DiscoveryFilter::GetActions() const {
return actions_;
}
std::vector<IdentityType> DiscoveryFilter::GetIdentities() const {
return identities_;
}
std::vector<PresenceZone> DiscoveryFilter::GetZones() const { return zones_; }
} // namespace presence
} // namespace nearby
-43
View File
@@ -1,43 +0,0 @@
// Copyright 2020 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_PRESENCE_DISCOVERY_FILTER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_FILTER_H_
#include <vector>
#include "internal/proto/credential.pb.h"
#include "presence/presence_action.h"
#include "presence/presence_zone.h"
namespace nearby {
namespace presence {
class DiscoveryFilter {
public:
DiscoveryFilter(const std::vector<PresenceAction>& = {},
const std::vector<::nearby::internal::IdentityType>& = {},
const std::vector<PresenceZone>& = {}) noexcept;
std::vector<PresenceAction> GetActions() const;
std::vector<::nearby::internal::IdentityType> GetIdentities() const;
std::vector<PresenceZone> GetZones() const;
private:
const std::vector<PresenceAction> actions_;
const std::vector<::nearby::internal::IdentityType> identities_;
const std::vector<PresenceZone> zones_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_FILTER_H_
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2020 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 "presence/discovery_filter.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::internal::IdentityType;
const PresenceAction kTestAction = {1};
const IdentityType kTestIdentity = {IdentityType::IDENTITY_TYPE_CONTACTS_GROUP};
TEST(DiscoveryFilterTest, DefaultConstructorWorks) {
DiscoveryFilter filter;
EXPECT_EQ(filter.GetActions().size(), 0);
EXPECT_EQ(filter.GetIdentities().size(), 0);
EXPECT_EQ(filter.GetZones().size(), 0);
}
TEST(DiscoveryFilterTest, PartiallyInitializationWorks) {
DiscoveryFilter filter1{{kTestAction}, {kTestIdentity}};
DiscoveryFilter filter2{{kTestAction}};
EXPECT_EQ(filter1.GetActions(), filter2.GetActions());
EXPECT_NE(filter1.GetIdentities(), filter2.GetIdentities());
EXPECT_EQ(filter1.GetZones(), filter2.GetZones());
EXPECT_EQ(filter1.GetActions()[0], kTestAction);
EXPECT_EQ(filter1.GetIdentities()[0], kTestIdentity);
EXPECT_EQ(filter1.GetZones().capacity(), 0);
}
} // namespace
} // namespace presence
} // namespace nearby
-34
View File
@@ -1,34 +0,0 @@
// Copyright 2020 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_PRESENCE_DISCOVERY_OPTIONS_H_
#define THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_OPTIONS_H_
namespace nearby {
namespace presence {
struct DiscoveryOptions {
const bool local_wifi_only_;
};
inline bool operator==(const DiscoveryOptions& o1, const DiscoveryOptions& o2) {
return o1.local_wifi_only_ == o2.local_wifi_only_;
}
inline bool operator!=(const DiscoveryOptions& o1, const DiscoveryOptions& o2) {
return !(o1 == o2);
}
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_OPTIONS_H_
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2020 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 "presence/discovery_options.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby {
namespace presence {
namespace {
constexpr bool kTestLocalWifiOnly = false;
TEST(DiscoveryOptionsTest, NoDefaultConstructor) {
EXPECT_FALSE(std::is_trivially_constructible<DiscoveryOptions>::value);
}
TEST(DiscoveryOptionsTest, ExplicitInitEquals) {
DiscoveryOptions option1 = {kTestLocalWifiOnly};
DiscoveryOptions option2 = {kTestLocalWifiOnly};
EXPECT_EQ(option1, option2);
EXPECT_EQ(option1.local_wifi_only_, kTestLocalWifiOnly);
}
TEST(DiscoveryOptionsTest, ExplicitInitNotEquals) {
DiscoveryOptions option1 = {kTestLocalWifiOnly};
DiscoveryOptions option2 = {!kTestLocalWifiOnly};
EXPECT_NE(option1, option2);
}
TEST(DiscoveryOptionsTest, CopyInitEquals) {
DiscoveryOptions option1 = {kTestLocalWifiOnly};
DiscoveryOptions option2 = {option1};
EXPECT_EQ(option1, option2);
}
} // namespace
} // namespace presence
} // namespace nearby
-67
View File
@@ -1,67 +0,0 @@
// Copyright 2023 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 "presence/fake_presence_client.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "presence/data_types.h"
#include "presence/presence_device.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
absl::StatusOr<ScanSessionId> FakePresenceClient::StartScan(
ScanRequest scan_request, ScanCallback callback) {
current_scan_session_id_++;
active_scan_sessions_.push_back(current_scan_session_id_);
absl::StatusOr<ScanSessionId> scan_session_id(current_scan_session_id_);
callback_ = std::move(callback);
return scan_session_id;
}
void FakePresenceClient::StopScan(ScanSessionId id) {
auto position =
std::find(active_scan_sessions_.begin(), active_scan_sessions_.end(), id);
if (position != active_scan_sessions_.end()) {
active_scan_sessions_.erase(position);
}
}
std::vector<uint64_t> FakePresenceClient::GetActiveScanSessions() {
return active_scan_sessions_;
}
void FakePresenceClient::CallStartScanCallback(absl::Status status) {
callback_.start_scan_cb(status);
}
void FakePresenceClient::CallOnDiscovered(PresenceDevice device) {
callback_.on_discovered_cb(device);
}
void FakePresenceClient::CallOnUpdated(PresenceDevice device) {
callback_.on_updated_cb(device);
}
void FakePresenceClient::CallOnLost(PresenceDevice device) {
callback_.on_lost_cb(device);
}
} // namespace presence
} // namespace nearby
-73
View File
@@ -1,73 +0,0 @@
// Copyright 2023 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_PRESENCE_FAKE_PRESENCE_CLIENT_H_
#define THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_
#include <optional>
#include <vector>
#include "absl/status/statusor.h"
#include "presence/broadcast_request.h"
#include "presence/presence_client.h"
#include "presence/presence_device.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
class FakePresenceClient : public PresenceClient {
public:
FakePresenceClient() = default;
FakePresenceClient(const FakePresenceClient&) = delete;
FakePresenceClient(FakePresenceClient&&) = default;
FakePresenceClient& operator=(const FakePresenceClient&) = delete;
~FakePresenceClient() = default;
absl::StatusOr<ScanSessionId> StartScan(ScanRequest scan_request,
ScanCallback callback) override;
void StopScan(ScanSessionId session_id) override;
// Not Implemented.
absl::StatusOr<BroadcastSessionId> StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) override {
return 0;
}
// Not Implemented.
void StopBroadcast(BroadcastSessionId session_id) override {}
// Not Implemented.
std::optional<PresenceDevice> GetLocalDevice() override {
return std::nullopt;
}
std::vector<uint64_t> GetActiveScanSessions();
void CallStartScanCallback(absl::Status status);
void CallOnDiscovered(PresenceDevice device);
void CallOnUpdated(PresenceDevice device);
void CallOnLost(PresenceDevice device);
private:
uint64_t current_scan_session_id_;
ScanCallback callback_;
std::vector<uint64_t> active_scan_sessions_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_
-107
View File
@@ -1,107 +0,0 @@
// Copyright 2020 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 "presence/fake_presence_service.h"
#include <memory>
#include <utility>
#include <vector>
#include "internal/interop/device_provider.h"
#include "internal/platform/borrowable.h"
#include "presence/fake_presence_client.h"
namespace nearby {
namespace presence {
FakePresenceService::FakePresenceService() = default;
std::unique_ptr<PresenceClient> FakePresenceService::CreatePresenceClient() {
auto fake = std::make_unique<FakePresenceClient>();
most_recent_fake_presence_client_ = fake.get();
return std::move(fake);
}
// Not implemented.
absl::StatusOr<ScanSessionId> FakePresenceService::StartScan(
ScanRequest scan_request, ScanCallback callback) {
return absl::Status(absl::StatusCode::kCancelled,
"StartScan not implemented yet");
}
// Not implemented.
void FakePresenceService::StopScan(ScanSessionId session_id) {}
// Not implemented.
absl::StatusOr<BroadcastSessionId> FakePresenceService::StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) {
return absl::Status(absl::StatusCode::kCancelled,
"StartBroadcast not implemented yet");
}
// Not implemented.
void FakePresenceService::StopBroadcast(BroadcastSessionId session_id) {}
void FakePresenceService::UpdateDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData& metadata,
bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) {
metadata_ = metadata;
if (!regen_credentials) {
// No need to call back on credentials_generated_cb.
return;
}
if (gen_credentials_status_.ok()) {
std::move(credentials_generated_cb.credentials_generated_cb)(
shared_credentials_);
} else {
std::move(credentials_generated_cb.credentials_generated_cb)(
gen_credentials_status_);
}
}
NearbyDeviceProvider* FakePresenceService::GetLocalDeviceProvider() {
return provider_;
}
void FakePresenceService::GetLocalPublicCredentials(
const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback) {
if (get_public_credentials_status_.ok()) {
std::move(callback.credentials_fetched_cb)(shared_credentials_);
return;
}
std::move(callback.credentials_fetched_cb)(get_public_credentials_status_);
}
void FakePresenceService::UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>& remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) {
if (update_remote_public_credentials_status_.ok()) {
remote_shared_credentials_ = remote_public_creds;
}
std::move(credentials_updated_cb.credentials_updated_cb)(
update_remote_public_credentials_status_);
}
} // namespace presence
} // namespace nearby
-122
View File
@@ -1,122 +0,0 @@
// Copyright 2023 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_PRESENCE_FAKE_PRESENCE_SERVICE_H_
#define THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_SERVICE_H_
#include "internal/interop/device_provider.h"
#include "internal/interop/fake_device_provider.h"
#include "internal/platform/borrowable.h"
#include "internal/proto/metadata.pb.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/presence_client.h"
#include "presence/presence_service.h"
namespace nearby {
namespace presence {
class FakePresenceClient;
class FakePresenceService : public PresenceService {
public:
FakePresenceService();
~FakePresenceService() override { lender_.Release(); }
// PresenceService:
std::unique_ptr<PresenceClient> CreatePresenceClient() override;
absl::StatusOr<ScanSessionId> StartScan(ScanRequest scan_request,
ScanCallback callback) override;
void StopScan(ScanSessionId session_id) override;
absl::StatusOr<BroadcastSessionId> StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) override;
void StopBroadcast(BroadcastSessionId session_id) override;
void UpdateDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData& metadata,
bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) override;
NearbyDeviceProvider* GetLocalDeviceProvider() override;
::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData()
override {
return metadata_;
}
void GetLocalPublicCredentials(
const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback) override;
void UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) override;
// Use for testing. Call this to set the response to
// `UpdateLocalDeviceMetadata`.
void SetUpdateLocalDeviceMetadataResponse(
absl::Status status,
std::vector<nearby::internal::SharedCredential> shared_credentials) {
shared_credentials_ = shared_credentials;
gen_credentials_status_ = status;
}
FakePresenceClient* GetMostRecentFakePresenceClient() {
return most_recent_fake_presence_client_;
}
// Used for testing to verify the remote credentials set.
std::vector<nearby::internal::SharedCredential> GetRemoteSharedCredentials() {
return remote_shared_credentials_;
}
void SetUpdateRemoteSharedCredentialsResult(absl::Status status) {
update_remote_public_credentials_status_ = status;
}
void SetLocalPublicCredentialsResult(
absl::Status status,
std::vector<nearby::internal::SharedCredential> shared_credentials) {
get_public_credentials_status_ = status;
shared_credentials_ = shared_credentials;
}
void SetDeviceProvider(NearbyDeviceProvider* provider) {
provider_ = provider;
}
private:
FakePresenceClient* most_recent_fake_presence_client_ = nullptr;
std::vector<nearby::internal::SharedCredential> shared_credentials_;
std::vector<nearby::internal::SharedCredential> remote_shared_credentials_;
absl::Status gen_credentials_status_;
absl::Status update_remote_public_credentials_status_;
absl::Status get_public_credentials_status_;
::nearby::internal::DeviceIdentityMetaData metadata_;
NearbyDeviceProvider* provider_;
::nearby::Lender<PresenceService*> lender_{this};
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_SERVICE_H_
-93
View File
@@ -1,93 +0,0 @@
# Copyright 2023 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
#
# http://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.
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
licenses(["notice"])
cc_library(
name = "fpp_manager",
srcs = [
"fpp_manager.cc",
],
hdrs = ["fpp_manager.h"],
visibility = [
"//presence:__subpackages__",
],
deps = [
"//internal/platform:logging",
"//presence:types",
"//presence/fpp/fpp_c_ffi",
"//presence/implementation:sensor_fusion",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "sensor_fusion_impl",
srcs = [
"sensor_fusion_impl.cc",
],
hdrs = ["sensor_fusion_impl.h"],
visibility = [
"//presence:__subpackages__",
],
deps = [
":fpp_manager",
"//presence/implementation:sensor_fusion",
"@com_google_absl//absl/status",
],
)
cc_test(
name = "fpp_manager_test",
size = "small",
srcs = ["fpp_manager_test.cc"],
deps = [
":fpp_manager",
"//presence/implementation:sensor_fusion",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "sensor_fusion_test",
size = "small",
srcs = ["sensor_fusion_test.cc"],
deps = [
":sensor_fusion_impl",
"//presence/implementation:sensor_fusion",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
-25
View File
@@ -1,25 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "fpp"
version = "0.1.0"
dependencies = [
"itertools",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
-9
View File
@@ -1,9 +0,0 @@
[package]
name = "fpp"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.10.5"
-37
View File
@@ -1,37 +0,0 @@
// Copyright 2023 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
//
// http://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.
const ADVERTISE_TX_POWER_HIGH_DB: i32 = 1;
const FSPL_AT_1_METER_DB: i32 = 40;
const MEASURED_POWER_AT_1_METER_DB_AT_HIGH_TX_POWER: i32 = -60;
pub fn compute_distance_meters_at_high_tx_power(rssi: i32) -> f64 {
let nominal_tx_power = ADVERTISE_TX_POWER_HIGH_DB;
let antenna_gain =
(nominal_tx_power - FSPL_AT_1_METER_DB) - MEASURED_POWER_AT_1_METER_DB_AT_HIGH_TX_POWER;
let tx_power_at_0_meters = nominal_tx_power - antenna_gain;
compute_distance_meters(tx_power_at_0_meters, rssi)
}
pub fn compute_distance_meters(tx_power_at_0_meters: i32, rssi: i32) -> f64 {
let fspl = tx_power_at_0_meters - rssi;
ble_fspl_to_meters(fspl)
}
fn ble_fspl_to_meters(fspl: i32) -> f64 {
let base: f64 = 10.0;
base.powi((fspl - FSPL_AT_1_METER_DB) / 20)
}
@@ -1,30 +0,0 @@
// Copyright 2023 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
//
// http://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.
use crate::fspl_converter::compute_distance_meters_at_high_tx_power;
#[test]
fn test_short_distance() {
assert_eq!(compute_distance_meters_at_high_tx_power(-40), 0.1);
}
#[test]
fn test_medium_distance() {
assert_eq!(compute_distance_meters_at_high_tx_power(-60), 1.0);
}
#[test]
fn test_large_distance() {
assert_eq!(compute_distance_meters_at_high_tx_power(-80), 10.0);
}
@@ -1,107 +0,0 @@
// Copyright 2023 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
//
// http://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.
pub(crate) const DEFAULT_TAP_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 0.02;
pub(crate) const DEFAULT_REACH_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 0.5;
pub(crate) const DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 1.2;
pub(crate) const DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 3.0;
pub(crate) const DEFAULT_CONSECUTIVE_SCANS_REQUIRED: u8 = 2;
const AMBIGUITY_METERS: f64 = 0.06;
/// Proximity state from device to another in terms of actionability
#[derive(Eq, Hash, Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum ProximityState {
/// Unknown proximity state
Unknown,
/// The device is within a tap zone (<0.02m)
Tap,
/// The device is within a reach zone (<0.5m)
Reach,
/// The device is within a short range zone (<1.2m)
ShortRange,
/// The device is within a long range zone (<3.0m)
LongRange,
/// The device is at a far range
Far,
}
/// Represents the confidence levels for a given measurement
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum MeasurementConfidence {
/// Measurement confidence is low, the default for BLE medium
Low,
/// Measurement confidence is medium
Medium,
/// Measurement confidence is High
High,
/// Measurement confidence is unknown
Unknown,
}
/// Data sources that are used to track presence
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum PresenceDataSource {
/// Data source for proximity estimate is BLE
Ble,
/// Data source for proximity estimate is UWB
Uwb,
/// Data source for proximity estimate is NAN
Nan,
/// Data source for proximity estimate is unknown
Unknown,
}
/// A PII-stripped subset of Bluetooth scan result
#[repr(C)]
pub struct BleScanResult {
/// Device ID of the nearby device
pub device_id: u64,
/// Transmitting power of signal
pub tx_power: MaybeTxPower,
/// RSSI value
pub rssi: i32,
/// Time scan result was obtained
pub elapsed_real_time_millis: u64,
}
/// Enum representing an optional tx power value
#[repr(C)]
pub enum MaybeTxPower {
/// Valid TX power with associated data value
Valid(i32),
/// Absent Tx Power
Invalid,
}
/// Describes the most accurate and recent measurement for a given device
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub struct ProximityEstimate {
/// Device ID of the nearby device
pub device_id: u64,
/// Distance to the nearby device in meters
pub distance_meters: f64,
/// Measurement confidence of the estimate
pub distance_confidence: MeasurementConfidence,
/// The time the proximity estimate was obtained (milliseconds since the
/// program start time)
pub elapsed_real_time_millis: u64,
/// Proximity state zone of the nearby device
pub proximity_state: ProximityState,
/// Medium through which the proximity estimate was computed
pub source: PresenceDataSource,
}
-37
View File
@@ -1,37 +0,0 @@
// Copyright 2023 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
//
// http://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.
#![deny(
missing_docs,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
//! Processes raw scan results from BLE, UWB and NAN and outputs proximity estimates/zones
mod fspl_converter;
/// Fused presence Utils
pub mod fused_presence_utils;
/// Presence detector module
pub mod presence_detector;
#[cfg(test)]
mod fspl_converter_test;
#[cfg(test)]
mod presence_detector_test;
-131
View File
@@ -1,131 +0,0 @@
// Copyright 2023 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
//
// http://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.
use std::collections::{HashMap, VecDeque};
use std::time::{Instant, SystemTime};
use itertools::Itertools;
use crate::fspl_converter::compute_distance_meters_at_high_tx_power;
use crate::fused_presence_utils::{
BleScanResult, MaybeTxPower, MeasurementConfidence, PresenceDataSource, ProximityEstimate,
ProximityState, DEFAULT_CONSECUTIVE_SCANS_REQUIRED,
DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS, DEFAULT_REACH_DISTANCE_THRESHOLD_METERS,
DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS, DEFAULT_TAP_DISTANCE_THRESHOLD_METERS,
};
const MAX_RSSI_FILTER_VALUE: i32 = 10;
const DEFAULT_ESTIMATED_DISTANCE_DATA_TTL_MILLIS: u128 = 4000;
/// Static function for getting proximity state from threshold
fn get_proximity_state_from_threshold(distance_meters: f64) -> ProximityState {
if distance_meters <= DEFAULT_TAP_DISTANCE_THRESHOLD_METERS {
return ProximityState::Tap;
}
if distance_meters <= DEFAULT_REACH_DISTANCE_THRESHOLD_METERS {
return ProximityState::Reach;
}
if distance_meters <= DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS {
return ProximityState::ShortRange;
}
if distance_meters <= DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS {
return ProximityState::LongRange;
}
ProximityState::Far
}
/// Tracks and computes proximity/presence state events.
pub struct PresenceDetector {
start_time: Instant,
last_range_update_time: RangingUpdateTime,
best_proximity_estimate_per_device: HashMap<u64, ProximityEstimate>,
transition_history: VecDeque<ProximityState>,
}
struct RangingUpdateTime(u128);
impl RangingUpdateTime {
pub fn is_expired(&self) -> bool {
let elapsed_real_time_millis = Instant::now().elapsed().as_millis();
elapsed_real_time_millis - self.0 > DEFAULT_ESTIMATED_DISTANCE_DATA_TTL_MILLIS
}
pub fn update(&mut self, start_time: Instant) {
self.0 = Instant::now().duration_since(start_time).as_millis();
}
}
impl PresenceDetector {
/// Creates a new instance of presence detector
pub fn new() -> Self {
PresenceDetector {
start_time: Instant::now(),
last_range_update_time: RangingUpdateTime(0),
best_proximity_estimate_per_device: HashMap::new(),
transition_history: VecDeque::with_capacity(
(DEFAULT_CONSECUTIVE_SCANS_REQUIRED + 1).into(),
),
}
}
/// Updates the presence detector with a new scan result and returns the
/// current proximity estimate
pub fn on_ble_scan_result(
&mut self,
ble_scan_result: BleScanResult,
) -> Option<ProximityEstimate> {
let device_id = ble_scan_result.device_id;
if ble_scan_result.rssi > MAX_RSSI_FILTER_VALUE {
return self.best_proximity_estimate_per_device.get(&device_id).copied();
}
if self.last_range_update_time.is_expired() {
self.transition_history.clear();
}
let mut tx_power: i32 = 0;
if let MaybeTxPower::Valid(some_tx_power) = ble_scan_result.tx_power {
tx_power = some_tx_power;
}
let rssi = ble_scan_result.rssi + tx_power;
let distance_meters = compute_distance_meters_at_high_tx_power(rssi);
let new_proximity_estimate = ProximityEstimate {
device_id,
distance_confidence: MeasurementConfidence::Low,
distance_meters,
proximity_state: get_proximity_state_from_threshold(distance_meters),
elapsed_real_time_millis: Instant::now().duration_since(self.start_time).as_millis()
as u64,
source: PresenceDataSource::Ble,
};
self.transition_history.push_front(new_proximity_estimate.proximity_state);
self.transition_history.truncate(DEFAULT_CONSECUTIVE_SCANS_REQUIRED.into());
if self.transition_history.iter().unique().count() == 1
&& self.transition_history.len() == DEFAULT_CONSECUTIVE_SCANS_REQUIRED.into()
{
self.best_proximity_estimate_per_device.insert(device_id, new_proximity_estimate);
self.last_range_update_time.update(self.start_time);
}
self.best_proximity_estimate_per_device.get(&device_id).copied()
}
/// Returns the current proximity estimate for a given device
pub fn get_proximity_estimate(&self, device_id: u64) -> Option<ProximityEstimate> {
self.best_proximity_estimate_per_device.get(&device_id).copied()
}
}
impl Default for PresenceDetector {
fn default() -> Self {
Self::new()
}
}
@@ -1,105 +0,0 @@
// Copyright 2023 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
//
// http://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.
use crate::fused_presence_utils::*;
use crate::presence_detector::*;
const BLE_SCAN_RESULT_REACH_ZONE: BleScanResult = BleScanResult {
device_id: 1234,
tx_power: { MaybeTxPower::Invalid },
rssi: -40,
elapsed_real_time_millis: 123456,
};
const BLE_SCAN_RESULT_BAD_RSSI: BleScanResult = BleScanResult {
rssi: 127,
..BLE_SCAN_RESULT_REACH_ZONE
};
const BLE_SCAN_RESULT_SHORT_RANGE_ZONE: BleScanResult = BleScanResult {
rssi: -60,
..BLE_SCAN_RESULT_REACH_ZONE
};
const REACH_PROXIMITY_ESTIMATE: ProximityEstimate = ProximityEstimate {
device_id: 1234,
distance_meters: 0.1,
distance_confidence: MeasurementConfidence::Low,
elapsed_real_time_millis: 0,
proximity_state: ProximityState::Reach,
source: PresenceDataSource::Ble,
};
const SHORT_RANGE_PROXIMITY_ESTIMATE: ProximityEstimate = ProximityEstimate {
distance_meters: 1.0,
proximity_state: ProximityState::ShortRange,
..REACH_PROXIMITY_ESTIMATE
};
#[test]
fn test_on_ble_scan_result_success() {
// Tests that the proximity state stored for each device is the accurate one after two
// consecutive scan results
let mut presence_detector = PresenceDetector::new();
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE),
None
);
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE),
Some(ProximityEstimate {
device_id: 1234,
distance_meters: 0.1,
distance_confidence: MeasurementConfidence::Low,
elapsed_real_time_millis: 0,
proximity_state: ProximityState::Reach,
source: PresenceDataSource::Ble
})
);
}
#[test]
fn test_on_ble_scan_result_bad_rssi() {
// Tests that scan results with bad RSSIs are ignored
let mut presence_detector = PresenceDetector::new();
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE),
None
);
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_BAD_RSSI),
None
);
}
#[test]
fn test_on_ble_scan_result_transition_to_new_zone() {
let mut presence_detector = PresenceDetector::new();
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE),
None
);
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE),
Some(REACH_PROXIMITY_ESTIMATE)
);
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_SHORT_RANGE_ZONE),
Some(REACH_PROXIMITY_ESTIMATE)
);
assert_eq!(
presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_SHORT_RANGE_ZONE),
Some(SHORT_RANGE_PROXIMITY_ESTIMATE)
);
}
-105
View File
@@ -1,105 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "fpp"
version = "0.1.0"
dependencies = [
"itertools",
]
[[package]]
name = "fpp_c_ffi"
version = "0.1.0"
dependencies = [
"fpp",
"lazy_static",
"rand",
]
[[package]]
name = "getrandom"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-11
View File
@@ -1,11 +0,0 @@
[package]
name = "fpp_c_ffi"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fpp = {path = "../fpp"}
lazy_static = "1.4.0"
rand = "0.8.5"
@@ -1,148 +0,0 @@
// Copyright 2023 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
//
// http://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 PRESENCE_DETECTOR_H_
#define PRESENCE_DETECTOR_H_
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
// Represents the confidence levels for a given measurement
enum class MeasurementConfidence {
/// Measurement confidence is low, the default for BLE medium
Low,
/// Measurement confidence is medium
Medium,
/// Measurement confidence is High
High,
/// Measurement confidence is unknown
Unknown,
};
/// Data sources that are used to track presence
enum class PresenceDataSource {
/// Data source for proximity estimate is BLE
Ble,
/// Data source for proximity estimate is UWB
Uwb,
/// Data source for proximity estimate is NAN
Nan,
/// Data source for proximity estimate is unknown
Unknown,
};
/// Proximity state from device to another in terms of actionability
enum class ProximityState {
/// Unknown proximity state
Unknown,
/// The device is within a tap zone (<0.02m)
Tap,
/// The device is within a reach zone (<0.5m)
Reach,
/// The device is within a short range zone (<1.2m)
ShortRange,
/// The device is within a long range zone (<3.0m)
LongRange,
/// The device is at a far range
Far,
};
/// Wraps the handle ID to an underlying PresenceDetector object
struct PresenceDetectorHandle {
uint64_t handle;
};
/// Enum representing an optional tx power value
struct MaybeTxPower {
enum class Tag {
/// Valid TX power with associated data value
Valid,
/// Absent Tx Power
Invalid,
};
struct Valid_Body {
int32_t _0;
};
Tag tag;
union {
Valid_Body valid;
};
};
/// A PII-stripped subset of Bluetooth scan result
struct BleScanResult {
/// Device ID of the nearby device
uint64_t device_id;
/// Transmitting power of signal
MaybeTxPower tx_power;
/// RSSI value
int32_t rssi;
/// Time scan result was obtained
uint64_t elapsed_real_time_millis;
};
/// Describes the most accurate and recent measurement for a given device
struct ProximityEstimate {
/// Device ID of the nearby device
uint64_t device_id;
/// Distance to the nearby device in meters
double distance_meters;
/// Measurement confidence of the estimate
MeasurementConfidence distance_confidence;
/// The time the proximity estimate was obtained
uint64_t elapsed_real_time_millis;
/// Proximity state zone of the nearby device
ProximityState proximity_state;
/// Medium through which the proximity estimate was computed
PresenceDataSource source;
};
extern "C" {
/// Creates a new presence detector object and returns the handle for the new
/// object
PresenceDetectorHandle presence_detector_create();
/// Updates PresenceDetector with a new scan result and returns an error code
/// if unsuccessful
///
/// # Safety
///
/// Ensure that the output parameter refers to an initialized instance
int32_t update_ble_scan_result(PresenceDetectorHandle presence_detector_handle,
BleScanResult ble_scan_result,
ProximityEstimate *proximity_estimate);
/// Gets the current proximity estimate for a given device ID
///
/// # Safety
///
/// Ensure that the output parameter refers to an initialized instance
int32_t get_proximity_estimate(PresenceDetectorHandle presence_detector_handle,
uint64_t device_id,
ProximityEstimate *proximity_estimate);
/// De-allocates memory for a presence detector object
int presence_detector_free(PresenceDetectorHandle presence_detector_handle);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // PRESENCE_DETECTOR_H_
-72
View File
@@ -1,72 +0,0 @@
// Copyright 2023 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
//
// http://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.
use core::marker::PhantomData;
use fpp::presence_detector::PresenceDetector;
use lazy_static::lazy_static;
use rand::Rng;
use std::collections::HashMap;
use std::sync::{Mutex, MutexGuard};
pub(crate) struct HandleMap<T> {
_marker: PhantomData<T>,
map: HashMap<u64, T>,
}
impl<T> HandleMap<T> {
pub(crate) fn init() -> Self {
Self {
_marker: Default::default(),
map: HashMap::new(),
}
}
/// inserts an entry into the map and returns the randomly generated handle to the entry
pub(crate) fn insert(&mut self, data: T) -> u64 {
let mut rng = rand::thread_rng();
let mut handle: u64 = rng.gen();
while self.map.contains_key(&handle) {
handle = rng.gen();
}
assert!(self.map.insert(handle, data).is_none());
handle
}
/// Removes an entry at a given handle returning an Option of the owned value
pub(crate) fn remove(&mut self, handle: &u64) -> Option<T> {
self.map.remove(handle)
}
/// Gets a reference to the entry stored at the specified handle
pub(crate) fn get(&mut self, handle: &u64) -> Option<&mut T> {
self.map.get_mut(handle)
}
}
// Returns a threadsafe instance of the global static hashmap tracking the PresenceDetector handles
pub(crate) fn get_presence_detector_handle_map(
) -> MutexGuard<'static, HandleMap<Box<PresenceDetector>>> {
PRESENCE_DETECTOR_HANDLE_MAP
.lock()
.unwrap_or_else(|err_guard| err_guard.into_inner())
}
// Global hashmap to track valid pointers, this is a safety precaution to make sure we are not
// reading from unsafe memory address's passed in by the caller
lazy_static! {
static ref PRESENCE_DETECTOR_HANDLE_MAP: Mutex<HandleMap<Box<PresenceDetector>>> =
Mutex::new(HandleMap::init());
}
-141
View File
@@ -1,141 +0,0 @@
// Copyright 2023 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
//
// http://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.
#![deny(
missing_docs,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
//! Rust FFI wrapper for PresenceDetector. Can be called from C/C++ clients
use fpp::fused_presence_utils::*;
use fpp::presence_detector::*;
use crate::handle_map::get_presence_detector_handle_map;
mod handle_map;
/// Wraps the handle ID to an underlying PresenceDetector object
#[repr(C)]
pub struct PresenceDetectorHandle {
handle: u64,
}
/// Enum class representing possible outputs of proximity data processing call
#[repr(C)]
pub enum ComputationStatus {
/// Returned if the proximity estimate calculation was successful
Success,
/// Returned if there is no computed proximity estimate
NoComputedProximityEstimate,
/// Returned if the handle is invalid
InvalidPresenceDetectorHandleError,
/// Returned if the output parameter is null
NullOutputParameterError,
}
impl ComputationStatus {
fn to_status_code(&self) -> i32 {
match self {
/// Status codes 100+ are considered errors
Self::Success => 1,
Self::NoComputedProximityEstimate => 2,
Self::InvalidPresenceDetectorHandleError => 101,
Self::NullOutputParameterError => 102,
}
}
}
/// Creates a new presence detector object and returns the handle for the new
/// object
#[no_mangle]
pub extern "C" fn presence_detector_create() -> PresenceDetectorHandle {
let handle = get_presence_detector_handle_map().insert(Box::new(PresenceDetector::new()));
PresenceDetectorHandle { handle }
}
/// Updates PresenceDetector with a new scan result and returns an error code if
/// unsuccessful
///
/// # Safety
///
/// Ensure that the output parameter refers to an initialized instance
#[no_mangle]
pub unsafe extern "C" fn update_ble_scan_result(
presence_detector_handle: PresenceDetectorHandle,
ble_scan_result: BleScanResult,
proximity_estimate: *mut ProximityEstimate,
) -> i32 {
if let Some(presence_detector) =
get_presence_detector_handle_map().get(&presence_detector_handle.handle)
{
if let Some(current_proximity_estimate) =
presence_detector.on_ble_scan_result(ble_scan_result)
{
if let Some(proximity_estimate) = proximity_estimate.as_mut() {
*proximity_estimate = current_proximity_estimate;
ComputationStatus::Success.to_status_code()
} else {
ComputationStatus::NullOutputParameterError.to_status_code()
}
} else {
ComputationStatus::NoComputedProximityEstimate.to_status_code()
}
} else {
ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code()
}
}
/// Gets the current proximity estimate for a given device ID
///
/// # Safety
///
/// Ensure that the output parameter refers to an initialized instance
#[no_mangle]
pub unsafe extern "C" fn get_proximity_estimate(
presence_detector_handle: PresenceDetectorHandle,
device_id: u64,
proximity_estimate: *mut ProximityEstimate,
) -> i32 {
if let Some(presence_detector) =
get_presence_detector_handle_map().get(&presence_detector_handle.handle)
{
presence_detector.get_proximity_estimate(device_id).map(|current_proximity_estimate| {
if let Some(proximity_estimate) = proximity_estimate.as_mut() {
*proximity_estimate = current_proximity_estimate;
return ComputationStatus::Success.to_status_code();
}
ComputationStatus::NullOutputParameterError.to_status_code()
});
}
ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code()
}
/// De-allocates memory for a presence detector object
#[no_mangle]
pub extern "C" fn presence_detector_free(
presence_detector_handle: PresenceDetectorHandle,
) -> std::os::raw::c_int {
if let Some(presence_detector) =
get_presence_detector_handle_map().remove(&presence_detector_handle.handle)
{
let _ = *presence_detector;
return ComputationStatus::Success.to_status_code();
}
ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code()
}
-168
View File
@@ -1,168 +0,0 @@
// Copyright 2023 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
//
// http://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 "presence/fpp/fpp_manager.h"
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "internal/platform/logging.h"
#include "presence/fpp/fpp_c_ffi/include/presence_detector.h"
#include "presence/implementation/sensor_fusion.h"
#include "presence/presence_zone.h"
namespace nearby {
namespace presence {
namespace {
// See
// https://source.corp.google.com/piper///depot/google3/third_party/nearby/presence/fpp/fpp_c_ffi/src/lib.rs;l=49
// for constants definition
constexpr int kSuccess = 1;
constexpr int kNoComputedProximityEstimate = 2;
constexpr int kInvalidPresenceDetectorHandleError = 101;
constexpr int kNullOutputParameterError = 102;
// Converts optional tx power to the rust api compatible equivalent
MaybeTxPower ConvertTxPower(std::optional<int8_t> txPower) {
if (txPower.has_value()) {
return {MaybeTxPower::Tag::Valid, {txPower.value()}};
}
return {MaybeTxPower::Tag::Invalid, {}};
}
// Converts FPP ProximityState struct to NP RangeType struct
PresenceZone::DistanceBoundary::RangeType ConvertProximityStateToRangeType(
ProximityState proximity_state) {
switch (proximity_state) {
case ProximityState::Tap:
return PresenceZone::DistanceBoundary::RangeType::kWithinTap;
case ProximityState::Reach:
return PresenceZone::DistanceBoundary::RangeType::kWithinReach;
case ProximityState::ShortRange:
case ProximityState::LongRange:
case ProximityState::Far:
return PresenceZone::DistanceBoundary::RangeType::kFar;
case ProximityState::Unknown:
default:
LOG(WARNING) << "Proximity state is unknown";
return PresenceZone::DistanceBoundary::RangeType::kRangeUnknown;
}
}
} // namespace
absl::Status FppManager::UpdateBleScanResult(uint64_t device_id,
std::optional<int8_t> txPower,
int rssi,
uint64_t elapsed_realtime_millis) {
if (zone_transition_callbacks_.empty()) {
return absl::InternalError("No callback registered");
}
BleScanResult ble_scan_result = {device_id, ConvertTxPower(txPower), rssi,
elapsed_realtime_millis};
ProximityEstimate default_proximity_estimate =
ProximityEstimate{device_id,
/*distanceMeters=*/0.0,
MeasurementConfidence::Unknown,
/*elapsedRealtime=*/0,
ProximityState::Unknown,
PresenceDataSource::Ble};
ProximityEstimate old_proximity_estimate =
current_proximity_estimates_.contains(device_id)
? current_proximity_estimates_[device_id]
: default_proximity_estimate;
ProximityEstimate new_proximity_estimate = default_proximity_estimate;
int status_code = update_ble_scan_result(
presence_detector_handle_, ble_scan_result, &new_proximity_estimate);
if (status_code == kNoComputedProximityEstimate) {
LOG(INFO) << "Insufficient number of scan results available to "
"compute proximity state";
return absl::OkStatus();
}
if (status_code == kSuccess) {
current_proximity_estimates_[device_id] = new_proximity_estimate;
CheckPresenceZoneChanged(device_id, old_proximity_estimate,
new_proximity_estimate);
return absl::OkStatus();
}
LOG(WARNING)
<< "Could not successfully update FPP with new scan result: Error code="
<< status_code;
return absl::InternalError(GetStatusStringFromCode(status_code));
}
void FppManager::RegisterZoneTransitionListener(
uint64_t callback_id, ZoneTransitionCallback callback) {
zone_transition_callbacks_[callback_id] = std::move(callback);
}
void FppManager::UnregisterZoneTransitionListener(uint64_t callback_id) {
zone_transition_callbacks_.erase(callback_id);
}
void FppManager::ResetProximityStateData() {
current_proximity_estimates_.clear();
}
std::optional<RangingData> FppManager::GetRangingData(uint64_t device_id) {
return ConvertProximityEstimateToRangingData(
current_proximity_estimates_[device_id]);
}
// Converts FPP ProximityEstimate struct to NP RangingData struct
RangingData FppManager::ConvertProximityEstimateToRangingData(
ProximityEstimate estimate) {
RangingMeasurement ranging_measurement = {
/*confidenceLevel=*/0.0, static_cast<float>(estimate.distance_meters)};
RangingPosition ranging_position = {
ranging_measurement, /*azimuth=*/std::nullopt,
/*elevation=*/std::nullopt, estimate.elapsed_real_time_millis};
ZoneTransition zone_transition = {
ConvertProximityStateToRangeType(estimate.proximity_state),
/*confidenceLevel=*/0.0};
return {DataSource::kBle, ranging_position, zone_transition,
std::vector<DeviceMotion>()};
}
void FppManager::CheckPresenceZoneChanged(uint64_t device_id,
ProximityEstimate old_estimate,
ProximityEstimate new_estimate) {
if (old_estimate.proximity_state != new_estimate.proximity_state) {
LOG(WARNING) << "Updating zone transition callbacks with new zone. Zone="
<< static_cast<int>(new_estimate.proximity_state);
for (auto& pair : zone_transition_callbacks_) {
pair.second.on_proximity_zone_changed(
device_id,
ConvertProximityStateToRangeType(new_estimate.proximity_state));
}
}
}
std::string FppManager::GetStatusStringFromCode(int status_code) {
switch (status_code) {
case kInvalidPresenceDetectorHandleError:
return "INVALID_PRESENCE_DETECTOR_HANDLE";
case kNullOutputParameterError:
return "NULL_OUTPUT_PARAMETER";
default:
LOG(WARNING) << "Error code is unknown";
return "UNKNOWN_ERROR";
}
}
} // namespace presence
} // namespace nearby
-86
View File
@@ -1,86 +0,0 @@
// Copyright 2023 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
//
// http://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_PRESENCE_FPP_FPP_MANAGER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_FPP_FPP_MANAGER_H_
#include <cstdint>
#include <optional>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "presence/fpp/fpp_c_ffi/include/presence_detector.h"
#include "presence/implementation/sensor_fusion.h"
#include "presence/presence_zone.h"
namespace nearby {
namespace presence {
// Manages fused presence updates and serves as a sync -> async converter class
// between fpp and NP sensor fusion
class FppManager {
public:
using RangeType = PresenceZone::DistanceBoundary::RangeType;
FppManager() { presence_detector_handle_ = presence_detector_create(); }
~FppManager() { presence_detector_free(presence_detector_handle_); }
/** Updates FPP with new BLE scan results. Returns status code */
absl::Status UpdateBleScanResult(uint64_t device_id,
std::optional<int8_t> txPower, int rssi,
uint64_t elapsed_realtime_millis);
/**
* Adds callback for updates of proximity zone transitions.
*/
void RegisterZoneTransitionListener(uint64_t callback_id,
ZoneTransitionCallback callback);
/**
* Unregister callback for updates of proximity zone transitions.
*/
void UnregisterZoneTransitionListener(uint64_t callback_id);
/**
* Clears all proximity state data
*/
void ResetProximityStateData();
/*
* Converts ProximityEstimate to a NP compatible struct
*/
RangingData ConvertProximityEstimateToRangingData(ProximityEstimate estimate);
/**
* Gets the most recent ranging data for a given device
*/
std::optional<RangingData> GetRangingData(uint64_t device_id);
/*
* Converts a status code to a string representation
*/
std::string GetStatusStringFromCode(int status_code);
private:
void CheckPresenceZoneChanged(uint64_t device_id,
ProximityEstimate old_estimate,
ProximityEstimate new_estimate);
absl::flat_hash_map<uint64_t, ProximityEstimate> current_proximity_estimates_;
absl::flat_hash_map<uint64_t, ZoneTransitionCallback>
zone_transition_callbacks_;
PresenceDetectorHandle presence_detector_handle_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_FPP_FPP_MANAGER_H_
-243
View File
@@ -1,243 +0,0 @@
// Copyright 2023 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
//
// http://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 "presence/fpp/fpp_manager.h"
#include <cstdint>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "presence/implementation/sensor_fusion.h"
namespace nearby {
namespace presence {
namespace {
constexpr uint64_t kDeviceId = 1234;
constexpr int kReachRssi = -40;
constexpr int kShortRangeRssi = -60;
constexpr int kCallbackId = 12345;
TEST(FppManager, UpdateBleScanResultSuccess) {
FppManager manager;
bool callback_called = false;
manager.RegisterZoneTransitionListener(
kCallbackId,
{.on_proximity_zone_changed =
[&callback_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
callback_called = true;
}});
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/0));
// State is only computed after second consecutive scan is fulfilled
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/2000));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
EXPECT_TRUE(callback_called);
}
TEST(FppManager, ZoneTransitionDetected) {
FppManager manager;
bool callback_called = false;
manager.RegisterZoneTransitionListener(
kCallbackId,
{.on_proximity_zone_changed =
[&callback_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
callback_called = true;
}});
// ProximityEstimate is only computed after consecutive scans is fulfilled
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/2000));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
EXPECT_TRUE(callback_called);
callback_called = false;
// Update with new zone
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kShortRangeRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
EXPECT_FALSE(callback_called);
// Update with consecutive scan of new zone
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kShortRangeRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kFar);
EXPECT_TRUE(callback_called);
}
TEST(FppManager, ConvertProximityEstimateToRangingData) {
FppManager manager;
ProximityEstimate proximity_estimate =
ProximityEstimate{kDeviceId,
0.1,
MeasurementConfidence::Low,
0,
ProximityState::Reach,
PresenceDataSource::Ble};
RangingData rangingData =
manager.ConvertProximityEstimateToRangingData(proximity_estimate);
EXPECT_EQ(rangingData.data_source, DataSource::kBle);
EXPECT_EQ(rangingData.position.distance.value, 0.1f);
EXPECT_EQ(rangingData.zone_transition->confidence_level, 0.0f);
EXPECT_EQ(rangingData.zone_transition->distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
ProximityEstimate unknown_proximity_estimate =
ProximityEstimate{kDeviceId,
0.0,
MeasurementConfidence::Low,
0,
ProximityState::Unknown,
PresenceDataSource::Ble};
RangingData unknown_rangingData =
manager.ConvertProximityEstimateToRangingData(unknown_proximity_estimate);
EXPECT_EQ(unknown_rangingData.zone_transition->distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kRangeUnknown);
ProximityEstimate tap_proximity_estimate =
ProximityEstimate{kDeviceId,
0.03,
MeasurementConfidence::Low,
0,
ProximityState::Tap,
PresenceDataSource::Ble};
RangingData tap_rangingData =
manager.ConvertProximityEstimateToRangingData(tap_proximity_estimate);
EXPECT_EQ(tap_rangingData.zone_transition->distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinTap);
}
TEST(FppManager, UpdateBleScanResultWithTxPowerSuccess) {
FppManager manager;
bool callback_called = false;
manager.RegisterZoneTransitionListener(
kCallbackId,
{.on_proximity_zone_changed =
[&callback_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
callback_called = true;
}});
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/20, kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/20, kReachRssi,
/*elapsed_real_time_millis=*/2000));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinTap);
EXPECT_TRUE(callback_called);
}
TEST(FppManager, UnregisterZoneTransitionListener) {
FppManager manager;
bool callback_called = false;
manager.RegisterZoneTransitionListener(
kCallbackId,
{.on_proximity_zone_changed =
[&callback_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
callback_called = true;
}});
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/2000));
EXPECT_TRUE(callback_called);
callback_called = false;
// Unregister listener and update with new zone
manager.UnregisterZoneTransitionListener(kCallbackId);
EXPECT_EQ(manager
.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kShortRangeRssi,
/*elapsed_real_time_millis=*/0)
.code(),
absl::StatusCode::kInternal);
EXPECT_EQ(manager
.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kShortRangeRssi,
/*elapsed_real_time_millis=*/0)
.code(),
absl::StatusCode::kInternal);
EXPECT_FALSE(callback_called);
}
TEST(FppManager, ResetProximityStateData) {
FppManager manager;
bool callback_called = false;
manager.RegisterZoneTransitionListener(
kCallbackId,
{.on_proximity_zone_changed =
[&callback_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
callback_called = true;
}});
ASSERT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/0));
// State is only computed after second consecutive scan is fulfilled
ASSERT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt,
kReachRssi,
/*elapsed_real_time_millis=*/2000));
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
EXPECT_TRUE(callback_called);
// Reset proximity state data
manager.ResetProximityStateData();
EXPECT_EQ(manager.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kRangeUnknown);
}
TEST(FppManager, GetStatusStringFromCode) {
FppManager manager;
EXPECT_EQ(manager.GetStatusStringFromCode(101),
"INVALID_PRESENCE_DETECTOR_HANDLE");
EXPECT_EQ(manager.GetStatusStringFromCode(102), "NULL_OUTPUT_PARAMETER");
}
} // namespace
} // namespace presence
} // namespace nearby
-68
View File
@@ -1,68 +0,0 @@
// Copyright 2023 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
//
// http://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 "presence/fpp/sensor_fusion_impl.h"
#include <cstdint>
#include <optional>
#include <utility>
#include "absl/status/status.h"
#include "presence/fpp/fpp_manager.h"
namespace nearby {
namespace presence {
std::vector<DataSource> SensorFusionImpl::GetDataSources(
uint64_t elapsed_realtime_millis,
const std::vector<DataSource>& available_sources) {
// TODO(b/264547688) - Implement
return std::vector<DataSource>();
}
absl::Status SensorFusionImpl::UpdateBleScanResult(
uint64_t device_id, std::optional<int8_t> txPower, int rssi,
uint64_t elapsed_realtime_millis) {
return fpp_manager_.UpdateBleScanResult(device_id, txPower, rssi,
elapsed_realtime_millis);
}
void SensorFusionImpl::UpdateUwbRangingResult(uint64_t device_id,
RangingPosition position) {
// TODO(b/264547688) - Implement
}
void SensorFusionImpl::RequestZoneTransitionUpdates(
ZoneTransitionCallback callback) {
int callback_id = ++id_generator_;
callback.on_callback_id_generated(callback_id);
fpp_manager_.RegisterZoneTransitionListener(callback_id, std::move(callback));
}
void SensorFusionImpl::RequestDeviceMotionUpdates(
SensorFusion::DeviceMotionCallback callback) {
// TODO(b/264547688) - Implement
}
void SensorFusionImpl::RemoveDeviceMotionUpdates(
SensorFusion::DeviceMotionCallback callback) {
// TODO(b/264547688) - Implement
}
void SensorFusionImpl::RemoveZoneTransitionUpdates(uint64_t callback_id) {
fpp_manager_.UnregisterZoneTransitionListener(callback_id);
}
std::optional<RangingData> SensorFusionImpl::GetRangingData(
uint64_t device_id) {
return fpp_manager_.GetRangingData(device_id);
}
} // namespace presence
} // namespace nearby
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2023 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
//
// http://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_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_
#include <cstdint>
#include <vector>
#include "presence/fpp/fpp_manager.h"
#include "presence/implementation/sensor_fusion.h"
namespace nearby {
namespace presence {
class SensorFusionImpl : public SensorFusion {
public:
~SensorFusionImpl() = default;
std::vector<DataSource> GetDataSources(
uint64_t elapsed_realtime_millis,
const std::vector<DataSource>& available_sources) override;
absl::Status UpdateBleScanResult(uint64_t device_id,
std::optional<int8_t> txPower, int rssi,
uint64_t elapsed_realtime_millis) override;
void UpdateUwbRangingResult(uint64_t device_id,
RangingPosition position) override;
void RequestZoneTransitionUpdates(ZoneTransitionCallback callback) override;
void RemoveZoneTransitionUpdates(uint64_t callback_id) override;
void RequestDeviceMotionUpdates(DeviceMotionCallback callback) override;
void RemoveDeviceMotionUpdates(DeviceMotionCallback callback) override;
std::optional<RangingData> GetRangingData(uint64_t device_id) override;
private:
FppManager fpp_manager_;
int id_generator_ = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_
-110
View File
@@ -1,110 +0,0 @@
// Copyright 2023 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
//
// http://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 <cstdint>
#include <optional>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "presence/fpp/sensor_fusion_impl.h"
namespace nearby {
namespace presence {
namespace {
constexpr uint64_t kDeviceId = 1234;
constexpr int kReachRssi = -40;
TEST(SensorFusion, RequestZoneTransitionUpdatesSuccess) {
SensorFusionImpl sensor_fusion_impl;
bool callback_called = false;
bool callback2_called = false;
sensor_fusion_impl.RequestZoneTransitionUpdates(
{.on_callback_id_generated = [&callback_called](uint64_t callback_id) {
callback_called = true;
EXPECT_EQ(callback_id, 1);
}});
sensor_fusion_impl.RequestZoneTransitionUpdates(
{.on_callback_id_generated = [&callback2_called](uint64_t callback_id2) {
callback2_called = true;
EXPECT_EQ(callback_id2, 2);
}});
EXPECT_TRUE(callback2_called);
}
TEST(SensorFusion, RemoveZoneTransitionUpdates) {
SensorFusionImpl sensor_fusion_impl;
bool callback_called = false;
sensor_fusion_impl.RequestZoneTransitionUpdates(
{.on_callback_id_generated = [&callback_called](uint64_t callback_id) {
callback_called = true;
EXPECT_EQ(callback_id, 1);
}});
sensor_fusion_impl.RemoveZoneTransitionUpdates(1);
EXPECT_EQ(
sensor_fusion_impl
.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi,
/*elapsed_real_time_millis=*/0)
.code(),
absl::StatusCode::kInternal);
}
TEST(SensorFusion, UpdateBleScanResult) {
SensorFusionImpl sensor_fusion_impl;
bool proximity_zone_changed_called = false;
sensor_fusion_impl.RequestZoneTransitionUpdates(
{.on_proximity_zone_changed =
[&proximity_zone_changed_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
proximity_zone_changed_called = true;
}});
EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult(
kDeviceId, /*txPower=*/std::nullopt, kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult(
kDeviceId, /*txPower=*/std::nullopt, kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_TRUE(proximity_zone_changed_called);
}
TEST(SensorFusion, GetRangingData) {
SensorFusionImpl sensor_fusion_impl;
bool proximity_zone_changed_called = false;
sensor_fusion_impl.RequestZoneTransitionUpdates(
{.on_proximity_zone_changed =
[&proximity_zone_changed_called](
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType range_type) {
proximity_zone_changed_called = true;
}});
EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult(
kDeviceId, /*txPower=*/std::nullopt, kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult(
kDeviceId, /*txPower=*/std::nullopt, kReachRssi,
/*elapsed_real_time_millis=*/0));
EXPECT_TRUE(proximity_zone_changed_called);
EXPECT_EQ(sensor_fusion_impl.GetRangingData(kDeviceId)
->zone_transition.value()
.distance_range_type,
PresenceZone::DistanceBoundary::RangeType::kWithinReach);
}
} // namespace
} // namespace presence
} // namespace nearby
-493
View File
@@ -1,493 +0,0 @@
# Copyright 2020 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.
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
licenses(["notice"])
filegroup(
name = "presence_internal_common_srcs",
srcs = [
"action_factory.cc",
"advertisement_factory.cc",
"advertisement_filter.cc",
"base_broadcast_request.cc",
"broadcast_manager.cc",
"connection_authenticator_impl.cc",
"credential_manager_impl.cc",
"ldt.cc",
"scan_manager.cc",
"service_controller_impl.cc",
],
)
filegroup(
name = "presence_internal_common_hdrs",
srcs = [
"action_factory.h",
"advertisement_decoder.h",
"advertisement_decoder_impl.h",
"advertisement_factory.h",
"advertisement_filter.h",
"base_broadcast_request.h",
"broadcast_manager.h",
"connection_authenticator.h",
"connection_authenticator_impl.h",
"credential_manager.h",
"credential_manager_impl.h",
"ldt.h",
"scan_manager.h",
"service_controller.h",
"service_controller_impl.h",
],
)
cc_library(
name = "internal",
srcs = [
"advertisement_decoder_rust_impl.cc",
":presence_internal_common_srcs",
],
hdrs = [
"advertisement_decoder_rust_impl.h",
":presence_internal_common_hdrs",
],
defines = ["USE_RUST_DECODER=1"],
visibility = [
"//presence:__subpackages__",
],
deps = [
"//internal/crypto",
"//internal/crypto_cros",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence:types",
"//presence/implementation/mediums",
"@beto-core//:ldt_np_adv_ffi",
"@beto-core//:np_c_ffi_types",
"@beto-core//:np_cpp_ffi",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/hash",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/log:die_if_null",
"@com_google_absl//absl/random",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_absl//absl/types:variant",
],
)
cc_library(
name = "internal_deprecated",
srcs = [
"advertisement_decoder_impl.cc",
":presence_internal_common_srcs",
],
hdrs = [
"advertisement_decoder_impl.h",
":presence_internal_common_hdrs",
],
visibility = [
"//presence:__subpackages__",
],
deps = [
"//devtools/rust:rust_okay_here",
"//internal/crypto",
"//internal/crypto_cros",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence:types",
"//presence/implementation/mediums",
"@beto-core//:ldt_np_adv_ffi",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/hash",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/log:die_if_null",
"@com_google_absl//absl/random",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_absl//absl/types:variant",
],
)
cc_library(
name = "sensor_fusion",
hdrs = ["sensor_fusion.h"],
visibility = [
"//presence:__subpackages__",
],
deps = [
"//presence:types",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "internal_test",
testonly = True,
srcs = [
],
hdrs = [
"mock_connection_authenticator.h",
"mock_credential_manager.h",
"mock_service_controller.h",
],
visibility = [
"//presence:__subpackages__",
],
deps = [
":internal",
"//internal/platform/implementation:comm",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings:string_view",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "advertisement_decoder_test",
size = "small",
srcs = ["advertisement_decoder_test.cc"],
deps = [
":internal_deprecated",
"//internal/platform:base",
"//internal/proto:credential_cc_proto",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "advertisement_decoder_new_format_test",
size = "small",
srcs = ["advertisement_decoder_new_format_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/proto:credential_cc_proto",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "advertisement_filter_test",
size = "small",
srcs = ["advertisement_filter_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/proto:credential_cc_proto",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "advertisement_factory_test",
size = "small",
srcs = ["advertisement_factory_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/proto:credential_cc_proto",
"//presence:types",
"//presence/implementation/mediums",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "broadcast_manager_test",
size = "small",
srcs = ["broadcast_manager_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/proto:credential_cc_proto",
"//presence/implementation/mediums",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "ldt_test",
size = "small",
srcs = ["ldt_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "base_broadcast_request_test",
srcs = ["base_broadcast_request_test.cc"],
deps = [
":internal",
"//internal/proto:credential_cc_proto",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/types:variant",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "action_factory_test",
size = "small",
srcs = ["action_factory_test.cc"],
deps = [
":internal",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "connection_authenticator_impl_test",
size = "small",
srcs = ["connection_authenticator_impl_test.cc"],
deps = [
":internal",
"//internal/crypto",
"//internal/crypto_cros",
"//internal/proto:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"//presence/proto:presence_frame_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "credential_manager_impl_test",
size = "small",
srcs = ["credential_manager_impl_test.cc"],
deps = [
":internal",
"//internal/platform:comm",
"//internal/platform:logging",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//net/proto2/contrib/parse_proto:testing",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "scan_manager_test",
size = "small",
srcs = ["scan_manager_test.cc"],
deps = [
":internal",
":internal_test",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:logging",
"//internal/platform:mac_address",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//presence:types",
"//presence/implementation/mediums",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:variant",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
cc_test(
name = "service_controller_impl_test",
size = "small",
srcs = ["service_controller_impl_test.cc"],
deps = [
":internal",
":internal_test",
"//internal/platform:comm",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/proto:credential_cc_proto",
"//net/proto2/contrib/parse_proto:testing",
"//presence/implementation/mediums",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
-106
View File
@@ -1,106 +0,0 @@
// Copyright 2022 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 "presence/implementation/action_factory.h"
#include <algorithm>
#include <cstdint>
#include <vector>
#include "internal/platform/logging.h"
#include "presence/data_element.h"
#include "presence/implementation/base_broadcast_request.h"
namespace nearby {
namespace presence {
constexpr int kContentTimestampMask = 0x0F;
constexpr int kContentTimestampShift = 28;
constexpr int kEmptyMask = 0;
constexpr int kActionSizeInBits = 32;
namespace {
int GetActionMask(ActionBit action) {
int bit = static_cast<int>(action);
if (bit < 0 || bit >= kActionSizeInBits) {
LOG(WARNING) << "Unsupported action " << static_cast<int>(action);
return kEmptyMask;
}
return 1 << (kActionSizeInBits - 1 - bit);
}
// The reverse of `GetActionMask()`
ActionBit GetActionFromBit(int bit) {
return ActionBit(kActionSizeInBits - 1 - bit);
}
int GetMask(const DataElement& element) {
int type = element.GetType();
switch (type) {
case DataElement::kContextTimestampFieldType: {
auto value = element.GetValue();
if (!value.empty()) {
return (value[0] & kContentTimestampMask) << kContentTimestampShift;
} else {
LOG(WARNING) << "Context timestamp Data Element without value";
return kEmptyMask;
}
}
case DataElement::kActionFieldType: {
if (element.GetValue().empty()) {
LOG(WARNING) << "Action Data Element without value";
return kEmptyMask;
}
return GetActionMask(ActionBit(element.GetValue()[0]));
}
}
LOG(WARNING) << "Data Element " << type
<< " not supported in base advertisement";
return kEmptyMask;
}
} // namespace
Action ActionFactory::CreateAction(
const std::vector<DataElement>& data_elements) {
Action action = {.action = 0};
std::for_each(data_elements.begin(), data_elements.end(),
[&](const auto& element) {
int mask = GetMask(element);
action.action |= mask;
});
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);
}
constexpr int kFirstUsedBit =
kActionSizeInBits - static_cast<int>(ActionBit::kLastAction);
for (int i = kFirstUsedBit; i < kContentTimestampShift; i++) {
int bit_mask = 1 << i;
if (action.action & bit_mask) {
output.emplace_back(DataElement(GetActionFromBit(i)));
}
}
}
} // namespace presence
} // namespace nearby
-45
View File
@@ -1,45 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_
#include <vector>
#include "presence/data_element.h"
#include "presence/implementation/base_broadcast_request.h"
namespace nearby {
namespace presence {
// 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);
// 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
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_
@@ -1,107 +0,0 @@
// Copyright 2022 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 "presence/implementation/action_factory.h"
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "presence/data_element.h"
#include "presence/implementation/base_broadcast_request.h"
namespace nearby {
namespace presence {
namespace {
using ::testing::ElementsAre;
constexpr uint32_t kActiveUnlockBitMask = 1 << 23;
constexpr uint32_t kFastPairBitMask = 1 << 17;
TEST(ActionFactory, CreateActiveUnlockAction) {
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
Action action = ActionFactory::CreateAction(data_elements);
EXPECT_EQ(action.action, kActiveUnlockBitMask);
}
TEST(ActionFactory, CreateActiveIgnoresUnsupportedActions) {
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
// The action is 32 bit, so the valid range is [0-31]
data_elements.emplace_back(ActionBit(-1));
data_elements.emplace_back(ActionBit(32));
Action action = ActionFactory::CreateAction(data_elements);
EXPECT_EQ(action.action, kActiveUnlockBitMask);
}
TEST(ActionFactory, CreateContextTimestamp) {
const std::string kTimestamp = absl::HexStringToBytes("0B");
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement::kContextTimestampFieldType,
kTimestamp);
Action action = ActionFactory::CreateAction(data_elements);
EXPECT_EQ(action.action, 0x0BU << 28);
}
TEST(ActionFactory, CreateContextTimestampAndFastPair) {
const std::string kTimestamp = absl::HexStringToBytes("0B");
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement::kContextTimestampFieldType,
kTimestamp);
data_elements.emplace_back(ActionBit::kFastPairSassAction);
Action action = ActionFactory::CreateAction(data_elements);
EXPECT_EQ(action.action, (0x0BU << 28) | kFastPairBitMask);
}
TEST(ActionFactory, DecodeActiveUnlockAction) {
constexpr Action kAction = {.action = kActiveUnlockBitMask};
std::vector<DataElement> data_elements;
ActionFactory::DecodeAction(kAction, data_elements);
EXPECT_THAT(
data_elements,
ElementsAre(DataElement(DataElement(ActionBit::kActiveUnlockAction))));
}
TEST(ActionFactory, DecodeContextTimestampAndFastPair) {
constexpr Action kAction = {.action = (0x0BU << 28) | kFastPairBitMask};
std::vector<DataElement> data_elements;
ActionFactory::DecodeAction(kAction, data_elements);
EXPECT_THAT(
data_elements,
ElementsAre(DataElement(DataElement::kContextTimestampFieldType,
absl::HexStringToBytes("0B")),
DataElement(DataElement(ActionBit::kFastPairSassAction))));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,59 +0,0 @@
// Copyright 2024 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_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_
#include <cstdint>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
namespace nearby {
namespace presence {
// The structured decoded form of a detected Nearby Presence advertisement
struct Advertisement {
uint8_t version = 0;
std::vector<DataElement> data_elements;
absl::StatusOr<internal::SharedCredential> public_credential =
absl::NotFoundError("");
internal::IdentityType identity_type = internal::IDENTITY_TYPE_UNSPECIFIED;
std::string metadata_key;
};
// Interface for decoding Nearby Presence advertisements from a payload of raw
// bytes into a structured, decrypted, and decoded format
class AdvertisementDecoder {
public:
// Is needed otherwise deleting an instance via a pointer to a base class
// results in undefined behavior
virtual ~AdvertisementDecoder() = default;
// Returns the structured and decoded contents of an advertisement given a
// payload of bytes as a string. Returns an error if the advertisement is
// misformatted or if it couldn't be decrypted.
virtual absl::StatusOr<Advertisement> DecodeAdvertisement(
absl::string_view advertisement) = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_
@@ -1,287 +0,0 @@
// Copyright 2022 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 "presence/implementation/advertisement_decoder_impl.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "internal/platform/logging.h"
#include "presence/data_element.h"
#include "presence/implementation/action_factory.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/ldt.h"
namespace nearby {
namespace presence {
namespace {
constexpr uint8_t kDataTypeMask =
(1 << DataElement::kDataElementLengthShift) - 1;
constexpr int kAdvertisementVersion = 0;
constexpr int kEncryptedIdentityAdditionalLength =
kSaltSize + kBaseMetadataSize;
constexpr int kEddystoneAdditionalLength = 20;
uint8_t GetDataElementType(uint8_t header) { return header & kDataTypeMask; }
size_t GetDataElementLength(uint8_t header) {
return header >> DataElement::kDataElementLengthShift;
}
// Verifies if the DE header describes a valid DE in v0 advertisement.
bool IsDataElementAllowed(uint8_t header) {
uint8_t data_type = GetDataElementType(header);
size_t length = GetDataElementLength(header);
switch (data_type) {
case DataElement::kSaltFieldType:
return length == 2;
case DataElement::kPublicIdentityFieldType:
return length == 0;
case DataElement::kPrivateGroupIdentityFieldType:
case DataElement::kContactsGroupIdentityFieldType:
return length >= 2 && length <= 6;
case DataElement::kTxPowerFieldType:
return length == 1;
case DataElement::kActionFieldType:
return length >= 1 && length <= 3;
case DataElement::kModelIdFieldType:
return length == 3;
case DataElement::kEddystoneIdFieldType:
return length == 0;
case DataElement::kAccountKeyDataFieldType:
return length <= 12;
case DataElement::kConnectionStatusFieldType:
return length <= 3;
case DataElement::kBatteryFieldType:
return length <= 3;
default:
return false;
}
}
bool IsEncryptedIdentity(int data_type) {
return data_type == DataElement::kPrivateGroupIdentityFieldType ||
data_type == DataElement::kContactsGroupIdentityFieldType;
}
bool IsIdentity(int data_type) {
return data_type == DataElement::kPublicIdentityFieldType ||
IsEncryptedIdentity(data_type);
}
internal::IdentityType GetIdentityType(int data_type) {
switch (data_type) {
case DataElement::kPrivateGroupIdentityFieldType:
return internal::IDENTITY_TYPE_PRIVATE_GROUP;
case DataElement::kContactsGroupIdentityFieldType:
return internal::IDENTITY_TYPE_CONTACTS_GROUP;
case DataElement::kPublicIdentityFieldType:
return internal::IDENTITY_TYPE_PUBLIC;
}
return internal::IDENTITY_TYPE_UNSPECIFIED;
}
// Returns the real length of a DE in v0 advertisement, which may be larger than
// the value in the header.
size_t GetDataElementTrueLength(uint8_t header) {
uint8_t data_type = GetDataElementType(header);
size_t length = GetDataElementLength(header);
if (IsEncryptedIdentity(data_type)) {
// The length of an encrypted DE is 16 bytes of overhead (salt + metadata
// key) + the actual payload, which is too long to fit in the 4-bit DE
// length field.
length += kEncryptedIdentityAdditionalLength;
} else if (data_type == DataElement::kEddystoneIdFieldType) {
// Length in the header is set to EID length minus 20, because EID is longer
// than 15 (the maximum length that can be stored in 4 bits.
length += kEddystoneAdditionalLength;
}
return length;
}
absl::StatusOr<DataElement> ParseDataElement(const absl::string_view input,
size_t& index) {
if (index >= input.size()) {
return absl::OutOfRangeError(absl::StrFormat(
"Data element (%s) is %d bytes long. Expected more than %d",
absl::BytesToHexString(input), input.size(), index));
}
uint8_t header = input[index];
if (!IsDataElementAllowed(header)) {
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported Data Element 0x%x", header));
}
uint8_t data_type = GetDataElementType(header);
size_t length = GetDataElementTrueLength(header);
++index;
size_t start = index;
index += length;
if (index > input.size()) {
return absl::OutOfRangeError(absl::StrFormat(
"Data element (%s) is %d bytes long. Expected at least %d",
absl::BytesToHexString(input), input.size(), index));
}
VLOG(1) << "Type: " << static_cast<int>(data_type)
<< " length: " << static_cast<int>(length)
<< " DE: " << absl::BytesToHexString(input.substr(start, length));
return DataElement(data_type, input.substr(start, length));
}
} // namespace
void DecodeBaseAction(absl::string_view serialized_action,
Advertisement& decoded_advertisement) {
if (serialized_action.empty() || serialized_action.size() > 3) {
LOG(WARNING) << "Base NP action \'"
<< absl::BytesToHexString(serialized_action)
<< "\' has wrong length " << serialized_action.size()
<< " , expected size in range [1 - 3]";
return;
}
// Action, 0-2 bytes in Big Endian order.
Action action = {.action = 0};
for (int i = 0; i < serialized_action.size(); ++i) {
int offset = (sizeof(uint32_t) - 1 - i) * 8;
action.action |= serialized_action[i] << offset;
}
ActionFactory::DecodeAction(action, decoded_advertisement.data_elements);
}
absl::StatusOr<std::string> DecryptLdt(
const std::vector<internal::SharedCredential>& credentials,
absl::string_view salt, absl::string_view encrypted_contents,
Advertisement& decoded_advertisement) {
if (credentials.empty()) {
return absl::UnavailableError("No credentials");
}
for (const auto& credential : credentials) {
absl::StatusOr<LdtEncryptor> encryptor = LdtEncryptor::Create(
credential.key_seed(), credential.metadata_encryption_key_tag_v0());
if (encryptor.ok()) {
absl::StatusOr<std::string> result =
encryptor->DecryptAndVerify(encrypted_contents, salt);
if (result.ok() && result->size() > kBaseMetadataSize) {
decoded_advertisement.public_credential = credential;
decoded_advertisement.metadata_key =
result->substr(0, kBaseMetadataSize);
return result->substr(kBaseMetadataSize);
}
}
}
return absl::UnavailableError(
"Couldn't decrypt the message with any credentials");
}
absl::Status DecryptDataElements(
const std::vector<internal::SharedCredential>& credentials,
const DataElement& elem, Advertisement& decoded_advertisement) {
if (elem.GetValue().size() <= kEncryptedIdentityAdditionalLength) {
return absl::OutOfRangeError(absl::StrFormat(
"Encrypted identity data element is too short - %d bytes",
elem.GetValue().size()));
}
absl::string_view salt = elem.GetValue().substr(0, kSaltSize);
decoded_advertisement.data_elements.emplace_back(DataElement::kSaltFieldType,
salt);
absl::string_view encrypted = elem.GetValue().substr(kSaltSize);
absl::StatusOr<std::string> decrypted =
DecryptLdt(credentials, salt, encrypted, decoded_advertisement);
if (!decrypted.ok()) {
LOG(WARNING) << "Failed to decrypt advertisement, status: "
<< decrypted.status();
return decrypted.status();
}
size_t index = 0;
while (index < decrypted->size()) {
absl::StatusOr<DataElement> internal_elem =
ParseDataElement(*decrypted, index);
if (!internal_elem.ok()) {
LOG(WARNING) << "Failed to read data element, status: "
<< internal_elem.status();
return internal_elem.status();
}
if (internal_elem->GetType() == DataElement::kActionFieldType) {
DecodeBaseAction(internal_elem->GetValue(), decoded_advertisement);
} else {
decoded_advertisement.data_elements.push_back(*std::move(internal_elem));
}
}
return absl::OkStatus();
}
absl::StatusOr<Advertisement> AdvertisementDecoderImpl::DecodeAdvertisement(
absl::string_view advertisement) {
Advertisement decoded_advertisement = Advertisement{};
std::vector<DataElement> result;
LOG(INFO) << "Advertisement: " << absl::BytesToHexString(advertisement);
if (advertisement.empty()) {
return absl::OutOfRangeError("Empty advertisement");
}
uint8_t version = advertisement[0];
VLOG(1) << "Version: " << version;
if (version != kAdvertisementVersion) {
return absl::UnimplementedError(absl::StrFormat(
"Advertisement version (%d) is not supported", version));
}
decoded_advertisement.version = version;
size_t index = 1;
absl::StatusOr<std::string> decrypted;
while (index < advertisement.size()) {
absl::StatusOr<DataElement> elem = ParseDataElement(advertisement, index);
if (!elem.ok()) {
LOG(WARNING) << "Failed to read data element, status: " << elem.status();
return elem.status();
}
if (IsIdentity(elem->GetType())) {
decoded_advertisement.identity_type = GetIdentityType(elem->GetType());
}
if (IsEncryptedIdentity(elem->GetType())) {
if (credentials_map_ == nullptr) {
return absl::FailedPreconditionError("Missing credentials");
}
auto identity_type_specific_creds =
(*credentials_map_)[decoded_advertisement.identity_type];
absl::Status status = DecryptDataElements(identity_type_specific_creds,
*elem, decoded_advertisement);
if (!status.ok()) {
return status;
}
} else {
if (elem->GetType() == DataElement::kActionFieldType) {
DecodeBaseAction(elem->GetValue(), decoded_advertisement);
} else {
decoded_advertisement.data_elements.push_back(*std::move(elem));
}
}
}
return std::move(decoded_advertisement);
}
} // namespace presence
} // namespace nearby
@@ -1,51 +0,0 @@
// Copyright 2022 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_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/proto/credential.pb.h"
#include "presence/implementation/advertisement_decoder.h"
namespace nearby {
namespace presence {
// Implements the C++ backed parsing and decrypting of advertisement bytes
class AdvertisementDecoderImpl : public AdvertisementDecoder {
public:
AdvertisementDecoderImpl() = default;
explicit AdvertisementDecoderImpl(
absl::flat_hash_map<nearby::internal::IdentityType,
std::vector<internal::SharedCredential>>*
credentials_map)
: credentials_map_(credentials_map) {}
absl::StatusOr<Advertisement> DecodeAdvertisement(
absl::string_view advertisement) override;
private:
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::SharedCredential>>*
credentials_map_ = nullptr;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_
@@ -1,156 +0,0 @@
// Copyright 2022 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 <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/implementation/advertisement_decoder_rust_impl.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::ByteArray; // NOLINT
using ::nearby::internal::IdentityType; // NOLINT
using ::nearby::internal::SharedCredential; // NOLINT
using ::testing::ElementsAre;
using ::testing::status::StatusIs;
TEST(AdvertisementDecoderImpl, DecodePublicAdvertisement) {
std::string V0AdvPlaintextBytes =
"00" // Adv Header V0 unencrypted
"1503"; // length 1 Tx Power DE value 3
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvPlaintextBytes));
ASSERT_OK(result);
EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC);
EXPECT_EQ(result->version, 0);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("03"))));
}
TEST(AdvertisementDecoderImpl, DecodePublicAdvertisementMultiDe) {
std::string V0AdvPlaintextMultiDeBytes =
"00" // Adv Header V0 unencrypted
"1505" // length 1 Tx Power DE value 5
"260040"; // length 2 actions de with NearbyShare bit set
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes(V0AdvPlaintextMultiDeBytes));
ASSERT_OK(result);
EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC);
EXPECT_EQ(result->version, 0);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(ActionBit::kNearbyShareAction)));
}
// V0 encrypted advertisement data - ripped out of np_adv/tests/examples_v0.rs
TEST(AdvertisementDecoderImpl, DecodeEncryptedAdvertisement) {
std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC";
ByteArray seed({
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
});
ByteArray known_mac({0x09, 0xFE, 0x9E, 0x81, 0xB7, 0x3E, 0x5E, 0xCC,
0x76, 0x59, 0x57, 0x71, 0xE0, 0x1F, 0xFB, 0x34,
0x38, 0xE7, 0x5F, 0x24, 0xA7, 0x69, 0x56, 0xA0,
0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD});
SharedCredential public_credential;
public_credential.set_key_seed(seed.AsStringView());
public_credential.set_metadata_encryption_key_tag_v0(
known_mac.AsStringView());
public_credential.set_id(12345678);
absl::flat_hash_map<IdentityType, std::vector<internal::SharedCredential>>
credentials;
credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back(
public_credential);
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(&credentials);
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvEncryptedBytes));
ASSERT_OK(result);
EXPECT_EQ(result->public_credential.value().id(), public_credential.id());
EXPECT_EQ(result->public_credential.value().key_seed(),
public_credential.key_seed());
EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_EQ(result->version, 0);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType,
absl::HexStringToBytes("2222")),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("03"))));
}
TEST(AdvertisementDecoderImpl, DecodeEncryptedAdvertisementNoCreds) {
std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC";
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvEncryptedBytes));
EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnavailable));
}
TEST(AdvertisementDecoderImpl, V1AdvCurrentlyUnsupported) {
std::string V1Adv =
"20" // Version header V1
"00" // format
"02" // section len
"1503"; // Tx power value 3
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(V1Adv));
EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnimplemented));
}
TEST(AdvertisementDecoderImpl, V0InvalidEmptyAdv) {
std::string V1Adv = "00";
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(V1Adv));
EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(AdvertisementDecoderImpl, V0InvalidAdvContents) {
std::string invalid_v0_adv =
"00" // Adv Header V0 unencrypted
"3503"; // length 3 Tx Power DE with only 1 byte
AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl();
absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(invalid_v0_adv));
EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,241 +0,0 @@
// Copyright 2022 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 "presence/implementation/advertisement_decoder_rust_impl.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "np_cpp_ffi_types.h"
#include "nearby_protocol.h"
#include "internal/platform/logging.h"
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder.h"
namespace nearby {
namespace presence {
namespace {
absl::StatusOr<::nearby_protocol::ActionType> MapAction(
const ActionBit action) {
return ::nearby_protocol::ActionType::TryBuildFromU8(
static_cast<uint8_t>(action));
}
void AddActionsToAdvertisement(const nearby_protocol::V0Actions& parsed_actions,
Advertisement& advertisement) {
for (const auto action : kAllActionBits) {
auto action_type = MapAction(action);
if (!action_type.ok()) {
LOG(WARNING) << "Advertisement contains an unsupported action bit: "
<< (int)action;
continue;
}
if (parsed_actions.HasAction(*action_type)) {
advertisement.data_elements.push_back(DataElement(action));
}
}
}
void ProcessDataElement(const nearby_protocol::V0DataElement& data_element,
Advertisement& advertisement) {
switch (data_element.GetKind()) {
case nearby_protocol::V0DataElementKind::TxPower: {
advertisement.data_elements.push_back(DataElement(
DataElement::kTxPowerFieldType, data_element.AsTxPower().GetAsI8()));
return;
}
case nearby_protocol::V0DataElementKind::Actions: {
AddActionsToAdvertisement(data_element.AsActions(), advertisement);
return;
}
default: {
LOG(WARNING) << "Unsupported data element type: "
<< (int)data_element.GetKind();
}
}
}
internal::IdentityType GetIdentityType(
nearby_protocol::DeserializedV0IdentityKind identity) {
switch (identity) {
case np_ffi::internal::DeserializedV0IdentityKind::Plaintext:
return internal::IdentityType::IDENTITY_TYPE_PUBLIC;
case np_ffi::internal::DeserializedV0IdentityKind::Decrypted:
return internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
}
}
absl::StatusOr<::nearby::internal::SharedCredential> FindById(
std::vector<::nearby::internal::SharedCredential> private_credentials,
uint64_t id) {
auto cred =
std::find_if(private_credentials.begin(), private_credentials.end(),
[&id](const auto& x) { return x.id() == id; });
if (cred == private_credentials.end()) {
return absl::NotFoundError("No credential found with id: " +
std::to_string(id));
}
return *cred;
}
absl::Status ProcessLegibleV0Adv(
nearby_protocol::LegibleDeserializedV0Advertisement legible_adv,
std::vector<::nearby::internal::SharedCredential> private_credentials,
Advertisement& advertisement) {
advertisement.identity_type = GetIdentityType(legible_adv.GetIdentityKind());
auto num_des = legible_adv.GetNumberOfDataElements();
auto payload = legible_adv.IntoPayload();
// TODO(b/333126765): salt isn't a DE, we should restructure the
// Advertisement struct to reflect this
if (advertisement.identity_type ==
internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP) {
auto cred_details = payload.TryGetIdentityDetails();
if (!cred_details.ok()) {
return cred_details.status();
}
advertisement.public_credential =
FindById(private_credentials, cred_details->cred_id);
// TODO(b/333126765): update salt to use unsigned char * to remove cast
std::string salt(reinterpret_cast<char const*>(cred_details->salt), 2);
advertisement.data_elements.push_back(DataElement(0x00, salt));
std::string metadata_key(
reinterpret_cast<char const*>(cred_details->identity_token), 14);
advertisement.metadata_key = std::move(metadata_key);
}
for (int i = 0; i < num_des; i++) {
auto de_result = payload.TryGetDataElement(i);
if (!de_result.ok()) {
return de_result.status();
}
ProcessDataElement(*de_result, advertisement);
}
return absl::OkStatus();
}
absl::Status ProcessV0Advertisement(
nearby_protocol::DeserializedV0Advertisement result,
std::vector<::nearby::internal::SharedCredential> private_credentials,
Advertisement& adv) {
switch (result.GetKind()) {
case nearby_protocol::DeserializedV0AdvertisementKind::Legible:
return ProcessLegibleV0Adv(result.IntoLegible(), private_credentials,
adv);
break;
case nearby_protocol::DeserializedV0AdvertisementKind::
NoMatchingCredentials: {
return absl::UnavailableError(
"Couldn't decrypt the message with any credentials");
}
}
}
} // namespace
absl::StatusOr<Advertisement> AdvertisementDecoderImpl::DecodeAdvertisement(
absl::string_view advertisement) {
auto byte_buffer = nearby_protocol::ByteBuffer<
nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(advertisement);
if (!byte_buffer.ok()) {
return absl::InvalidArgumentError("Invalid length advertisement");
}
Advertisement decoded_advertisement;
const nearby_protocol::RawAdvertisementPayload payload(byte_buffer.value());
auto deserialize_result =
nearby_protocol::Deserializer::DeserializeAdvertisement(payload,
cred_book_);
switch (deserialize_result.GetKind()) {
case np_ffi::internal::DeserializeAdvertisementResultKind::Error: {
return absl::InvalidArgumentError("Invalid advertisement format");
}
case np_ffi::internal::DeserializeAdvertisementResultKind::V1: {
return absl::UnimplementedError(
absl::StrFormat("V1 Advertisement format is not supported"));
}
case np_ffi::internal::DeserializeAdvertisementResultKind::V0: {
decoded_advertisement.version = 0;
auto result =
ProcessV0Advertisement(deserialize_result.IntoV0(),
private_credentials_, decoded_advertisement);
if (!result.ok()) {
return result;
}
break;
}
}
return decoded_advertisement;
}
nearby_protocol::CredentialBook
AdvertisementDecoderImpl::InitializeCredentialBook(
absl::flat_hash_map<nearby::internal::IdentityType,
std::vector<::nearby::internal::SharedCredential>>*
credentials_map) {
if (credentials_map == nullptr) {
nearby_protocol::CredentialSlab slab;
nearby_protocol::CredentialBook cred_book(slab);
return cred_book;
}
nearby_protocol::CredentialSlab slab;
for (const auto& credential : (*credentials_map)
[internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP]) {
// Make sure the vector is not empty, as this is a prerequisite of the Rust
// code we call into.
std::vector<uint8_t> metadata_bytes(1);
if (!credential.encrypted_metadata_bytes_v0().empty()) {
metadata_bytes =
std::vector<uint8_t>(credential.encrypted_metadata_bytes_v0().begin(),
credential.encrypted_metadata_bytes_v0().end());
}
nearby_protocol::MatchedCredentialData matched_cred(credential.id(),
metadata_bytes);
auto key_seed = credential.key_seed();
std::array<uint8_t, 32> key_seed_array;
std::copy(key_seed.begin(), key_seed.end(), key_seed_array.data());
auto tag = credential.metadata_encryption_key_tag_v0();
std::array<uint8_t, 32> tag_array;
std::copy(tag.begin(), tag.end(), tag_array.data());
auto matchable_credential = nearby_protocol::V0MatchableCredential(
key_seed_array, tag_array, matched_cred);
slab.AddV0Credential(matchable_credential);
}
nearby_protocol::CredentialBook cred_book(slab);
return cred_book;
}
} // namespace presence
} // namespace nearby
@@ -1,61 +0,0 @@
// Copyright 2022 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_PRESENCE_ADVERTISEMENT_DECODER_RUST_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_RUST_IMPL_H_
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "nearby_protocol.h"
#include "presence/implementation/advertisement_decoder.h"
namespace nearby {
namespace presence {
// Implements the Rust backed parsing and decrypting of advertisement bytes
class AdvertisementDecoderImpl : public AdvertisementDecoder {
public:
AdvertisementDecoderImpl()
: cred_book_(InitializeCredentialBook(nullptr)),
private_credentials_(
std::vector<::nearby::internal::SharedCredential>()) {}
explicit AdvertisementDecoderImpl(
absl::flat_hash_map<nearby::internal::IdentityType,
std::vector<internal::SharedCredential>>*
credentials_map)
: cred_book_(InitializeCredentialBook(credentials_map)),
private_credentials_(
(*credentials_map)
[internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP]) {}
absl::StatusOr<Advertisement> DecodeAdvertisement(
absl::string_view advertisement) override;
private:
nearby_protocol::CredentialBook InitializeCredentialBook(
absl::flat_hash_map<nearby::internal::IdentityType,
std::vector<::nearby::internal::SharedCredential>>*
credentials_map);
nearby_protocol::CredentialBook cred_book_;
std::vector<::nearby::internal::SharedCredential> private_credentials_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_
@@ -1,226 +0,0 @@
// Copyright 2022 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 "presence/implementation/advertisement_decoder.h"
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder_impl.h"
#include "presence/scan_request.h"
#include "presence/scan_request_builder.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::ByteArray; // NOLINT
using ::nearby::internal::IdentityType; // NOLINT
using ::nearby::internal::SharedCredential; // NOLINT
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;
using ::testing::status::StatusIs;
constexpr absl::string_view kAccountName = "test account";
ScanRequest GetScanRequest() {
return {.account_name = std::string(kAccountName),
.identity_types = {
IdentityType::IDENTITY_TYPE_PRIVATE_GROUP,
IdentityType::IDENTITY_TYPE_CONTACTS_GROUP,
IdentityType::IDENTITY_TYPE_PUBLIC,
}};
}
ScanRequest GetScanRequest(std::vector<SharedCredential> credentials) {
LegacyPresenceScanFilter scan_filter = {.remote_public_credentials =
credentials};
return ScanRequestBuilder()
.SetAccountName(kAccountName)
.AddIdentityType(IdentityType::IDENTITY_TYPE_PRIVATE_GROUP)
.AddIdentityType(IdentityType::IDENTITY_TYPE_CONTACTS_GROUP)
.AddIdentityType(IdentityType::IDENTITY_TYPE_PUBLIC)
.Build();
}
SharedCredential GetPublicCredential() {
// Values copied from LDT tests
ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72,
184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152,
200, 193, 94, 107, 28, 194, 182, 32, 205, 57});
ByteArray known_mac({0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81,
0x75, 0x8D, 0x97, 0x6B, 0x5A, 0x62, 0x1C, 0x05,
0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE, 0x59, 0x87,
0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E});
SharedCredential public_credential;
public_credential.set_key_seed(seed.AsStringView());
public_credential.set_metadata_encryption_key_tag_v0(
known_mac.AsStringView());
return public_credential;
}
TEST(AdvertisementDecoderImpl,
DecodeBaseNpV0PublicIdentityWithTxAndActionFields) {
AdvertisementDecoderImpl decoder;
// v0 public identity, power and action, action value 8 for active unlock.
// These values all come from
// //third_party/nearby/presence/implementation/advertisement_factory_test.cc
auto result =
decoder.DecodeAdvertisement(absl::HexStringToBytes("000315FF260080"));
ASSERT_OK(result);
EXPECT_THAT(result->data_elements,
UnorderedElementsAre(
DataElement(DataElement::kPublicIdentityFieldType, ""),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("ff")),
DataElement(DataElement(ActionBit::kActiveUnlockAction))));
}
TEST(AdvertisementDecoderImpl, DecodeBaseNpPublicAdvertisement) {
const std::string salt = "AB";
AdvertisementDecoderImpl decoder;
const absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("002041420337C1C2C31BEE"));
ASSERT_OK(result);
EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC);
EXPECT_EQ(result->version, 0);
EXPECT_THAT(
result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPublicIdentityFieldType, ""),
DataElement(DataElement::kModelIdFieldType,
absl::HexStringToBytes("C1C2C3")),
DataElement(DataElement::kBatteryFieldType,
absl::HexStringToBytes("EE"))));
}
TEST(AdvertisementDecoderImpl, DecodeBaseNpWithTxAndActionFields) {
std::string salt = "AB";
AdvertisementDecoderImpl decoder;
auto result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("0020414203155036B04180"));
ASSERT_OK(result);
EXPECT_THAT(result->data_elements,
UnorderedElementsAre(
DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPublicIdentityFieldType, ""),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("50")),
DataElement(DataElement::kContextTimestampFieldType,
absl::HexStringToBytes("0B")),
DataElement(DataElement(ActionBit::kTapToTransferAction)),
DataElement(DataElement(ActionBit::kNearbyShareAction))));
}
TEST(AdvertisementDecoderImpl, DecodeBaseNpPrivateAdvertisement) {
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<IdentityType, std::vector<internal::SharedCredential>>
credentials;
credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back(
GetPublicCredential());
AdvertisementDecoderImpl decoder(&credentials);
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c"));
ASSERT_OK(result);
EXPECT_EQ(result->metadata_key, metadata_key.AsStringView());
EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("08"))));
}
TEST(AdvertisementDecoderImpl, InvalidEncryptedContent) {
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<IdentityType, std::vector<internal::SharedCredential>>
credentials;
credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back(
GetPublicCredential());
AdvertisementDecoderImpl decoder(&credentials);
EXPECT_THAT(decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00414142f085d661ac8cb110e792e7faeb736294")),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementDecoderImpl, UnsupportedDataElement) {
std::string valid_header_and_salt = absl::HexStringToBytes("00204142");
AdvertisementDecoderImpl decoder;
EXPECT_THAT(decoder.DecodeAdvertisement(valid_header_and_salt +
absl::HexStringToBytes("0D")),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(AdvertisementDecoderImpl, InvalidAdvertisementFieldTooShort) {
AdvertisementDecoderImpl decoder;
// 0x59 header means 5 bytes long Account Key Data but only 4 bytes follow.
EXPECT_THAT(
decoder.DecodeAdvertisement(absl::HexStringToBytes("0059A0A1A2A3")),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementDecoderImpl, ZeroLengthPayload) {
AdvertisementDecoderImpl decoder;
// A action with type 0xA and no payload
const absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes("000A"));
ASSERT_OK(result);
EXPECT_THAT(result->data_elements, ElementsAre(DataElement(0xA, "")));
}
TEST(AdvertisementDecoderImpl, EmptyAdvertisement) {
AdvertisementDecoderImpl decoder;
EXPECT_THAT(decoder.DecodeAdvertisement(""),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementDecoderImpl, UnsupportedAdvertisementVersion) {
AdvertisementDecoderImpl decoder;
EXPECT_THAT(decoder.DecodeAdvertisement(
absl::HexStringToBytes("012041420318CD29EEFF")),
StatusIs(absl::StatusCode::kUnimplemented));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,236 +0,0 @@
// Copyright 2022 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 "presence/implementation/advertisement_factory.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/logging.h"
#include "internal/platform/uuid.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/ldt.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::internal::IdentityType;
constexpr uint8_t kBaseVersion = 0;
constexpr size_t kMaxBaseNpAdvSize = 26;
absl::StatusOr<uint8_t> CreateDataElementHeader(size_t length,
unsigned data_type) {
if (length > DataElement::kMaxDataElementLength) {
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported Data Element length: %d", length));
}
if (data_type > DataElement::kMaxDataElementType) {
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported Data Element type: %d", data_type));
}
return (length << DataElement::kDataElementLengthShift) | data_type;
}
absl::Status AppendDataElement(unsigned data_type,
absl::string_view data_element,
std::string& output) {
auto header = CreateDataElementHeader(data_element.size(), data_type);
if (!header.ok()) {
LOG(WARNING) << "Can't add Data element type: " << data_type
<< ", length: " << data_element.size();
return header.status();
}
output.push_back(*header);
output.insert(output.end(), data_element.begin(), data_element.end());
return absl::OkStatus();
}
uint8_t GetIdentityFieldType(IdentityType type) {
switch (type) {
case IdentityType::IDENTITY_TYPE_PRIVATE_GROUP:
return DataElement::kPrivateGroupIdentityFieldType;
case IdentityType::IDENTITY_TYPE_CONTACTS_GROUP:
return DataElement::kContactsGroupIdentityFieldType;
case IdentityType::IDENTITY_TYPE_PUBLIC:
ABSL_FALLTHROUGH_INTENDED;
default:
return DataElement::kPublicIdentityFieldType;
}
}
std::string SerializeAction(const Action& action) {
std::string output;
uint32_t input = action.action;
for (int i = 3; i >= 0; --i) {
if (input == 0) {
return output;
}
int shift = 8 * i;
output.push_back(static_cast<char>((input >> shift) & 0xFF));
input &= (1 << shift) - 1;
}
return output;
}
bool RequiresCredentials(IdentityType identity_type) {
return identity_type == IdentityType::IDENTITY_TYPE_PRIVATE_GROUP ||
identity_type == IdentityType::IDENTITY_TYPE_CONTACTS_GROUP;
}
} // namespace
absl::StatusOr<AdvertisementData> AdvertisementFactory::CreateAdvertisement(
const BaseBroadcastRequest& request,
absl::optional<LocalCredential> credential) const {
AdvertisementData advert = {};
if (absl::holds_alternative<BaseBroadcastRequest::BasePresence>(
request.variant)) {
return CreateBaseNpAdvertisement(request, std::move(credential));
}
return advert;
}
absl::StatusOr<AdvertisementData>
AdvertisementFactory::CreateBaseNpAdvertisement(
const BaseBroadcastRequest& request,
absl::optional<LocalCredential> credential) const {
const auto& presence =
absl::get<BaseBroadcastRequest::BasePresence>(request.variant);
std::string payload;
payload.reserve(kMaxBaseNpAdvSize);
payload.push_back(kBaseVersion);
absl::Status result;
std::string tx_power = {static_cast<char>(request.tx_power)};
std::string action = SerializeAction(presence.action);
uint8_t identity_type =
GetIdentityFieldType(presence.credential_selector.identity_type);
bool needs_encryption =
identity_type != DataElement::kPublicIdentityFieldType;
if (needs_encryption) {
if (request.salt.size() != kSaltSize) {
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported salt size %d", request.salt.size()));
}
if (!credential) {
return absl::FailedPreconditionError("Missing credentials");
}
std::string unencrypted;
result = AppendDataElement(DataElement::kTxPowerFieldType, tx_power,
unencrypted);
if (!result.ok()) {
return result;
}
result =
AppendDataElement(DataElement::kActionFieldType, action, unencrypted);
if (!result.ok()) {
return result;
}
VLOG(1) << "Unencrypted advertisement payload "
<< absl::BytesToHexString(unencrypted);
absl::StatusOr<std::string> encrypted =
EncryptDataElements(*credential, request.salt, unencrypted);
if (!encrypted.ok()) {
return encrypted.status();
}
if (encrypted->size() <= kBaseMetadataSize) {
return absl::OutOfRangeError(
absl::StrFormat("Encrypted identity DE is too short - %d bytes. "
"Expected more than %d",
encrypted->size(), kBaseMetadataSize));
}
// The Identity DE header does not include the length of salt nor metadata.
absl::StatusOr<uint8_t> identity_header = CreateDataElementHeader(
encrypted->size() - kBaseMetadataSize, identity_type);
if (!identity_header.ok()) {
return identity_header.status();
}
payload.push_back(*identity_header);
// In the encrypted format, salt is not a DE (thus no header)
payload.append(request.salt);
payload.append(*encrypted);
} else {
result = AppendDataElement(identity_type, "", payload);
if (!result.ok()) {
return result;
}
if (!request.salt.empty()) {
result =
AppendDataElement(DataElement::kSaltFieldType, request.salt, payload);
if (!result.ok()) {
return result;
}
}
result =
AppendDataElement(DataElement::kTxPowerFieldType, tx_power, payload);
if (!result.ok()) {
return result;
}
result = AppendDataElement(DataElement::kActionFieldType, action, payload);
if (!result.ok()) {
return result;
}
}
return AdvertisementData{.is_extended_advertisement = false,
.content = payload};
}
absl::StatusOr<std::string> AdvertisementFactory::EncryptDataElements(
const LocalCredential& credential, absl::string_view salt,
absl::string_view data_elements) const {
if (credential.metadata_encryption_key_v0().size() != kBaseMetadataSize) {
return absl::FailedPreconditionError(absl::StrFormat(
"Metadata key size %d, expected %d",
credential.metadata_encryption_key_v0().size(), kBaseMetadataSize));
}
// HMAC is not used during encryption, so we can pass an empty value.
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(credential.key_seed(), /*known_hmac=*/"");
if (!encryptor.ok()) {
return encryptor.status();
}
std::string plaintext =
absl::StrCat(credential.metadata_encryption_key_v0(), data_elements);
return encryptor->Encrypt(plaintext, salt);
}
absl::StatusOr<CredentialSelector> AdvertisementFactory::GetCredentialSelector(
const BaseBroadcastRequest& request) {
if (absl::holds_alternative<BaseBroadcastRequest::BasePresence>(
request.variant)) {
const auto& presence =
absl::get<BaseBroadcastRequest::BasePresence>(request.variant);
if (RequiresCredentials(presence.credential_selector.identity_type)) {
return presence.credential_selector;
}
}
return absl::NotFoundError("credentials not required");
}
} // namespace presence
} // namespace nearby
@@ -1,63 +0,0 @@
// Copyright 2022 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_PRESENCE_ADVERTISEMENT_FACTORY_H_
#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_
#include <string>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
namespace presence {
// Builds BLE advertisements from broadcast requests.
class AdvertisementFactory {
public:
using LocalCredential = internal::LocalCredential;
// Returns a `CredentialSelector` if credentials are required to create an
// advertisement from the `request`.
static absl::StatusOr<CredentialSelector> GetCredentialSelector(
const BaseBroadcastRequest& request);
// Returns a BLE advertisement for given `request.
absl::StatusOr<AdvertisementData> CreateAdvertisement(
const BaseBroadcastRequest& request,
absl::optional<LocalCredential> credential) const; // NOLINT
absl::StatusOr<AdvertisementData> CreateAdvertisement(
const BaseBroadcastRequest& request) const {
return CreateAdvertisement(request,
absl::optional<LocalCredential>()); // NOLINT
}
private:
absl::StatusOr<AdvertisementData> CreateBaseNpAdvertisement(
const BaseBroadcastRequest& request,
absl::optional<LocalCredential> credential) const; // NOLINT
absl::StatusOr<std::string> EncryptDataElements(
const LocalCredential& credential, absl::string_view salt,
absl::string_view data_elements) const;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_
@@ -1,148 +0,0 @@
// Copyright 2022 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 "presence/implementation/advertisement_factory.h"
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/action_factory.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::ByteArray; // NOLINT
using ::nearby::internal::IdentityType;
using ::nearby::internal::LocalCredential; // NOLINT
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::status::StatusIs;
LocalCredential CreateLocalCredential(IdentityType identity_type) {
// Values copied from LDT tests
ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72,
184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152,
200, 193, 94, 107, 28, 194, 182, 32, 205, 57});
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
LocalCredential private_credential;
private_credential.set_identity_type(identity_type);
private_credential.set_key_seed(seed.AsStringView());
private_credential.set_metadata_encryption_key_v0(
metadata_key.AsStringView());
return private_credential;
}
TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) {
std::string account_name = "Test account";
std::string salt = "AB";
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
absl::StatusOr<AdvertisementData> result =
AdvertisementFactory().CreateAdvertisement(
request, CreateLocalCredential(kIdentity));
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content),
"00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c");
}
TEST(AdvertisementFactory, CreateAdvertisementFromTrustedIdentity) {
std::string account_name = "Test account";
std::string salt = "AB";
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_CONTACTS_GROUP;
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
data_elements.emplace_back(ActionBit::kPresenceManagerAction);
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
absl::StatusOr<AdvertisementData> result =
AdvertisementFactory().CreateAdvertisement(
request, CreateLocalCredential(kIdentity));
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content),
"0052414257a35c020f1c547d7e169303196d75da7118ba");
}
TEST(AdvertisementFactory, CreateAdvertisementFromPublicIdentity) {
std::string salt = "AB";
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PUBLIC;
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
absl::StatusOr<AdvertisementData> result =
AdvertisementFactory().CreateAdvertisement(request);
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content), "00032041421505260080");
}
TEST(AdvertisementFactory, CreateAdvertisementFailsWhenSaltIsTooShort) {
std::string salt = "AB";
constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE_GROUP;
std::vector<DataElement> data_elements;
data_elements.emplace_back(ActionBit::kActiveUnlockAction);
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
// Override the salt with invalid value
request.salt = "C";
EXPECT_THAT(AdvertisementFactory().CreateAdvertisement(request),
StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,119 +0,0 @@
// Copyright 2024 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 "presence/implementation/advertisement_filter.h"
#include <algorithm>
#include <vector>
#include "absl/types/variant.h"
#include "internal/platform/logging.h"
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
bool Contains(const std::vector<DataElement>& data_elements,
const DataElement& data_element) {
return std::find(data_elements.begin(), data_elements.end(), data_element) !=
data_elements.end();
}
bool ContainsAll(const std::vector<DataElement>& data_elements,
const std::vector<DataElement>& extended_properties) {
for (const auto& filter_element : extended_properties) {
if (!Contains(data_elements, filter_element)) {
return false;
}
}
return true;
}
bool ContainsAny(const std::vector<DataElement>& data_elements,
const std::vector<int>& actions) {
if (actions.empty()) {
return true;
}
for (int action : actions) {
if (Contains(data_elements, DataElement(ActionBit(action)))) {
return true;
}
}
return false;
}
bool AdvertisementFilter::MatchesScanFilter(
const Advertisement& advertisement) {
// Verify the identity is one requested in the scan_request.
// Per the Public API of scan_request, if identity_types provided in the
// scan_request is empty then decode advertisements of every identity type
auto requested_identity_types = scan_request_.identity_types;
if (!requested_identity_types.empty() &&
!(std::find(
requested_identity_types.begin(), requested_identity_types.end(),
advertisement.identity_type) != requested_identity_types.end())) {
LOG(INFO) << "Skipping advertisement with identity type: "
<< advertisement.identity_type
<< " because that identity type was not requested in the scan "
"request";
return false;
}
// The advertisement matches the scan request when it matches at least
// one of the filters in the request.
if (scan_request_.scan_filters.empty()) {
return true;
}
// NOLINT is used to suppress google3-legacy-absl-backport lints because the
// the suggestion is not compatible with Chrome
for (const auto& filter : scan_request_.scan_filters) {
if (absl::holds_alternative<PresenceScanFilter>(filter)) { // NOLINT
if (MatchesScanFilter(advertisement.data_elements,
absl::get<PresenceScanFilter>(filter))) { // NOLINT
return true;
}
} else if (absl::holds_alternative<LegacyPresenceScanFilter>( // NOLINT
filter)) {
if (MatchesScanFilter(
advertisement.data_elements,
absl::get<LegacyPresenceScanFilter>(filter))) { // NOLINT
return true;
}
}
}
return false;
}
bool AdvertisementFilter::MatchesScanFilter(
const std::vector<DataElement>& data_elements,
const PresenceScanFilter& filter) {
// The advertisement must contain all Data Elements in scan request.
return ContainsAll(data_elements, filter.extended_properties);
}
bool AdvertisementFilter::MatchesScanFilter(
const std::vector<DataElement>& data_elements,
const LegacyPresenceScanFilter& filter) {
// The advertisement must:
// * contain any Action from scan request,
// * contain all Data Elements in scan request.
return ContainsAny(data_elements, filter.actions) &&
ContainsAll(data_elements, filter.extended_properties);
}
} // namespace presence
} // namespace nearby
@@ -1,46 +0,0 @@
// Copyright 2024 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_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_
#include <vector>
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
class AdvertisementFilter {
public:
explicit AdvertisementFilter(ScanRequest scan_request)
: scan_request_(scan_request) {}
// Returns true if the decoded advertisement in `data_elements` matches the
// filters in `scan_request`.
bool MatchesScanFilter(const Advertisement& adv);
private:
bool MatchesScanFilter(const std::vector<DataElement>& data_elements,
const PresenceScanFilter& filter);
bool MatchesScanFilter(const std::vector<DataElement>& data_elements,
const LegacyPresenceScanFilter& filter);
ScanRequest scan_request_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_
@@ -1,174 +0,0 @@
// Copyright 2024 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 "presence/implementation/advertisement_filter.h"
#include <array>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/scan_request.h"
#include "presence/scan_request_builder.h"
namespace nearby {
namespace presence {
namespace {
TEST(AdvertisementFilter, MatchesScanFilterNoFilterPasses) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")};
ScanRequest empty_scan_request = {};
AdvertisementFilter adv_filter(empty_scan_request);
// A scan request without scan filters matches any advertisement
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.data_elements = {DataElement(
DataElement::kPrivateGroupIdentityFieldType, "payload")}}));
EXPECT_TRUE(adv_filter.MatchesScanFilter({}));
}
TEST(AdvertisementFilter, MatchesPresenceScanFilter) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")};
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
DataElement salt2 = DataElement(DataElement::kSaltFieldType, "salt 2");
PresenceScanFilter filter = {.extended_properties = {model_id, salt}};
AdvertisementFilter adv_filter(
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(adv_filter.MatchesScanFilter({}));
EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}}));
EXPECT_TRUE(
adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}}));
EXPECT_TRUE(
adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}}));
EXPECT_FALSE(
adv_filter.MatchesScanFilter({.data_elements = {salt2, model_id}}));
}
TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilter) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")};
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
DataElement salt2 = DataElement(DataElement::kSaltFieldType, "salt 2");
LegacyPresenceScanFilter filter = {.extended_properties = {model_id, salt}};
AdvertisementFilter adv_filter(
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(adv_filter.MatchesScanFilter(Advertisement{}));
EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}}));
EXPECT_TRUE(
adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}}));
EXPECT_TRUE(
adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}}));
EXPECT_FALSE(adv_filter.MatchesScanFilter(
Advertisement{.data_elements = {salt2, model_id}}));
}
TEST(AdvertisementFilter,
EncryptedIdentityFilterIgnoresPublicIdentityAdvertisement) {
AdvertisementFilter adv_filter(
{.identity_types = {
internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP,
internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP}});
EXPECT_FALSE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC}));
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}));
}
TEST(AdvertisementFilter, PublicIdentityFilterMatchesPublicIdentityAdv) {
AdvertisementFilter adv_filter(
{.identity_types = {internal::IdentityType::IDENTITY_TYPE_PUBLIC}});
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC}));
EXPECT_FALSE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}));
}
TEST(AdvertisementFilter, EmptyIdentityFilterMatchesAllAdvIdentityTypes) {
AdvertisementFilter adv_filter({});
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC}));
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}));
}
TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilterWithActions) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")};
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
DataElement ttt_action = DataElement(ActionBit::kTapToTransferAction);
LegacyPresenceScanFilter filter = {
.actions = {static_cast<int>(ActionBit::kActiveUnlockAction),
static_cast<int>(ActionBit::kTapToTransferAction)},
.extended_properties = {model_id, salt}};
AdvertisementFilter adv_filter(
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(
adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}}));
EXPECT_TRUE(adv_filter.MatchesScanFilter(
{.data_elements = {salt, ttt_action, model_id}}));
}
TEST(AdvertisementFilter, MatchesMultipleFilters) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")};
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
DataElement ttt_action = DataElement(ActionBit::kTapToTransferAction);
PresenceScanFilter presence_filter = {.extended_properties = {model_id}};
LegacyPresenceScanFilter legacy_filter = {
.actions = {static_cast<int>(ActionBit::kActiveUnlockAction),
static_cast<int>(ActionBit::kTapToTransferAction)},
.extended_properties = {salt}};
AdvertisementFilter adv_filter(ScanRequestBuilder()
.AddScanFilter(presence_filter)
.AddScanFilter(legacy_filter)
.Build());
EXPECT_TRUE(adv_filter.MatchesScanFilter({.data_elements = {model_id}}));
EXPECT_TRUE(
adv_filter.MatchesScanFilter({.data_elements = {salt, ttt_action}}));
EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {ttt_action}}));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,112 +0,0 @@
// Copyright 2022 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 "presence/implementation/base_broadcast_request.h"
#include <string>
#include <variant>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/crypto.h"
#include "internal/platform/logging.h"
#include "presence/broadcast_request.h"
#include "presence/implementation/action_factory.h"
namespace nearby {
namespace presence {
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetSalt(
absl::string_view salt) {
if (salt.size() != kSaltSize) {
LOG(WARNING) << "Unsupported salt length: " << salt.size();
} else {
salt_ = std::string(salt);
}
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetTxPower(
int8_t tx_power) {
tx_power_ = tx_power;
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetAction(
const Action& action) {
action_ = action;
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetPowerMode(
PowerMode power_mode) {
power_mode_ = power_mode;
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetAccountName(
absl::string_view account_name) {
account_name_ = std::string(account_name);
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetManagerAppId(
absl::string_view manager_app_id) {
manager_app_id_ = std::string(manager_app_id);
return *this;
}
BasePresenceRequestBuilder::operator BaseBroadcastRequest() const {
BaseBroadcastRequest::BasePresence presence{
.credential_selector = {.manager_app_id = manager_app_id_,
.account_name = account_name_,
.identity_type = identity_},
.action = action_};
std::string bytes(kSaltSize, 0);
RandBytes(const_cast<std::string::value_type*>(bytes.data()), bytes.size());
BaseBroadcastRequest broadcast_request{
.variant = presence,
.salt = salt_.size() == kSaltSize ? salt_ : bytes,
.tx_power = tx_power_,
.power_mode = power_mode_};
return broadcast_request;
}
absl::StatusOr<BaseBroadcastRequest> BaseBroadcastRequest::Create(
const BroadcastRequest& request) {
if (absl::holds_alternative<PresenceBroadcast>(request.variant)) {
const auto& presence_request =
absl::get<PresenceBroadcast>(request.variant);
if (presence_request.sections.empty()) {
return absl::InvalidArgumentError("Missing broadcast sections");
}
if (presence_request.sections.size() > 1) {
LOG(WARNING) << "Only first section is used in BLE 4.2 advertisement";
}
const PresenceBroadcast::BroadcastSection& section =
presence_request.sections.front();
return BaseBroadcastRequest(
BasePresenceRequestBuilder(section.identity)
.SetTxPower(request.tx_power)
.SetAction(ActionFactory::CreateAction(section.extended_properties))
.SetPowerMode(request.power_mode)
.SetManagerAppId(section.manager_app_id)
.SetAccountName(section.account_name));
}
return absl::UnimplementedError("Request not supported");
}
} // namespace presence
} // namespace nearby
@@ -1,102 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_
#include <stdint.h>
#include <cstddef>
#include <string>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "presence/broadcast_request.h"
#include "presence/power_mode.h"
namespace nearby {
namespace presence {
constexpr int8_t kUnspecifiedTxPower = -128;
constexpr size_t kSaltSize = 2;
// The identity metadata size in the base advertisement
constexpr size_t kBaseMetadataSize = 14;
/** Defines the action (intended actions) of base NP advertisement */
struct Action {
uint32_t action;
};
/** Defines a Nearby Presence broadcast request */
struct BaseBroadcastRequest {
// Creates `BaseBroadcastRequest` from the public API request in
// `BroadcastRequest`.
static absl::StatusOr<BaseBroadcastRequest> Create(
const BroadcastRequest& request);
struct BasePresence {
CredentialSelector credential_selector;
Action action;
};
struct BaseFastPair {
struct Discoverable {
std::string model_id;
};
struct Nondiscoverable {
std::string account_key_data;
std::string battery_info;
};
absl::variant<Discoverable, Nondiscoverable> advertisement;
};
struct BaseEddystone {
std::string ephemeral_id;
};
absl::variant<BasePresence, BaseFastPair, BaseEddystone> variant;
std::string salt;
int8_t tx_power;
unsigned int interval_ms;
PowerMode power_mode;
};
/** Builds a brodacast request variant with NP identity for BLE 4.2 */
class BasePresenceRequestBuilder {
public:
explicit BasePresenceRequestBuilder(
const nearby::internal::IdentityType& identity)
: identity_(identity) {}
BasePresenceRequestBuilder& SetSalt(absl::string_view salt);
BasePresenceRequestBuilder& SetTxPower(int8_t tx_power);
BasePresenceRequestBuilder& SetAction(const Action& action);
BasePresenceRequestBuilder& SetPowerMode(PowerMode power_mode);
BasePresenceRequestBuilder& SetAccountName(absl::string_view account_name);
BasePresenceRequestBuilder& SetManagerAppId(absl::string_view manager_app_id);
explicit operator BaseBroadcastRequest() const;
private:
nearby::internal::IdentityType identity_;
std::string salt_;
int8_t tx_power_ = kUnspecifiedTxPower;
Action action_;
PowerMode power_mode_ = PowerMode::kNoPower;
std::string account_name_;
std::string manager_app_id_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_
@@ -1,94 +0,0 @@
// Copyright 2022 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 "presence/implementation/base_broadcast_request.h"
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/types/variant.h"
#include "internal/proto/credential.pb.h"
#include "presence/broadcast_request.h"
#include "presence/data_element.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::internal::IdentityType;
using ::testing::status::StatusIs;
TEST(BroadcastRequestTest, CreateBasePresenceRequest) {
nearby::internal::IdentityType identity;
constexpr int8_t kTxPower = -13;
BaseBroadcastRequest request = BaseBroadcastRequest(
BasePresenceRequestBuilder(identity).SetTxPower(kTxPower).SetPowerMode(
PowerMode::kBalanced));
EXPECT_TRUE(absl::holds_alternative<BaseBroadcastRequest::BasePresence>(
request.variant));
EXPECT_EQ(request.salt.size(), 2);
EXPECT_EQ(request.tx_power, kTxPower);
EXPECT_EQ(request.power_mode, PowerMode::kBalanced);
}
TEST(BroadcastRequestTest, CreateFromPresenceRequest) {
constexpr int8_t kTxPower = 30;
constexpr uint32_t kExpectedAction =
(1 << 23); // encoded kActiveUnlockAction
std::string account_name = "Test account";
std::string manager_app_id = "Manager app id";
PresenceBroadcast::BroadcastSection section = {
.identity = internal::IDENTITY_TYPE_PUBLIC,
.extended_properties = {DataElement(
DataElement(ActionBit::kActiveUnlockAction))},
.account_name = account_name,
.manager_app_id = manager_app_id};
PresenceBroadcast presence_request = {.sections = {section}};
BroadcastRequest input = {.tx_power = kTxPower, .variant = presence_request};
absl::StatusOr<BaseBroadcastRequest> request =
BaseBroadcastRequest::Create(input);
ASSERT_OK(request);
EXPECT_THAT(request->tx_power, kTxPower);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.credential_selector.identity_type,
IdentityType::IDENTITY_TYPE_PUBLIC);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.action.action,
kExpectedAction);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.credential_selector.account_name,
account_name);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.credential_selector.manager_app_id,
manager_app_id);
}
TEST(BroadcastRequestTest, CreateFromEmptyPresenceRequestFails) {
BroadcastRequest empty = {
.variant = PresenceBroadcast(),
};
EXPECT_THAT(BaseBroadcastRequest::Create(empty),
StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,269 +0,0 @@
// Copyright 2022 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 "presence/implementation/broadcast_manager.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/implementation/crypto.h"
#include "internal/platform/logging.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/implementation/advertisement_factory.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
namespace presence {
namespace {
using AdvertisingCallback = ::nearby::api::ble::BleMedium::AdvertisingCallback;
using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession;
using LocalCredential = internal::LocalCredential;
uint16_t SaltToInt(absl::string_view salt) {
if (salt.length() < 2) return 0;
uint16_t b0 = salt[0];
uint16_t b1 = salt[1];
return b0 << 8 | b1;
}
std::string SaltFromInt(uint16_t x) {
std::string salt;
salt.resize(2);
salt[0] = x >> 8 & 0xFF;
salt[1] = x & 0xFF;
return salt;
}
// Selects a salt that has not been used yet. The salt is added to
// `credential.consumed_salts`.
// We may fail to find an unused salt. In this unlikely event, an already
// consumed salt is returned.
std::string SelectSalt(LocalCredential& credential,
absl::string_view preferred_salt) {
// NP certificate guidelines say that we should try to get an unused salt 128
// times.
constexpr int kMaxSaltSelectRetries = 128;
uint16_t s = SaltToInt(preferred_salt);
for (int i = 0; i < kMaxSaltSelectRetries; i++) {
if (!credential.consumed_salts().contains(s)) {
break;
}
s = nearby::RandData<uint16_t>();
}
credential.mutable_consumed_salts()->insert({s, true});
return SaltFromInt(s);
}
} // namespace
absl::StatusOr<BroadcastSessionId> BroadcastManager::StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) {
absl::StatusOr<BaseBroadcastRequest> request =
BaseBroadcastRequest::Create(broadcast_request);
if (!request.ok()) {
LOG(WARNING) << "Invalid broadcast request, reason: " << request.status();
callback.start_broadcast_cb(request.status());
return request.status();
}
BroadcastSessionId id = GenerateBroadcastSessionId();
RunOnServiceControllerThread(
"start-broadcast",
[this, id, power_mode = broadcast_request.power_mode, request = *request,
broadcast_callback = std::move(
callback)]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable {
sessions_.insert({id, BroadcastSessionState(
std::move(broadcast_callback), power_mode)});
FetchCredentials(id, std::move(request));
});
return id;
}
void BroadcastManager::FetchCredentials(
BroadcastSessionId id, BaseBroadcastRequest broadcast_request) {
absl::StatusOr<CredentialSelector> credential_selector =
AdvertisementFactory::GetCredentialSelector(broadcast_request);
if (!credential_selector.ok()) {
// Public advertisement, we don't need credential to advertise.
Advertise(id, broadcast_request, /*credentials=*/{});
return;
}
credential_manager_->GetLocalCredentials(
*credential_selector,
GetLocalCredentialsResultCallback{
.credentials_fetched_cb =
[this, id, broadcast_request = std::move(broadcast_request),
selector = *credential_selector](
absl::StatusOr<
std::vector<::nearby::internal::LocalCredential>>
credentials) {
if (!credentials.ok()) {
LOG(WARNING) << "Failed to fetch credentials, status: "
<< credentials.status();
NotifyStartCallbackStatus(id, credentials.status());
return;
}
RunOnServiceControllerThread(
"advertise-non-public",
[this, id, broadcast_request = std::move(broadcast_request),
credentials = std::move(*credentials),
selector = std::move(selector)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) mutable {
absl::optional<LocalCredential> credential =
Advertise(id, broadcast_request, credentials);
if (credential) {
credential_manager_->UpdateLocalCredential(
selector, std::move(*credential),
{[](absl::Status status) {
if (!status.ok()) {
LOG(WARNING) << "Failed to update private "
"credential, status: "
<< status;
}
}});
}
});
}});
}
absl::optional<LocalCredential> BroadcastManager::SelectCredential( // NOLINT
BaseBroadcastRequest& broadcast_request,
std::vector<LocalCredential> credentials) {
if (credentials.empty()) {
return absl::optional<LocalCredential>(); // NOLINT
}
auto credential =
std::min_element(credentials.begin(), credentials.end(),
[](const LocalCredential& a, const LocalCredential& b) {
return a.start_time_millis() < b.start_time_millis();
});
if (credential == credentials.end()) {
LOG(WARNING) << "No active credentials";
return absl::optional<LocalCredential>(); // NOLINT
}
std::string salt = SelectSalt(*credential, broadcast_request.salt);
if (salt != broadcast_request.salt) {
VLOG(1) << "Changed salt";
broadcast_request.salt = salt;
}
return *credential;
}
absl::optional<LocalCredential> BroadcastManager::Advertise( // NOLINT
BroadcastSessionId id, BaseBroadcastRequest broadcast_request,
std::vector<LocalCredential> credentials) {
auto it = sessions_.find(id);
if (it == sessions_.end()) {
LOG(INFO) << "Broadcast session terminated, id: " << id;
return absl::optional<LocalCredential>(); // NOLINT
}
absl::optional<LocalCredential> credential = // NOLINT
SelectCredential(broadcast_request, std::move(credentials));
absl::StatusOr<AdvertisementData> advertisement =
AdvertisementFactory().CreateAdvertisement(broadcast_request, credential);
if (!advertisement.ok()) {
LOG(WARNING) << "Can't create advertisement, reason: "
<< advertisement.status();
NotifyStartCallbackStatus(id, advertisement.status());
return absl::optional<LocalCredential>(); // NOLINT
}
std::unique_ptr<AdvertisingSession> session =
mediums_->GetBle().StartAdvertising(
*advertisement, it->second.GetPowerMode(),
AdvertisingCallback{
.start_advertising_result = [this, id](absl::Status status) {
NotifyStartCallbackStatus(id, status);
}});
if (!session) {
NotifyStartCallbackStatus(id,
absl::InternalError("Can't start advertising"));
return absl::optional<LocalCredential>(); // NOLINT
}
it->second.SetAdvertisingSession(std::move(session));
return credential;
}
void BroadcastManager::NotifyStartCallbackStatus(BroadcastSessionId id,
absl::Status status) {
RunOnServiceControllerThread("started-broadcast-cb",
[this, id, status]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) {
auto it = sessions_.find(id);
if (it == sessions_.end()) {
return;
}
it->second.CallStartedCallback(status);
if (!status.ok()) {
// Delete failed session.
sessions_.erase(it);
}
});
}
void BroadcastManager::StopBroadcast(BroadcastSessionId id) {
RunOnServiceControllerThread(
"stop-broadcast", [this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) {
auto it = sessions_.find(id);
if (it == sessions_.end()) {
VLOG(1) << absl::StrFormat("BroadcastSession(0x%x) not found", id);
return;
}
it->second.StopAdvertising();
sessions_.erase(it);
});
}
BroadcastSessionId BroadcastManager::GenerateBroadcastSessionId() {
return nearby::RandData<BroadcastSessionId>();
}
void BroadcastManager::BroadcastSessionState::SetAdvertisingSession(
std::unique_ptr<AdvertisingSession> session) {
advertising_session_ = std::move(session);
}
void BroadcastManager::BroadcastSessionState::CallStartedCallback(
absl::Status status) {
BroadcastCallback callback = std::move(broadcast_callback_);
if (callback.start_broadcast_cb) {
callback.start_broadcast_cb(status);
}
}
void BroadcastManager::BroadcastSessionState::StopAdvertising() {
std::unique_ptr<AdvertisingSession> advertising_session =
std::move(advertising_session_);
if (advertising_session) {
absl::Status status = advertising_session->stop_advertising();
if (!status.ok()) {
LOG(WARNING) << "StopAdvertising error: " << status;
}
}
}
} // namespace presence
} // namespace nearby
-109
View File
@@ -1,109 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/credential_manager.h"
#include "presence/implementation/mediums/mediums.h"
#include "presence/power_mode.h"
namespace nearby {
namespace presence {
// The instance of BroadcastManager is owned by {@code ServiceControllerImpl}.
// Helping service controller to manage broadcast requests and callbacks.
class BroadcastManager {
public:
using SingleThreadExecutor = ::nearby::SingleThreadExecutor;
using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession;
using Runnable = ::nearby::Runnable;
using LocalCredential = internal::LocalCredential;
BroadcastManager(Mediums& mediums, CredentialManager& credential_manager,
SingleThreadExecutor& executor) {
mediums_ = &mediums, credential_manager_ = &credential_manager,
executor_ = &executor;
}
~BroadcastManager() = default;
absl::StatusOr<BroadcastSessionId> StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback);
void StopBroadcast(BroadcastSessionId);
private:
Mediums* mediums_;
CredentialManager* credential_manager_;
SingleThreadExecutor* executor_;
class BroadcastSessionState {
public:
explicit BroadcastSessionState(BroadcastCallback broadcast_callback,
PowerMode power_mode)
: broadcast_callback_(std::move(broadcast_callback)),
power_mode_(power_mode) {}
void SetAdvertisingSession(std::unique_ptr<AdvertisingSession> session);
void CallStartedCallback(absl::Status status);
void StopAdvertising();
PowerMode GetPowerMode() { return power_mode_; }
private:
BroadcastCallback broadcast_callback_;
PowerMode power_mode_;
std::unique_ptr<AdvertisingSession> advertising_session_;
};
BroadcastSessionId GenerateBroadcastSessionId();
void NotifyStartCallbackStatus(BroadcastSessionId id, absl::Status status);
void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) {
executor_->Execute(std::string(name), std::move(runnable));
}
void FetchCredentials(BroadcastSessionId id,
BaseBroadcastRequest broadcast_request)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
absl::optional<LocalCredential> SelectCredential( // NOLINT
BaseBroadcastRequest& broadcast_request,
std::vector<LocalCredential> credentials);
// Returns the private credential, if any, selected to generate the
// advertisement. A salt used in the advertisement is added to the returned
// private credential. The caller must save it in the storage.
absl::optional<LocalCredential> Advertise( // NOLINT
BroadcastSessionId id, BaseBroadcastRequest broadcast_request,
std::vector<LocalCredential> credentials)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
absl::flat_hash_map<BroadcastSessionId, BroadcastSessionState> sessions_
ABSL_GUARDED_BY(*executor_);
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_
@@ -1,177 +0,0 @@
// Copyright 2022 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 "presence/implementation/broadcast_manager.h"
#include <memory>
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
#include "internal/platform/medium_environment.h"
#include "internal/proto/credential.pb.h"
#include "presence/implementation/credential_manager_impl.h"
#include "presence/implementation/mediums/mediums.h"
namespace nearby {
namespace presence {
namespace {
using FeatureFlags = ::nearby::FeatureFlags::Flags;
using internal::IdentityType;
using ::nearby::CountDownLatch;
using ::nearby::MediumEnvironment;
using ::testing::status::StatusIs;
constexpr FeatureFlags kTestCases[] = {
FeatureFlags{},
};
constexpr absl::string_view kAccountName = "Test account";
constexpr int8_t kTxPower = 30;
BroadcastRequest CreateBroadcastRequest(IdentityType identity) {
PresenceBroadcast::BroadcastSection section = {
.identity = identity,
.extended_properties = {DataElement(
DataElement(ActionBit::kActiveUnlockAction))},
.account_name = std::string(kAccountName)};
PresenceBroadcast presence_request = {.sections = {section}};
BroadcastRequest request = {.tx_power = kTxPower,
.variant = presence_request};
return request;
}
class MediumEnvironmentStarter {
public:
MediumEnvironmentStarter() { MediumEnvironment::Instance().Start(); }
~MediumEnvironmentStarter() { MediumEnvironment::Instance().Stop(); }
};
class BroadcastManagerTest : public testing::TestWithParam<FeatureFlags> {
protected:
void TearDown() override {
MediumEnvironment::Instance().Sync();
// Finish pending tasks before destroying BroadcastManager
executor_.Shutdown();
}
bool IsAdvertising() {
WaitForServiceControllerTasks();
MediumEnvironment::Instance().Sync();
return MediumEnvironment::Instance()
.GetBleMediumStatus(*mediums_.GetBle().GetImpl())
->is_advertising;
}
BroadcastCallback CreateBroadcastCallback() {
return BroadcastCallback{.start_broadcast_cb = [this](absl::Status status) {
start_broadcast_status_.Set(status);
}};
}
void WaitForServiceControllerTasks() {
CountDownLatch latch(1);
executor_.Execute([&]() { latch.CountDown(); });
latch.Await();
}
// The medium environment must be initialized (started) before the service
// controller.
MediumEnvironmentStarter env_;
nearby::Future<absl::Status> start_broadcast_status_;
BroadcastCallback broadcast_callback_{
.start_broadcast_cb = [this](absl::Status status) {
start_broadcast_status_.Set(status);
}};
Mediums mediums_;
SingleThreadExecutor executor_;
CredentialManagerImpl credential_manager_{&executor_};
BroadcastManager broadcast_manager_{mediums_, credential_manager_, executor_};
};
INSTANTIATE_TEST_SUITE_P(ParametrisedBroadcastManagerTest, BroadcastManagerTest,
testing::ValuesIn(kTestCases));
TEST_P(BroadcastManagerTest, StartBroadcastPublicIdentity) {
absl::StatusOr<BroadcastSessionId> session =
broadcast_manager_.StartBroadcast(
CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC),
CreateBroadcastCallback());
EXPECT_OK(session);
EXPECT_TRUE(start_broadcast_status_.Get().ok());
EXPECT_OK(start_broadcast_status_.Get().GetResult());
EXPECT_TRUE(IsAdvertising());
}
TEST_P(BroadcastManagerTest, StartAndStopBroadcast) {
absl::StatusOr<BroadcastSessionId> session =
broadcast_manager_.StartBroadcast(
CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC),
CreateBroadcastCallback());
ASSERT_OK(session);
EXPECT_TRUE(IsAdvertising());
broadcast_manager_.StopBroadcast(*session);
EXPECT_FALSE(IsAdvertising());
}
TEST_P(BroadcastManagerTest, StopBroadcastTwiceNoSideEffects) {
absl::StatusOr<BroadcastSessionId> session =
broadcast_manager_.StartBroadcast(
CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC),
CreateBroadcastCallback());
ASSERT_OK(session);
EXPECT_TRUE(IsAdvertising());
broadcast_manager_.StopBroadcast(*session);
broadcast_manager_.StopBroadcast(*session);
}
TEST_P(BroadcastManagerTest, StopBroadcastInvalidSessionNoSideEffects) {
broadcast_manager_.StopBroadcast(123456);
}
TEST_P(BroadcastManagerTest, StartBroadcastInvalidRequestFails) {
absl::StatusOr<BroadcastSessionId> session =
broadcast_manager_.StartBroadcast(BroadcastRequest{},
CreateBroadcastCallback());
EXPECT_THAT(session, StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_TRUE(start_broadcast_status_.Get().ok());
EXPECT_THAT(start_broadcast_status_.Get().GetResult(),
StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_FALSE(IsAdvertising());
}
TEST_P(BroadcastManagerTest, StartBroadcastPrivateIdentityFails) {
// TODO(b/256249404): Support private identity.
absl::StatusOr<BroadcastSessionId> session =
broadcast_manager_.StartBroadcast(
CreateBroadcastRequest(internal::IDENTITY_TYPE_PRIVATE_GROUP),
CreateBroadcastCallback());
ASSERT_OK(session);
EXPECT_TRUE(start_broadcast_status_.Get().ok());
EXPECT_THAT(start_broadcast_status_.Get().GetResult(),
StatusIs(absl::StatusCode::kNotFound));
EXPECT_FALSE(IsAdvertising());
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,103 +0,0 @@
// Copyright 2023 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_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_
#include <optional>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
namespace nearby {
namespace presence {
class ConnectionAuthenticator {
public:
struct OneWayInitiatorData {
std::string shared_credential_hash;
};
struct TwoWayInitiatorData {
std::string shared_credential_hash;
std::string private_key_signature;
};
struct ResponderData {
std::string private_key_signature;
};
using InitiatorData = absl::variant<OneWayInitiatorData, TwoWayInitiatorData>;
virtual ~ConnectionAuthenticator() = default;
// Builds a signed message to be returned to Nearby Connections for
// authentication on the other side of the connection.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// local_credential - The local credential used to sign the derived
// information. If this is std::nullopt, then we will be
// performing one-way authentication.
// shared_credential - The shared credential used to decrypt the advertisement
// from the remote device.
virtual absl::StatusOr<InitiatorData> BuildSignedMessageAsInitiator(
absl::string_view ukey2_secret,
std::optional<const internal::LocalCredential> local_credential,
const internal::SharedCredential& shared_credential) const = 0;
// Builds a signed message to be returned to Nearby Connections for
// authentication on the other side of the connection.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// local_credential - The local credential used to sign the derived
// information so the initiator can verify against our
// shared credential.
virtual absl::StatusOr<ResponderData> BuildSignedMessageAsResponder(
absl::string_view ukey2_secret,
const internal::LocalCredential& local_credential) const = 0;
// Verifies a signed message received from the responder (broadcaster) of the
// Nearby Presence advertisement.
// authentication_data - the data required to verify the connection, received
// from the responder.
// ukey2_secret - the shared secret derived from the ukey2 handshake in NC.
// shared_credentials - the set of shared credentials that can be used to
// verify the responder data.
virtual absl::Status VerifyMessageAsInitiator(
ResponderData authentication_data, absl::string_view ukey2_secret,
const std::vector<internal::SharedCredential>& shared_credentials)
const = 0;
// Verifies a signed message received from the Nearby Connections peer.
// Returns the matched local credential if the verification was successful.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// received_frame - The received frame from Nearby Connections.
// local_credentials - The set of local credentials that may contain the
// required keyseed hash.
// shared_credentials - The set of shared credentials that can be used to
// verify the signed contents of the frame.
virtual absl::StatusOr<internal::LocalCredential> VerifyMessageAsResponder(
absl::string_view ukey2_secret, InitiatorData initiator_data,
const std::vector<internal::LocalCredential>& local_credentials,
const std::vector<internal::SharedCredential>& shared_credentials)
const = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_
@@ -1,197 +0,0 @@
// Copyright 2023 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 "presence/implementation/connection_authenticator_impl.h"
#include <optional>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
#include "internal/crypto/ed25519.h"
#include "internal/crypto_cros/hkdf.h"
#include "internal/crypto_cros/secure_util.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
namespace nearby {
namespace presence {
namespace {
constexpr int kPresenceAuthenticatorVersion = 1;
constexpr int kPresenceAuthenticatorHkdfKeySize = 32;
constexpr char kBroadcasterMessageHeader[] =
"Nearby Presence Broadcaster Signature";
constexpr char kDiscovererMessageHeader[] =
"Nearby Presence Discoverer Signature";
constexpr char kHkdfSalt[] = "Google Nearby";
constexpr char kBroadcasterHkdfInfo[] =
"Nearby Presence Broadcaster Credential Hash";
constexpr char kDiscovererHkdfInfo[] =
"Nearby Presence Discoverer Credential Hash";
} // namespace
absl::StatusOr<ConnectionAuthenticator::InitiatorData>
ConnectionAuthenticatorImpl::BuildSignedMessageAsInitiator(
absl::string_view ukey2_secret,
std::optional<const internal::LocalCredential> local_credential,
const internal::SharedCredential& shared_credential) const {
auto shared_credential_hash = crypto::HkdfSha256(
absl::StrCat(ukey2_secret, shared_credential.key_seed()), kHkdfSalt,
kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize);
if (local_credential.has_value()) {
// two-way authentication, private identity.
auto signer = crypto::Ed25519Signer::Create(
(*local_credential).connection_signing_key().key());
if (!signer.ok()) {
return signer.status();
}
auto pkey_signature =
signer->Sign(absl::StrCat(kDiscovererMessageHeader, ukey2_secret));
if (!pkey_signature.has_value()) {
return absl::InternalError("Signing using private key failed.");
}
return ConnectionAuthenticator::TwoWayInitiatorData{
.shared_credential_hash = shared_credential_hash,
.private_key_signature = *pkey_signature,
};
}
// one-way authentication, trusted identity.
return ConnectionAuthenticator::OneWayInitiatorData{
.shared_credential_hash = shared_credential_hash,
};
}
absl::StatusOr<ConnectionAuthenticator::ResponderData>
ConnectionAuthenticatorImpl::BuildSignedMessageAsResponder(
absl::string_view ukey2_secret,
const internal::LocalCredential& local_credential) const {
auto signer = crypto::Ed25519Signer::Create(
local_credential.connection_signing_key().key());
if (!signer.ok()) {
return signer.status();
}
auto pkey_signature =
signer->Sign(absl::StrCat(kBroadcasterMessageHeader, ukey2_secret));
if (!pkey_signature.has_value()) {
return absl::InternalError("Signing using private key failed.");
}
return ConnectionAuthenticator::ResponderData{.private_key_signature =
*pkey_signature};
}
absl::Status ConnectionAuthenticatorImpl::VerifyMessageAsInitiator(
ResponderData authentication_data, absl::string_view ukey2_secret,
const std::vector<internal::SharedCredential>& shared_credentials) const {
if (authentication_data.private_key_signature.empty()) {
return absl::InvalidArgumentError("Empty private key signature.");
}
for (const auto& shared_credential : shared_credentials) {
auto verifier = crypto::Ed25519Verifier::Create(
shared_credential.connection_signature_verification_key());
if (!verifier.ok()) {
continue;
}
// Verify ED25519 signature, returning true if verification succeeded.
if (verifier
->Verify(absl::StrCat(kBroadcasterMessageHeader, ukey2_secret),
authentication_data.private_key_signature)
.ok()) {
return absl::OkStatus();
}
}
return absl::InternalError("Unable to verify responder's private key sig.");
}
absl::StatusOr<internal::LocalCredential>
ConnectionAuthenticatorImpl::VerifyMessageAsResponder(
absl::string_view ukey2_secret, InitiatorData initiator_data,
const std::vector<internal::LocalCredential>& local_credentials,
const std::vector<internal::SharedCredential>& shared_credentials) const {
std::string shared_credential_hash;
std::optional<internal::LocalCredential> matched_local_credential;
if (absl::holds_alternative<OneWayInitiatorData>(initiator_data)) {
// one-way. we only need to verify if the hash matches one of our
// local credentials.
auto auth_data = absl::get<OneWayInitiatorData>(initiator_data);
if (auth_data.shared_credential_hash.size() !=
kPresenceAuthenticatorHkdfKeySize) {
return absl::InvalidArgumentError("Invalid shared credential hash size.");
}
for (const auto& local_credential : local_credentials) {
// Verify Credential ID hash.
auto cid_hash = crypto::HkdfSha256(
absl::StrCat(ukey2_secret, local_credential.key_seed()), kHkdfSalt,
kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize);
if (crypto::SecureMemEqual(cid_hash.c_str(),
auth_data.shared_credential_hash.c_str(),
kPresenceAuthenticatorHkdfKeySize)) {
matched_local_credential = local_credential;
}
}
} else {
// two-way. we need to verify if the hash matches one of our local
// credentials _and_ make sure it matches one of our shared credentials.
// We want to check each shared credential to verify using its public key.
// Match the local credential.
auto auth_data = absl::get<TwoWayInitiatorData>(initiator_data);
if (auth_data.shared_credential_hash.size() !=
kPresenceAuthenticatorHkdfKeySize) {
return absl::InvalidArgumentError("Invalid shared credential hash size.");
}
if (auth_data.private_key_signature.empty()) {
return absl::InvalidArgumentError("Empty private key signature.");
}
for (const auto& local_credential : local_credentials) {
// Verify Credential ID hash.
auto cid_hash = crypto::HkdfSha256(
absl::StrCat(ukey2_secret, local_credential.key_seed()), kHkdfSalt,
kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize);
if (crypto::SecureMemEqual(cid_hash.c_str(),
auth_data.shared_credential_hash.c_str(),
kPresenceAuthenticatorHkdfKeySize)) {
matched_local_credential = local_credential;
}
}
// Now, match our shared credential.
std::optional<internal::SharedCredential> matched_shared_credential;
for (const auto& shared_credential : shared_credentials) {
auto verifier = crypto::Ed25519Verifier::Create(
shared_credential.connection_signature_verification_key());
if (!verifier.ok() ||
verifier
->Verify(absl::StrCat(kDiscovererMessageHeader, ukey2_secret),
auth_data.private_key_signature)
.ok()) {
matched_shared_credential = shared_credential;
break;
}
}
if (!matched_shared_credential.has_value()) {
return absl::InternalError("Unable to verify shared credential.");
}
}
if (matched_local_credential.has_value()) {
return *matched_local_credential;
}
return absl::InternalError("Unable to verify local credential.");
}
} // namespace presence
} // namespace nearby
@@ -1,85 +0,0 @@
// Copyright 2023 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_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_
#include <optional>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
#include "presence/implementation/connection_authenticator.h"
namespace nearby {
namespace presence {
class ConnectionAuthenticatorImpl : public ConnectionAuthenticator {
public:
// Builds a signed message to be returned to Nearby Connections for
// authentication on the other side of the connection.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// local_credential - The local credential used to sign the derived
// information. If this is std::nullopt, then we will be
// performing one-way authentication.
// shared_credential - The shared credential used to decrypt the advertisement
// from the remote device.
absl::StatusOr<InitiatorData> BuildSignedMessageAsInitiator(
absl::string_view ukey2_secret,
std::optional<const internal::LocalCredential> local_credential,
const internal::SharedCredential& shared_credential) const override;
// Builds a signed message to be returned to Nearby Connections for
// authentication on the other side of the connection.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// local_credential - The local credential used to sign the derived
// information so the initiator can verify against our
// shared credential.
absl::StatusOr<ResponderData> BuildSignedMessageAsResponder(
absl::string_view ukey2_secret,
const internal::LocalCredential& local_credential) const override;
// Verifies a signed message received from the responder (broadcaster) of the
// Nearby Presence advertisement.
// authentication_data - the data required to verify the connection, received
// from the responder.
// ukey2_secret - the shared secret derived from the ukey2 handshake in NC.
// shared_credentials - the set of shared credentials that can be used to
// verify the responder data.
absl::Status VerifyMessageAsInitiator(
ResponderData authentication_data, absl::string_view ukey2_secret,
const std::vector<internal::SharedCredential>& shared_credentials)
const override;
// Verifies a signed message received from the Nearby Connections peer.
// ukey2_secret - The shared secret derived from the UKEY2 handshake in NC.
// received_frame - The received frame from Nearby Connections.
// local_credentials - The set of local credentials that may contain the
// required keyseed hash.
// shared_credentials - The set of shared credentials that can be used to
// verify the signed contents of the frame.
absl::StatusOr<internal::LocalCredential> VerifyMessageAsResponder(
absl::string_view ukey2_secret, InitiatorData initiator_data,
const std::vector<internal::LocalCredential>& local_credentials,
const std::vector<internal::SharedCredential>& shared_credentials)
const override;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_
@@ -1,272 +0,0 @@
// Copyright 2023 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 "presence/implementation/connection_authenticator_impl.h"
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/crypto/ed25519.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
namespace nearby {
namespace presence {
namespace {
using ::protobuf_matchers::EqualsProto;
using ::testing::status::StatusIs;
constexpr char kUkey2Secret[] = {0x34, 0x56, 0x78, 0x90};
constexpr char kKeySeed1[] = {1, 2, 3, 4, 5, 6, 7, 8};
constexpr char kKeySeed2[] = {8, 7, 6, 5, 4, 3, 2, 1};
internal::LocalCredential BuildLocalCredential(
const crypto::Ed25519KeyPair& key_pair, absl::string_view key_seed) {
internal::LocalCredential local_credential;
local_credential.mutable_connection_signing_key()->set_key(
absl::StrCat(key_pair.private_key, key_pair.public_key));
local_credential.set_key_seed(key_seed);
return local_credential;
}
internal::SharedCredential BuildSharedCredential(
const crypto::Ed25519KeyPair& key_pair, absl::string_view key_seed) {
internal::SharedCredential shared_credential;
shared_credential.set_connection_signature_verification_key(
key_pair.public_key);
shared_credential.set_key_seed(key_seed);
return shared_credential;
}
class PresenceAuthenticatorTest : public ::testing::Test {
protected:
void SetUp() override {
auto key_pair_or_status = crypto::Ed25519Signer::CreateNewKeyPair();
ASSERT_OK_AND_ASSIGN(auto key_pair1, key_pair_or_status);
auto key_pair2_or_status = crypto::Ed25519Signer::CreateNewKeyPair();
ASSERT_OK_AND_ASSIGN(auto key_pair2, key_pair2_or_status);
initiator_local_credential_ = BuildLocalCredential(key_pair1, kKeySeed1);
initiator_shared_credential_ = BuildSharedCredential(key_pair1, kKeySeed1);
initiator_shared_credential_wrong_key_ =
BuildSharedCredential(key_pair2, kKeySeed1);
responder_local_credential_ = BuildLocalCredential(key_pair2, kKeySeed2);
responder_shared_credential_ = BuildSharedCredential(key_pair2, kKeySeed2);
responder_shared_credential_wrong_key_ =
BuildSharedCredential(key_pair1, kKeySeed2);
}
internal::LocalCredential initiator_local_credential_;
internal::LocalCredential responder_local_credential_;
internal::SharedCredential initiator_shared_credential_;
internal::SharedCredential initiator_shared_credential_wrong_key_;
internal::SharedCredential responder_shared_credential_;
internal::SharedCredential responder_shared_credential_wrong_key_;
};
TEST_F(PresenceAuthenticatorTest, TestTwoWayInitiatorSignResponderVerify) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
auto local_credential = responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_});
ASSERT_TRUE(local_credential.ok());
EXPECT_THAT(*local_credential, EqualsProto(responder_local_credential_));
}
TEST_F(PresenceAuthenticatorTest, TestOneWayInitiatorSignResponderVerify) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(
ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, std::nullopt, responder_shared_credential_));
auto local_credential = responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_});
ASSERT_TRUE(local_credential.ok());
EXPECT_THAT(*local_credential, EqualsProto(responder_local_credential_));
}
TEST_F(PresenceAuthenticatorTest, TestResponderSignInitiatorVerify) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data,
responder_authenticator.BuildSignedMessageAsResponder(
kUkey2Secret, responder_local_credential_));
EXPECT_OK(initiator_authenticator.VerifyMessageAsInitiator(
auth_data, kUkey2Secret, {responder_shared_credential_}));
}
TEST_F(PresenceAuthenticatorTest,
TestTwoWayInitiatorSignResponderVerifyNoSharedCredentialMatchFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {}, {initiator_shared_credential_}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestOneWayInitiatorSignResponderVerifyNoMatchCredentialFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(
ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, std::nullopt, responder_shared_credential_));
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {}, {initiator_shared_credential_}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestOneWayInitiatorSignResponderVerifyNoCredentialFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(
ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, std::nullopt, responder_shared_credential_));
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {}, {}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestTwoWayInitiatorSignResponderVerifyNoMatchCredentialFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {}, {initiator_shared_credential_}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestTwoWayInitiatorSignResponderVerifyWrongKeyFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_wrong_key_}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestResponderSignInitiatorVerifyNoMatchCredentialFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data,
responder_authenticator.BuildSignedMessageAsResponder(
kUkey2Secret, responder_local_credential_));
EXPECT_THAT(initiator_authenticator.VerifyMessageAsInitiator(
auth_data, kUkey2Secret, {}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestResponderSignInitiatorVerifyWrongKeyFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data,
responder_authenticator.BuildSignedMessageAsResponder(
kUkey2Secret, responder_local_credential_));
EXPECT_THAT(
initiator_authenticator.VerifyMessageAsInitiator(
auth_data, kUkey2Secret, {responder_shared_credential_wrong_key_}),
StatusIs(absl::StatusCode::kInternal));
}
TEST_F(PresenceAuthenticatorTest,
TestTwoWayInitiatorSignResponderVerifyNoCidHashFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
std::get<ConnectionAuthenticator::TwoWayInitiatorData>(auth_data)
.shared_credential_hash.clear();
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_wrong_key_}),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_F(PresenceAuthenticatorTest,
TestTwoWayInitiatorSignResponderVerifyNoPkeySigFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, initiator_local_credential_,
responder_shared_credential_));
std::get<ConnectionAuthenticator::TwoWayInitiatorData>(auth_data)
.private_key_signature.clear();
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_wrong_key_}),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_F(PresenceAuthenticatorTest,
TestOneWayInitiatorSignResponderVerifyNoCidHashFails) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(
ConnectionAuthenticator::InitiatorData auth_data,
initiator_authenticator.BuildSignedMessageAsInitiator(
kUkey2Secret, std::nullopt, responder_shared_credential_));
std::get<ConnectionAuthenticator::OneWayInitiatorData>(auth_data)
.shared_credential_hash.clear();
EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder(
kUkey2Secret, auth_data, {responder_local_credential_},
{initiator_shared_credential_}),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_F(PresenceAuthenticatorTest,
TestResponderSignInitiatorVerifyNoPkeySigFail) {
ConnectionAuthenticatorImpl responder_authenticator;
ConnectionAuthenticatorImpl initiator_authenticator;
ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data,
responder_authenticator.BuildSignedMessageAsResponder(
kUkey2Secret, responder_local_credential_));
auth_data.private_key_signature.clear();
EXPECT_THAT(initiator_authenticator.VerifyMessageAsInitiator(
auth_data, kUkey2Secret, {responder_shared_credential_}),
StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace presence
} // namespace nearby
@@ -1,115 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_
#include <functional>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/metadata.pb.h"
namespace nearby {
namespace presence {
using SubscriberId = uint64_t;
/*
* The instance of CredentialManager is owned by {@code ServiceControllerImpl}.
* Helping service controller to manage local credentials and coordinate with
* downloaded remote credentials.
*/
class CredentialManager {
public:
CredentialManager() = default;
virtual ~CredentialManager() = default;
// Used to (re)generate users private and public credentials.
// The generated private credentials will be saved to creds storage.
// The generated public credentials will be returned inside the
// credentials_generated_cb for manager app to upload to web.
// The users own public credentials wont be saved on local credential
// storage.
virtual void GenerateCredentials(
const nearby::internal::DeviceIdentityMetaData& device_identity_metadata,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) = 0;
// Update remote public credentials.
virtual void UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) = 0;
virtual void UpdateLocalCredential(
const CredentialSelector& credential_selector,
nearby::internal::LocalCredential credential,
SaveCredentialsResultCallback result_callback) = 0;
// Used to fetch private creds when broadcasting.
virtual void GetLocalCredentials(
const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) = 0;
// Used to fetch local/remote public creds based on the value
// of public_credential_type.
virtual void GetPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) = 0;
// Subscribes for public credentials updates. The `callback` is triggered when
// the public credentials are fetched initially, and then every time the
// credentials change.
virtual SubscriberId SubscribeForPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) = 0;
// Unsubscribes from public credentials updates. No new callbacks will be
// triggered after this function returns. If there is a callback already
// running, that callback may continue after
// `UnsubscribeFromPublicCredentials()` return.
virtual void UnsubscribeFromPublicCredentials(SubscriberId id) = 0;
// Decrypts the device identity metadata from a public credential.
// Returns an empty string if decryption fails.
virtual std::string DecryptDeviceIdentityMetaData(
absl::string_view metadata_encryption_key, absl::string_view key_seed,
absl::string_view metadata_string) = 0;
// If `regen_credentials` is set to true, regenerating credentials.
virtual void SetDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData&
device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) = 0;
virtual ::nearby::internal::DeviceIdentityMetaData
GetDeviceIdentityMetaData() = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_
@@ -1,834 +0,0 @@
// Copyright 2022 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 "presence/implementation/credential_manager_impl.h"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include "internal/crypto_cros/aead.h"
#include "internal/crypto_cros/ec_private_key.h"
#include "internal/crypto_cros/hkdf.h"
#include "internal/platform/base64_utils.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/crypto.h"
#include "internal/platform/future.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/implementation/crypto.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/logging.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
#include "presence/data_types.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/ldt.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::Base64Utils;
using ::nearby::Crypto;
using ::nearby::Exception;
using ::nearby::ExceptionOr;
using ::nearby::Future;
using ::nearby::internal::IdentityType;
using ::nearby::internal::LocalCredential;
using ::nearby::internal::SharedCredential;
// Key to retrieve local device's Private/Public Key Credentials from key store.
constexpr char kPairedKeyAliasPrefix[] = "nearby_presence_paired_key_alias_";
// Use an empty string because Chromium only supports 1 account.
// Windows & Apple will have their own Identity Provider.
constexpr absl::string_view kEmptyAccountName = "";
// The expected number of valid local credentials to be stored on local device.
constexpr int kExpectedValidLocalCredtialSize = 6;
// The expiration time in days for a credential.
constexpr int kCredentialLifeCycleDays = 5;
// The minimum size of bytes to generate credential id.
constexpr int kExpectedByteSizeOfCredentialId = 8;
// Returns a random duration in [0, max_duration] range.
absl::Duration RandomDuration(absl::Duration max_duration) {
uint32_t random = nearby::RandData<uint32_t>();
return max_duration * random / std::numeric_limits<uint32_t>::max();
}
std::string CustomizeBytesSize(absl::string_view bytes, size_t len) {
return crypto::HkdfSha256(
/*ikm=*/std::string(bytes), // NOLINT
/*salt=*/std::string(CredentialManagerImpl::kAuthenticityKeyByteSize, 0),
/*info=*/"", /*derived_key_size=*/len);
}
} // namespace
// Returns a positive long value extracted from a byte array.
int64_t GenerateIdFromByteArray(const ByteArray& input) {
size_t inputLength = input.size();
ByteArray processed_bytes(kExpectedByteSizeOfCredentialId);
// Only use first 8 bytes if the input is longer than 8 bytes.
if (inputLength > kExpectedByteSizeOfCredentialId) {
processed_bytes.CopyAt(0, input);
} else {
// Extend the input with zeros if it's shorter than 8 bytes
processed_bytes.CopyAt(kExpectedByteSizeOfCredentialId - inputLength,
input);
}
int64_t id = 0;
for (int i = 0; i < kExpectedByteSizeOfCredentialId; ++i) {
id |= (static_cast<int64_t>(processed_bytes.data()[i]) << (8 * i));
}
if (id == std::numeric_limits<int64_t>::min())
return std::numeric_limits<int64_t>::max();
return std::abs(id);
}
void CredentialManagerImpl::GenerateCredentials(
const DeviceIdentityMetaData& device_identity_metadata,
absl::string_view manager_app_id,
const std::vector<IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) {
std::vector<SharedCredential> public_credentials;
std::vector<LocalCredential> private_credentials;
for (auto identity_type : identity_types) {
absl::Time start_time = SystemClock::ElapsedRealtime();
absl::Duration gap = credential_life_cycle_days * absl::Hours(24);
for (int index = 0; index < contiguous_copy_of_credentials; index++) {
auto public_private_credentials =
CreateLocalCredential(device_identity_metadata, identity_type,
start_time, start_time + gap);
if (public_private_credentials.second.identity_type() !=
IdentityType::IDENTITY_TYPE_UNSPECIFIED) {
private_credentials.push_back(public_private_credentials.first);
public_credentials.push_back(public_private_credentials.second);
}
start_time += gap;
}
}
// Create credential_storage object and invoke SaveCredentials.
credential_storage_ptr_->SaveCredentials(
manager_app_id, kEmptyAccountName, private_credentials,
public_credentials, PublicCredentialType::kLocalPublicCredential,
SaveCredentialsResultCallback{
.credentials_saved_cb =
[this, manager_app_id = std::string(manager_app_id),
account_name = kEmptyAccountName,
callback = std::move(credentials_generated_cb),
public_credentials](absl::Status status) mutable {
if (!status.ok()) {
LOG(WARNING) << "Save credentials failed with: " << status;
std::move(callback.credentials_generated_cb)(status);
return;
}
std::move(callback.credentials_generated_cb)(
std::move(public_credentials));
RunOnServiceControllerThread(
"local-creds-changed",
[this, manager_app_id = std::string(manager_app_id),
account_name = std::string(account_name)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
OnCredentialsChanged(
manager_app_id, account_name,
PublicCredentialType::kLocalPublicCredential);
});
}});
}
void CredentialManagerImpl::UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<SharedCredential>& remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) {
credential_storage_ptr_->SaveCredentials(
manager_app_id, account_name, /* private_credentials */ {},
remote_public_creds, PublicCredentialType::kRemotePublicCredential,
SaveCredentialsResultCallback{
.credentials_saved_cb =
[this, manager_app_id = std::string(manager_app_id),
account_name = std::string(account_name),
callback = std::move(credentials_updated_cb)](
absl::Status status) mutable {
if (!status.ok()) {
LOG(WARNING)
<< "Update remote credentials failed with: " << status;
} else {
RunOnServiceControllerThread(
"remote-creds-changed",
[this, manager_app_id = std::string(manager_app_id),
account_name = std::string(account_name)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
OnCredentialsChanged(
manager_app_id, account_name,
PublicCredentialType::kRemotePublicCredential);
});
}
std::move(callback.credentials_updated_cb)(status);
}});
}
std::pair<LocalCredential, SharedCredential>
CredentialManagerImpl::CreateLocalCredential(
const DeviceIdentityMetaData& device_identity_metadata,
IdentityType identity_type, absl::Time start_time, absl::Time end_time) {
LocalCredential private_credential;
private_credential.set_start_time_millis(absl::ToUnixMillis(start_time));
private_credential.set_end_time_millis(absl::ToUnixMillis(end_time));
private_credential.set_identity_type(identity_type);
// Creates an AES key to encrypt the whole broadcast.
std::string secret_key(kAuthenticityKeyByteSize, 0);
RandBytes(const_cast<std::string::value_type*>(secret_key.data()),
secret_key.size());
private_credential.set_key_seed(secret_key);
// Uses SHA-256 algorithm to generate the credential ID from the
// authenticity key
auto secret_id = Crypto::Sha256(secret_key);
// Does not expect to fail here since Crypto::Sha256 should not return
// empty ByteArray.
CHECK(!secret_id.Empty()) << "Crypto::Sha256 failed!";
private_credential.set_id(GenerateIdFromByteArray(secret_id));
std::string alias = Base64Utils::Encode(secret_id);
auto prefixedAlias = kPairedKeyAliasPrefix + alias;
// Generate key pair. Store the private key in private credential.
auto key_pair = crypto::ECPrivateKey::Create();
std::vector<uint8_t> private_key;
key_pair->ExportPrivateKey(&private_key);
private_credential.mutable_connection_signing_key()->set_key(
std::string(private_key.begin(), private_key.end()));
// Create an AES key to encrypt the device identity metadata.
std::string metadata_key(kBaseMetadataSize, 0);
RandBytes(const_cast<std::string::value_type*>(metadata_key.data()),
metadata_key.size());
private_credential.set_metadata_encryption_key_v0(metadata_key);
// Generate the public credential
std::vector<uint8_t> public_key;
key_pair->ExportPublicKey(&public_key);
return std::pair<LocalCredential, SharedCredential>(
private_credential,
CreatePublicCredential(private_credential, device_identity_metadata,
public_key));
}
SharedCredential CredentialManagerImpl::CreatePublicCredential(
const LocalCredential& private_credential,
const DeviceIdentityMetaData& device_identity_metadata,
const std::vector<uint8_t>& public_key) {
// The start time in the public credential should be decreased by a random
// value in 0 - 3 hours range.
// The end time should be increased by a random value in 0 - 3 hours range.
// This improves privacy by making it harder to correlate certificates.
absl::Time start_time =
absl::FromUnixMillis(private_credential.start_time_millis()) -
RandomDuration(absl::Hours(3));
absl::Time end_time =
absl::FromUnixMillis(private_credential.end_time_millis()) +
RandomDuration(absl::Hours(3));
SharedCredential public_credential;
public_credential.set_identity_type(private_credential.identity_type());
public_credential.set_id(private_credential.id());
public_credential.set_key_seed(private_credential.key_seed());
public_credential.set_start_time_millis(absl::ToUnixMillis(start_time));
public_credential.set_end_time_millis(absl::ToUnixMillis(end_time));
// Set up the public key. Note, we are setting the "connection" key but we are
// not setting the "advertisement" key because the latter is not used yet.
public_credential.set_connection_signature_verification_key(
std::string(public_key.begin(), public_key.end()));
auto metadata_encryption_key_tag =
Crypto::Sha256(private_credential.metadata_encryption_key_v0());
public_credential.set_metadata_encryption_key_tag_v0(
std::string(metadata_encryption_key_tag.AsStringView()));
auto encrypted_meta_data = EncryptDeviceIdentityMetaData(
private_credential.metadata_encryption_key_v0(),
private_credential.key_seed(),
device_identity_metadata.SerializeAsString());
if (encrypted_meta_data.empty()) {
LOG(ERROR) << "Fails to encrypt the device identity metadata.";
public_credential.set_identity_type(
IdentityType::IDENTITY_TYPE_UNSPECIFIED);
return public_credential;
}
public_credential.set_encrypted_metadata_bytes_v0(encrypted_meta_data);
return public_credential;
}
std::string CredentialManagerImpl::DecryptDeviceIdentityMetaData(
absl::string_view metadata_encryption_key, absl::string_view key_seed,
absl::string_view metadata_string) {
crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
std::vector<uint8_t> derived_key =
ExtendMetadataEncryptionKey(metadata_encryption_key);
aead.Init(derived_key);
auto iv = CustomizeBytesSize(key_seed, CredentialManagerImpl::kAesGcmIVSize);
std::vector<uint8_t> iv_bytes(iv.begin(), iv.end());
std::vector<uint8_t> encrypted_metadata_bytes(metadata_string.begin(),
metadata_string.end());
auto result = aead.Open(encrypted_metadata_bytes,
/*nonce=*/
iv_bytes,
/*additional_data=*/absl::Span<uint8_t>());
return std::string(result.value().begin(), result.value().end());
}
std::string CredentialManagerImpl::EncryptDeviceIdentityMetaData(
absl::string_view metadata_encryption_key, absl::string_view key_seed,
absl::string_view metadata_string) {
crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
std::vector<uint8_t> derived_key =
ExtendMetadataEncryptionKey(metadata_encryption_key);
aead.Init(derived_key);
auto iv = CustomizeBytesSize(key_seed, kAesGcmIVSize);
std::vector<uint8_t> iv_bytes(iv.begin(), iv.end());
std::vector<uint8_t> metadata_bytes(metadata_string.begin(),
metadata_string.end());
metadata_bytes.resize(metadata_string.size());
auto encrypted = aead.Seal(metadata_bytes,
/*nonce=*/
iv_bytes,
/*additional_data=*/absl::Span<uint8_t>());
return std::string(encrypted.begin(), encrypted.end());
}
std::vector<uint8_t> CredentialManagerImpl::ExtendMetadataEncryptionKey(
absl::string_view metadata_encryption_key) {
return crypto::HkdfSha256(
std::vector<uint8_t>(metadata_encryption_key.begin(),
metadata_encryption_key.end()),
/*salt=*/absl::Span<uint8_t>(),
/*info=*/absl::Span<uint8_t>(), kNearbyPresenceNumBytesAesGcmKeySize);
}
void CredentialManagerImpl::GetLocalCredentials(
const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
credential_storage_ptr_->GetLocalCredentials(
credential_selector,
GetLocalCredentialsResultCallback{
.credentials_fetched_cb =
[this, credential_selector, callback = std::move(callback)](
absl::StatusOr<std::vector<LocalCredential>>
get_local_credentials_result) mutable {
if (!get_local_credentials_result.ok()) {
callback.credentials_fetched_cb(
get_local_credentials_result.status());
return;
}
CheckCredentialsAndRefillIfNeeded(
credential_selector,
/* credentials_list_variant */
&get_local_credentials_result.value(),
/* callback_for_local_credentials */
std::move(callback),
/* callback_for_shared_credentials */
std::nullopt);
},
});
}
void CredentialManagerImpl::GetPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) {
// Not going to refill for remote SharedCredentials.
if (public_credential_type == PublicCredentialType::kRemotePublicCredential) {
credential_storage_ptr_->GetPublicCredentials(
credential_selector, public_credential_type, std::move(callback));
return;
}
credential_storage_ptr_->GetPublicCredentials(
credential_selector, public_credential_type,
GetPublicCredentialsResultCallback{
.credentials_fetched_cb =
[this, credential_selector, callback = std::move(callback)](
absl::StatusOr<std::vector<SharedCredential>>
get_shared_credentials_result) mutable {
if (!get_shared_credentials_result.ok()) {
callback.credentials_fetched_cb(
get_shared_credentials_result.status());
return;
}
CheckCredentialsAndRefillIfNeeded(
credential_selector,
/* credentials_list_variant */
&get_shared_credentials_result.value(),
/* callback_for_local_credentials */ std::nullopt,
/* callback_for_shared_credentials */
std::move(callback));
},
});
}
ExceptionOr<std::vector<LocalCredential>>
CredentialManagerImpl::GetLocalCredentialsSync(
const CredentialSelector& credential_selector, absl::Duration timeout) {
Future<std::vector<LocalCredential>> result;
GetLocalCredentials(credential_selector,
{.credentials_fetched_cb =
[result](absl::StatusOr<std::vector<LocalCredential>>
credentials) mutable {
if (!credentials.ok()) {
result.SetException({Exception::kFailed});
} else {
result.Set(std::move(*credentials));
}
}});
return result.Get(timeout);
}
ExceptionOr<std::vector<SharedCredential>>
CredentialManagerImpl::GetPublicCredentialsSync(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type, absl::Duration timeout) {
Future<std::vector<SharedCredential>> result;
GetPublicCredentials(
credential_selector, public_credential_type,
{.credentials_fetched_cb =
[result](absl::StatusOr<std::vector<SharedCredential>>
credentials) mutable {
if (!credentials.ok()) {
result.SetException({Exception::kFailed});
} else {
result.Set(std::move(*credentials));
}
}});
return result.Get(timeout);
}
// TODO(b/326063431): The intent of this method is likely for
// GetPublicCredentials() to be called after the AddSubscriber() calls, but
// it's unlikely that this is happening on a real device. Manually verify.
SubscriberId CredentialManagerImpl::SubscribeForPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) {
SubscriberId id = nearby::RandData<SubscriberId>();
RunOnServiceControllerThread(
"add-subscriber",
[this, key = SubscriberKey{credential_selector, public_credential_type},
id, callback = std::move(callback)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable {
AddSubscriber(key, id, std::move(callback));
});
GetPublicCredentials(credential_selector, public_credential_type,
CreateNotifySubscribersCallback(
{credential_selector, public_credential_type}));
return id;
}
void CredentialManagerImpl::UnsubscribeFromPublicCredentials(SubscriberId id) {
RunOnServiceControllerThread("remove-subscriber",
[this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(
*executor_) { RemoveSubscriber(id); });
}
void CredentialManagerImpl::AddSubscriber(
SubscriberKey key, SubscriberId id,
GetPublicCredentialsResultCallback callback) {
subscribers_[key].push_back(Subscriber(id, std::move(callback)));
}
void CredentialManagerImpl::RemoveSubscriber(SubscriberId id) {
for (auto& entry : subscribers_) {
auto it = std::find_if(
entry.second.begin(), entry.second.end(),
[&](Subscriber& subscriber) { return subscriber.GetId() == id; });
if (it != entry.second.end()) {
entry.second.erase(it);
if (subscribers_[entry.first].empty()) {
subscribers_.erase(entry.first);
}
return;
}
}
}
absl::flat_hash_set<IdentityType>
CredentialManagerImpl::GetSubscribedIdentities(
absl::string_view manager_app_id, absl::string_view account_name,
PublicCredentialType credential_type) const {
absl::flat_hash_set<IdentityType> identities;
for (auto& entry : subscribers_) {
const SubscriberKey& key = entry.first;
if (key.public_credential_type == credential_type &&
key.credential_selector.manager_app_id == manager_app_id &&
key.credential_selector.account_name == account_name) {
identities.insert(key.credential_selector.identity_type);
}
}
return identities;
}
void CredentialManagerImpl::OnCredentialsChanged(
absl::string_view manager_app_id, absl::string_view account_name,
PublicCredentialType credential_type) {
LOG(INFO) << "OnCredentialsChanged for app " << manager_app_id << ", account "
<< account_name;
for (IdentityType identity_type :
GetSubscribedIdentities(manager_app_id, account_name, credential_type)) {
CredentialSelector credential_selector = {
.manager_app_id = std::string(manager_app_id),
.account_name = std::string(account_name),
.identity_type = identity_type};
GetPublicCredentials(credential_selector, credential_type,
CreateNotifySubscribersCallback(
{credential_selector, credential_type}));
}
}
GetPublicCredentialsResultCallback
CredentialManagerImpl::CreateNotifySubscribersCallback(SubscriberKey key) {
return GetPublicCredentialsResultCallback{
.credentials_fetched_cb =
[this,
key](absl::StatusOr<std::vector<SharedCredential>> credentials) {
if (!credentials.ok()) {
LOG(WARNING) << "Failed to get public credentials: error code: "
<< credentials.status();
return;
}
RunOnServiceControllerThread(
"notify-subscribers",
[this, key, credentials = std::move(*credentials)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
NotifySubscribers(key, credentials);
});
}};
}
void CredentialManagerImpl::NotifySubscribers(
const SubscriberKey& key, std::vector<SharedCredential> credentials) {
// We are on `executor_` thread, so we can iterate over `subscribers_`
// without locking.
auto it = subscribers_.find(key);
if (it == subscribers_.end()) {
LOG(WARNING) << "No subscribers for (app: "
<< key.credential_selector.manager_app_id
<< ", account: " << key.credential_selector.account_name
<< ", identity type: "
<< static_cast<int>(key.credential_selector.identity_type)
<< ", credential type: "
<< static_cast<int>(key.public_credential_type) << ")";
return;
}
for (auto& subscriber : it->second) {
subscriber.NotifyCredentialsFetched(credentials);
}
}
void CredentialManagerImpl::Subscriber::NotifyCredentialsFetched(
std::vector<SharedCredential>& credentials) {
callback_.credentials_fetched_cb(credentials);
}
void CredentialManagerImpl::UpdateLocalCredential(
const CredentialSelector& credential_selector,
nearby::internal::LocalCredential credential,
SaveCredentialsResultCallback result_callback) {
credential_storage_ptr_->UpdateLocalCredential(
credential_selector.manager_app_id, credential_selector.account_name,
std::move(credential), std::move(result_callback));
}
void CredentialManagerImpl::CheckCredentialsAndRefillIfNeeded(
const CredentialSelector& credential_selector,
absl::variant<std::vector<nearby::internal::LocalCredential>*,
std::vector<nearby::internal::SharedCredential>*>
credential_list_variant,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials) {
bool invoked_for_local = false;
int valid_credentials_count = 0;
int64_t current_time_millis =
absl::ToUnixMillis(SystemClock::ElapsedRealtime());
int64_t last_valid_end_time_millis = current_time_millis;
std::vector<LocalCredential> valid_local_credentials;
std::vector<SharedCredential> valid_shared_credentials;
if (absl::holds_alternative<std::vector<nearby::internal::LocalCredential>*>(
credential_list_variant) &&
callback_for_local_credentials.has_value()) {
invoked_for_local = true;
for (auto& credential :
*absl::get<std::vector<nearby::internal::LocalCredential>*>(
credential_list_variant)) {
if (credential.end_time_millis() < current_time_millis) {
continue;
}
valid_credentials_count++;
if (last_valid_end_time_millis < credential.end_time_millis()) {
last_valid_end_time_millis = credential.end_time_millis();
}
valid_local_credentials.push_back(credential);
}
} else if (absl::holds_alternative<
std::vector<nearby::internal::SharedCredential>*>(
credential_list_variant) &&
callback_for_shared_credentials.has_value()) {
for (auto& credential :
*absl::get<std::vector<nearby::internal::SharedCredential>*>(
credential_list_variant)) {
if (credential.end_time_millis() < current_time_millis) {
continue;
}
valid_credentials_count++;
if (last_valid_end_time_millis < credential.end_time_millis()) {
last_valid_end_time_millis = credential.end_time_millis();
}
valid_shared_credentials.push_back(credential);
}
} else {
LOG(ERROR) << "Bad parameters for CheckCredentialsAndRefillIfNeeded";
return;
}
// Most invokes are expected to return early here as it already got enough
// valid credentials, no need to refill.
// Otherwise, the long process of refill (another read, merge, then save)
// would start.
if (valid_credentials_count >= kExpectedValidLocalCredtialSize) {
if (invoked_for_local) {
callback_for_local_credentials.value().credentials_fetched_cb(
valid_local_credentials);
} else {
callback_for_shared_credentials.value().credentials_fetched_cb(
valid_shared_credentials);
}
return;
}
// Already got the valid credential list for either local or shared.
// Now get the other credentials list from storage, prune them, and begin
// the process of appending new credentials onto them.
if (invoked_for_local) {
credential_storage_ptr_->GetPublicCredentials(
credential_selector, PublicCredentialType::kLocalPublicCredential,
GetPublicCredentialsResultCallback{
.credentials_fetched_cb =
[this, current_time_millis, last_valid_end_time_millis,
credential_selector,
valid_local_credentials = std::move(valid_local_credentials),
valid_shared_credentials = std::move(valid_shared_credentials),
callback_for_local_credentials =
std::move(callback_for_local_credentials),
callback_for_shared_credentials =
std::move(callback_for_shared_credentials)](
absl::StatusOr<
std::vector<nearby::internal::SharedCredential>>
result) mutable {
if (!result.ok()) {
callback_for_local_credentials.value()
.credentials_fetched_cb(result.status());
return;
}
for (const auto& credential : result.value()) {
if (credential.end_time_millis() >= current_time_millis) {
valid_shared_credentials.push_back(credential);
}
}
RefillRemainingValidCredentialsWithNewCredentials(
credential_selector, valid_local_credentials,
valid_shared_credentials,
/*start_time_to_generate_new_credentials_millis=*/
last_valid_end_time_millis,
std::move(callback_for_local_credentials),
std::move(callback_for_shared_credentials));
},
});
} else {
credential_storage_ptr_->GetLocalCredentials(
credential_selector,
GetLocalCredentialsResultCallback{
.credentials_fetched_cb =
[this, current_time_millis, last_valid_end_time_millis,
credential_selector,
valid_local_credentials = std::move(valid_local_credentials),
valid_shared_credentials = std::move(valid_shared_credentials),
callback_for_local_credentials =
std::move(callback_for_local_credentials),
callback_for_shared_credentials =
std::move(callback_for_shared_credentials)](
absl::StatusOr<
std::vector<nearby::internal::LocalCredential>>
result) mutable {
if (!result.ok()) {
callback_for_local_credentials.value()
.credentials_fetched_cb(result.status());
return;
}
for (const auto& credential : result.value()) {
if (credential.end_time_millis() >= current_time_millis) {
valid_local_credentials.push_back(
credential); // RESTORE TODO
}
}
RefillRemainingValidCredentialsWithNewCredentials(
credential_selector, valid_local_credentials,
valid_shared_credentials,
/*start_time_to_generate_new_credentials_millis=*/
last_valid_end_time_millis,
std::move(callback_for_local_credentials),
std::move(callback_for_shared_credentials));
},
});
}
}
void CredentialManagerImpl::RefillRemainingValidCredentialsWithNewCredentials(
const CredentialSelector& credential_selector,
std::vector<LocalCredential> valid_local_credentials,
std::vector<SharedCredential> valid_shared_credentials,
int64_t start_time_to_generate_new_credentials_millis,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials) {
// The number of valid credentials has already been determined by pruning
// valid_local_credentials and valid_shared_credentials. They must match
// in size.
int valid_credentials_count = valid_local_credentials.size();
CHECK_EQ(valid_credentials_count, valid_shared_credentials.size());
std::vector<LocalCredential> newly_generated_local_credentials;
std::vector<SharedCredential> newly_generated_shared_credentials;
// Generate more credential pairs to refill the expired ones.
auto start_time =
absl::FromUnixMillis(start_time_to_generate_new_credentials_millis);
auto gap = kCredentialLifeCycleDays * absl::Hours(24);
for (int i = 0; i < kExpectedValidLocalCredtialSize - valid_credentials_count;
i++) {
auto pair = CreateLocalCredential(device_identity_metadata_,
credential_selector.identity_type,
start_time, start_time + gap);
newly_generated_local_credentials.push_back(std::move(pair.first));
newly_generated_shared_credentials.push_back(std::move(pair.second));
start_time += gap;
}
// Now merge newly generated credentials to already existing valid ones.
valid_local_credentials.insert(valid_local_credentials.end(),
newly_generated_local_credentials.begin(),
newly_generated_local_credentials.end());
valid_shared_credentials.insert(valid_shared_credentials.end(),
newly_generated_shared_credentials.begin(),
newly_generated_shared_credentials.end());
// Save merged local and shared credential lists to storage
credential_storage_ptr_->SaveCredentials(
credential_selector.manager_app_id, credential_selector.account_name,
valid_local_credentials, valid_shared_credentials,
PublicCredentialType::kLocalPublicCredential,
SaveCredentialsResultCallback{
.credentials_saved_cb =
[this, valid_local_credentials, valid_shared_credentials,
callback_for_local_credentials =
std::move(callback_for_local_credentials),
callback_for_shared_credentials =
std::move(callback_for_shared_credentials)](
absl::Status status) mutable {
OnCredentialRefillComplete(
std::move(status), valid_local_credentials,
valid_shared_credentials,
std::move(callback_for_local_credentials),
std::move(callback_for_shared_credentials));
},
});
}
void CredentialManagerImpl::OnCredentialRefillComplete(
absl::Status save_credentials_status,
std::vector<LocalCredential> valid_local_credentials,
std::vector<SharedCredential> valid_shared_credentials,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials) {
if (!save_credentials_status.ok()) {
LOG(ERROR) << "Save credentials failed with: " << save_credentials_status;
if (callback_for_local_credentials.has_value()) {
callback_for_local_credentials.value().credentials_fetched_cb(
save_credentials_status);
} else {
callback_for_shared_credentials.value().credentials_fetched_cb(
save_credentials_status);
}
return;
}
if (callback_for_local_credentials.has_value()) {
callback_for_local_credentials.value().credentials_fetched_cb(
valid_local_credentials);
} else {
callback_for_shared_credentials.value().credentials_fetched_cb(
valid_shared_credentials);
}
}
bool CredentialManagerImpl::WaitForLatch(absl::string_view method_name,
CountDownLatch* latch) {
Exception await_exception = latch->Await();
if (!await_exception.Ok()) {
LOG(ERROR) << "Blocked in " << method_name
<< " with exeception code: " << await_exception.value;
return false;
}
return true;
}
} // namespace presence
} // namespace nearby
@@ -1,262 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_
#include <atomic>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/log/die_if_null.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/variant.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/credential_storage_impl.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/metadata.pb.h"
#include "presence/implementation/credential_manager.h"
namespace nearby {
namespace presence {
class CredentialManagerImpl : public CredentialManager {
public:
using IdentityType = ::nearby::internal::IdentityType;
using DeviceIdentityMetaData = ::nearby::internal::DeviceIdentityMetaData;
explicit CredentialManagerImpl(SingleThreadExecutor* executor)
: executor_(ABSL_DIE_IF_NULL(executor)) {
credential_storage_ptr_ = std::make_unique<nearby::CredentialStorageImpl>();
}
// Test purpose only.
CredentialManagerImpl(
SingleThreadExecutor* executor,
std::unique_ptr<nearby::CredentialStorageImpl> credential_storage_ptr)
: executor_(ABSL_DIE_IF_NULL(executor)),
credential_storage_ptr_(std::move(credential_storage_ptr)) {}
// AES only supports key sizes of 16, 24 or 32 bytes.
static constexpr int kAuthenticityKeyByteSize = 32;
// Length of key in bytes required by AES-GCM encryption.
static constexpr size_t kNearbyPresenceNumBytesAesGcmKeySize = 32;
// Modify this to 12 after use real AES.
static constexpr int kAesGcmIVSize = 12;
void GenerateCredentials(
const DeviceIdentityMetaData& device_identity_metadata,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) override;
void UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) override;
void UpdateLocalCredential(
const CredentialSelector& credential_selector,
nearby::internal::LocalCredential credential,
SaveCredentialsResultCallback result_callback) override;
void GetLocalCredentials(const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) override;
// Blocking version of `GetLocalCredentials`
nearby::ExceptionOr<std::vector<nearby::internal::LocalCredential>>
GetLocalCredentialsSync(const CredentialSelector& credential_selector,
absl::Duration timeout);
// Used to fetch local/remote public creds based on the value of
// public_credential_type.
void GetPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) override;
// Blocking version of `GetPublicCredentials`.
::nearby::ExceptionOr<std::vector<::nearby::internal::SharedCredential>>
GetPublicCredentialsSync(const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
absl::Duration timeout);
SubscriberId SubscribeForPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) override;
void UnsubscribeFromPublicCredentials(SubscriberId id) override;
std::string DecryptDeviceIdentityMetaData(
absl::string_view metadata_encryption_key, absl::string_view key_seed,
absl::string_view metadata_string) override;
std::pair<nearby::internal::LocalCredential,
nearby::internal::SharedCredential>
CreateLocalCredential(const DeviceIdentityMetaData& device_identity_metadata,
IdentityType identity_type, absl::Time start_time,
absl::Time end_time);
nearby::internal::SharedCredential CreatePublicCredential(
const nearby::internal::LocalCredential& private_credential,
const DeviceIdentityMetaData& device_identity_metadata,
const std::vector<uint8_t>& public_key);
virtual std::string EncryptDeviceIdentityMetaData(
absl::string_view metadata_encryption_key, absl::string_view key_seed,
absl::string_view metadata_string);
// Extend the key from 16 bytes to 32 bytes.
std::vector<uint8_t> ExtendMetadataEncryptionKey(
absl::string_view metadata_encryption_key);
void SetDeviceIdentityMetaData(
const DeviceIdentityMetaData& device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) override {
device_identity_metadata_ = device_identity_metadata;
if (regen_credentials) {
GenerateCredentials(device_identity_metadata, manager_app_id,
identity_types, credential_life_cycle_days,
contiguous_copy_of_credentials,
std::move(credentials_generated_cb));
}
}
::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData()
override {
return device_identity_metadata_;
}
private:
struct SubscriberKey {
CredentialSelector credential_selector;
PublicCredentialType public_credential_type;
template <typename H>
friend H AbslHashValue(H h, const SubscriberKey& key) {
return H::combine(std::move(h), key.credential_selector,
key.public_credential_type);
}
friend bool operator==(const SubscriberKey& a, const SubscriberKey& b) {
return a.public_credential_type == b.public_credential_type &&
a.credential_selector == b.credential_selector;
}
};
class Subscriber {
public:
Subscriber(SubscriberId id, GetPublicCredentialsResultCallback callback)
: callback_(std::move(callback)), id_(id) {}
SubscriberId GetId() const { return id_; }
// Notifies the subscriber about fetched credentials.
void NotifyCredentialsFetched(
std::vector<::nearby::internal::SharedCredential>& credentials);
private:
GetPublicCredentialsResultCallback callback_;
SubscriberId id_;
};
void RunOnServiceControllerThread(absl::string_view name,
Runnable&& runnable) {
executor_->Execute(std::string(name), std::move(runnable));
}
bool WaitForLatch(absl::string_view method_name, CountDownLatch* latch);
// The similar flow to check-expired-then-refill-if-needed is needed in both
// GetLocalCredentials() and GetPublicCredentials(). The high level flow is:
// check if there're expired creds from the result credentials list from
// GetLocal/GetPublic, if some creds expired, prune the expired, merge with
// newly generated ones. Then get the corresponding(local/shared) creds list
// from the storage, also prune expired, merge with newly
// generated. Then finally, save the newly merged two lists (local & shared)
// to storage. For re-use purpose, this private function is made to be able
// to take in different parameters from both GetLocalCredentials() and
// GetPublicCredentials().
void CheckCredentialsAndRefillIfNeeded(
const CredentialSelector& credential_selector,
absl::variant<std::vector<nearby::internal::LocalCredential>*,
std::vector<nearby::internal::SharedCredential>*>
credential_list_variant,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials);
void RefillRemainingValidCredentialsWithNewCredentials(
const CredentialSelector& credential_selector,
std::vector<nearby::internal::LocalCredential> valid_local_credentials,
std::vector<nearby::internal::SharedCredential> valid_shared_credentials,
int64_t start_time_to_generate_new_credentials_millis,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials);
void OnCredentialRefillComplete(
absl::Status save_credentials_status,
std::vector<nearby::internal::LocalCredential> valid_local_credentials,
std::vector<nearby::internal::SharedCredential> valid_shared_credentials,
std::optional<GetLocalCredentialsResultCallback>
callback_for_local_credentials,
std::optional<GetPublicCredentialsResultCallback>
callback_for_shared_credentials);
void OnCredentialsChanged(absl::string_view manager_app_id,
absl::string_view account_name,
PublicCredentialType credential_type)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void NotifySubscribers(
const SubscriberKey& key,
std::vector<::nearby::internal::SharedCredential> credentials)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void AddSubscriber(SubscriberKey key, SubscriberId id,
GetPublicCredentialsResultCallback callback)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void RemoveSubscriber(SubscriberId id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
absl::flat_hash_set<IdentityType> GetSubscribedIdentities(
absl::string_view manager_app_id, absl::string_view account_name,
PublicCredentialType credential_type) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
GetPublicCredentialsResultCallback CreateNotifySubscribersCallback(
SubscriberKey key);
absl::flat_hash_map<SubscriberKey, std::vector<Subscriber>> subscribers_
ABSL_GUARDED_BY(*executor_);
SingleThreadExecutor* executor_;
std::unique_ptr<nearby::CredentialStorageImpl> credential_storage_ptr_;
DeviceIdentityMetaData device_identity_metadata_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_
@@ -1,721 +0,0 @@
// Copyright 2022 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 "presence/implementation/credential_manager_impl.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "net/proto2/contrib/parse_proto/testing.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/credential_storage_impl.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/implementation/crypto.h"
#include "internal/platform/logging.h"
#include "internal/platform/medium_environment.h"
#include "internal/proto/credential.pb.h"
#include "presence/implementation/base_broadcast_request.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::CountDownLatch;
using ::nearby::Crypto;
using ::nearby::MediumEnvironment;
using ::nearby::internal::IdentityType;
using ::nearby::internal::LocalCredential;
using ::nearby::internal::DeviceIdentityMetaData;
using ::nearby::internal::SharedCredential;
using ::nearby::internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP;
using ::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
using ::protobuf_matchers::EqualsProto;
using ::testing::UnorderedPointwise;
using ::testing::status::StatusIs;
constexpr absl::string_view kManagerAppId = "TEST_MANAGER_APP";
constexpr absl::string_view kAccountName = "";
constexpr int kExpectedPresenceCredentialListSize = 6;
constexpr int kExpectedPresenceCredentialValidDays = 5;
DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() {
DeviceIdentityMetaData device_identity_metadata;
device_identity_metadata.set_device_type(
internal::DeviceType::DEVICE_TYPE_PHONE);
device_identity_metadata.set_device_name("NP test device");
device_identity_metadata.set_bluetooth_mac_address("FF:FF:FF:FF:FF:FF");
device_identity_metadata.set_device_id("\x12\xab\xcd");
return device_identity_metadata;
}
CredentialSelector BuildDefaultCredentialSelector() {
CredentialSelector credential_selector;
credential_selector.manager_app_id = std::string(kManagerAppId);
credential_selector.account_name = std::string(kAccountName);
credential_selector.identity_type = IDENTITY_TYPE_PRIVATE_GROUP;
return credential_selector;
}
class CredentialManagerImplTest : public ::testing::Test {
public:
class MockCredentialStorage : public nearby::CredentialStorageImpl {
public:
MOCK_METHOD(void, SaveCredentials,
(absl::string_view manager_app_id,
absl::string_view account_name,
const std::vector<LocalCredential>& private_credentials,
const std::vector<SharedCredential>& public_credentials,
PublicCredentialType public_credential_type,
SaveCredentialsResultCallback callback),
(override));
MOCK_METHOD(
void, GetPublicCredentials,
(const ::nearby::presence::CredentialSelector& credential_selector,
::nearby::presence::PublicCredentialType public_credential_type,
::nearby::presence::GetPublicCredentialsResultCallback callback),
(override));
};
class FakeCredentialStorage : public nearby::CredentialStorageImpl {
public:
// nearby::CredentialStorageImpl:
void SaveCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<LocalCredential>& private_credentials,
const std::vector<SharedCredential>& public_credentials,
PublicCredentialType public_credential_type,
SaveCredentialsResultCallback callback) override {
// Capture the credentials before actually saving them, so that they
// can be manipulated later on.
private_credentials_ = private_credentials;
public_credentials_ = public_credentials;
nearby::CredentialStorageImpl::SaveCredentials(
manager_app_id, account_name, private_credentials, public_credentials,
public_credential_type, std::move(callback));
}
void GetLocalCredentials(
const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) override {
if (private_credentials_.has_value()) {
callback.credentials_fetched_cb(private_credentials_.value());
} else {
nearby::CredentialStorageImpl::GetLocalCredentials(credential_selector,
std::move(callback));
}
}
void GetPublicCredentials(
const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) override {
if (public_credentials_.has_value()) {
callback.credentials_fetched_cb(public_credentials_.value());
} else {
nearby::CredentialStorageImpl::GetPublicCredentials(
credential_selector, public_credential_type, std::move(callback));
}
}
std::optional<std::vector<::nearby::internal::SharedCredential>>
public_credentials_;
std::optional<std::vector<::nearby::internal::LocalCredential>>
private_credentials_;
};
class MockCredentialManager : public CredentialManagerImpl {
public:
explicit MockCredentialManager(SingleThreadExecutor* executor)
: CredentialManagerImpl(executor) {}
MOCK_METHOD(std::string, EncryptDeviceIdentityMetaData,
(absl::string_view metadata_encryption_key,
absl::string_view key_seed, absl::string_view metadata_string),
(override));
};
~CredentialManagerImplTest() override { executor_.Shutdown(); }
// Waits for active tasks in the background thread to complete.
void Fence() {
// A runnable on medium environment thread can add a task on "our" executor,
// and vice-versa. We need to wait for tasks on both threads in a loop a few
// times to make sure that all tasks have finished.
for (int i = 0; i < 3; i++) {
MediumEnvironment::Instance().Sync();
CountDownLatch latch(1);
executor_.Execute([&]() { latch.CountDown(); });
latch.Await();
}
}
void AddLocalIdentity(absl::string_view manager_app_id,
absl::string_view account_name,
IdentityType identity_type) {
auto public_credentials = GenerateCredentialsSync(
CreateTestDeviceIdentityMetaData(), manager_app_id, {identity_type},
/*credential_life_cycle_days=*/kExpectedPresenceCredentialValidDays,
/*contiguous_copy_of_credentials=*/1);
EXPECT_OK(public_credentials);
}
absl::StatusOr<std::vector<SharedCredential>> GenerateCredentialsSync(
const DeviceIdentityMetaData& device_identity_metadata,
absl::string_view manager_app_id,
const std::vector<IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials) {
absl::StatusOr<std::vector<SharedCredential>> public_credentials;
CountDownLatch latch(1);
credential_manager_.GenerateCredentials(
device_identity_metadata, manager_app_id, identity_types,
credential_life_cycle_days, contiguous_copy_of_credentials,
{.credentials_generated_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials = credentials;
latch.CountDown();
}});
EXPECT_TRUE(latch.Await().Ok());
return public_credentials;
}
std::vector<LocalCredential> GetLocalCredentialsSync(
CredentialSelector credential_selector) {
auto private_credentials = credential_manager_.GetLocalCredentialsSync(
credential_selector, absl::Seconds(1));
EXPECT_TRUE(private_credentials.ok());
return private_credentials.GetResult();
}
std::vector<SharedCredential> GetPublicCredentialsSync(
CredentialSelector credential_selector,
PublicCredentialType public_credential_type) {
auto public_credentials = credential_manager_.GetPublicCredentialsSync(
credential_selector, public_credential_type, absl::Seconds(1));
EXPECT_TRUE(public_credentials.ok());
return public_credentials.GetResult();
}
protected:
SingleThreadExecutor executor_;
CredentialManagerImpl credential_manager_{&executor_};
MockCredentialManager mock_credential_manager_{&executor_};
};
TEST_F(CredentialManagerImplTest, CreateOneCredentialSuccessfully) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
constexpr absl::Time kStartTime = absl::FromUnixSeconds(100000);
constexpr absl::Time kEndTime = absl::FromUnixSeconds(200000);
auto credentials = credential_manager_.CreateLocalCredential(
device_identity_metadata, IDENTITY_TYPE_PRIVATE_GROUP, kStartTime,
kEndTime);
LocalCredential private_credential = credentials.first;
// Verify the private credential.
EXPECT_EQ(private_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_NE(private_credential.id(), 0);
EXPECT_EQ(private_credential.start_time_millis(),
absl::ToUnixMillis(kStartTime));
EXPECT_EQ(private_credential.end_time_millis(), absl::ToUnixMillis(kEndTime));
EXPECT_EQ(private_credential.key_seed().size(),
CredentialManagerImpl::kAuthenticityKeyByteSize);
EXPECT_FALSE(private_credential.connection_signing_key().key().empty());
EXPECT_EQ(private_credential.metadata_encryption_key_v0().size(),
kBaseMetadataSize);
SharedCredential public_credential = credentials.second;
// Verify the public credential.
EXPECT_EQ(public_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_NE(public_credential.id(), 0);
EXPECT_EQ(private_credential.id(), public_credential.id());
EXPECT_EQ(private_credential.key_seed(), public_credential.key_seed());
EXPECT_LE(public_credential.start_time_millis(),
absl::ToUnixMillis(kStartTime));
EXPECT_GE(public_credential.start_time_millis(),
absl::ToUnixMillis(kStartTime - absl::Hours(3)));
EXPECT_GE(public_credential.end_time_millis(), absl::ToUnixMillis(kEndTime));
EXPECT_LE(public_credential.end_time_millis(),
absl::ToUnixMillis(kEndTime + absl::Hours(3)));
EXPECT_EQ(Crypto::Sha256(private_credential.metadata_encryption_key_v0())
.AsStringView(),
public_credential.metadata_encryption_key_tag_v0());
EXPECT_FALSE(
public_credential.connection_signature_verification_key().empty());
EXPECT_FALSE(public_credential.encrypted_metadata_bytes_v0().empty());
auto decrypted_metadata = credential_manager_.DecryptDeviceIdentityMetaData(
private_credential.metadata_encryption_key_v0(),
public_credential.key_seed(),
public_credential.encrypted_metadata_bytes_v0());
EXPECT_EQ(device_identity_metadata.SerializeAsString(), decrypted_metadata);
}
TEST_F(CredentialManagerImplTest, GenerateCredentialsSuccessfully) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identityTypes{IDENTITY_TYPE_PRIVATE_GROUP};
absl::Time previous_start_time;
absl::Time previous_end_time;
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identityTypes,
kExpectedPresenceCredentialValidDays,
kExpectedPresenceCredentialListSize);
EXPECT_OK(public_credentials);
EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize);
for (int i = 0; i < kExpectedPresenceCredentialListSize; i++) {
SharedCredential& public_credential = public_credentials->at(i);
EXPECT_EQ(public_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP);
EXPECT_NE(public_credential.id(), 0);
absl::Time start_time_millis =
absl::FromUnixMillis(public_credential.start_time_millis());
absl::Time end_time_millis =
absl::FromUnixMillis(public_credential.end_time_millis());
if (i > 0) {
EXPECT_GT(start_time_millis, previous_start_time);
EXPECT_GE(previous_end_time, start_time_millis);
EXPECT_GT(end_time_millis, previous_end_time);
}
EXPECT_LT(start_time_millis +
absl::Hours(24) * kExpectedPresenceCredentialValidDays,
end_time_millis);
EXPECT_FALSE(public_credential.encrypted_metadata_bytes_v0().empty());
previous_start_time = start_time_millis;
previous_end_time = end_time_millis;
}
}
TEST_F(CredentialManagerImplTest,
SubscribeCallsCallbackWithExistingCredentials) {
absl::StatusOr<std::vector<SharedCredential>> public_credentials1;
absl::StatusOr<std::vector<SharedCredential>> public_credentials2;
AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP);
SubscriberId id1 = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = IDENTITY_TYPE_PRIVATE_GROUP},
PublicCredentialType::kLocalPublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials1 = std::move(credentials);
}});
SubscriberId id2 = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = IDENTITY_TYPE_PRIVATE_GROUP},
PublicCredentialType::kLocalPublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials2 = std::move(credentials);
}});
Fence();
EXPECT_OK(public_credentials1);
EXPECT_OK(public_credentials2);
EXPECT_EQ(public_credentials1->size(), kExpectedPresenceCredentialListSize);
EXPECT_EQ(public_credentials2->size(), kExpectedPresenceCredentialListSize);
// Cleanup
credential_manager_.UnsubscribeFromPublicCredentials(id1);
credential_manager_.UnsubscribeFromPublicCredentials(id2);
Fence();
}
TEST_F(CredentialManagerImplTest,
SubscribeCallsCallbackWithUpdatedCredentials) {
absl::StatusOr<std::vector<SharedCredential>> public_credentials;
SubscriberId id = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = IDENTITY_TYPE_PRIVATE_GROUP},
PublicCredentialType::kLocalPublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials = std::move(credentials);
}});
Fence();
EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kUnknown));
AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP);
Fence();
ASSERT_OK(public_credentials);
EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize);
// Cleanup
credential_manager_.UnsubscribeFromPublicCredentials(id);
Fence();
}
TEST_F(CredentialManagerImplTest, NoCallbacksAfterUnsubscribe) {
absl::StatusOr<std::vector<SharedCredential>> public_credentials;
SubscriberId id = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = IDENTITY_TYPE_PRIVATE_GROUP},
PublicCredentialType::kLocalPublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials = std::move(credentials);
}});
credential_manager_.UnsubscribeFromPublicCredentials(id);
AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP);
Fence();
EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kUnknown));
}
TEST_F(CredentialManagerImplTest,
GenerateCredentialsSuccessfullyButStoreFailed) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
auto credential_storage_ptr =
std::make_unique<CredentialManagerImplTest::MockCredentialStorage>();
EXPECT_CALL(*credential_storage_ptr, SaveCredentials)
.WillOnce(::testing::Invoke(
[](absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<LocalCredential>& private_credentials,
const std::vector<SharedCredential>& public_credentials,
PublicCredentialType public_credential_type,
SaveCredentialsResultCallback callback) {
callback.credentials_saved_cb(
absl::FailedPreconditionError("Expected failure"));
}));
credential_manager_ =
CredentialManagerImpl(&executor_, std::move(credential_storage_ptr));
std::vector<IdentityType> identityTypes{IDENTITY_TYPE_PRIVATE_GROUP};
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identityTypes,
kExpectedPresenceCredentialValidDays,
kExpectedPresenceCredentialListSize);
EXPECT_THAT(public_credentials,
StatusIs(absl::StatusCode::kFailedPrecondition));
}
TEST_F(CredentialManagerImplTest, UpdateRemotePublicCredentialsSuccessfully) {
SharedCredential public_credential_for_test;
public_credential_for_test.set_identity_type(
IdentityType::IDENTITY_TYPE_CONTACTS_GROUP);
std::vector<SharedCredential> public_credentials{
{public_credential_for_test}};
nearby::CountDownLatch updated_latch(1);
UpdateRemotePublicCredentialsCallback update_credentials_cb{
.credentials_updated_cb =
[&updated_latch](absl::Status status) {
if (status.ok()) {
updated_latch.CountDown();
}
},
};
credential_manager_.UpdateRemotePublicCredentials(
kManagerAppId, kAccountName, public_credentials,
std::move(update_credentials_cb));
EXPECT_TRUE(updated_latch.Await().Ok());
}
TEST_F(CredentialManagerImplTest,
UpdateRemotePublicCredentialsNotifiesSubscribers) {
absl::StatusOr<std::vector<SharedCredential>> subscribed_credentials;
SharedCredential public_credential_for_test;
public_credential_for_test.set_identity_type(
IdentityType::IDENTITY_TYPE_PRIVATE_GROUP);
std::vector<SharedCredential> public_credentials{
{public_credential_for_test}};
nearby::CountDownLatch updated_latch(1);
UpdateRemotePublicCredentialsCallback update_credentials_cb{
.credentials_updated_cb =
[&updated_latch](absl::Status status) {
if (status.ok()) {
updated_latch.CountDown();
}
},
};
SubscriberId id1 = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{
.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = internal::IDENTITY_TYPE_PRIVATE_GROUP},
PublicCredentialType::kRemotePublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
subscribed_credentials = std::move(credentials);
}});
SubscriberId id2 = credential_manager_.SubscribeForPublicCredentials(
CredentialSelector{
.manager_app_id = std::string(kManagerAppId),
.account_name = std::string(kAccountName),
.identity_type = internal::IDENTITY_TYPE_CONTACTS_GROUP},
PublicCredentialType::kRemotePublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
// This callback should not be called because there are no Trusted
// credentials in this test.
GTEST_FAIL();
}});
credential_manager_.UpdateRemotePublicCredentials(
kManagerAppId, kAccountName, public_credentials,
std::move(update_credentials_cb));
EXPECT_TRUE(updated_latch.Await().Ok());
Fence();
EXPECT_OK(subscribed_credentials);
EXPECT_EQ(subscribed_credentials->size(), 1);
credential_manager_.UnsubscribeFromPublicCredentials(id1);
credential_manager_.UnsubscribeFromPublicCredentials(id2);
}
TEST_F(CredentialManagerImplTest, GetLocalCredentialsFailed) {
absl::StatusOr<std::vector<LocalCredential>> private_credentials;
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
credential_manager_.GetLocalCredentials(
credential_selector,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<LocalCredential>> credentials) {
private_credentials = std::move(credentials);
}});
EXPECT_THAT(private_credentials, StatusIs(absl::StatusCode::kNotFound));
}
TEST_F(CredentialManagerImplTest, GetPublicCredentialsFailed) {
absl::StatusOr<std::vector<SharedCredential>> public_credentials;
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
CountDownLatch latch(1);
credential_manager_.GetPublicCredentials(
credential_selector, PublicCredentialType::kLocalPublicCredential,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials = std::move(credentials);
latch.CountDown();
}});
EXPECT_TRUE(latch.Await().Ok());
EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kNotFound));
}
TEST_F(CredentialManagerImplTest, GetCredentialsSuccessfully) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP};
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays,
kExpectedPresenceCredentialListSize);
EXPECT_OK(public_credentials);
EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize);
auto private_credentials = GetLocalCredentialsSync(credential_selector);
EXPECT_FALSE(private_credentials.empty());
}
TEST_F(CredentialManagerImplTest, PublicCredentialsFailEncryption) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
absl::StatusOr<std::vector<SharedCredential>> public_credentials;
auto credential_manager_ptr =
std::make_unique<CredentialManagerImplTest::MockCredentialManager>(
&executor_);
EXPECT_CALL(*credential_manager_ptr, EncryptDeviceIdentityMetaData)
.WillOnce(::testing::Invoke(
[](absl::string_view metadata_encryption_key,
absl::string_view key_seed,
absl::string_view metadata_string) { return ""; }));
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP};
CountDownLatch latch(1);
credential_manager_ptr->GenerateCredentials(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays, 1,
{.credentials_generated_cb =
[&](absl::StatusOr<std::vector<SharedCredential>> credentials) {
public_credentials = std::move(credentials);
latch.CountDown();
}});
EXPECT_TRUE(latch.Await().Ok());
EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_F(CredentialManagerImplTest, UpdateLocalCredential) {
constexpr int kSelectedCredentialId = 2;
constexpr uint16_t kSalt = 1000;
absl::Status update_status = absl::UnknownError("");
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP,
IDENTITY_TYPE_CONTACTS_GROUP};
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays,
kExpectedPresenceCredentialListSize);
auto private_credentials = GetLocalCredentialsSync(credential_selector);
EXPECT_EQ(kExpectedPresenceCredentialListSize, private_credentials.size());
ASSERT_OK(public_credentials);
// Modify a private credential
auto credential = private_credentials.at(kSelectedCredentialId);
EXPECT_TRUE(
private_credentials.at(kSelectedCredentialId).consumed_salts().empty());
credential.mutable_consumed_salts()->insert({kSalt, true});
credential_manager_.UpdateLocalCredential(
credential_selector, credential,
{[&](absl::Status status) { update_status = status; }});
EXPECT_OK(update_status);
// Verify that the modified credential has the new field in the new
// retrieved list of credentials.
auto modified_private_credentials =
GetLocalCredentialsSync(credential_selector);
EXPECT_TRUE(modified_private_credentials.at(kSelectedCredentialId)
.consumed_salts()
.at(kSalt));
}
TEST_F(CredentialManagerImplTest, EncryptAndDecryptDeviceIdentityMetaData) {
constexpr absl::string_view kMetadataEncryptionKeyBase16 =
"6331578C6E244074111B2ED0BBDB";
constexpr absl::string_view kSeed = "123456";
auto encrypted_meta_data = credential_manager_.EncryptDeviceIdentityMetaData(
kMetadataEncryptionKeyBase16, kSeed,
CreateTestDeviceIdentityMetaData().SerializeAsString());
auto decrypted_meta_data = credential_manager_.DecryptDeviceIdentityMetaData(
kMetadataEncryptionKeyBase16, kSeed, encrypted_meta_data);
DeviceIdentityMetaData device_identity_metadata;
ASSERT_TRUE(device_identity_metadata.ParseFromString(decrypted_meta_data));
EXPECT_EQ(device_identity_metadata.device_id(), "\x12\xab\xcd");
EXPECT_EQ(device_identity_metadata.device_type(),
internal::DeviceType::DEVICE_TYPE_PHONE);
EXPECT_EQ(device_identity_metadata.device_name(), "NP test device");
EXPECT_EQ(device_identity_metadata.bluetooth_mac_address(),
"FF:FF:FF:FF:FF:FF");
}
TEST_F(CredentialManagerImplTest, RefillCredentialsInGetLocalCredentials) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP};
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays, 1);
EXPECT_OK(public_credentials);
EXPECT_EQ(1, public_credentials->size());
// only generate 1 creds, expecting GetLocal would trigger refill to
// kExpectedPresenceCredentialListSize.
auto private_credentials = GetLocalCredentialsSync(credential_selector);
EXPECT_EQ(kExpectedPresenceCredentialListSize, private_credentials.size());
}
TEST_F(CredentialManagerImplTest, RefillCredentialsInGetSharedCredentials) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP};
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays, 1);
EXPECT_OK(public_credentials);
EXPECT_EQ(1, public_credentials->size());
// Only generated 1 creds, expecting GetPublicCredentials for
// kLocalPublicCredential type would trigger refill to
// kExpectedPresenceCredentialListSize.
auto refilled_public_credentials = GetPublicCredentialsSync(
credential_selector, PublicCredentialType::kLocalPublicCredential);
EXPECT_EQ(kExpectedPresenceCredentialListSize,
refilled_public_credentials.size());
}
TEST_F(CredentialManagerImplTest, RefillExpiredCredsInGetLocal) {
auto device_identity_metadata = CreateTestDeviceIdentityMetaData();
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE_GROUP};
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
auto credential_storage =
std::make_unique<CredentialManagerImplTest::FakeCredentialStorage>();
auto* credential_storage_ptr = credential_storage.get();
credential_manager_ =
CredentialManagerImpl(&executor_, std::move(credential_storage));
auto public_credentials = GenerateCredentialsSync(
device_identity_metadata, kManagerAppId, identity_types,
kExpectedPresenceCredentialValidDays,
kExpectedPresenceCredentialListSize);
ASSERT_OK(public_credentials);
EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize);
// Now that we have generated kExpectedPresenceCredentialListSize valid creds,
// tweak the first credential's end time, in both credential lists, to
// make them expired.
auto expiry_time = absl::ToUnixMillis(absl::Now() - absl::Hours(1));
credential_storage_ptr->private_credentials_.value()
.at(0)
.set_end_time_millis(expiry_time);
credential_storage_ptr->public_credentials_.value().at(0).set_end_time_millis(
expiry_time);
auto old_private_credentials =
credential_storage_ptr->private_credentials_.value();
auto refilled_private_credentials =
GetLocalCredentialsSync(credential_selector);
EXPECT_EQ(kExpectedPresenceCredentialListSize,
refilled_private_credentials.size());
// Verifying the expired one private_credentials->at(0) is pruned in the new
// list.
EXPECT_EQ(old_private_credentials.at(1).secret_id(),
refilled_private_credentials.at(0).secret_id());
// Verifying the new generated cred's start time is the same as previously
// existing list's last cred's end time.
EXPECT_EQ(
old_private_credentials.at(5).end_time_millis(),
refilled_private_credentials.at(kExpectedPresenceCredentialListSize - 1)
.start_time_millis());
}
} // namespace
} // namespace presence
} // namespace nearby
-109
View File
@@ -1,109 +0,0 @@
// Copyright 2022 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 "presence/implementation/ldt.h"
#include <algorithm>
#include <cstdint>
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#ifdef NEARBY_CHROMIUM
#include "third_party/nearby/src/presence/implementation/np_ldt.h"
#else
#include "np_ldt.h"
#endif
namespace nearby {
namespace presence {
namespace {
// NP LDT library says that 0 is returned when `NpLdtCreate()` fails.
constexpr uint64_t kInvalidLdtHandle = 0;
template <class T>
T FromStringView(absl::string_view data) {
T result{
.bytes = {0},
};
memcpy(result.bytes, data.data(),
std::min(sizeof(result.bytes), data.size()));
return result;
}
} // namespace
LdtEncryptor::LdtEncryptor(LdtEncryptor&& other)
: ldt_encrypt_handle_(other.ldt_encrypt_handle_),
ldt_decrypt_handle_(other.ldt_decrypt_handle_) {
other.ldt_encrypt_handle_.handle = kInvalidLdtHandle;
other.ldt_decrypt_handle_.handle = kInvalidLdtHandle;
}
LdtEncryptor::~LdtEncryptor() {
if (ldt_encrypt_handle_.handle != kInvalidLdtHandle) {
NpLdtEncryptClose(ldt_encrypt_handle_);
}
if (ldt_decrypt_handle_.handle != kInvalidLdtHandle) {
NpLdtDecryptClose(ldt_decrypt_handle_);
}
}
absl::StatusOr<LdtEncryptor> LdtEncryptor::Create(
absl::string_view key_seed, absl::string_view known_hmac) {
NpLdtEncryptHandle encrypt_handle =
NpLdtEncryptCreate(FromStringView<NpLdtKeySeed>(key_seed));
NpLdtDecryptHandle decrypt_handle =
NpLdtDecryptCreate(FromStringView<NpLdtKeySeed>(key_seed),
FromStringView<NpMetadataKeyHmac>(known_hmac));
if (encrypt_handle.handle == kInvalidLdtHandle) {
return absl::UnavailableError("Failed to create LDT encryptor");
}
if (decrypt_handle.handle == kInvalidLdtHandle) {
return absl::UnavailableError("Failed to create LDT decrypter");
}
return LdtEncryptor(encrypt_handle, decrypt_handle);
}
absl::StatusOr<std::string> LdtEncryptor::Encrypt(absl::string_view data,
absl::string_view salt) {
std::string encrypted = std::string(data);
NP_LDT_RESULT result = NpLdtEncrypt(
ldt_encrypt_handle_, reinterpret_cast<uint8_t*>(encrypted.data()),
encrypted.size(), FromStringView<NpLdtSalt>(salt));
if (result == NP_LDT_SUCCESS) {
return encrypted;
}
return absl::InternalError(
absl::StrFormat("LDT encryption failed, errorcode %d", result));
}
absl::StatusOr<std::string> LdtEncryptor::DecryptAndVerify(
absl::string_view data, absl::string_view salt) {
std::string encrypted = std::string(data);
NP_LDT_RESULT result = NpLdtDecryptAndVerify(
ldt_decrypt_handle_, reinterpret_cast<uint8_t*>(encrypted.data()),
encrypted.size(), FromStringView<NpLdtSalt>(salt));
if (result == NP_LDT_SUCCESS) {
return encrypted;
}
return absl::InternalError(
absl::StrFormat("LDT encryption failed, errorcode %d", result));
}
} // namespace presence
} // namespace nearby
-76
View File
@@ -1,76 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_LDT_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_LDT_H_
#include <string>
#include <utility>
#ifdef NEARBY_CHROMIUM
#include "third_party/nearby/src/presence/implementation/np_ldt.h"
#else
#include "np_ldt.h"
#endif
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
namespace nearby {
namespace presence {
// C++ abstraction on top of LDT C API.
class LdtEncryptor {
public:
LdtEncryptor(const LdtEncryptor&) = delete;
LdtEncryptor(LdtEncryptor&& other);
LdtEncryptor& operator=(const LdtEncryptor&) = delete;
LdtEncryptor& operator=(LdtEncryptor&& other) {
std::swap(ldt_encrypt_handle_, other.ldt_encrypt_handle_);
std::swap(ldt_decrypt_handle_, other.ldt_decrypt_handle_);
return *this;
}
~LdtEncryptor();
// Creates an instance of `LdtEncryptor`.
// `key_seed` is used to generate LDT encryption and decryption keys.
// `known_hmac` is used during decryption to verify if the message was
// encrypted with the expected key.
static absl::StatusOr<LdtEncryptor> Create(absl::string_view key_seed,
absl::string_view known_hmac);
// Encrypts `data`, which must be 16 - 31 bytes long.
absl::StatusOr<std::string> Encrypt(absl::string_view data,
absl::string_view salt);
// Decrypts `data` and verifies if it was encrypted with a key generated from
// `key_seed`.
absl::StatusOr<std::string> DecryptAndVerify(absl::string_view data,
absl::string_view salt);
private:
explicit LdtEncryptor(NpLdtEncryptHandle ldt_encrypt_handle,
NpLdtDecryptHandle ldt_decrypt_handle)
: ldt_encrypt_handle_(ldt_encrypt_handle),
ldt_decrypt_handle_(ldt_decrypt_handle) {}
// An opaque handle to the underlying LDT implementation. It can be null iff
// this object has already been destroyed.
NpLdtEncryptHandle ldt_encrypt_handle_;
NpLdtDecryptHandle ldt_decrypt_handle_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_LDT_H_
-47
View File
@@ -1,47 +0,0 @@
// 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 "presence/implementation/np_ldt.h"
// Placeholder, empty implementations of LDT utilities. They will be replaced
// with implementations in Rust.
NpLdtEncryptHandle NpLdtEncryptCreate(NpLdtKeySeed key_seed) {
NpLdtEncryptHandle handle = {0};
return handle;
}
NpLdtDecryptHandle NpLdtDecryptCreate(NpLdtKeySeed key_seed,
NpMetadataKeyHmac hmac_tag) {
NpLdtDecryptHandle handle = {0};
return handle;
}
NP_LDT_RESULT NpLdtEncryptClose(NpLdtEncryptHandle handle) {
return NP_LDT_SUCCESS;
}
NP_LDT_RESULT NpLdtDecryptClose(NpLdtDecryptHandle handle) {
return NP_LDT_SUCCESS;
}
NP_LDT_RESULT NpLdtEncrypt(NpLdtEncryptHandle handle, uint8_t* buffer,
size_t buffer_len, NpLdtSalt salt) {
return NP_LDT_SUCCESS;
}
NP_LDT_RESULT NpLdtDecryptAndVerify(NpLdtDecryptHandle handle, uint8_t* buffer,
size_t buffer_len, NpLdtSalt salt) {
return NP_LDT_SUCCESS;
}
-98
View File
@@ -1,98 +0,0 @@
// Copyright 2022 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 "presence/implementation/ldt.h"
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
namespace nearby {
namespace presence {
namespace {
using ::nearby::ByteArray;
// Test data from Android tests.
constexpr absl::string_view kKeySeedBase16 =
"CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39";
constexpr absl::string_view kKnownMacBase16 =
"B4C59FA599241B81758D976B5A621C05232FE1BF89AE5987CA254C3554DCE50E";
constexpr absl::string_view kPlainTextBase16 =
"CD683FE1A1D1F846543D0A13D4AEA40040C8D67B";
constexpr absl::string_view kCipherTextBase16 =
"61E481C12F4DE24F2D4AB22D8908F80D3A3F9B40";
constexpr absl::string_view kSaltBase16 = "0C0F";
TEST(Ldt, EncryptAndDecrypt) {
// Test data copied from NP LDT tests
ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72,
184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152,
200, 193, 94, 107, 28, 194, 182, 32, 205, 57});
ByteArray known_mac({0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81,
0x75, 0x8D, 0x97, 0x6B, 0x5A, 0x62, 0x1C, 0x05,
0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE, 0x59, 0x87,
0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E});
ByteArray test_data({205, 104, 63, 225, 161, 209, 248, 70, 84, 61,
10, 19, 212, 174, 164, 0, 64, 200, 214, 123});
ByteArray salt({12, 15});
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(seed.AsStringView(), known_mac.AsStringView());
ASSERT_OK(encryptor);
absl::StatusOr<std::string> encrypted =
encryptor->Encrypt(test_data.AsStringView(), salt.AsStringView());
ASSERT_OK(encrypted);
absl::StatusOr<std::string> decrypted =
encryptor->DecryptAndVerify(*encrypted, salt.AsStringView());
ASSERT_OK(decrypted);
EXPECT_EQ(*decrypted, test_data.AsStringView());
}
TEST(Ldt, EncryptAndroidData) {
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(absl::HexStringToBytes(kKeySeedBase16),
absl::HexStringToBytes(kKnownMacBase16));
ASSERT_OK(encryptor);
absl::StatusOr<std::string> encrypted =
encryptor->Encrypt(absl::HexStringToBytes(kPlainTextBase16),
absl::HexStringToBytes(kSaltBase16));
ASSERT_OK(encrypted);
EXPECT_EQ(*encrypted, absl::HexStringToBytes(kCipherTextBase16));
}
TEST(Ldt, DecryptAndroidData) {
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(absl::HexStringToBytes(kKeySeedBase16),
absl::HexStringToBytes(kKnownMacBase16));
ASSERT_OK(encryptor);
absl::StatusOr<std::string> decrypted =
encryptor->DecryptAndVerify(absl::HexStringToBytes(kCipherTextBase16),
absl::HexStringToBytes(kSaltBase16));
ASSERT_OK(decrypted);
EXPECT_EQ(*decrypted, absl::HexStringToBytes(kPlainTextBase16));
}
} // namespace
} // namespace presence
} // namespace nearby
-70
View File
@@ -1,70 +0,0 @@
# Copyright 2020 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.
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
licenses(["notice"])
cc_library(
name = "mediums",
srcs = [
],
hdrs = [
"advertisement_data.h",
"ble.h",
"mediums.h",
],
visibility = [
"//presence:__subpackages__",
],
deps = [
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//presence:types",
],
)
cc_test(
name = "mediums_test",
size = "small",
srcs = [
"ble_test.cc",
],
shard_count = 16,
deps = [
":mediums",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:variant",
"@com_google_googletest//:gtest_main",
] + select({
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
"//conditions:default": [
"//internal/platform/implementation/g3",
],
}),
)
@@ -1,34 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_
#include <string>
namespace nearby {
namespace presence {
// Nearby Presence advertisement data over the air.
struct AdvertisementData {
// If true, the advertisement needs to be broadcasted over BLE 5.0.
bool is_extended_advertisement;
// The advertised data.
std::string content;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_
-111
View File
@@ -1,111 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_
#include <memory>
#include <utility>
#include "internal/platform/ble.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/uuid.h"
#include "presence/implementation/mediums/advertisement_data.h"
#include "presence/power_mode.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
/** Presence advertisement service data uuid. */
ABSL_CONST_INIT const nearby::Uuid kPresenceServiceUuid(0x0000fcf100001000,
0x800000805f9b34fb);
/*
* This Ble class utilizes platform/ble BleMedium, provides ble functions
* for presence logic layer to invoke.
* This class would have states like if ble is available or not, if it's doing
* broadcast/scan.
*/
class Ble {
public:
using TxPowerLevel = ::nearby::api::ble::TxPowerLevel;
using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession;
using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback;
using AdvertiseParameters = ::nearby::api::ble::AdvertiseParameters;
using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession;
using AdvertisingCallback =
::nearby::api::ble::BleMedium::AdvertisingCallback;
using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData;
using BleMedium = ::nearby::api::ble::BleMedium;
explicit Ble(nearby::BluetoothAdapter& bluetooth_adapter)
: medium_(bluetooth_adapter) {}
bool IsAvailable() const { return medium_.IsValid(); }
// Starts broadcasting NP advertisement in `payload`. The caller should use
// the returned `AdvertisingSession` to stop the broadcast.
std::unique_ptr<AdvertisingSession> StartAdvertising(
const AdvertisementData& payload, PowerMode power_mode,
AdvertisingCallback callback) {
BleAdvertisementData advertising_data = {
.is_extended_advertisement = payload.is_extended_advertisement};
advertising_data.service_data.insert(
{kPresenceServiceUuid, nearby::ByteArray(payload.content)});
AdvertiseParameters advertise_set_parameters = {
.tx_power_level = ConvertPowerModeToPowerLevel(power_mode),
.is_connectable = true,
};
return medium_.StartAdvertising(advertising_data, advertise_set_parameters,
std::move(callback));
}
// Starts scanning for NP advertisements. The caller should use the returned
// `ScanningSession` to stop scanning.
std::unique_ptr<ScanningSession> StartScanning(ScanRequest scan_request,
ScanningCallback callback) {
return medium_.StartScanning(
kPresenceServiceUuid,
ConvertPowerModeToPowerLevel(scan_request.power_mode),
std::move(callback));
}
// Provides access to platform implementation. It's used in tests.
BleMedium* GetImpl() const { return medium_.GetImpl(); }
private:
TxPowerLevel ConvertPowerModeToPowerLevel(PowerMode power_mode) {
switch (power_mode) {
case PowerMode::kNoPower:
return TxPowerLevel::kUnknown;
case PowerMode::kLowPower:
return TxPowerLevel::kLow;
case PowerMode::kBalanced:
return TxPowerLevel::kMedium;
case PowerMode::kLowLatency:
return TxPowerLevel::kHigh;
}
return TxPowerLevel::kUnknown;
}
nearby::BleMedium medium_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_
-177
View File
@@ -1,177 +0,0 @@
// Copyright 2022 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 "presence/implementation/mediums/ble.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/time/time.h"
#include "absl/types/variant.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/uuid.h"
#include "presence/data_element.h"
#include "presence/implementation/mediums/advertisement_data.h"
#include "presence/power_mode.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
namespace {
using FeatureFlags = ::nearby::FeatureFlags::Flags;
using BleMediumStatus = ::nearby::MediumEnvironment::BleMediumStatus;
using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession;
using TxPowerLevel = ::nearby::api::ble::TxPowerLevel;
using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback;
using Uuid = ::nearby::Uuid;
using ::nearby::api::ble::BleAdvertisementData;
using ::nearby::api::ble::BlePeripheral;
using AdvertisingCallback = ::nearby::api::ble::BleMedium::AdvertisingCallback;
using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession;
constexpr FeatureFlags kTestCases[] = {
FeatureFlags{},
};
class BleTest : public testing::TestWithParam<FeatureFlags> {
public:
constexpr static absl::Duration kWaitDuration = absl::Milliseconds(1000);
std::string account_name_ = "Test-Name";
constexpr static PowerMode kPowerMode = PowerMode::kBalanced;
std::vector<nearby::internal::IdentityType> identity_types_ = {
nearby::internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP,
};
std::vector<DataElement> extended_properties_ = {
DataElement{DataElement::kTxPowerFieldType, "-10"}};
std::vector<absl::variant<PresenceScanFilter, LegacyPresenceScanFilter> >
filters_ = {PresenceScanFilter{
.scan_type = ScanType::kPresenceScan,
.extended_properties = extended_properties_,
}};
constexpr static bool kUseBle = true;
constexpr static ScanType kScanType = ScanType::kPresenceScan;
constexpr static bool kScanOnlyWhenScreenOn = true;
ScanRequest scan_request_ = {
.account_name = account_name_,
.identity_types = identity_types_,
.scan_filters = filters_,
.use_ble = kUseBle,
.scan_type = kScanType,
.power_mode = kPowerMode,
.scan_only_when_screen_on = kScanOnlyWhenScreenOn,
};
protected:
std::optional<BleMediumStatus> GetBleStatus(const Ble& ble) {
return env_.GetBleMediumStatus(*ble.GetImpl());
}
nearby::MediumEnvironment& env_{nearby::MediumEnvironment::Instance()};
};
INSTANTIATE_TEST_SUITE_P(ParametrisedBleTest, BleTest,
::testing::ValuesIn(kTestCases));
// Using MediumEnvironment to verify the start&stop StartScanning callback flows
// are working as intended.
TEST_P(BleTest, CanStartThenStopScanning) {
env_.Start();
::nearby::BluetoothAdapter adapter;
Ble ble(adapter);
ScanRequest scan_request{
.power_mode = PowerMode::kBalanced,
};
ScanningCallback scanning_callback;
nearby::CountDownLatch started_scanning_latch(1);
std::unique_ptr<ScanningSession> scannning_session = ble.StartScanning(
scan_request, ScanningCallback{
.start_scanning_result =
[&started_scanning_latch](absl::Status status) {
if (status.ok()) {
started_scanning_latch.CountDown();
}
},
});
EXPECT_TRUE(started_scanning_latch.Await(kWaitDuration).result());
EXPECT_TRUE(GetBleStatus(ble).has_value() &&
GetBleStatus(ble).value().is_scanning == true);
absl::Status stop_scanning_status = scannning_session->stop_scanning();
EXPECT_OK(stop_scanning_status);
EXPECT_TRUE(GetBleStatus(ble).has_value() &&
GetBleStatus(ble).value().is_scanning == false);
env_.Stop();
}
TEST_P(BleTest, AdvertiseAndScan) {
// Create two Ble devices, one advertises, the other one scans, and verify
// that the NP advertisement was sent from one to the other.
env_.Start();
nearby::BluetoothAdapter client_adapter;
Ble client(client_adapter);
nearby::BluetoothAdapter server_adapter;
Ble server(server_adapter);
AdvertisementData advert_data = {.is_extended_advertisement = false,
.content = "my advertisement"};
ScanRequest scan_request{
.power_mode = PowerMode::kBalanced,
};
nearby::CountDownLatch advertise_latch(1);
nearby::CountDownLatch scan_latch(1);
std::vector<BleAdvertisementData> advertisements;
std::unique_ptr<ScanningSession> scanning_session = client.StartScanning(
scan_request,
ScanningCallback{.advertisement_found_cb =
[&](BlePeripheral::UniqueId peripheral_id,
BleAdvertisementData advertisement_data) {
advertisements.push_back(advertisement_data);
scan_latch.CountDown();
}});
std::unique_ptr<nearby::api::ble::BleMedium::AdvertisingSession>
advertising_session = server.StartAdvertising(
advert_data, PowerMode::kBalanced,
AdvertisingCallback{
.start_advertising_result = [&](absl::Status status) {
advertise_latch.CountDown();
}});
EXPECT_TRUE(advertise_latch.Await(kWaitDuration).result());
EXPECT_TRUE(scan_latch.Await(kWaitDuration).result());
EXPECT_OK(scanning_session->stop_scanning());
EXPECT_OK(advertising_session->stop_advertising());
ASSERT_FALSE(advertisements.empty());
EXPECT_EQ(advertisements[0]
.service_data.find(kPresenceServiceUuid)
->second.AsStringView(),
advert_data.content);
env_.Stop();
}
} // namespace
} // namespace presence
} // namespace nearby
-41
View File
@@ -1,41 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_
#include "internal/platform/bluetooth_adapter.h"
#include "presence/implementation/mediums/ble.h"
namespace nearby {
namespace presence {
/*
* This class owns medium instance like Ble and etc. And the instance of
* this class will be owned in {@code ServiceControllerImpl}.
*/
class Mediums {
public:
// Returns a handle to the Ble medium.
Ble& GetBle() { return ble_; }
private:
nearby::BluetoothAdapter adapter_;
Ble ble_{adapter_};
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_
@@ -1,63 +0,0 @@
// Copyright 2023 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_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_
#include <optional>
#include <vector>
#include "gmock/gmock.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/proto/credential.pb.h"
#include "internal/proto/local_credential.pb.h"
#include "presence/implementation/connection_authenticator.h"
namespace nearby {
namespace presence {
/*
* This class is for unit tests, mocking {@code ConnectionAuthenticator}
* functions in `PresenceDeviceProviderTest`.
*/
class MockConnectionAuthenticator : public ConnectionAuthenticator {
public:
MOCK_METHOD(absl::StatusOr<InitiatorData>, BuildSignedMessageAsInitiator,
(absl::string_view ukey2_secret,
std::optional<const internal::LocalCredential> local_credential,
const internal::SharedCredential& shared_credential),
(const, override));
MOCK_METHOD(absl::StatusOr<ResponderData>, BuildSignedMessageAsResponder,
(absl::string_view ukey2_secret,
const internal::LocalCredential& local_credential),
(const, override));
MOCK_METHOD(
absl::Status, VerifyMessageAsInitiator,
(ResponderData authentication_data, absl::string_view ukey2_secret,
const std::vector<internal::SharedCredential>& shared_credentials),
(const, override));
MOCK_METHOD(
absl::StatusOr<internal::LocalCredential>, VerifyMessageAsResponder,
(absl::string_view ukey2_secret, InitiatorData initiator_data,
const std::vector<internal::LocalCredential>& local_credentials,
const std::vector<internal::SharedCredential>& shared_credentials),
(const, override));
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_
@@ -1,86 +0,0 @@
// Copyright 2023 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_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "presence/implementation/credential_manager.h"
namespace nearby {
namespace presence {
class MockCredentialManager : public CredentialManager {
public:
MOCK_METHOD(
void, GenerateCredentials,
(const nearby::internal::DeviceIdentityMetaData& device_identity_metadata,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb),
(override));
MOCK_METHOD(void, UpdateRemotePublicCredentials,
(absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb),
(override));
MOCK_METHOD(void, UpdateLocalCredential,
(const CredentialSelector& credential_selector,
nearby::internal::LocalCredential credential,
SaveCredentialsResultCallback result_callback),
(override));
MOCK_METHOD(void, GetLocalCredentials,
(const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback),
(override));
MOCK_METHOD(void, GetPublicCredentials,
(const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback),
(override));
MOCK_METHOD(SubscriberId, SubscribeForPublicCredentials,
(const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback),
(override));
MOCK_METHOD(void, UnsubscribeFromPublicCredentials, (SubscriberId id),
(override));
MOCK_METHOD(std::string, DecryptDeviceIdentityMetaData,
(absl::string_view metadata_encryption_key,
absl::string_view key_seed, absl::string_view metadata_string),
(override));
MOCK_METHOD(
void, SetDeviceIdentityMetaData,
(const ::nearby::internal::DeviceIdentityMetaData&
device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb),
(override));
MOCK_METHOD(::nearby::internal::DeviceIdentityMetaData,
GetDeviceIdentityMetaData, (), (override));
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_
@@ -1,82 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "presence/implementation/service_controller.h"
namespace nearby {
namespace presence {
/*
* This class is for unit test, mocking {@code ServiceController} functions.
*/
class MockServiceController : public ServiceController {
public:
MockServiceController() = default;
~MockServiceController() override = default;
MOCK_METHOD(absl::StatusOr<ScanSessionId>, StartScan,
(ScanRequest scan_request, ScanCallback callback), (override));
MOCK_METHOD(void, StopScan, (ScanSessionId session_id), (override));
MOCK_METHOD(absl::StatusOr<BroadcastSessionId>, StartBroadcast,
(BroadcastRequest broadcast_request, BroadcastCallback callback),
(override));
MOCK_METHOD(void, StopBroadcast, (BroadcastSessionId session_id), (override));
MOCK_METHOD(
void, UpdateLocalDeviceMetadata,
(const ::nearby::internal::Metadata& metadata, bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb),
(override));
MOCK_METHOD(
void, UpdateDeviceIdentityMetaData,
(const ::nearby::internal::DeviceIdentityMetaData&
device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb),
(override));
MOCK_METHOD(::nearby::internal::DeviceIdentityMetaData,
GetDeviceIdentityMetaData, (), (override));
MOCK_METHOD(void, GetLocalPublicCredentials,
(const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback),
(override));
MOCK_METHOD(void, UpdateRemotePublicCredentials,
(absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb),
(override));
MOCK_METHOD(void, GetLocalCredentials,
(const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback),
(override));
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_
-125
View File
@@ -1,125 +0,0 @@
#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_
// Copyright 2022 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.
// C API for Rust implementation of LDT [1], tailored to Nearby Presence's
// BLE 4.2 legacy format advertisement parsing usecase.
//
// [1] https://eprint.iacr.org/2017/841.pdf
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
// Individual encrypt/decrypt API, useful when creating advertisements or when
// decrypting advertisements from a known origin
// The allocated handle to use for encryption
typedef struct {
uint64_t handle;
} NpLdtEncryptHandle;
// The allocated handle to use for decryption
typedef struct {
uint64_t handle;
} NpLdtDecryptHandle;
// Key material from the Nearby Presence credential from which keys will be
// derived.
typedef struct {
uint8_t bytes[32];
} NpLdtKeySeed;
typedef struct {
uint8_t bytes[32];
} NpMetadataKeyHmac;
typedef struct {
uint8_t bytes[2];
} NpLdtSalt;
// Possible result codes returned from the LDT NP API's
typedef enum {
// Call to api was succesful
NP_LDT_SUCCESS = 0,
// Payload of invalid length was provided must be >= 16 and <=31 bytes
NP_LDT_ERROR_INVALID_LENGTH = -1,
// The provided metadata hmac did not match the calculated hmac on call to
// decrypt and verify
NP_LDT_ERROR_MAC_MISMATCH = -2,
} NP_LDT_RESULT;
// Allocate an LDT-XTS-AES128 Decryption cipher using the "swap" mix function.
//
// `key_seed` is the key material from the Nearby Presence credential from which
// the LDT key will be derived.
// 'hmac_tag' is the hmac auth tag calculated on the metadata key used to verify
// decryption was successful
//
// Returns 0 on error, or a non-zero handle on success.
NpLdtDecryptHandle NpLdtDecryptCreate(NpLdtKeySeed key_seed,
NpMetadataKeyHmac hmac_tag);
// Allocate an LDT-XTS-AES128 Encryption cipher using the "swap" mix function.
//
// `key_seed` is the key material from the Nearby Presence credential from which
// the LDT key will be derived.
//
// Returns 0 on error, or a non-zero handle on success.
NpLdtEncryptHandle NpLdtEncryptCreate(NpLdtKeySeed key_seed);
// Release allocated resources for an NpLdtEncryptHandle
//
// Returns 0 on success or an NP_LDT_RESULT error code on failure
NP_LDT_RESULT NpLdtEncryptClose(NpLdtEncryptHandle handle);
// Release allocated resources for an NpLdtDecryptHandle
//
// Returns 0 on success or an NP_LDT_RESULT error code on failure
NP_LDT_RESULT NpLdtDecryptClose(NpLdtDecryptHandle handle);
// Encrypt a 16-31 byte buffer in-place.
//
// `buffer` is a pointer to a 16-31 byte plaintext, with length in `buffer_len`.
// `salt` is the big-endian 2 byte salt that will be used in the Nearby
// Presence advertisement, which will be incorporated into the tweaks LDT uses
// while encrypting.
//
// Returns 0 on success, in which case `buffer` will now contain ciphertext.
// Returns an NP_LDT_RESULT error code on failure
NP_LDT_RESULT NpLdtEncrypt(NpLdtEncryptHandle handle, uint8_t* buffer,
size_t buffer_len, NpLdtSalt salt);
// Decrypt a 16-31 byte buffer in-place.
//
// `buffer` is a pointer to a 16-31 byte ciphertext, with length in
// `buffer_len`.
// `salt` is the big-endian 2 byte salt found in the Nearby Presence
// advertisement, which will be incorporated into the tweaks LDT uses while
// decrypting.
//
// Returns 0 on success, in which case `buffer` will now contain plaintext.
// Returns an NP_LDT_RESULT error code on failure
NP_LDT_RESULT NpLdtDecryptAndVerify(NpLdtDecryptHandle handle, uint8_t* buffer,
size_t buffer_len, NpLdtSalt salt);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_
-280
View File
@@ -1,280 +0,0 @@
// Copyright 2022 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 "presence/implementation/scan_manager.h"
#include <assert.h>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "internal/platform/future.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/implementation/crypto.h"
#include "internal/platform/logging.h"
#include "presence//implementation/advertisement_filter.h"
#include "presence/data_element.h"
#include "presence/data_types.h"
#include "presence/device_motion.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/implementation/mediums/ble.h"
#include "presence/presence_action.h"
#include "presence/presence_device.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
namespace {
using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData;
using BlePeripheral = ::nearby::api::ble::BlePeripheral;
using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession;
using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback;
} // namespace
ScanSessionId ScanManager::StartScan(ScanRequest scan_request,
ScanCallback cb) {
ScanSessionId id = nearby::RandData<ScanSessionId>();
RunOnServiceControllerThread(
"start-scan",
[this, id, scan_request,
scan_callback =
std::move(cb)]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable {
ScanningCallback callback = ScanningCallback{
.start_scanning_result =
[start_scan_client = std::move(scan_callback.start_scan_cb)](
absl::Status ble_status) mutable {
start_scan_client(ble_status);
},
.advertisement_found_cb =
[this, id](BlePeripheral::UniqueId peripheral_id,
BleAdvertisementData data) {
RunOnServiceControllerThread(
"notify-found-ble",
[this, id, data = std::move(data), peripheral_id]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
NotifyFoundBle(id, data, peripheral_id);
});
},
.advertisement_lost_cb =
[this, id](BlePeripheral::UniqueId peripheral_id) {
RunOnServiceControllerThread(
"notify-lost-ble",
[this, id, peripheral_id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(
*executor_) { NotifyLostBle(id, peripheral_id); });
}};
FetchCredentials(id, scan_request);
scan_sessions_.insert(
{id, ScanSessionState{
.request = scan_request,
.callback = std::move(scan_callback),
.decoder = AdvertisementDecoderImpl(),
.advertisement_filter = AdvertisementFilter(scan_request),
.scanning_session = mediums_->GetBle().StartScanning(
scan_request, std::move(callback))}});
});
return id;
}
void ScanManager::StopScan(ScanSessionId id) {
RunOnServiceControllerThread(
"stop-scan", [this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
auto it = scan_sessions_.find(id);
if (it == scan_sessions_.end()) {
return;
}
if (it->second.scanning_session) {
absl::Status status = it->second.scanning_session->stop_scanning();
if (!status.ok()) {
LOG(WARNING) << "StopScan error: " << status;
}
}
scan_sessions_.erase(it);
});
}
void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data,
BlePeripheral::UniqueId peripheral_id) {
auto it = scan_sessions_.find(id);
if (it == scan_sessions_.end()) {
return;
}
auto advertisement_data =
data.service_data[kPresenceServiceUuid].AsStringView();
auto advert = it->second.decoder.DecodeAdvertisement(advertisement_data);
if (!advert.ok()) {
// This advertisement is not relevant to the current element, skip.
return;
}
std::string remote_address = absl::StrCat(absl::Hex(peripheral_id));
if (it->second.advertisement_filter.MatchesScanFilter(*advert)) {
internal::DeviceIdentityMetaData device_identity_metadata;
device_identity_metadata.set_bluetooth_mac_address(remote_address);
if (!device_unique_id_to_endpoint_id_map_.contains(peripheral_id)) {
PresenceDevice device(DeviceMotion(), device_identity_metadata,
advert->identity_type);
// Ok if the advertisement is for trusted/private identity.
if (advert->public_credential.ok()) {
device.SetDecryptSharedCredential(*(advert->public_credential));
}
device.AddExtendedProperties(advert->data_elements);
for (const auto& data_element : advert->data_elements) {
if (data_element.GetType() == DataElement::kActionFieldType) {
device.AddAction(PresenceAction(static_cast<int>(
static_cast<uint8_t>(data_element.GetValue()[0]))));
}
}
device_unique_id_to_endpoint_id_map_.emplace(peripheral_id,
device.GetEndpointId());
it->second.callback.on_discovered_cb(std::move(device));
} else {
PresenceDevice device(
device_unique_id_to_endpoint_id_map_.at(peripheral_id));
device.SetDeviceIdentityMetaData(device_identity_metadata);
// Ok if the advertisement is for trusted/private identity.
if (advert->public_credential.ok()) {
device.SetDecryptSharedCredential(*(advert->public_credential));
}
device.AddExtendedProperties(advert->data_elements);
for (const auto& data_element : advert->data_elements) {
if (data_element.GetType() == DataElement::kActionFieldType) {
device.AddAction(PresenceAction(static_cast<int>(
static_cast<uint8_t>(data_element.GetValue()[0]))));
}
}
it->second.callback.on_updated_cb(std::move(device));
}
}
}
void ScanManager::NotifyLostBle(ScanSessionId id,
BlePeripheral::UniqueId peripheral_id) {
auto it = scan_sessions_.find(id);
if (it == scan_sessions_.end()) {
return;
}
std::string remote_address = absl::StrCat(absl::Hex(peripheral_id));
if (device_unique_id_to_endpoint_id_map_.contains(peripheral_id)) {
internal::DeviceIdentityMetaData device_identity_metadata;
device_identity_metadata.set_bluetooth_mac_address(
std::string(remote_address));
PresenceDevice device(
device_unique_id_to_endpoint_id_map_.at(peripheral_id));
device.SetDeviceIdentityMetaData(device_identity_metadata);
device_unique_id_to_endpoint_id_map_.erase(peripheral_id);
it->second.callback.on_lost_cb(std::move(device));
}
}
std::vector<CredentialSelector> GetCredentialSelectors(
const ScanRequest& scan_request) {
std::vector<nearby::internal::IdentityType> all_types = {
nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP,
nearby::internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP,
nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC};
std::vector<CredentialSelector> selectors;
for (auto identity_type :
(scan_request.identity_types.empty() ? all_types
: scan_request.identity_types)) {
selectors.push_back(
CredentialSelector{.manager_app_id = scan_request.manager_app_id,
.account_name = scan_request.account_name,
.identity_type = identity_type});
}
return selectors;
}
void ScanManager::FetchCredentials(ScanSessionId id,
const ScanRequest& scan_request) {
std::vector<CredentialSelector> credential_selectors =
GetCredentialSelectors(scan_request);
for (const CredentialSelector& selector : credential_selectors) {
// Not fetching for PUBLIC.
if (selector.identity_type == internal::IDENTITY_TYPE_UNSPECIFIED ||
selector.identity_type == internal::IDENTITY_TYPE_PUBLIC) {
LOG(INFO) << __func__ << ": skip feteching creds for identity type: "
<< selector.identity_type;
continue;
}
credential_manager_->GetPublicCredentials(
selector, PublicCredentialType::kRemotePublicCredential,
{.credentials_fetched_cb =
[this, id, identity_type = selector.identity_type](
absl::StatusOr<
std::vector<::nearby::internal::SharedCredential>>
credentials) {
if (!credentials.ok()) {
LOG(WARNING)
<< "Failed to fetch credentials: " << credentials.status();
return;
}
RunOnServiceControllerThread(
"update-credentials",
[this, id, identity_type,
credentials = std::move(*credentials)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
UpdateCredentials(id, identity_type,
std::move(credentials));
});
}});
}
}
void ScanManager::UpdateCredentials(ScanSessionId id,
IdentityType identity_type,
std::vector<SharedCredential> credentials) {
// Credentials should never get fetched for PUBLIC of No-Identity requests
assert(identity_type != internal::IDENTITY_TYPE_UNSPECIFIED);
assert(identity_type != internal::IDENTITY_TYPE_PUBLIC);
auto it = scan_sessions_.find(id);
if (it == scan_sessions_.end()) {
return;
}
ScanSessionState& session = it->second;
session.credentials[identity_type] = std::move(credentials);
session.decoder = AdvertisementDecoderImpl(&session.credentials);
}
int ScanManager::ScanningCallbacksLengthForTest() {
::nearby::Future<int> count;
RunOnServiceControllerThread("callbacks-size",
[&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
count.Set(scan_sessions_.size());
});
return count.Get().GetResult();
}
} // namespace presence
} // namespace nearby
-109
View File
@@ -1,109 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/mutex.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_types.h"
#include "presence/implementation/advertisement_filter.h"
#include "presence/implementation/credential_manager.h"
#include "presence/implementation/mediums/mediums.h"
#include "presence/scan_request.h"
#ifdef USE_RUST_DECODER
#include "presence/implementation/advertisement_decoder_rust_impl.h"
#else
#include "presence/implementation/advertisement_decoder_impl.h"
#endif
namespace nearby {
namespace presence {
// The instance of ScanManager is owned by `ServiceControllerImpl`.
// Helping service controller to manage scan requests and callbacks.
class ScanManager {
public:
using SingleThreadExecutor = ::nearby::SingleThreadExecutor;
using Mutex = ::nearby::Mutex;
using MutexLock = ::nearby::MutexLock;
using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession;
using Runnable = ::nearby::Runnable;
using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData;
using SharedCredential = ::nearby::internal::SharedCredential;
using IdentityType = ::nearby::internal::IdentityType;
ScanManager(Mediums& mediums, CredentialManager& credential_manager,
SingleThreadExecutor& executor) {
mediums_ = &mediums, credential_manager_ = &credential_manager;
executor_ = &executor;
}
~ScanManager() = default;
ScanSessionId StartScan(ScanRequest scan_request, ScanCallback cb);
void StopScan(ScanSessionId session_id);
// Below functions are test only.
// Reference: go/totw/135#augmenting-the-public-api-for-tests
int ScanningCallbacksLengthForTest();
private:
struct ScanSessionState {
ScanRequest request;
ScanCallback callback;
absl::flat_hash_map<IdentityType, std::vector<SharedCredential>>
credentials;
AdvertisementDecoderImpl decoder;
AdvertisementFilter advertisement_filter;
std::unique_ptr<ScanningSession> scanning_session;
};
void NotifyFoundBle(ScanSessionId id, BleAdvertisementData data,
nearby::api::ble::BlePeripheral::UniqueId peripheral_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void NotifyLostBle(ScanSessionId id,
nearby::api::ble::BlePeripheral::UniqueId peripheral_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void FetchCredentials(ScanSessionId id, const ScanRequest& scan_request)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void UpdateCredentials(ScanSessionId id, IdentityType identity_type,
std::vector<SharedCredential> credentials)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) {
executor_->Execute(std::string(name), std::move(runnable));
}
Mediums* mediums_;
CredentialManager* credential_manager_;
absl::flat_hash_map<ScanSessionId, ScanSessionState> scan_sessions_
ABSL_GUARDED_BY(*executor_);
absl::flat_hash_map<nearby::api::ble::BlePeripheral::UniqueId, std::string>
device_unique_id_to_endpoint_id_map_ ABSL_GUARDED_BY(*executor_);
SingleThreadExecutor* executor_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_
@@ -1,453 +0,0 @@
// Copyright 2022 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 "presence/implementation/scan_manager.h"
#include <atomic>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/types/variant.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/logging.h"
#include "internal/platform/mac_address.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.proto.h"
#include "presence/data_element.h"
#include "presence/data_types.h"
#include "presence/implementation/credential_manager_impl.h"
#include "presence/implementation/mediums/advertisement_data.h"
#include "presence/implementation/mediums/ble.h"
#include "presence/implementation/mediums/mediums.h"
#include "presence/implementation/mock_credential_manager.h"
#include "presence/power_mode.h"
#include "presence/presence_action.h"
#include "presence/presence_device.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
namespace {
using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession;
using AdvertisingCallback =
::nearby::api::ble::BleMedium::AdvertisingCallback;
using ::nearby::SingleThreadExecutor;
using CountDownLatch = ::nearby::CountDownLatch;
using ::testing::Contains;
class ScanManagerTest : public testing::Test {
protected:
void SetUp() override { env_.Start(); }
void TearDown() override {
executor_.Shutdown();
env_.Stop();
}
std::unique_ptr<AdvertisingSession> StartAdvertisingOn(Ble& ble) {
auto advertisement = AdvertisementData{
.is_extended_advertisement = false,
.content = {0x00, 0x26, 0x00, 0x40},
};
std::unique_ptr<AdvertisingSession> session = ble.StartAdvertising(
advertisement, PowerMode::kLowPower,
AdvertisingCallback{.start_advertising_result = [](absl::Status) {}});
env_.Sync();
return session;
}
ScanRequest MakeDefaultScanRequest() {
std::vector<absl::variant<PresenceScanFilter, LegacyPresenceScanFilter>>
filters = {PresenceScanFilter{
.scan_type = ScanType::kPresenceScan,
.extended_properties = MakeDefaultExtendedProperties(),
}};
return {
.account_name = "Test account",
.identity_types = MakeDefaultIdentityTypes(),
.scan_filters = filters,
.use_ble = true,
.scan_type = ScanType::kPresenceScan,
.power_mode = PowerMode::kBalanced,
.scan_only_when_screen_on = true,
};
}
ScanCallback MakeDefaultScanCallback() {
return {
.start_scan_cb =
[this](absl::Status status) {
if (status.ok()) {
start_latch_.CountDown();
}
},
.on_discovered_cb =
[this](PresenceDevice pd) { found_latch_.CountDown(); },
.on_updated_cb =
[this](PresenceDevice pd) { updated_latch_.CountDown(); },
.on_lost_cb = [this](PresenceDevice pd) { lost_latch_.CountDown(); }};
}
std::vector<nearby::internal::IdentityType> MakeDefaultIdentityTypes() {
return {
nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC,
};
}
std::vector<DataElement> MakeDefaultExtendedProperties() {
return {DataElement(ActionBit::kNearbyShareAction)};
}
SingleThreadExecutor executor_;
CredentialManagerImpl credential_manager_{&executor_};
nearby::MediumEnvironment& env_ = {nearby::MediumEnvironment::Instance()};
CountDownLatch start_latch_{1};
CountDownLatch found_latch_{1};
CountDownLatch updated_latch_{1};
CountDownLatch lost_latch_{1};
};
TEST_F(ScanManagerTest, CanStartThenStopScanning) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
// Set up advertiser
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
// Start scanning
ScanSessionId scan_session =
manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback());
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
EXPECT_TRUE(start_latch_.Await().Ok());
EXPECT_TRUE(found_latch_.Await().Ok());
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
TEST_F(ScanManagerTest, CannotStopScanTwice) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
ScanSessionId scan_session =
manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback());
LOG(INFO) << "Start scan";
EXPECT_TRUE(start_latch_.Await().Ok());
// Ensure that we have started scanning before we try to stop.
env_.Sync();
LOG(INFO) << "Stop scan";
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
LOG(INFO) << "Stop scan again";
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
TEST_F(ScanManagerTest, TestNoFilter) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
// Set up advertiser
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
// Start scanning
ScanRequest scan_request_no_filter = MakeDefaultScanRequest();
scan_request_no_filter.scan_filters.clear();
ScanSessionId scan_session =
manager.StartScan(scan_request_no_filter, MakeDefaultScanCallback());
ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
ASSERT_TRUE(mediums.GetBle().IsAvailable());
EXPECT_TRUE(start_latch_.Await().Ok());
EXPECT_TRUE(found_latch_.Await().Ok());
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
TEST_F(ScanManagerTest, PresenceMetadataIsRetained) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
// Set up advertiser
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
std::string address =
absl::StrCat(absl::Hex(server_adapter.GetAddress().address()));
ScanCallback callback = {
.start_scan_cb =
[this](absl::Status status) {
if (status.ok()) {
start_latch_.CountDown();
}
},
.on_discovered_cb =
[this, &address](PresenceDevice pd) {
if (pd.GetDeviceIdentityMetadata().bluetooth_mac_address() ==
address) {
EXPECT_THAT(pd.GetExtendedProperties(),
Contains(DataElement(ActionBit::kNearbyShareAction))
.Times(1));
EXPECT_THAT(
pd.GetActions(),
Contains(PresenceAction{(int)ActionBit::kNearbyShareAction})
.Times(1));
found_latch_.CountDown();
}
},
.on_updated_cb =
[this, &address](PresenceDevice pd) {
if (pd.GetDeviceIdentityMetadata().bluetooth_mac_address() ==
address) {
EXPECT_THAT(pd.GetExtendedProperties(),
Contains(DataElement(ActionBit::kNearbyShareAction))
.Times(1));
EXPECT_THAT(
pd.GetActions(),
Contains(PresenceAction{(int)ActionBit::kNearbyShareAction})
.Times(1));
updated_latch_.CountDown();
}
}};
// Start scanning
ScanRequest scan_request_no_filter = MakeDefaultScanRequest();
scan_request_no_filter.scan_filters.clear();
auto scan_session =
manager.StartScan(scan_request_no_filter, std::move(callback));
ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
ASSERT_TRUE(mediums.GetBle().IsAvailable());
EXPECT_TRUE(start_latch_.Await().Ok());
EXPECT_TRUE(found_latch_.Await().Ok());
// Advertise again to trigger `on_updated_cb`
advertising_session = StartAdvertisingOn(ble2);
EXPECT_TRUE(updated_latch_.Await().Ok());
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
TEST_F(ScanManagerTest, DiscoverThenLoseAdvertisement) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
// Set up advertiser
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
// Start scanning
ScanSessionId scan_session =
manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback());
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
EXPECT_TRUE(start_latch_.Await().Ok());
EXPECT_TRUE(found_latch_.Await().Ok());
// Stop advertising to trigger `on_lost_cb`
EXPECT_OK(advertising_session->stop_advertising());
env_.Sync();
EXPECT_TRUE(lost_latch_.Await().Ok());
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
TEST_F(ScanManagerTest, StopOneSessionFromAnotherDeadlock) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
CountDownLatch start_latch2{1};
CountDownLatch found_latch2{1};
// Start scanning
std::vector<DataElement> extended_properties_mismatch = {
DataElement(ActionBit::kInstantTetheringAction)};
std::vector<absl::variant<PresenceScanFilter, LegacyPresenceScanFilter>>
mismatch_filters = {PresenceScanFilter{
.scan_type = ScanType::kPresenceScan,
.extended_properties = extended_properties_mismatch,
}};
ScanRequest scan_request_mismatch = {
.account_name = "Test account",
.identity_types = MakeDefaultIdentityTypes(),
.scan_filters = mismatch_filters,
.use_ble = true,
.scan_type = ScanType::kPresenceScan,
.power_mode = PowerMode::kBalanced,
.scan_only_when_screen_on = true,
};
// we use scan_request_mismatch so this session's discovery doesn't get
// triggered.
ScanSessionId scan_session =
manager.StartScan(scan_request_mismatch, MakeDefaultScanCallback());
ScanCallback scanning_callback2 = {.start_scan_cb =
[&](absl::Status status) {
if (status.ok()) {
start_latch2.CountDown();
}
},
.on_discovered_cb =
[&](PresenceDevice pd) {
LOG(INFO) << "scansession2 found";
found_latch2.CountDown();
manager.StopScan(scan_session);
}};
ScanSessionId scan_session2 = manager.StartScan(
MakeDefaultScanRequest(), std::move(scanning_callback2));
ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 2);
// Set up advertiser
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
EXPECT_TRUE(found_latch2.Await().Ok());
// Session was stopped before, this should not be able to stop successfully.
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
ASSERT_TRUE(mediums.GetBle().IsAvailable());
manager.StopScan(scan_session2);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
// Receive a BLE advertisement after StopScan. `on_discovered_cb`
// must not be called.
TEST_F(ScanManagerTest, NoDeviceFoundAfterStopScan) {
Mediums mediums;
ScanManager manager(mediums, credential_manager_, executor_);
CountDownLatch start_scan_latch{1};
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::atomic_bool stopped = false;
ScanSessionId scan_session = manager.StartScan(
MakeDefaultScanRequest(),
ScanCallback{.start_scan_cb =
[&start_scan_latch](absl::Status status) {
if (status.ok()) {
start_scan_latch.CountDown();
}
},
.on_discovered_cb =
[&](PresenceDevice pd) { EXPECT_FALSE(stopped); }});
start_scan_latch.Await();
manager.StopScan(scan_session);
stopped = true;
std::unique_ptr<AdvertisingSession> advertising_session =
StartAdvertisingOn(ble2);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
executor_.Shutdown();
}
internal::SharedCredential GetPublicCredential() {
// Values copied from LDT tests
ByteArray seed({
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
});
ByteArray known_mac({0x09, 0xFE, 0x9E, 0x81, 0xB7, 0x3E, 0x5E, 0xCC,
0x76, 0x59, 0x57, 0x71, 0xE0, 0x1F, 0xFB, 0x34,
0x38, 0xE7, 0x5F, 0x24, 0xA7, 0x69, 0x56, 0xA0,
0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD});
internal::SharedCredential public_credential;
public_credential.set_key_seed(seed.AsStringView());
public_credential.set_metadata_encryption_key_tag_v0(
known_mac.AsStringView());
return public_credential;
}
std::vector<internal::SharedCredential> BuildSharedCredentials() {
return {GetPublicCredential()};
}
TEST_F(ScanManagerTest, ScanningE2EWithEncryptedAdvertisementAndCredentials) {
Mediums mediums;
auto mock_credential_manager = MockCredentialManager();
EXPECT_CALL(mock_credential_manager, GetPublicCredentials)
.WillOnce([&](const CredentialSelector& credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) {
callback.credentials_fetched_cb(BuildSharedCredentials());
});
ScanManager manager(mediums, mock_credential_manager, executor_);
// Set up advertiser to broadcast a private identity adv
nearby::BluetoothAdapter server_adapter;
Ble ble2(server_adapter);
std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC";
std::string payload = absl::HexStringToBytes(V0AdvEncryptedBytes);
auto advertisement = AdvertisementData{
.is_extended_advertisement = false,
.content = payload,
};
std::unique_ptr<AdvertisingSession> session = ble2.StartAdvertising(
advertisement, PowerMode::kLowPower,
AdvertisingCallback{.start_advertising_result = [](absl::Status) {}});
env_.Sync();
std::vector<
absl::variant<PresenceScanFilter, LegacyPresenceScanFilter>> // NOLINT
filters = {PresenceScanFilter{
.scan_type = ScanType::kPresenceScan,
.extended_properties = {DataElement(DataElement::kTxPowerFieldType,
3)},
}};
ScanRequest scan_request = {
.account_name = "Test account",
.identity_types =
{nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP},
.scan_filters = filters,
.use_ble = true,
.scan_type = ScanType::kPresenceScan,
.power_mode = PowerMode::kBalanced,
.scan_only_when_screen_on = true,
};
// Start scanning
ScanSessionId scan_session =
manager.StartScan(scan_request, MakeDefaultScanCallback());
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1);
EXPECT_TRUE(start_latch_.Await().Ok());
EXPECT_TRUE(found_latch_.Await().Ok());
manager.StopScan(scan_session);
EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0);
}
} // namespace
} // namespace presence
} // namespace nearby
-153
View File
@@ -1,153 +0,0 @@
// Copyright 2022 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_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_
#include <cstdint>
#include <functional>
#include <optional>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "presence/device_motion.h"
#include "presence/presence_zone.h"
namespace nearby {
namespace presence {
enum class DataSource {
kUnknown = 0,
kBle = 1,
kUwb = 2,
kNanRtt = 4,
};
struct RangingMeasurement {
// [0.0, 1.0], 1.0 is the max confidence.
float confidence_level;
float value;
};
struct RangingPosition {
RangingMeasurement distance;
std::optional<RangingMeasurement> azimuth;
std::optional<RangingMeasurement> elevation;
uint64_t elapsed_realtime_millis;
};
struct ZoneTransition {
PresenceZone::DistanceBoundary::RangeType distance_range_type;
float confidence_level;
};
struct RangingData {
DataSource data_source;
RangingPosition position;
std::optional<ZoneTransition> zone_transition;
std::vector<DeviceMotion> device_motions;
};
struct ZoneTransitionCallback {
absl::AnyInvocable<void(
uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType proximity_zone)>
on_proximity_zone_changed =
[](uint64_t device_id,
PresenceZone::DistanceBoundary::RangeType proximity_zone) {};
absl::AnyInvocable<void(uint64_t callback_id)> on_callback_id_generated =
[](uint64_t callback_id) {};
};
class SensorFusion {
public:
virtual ~SensorFusion() = default;
// Called when a device motion gesture is detected.
typedef std::function<void(DeviceMotion::MotionType detected_device_motion)>
DeviceMotionCallback;
/**
* Returns a list of data sources would be used by the sensor fusion if they
* are available.
* This is to control what kinds of sources the NP scan engine should use for
* ranging. For instance, if both NAN RTT and UWB are supported, FPP may
* decide NAN RTT isn't useful at a certain moment, so NP scan engine won't
* try to request NAN RTT.
*
* @param elapsed_realtime_millis Elapsed timestamp since boot of the data
* source query.
* @param available_sources A bit mask of data sources that are available.
*/
virtual std::vector<DataSource> GetDataSources(
uint64_t elapsed_realtime_millis,
const std::vector<DataSource>& available_sources) = 0;
/**
* Updates BLE scanned results to Sensor Fusion.
*
* @param device_id A unique device id of the peer device.
* @param txPower Calibrated TX power of the scan result, {@code
* std::nullopt} if the calibrated TX power is not available.
* @param rssi Received signal strength indicator for the scan result.
* @param elapsed_realtime_millis Elapsed timestamp since boot when the
* scan result is discovered.
*/
virtual absl::Status UpdateBleScanResult(
uint64_t device_id, std::optional<int8_t> txPower, int rssi,
uint64_t elapsed_realtime_millis) = 0;
/**
* Updates UWB ranging results to Sensor Fusion.
*
* @param device_id A unique device id of the peer device.
* @param position UWB ranging result (distance and optionally angle)
*/
virtual void UpdateUwbRangingResult(uint64_t device_id,
RangingPosition position) = 0;
/**
* Adds callback for updates of proximity zone transitions.
*/
virtual void RequestZoneTransitionUpdates(
ZoneTransitionCallback callback) = 0;
/**
* Removes callback for updates of proximity zone transitions.
*/
virtual void RemoveZoneTransitionUpdates(uint64_t callback_id) = 0;
/**
* Adds callback for updates of device motion events.
*/
virtual void RequestDeviceMotionUpdates(DeviceMotionCallback callback) = 0;
/**
* Remove callback for updates of device motion events.
*/
virtual void RemoveDeviceMotionUpdates(DeviceMotionCallback callback) = 0;
/**
* Returns the best ranging estimate to a given device. Returns {@code
* std::nullopt} if the sensor fusion cannot produce a ranging estimate.
*
* @param device_id Id of the peer device.
*/
virtual std::optional<RangingData> GetRangingData(uint64_t device_id) = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_
@@ -1,76 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_
#include <vector>
#include "absl/status/statusor.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/proto/metadata.pb.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
/*
* This class is owned in {@code PresenceService}. It specifies the function
* signatures. {@code ServiceControllerImpl} and {@code MockServiceController}
* inherit this class and provides real implementation and mock impl for tests.
*/
class ServiceController {
public:
ServiceController() = default;
virtual ~ServiceController() = default;
virtual absl::StatusOr<ScanSessionId> StartScan(ScanRequest scan_request,
ScanCallback callback) = 0;
virtual void StopScan(ScanSessionId session_id) = 0;
virtual absl::StatusOr<BroadcastSessionId> StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) = 0;
virtual void StopBroadcast(BroadcastSessionId session_id) = 0;
virtual void UpdateLocalDeviceMetadata(
const ::nearby::internal::Metadata& metadata, bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) = 0;
virtual void UpdateDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData&
device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) = 0;
virtual ::nearby::internal::DeviceIdentityMetaData
GetDeviceIdentityMetaData() = 0;
virtual void GetLocalPublicCredentials(
const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback) = 0;
virtual void UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) = 0;
virtual void GetLocalCredentials(
const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) = 0;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_
@@ -1,91 +0,0 @@
// Copyright 2020 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 "presence/implementation/service_controller_impl.h"
#include <utility>
#include <vector>
#include "absl/status/statusor.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "presence/data_types.h"
#include "presence/implementation/credential_manager.h"
namespace nearby {
namespace presence {
absl::StatusOr<ScanSessionId> ServiceControllerImpl::StartScan(
ScanRequest scan_request, ScanCallback callback) {
return scan_manager_.StartScan(scan_request, std::move(callback));
}
void ServiceControllerImpl::StopScan(ScanSessionId id) {
scan_manager_.StopScan(id);
}
absl::StatusOr<BroadcastSessionId> ServiceControllerImpl::StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) {
return broadcast_manager_.StartBroadcast(broadcast_request,
std::move(callback));
}
void ServiceControllerImpl::StopBroadcast(BroadcastSessionId id) {
broadcast_manager_.StopBroadcast(id);
}
// TODO(b/327629276): Remove this function.
void ServiceControllerImpl::UpdateLocalDeviceMetadata(
const ::nearby::internal::Metadata& metadata, bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) {}
void ServiceControllerImpl::UpdateDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData& device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) {
credential_manager_.SetDeviceIdentityMetaData(
device_identity_metadata, regen_credentials, manager_app_id,
identity_types, credential_life_cycle_days,
contiguous_copy_of_credentials, std::move(credentials_generated_cb));
}
void ServiceControllerImpl::GetLocalPublicCredentials(
const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback) {
credential_manager_.GetPublicCredentials(
credential_selector, PublicCredentialType::kLocalPublicCredential,
std::move(callback));
}
void ServiceControllerImpl::UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>& remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) {
credential_manager_.UpdateRemotePublicCredentials(
manager_app_id, account_name, remote_public_creds,
std::move(credentials_updated_cb));
}
void ServiceControllerImpl::GetLocalCredentials(
const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
credential_manager_.GetLocalCredentials(credential_selector,
std::move(callback));
}
} // namespace presence
} // namespace nearby
@@ -1,110 +0,0 @@
// Copyright 2020 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_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/metadata.pb.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/implementation/broadcast_manager.h"
#include "presence/implementation/credential_manager.h"
#include "presence/implementation/scan_manager.h"
#include "presence/implementation/service_controller.h"
#include "presence/scan_request.h"
/*
* This class implements {@code ServiceController} functions. Owns mediums and
* other managers instances.
*/
namespace nearby {
namespace presence {
class ServiceControllerImpl : public ServiceController {
public:
using SingleThreadExecutor = ::nearby::SingleThreadExecutor;
ServiceControllerImpl(SingleThreadExecutor* executor,
CredentialManager* credential_manager,
ScanManager* scan_manager,
BroadcastManager* broadcast_manager)
: executor_(*executor),
credential_manager_(*credential_manager),
scan_manager_(*scan_manager),
broadcast_manager_(*broadcast_manager) {}
~ServiceControllerImpl() override { executor_.Shutdown(); }
absl::StatusOr<ScanSessionId> StartScan(ScanRequest scan_request,
ScanCallback callback) override;
void StopScan(ScanSessionId session_id) override;
absl::StatusOr<BroadcastSessionId> StartBroadcast(
BroadcastRequest broadcast_request, BroadcastCallback callback) override;
void StopBroadcast(BroadcastSessionId) override;
void UpdateLocalDeviceMetadata(
const ::nearby::internal::Metadata& metadata, bool regen_credentials,
absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) override;
void UpdateDeviceIdentityMetaData(
const ::nearby::internal::DeviceIdentityMetaData&
device_identity_metadata,
bool regen_credentials, absl::string_view manager_app_id,
const std::vector<nearby::internal::IdentityType>& identity_types,
int credential_life_cycle_days, int contiguous_copy_of_credentials,
GenerateCredentialsResultCallback credentials_generated_cb) override;
::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData()
override {
return credential_manager_.GetDeviceIdentityMetaData();
}
void GetLocalPublicCredentials(
const CredentialSelector& credential_selector,
GetPublicCredentialsResultCallback callback) override;
void UpdateRemotePublicCredentials(
absl::string_view manager_app_id, absl::string_view account_name,
const std::vector<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb) override;
void GetLocalCredentials(const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) override;
SingleThreadExecutor& GetBackgroundExecutor() { return executor_; }
private:
void NotifyStartCallbackStatus(BroadcastSessionId id, absl::Status status);
void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) {
executor_.Execute(std::string(name), std::move(runnable));
}
SingleThreadExecutor& executor_;
CredentialManager& credential_manager_;
ScanManager& scan_manager_;
BroadcastManager& broadcast_manager_;
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_
@@ -1,102 +0,0 @@
// Copyright 2023 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 "presence/implementation/service_controller_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.pb.h"
#include "presence/implementation/broadcast_manager.h"
#include "presence/implementation/mediums/mediums.h"
#include "presence/implementation/mock_credential_manager.h"
#include "presence/implementation/scan_manager.h"
namespace nearby {
namespace presence {
namespace {
constexpr absl::string_view kManagerAppId = "TEST_MANAGER_APP";
constexpr absl::string_view kAccountName = "test account";
constexpr absl::string_view kSecretId1 = "1111111";
constexpr absl::string_view kSecretId2 = "2222222";
constexpr absl::string_view kSecretId3 = "3333333";
CredentialSelector BuildDefaultCredentialSelector() {
CredentialSelector credential_selector;
credential_selector.manager_app_id = std::string(kManagerAppId);
credential_selector.account_name = std::string(kAccountName);
credential_selector.identity_type =
::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
return credential_selector;
}
std::vector<internal::LocalCredential> BuildLocalCredentials() {
internal::LocalCredential local_credential1;
local_credential1.set_secret_id(kSecretId1);
internal::LocalCredential local_credential2;
local_credential2.set_secret_id(kSecretId2);
internal::LocalCredential local_credential3;
local_credential3.set_secret_id(kSecretId3);
return {local_credential1, local_credential2, local_credential3};
}
TEST(ServiceControllerImplTest, GetLocalCredentials) {
auto mock_credential_manager = std::make_unique<MockCredentialManager>();
EXPECT_CALL(*mock_credential_manager.get(), GetLocalCredentials)
.WillOnce([&](const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
callback.credentials_fetched_cb(BuildLocalCredentials());
});
Mediums mediums;
SingleThreadExecutor executor;
ScanManager scan_manager{mediums, *mock_credential_manager, executor};
BroadcastManager broadcast_manager{mediums, *mock_credential_manager,
executor};
auto service_controller = std::make_unique<ServiceControllerImpl>(
&executor, mock_credential_manager.get(), &scan_manager,
&broadcast_manager);
CredentialSelector credential_selector = BuildDefaultCredentialSelector();
absl::StatusOr<std::vector<::nearby::internal::LocalCredential>>
private_credentials;
service_controller->GetLocalCredentials(
credential_selector,
{.credentials_fetched_cb =
[&](absl::StatusOr<std::vector<::nearby::internal::LocalCredential>>
credentials) {
private_credentials = std::move(credentials);
}});
EXPECT_OK(private_credentials);
ASSERT_EQ(3u, private_credentials->size());
ASSERT_EQ(private_credentials->at(0).secret_id(), kSecretId1);
ASSERT_EQ(private_credentials->at(1).secret_id(), kSecretId2);
ASSERT_EQ(private_credentials->at(2).secret_id(), kSecretId3);
}
} // namespace
} // namespace presence
} // namespace nearby
-35
View File
@@ -1,35 +0,0 @@
// Copyright 2020 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_PRESENCE_POWER_MODE_H_
#define THIRD_PARTY_NEARBY_PRESENCE_POWER_MODE_H_
namespace nearby {
namespace presence {
// High level concept of Power mode for Scan and Broadcast.
// More frequent, more power consumption, but less interval and latency.
// Native platforms would decide the specific interval based on their own
// configs.
enum class PowerMode {
kNoPower = 0,
kLowPower = 1,
kBalanced = 2,
kLowLatency = 3,
};
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_POWER_MODE_H_
-31
View File
@@ -1,31 +0,0 @@
// Copyright 2020 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 "presence/presence_action.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace presence {
PresenceAction::PresenceAction(int action_identifier)
: action_identifier_(action_identifier) {
CHECK(kMinActionIdentifierValue <= action_identifier_ &&
action_identifier_ <= kMaxActionIdentifierValue);
}
int PresenceAction::GetActionIdentifier() const { return action_identifier_; }
} // namespace presence
} // namespace nearby
-41
View File
@@ -1,41 +0,0 @@
// Copyright 2020 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_PRESENCE_PRESENCE_ACTION_H_
#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ACTION_H_
namespace nearby {
namespace presence {
class PresenceAction {
public:
PresenceAction(int action_identifier = 1);
int GetActionIdentifier() const;
private:
static constexpr int kMinActionIdentifierValue = 1;
static constexpr int kMaxActionIdentifierValue = 255;
const int action_identifier_;
};
inline bool operator==(const PresenceAction& a1, const PresenceAction& a2) {
return a1.GetActionIdentifier() == a2.GetActionIdentifier();
}
inline bool operator!=(const PresenceAction& a1, const PresenceAction& a2) {
return !(a1 == a2);
}
} // namespace presence
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ACTION_H_

Some files were not shown because too many files have changed in this diff Show More