mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
remove unused code.
PiperOrigin-RevId: 911531448
This commit is contained in:
committed by
Copybara-Service
parent
353f7d5a1d
commit
6b2f1b96eb
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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",
|
||||
],
|
||||
}),
|
||||
)
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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",
|
||||
],
|
||||
}),
|
||||
)
|
||||
Generated
-25
@@ -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",
|
||||
]
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
Generated
-105
@@ -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"
|
||||
@@ -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_
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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",
|
||||
],
|
||||
}),
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 user’s 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 user’s own public credentials won’t 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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user