Internal change

PiperOrigin-RevId: 598901559
This commit is contained in:
hai007
2024-01-16 12:54:38 -08:00
committed by Copybara-Service
parent 4bdb5000ee
commit c40a65e492
122 changed files with 31541 additions and 19 deletions
+3 -5
View File
@@ -31,14 +31,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Disbale layering_check to work around ABSL build failure.
# see https://github.com/bazelbuild/bazel/issues/15359
- name: Build Connections
run: CC=clang CXX=clang++ bazel build --features=-layering_check //connections:core --spawn_strategy=standalone
run: CC=clang CXX=clang++ bazel build //connections:core --spawn_strategy=standalone
- name: Build Presence
run: CC=clang CXX=clang++ bazel build --features=-layering_check //presence --spawn_strategy=standalone
run: CC=clang CXX=clang++ bazel build //presence --spawn_strategy=standalone
- name: Build Sharing
run: CC=clang CXX=clang++ bazel build --features=-layering_check //sharing/proto:all //sharing/internal/public:nearby_context //sharing/common:all //sharing/scheduling:scheduling //sharing/fast_initiation:nearby_fast_initiation --spawn_strategy=standalone
run: CC=clang CXX=clang++ bazel build //sharing/proto/... //sharing/internal/public:nearby_context //sharing/common:all //sharing/scheduling //sharing/fast_initiation:nearby_fast_initiation //sharing/analytics --spawn_strategy=standalone
build-rust-linux:
name: Build Rust on Linux
+19 -10
View File
@@ -35,6 +35,12 @@ http_archive(
urls = ["https://github.com/bazelbuild/platforms/archive/4ad40ef271da8176d4fc0194d2089b8a76e19d7b.zip"],
)
http_archive(
name = "rules_cc",
strip_prefix = "rules_cc-0.0.9",
urls = ["https://github.com/bazelbuild/rules_cc/archive/refs/tags/0.0.9.tar.gz"],
)
http_archive(
name = "com_google_absl",
strip_prefix = "abseil-cpp-4038192a57cb75f7ee671f81a3378ff4c74c4f8e",
@@ -50,22 +56,29 @@ http_archive(
http_archive(
name = "com_google_protobuf",
strip_prefix = "protobuf-3.17.0",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.17.0.tar.gz"],
strip_prefix = "protobuf-3.19.6",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.6.tar.gz"],
)
http_archive(
name = "com_google_protobuf_cc",
strip_prefix = "protobuf-3.17.0",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.17.0.tar.gz"],
strip_prefix = "protobuf-3.19.6",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.6.tar.gz"],
)
http_archive(
name = "com_google_protobuf_java",
strip_prefix = "protobuf-3.17.0",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.17.0.tar.gz"],
strip_prefix = "protobuf-3.19.6",
urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.6.tar.gz"],
)
# Load common dependencies.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies", "rules_cc_toolchains")
rules_cc_dependencies()
rules_cc_toolchains()
http_archive(
name = "com_google_glog",
sha256 = "f28359aeba12f30d73d9e4711ef356dc842886968112162bc73002645139c39c",
@@ -112,10 +125,6 @@ cc_library(
],
)
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
# Load common dependencies.
protobuf_deps()
http_archive(
name = "com_google_googletest",
strip_prefix = "googletest-main",
+288
View File
@@ -0,0 +1,288 @@
licenses(["notice"])
cc_library(
name = "connection_types",
hdrs = ["nearby_connections_types.h"],
deps = [
"//internal/crypto_cros",
"//sharing/common:compatible_u8_string",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "types",
srcs = [
"advertisement.cc",
"attachment.cc",
"attachment_info.cc",
"file_attachment.cc",
"share_target.cc",
"text_attachment.cc",
"wifi_credentials_attachment.cc",
],
hdrs = [
"advertisement.h",
"attachment.h",
"attachment_info.h",
"file_attachment.h",
"nearby_connection.h",
"nearby_connections_manager.h",
"nearby_connections_types.h",
"nearby_sharing_decoder.h",
"share_target.h",
"text_attachment.h",
"transfer_metadata.h",
"transfer_metadata_builder.h",
"wifi_credentials_attachment.h",
],
visibility = [
"//location/nearby/cpp/sharing:__subpackages__",
"//sharing:__subpackages__",
],
deps = [
"//internal/crypto_cros",
"//internal/network:types",
"//sharing/common",
"//sharing/common:compatible_u8_string",
"//sharing/internal/base",
"//sharing/internal/public:logging",
"//sharing/proto:share_cc_proto",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "nearby_sharing_service",
srcs = [
"incoming_frames_reader.cc",
"incoming_share_target_info.cc",
"nearby_connection_impl.cc",
"nearby_connections_manager.cc",
"nearby_connections_manager_factory.cc",
"nearby_connections_manager_impl.cc",
"nearby_connections_service.cc",
"nearby_connections_service_impl.cc",
"nearby_connections_stream_buffer_manager.cc",
"nearby_file_handler.cc",
"nearby_share_profile_info_provider_impl.cc",
"nearby_sharing_decoder_impl.cc",
"nearby_sharing_event_logger.cc",
"nearby_sharing_service.cc",
"nearby_sharing_service_extension.cc",
"nearby_sharing_service_factory.cc",
"nearby_sharing_service_impl.cc",
"nearby_sharing_settings.cc",
"nearby_sharing_util.cc",
"outgoing_share_target_info.cc",
"paired_key_verification_runner.cc",
"payload_tracker.cc",
"share_target_info.cc",
"transfer_manager.cc",
"transfer_metadata.cc",
"transfer_metadata_builder.cc",
],
hdrs = [
"connection_lifecycle_listener.h",
"constants.h",
"endpoint_discovery_listener.h",
"incoming_frames_reader.h",
"incoming_share_target_info.h",
"nearby_connection_impl.h",
"nearby_connections_manager_factory.h",
"nearby_connections_manager_impl.h",
"nearby_connections_service.h",
"nearby_connections_service_impl.h",
"nearby_connections_stream_buffer_manager.h",
"nearby_file_handler.h",
"nearby_share_profile_info_provider_impl.h",
"nearby_sharing_decoder_impl.h",
"nearby_sharing_event_logger.h",
"nearby_sharing_service.h",
"nearby_sharing_service_extension.h",
"nearby_sharing_service_factory.h",
"nearby_sharing_service_impl.h",
"nearby_sharing_settings.h",
"nearby_sharing_util.h",
"outgoing_share_target_info.h",
"paired_key_verification_runner.h",
"payload_listener.h",
"payload_tracker.h",
"share_target_discovered_callback.h",
"share_target_info.h",
"transfer_manager.h",
"transfer_update_callback.h",
"//sharing/flags:nearby_sharing_feature_flags.h",
],
copts = [
"-DNEARBY_SHARING_DLL",
],
visibility = [
"//location/nearby/cpp/sharing:__subpackages__",
"//sharing:__subpackages__",
],
deps = [
":types",
"//connections:core",
"//connections:core_types",
"//connections/implementation:internal",
"//internal/analytics:event_logger",
"//internal/base",
"//internal/base:bluetooth_address",
"//internal/flags:flag_reader",
"//internal/flags:nearby_flags",
"//internal/network:nearby_http_client",
"//internal/network:types",
"//internal/platform:base",
"//internal/platform:types",
"//internal/platform/implementation:types",
"//proto:sharing_enums_cc_proto",
"//sharing/analytics",
"//sharing/certificates",
"//sharing/client",
"//sharing/common",
"//sharing/common:compatible_u8_string",
"//sharing/contacts",
"//sharing/fast_initiation:nearby_fast_initiation",
"//sharing/flags:nearby_sharing_feature_flags_cpp_consts_generated",
"//sharing/internal/api:platform",
"//sharing/internal/base",
"//sharing/internal/base:utf_utils",
"//sharing/internal/public:logging",
"//sharing/internal/public:nearby_context",
"//sharing/internal/public:types",
"//sharing/local_device_data",
"//sharing/proto:share_cc_proto",
"//sharing/scheduling",
"@com_google_absl//absl/algorithm:container",
"@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/meta:type_traits",
"@com_google_absl//absl/random",
"@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:span",
"@com_google_protobuf//:protobuf_lite",
],
)
cc_library(
name = "test_support",
testonly = True,
srcs = [
"fake_nearby_connection.cc",
"fake_nearby_connections_manager.cc",
"fake_nearby_sharing_service.cc",
],
hdrs = [
"fake_nearby_connection.h",
"fake_nearby_connections_manager.h",
"fake_nearby_sharing_service.h",
],
visibility = ["//visibility:public"],
deps = [
":nearby_sharing_service",
":types",
"//internal/base",
"//sharing/common:enum",
"//sharing/internal/public:logging",
"//sharing/local_device_data",
"//sharing/proto:share_cc_proto",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "nearby_connections_types_payload_test",
srcs = ["nearby_connections_types_payload_test.cc"],
deps = [
":connection_types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "nearby_sharing_service_test",
size = "small",
timeout = "short",
srcs = [
"fake_nearby_connections_service.h",
"incoming_frames_reader_test.cc",
"nearby_connection_impl_test.cc",
"nearby_connections_manager_impl_test.cc",
"nearby_connections_stream_buffer_manager_test.cc",
"nearby_connections_types_test.cc",
"nearby_file_handler_test.cc",
"nearby_share_profile_info_provider_impl_test.cc",
"nearby_sharing_event_logger_test.cc",
"nearby_sharing_service_extension_test.cc",
"nearby_sharing_service_impl_test.cc",
"nearby_sharing_service_test.cc",
"nearby_sharing_settings_test.cc",
"paired_key_verification_runner_test.cc",
"payload_tracker_test.cc",
"share_target_test.cc",
"text_attachment_test.cc",
"transfer_manager_test.cc",
"transfer_metadata_test.cc",
],
shard_count = 8,
deps = [
":nearby_sharing_service",
":test_support",
":types",
"//base:casts",
"//connections:core_types",
"//internal/account",
"//internal/analytics:event_logger",
"//internal/flags:nearby_flags",
"//internal/network:types",
"//internal/platform/implementation:types",
"//internal/platform/implementation/g3", # fixdeps: keep
"//internal/test",
"//proto:sharing_enums_cc_proto",
"//sharing/certificates",
"//sharing/certificates:test_support",
"//sharing/common",
"//sharing/common:compatible_u8_string",
"//sharing/contacts",
"//sharing/contacts:test_support",
"//sharing/fast_initiation:nearby_fast_initiation",
"//sharing/fast_initiation:test_support",
"//sharing/flags:nearby_sharing_feature_flags_cpp_consts_generated",
"//sharing/internal/api:mock_sharing_platform",
"//sharing/internal/api:platform",
"//sharing/internal/public:types",
"//sharing/internal/test:nearby_test",
"//sharing/local_device_data",
"//sharing/local_device_data:test_support",
"//sharing/proto:share_cc_proto",
"//sharing/proto/analytics:sharing_log_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
"@com_google_protobuf//:protobuf_lite",
],
)
+219
View File
@@ -0,0 +1,219 @@
// 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 "sharing/advertisement.h"
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/types/span.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace sharing {
namespace {
// v1 advertisements:
// - ParseVersion() --> 0
//
// v2 advertisements:
// - ParseVersion() --> 1
// - Backwards compatible; no changes in advertisement data--aside from the
// version number--or parsing logic compared to v1.
// - Only used by GmsCore at the moment.
constexpr int kMaxSupportedAdvertisementParsedVersionNumber = 1;
// The bit mask for parsing and writing Version.
constexpr uint8_t kVersionBitmask = 0b111;
// The bit mask for parsing and writing Visibility.
constexpr uint8_t kVisibilityBitmask = 0b1;
// The bit mask for parsing and writing Device Type.
constexpr uint8_t kDeviceTypeBitmask = 0b111;
const uint8_t kMinimumSize =
/* Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)=
*/
1 + sharing::Advertisement::kSaltSize +
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize;
uint8_t ConvertVersion(int version) {
return static_cast<uint8_t>((version & kVersionBitmask) << 5);
}
uint8_t ConvertDeviceType(ShareTargetType type) {
return static_cast<uint8_t>((static_cast<int32_t>(type) & kDeviceTypeBitmask)
<< 1);
}
uint8_t ConvertHasDeviceName(bool hasDeviceName) {
return static_cast<uint8_t>((hasDeviceName ? 0 : 1) << 4);
}
int ParseVersion(uint8_t b) { return (b >> 5) & kVersionBitmask; }
bool IsKnownDeviceValue(int32_t value) {
switch (value) {
case 0:
case 1:
case 2:
case 3:
return true;
default:
return false;
}
}
ShareTargetType ParseDeviceType(uint8_t b) {
int32_t intermediate = static_cast<int32_t>(b >> 1 & kDeviceTypeBitmask);
if (IsKnownDeviceValue(intermediate)) {
return static_cast<ShareTargetType>(intermediate);
}
return ShareTargetType::kUnknown;
}
bool ParseHasDeviceName(uint8_t b) {
return ((b >> 4) & kVisibilityBitmask) == 0;
}
} // namespace
// static
std::unique_ptr<Advertisement> Advertisement::NewInstance(
std::vector<uint8_t> salt, std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type, std::optional<std::string> device_name) {
if (salt.size() != Advertisement::kSaltSize) {
NL_LOG(ERROR) << "Failed to create advertisement because the salt did "
"not match the expected length "
<< salt.size();
return nullptr;
}
if (encrypted_metadata_key.size() !=
Advertisement::kMetadataEncryptionKeyHashByteSize) {
NL_LOG(ERROR) << "Failed to create advertisement because the encrypted "
"metadata key did "
"not match the expected length "
<< encrypted_metadata_key.size();
return nullptr;
}
if (device_name.has_value() && device_name->size() > UINT8_MAX) {
NL_LOG(ERROR) << "Failed to create advertisement because device name "
"was over UINT8_MAX: "
<< device_name->size();
return nullptr;
}
// Using `new` to access a non-public constructor.
return std::make_unique<Advertisement>(
/* version= */ 0, std::move(salt), std::move(encrypted_metadata_key),
device_type, std::move(device_name));
}
std::vector<uint8_t> Advertisement::ToEndpointInfo() {
int size = kMinimumSize + (device_name_.has_value() ? 1 : 0) +
(device_name_.has_value() ? device_name_->size() : 0);
std::vector<uint8_t> endpoint_info;
endpoint_info.reserve(size);
endpoint_info.push_back(
static_cast<uint8_t>(ConvertVersion(version_) |
ConvertHasDeviceName(device_name_.has_value()) |
ConvertDeviceType(device_type_)));
endpoint_info.insert(endpoint_info.end(), salt_.begin(), salt_.end());
endpoint_info.insert(endpoint_info.end(), encrypted_metadata_key_.begin(),
encrypted_metadata_key_.end());
if (device_name_.has_value()) {
endpoint_info.push_back(static_cast<uint8_t>(device_name_->size() & 0xff));
endpoint_info.insert(endpoint_info.end(), device_name_->begin(),
device_name_->end());
}
return endpoint_info;
}
std::unique_ptr<Advertisement> Advertisement::FromEndpointInfo(
absl::Span<const uint8_t> endpoint_info) {
if (endpoint_info.size() < kMinimumSize) {
NL_LOG(ERROR) << "Failed to parse advertisement because it was too short.";
return nullptr;
}
auto iter = endpoint_info.begin();
uint8_t first_byte = *iter++;
int version = ParseVersion(first_byte);
if (version < 0 || version > kMaxSupportedAdvertisementParsedVersionNumber) {
NL_LOG(ERROR)
<< "Failed to parse advertisement; unsupported version number "
<< version;
return nullptr;
}
bool has_device_name = ParseHasDeviceName(first_byte);
ShareTargetType device_type = ParseDeviceType(first_byte);
std::vector<uint8_t> salt(iter, iter + Advertisement::kSaltSize);
iter += Advertisement::kSaltSize;
std::vector<uint8_t> encrypted_metadata_key(
iter, iter + Advertisement::kMetadataEncryptionKeyHashByteSize);
iter += Advertisement::kMetadataEncryptionKeyHashByteSize;
int device_name_length = 0;
if (iter != endpoint_info.end()) device_name_length = *iter++ & 0xff;
if (endpoint_info.end() - iter < device_name_length ||
(device_name_length == 0 && has_device_name)) {
NL_LOG(ERROR)
<< "Failed to parse advertisement because the device name did "
"not match the expected length "
<< device_name_length;
return nullptr;
}
std::optional<std::string> optional_device_name;
if (device_name_length > 0) {
optional_device_name = std::string(iter, iter + device_name_length);
iter += device_name_length;
}
return Advertisement::NewInstance(
std::move(salt), std::move(encrypted_metadata_key), device_type,
std::move(optional_device_name));
}
// private
Advertisement::Advertisement(int version, std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type,
std::optional<std::string> device_name)
: version_(version),
salt_(std::move(salt)),
encrypted_metadata_key_(std::move(encrypted_metadata_key)),
device_type_(device_type),
device_name_(std::move(device_name)) {}
} // namespace sharing
} // namespace nearby
+93
View File
@@ -0,0 +1,93 @@
// 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_SHARING_ADVERTISEMENT_H_
#define THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/types/span.h"
#include "sharing/common/nearby_share_enums.h"
namespace nearby {
namespace sharing {
// An advertisement in the form of
// [VERSION|VISIBILITY][SALT][ACCOUNT_IDENTIFIER][LEN][DEVICE_NAME].
// A device name indicates the advertisement is visible to everyone;
// a missing device name indicates the advertisement is contacts-only.
class Advertisement {
public:
static constexpr uint8_t kSaltSize = 2;
static constexpr uint8_t kMetadataEncryptionKeyHashByteSize = 14;
static std::unique_ptr<Advertisement> NewInstance(
std::vector<uint8_t> salt, std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type, std::optional<std::string> device_name);
Advertisement(int version, std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
ShareTargetType device_type,
std::optional<std::string> device_name);
~Advertisement() = default;
Advertisement(const Advertisement&) = default;
Advertisement& operator=(const Advertisement&) = default;
Advertisement(Advertisement&&) = default;
Advertisement& operator=(Advertisement&&) = default;
std::vector<uint8_t> ToEndpointInfo();
int version() const { return version_; }
const std::vector<uint8_t>& salt() const { return salt_; }
const std::vector<uint8_t>& encrypted_metadata_key() const {
return encrypted_metadata_key_;
}
ShareTargetType device_type() const { return device_type_; }
const std::optional<std::string>& device_name() const { return device_name_; }
bool HasDeviceName() const { return device_name_.has_value(); }
static std::unique_ptr<Advertisement> FromEndpointInfo(
absl::Span<const uint8_t> endpoint_info);
private:
// The version of the advertisement. Different versions can have different
// ways of parsing the endpoint id.
int version_;
// Random bytes that were used as salt during encryption of public certificate
// metadata.
std::vector<uint8_t> salt_ = {};
// An encrypted symmetric key that was used to encrypt public certificate
// metadata, including an account identifier signifying the remote device.
// The key can be decrypted using |salt| and the corresponding public
// certificate's secret/authenticity key.
std::vector<uint8_t> encrypted_metadata_key_ = {};
// The type of device that the advertisement identifies.
ShareTargetType device_type_ = ShareTargetType::kUnknown;
// The human-readable name of the remote device.
std::optional<std::string> device_name_ = std::nullopt;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ADVERTISEMENT_H_
+61
View File
@@ -0,0 +1,61 @@
# 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.
licenses(["notice"])
cc_library(
name = "analytics",
srcs = [
"analytics_recorder.cc",
],
hdrs = [
"analytics_device_settings.h",
"analytics_information.h",
"analytics_recorder.h",
],
visibility = ["//visibility:public"],
deps = [
"//internal/analytics:event_logger",
"//proto:sharing_enums_cc_proto",
"//sharing:types",
"//sharing/common",
"//sharing/internal/public:logging",
"//sharing/proto:share_cc_proto",
"//sharing/proto/analytics:sharing_log_cc_proto",
"@com_google_absl//absl/random",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//:protobuf_lite",
],
)
cc_test(
name = "analytics_test",
srcs = ["analytics_recorder_test.cc"],
deps = [
":analytics",
"//internal/analytics:event_logger",
"//internal/platform/implementation/g3", # fixdeps: keep
"//proto:sharing_enums_cc_proto",
"//sharing:types",
"//sharing/common",
"//sharing/proto:share_cc_proto",
"//sharing/proto/analytics:sharing_log_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//:protobuf_lite",
],
)
@@ -0,0 +1,36 @@
// 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_SHARING_ANALYTICS_ANALYTICS_DEVICE_SETTINGS_H_
#define THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_DEVICE_SETTINGS_H_
#include "sharing/common/nearby_share_enums.h"
#include "sharing/proto/enums.pb.h"
namespace nearby {
namespace sharing {
namespace analytics {
struct AnalyticsDeviceSettings {
bool is_fast_init_notification_enabled;
int device_name_size;
::nearby::sharing::proto::DataUsage data_usage;
::nearby::sharing::proto::DeviceVisibility visibility;
};
} // namespace analytics
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_DEVICE_SETTINGS_H_
+47
View File
@@ -0,0 +1,47 @@
// 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_SHARING_ANALYTICS_ANALYTICS_INFORMATION_H_
#define THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_INFORMATION_H_
namespace nearby {
namespace sharing {
namespace analytics {
enum class SendSurfaceState : int {
// Default, invalid state.
kUnknown = -1,
// Background share sheet only listens to transfer update.
kBackground = 0,
// Foreground share sheet both scans and listens to transfer update.
kForeground = 1,
// Direct share service only scans and listens to share target onFound and
// onLost.
kDirectShareService = 2,
// Foreground share sheet both scans and listens to transfer update, but does
// not trigger FastInit HUN.
kForegroundRetry = 3,
// A foreground surface that is registered from 2nd and 3rd party apps.
kExternal = 4,
};
struct AnalyticsInformation {
SendSurfaceState send_surface_state;
};
} // namespace analytics
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_INFORMATION_H_
File diff suppressed because it is too large Load Diff
+225
View File
@@ -0,0 +1,225 @@
// Copyright 2022-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_SHARING_ANALYTICS_ANALYTICS_RECORDER_H_
#define THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_RECORDER_H_
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "internal/analytics/event_logger.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/analytics/analytics_device_settings.h"
#include "sharing/analytics/analytics_information.h"
#include "sharing/attachment.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/proto/analytics/nearby_sharing_log.pb.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/share_target.h"
#include "google/protobuf/message_lite.h"
namespace nearby {
namespace sharing {
namespace analytics {
class AnalyticsRecorder {
public:
explicit AnalyticsRecorder(::nearby::analytics::EventLogger* event_logger)
: event_logger_(event_logger) {}
~AnalyticsRecorder() = default;
void NewEstablishConnection(
int64_t session_id,
::location::nearby::proto::sharing::EstablishConnectionStatus
connection_status,
ShareTarget share_target, int transfer_position,
int concurrent_connections, int64_t duration_millis,
std::optional<std::string> referrer_package);
void NewAcceptAgreements();
void NewDeclineAgreements();
void NewAddContact();
void NewRemoveContact();
void NewTapFeedback();
void NewTapHelp();
void NewLaunchDeviceContactConsent(
::location::nearby::proto::sharing::ConsentAcceptanceStatus status);
void NewAdvertiseDevicePresenceEnd(int64_t session_id);
void NewAdvertiseDevicePresenceStart(
int64_t session_id, ::nearby::sharing::proto::DeviceVisibility visibility,
::location::nearby::proto::sharing::SessionStatus status,
::nearby::sharing::proto::DataUsage data_usage,
std::optional<std::string> referrer_package);
void NewDescribeAttachments(
const std::vector<std::unique_ptr<Attachment>>& attachments);
void NewDiscoverShareTarget(
ShareTarget share_target, int64_t session_id,
int64_t latency_since_scanning_start_millis, int64_t flow_id,
std::optional<std::string> referrer_package,
int64_t latency_since_send_surface_registered_millis);
void NewEnableNearbySharing(
::location::nearby::proto::sharing::NearbySharingStatus status);
void NewOpenReceivedAttachments(
const std::vector<std::unique_ptr<Attachment>>& attachments,
int64_t session_id);
void NewProcessReceivedAttachmentsEnd(
int64_t session_id,
::location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus
status);
void NewReceiveAttachmentsEnd(
int64_t session_id, int64_t received_bytes,
::location::nearby::proto::sharing::AttachmentTransmissionStatus status,
std::optional<std::string> referrer_package);
void NewReceiveAttachmentsStart(
int64_t session_id,
const std::vector<std::unique_ptr<Attachment>>& attachments);
void NewReceiveFastInitialization(int64_t timeElapseSinceScreenUnlockMillis);
void NewAcceptFastInitialization();
void NewDismissFastInitialization();
void NewReceiveIntroduction(
int64_t session_id, ShareTarget share_target,
std::optional<std::string> referrer_package,
::location::nearby::proto::sharing::OSType share_target_os_type);
void NewRespondToIntroduction(
::location::nearby::proto::sharing::ResponseToIntroduction action,
int64_t session_id);
void NewTapPrivacyNotification();
void NewDismissPrivacyNotification();
void NewScanForShareTargetsEnd(int64_t session_id);
void NewScanForShareTargetsStart(
int64_t session_id,
::location::nearby::proto::sharing::SessionStatus status,
AnalyticsInformation analytics_information, int64_t flow_id,
std::optional<std::string> referrer_package);
void NewSendAttachmentsEnd(
int64_t session_id, int64_t sent_bytes, ShareTarget share_target,
::location::nearby::proto::sharing::AttachmentTransmissionStatus status,
int transfer_position, int concurrent_connections,
int64_t duration_millis, std::optional<std::string> referrer_package,
::location::nearby::proto::sharing::ConnectionLayerStatus
connection_layer_status,
::location::nearby::proto::sharing::OSType share_target_os_type);
void NewSendAttachmentsStart(
int64_t session_id,
const std::vector<std::unique_ptr<Attachment>>& attachments,
int transfer_position, int concurrent_connections);
void NewSendFastInitialization();
void NewSendStart(int64_t session_id, int transfer_position,
int concurrent_connections, ShareTarget share_target);
void NewSendIntroduction(
ShareTargetType target_type, int64_t session_id,
::location::nearby::proto::sharing::DeviceRelationship relationship,
::location::nearby::proto::sharing::OSType share_target_os_type);
void NewSendIntroduction(
int64_t session_id, ShareTarget share_target, int transfer_position,
int concurrent_connections,
::location::nearby::proto::sharing::OSType share_target_os_type);
void NewSetVisibility(
::nearby::sharing::proto::DeviceVisibility src_visibility,
::nearby::sharing::proto::DeviceVisibility dst_visibility,
int64_t duration_millis);
void NewDeviceSettings(AnalyticsDeviceSettings settings);
void NewFastShareServerResponse(
::location::nearby::proto::sharing::ServerActionName name,
::location::nearby::proto::sharing::ServerResponseState state,
int64_t latency_millis);
void NewSetDataUsage(::nearby::sharing::proto::DataUsage original_preference,
::nearby::sharing::proto::DataUsage preference);
void NewAddQuickSettingsTile();
void NewRemoveQuickSettingsTile();
void NewTapQuickSettingsTile();
void NewToggleShowNotification(
::location::nearby::proto::sharing::ShowNotificationStatus prev_status,
::location::nearby::proto::sharing::ShowNotificationStatus
current_status);
void NewSetDeviceName(int device_name_size);
void NewRequestSettingPermissions(
::location::nearby::proto::sharing::PermissionRequestType type,
::location::nearby::proto::sharing::PermissionRequestResult result);
void NewInstallAPKStatus(
::location::nearby::proto::sharing::InstallAPKStatus status,
::location::nearby::proto::sharing::ApkSource source);
void NewVerifyAPKStatus(
::location::nearby::proto::sharing::VerifyAPKStatus status,
::location::nearby::proto::sharing::ApkSource source);
void NewSendDesktopNotification(
::location::nearby::proto::sharing::DesktopNotification event);
void NewSendDesktopTransferEvent(
::location::nearby::proto::sharing::DesktopTransferEventType event);
// Generates a random number for session ID or flow ID.
int64_t GenerateNextId();
private:
std::unique_ptr<::nearby::sharing::analytics::proto::SharingLog>
CreateSharingLog(
::location::nearby::proto::sharing::EventCategory event_category,
::location::nearby::proto::sharing::EventType event_type);
void LogEvent(const ::google::protobuf::MessageLite& message);
::nearby::analytics::EventLogger* event_logger_ = nullptr;
};
} // namespace analytics
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_RECORDER_H_
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
// 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 "sharing/attachment.h"
#include <stdint.h>
#include "internal/crypto_cros/random.h"
namespace nearby {
namespace sharing {
namespace {
int64_t CreateRandomId() {
int64_t id;
crypto::RandBytes(&id, sizeof(id));
return id;
}
} // namespace
// TODO(b/258690183): Add unit tests for Attachment with same and different ids
Attachment::Attachment(Attachment::Family family, int64_t size,
int32_t batch_id, SourceType source_type)
: id_(CreateRandomId()),
family_(family),
size_(size),
batch_id_(batch_id),
source_type_(source_type) {}
Attachment::Attachment(int64_t id, Attachment::Family family, int64_t size,
int32_t batch_id, SourceType source_type)
: id_(id),
family_(family),
size_(size),
batch_id_(batch_id),
source_type_(source_type) {}
Attachment::Attachment(const Attachment&) = default;
Attachment::Attachment(Attachment&&) = default;
Attachment& Attachment::operator=(const Attachment&) = default;
Attachment& Attachment::operator=(Attachment&&) = default;
Attachment::~Attachment() = default;
} // namespace sharing
} // namespace nearby
+84
View File
@@ -0,0 +1,84 @@
// 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_SHARING_ATTACHMENT_H_
#define THIRD_PARTY_NEARBY_SHARING_ATTACHMENT_H_
#include <cstdint>
#include "absl/strings/string_view.h"
#include "sharing/common/nearby_share_enums.h"
namespace nearby {
namespace sharing {
struct ShareTarget;
// A single attachment to be sent by / received from a ShareTarget, can be
// either a file or text.
class Attachment {
public:
enum class Family {
kFile,
kText,
kWifiCredentials,
kMaxValue = kWifiCredentials
};
// TODO(b/269353084): Auto-generate codes when a new source type in
// sharing_enums.proto is added.
enum class SourceType {
kUnknown,
kContextMenu,
kDragAndDrop,
kSelectFilesButton,
kPaste,
kSelectFoldersButton,
kMaxValue = kSelectFoldersButton
};
Attachment(Family family, int64_t size, int32_t batch_id,
SourceType source_type);
Attachment(int64_t id, Family family, int64_t size, int32_t batch_id,
SourceType source_type);
Attachment(const Attachment&);
Attachment(Attachment&&);
Attachment& operator=(const Attachment&);
Attachment& operator=(Attachment&&);
virtual ~Attachment();
int64_t id() const { return id_; }
Family family() const { return family_; }
int64_t size() const { return size_; }
void set_size(int64_t size) { size_ = size; }
int32_t batch_id() const { return batch_id_; }
SourceType source_type() const { return source_type_; }
// Move the attachment to share target.
virtual void MoveToShareTarget(ShareTarget& share_target) = 0;
virtual absl::string_view GetDescription() const = 0;
virtual ShareType GetShareType() const = 0;
private:
int64_t id_;
Family family_;
int64_t size_;
int32_t batch_id_;
SourceType source_type_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ATTACHMENT_H_
+29
View File
@@ -0,0 +1,29 @@
// 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 "sharing/attachment_info.h"
#include <stdint.h>
namespace nearby {
namespace sharing {
AttachmentInfo::AttachmentInfo() = default;
AttachmentInfo::~AttachmentInfo() = default;
AttachmentInfo::AttachmentInfo(AttachmentInfo&&) = default;
AttachmentInfo& AttachmentInfo::operator=(AttachmentInfo&&) = default;
} // namespace sharing
} // namespace nearby
+43
View File
@@ -0,0 +1,43 @@
// 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_SHARING_ATTACHMENT_INFO_H_
#define THIRD_PARTY_NEARBY_SHARING_ATTACHMENT_INFO_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <optional>
#include <string>
namespace nearby {
namespace sharing {
// Ties associated information to an Attachment.
struct AttachmentInfo {
AttachmentInfo();
~AttachmentInfo();
AttachmentInfo(AttachmentInfo&&);
AttachmentInfo& operator=(AttachmentInfo&&);
std::optional<int64_t> payload_id;
std::string text_body;
std::filesystem::path file_path;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ATTACHMENT_INFO_H_
+81
View File
@@ -0,0 +1,81 @@
// 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_SHARING_CONNECTION_LIFECYCLE_LISTENER_H_
#define THIRD_PARTY_NEARBY_SHARING_CONNECTION_LIFECYCLE_LISTENER_H_
#include "absl/strings/string_view.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
// Listener for lifecycle events associated with a connection to a remote
// endpoint. Methods in this interface are called from the utility process, and
// are used by the browser process to listen for connection status associated
// with remote endpoints.
class ConnectionLifecycleListener {
public:
virtual ~ConnectionLifecycleListener() = default;
// A basic encrypted channel has been created between this device and the
// remote endpoint. Both sides are now asked if they wish to accept or
// reject the connection before any data can be sent over this channel.
//
// Optionally, the caller can verify if this device is connected to the
// correct remote before accepting the connection. Typically, this involves
// showing ConnectionInfo::authentication_token on both devices and having the
// users manually compare and confirm. Both devices are given an identical
// authentication token.
//
// Call NearbyConnections::AcceptConnection() to accept the connection, or
// NearbyConnections::RejectConnection() to close the connection.
//
// endpoint_id - The identifier for the remote endpoint.
// info - Other relevant information about the connection.
virtual void OnConnectionInitiated(absl::string_view endpoint_id,
ConnectionInfo info) = 0;
// Called after both sides have accepted the connection.
//
// endpoint_id - The identifier for the remote endpoint.
virtual void OnConnectionAccepted(absl::string_view endpoint_id) = 0;
// Called when either side rejected the connection.
// Call NearbyConnections::DisconnectFromEndpoint() to terminate connection.
//
// endpoint_id - The identifier for the remote endpoint.
// status - The result of the connection. Valid values are
// Status::kSuccess and Status::kConnectionRejected}.
virtual void OnConnectionRejected(absl::string_view endpoint_id,
Status status) = 0;
// Called when a remote endpoint is disconnected or has become unreachable.
// At this point service (re-)discovery may start again.
//
// endpoint_id - The identifier for the remote endpoint.
virtual void OnDisconnected(absl::string_view endpoint_id) = 0;
// Called when the connection's available bandwidth has changed.
//
// endpoint_id - The identifier for the remote endpoint.
// quality - The new quality for the connection.
virtual void OnBandwidthChanged(absl::string_view endpoint_id,
Medium medium) = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_CONNECTION_LIFECYCLE_LISTENER_H_
+73
View File
@@ -0,0 +1,73 @@
// 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_SHARING_CONSTANTS_H_
#define THIRD_PARTY_NEARBY_SHARING_CONSTANTS_H_
#include <cstdint>
#include "absl/time/time.h"
namespace nearby {
namespace sharing {
// Timeout for reading a response frame from the remote device.
constexpr absl::Duration kReadResponseFrameTimeout = absl::Seconds(60);
// Timeout for initiating a connection to a remote device.
constexpr absl::Duration kInitiateNearbyConnectionTimeout = absl::Seconds(60);
// The delay before the sender will disconnect from the receiver after sending a
// file. Note that the receiver is expected to immediately disconnect, so this
// delay is a worst-effort disconnection. Disconnecting too early might
// interrupt in flight packets, especially over Wi-Fi LAN.
constexpr absl::Duration kOutgoingDisconnectionDelay = absl::Seconds(60);
// The delay before the receiver will disconnect from the sender after rejecting
// an incoming file. The sender is expected to disconnect immediately after
// reading the rejection frame.
constexpr absl::Duration kIncomingRejectionDelay = absl::Seconds(2);
// The delay before the initiator of the cancellation will disconnect from the
// other device. The device that did not initiate the cancellation is expected
// to disconnect immediately after reading the cancellation frame.
constexpr absl::Duration kInitiatorCancelDelay = absl::Seconds(5);
// Timeout for reading a frame from the remote device.
constexpr absl::Duration kReadFramesTimeout = absl::Seconds(15);
// Time to delay running the task to invalidate send and receive surfaces.
constexpr absl::Duration kInvalidateDelay = absl::Milliseconds(500);
// Time between successive progress updates.
constexpr absl::Duration kMinProgressUpdateFrequency = absl::Milliseconds(100);
// Attachments size threshold for transferring high quality medium. The default
// value is 1MB to match the default setting on Android.
constexpr int64_t kAttachmentsSizeThresholdOverHighQualityMedium = 1000000;
// If true, the user will be able to accept incoming Wi-Fi Credential
// attachments and join the network when the attachment is opened.
constexpr bool kSupportReceivingWifiCredentials = true;
// Time between successive instantaneous transfer speed in seconds.
constexpr double kTransferSpeedUpdateInterval = 1.0;
// Time between successive transfer completion ETA in seconds.
constexpr double kEstimatedTimeRemainingUpdateInterval = 3.0;
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_CONSTANTS_H_
+50
View File
@@ -0,0 +1,50 @@
// 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_SHARING_ENDPOINT_DISCOVERY_LISTENER_H_
#define THIRD_PARTY_NEARBY_SHARING_ENDPOINT_DISCOVERY_LISTENER_H_
#include "absl/strings/string_view.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
// Listener invoked during endpoint discovery. This interface is used by the
// browser process to listen for a remote endpoint's status during endpoint
// discovery.
class EndpointDiscoveryListener {
public:
virtual ~EndpointDiscoveryListener() = default;
// Called when a remote endpoint is discovered.
//
// endpoint_id - The ID of the remote endpoint that was discovered.
// info - Further information about the remote endpoint which may
// include the human-readable name if it is advertising in high
// visibility mode.
virtual void OnEndpointFound(absl::string_view endpoint_id,
DiscoveredEndpointInfo& info) = 0;
// Called when a remote endpoint is no longer discoverable; only called for
// endpoints that previously had been passed to OnEndpointFound().
//
// endpoint_id - The ID of the remote endpoint that was lost.
virtual void OnEndpointLost(absl::string_view endpoint_id) = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_ENDPOINT_DISCOVERY_LISTENER_H_
+94
View File
@@ -0,0 +1,94 @@
// 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 "sharing/fake_nearby_connection.h"
#include <stdint.h>
#include <functional>
#include <optional>
#include <queue>
#include <utility>
#include <vector>
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connection.h"
namespace nearby {
namespace sharing {
FakeNearbyConnection::FakeNearbyConnection() = default;
FakeNearbyConnection::~FakeNearbyConnection() = default;
void FakeNearbyConnection::Read(ReadCallback callback) {
NL_DCHECK(!closed_);
callback_ = std::move(callback);
MaybeRunCallback();
}
void FakeNearbyConnection::Write(std::vector<uint8_t> bytes) {
NL_DCHECK(!closed_);
write_data_.push(std::move(bytes));
}
void FakeNearbyConnection::Close() {
NL_DCHECK(!closed_);
closed_ = true;
if (disconnect_listener_) {
std::move(disconnect_listener_)();
}
if (callback_) {
has_read_callback_been_run_ = true;
auto callback = std::move(callback_);
callback_ = nullptr;
callback(std::nullopt);
}
}
void FakeNearbyConnection::SetDisconnectionListener(
std::function<void()> listener) {
NL_DCHECK(!closed_);
disconnect_listener_ = std::move(listener);
}
void FakeNearbyConnection::AppendReadableData(std::vector<uint8_t> bytes) {
NL_DCHECK(!closed_);
read_data_.push(std::move(bytes));
MaybeRunCallback();
}
std::vector<uint8_t> FakeNearbyConnection::GetWrittenData() {
if (write_data_.empty()) return {};
std::vector<uint8_t> bytes = std::move(write_data_.front());
write_data_.pop();
return bytes;
}
bool FakeNearbyConnection::IsClosed() { return closed_; }
void FakeNearbyConnection::MaybeRunCallback() {
NL_DCHECK(!closed_);
if (!callback_ || read_data_.empty()) return;
auto item = std::move(read_data_.front());
read_data_.pop();
has_read_callback_been_run_ = true;
auto callback = std::move(callback_);
callback_ = nullptr;
callback(std::move(item));
}
} // namespace sharing
} // namespace nearby
+61
View File
@@ -0,0 +1,61 @@
// 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_SHARING_FAKE_NEARBY_CONNECTION_H_
#define THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTION_H_
#include <stdint.h>
#include <functional>
#include <queue>
#include <vector>
#include "sharing/nearby_connection.h"
namespace nearby {
namespace sharing {
class FakeNearbyConnection : public NearbyConnection {
public:
FakeNearbyConnection();
~FakeNearbyConnection() override;
// NearbyConnection:
void Read(ReadCallback callback) override;
void Write(std::vector<uint8_t> bytes) override;
void Close() override;
void SetDisconnectionListener(std::function<void()> listener) override;
void AppendReadableData(std::vector<uint8_t> bytes);
std::vector<uint8_t> GetWrittenData();
bool IsClosed();
bool has_read_callback_been_run() { return has_read_callback_been_run_; }
private:
void MaybeRunCallback();
bool closed_ = false;
bool has_read_callback_been_run_ = false;
ReadCallback callback_;
std::queue<std::vector<uint8_t>> read_data_;
std::queue<std::vector<uint8_t>> write_data_;
std::function<void()> disconnect_listener_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTION_H_
+317
View File
@@ -0,0 +1,317 @@
// 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 "sharing/fake_nearby_connections_manager.h"
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/proto/enums.pb.h"
namespace nearby {
namespace sharing {
using DataUsage = ::nearby::sharing::proto::DataUsage;
FakeNearbyConnectionsManager::FakeNearbyConnectionsManager() = default;
FakeNearbyConnectionsManager::~FakeNearbyConnectionsManager() = default;
void FakeNearbyConnectionsManager::Shutdown() {
NL_DCHECK(!IsAdvertising());
NL_DCHECK(!IsDiscovering());
is_shutdown_ = true;
}
void FakeNearbyConnectionsManager::StartAdvertising(
std::vector<uint8_t> endpoint_info, IncomingConnectionListener* listener,
PowerLevel power_level, DataUsage data_usage,
ConnectionsCallback callback) {
NL_DCHECK(!IsAdvertising());
is_shutdown_ = false;
advertising_listener_ = listener;
advertising_data_usage_ = data_usage;
advertising_power_level_ = power_level;
advertising_endpoint_info_ = std::move(endpoint_info);
if (capture_next_start_advertising_callback_) {
pending_start_advertising_callback_ = std::move(callback);
capture_next_start_advertising_callback_ = false;
} else {
std::move(callback)(Status::kSuccess);
}
}
void FakeNearbyConnectionsManager::StopAdvertising(
ConnectionsCallback callback) {
NL_DCHECK(IsAdvertising());
NL_DCHECK(!is_shutdown());
advertising_listener_ = nullptr;
advertising_data_usage_ = DataUsage::UNKNOWN_DATA_USAGE;
advertising_power_level_ = PowerLevel::kUnknown;
advertising_endpoint_info_.reset();
if (capture_next_stop_advertising_callback_) {
pending_stop_advertising_callback_ = std::move(callback);
capture_next_stop_advertising_callback_ = false;
} else {
std::move(callback)(Status::kSuccess);
}
}
void FakeNearbyConnectionsManager::StartDiscovery(
DiscoveryListener* listener, DataUsage data_usage,
ConnectionsCallback callback) {
is_shutdown_ = false;
discovery_listener_ = listener;
std::move(callback)(Status::kSuccess);
}
void FakeNearbyConnectionsManager::StopDiscovery() {
NL_DCHECK(IsDiscovering());
NL_DCHECK(!is_shutdown());
discovery_listener_ = nullptr;
}
void FakeNearbyConnectionsManager::Connect(
std::vector<uint8_t> endpoint_info, absl::string_view endpoint_id,
std::optional<std::vector<uint8_t>> bluetooth_mac_address,
DataUsage data_usage, TransportType transport_type,
NearbyConnectionCallback callback) {
NL_DCHECK(!is_shutdown());
connected_data_usage_ = data_usage;
transport_type_ = transport_type;
connection_endpoint_infos_.emplace(endpoint_id, std::move(endpoint_info));
std::move(callback)(connection_, Status::kUnknown);
}
void FakeNearbyConnectionsManager::Disconnect(absl::string_view endpoint_id) {
NL_DCHECK(!is_shutdown());
connection_endpoint_infos_.erase(std::string(endpoint_id));
}
void FakeNearbyConnectionsManager::Send(
absl::string_view endpoint_id, std::unique_ptr<Payload> payload,
std::weak_ptr<PayloadStatusListener> listener) {
NL_DCHECK(!is_shutdown());
if (send_payload_callback_)
send_payload_callback_(std::move(payload), listener);
}
void FakeNearbyConnectionsManager::RegisterPayloadStatusListener(
int64_t payload_id, std::weak_ptr<PayloadStatusListener> listener) {
NL_DCHECK(!is_shutdown());
payload_status_listeners_[payload_id] = listener;
}
void FakeNearbyConnectionsManager::RegisterPayloadPath(
int64_t payload_id, const std::filesystem::path& file_path,
ConnectionsCallback callback) {
NL_DCHECK(!is_shutdown());
registered_payload_paths_[payload_id] = file_path;
auto it = payload_path_status_.find(payload_id);
if (it == payload_path_status_.end()) {
std::move(callback)(nearby::sharing::Status::kPayloadUnknown);
return;
}
std::move(callback)(it->second);
}
Payload* FakeNearbyConnectionsManager::GetIncomingPayload(int64_t payload_id) {
NL_DCHECK(!is_shutdown());
auto it = incoming_payloads_.find(payload_id);
if (it == incoming_payloads_.end()) return nullptr;
return it->second.get();
}
void FakeNearbyConnectionsManager::Cancel(int64_t payload_id) {
NL_DCHECK(!is_shutdown());
std::weak_ptr<PayloadStatusListener> listener =
GetRegisteredPayloadStatusListener(payload_id);
if (auto weak_listener = listener.lock()) {
auto status_update = std::make_unique<PayloadTransferUpdate>();
status_update->payload_id = payload_id;
status_update->status = PayloadStatus::kCanceled;
status_update->total_bytes = 0;
status_update->bytes_transferred = 0;
weak_listener->OnStatusUpdate(std::move(status_update),
/*upgraded_medium=*/std::nullopt);
payload_status_listeners_.erase(payload_id);
}
canceled_payload_ids_.insert(payload_id);
}
void FakeNearbyConnectionsManager::ClearIncomingPayloads() {
incoming_payloads_.clear();
payload_status_listeners_.clear();
}
std::optional<std::vector<uint8_t>>
FakeNearbyConnectionsManager::GetRawAuthenticationToken(
absl::string_view endpoint_id) {
NL_DCHECK(!is_shutdown());
auto iter = endpoint_auth_tokens_.find(std::string(endpoint_id));
if (iter != endpoint_auth_tokens_.end()) return iter->second;
return std::nullopt;
}
void FakeNearbyConnectionsManager::SetRawAuthenticationToken(
absl::string_view endpoint_id, std::vector<uint8_t> token) {
endpoint_auth_tokens_[std::string(endpoint_id)] = std::move(token);
}
void FakeNearbyConnectionsManager::UpgradeBandwidth(
absl::string_view endpoint_id) {
upgrade_bandwidth_endpoint_ids_.insert(std::string(endpoint_id));
}
void FakeNearbyConnectionsManager::OnEndpointFound(
absl::string_view endpoint_id,
std::unique_ptr<DiscoveredEndpointInfo> info) {
if (discovery_listener_ == nullptr) return;
discovery_listener_->OnEndpointDiscovered(endpoint_id, info->endpoint_info);
}
void FakeNearbyConnectionsManager::OnEndpointLost(
absl::string_view endpoint_id) {
if (!discovery_listener_) return;
discovery_listener_->OnEndpointLost(endpoint_id);
}
bool FakeNearbyConnectionsManager::IsAdvertising() const {
return advertising_listener_ != nullptr;
}
bool FakeNearbyConnectionsManager::IsDiscovering() const {
return discovery_listener_ != nullptr;
}
bool FakeNearbyConnectionsManager::DidUpgradeBandwidth(
absl::string_view endpoint_id) const {
return upgrade_bandwidth_endpoint_ids_.find(endpoint_id) !=
upgrade_bandwidth_endpoint_ids_.end();
}
void FakeNearbyConnectionsManager::SetPayloadPathStatus(
int64_t payload_id, ConnectionsStatus status) {
payload_path_status_[payload_id] = status;
}
std::weak_ptr<FakeNearbyConnectionsManager::PayloadStatusListener>
FakeNearbyConnectionsManager::GetRegisteredPayloadStatusListener(
int64_t payload_id) {
auto it = payload_status_listeners_.find(payload_id);
if (it != payload_status_listeners_.end()) return it->second;
return std::weak_ptr<FakeNearbyConnectionsManager::PayloadStatusListener>();
}
void FakeNearbyConnectionsManager::SetIncomingPayload(
int64_t payload_id, std::unique_ptr<Payload> payload) {
incoming_payloads_[payload_id] = std::move(payload);
}
bool FakeNearbyConnectionsManager::WasPayloadCanceled(
int64_t payload_id) const {
return absl::c_linear_search(canceled_payload_ids_, payload_id);
}
std::optional<std::filesystem::path>
FakeNearbyConnectionsManager::GetRegisteredPayloadPath(int64_t payload_id) {
auto it = registered_payload_paths_.find(payload_id);
if (it == registered_payload_paths_.end()) return std::nullopt;
return it->second;
}
void FakeNearbyConnectionsManager::CleanupForProcessStopped() {
advertising_listener_ = nullptr;
advertising_data_usage_ = DataUsage::UNKNOWN_DATA_USAGE;
advertising_power_level_ = PowerLevel::kUnknown;
advertising_endpoint_info_.reset();
discovery_listener_ = nullptr;
is_shutdown_ = true;
}
FakeNearbyConnectionsManager::ConnectionsCallback
FakeNearbyConnectionsManager::GetStartAdvertisingCallback() {
capture_next_start_advertising_callback_ = true;
FakeNearbyConnectionsManager::ConnectionsCallback callback =
[&](ConnectionsStatus status) { HandleStartAdvertisingCallback(status); };
return callback;
}
FakeNearbyConnectionsManager::ConnectionsCallback
FakeNearbyConnectionsManager::GetStopAdvertisingCallback() {
capture_next_stop_advertising_callback_ = true;
ConnectionsCallback callback = [&](ConnectionsStatus status) {
HandleStopAdvertisingCallback(status);
};
return callback;
}
void FakeNearbyConnectionsManager::HandleStartAdvertisingCallback(
ConnectionsStatus status) {
if (pending_start_advertising_callback_) {
std::move(pending_start_advertising_callback_)(status);
}
capture_next_start_advertising_callback_ = false;
}
void FakeNearbyConnectionsManager::HandleStopAdvertisingCallback(
ConnectionsStatus status) {
if (pending_stop_advertising_callback_) {
std::move(pending_stop_advertising_callback_)(status);
}
capture_next_stop_advertising_callback_ = false;
}
void FakeNearbyConnectionsManager::SetCustomSavePath(
absl::string_view custom_save_path) {
custom_save_path_ = custom_save_path;
}
std::string FakeNearbyConnectionsManager::Dump() const { return ""; }
} // namespace sharing
} // namespace nearby
+175
View File
@@ -0,0 +1,175 @@
// 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_SHARING_FAKE_NEARBY_CONNECTIONS_MANAGER_H_
#define THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTIONS_MANAGER_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
// Fake NearbyConnectionsManager for testing.
class FakeNearbyConnectionsManager : public NearbyConnectionsManager {
public:
FakeNearbyConnectionsManager();
~FakeNearbyConnectionsManager() override;
// NearbyConnectionsManager:
void Shutdown() override;
void StartAdvertising(std::vector<uint8_t> endpoint_info,
IncomingConnectionListener* listener,
PowerLevel power_level, proto::DataUsage data_usage,
ConnectionsCallback callback) override;
void StopAdvertising(ConnectionsCallback callback) override;
void StartDiscovery(DiscoveryListener* listener, proto::DataUsage data_usage,
ConnectionsCallback callback) override;
void StopDiscovery() override;
void Connect(std::vector<uint8_t> endpoint_info,
absl::string_view endpoint_id,
std::optional<std::vector<uint8_t>> bluetooth_mac_address,
proto::DataUsage data_usage, TransportType transport_type,
NearbyConnectionCallback callback) override;
void Disconnect(absl::string_view endpoint_id) override;
void Send(absl::string_view endpoint_id, std::unique_ptr<Payload> payload,
std::weak_ptr<PayloadStatusListener> listener) override;
void RegisterPayloadStatusListener(
int64_t payload_id,
std::weak_ptr<PayloadStatusListener> listener) override;
void RegisterPayloadPath(int64_t payload_id,
const std::filesystem::path& file_path,
ConnectionsCallback callback) override;
Payload* GetIncomingPayload(int64_t payload_id) override;
void Cancel(int64_t payload_id) override;
void ClearIncomingPayloads() override;
std::optional<std::vector<uint8_t>> GetRawAuthenticationToken(
absl::string_view endpoint_id) override;
void UpgradeBandwidth(absl::string_view endpoint_id) override;
void SetCustomSavePath(absl::string_view custom_save_path) override;
// Testing methods
void SetRawAuthenticationToken(absl::string_view endpoint_id,
std::vector<uint8_t> token);
void OnEndpointFound(absl::string_view endpoint_id,
std::unique_ptr<DiscoveredEndpointInfo> info);
void OnEndpointLost(absl::string_view endpoint_id);
bool IsAdvertising() const;
bool IsDiscovering() const;
bool DidUpgradeBandwidth(absl::string_view endpoint_id) const;
void SetPayloadPathStatus(int64_t payload_id, ConnectionsStatus status);
std::weak_ptr<PayloadStatusListener> GetRegisteredPayloadStatusListener(
int64_t payload_id);
void SetIncomingPayload(int64_t payload_id, std::unique_ptr<Payload> payload);
std::optional<std::filesystem::path> GetRegisteredPayloadPath(
int64_t payload_id);
bool WasPayloadCanceled(int64_t payload_id) const;
void CleanupForProcessStopped();
ConnectionsCallback GetStartAdvertisingCallback();
ConnectionsCallback GetStopAdvertisingCallback();
bool is_shutdown() const { return is_shutdown_; }
proto::DataUsage advertising_data_usage() const {
return advertising_data_usage_;
}
PowerLevel advertising_power_level() const {
return advertising_power_level_;
}
void set_nearby_connection(NearbyConnection* connection) {
connection_ = connection;
}
proto::DataUsage connected_data_usage() const {
return connected_data_usage_;
}
TransportType transport_type() const { return transport_type_; }
void set_send_payload_callback(
std::function<void(std::unique_ptr<Payload>,
std::weak_ptr<PayloadStatusListener>)>
callback) {
send_payload_callback_ = std::move(callback);
}
const std::optional<std::vector<uint8_t>>& advertising_endpoint_info() {
return advertising_endpoint_info_;
}
std::optional<std::vector<uint8_t>> connection_endpoint_info(
absl::string_view endpoint_id) {
auto it = connection_endpoint_infos_.find(std::string(endpoint_id));
if (it == connection_endpoint_infos_.end()) return std::nullopt;
return it->second;
}
bool has_incoming_payloads() { return !incoming_payloads_.empty(); }
private:
void HandleStartAdvertisingCallback(ConnectionsStatus status);
void HandleStopAdvertisingCallback(ConnectionsStatus status);
IncomingConnectionListener* advertising_listener_ = nullptr;
DiscoveryListener* discovery_listener_ = nullptr;
bool is_shutdown_ = false;
proto::DataUsage advertising_data_usage_ =
proto::DataUsage::UNKNOWN_DATA_USAGE;
PowerLevel advertising_power_level_ = PowerLevel::kUnknown;
absl::flat_hash_set<std::string> upgrade_bandwidth_endpoint_ids_;
std::map<std::string, std::vector<uint8_t>> endpoint_auth_tokens_;
NearbyConnection* connection_ = nullptr;
proto::DataUsage connected_data_usage_ = proto::DataUsage::UNKNOWN_DATA_USAGE;
TransportType transport_type_ = TransportType::kAny;
std::function<void(std::unique_ptr<Payload>,
std::weak_ptr<PayloadStatusListener>)>
send_payload_callback_;
std::optional<std::vector<uint8_t>> advertising_endpoint_info_;
std::set<std::string> disconnected_endpoints_;
std::set<int64_t> canceled_payload_ids_;
bool capture_next_stop_advertising_callback_ = false;
ConnectionsCallback pending_stop_advertising_callback_;
bool capture_next_start_advertising_callback_ = false;
ConnectionsCallback pending_start_advertising_callback_;
std::string custom_save_path_;
// Maps endpoint_id to endpoint_info.
std::map<std::string, std::vector<uint8_t>> connection_endpoint_infos_;
std::map<int64_t, ConnectionsStatus> payload_path_status_;
std::map<int64_t, std::weak_ptr<PayloadStatusListener>>
payload_status_listeners_;
std::map<int64_t, std::unique_ptr<Payload>> incoming_payloads_;
std::map<int64_t, std::filesystem::path> registered_payload_paths_;
std::string Dump() const override;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTIONS_MANAGER_H_
+119
View File
@@ -0,0 +1,119 @@
// 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_SHARING_FAKE_NEARBY_CONNECTIONS_SERVICE_H_
#define THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTIONS_SERVICE_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
class FakeNearbyConnectionsService : public NearbyConnectionsService {
public:
FakeNearbyConnectionsService() = default;
FakeNearbyConnectionsService(const FakeNearbyConnectionsService&) = default;
FakeNearbyConnectionsService& operator=(const FakeNearbyConnectionsService&) =
default;
FakeNearbyConnectionsService(FakeNearbyConnectionsService&&) = default;
FakeNearbyConnectionsService& operator=(FakeNearbyConnectionsService&&) =
default;
~FakeNearbyConnectionsService() override = default;
MOCK_METHOD(void, StartAdvertising,
(absl::string_view service_id,
const std::vector<uint8_t>& endpoint_info,
AdvertisingOptions advertising_options,
ConnectionListener advertising_listener,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, StopAdvertising,
(absl::string_view service_id,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, StartDiscovery,
(absl::string_view service_id, DiscoveryOptions discovery_options,
DiscoveryListener discovery_listener,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, StopDiscovery,
(absl::string_view service_id,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, RequestConnection,
(absl::string_view service_id,
const std::vector<uint8_t>& endpoint_info,
absl::string_view endpoint_id,
ConnectionOptions connection_options,
ConnectionListener connection_listener,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, DisconnectFromEndpoint,
(absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, SendPayload,
(absl::string_view service_id,
absl::Span<const std::string> endpoint_ids,
std::unique_ptr<Payload> payload,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, CancelPayload,
(absl::string_view service_id, int64_t payload_id,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, InitiateBandwidthUpgrade,
(absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, AcceptConnection,
(absl::string_view service_id, absl::string_view endpoint_id,
PayloadListener payload_listener,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(void, StopAllEndpoints,
(std::function<void(Status status)> callback), (override));
MOCK_METHOD(void, SetCustomSavePath,
(absl::string_view path,
std::function<void(Status status)> callback),
(override));
MOCK_METHOD(std::string, Dump, (), (const override));
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_CONNECTIONS_SERVICE_H_
+322
View File
@@ -0,0 +1,322 @@
// 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 "sharing/fake_nearby_sharing_service.h"
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/base/observer_list.h"
#include "sharing/attachment.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/share_target.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
namespace nearby {
namespace sharing {
void FakeNearbySharingService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void FakeNearbySharingService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool FakeNearbySharingService::HasObserver(Observer* observer) {
return observers_.HasObserver(observer);
}
// Shutdown the Nearby Sharing service, and cleanup.
void FakeNearbySharingService::Shutdown(
std::function<void(StatusCodes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Registers a send surface for handling payload transfer status and device
// discovery.
void FakeNearbySharingService::RegisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) {
if (state == SendSurfaceState::kForeground) {
foreground_send_transfer_callbacks_.AddObserver(transfer_callback);
foreground_send_discovered_callbacks_.AddObserver(discovery_callback);
} else {
background_send_transfer_callbacks_.AddObserver(transfer_callback);
background_send_discovered_callbacks_.AddObserver(discovery_callback);
}
status_codes_callback(StatusCodes::kOk);
}
// Unregisters the current send surface.
void FakeNearbySharingService::UnregisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback,
std::function<void(StatusCodes)> status_codes_callback) {
foreground_send_transfer_callbacks_.RemoveObserver(transfer_callback);
foreground_send_discovered_callbacks_.RemoveObserver(discovery_callback);
background_send_transfer_callbacks_.RemoveObserver(transfer_callback);
background_send_discovered_callbacks_.RemoveObserver(discovery_callback);
status_codes_callback(StatusCodes::kOk);
}
// Registers a receiver surface for handling payload transfer status.
void FakeNearbySharingService::RegisterReceiveSurface(
TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) {
if (state == ReceiveSurfaceState::kForeground) {
foreground_receive_transfer_callbacks_.AddObserver(transfer_callback);
} else {
background_receive_transfer_callbacks_.AddObserver(transfer_callback);
}
status_codes_callback(StatusCodes::kOk);
}
// Unregisters the current receive surface.
void FakeNearbySharingService::UnregisterReceiveSurface(
TransferUpdateCallback* transfer_callback,
std::function<void(StatusCodes)> status_codes_callback) {
foreground_receive_transfer_callbacks_.RemoveObserver(transfer_callback);
background_receive_transfer_callbacks_.RemoveObserver(transfer_callback);
status_codes_callback(StatusCodes::kOk);
}
// Unregisters all foreground receive surfaces.
void FakeNearbySharingService::ClearForegroundReceiveSurfaces(
std::function<void(StatusCodes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Returns true if a foreground receive surface is registered.
bool FakeNearbySharingService::IsInHighVisibility() const { return false; }
// Returns true if there is an ongoing file transfer.
bool FakeNearbySharingService::IsTransferring() const { return false; }
// Returns true if we're currently receiving a file.
bool FakeNearbySharingService::IsReceivingFile() const { return false; }
// Returns true if we're currently sending a file.
bool FakeNearbySharingService::IsSendingFile() const { return false; }
// Returns true if we're currently attempting to connect to a
// remote device.
bool FakeNearbySharingService::IsConnecting() const { return false; }
// Returns true if we are currently scanning for remote devices.
bool FakeNearbySharingService::IsScanning() const { return false; }
// Sends |attachments| to the remote |share_target|.
void FakeNearbySharingService::SendAttachments(
const ShareTarget& share_target,
std::vector<std::unique_ptr<Attachment>> attachments,
std::function<void(StatusCodes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Accepts incoming share from the remote |share_target|.
void FakeNearbySharingService::Accept(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Rejects incoming share from the remote |share_target|.
void FakeNearbySharingService::Reject(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Cancels outgoing shares to the remote |share_target|.
void FakeNearbySharingService::Cancel(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Returns true if the local user cancelled the transfer to remote
// |share_target|.
bool FakeNearbySharingService::DidLocalUserCancelTransfer(
const ShareTarget& share_target) {
return false;
}
// Opens attachments from the remote |share_target|.
void FakeNearbySharingService::Open(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) {
status_codes_callback(StatusCodes::kOk);
}
// Opens an url target on a browser instance.
void FakeNearbySharingService::OpenUrl(const ::nearby::network::Url& url) {}
// Copies text to cache/clipboard.
void FakeNearbySharingService::CopyText(absl::string_view text) {}
// Sets a cleanup callback to be called once done with transfer for ARC.
void FakeNearbySharingService::SetArcTransferCleanupCallback(
std::function<void()> callback) {}
std::string FakeNearbySharingService::Dump() const { return ""; }
NearbyShareSettings* FakeNearbySharingService::GetSettings() { return nullptr; }
NearbyShareHttpNotifier* FakeNearbySharingService::GetHttpNotifier() {
return nullptr;
}
NearbyShareLocalDeviceDataManager*
FakeNearbySharingService::GetLocalDeviceDataManager() {
return nullptr;
}
NearbyShareContactManager* FakeNearbySharingService::GetContactManager() {
return nullptr;
}
NearbyShareCertificateManager*
FakeNearbySharingService::GetCertificateManager() {
return nullptr;
}
AccountManager* FakeNearbySharingService::GetAccountManager() {
return nullptr;
}
void FakeNearbySharingService::FireHighVisibilityChangeRequested() {
for (auto& observer : observers_.GetObservers()) {
observer->OnHighVisibilityChangeRequested();
}
}
void FakeNearbySharingService::FireHighVisibilityChanged(
bool in_high_visibility) {
for (auto& observer : observers_.GetObservers()) {
observer->OnHighVisibilityChanged(in_high_visibility);
}
}
void FakeNearbySharingService::FireStartAdvertisingFailure() {
for (auto& observer : observers_.GetObservers()) {
observer->OnStartAdvertisingFailure();
}
}
void FakeNearbySharingService::FireStartDiscoveryResult(bool success) {
for (auto& observer : observers_.GetObservers()) {
observer->OnStartDiscoveryResult(success);
}
}
void FakeNearbySharingService::FireFastInitiationDevicesDetected() {
for (auto& observer : observers_.GetObservers()) {
observer->OnFastInitiationDevicesDetected();
}
}
void FakeNearbySharingService::FireFastInitiationDevicesNotDetected() {
for (auto& observer : observers_.GetObservers()) {
observer->OnFastInitiationDevicesNotDetected();
}
}
void FakeNearbySharingService::FireFastInitiationScanningStopped() {
for (auto& observer : observers_.GetObservers()) {
observer->OnFastInitiationScanningStopped();
}
}
void FakeNearbySharingService::FireShutdown() {
for (auto& observer : observers_.GetObservers()) {
observer->OnShutdown();
}
}
void FakeNearbySharingService::FireSendTransferUpdate(
SendSurfaceState state, ShareTarget share_target,
TransferMetadata transfer_metadata) {
if (state == SendSurfaceState::kForeground) {
for (auto& transfer_callback :
foreground_send_transfer_callbacks_.GetObservers()) {
transfer_callback->OnTransferUpdate(share_target, transfer_metadata);
}
} else {
for (auto& transfer_callback :
background_send_transfer_callbacks_.GetObservers()) {
transfer_callback->OnTransferUpdate(share_target, transfer_metadata);
}
}
}
void FakeNearbySharingService::FireReceiveTransferUpdate(
ReceiveSurfaceState state, ShareTarget share_target,
TransferMetadata transfer_metadata) {
if (state == ReceiveSurfaceState::kForeground) {
for (auto& transfer_callback :
foreground_receive_transfer_callbacks_.GetObservers()) {
transfer_callback->OnTransferUpdate(share_target, transfer_metadata);
}
} else {
for (auto& transfer_callback :
foreground_receive_transfer_callbacks_.GetObservers()) {
transfer_callback->OnTransferUpdate(share_target, transfer_metadata);
}
}
}
// Fire discovery events.
void FakeNearbySharingService::FireShareTargetDiscovered(
SendSurfaceState state, ShareTarget share_target) {
if (state == SendSurfaceState::kForeground) {
for (auto& discovered_callback :
foreground_send_discovered_callbacks_.GetObservers()) {
discovered_callback->OnShareTargetDiscovered(share_target);
}
} else {
for (auto& discovered_callback :
background_send_discovered_callbacks_.GetObservers()) {
discovered_callback->OnShareTargetDiscovered(share_target);
}
}
}
void FakeNearbySharingService::FireShareTargetLost(SendSurfaceState state,
ShareTarget share_target) {
if (state == SendSurfaceState::kForeground) {
for (auto& discovered_callback :
foreground_send_discovered_callbacks_.GetObservers()) {
discovered_callback->OnShareTargetLost(share_target);
}
} else {
for (auto& discovered_callback :
background_send_discovered_callbacks_.GetObservers()) {
discovered_callback->OnShareTargetLost(share_target);
}
}
}
} // namespace sharing
} // namespace nearby
+183
View File
@@ -0,0 +1,183 @@
// 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_SHARING_FAKE_NEARBY_SHARING_SERVICE_H_
#define THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_SHARING_SERVICE_H_
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/base/observer_list.h"
#include "sharing/attachment.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/share_target.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
namespace nearby {
namespace sharing {
class FakeNearbySharingService : public NearbySharingService {
public:
~FakeNearbySharingService() override = default;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
bool HasObserver(Observer* observer) override;
// Shutdown the Nearby Sharing service, and cleanup.
void Shutdown(
std::function<void(StatusCodes)> status_codes_callback) override;
// Register a send surface for handling payload transfer status and device.
// discovery.
void RegisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) override;
// Unregisters the current send surface.
void UnregisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback,
std::function<void(StatusCodes)> status_codes_callback) override;
// Registers a receiver surface for handling payload transfer status.
void RegisterReceiveSurface(
TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) override;
// Unregisters the current receive surface.
void UnregisterReceiveSurface(
TransferUpdateCallback* transfer_callback,
std::function<void(StatusCodes)> status_codes_callback) override;
// Unregisters all foreground receive surfaces.
void ClearForegroundReceiveSurfaces(
std::function<void(StatusCodes)> status_codes_callback) override;
// Returns true if a foreground receive surface is registered.
bool IsInHighVisibility() const override;
// Returns true if there is an ongoing file transfer.
bool IsTransferring() const override;
// Returns true if we're currently receiving a file.
bool IsReceivingFile() const override;
// Returns true if we're currently sending a file.
bool IsSendingFile() const override;
// Returns true if we're currently attempting to connect to a
// remote device.
bool IsConnecting() const override;
// Returns true if we are currently scanning for remote devices.
bool IsScanning() const override;
// Sends |attachments| to the remote |share_target|.
void SendAttachments(
const ShareTarget& share_target,
std::vector<std::unique_ptr<Attachment>> attachments,
std::function<void(StatusCodes)> status_codes_callback) override;
// Accepts incoming share from the remote |share_target|.
void Accept(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
// Rejects incoming share from the remote |share_target|.
void Reject(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
// Cancels outgoing shares to the remote |share_target|.
void Cancel(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
// Returns true if the local user cancelled the transfer to remote
// |share_target|.
bool DidLocalUserCancelTransfer(const ShareTarget& share_target) override;
// Opens attachments from the remote |share_target|.
void Open(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback)
override;
// Opens an url target on a browser instance.
void OpenUrl(const ::nearby::network::Url& url) override;
// Copies text to cache/clipboard.
void CopyText(absl::string_view text) override;
// Sets a cleanup callback to be called once done with transfer for ARC.
void SetArcTransferCleanupCallback(std::function<void()> callback) override;
std::string Dump() const override;
NearbyShareSettings* GetSettings() override;
NearbyShareHttpNotifier* GetHttpNotifier() override;
NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() override;
NearbyShareContactManager* GetContactManager() override;
NearbyShareCertificateManager* GetCertificateManager() override;
AccountManager* GetAccountManager() override;
// Fake methods to support test scenarios.
// Fire observer events.
void FireHighVisibilityChangeRequested();
void FireHighVisibilityChanged(bool in_high_visibility);
void FireStartAdvertisingFailure();
void FireStartDiscoveryResult(bool success);
void FireFastInitiationDevicesDetected();
void FireFastInitiationDevicesNotDetected();
void FireFastInitiationScanningStopped();
void FireShutdown();
// Fire transfer update events.
void FireSendTransferUpdate(SendSurfaceState state, ShareTarget share_target,
TransferMetadata transfer_metadata);
void FireReceiveTransferUpdate(ReceiveSurfaceState state,
ShareTarget share_target,
TransferMetadata transfer_metadata);
// Fire discovery events.
void FireShareTargetDiscovered(SendSurfaceState state,
ShareTarget share_target);
void FireShareTargetLost(SendSurfaceState state, ShareTarget share_target);
private:
ObserverList<Observer> observers_;
ObserverList<NearbyShareSettings::Observer> settings_observers_;
ObserverList<TransferUpdateCallback> foreground_send_transfer_callbacks_;
ObserverList<TransferUpdateCallback> background_send_transfer_callbacks_;
ObserverList<ShareTargetDiscoveredCallback>
foreground_send_discovered_callbacks_;
ObserverList<ShareTargetDiscoveredCallback>
background_send_discovered_callbacks_;
ObserverList<TransferUpdateCallback> foreground_receive_transfer_callbacks_;
ObserverList<TransferUpdateCallback> background_receive_transfer_callbacks_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_FAKE_NEARBY_SHARING_SERVICE_H_
+118
View File
@@ -0,0 +1,118 @@
// 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 "sharing/file_attachment.h"
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "sharing/attachment.h"
#include "sharing/common/compatible_u8_string.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/base/mime.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
namespace nearby {
namespace sharing {
namespace {
FileAttachment::Type FileAttachmentTypeFromMimeType(
absl::string_view mime_type) {
if (absl::StartsWith(mime_type, "image/"))
return service::proto::FileMetadata::IMAGE;
if (absl::StartsWith(mime_type, "video/"))
return service::proto::FileMetadata::VIDEO;
if (absl::StartsWith(mime_type, "audio/"))
return service::proto::FileMetadata::AUDIO;
return service::proto::FileMetadata::UNKNOWN;
}
std::string MimeTypeFromPath(const std::filesystem::path& path) {
std::string extension = path.extension().string();
return extension.empty() ? "application/octet-stream"
: nearby::utils::GetWellKnownMimeTypeFromExtension(
extension.substr(1));
}
} // namespace
FileAttachment::FileAttachment(std::filesystem::path file_path,
std::string parent_folder, int32_t batch_id,
SourceType source_type)
: Attachment(Attachment::Family::kFile, /*size=*/0, batch_id, source_type),
mime_type_(MimeTypeFromPath(file_path)),
type_(FileAttachmentTypeFromMimeType(mime_type_)),
file_path_(std::move(file_path)),
parent_folder_(std::move(parent_folder)) {
file_name_ =
GetCompatibleU8String(file_path_.value_or(L"").filename().u8string());
}
FileAttachment::FileAttachment(int64_t id, int64_t size, std::string file_name,
std::string mime_type, Type type,
std::string parent_folder, int32_t batch_id,
SourceType source_type)
: Attachment(id, Attachment::Family::kFile, size, batch_id, source_type),
file_name_(std::move(file_name)),
mime_type_(std::move(mime_type)),
type_(type),
parent_folder_(std::move(parent_folder)) {}
void FileAttachment::MoveToShareTarget(ShareTarget& share_target) {
share_target.file_attachments.push_back(std::move(*this));
}
absl::string_view FileAttachment::GetDescription() const { return file_name_; }
ShareType FileAttachment::GetShareType() const {
switch (type()) {
case service::proto::FileMetadata::IMAGE:
return ShareType::kImageFile;
case service::proto::FileMetadata::VIDEO:
return ShareType::kVideoFile;
case service::proto::FileMetadata::AUDIO:
return ShareType::kAudioFile;
default:
break;
}
// Try matching on mime type if the attachment type is unrecognized.
if (mime_type() == "application/pdf") {
return ShareType::kPdfFile;
} else if (mime_type() == "application/vnd.google-apps.document") {
return ShareType::kGoogleDocsFile;
} else if (mime_type() == "application/vnd.google-apps.spreadsheet") {
return ShareType::kGoogleSheetsFile;
} else if (mime_type() == "application/vnd.google-apps.presentation") {
return ShareType::kGoogleSlidesFile;
} else if (mime_type() == "text/plain") {
return ShareType::kTextFile;
} else {
return ShareType::kUnknownFile;
}
}
} // namespace sharing
} // namespace nearby
+83
View File
@@ -0,0 +1,83 @@
// 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_SHARING_FILE_ATTACHMENT_H_
#define THIRD_PARTY_NEARBY_SHARING_FILE_ATTACHMENT_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <optional>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "sharing/attachment.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
// A single attachment to be sent by / received from a |ShareTarget|, can be
// either a file or text.
struct ShareTarget;
class FileAttachment : public Attachment {
public:
using Type = nearby::sharing::service::proto::FileMetadata::Type;
explicit FileAttachment(std::filesystem::path file_path,
std::string parent_folder = "", int32_t batch_id = 0,
SourceType source_type = SourceType::kUnknown);
FileAttachment(int64_t id, int64_t size, std::string file_name,
std::string mime_type, Type type,
std::string parent_folder = "", int32_t batch_id = 0,
SourceType source_type = SourceType::kUnknown);
FileAttachment(const FileAttachment&) = default;
FileAttachment(FileAttachment&&) = default;
FileAttachment& operator=(const FileAttachment&) = default;
FileAttachment& operator=(FileAttachment&&) = default;
~FileAttachment() override = default;
absl::string_view file_name() const { return file_name_; }
absl::string_view mime_type() const { return mime_type_; }
absl::string_view parent_folder() const { return parent_folder_; }
Type type() const { return type_; }
const std::optional<std::filesystem::path>& file_path() const {
return file_path_;
}
// Attachment:
void MoveToShareTarget(ShareTarget& share_target) override;
absl::string_view GetDescription() const override;
ShareType GetShareType() const override;
void set_file_path(std::optional<std::filesystem::path> path) {
file_path_ = std::move(path);
}
private:
// File name should be in UTF8 format.
std::string file_name_;
std::string mime_type_;
Type type_;
std::optional<std::filesystem::path> file_path_;
std::string parent_folder_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_FILE_ATTACHMENT_H_
+257
View File
@@ -0,0 +1,257 @@
// Copyright 2022-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 "sharing/incoming_frames_reader.h"
#include <stdint.h>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <queue>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/platform/mutex_lock.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
namespace {
using FrameType = ::nearby::sharing::service::proto::V1Frame_FrameType;
using V1Frame = ::nearby::sharing::service::proto::V1Frame;
using Frame = ::nearby::sharing::service::proto::Frame;
std::ostream& operator<<(std::ostream& out, const FrameType& obj) {
out << static_cast<std::underlying_type<FrameType>::type>(obj);
return out;
}
} // namespace
IncomingFramesReader::IncomingFramesReader(Context* context,
NearbySharingDecoder* decoder,
NearbyConnection* connection)
: connection_(connection), decoder_(decoder) {
NL_DCHECK(decoder);
NL_DCHECK(connection);
timeout_timer_ = context->CreateTimer();
}
IncomingFramesReader::~IncomingFramesReader() {
MutexLock lock(&mutex_);
NL_LOG(INFO) << "~IncomingFramesReader is called";
Done(std::nullopt);
}
void IncomingFramesReader::ReadFrame(
std::function<void(std::optional<V1Frame>)> callback) {
MutexLock lock(&mutex_);
if (!read_frame_info_queue_.empty()) {
ReadFrameInfo read_fame_info{std::nullopt, std::move(callback),
std::nullopt};
read_frame_info_queue_.push(std::move(read_fame_info));
return;
}
// Check in the cache for frame.
std::optional<V1Frame> cached_frame = GetCachedFrame(std::nullopt);
if (cached_frame.has_value()) {
callback(std::move(cached_frame));
return;
}
ReadFrameInfo read_fame_info{std::nullopt, std::move(callback), std::nullopt};
read_frame_info_queue_.push(std::move(read_fame_info));
ReadNextFrame();
}
void IncomingFramesReader::ReadFrame(
FrameType frame_type, std::function<void(std::optional<V1Frame>)> callback,
absl::Duration timeout) {
MutexLock lock(&mutex_);
if (!read_frame_info_queue_.empty()) {
ReadFrameInfo read_fame_info{frame_type, std::move(callback), timeout};
read_frame_info_queue_.push(std::move(read_fame_info));
return;
}
// Check in the cache for frame.
std::optional<V1Frame> cached_frame = GetCachedFrame(frame_type);
if (cached_frame.has_value()) {
callback(std::move(cached_frame));
return;
}
ReadFrameInfo read_fame_info{frame_type, std::move(callback), timeout};
read_frame_info_queue_.push(std::move(read_fame_info));
if (timeout_timer_->IsRunning()) {
timeout_timer_->Stop();
}
timeout_timer_->Start(
timeout / absl::Milliseconds(1), 0, [&, reader = GetWeakPtr()]() {
auto frame_reader = reader.lock();
if (frame_reader == nullptr) {
NL_LOG(WARNING) << "IncomingFramesReader is released before.";
return;
}
OnTimeout();
});
ReadNextFrame();
}
void IncomingFramesReader::ReadNextFrame() {
connection_->Read(
[&, reader = GetWeakPtr()](std::optional<std::vector<uint8_t>> bytes) {
auto frame_reader = reader.lock();
if (frame_reader == nullptr) {
NL_LOG(WARNING) << "IncomingFramesReader is released before.";
return;
}
OnDataReadFromConnection(std::move(bytes));
});
}
void IncomingFramesReader::OnTimeout() {
MutexLock lock(&mutex_);
NL_LOG(WARNING) << __func__ << ": Timed out reading from NearbyConnection.";
Done(std::nullopt);
}
void IncomingFramesReader::OnDataReadFromConnection(
std::optional<std::vector<uint8_t>> bytes) {
MutexLock lock(&mutex_);
if (read_frame_info_queue_.empty()) {
return;
}
if (!bytes.has_value()) {
NL_LOG(WARNING) << __func__ << ": Failed to read frame";
Done(std::nullopt);
return;
}
std::unique_ptr<Frame> frame =
decoder_->DecodeFrame(absl::MakeSpan(bytes->data(), bytes->size()));
if (frame == nullptr) {
NL_LOG(WARNING)
<< __func__
<< ": Cannot decode frame. Not currently bound to nearby process";
Done(std::nullopt);
return;
}
OnFrameDecoded(std::move(*frame));
}
void IncomingFramesReader::OnFrameDecoded(std::optional<Frame> frame) {
if (!frame.has_value()) {
ReadNextFrame();
return;
}
if (frame->version() != Frame::V1) {
NL_VLOG(1) << __func__ << ": Frame read does not have V1Frame";
ReadNextFrame();
return;
}
auto v1_frame = frame->v1();
FrameType v1_frame_type = v1_frame.type();
const ReadFrameInfo& frame_info = read_frame_info_queue_.front();
if (frame_info.frame_type.has_value() &&
*frame_info.frame_type != v1_frame_type) {
NL_LOG(WARNING) << __func__ << ": Failed to read frame of type "
<< *frame_info.frame_type << ", but got frame of type "
<< v1_frame_type << ". Cached for later.";
cached_frames_.insert({v1_frame_type, std::move(v1_frame)});
ReadNextFrame();
return;
}
Done(std::move(v1_frame));
}
void IncomingFramesReader::Done(std::optional<V1Frame> frame) {
if (read_frame_info_queue_.empty()) {
return;
}
if (timeout_timer_ != nullptr) {
timeout_timer_->Stop();
}
bool is_empty_frame = !frame.has_value();
ReadFrameInfo read_frame_info = std::move(read_frame_info_queue_.front());
read_frame_info_queue_.pop();
read_frame_info.callback(std::move(frame));
if (is_empty_frame) {
// should complete all pending readers.
while (!read_frame_info_queue_.empty()) {
read_frame_info = std::move(read_frame_info_queue_.front());
read_frame_info_queue_.pop();
read_frame_info.callback(std::nullopt);
}
return;
}
if (!read_frame_info_queue_.empty()) {
ReadFrameInfo read_frame_info = std::move(read_frame_info_queue_.front());
read_frame_info_queue_.pop();
if (read_frame_info.timeout.has_value()) {
ReadFrame(*read_frame_info.frame_type,
std::move(read_frame_info.callback), *read_frame_info.timeout);
} else {
ReadFrame(std::move(read_frame_info.callback));
}
}
}
std::optional<V1Frame> IncomingFramesReader::GetCachedFrame(
std::optional<nearby::sharing::service::proto::V1Frame_FrameType>
frame_type) {
NL_VLOG(1) << __func__ << ": Fetching cached frame";
if (frame_type.has_value())
NL_VLOG(1) << __func__ << ": Requested frame type - " << *frame_type;
auto iter = frame_type.has_value() ? cached_frames_.find(*frame_type)
: cached_frames_.begin();
if (iter == cached_frames_.end()) return std::nullopt;
NL_VLOG(1) << __func__ << ": Successfully read cached frame";
std::optional<V1Frame> frame = std::move(iter->second);
cached_frames_.erase(iter);
return frame;
}
} // namespace sharing
} // namespace nearby
+113
View File
@@ -0,0 +1,113 @@
// Copyright 2022-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_SHARING_INCOMING_FRAMES_READER_H_
#define THIRD_PARTY_NEARBY_SHARING_INCOMING_FRAMES_READER_H_
#include <stdint.h>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <queue>
#include <vector>
#include "absl/time/time.h"
#include "internal/platform/mutex.h"
#include "internal/platform/timer.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
// Helper class to read incoming frames from Nearby devices.
class IncomingFramesReader
: public std::enable_shared_from_this<IncomingFramesReader> {
public:
IncomingFramesReader(Context* context, NearbySharingDecoder* decoder,
NearbyConnection* connection);
virtual ~IncomingFramesReader();
IncomingFramesReader(const IncomingFramesReader&) = delete;
IncomingFramesReader& operator=(IncomingFramesReader&) = delete;
// Reads an incoming frame from |connection|. |callback| is called
// with the frame read from connection or nullopt if connection socket is
// closed.
//
// Note: Callers are expected wait for |callback| to be run before scheduling
// subsequent calls to ReadFrame(..).
virtual void ReadFrame(
std::function<
void(std::optional<nearby::sharing::service::proto::V1Frame>)>
callback);
// Reads a frame of type |frame_type| from |connection|. |callback| is called
// with the frame read from connection or nullopt if connection socket is
// closed or |timeout| units of time have passed.
//
// Note: Callers are expected wait for |callback| to be run before scheduling
// subsequent calls to ReadFrame(..).
virtual void ReadFrame(
nearby::sharing::service::proto::V1Frame_FrameType frame_type,
std::function<
void(std::optional<nearby::sharing::service::proto::V1Frame>)>
callback,
absl::Duration timeout);
std::weak_ptr<IncomingFramesReader> GetWeakPtr() {
return this->weak_from_this();
}
private:
struct ReadFrameInfo {
std::optional<nearby::sharing::service::proto::V1Frame_FrameType>
frame_type = std::nullopt;
std::function<void(std::optional<nearby::sharing::service::proto::V1Frame>)>
callback = nullptr;
std::optional<absl::Duration> timeout = std::nullopt;
};
void ReadNextFrame();
void OnDataReadFromConnection(std::optional<std::vector<uint8_t>> bytes);
void OnFrameDecoded(
std::optional<nearby::sharing::service::proto::Frame> frame);
void OnTimeout();
void Done(std::optional<nearby::sharing::service::proto::V1Frame> frame);
std::optional<nearby::sharing::service::proto::V1Frame> GetCachedFrame(
std::optional<nearby::sharing::service::proto::V1Frame_FrameType>
frame_type);
NearbyConnection* connection_;
NearbySharingDecoder* decoder_ = nullptr;
RecursiveMutex mutex_;
std::queue<ReadFrameInfo> read_frame_info_queue_;
std::function<void()> timeout_callback_;
// Caches frames read from NearbyConnection which are not used immediately.
std::map<nearby::sharing::service::proto::V1Frame_FrameType,
std::optional<nearby::sharing::service::proto::V1Frame>>
cached_frames_;
std::unique_ptr<Timer> timeout_timer_ = nullptr;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_INCOMING_FRAMES_READER_H_
+317
View File
@@ -0,0 +1,317 @@
// Copyright 2022-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 "sharing/incoming_frames_reader.h"
#include <stdint.h>
#include <memory>
#include <optional>
#include <vector>
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "internal/test/fake_clock.h"
#include "internal/test/fake_task_runner.h"
#include "sharing/fake_nearby_connection.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/nearby_sharing_decoder_impl.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
namespace {
using ::nearby::sharing::service::proto::V1Frame;
constexpr absl::Duration kTimeout = absl::Milliseconds(1000);
std::optional<std::vector<uint8_t>> GetIntroductionFrame() {
nearby::sharing::service::proto::Frame frame =
nearby::sharing::service::proto::Frame();
frame.set_version(nearby::sharing::service::proto::Frame::V1);
V1Frame* v1frame = frame.mutable_v1();
v1frame->set_type(service::proto::V1Frame::INTRODUCTION);
v1frame->mutable_introduction();
std::vector<uint8_t> data;
data.resize(frame.ByteSize());
if (frame.SerializeToArray(data.data(), data.size())) {
return data;
}
return std::nullopt;
}
std::optional<std::vector<uint8_t>> GetCancelFrame() {
nearby::sharing::service::proto::Frame frame =
nearby::sharing::service::proto::Frame();
frame.set_version(nearby::sharing::service::proto::Frame::V1);
V1Frame* v1frame = frame.mutable_v1();
v1frame->set_type(service::proto::V1Frame::CANCEL);
std::vector<uint8_t> data;
data.resize(frame.ByteSize());
if (frame.SerializeToArray(data.data(), data.size())) {
return data;
}
return std::nullopt;
}
std::optional<std::vector<uint8_t>> GetInvalidFrame() {
std::vector<uint8_t> data;
data.push_back(0xff);
data.push_back(0x00);
data.push_back(0x02);
return data;
}
class IncomingFramesReaderTest : public testing::Test {
public:
IncomingFramesReaderTest() = default;
~IncomingFramesReaderTest() override = default;
void SetUp() override {
frames_reader_ = std::make_shared<IncomingFramesReader>(
context(), &nearby_sharing_decoder_, &fake_nearby_connection_);
}
FakeNearbyConnection& connection() { return fake_nearby_connection_; }
IncomingFramesReader* frames_reader() { return frames_reader_.get(); }
void FastForward(absl::Duration delta) {
FakeClock* fake_clock = reinterpret_cast<FakeClock*>(context()->GetClock());
fake_clock->FastForward(delta);
}
void Sync() {
EXPECT_TRUE(
FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Seconds(1)));
}
void ReleaseFrameReader() { frames_reader_.reset(); }
private:
FakeNearbyConnection fake_nearby_connection_;
NearbySharingDecoderImpl nearby_sharing_decoder_;
std::shared_ptr<IncomingFramesReader> frames_reader_ = nullptr;
Context* context() {
static Context* context = new FakeContext();
return context;
}
};
TEST_F(IncomingFramesReaderTest, ReadTimedOut) {
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame, std::nullopt);
notification.Notify();
},
kTimeout);
Sync();
FastForward(kTimeout);
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
// Ensure that the OnDataReadFromConnection callback is not run since the
// read timed out.
EXPECT_FALSE(connection().has_read_callback_been_run());
// Ensure that the IncomingFramesReader does not close the connection.
EXPECT_FALSE(connection().IsClosed());
}
TEST_F(IncomingFramesReaderTest, ReadAnyFrameSuccessful) {
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
absl::Notification notification;
frames_reader()->ReadFrame([&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
notification.Notify();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReadSuccessful) {
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
notification.Notify();
},
kTimeout);
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReadSuccessful_JumbledFramesOrdering) {
std::optional<std::vector<uint8_t>> cancel_frame = GetCancelFrame();
ASSERT_TRUE(cancel_frame.has_value());
connection().AppendReadableData(*cancel_frame);
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
notification.Notify();
},
kTimeout);
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, JumbledFramesOrdering_ReadFromCache) {
std::optional<std::vector<uint8_t>> cancel_frame = GetCancelFrame();
ASSERT_TRUE(cancel_frame.has_value());
connection().AppendReadableData(*cancel_frame);
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
notification.Notify();
},
kTimeout);
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
// Reading any frame should return CancelFrame.
absl::Notification cancel_notification;
frames_reader()->ReadFrame([&](std::optional<V1Frame> frame) {
ASSERT_NE(frame, std::nullopt);
EXPECT_EQ(frame->type(), service::proto::V1Frame::CANCEL);
cancel_notification.Notify();
});
EXPECT_TRUE(cancel_notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReadAfterConnectionClosed) {
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame, std::nullopt);
notification.Notify();
},
kTimeout);
Sync();
connection().Close();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReadTwoFramesWithTimeoutSuccessfully) {
absl::Notification notification;
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
},
kTimeout);
frames_reader()->ReadFrame(
service::proto::V1Frame::CANCEL,
[&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::CANCEL);
notification.Notify();
},
kTimeout);
std::optional<std::vector<uint8_t>> cancel_frame = GetCancelFrame();
ASSERT_TRUE(cancel_frame.has_value());
connection().AppendReadableData(*cancel_frame);
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
Sync();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReadTwoFramesWithoutTimeoutSuccessfully) {
absl::Notification notification;
frames_reader()->ReadFrame([&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::INTRODUCTION);
});
frames_reader()->ReadFrame([&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame->type(), service::proto::V1Frame::CANCEL);
notification.Notify();
});
std::optional<std::vector<uint8_t>> introduction_frame =
GetIntroductionFrame();
ASSERT_TRUE(introduction_frame.has_value());
connection().AppendReadableData(*introduction_frame);
std::optional<std::vector<uint8_t>> cancel_frame = GetCancelFrame();
ASSERT_TRUE(cancel_frame.has_value());
connection().AppendReadableData(*cancel_frame);
Sync();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
TEST_F(IncomingFramesReaderTest, ReleaseFrameReaderDuringRead) {
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) { EXPECT_EQ(frame, std::nullopt); },
kTimeout);
frames_reader()->ReadFrame(
service::proto::V1Frame::INTRODUCTION,
[&](std::optional<V1Frame> frame) { EXPECT_EQ(frame, std::nullopt); },
kTimeout);
ReleaseFrameReader();
EXPECT_EQ(frames_reader(), nullptr);
}
TEST_F(IncomingFramesReaderTest, ReadInvalidFrame) {
absl::Notification notification;
frames_reader()->ReadFrame([&](std::optional<V1Frame> frame) {
EXPECT_EQ(frame, std::nullopt);
notification.Notify();
});
std::optional<std::vector<uint8_t>> invalid_frame = GetInvalidFrame();
ASSERT_TRUE(invalid_frame.has_value());
connection().AppendReadableData(*invalid_frame);
Sync();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kTimeout));
}
} // namespace
} // namespace sharing
} // namespace nearby
+31
View File
@@ -0,0 +1,31 @@
// 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 "sharing/incoming_share_target_info.h"
namespace nearby {
namespace sharing {
IncomingShareTargetInfo::IncomingShareTargetInfo() = default;
IncomingShareTargetInfo::IncomingShareTargetInfo(IncomingShareTargetInfo&&) =
default;
IncomingShareTargetInfo& IncomingShareTargetInfo::operator=(
IncomingShareTargetInfo&&) = default;
IncomingShareTargetInfo::~IncomingShareTargetInfo() = default;
} // namespace sharing
} // namespace nearby
+34
View File
@@ -0,0 +1,34 @@
// 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_SHARING_INCOMING_SHARE_TARGET_INFO_H_
#define THIRD_PARTY_NEARBY_SHARING_INCOMING_SHARE_TARGET_INFO_H_
#include "sharing/share_target_info.h"
namespace nearby {
namespace sharing {
class IncomingShareTargetInfo : public ShareTargetInfo {
public:
IncomingShareTargetInfo();
IncomingShareTargetInfo(IncomingShareTargetInfo&&);
IncomingShareTargetInfo& operator=(IncomingShareTargetInfo&&);
~IncomingShareTargetInfo() override;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_INCOMING_SHARE_TARGET_INFO_H_
+67
View File
@@ -0,0 +1,67 @@
# 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.
licenses(["notice"])
cc_library(
name = "utf_utils",
srcs = [
"utf_string_conversions.cc",
],
hdrs = [
"utf_string_configuration.h",
"utf_string_conversions.h",
],
visibility = ["//visibility:public"],
deps = [
"//sharing/internal/public:logging",
"//third_party/icu_utf:icu_utf_assistant",
],
)
cc_library(
name = "base",
srcs = [
"encode.cc",
"mime.cc",
],
hdrs = [
"encode.h",
"mime.h",
],
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)
cc_test(
name = "base_test",
size = "small",
timeout = "short",
srcs = [
"encode_test.cc",
"utf_string_conversions_test.cc",
],
shard_count = 8,
deps = [
":base",
":utf_utils",
"//internal/platform/implementation/g3", # fixdeps: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2021 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 "sharing/internal/base/encode.h"
#include <stdint.h>
#include <iomanip>
#include <ios>
#include <sstream>
#include <string>
#include "absl/types/span.h"
namespace nearby {
namespace utils {
// Returns uppercase string.
std::string HexEncode(absl::Span<const uint8_t> data) {
std::ostringstream stream;
stream << std::hex << std::setfill('0') << std::uppercase;
for (uint8_t val : data) {
stream << std::setw(2) << static_cast<int>(val);
}
return stream.str();
}
} // namespace utils
} // namespace nearby
+32
View File
@@ -0,0 +1,32 @@
// Copyright 2021 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_SHARING_INTERNAL_BASE_ENCODE_H_
#define THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_ENCODE_H_
#include <stdint.h>
#include <string>
#include "absl/types/span.h"
namespace nearby {
namespace utils {
std::string HexEncode(absl::Span<const uint8_t> data);
} // namespace utils
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_ENCODE_H_
+42
View File
@@ -0,0 +1,42 @@
// 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 "sharing/internal/base/encode.h"
#include <stdint.h>
#include <vector>
#include "gtest/gtest.h"
namespace nearby {
namespace utils {
namespace {
TEST(HexEncode, HexEncodeNormal) {
std::vector<uint8_t> data = {0x04, 0x3f, 0xf7, 0xe6, 0xf8, 0xc7, 0x0c};
EXPECT_EQ(HexEncode(data), "043FF7E6F8C70C");
}
TEST(HexEncode, HexEncodeEmpty) {
EXPECT_EQ(HexEncode(std::vector<uint8_t>({})), "");
}
TEST(HexEncode, HexEncodeWithPadding) {
EXPECT_EQ(HexEncode(std::vector<uint8_t>({0, 0, 0, 0})), "00000000");
}
} // namespace
} // namespace utils
} // namespace nearby
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
// 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_SHARING_INTERNAL_BASE_MIME_H_
#define THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_MIME_H_
#include <string>
#include "absl/strings/string_view.h"
namespace nearby {
namespace utils {
std::string GetWellKnownMimeTypeFromExtension(absl::string_view extension);
} // namespace utils
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_MIME_H_
@@ -0,0 +1,120 @@
// Copyright 2021 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_SHARING_INTERNAL_BASE_UTF_STRING_CONFIGURATION_H_
#define THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_UTF_STRING_CONFIGURATION_H_
// A set of macros to use for platform detection.
#if defined(__native_client__)
// __native_client__ must be first, so that other OS_ defines are not set.
#define OS_NACL 1
#define OS_NACL_SFI
#elif defined(ANDROID)
#define OS_ANDROID 1
#elif defined(__APPLE__)
// Only include TargetConditionals after testing ANDROID as some Android builds
// on the Mac have this header available and it's not needed unless the target
// is really an Apple platform.
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define OS_IOS 1
#else
#define OS_MAC 1
#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#elif defined(__linux__)
#if !defined(OS_CHROMEOS)
// Do not define OS_LINUX on Chrome OS build.
// The OS_CHROMEOS macro is defined in GN.
#define OS_LINUX 1
#endif // !defined(OS_CHROMEOS)
#if defined(__GLIBC__) && !defined(__UCLIBC__)
// We really are using glibc, not uClibc pretending to be glibc.
#define LIBC_GLIBC 1
#endif
#elif defined(_WIN32)
#define OS_WIN 1
#elif defined(__Fuchsia__)
#define OS_FUCHSIA 1
#elif defined(__FreeBSD__)
#define OS_FREEBSD 1
#elif defined(__NetBSD__)
#define OS_NETBSD 1
#elif defined(__OpenBSD__)
#define OS_OPENBSD 1
#elif defined(__sun)
#define OS_SOLARIS 1
#elif defined(__QNXNTO__)
#define OS_QNX 1
#elif defined(_AIX)
#define OS_AIX 1
#elif defined(__asmjs__) || defined(__wasm__)
#define OS_ASMJS 1
#elif defined(__MVS__)
#define OS_ZOS 1
#else
#error Please add support for your platform in build/build_config.h
#endif
// NOTE: Adding a new port? Please follow
// https://chromium.googlesource.com/chromium/src/+/main/docs/new_port_policy.md
#if defined(OS_MAC) || defined(OS_IOS)
#define OS_APPLE 1
#endif
// For access to standard BSD features, use OS_BSD instead of a
// more specific macro.
#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
#define OS_BSD 1
#endif
// For access to standard POSIX features, use OS_POSIX instead of a
// more specific macro.
#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \
defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) || \
defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \
defined(OS_SOLARIS) || defined(OS_ZOS)
#define OS_POSIX 1
#endif
// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on
// Windows.
#if defined(__GNUC__)
#define COMPILER_GCC 1
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#else
#error Please add support for your compiler in build/build_config.h
#endif
// Type detection for wchar_t.
#if defined(OS_WIN)
#define WCHAR_T_IS_UTF16
#elif defined(OS_FUCHSIA)
#define WCHAR_T_IS_UTF32
#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
#define WCHAR_T_IS_UTF32
#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
// compile in this mode (in particular, Chrome doesn't). This is intended for
// other projects using base who manage their own dependencies and make sure
// short wchar works for them.
#define WCHAR_T_IS_UTF16
#else
#error Please add support for your compiler in build/build_config.h
#endif
#endif // THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_UTF_STRING_CONFIGURATION_H_
@@ -0,0 +1,510 @@
// Copyright 2021 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 "sharing/internal/base/utf_string_conversions.h"
#include <limits.h>
#include <stdint.h>
#include <cstddef>
#include <string>
#include <string_view>
#include <type_traits>
#include "third_party/icu_utf/icu_utf.h"
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace utils {
namespace {
using MachineWord = uintptr_t;
constexpr int32_t kErrorCodePoint = 0xFFFD;
inline bool IsMachineWordAligned(const void* pointer) {
return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1));
}
template <class Char>
bool DoIsStringAscii(const Char* characters, size_t length) {
// Bitmasks to detect non-ASCII characters for character sizes of 8, 16 and 32
// bits.
constexpr MachineWord NonASCIIMasks[] = {
0, MachineWord(0x8080808080808080ULL), MachineWord(0xFF80FF80FF80FF80ULL),
0, MachineWord(0xFFFFFF80FFFFFF80ULL),
};
if (!length) return true;
constexpr MachineWord non_ascii_bit_mask = NonASCIIMasks[sizeof(Char)];
static_assert(non_ascii_bit_mask, "Error: Invalid Mask");
MachineWord all_char_bits = 0;
const Char* end = characters + length;
// Prologue: align the input.
while (!IsMachineWordAligned(characters) && characters < end)
all_char_bits |= *characters++;
if (all_char_bits & non_ascii_bit_mask) return false;
// Compare the values of CPU word size.
constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
constexpr int batch_count = 16;
while (characters <= end - batch_count * chars_per_word) {
all_char_bits = 0;
for (int i = 0; i < batch_count; ++i) {
all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
characters += chars_per_word;
}
if (all_char_bits & non_ascii_bit_mask) return false;
}
// Process the remaining words.
all_char_bits = 0;
while (characters <= end - chars_per_word) {
all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
characters += chars_per_word;
}
// Process the remaining bytes.
while (characters < end) all_char_bits |= *characters++;
return !(all_char_bits & non_ascii_bit_mask);
}
inline bool IsValidCharacter(uint32_t code_point) {
// Excludes non-characters (U+FDD0..U+FDEF, and all code points
// ending in 0xFFFE or 0xFFFF) from the set of valid code points.
// https://unicode.org/faq/private_use.html#nonchar1
return code_point < 0xD800u ||
(code_point >= 0xE000u && code_point < 0xFDD0u) ||
(code_point > 0xFDEFu && code_point <= 0x10FFFFu &&
(code_point & 0xFFFEu) != 0xFFFEu);
}
template <bool (*Validator)(uint32_t)>
inline bool DoIsStringUtf8(std::string_view str) {
const char* src = str.data();
int32_t src_len = static_cast<int32_t>(str.length());
int32_t char_index = 0;
while (char_index < src_len) {
int32_t code_point;
CBU8_NEXT(src, char_index, src_len, code_point);
if (!Validator(code_point)) return false;
}
return true;
}
// Size coefficient ----------------------------------------------------------
// The maximum number of codeunits in the destination encoding corresponding to
// one codeunit in the source encoding.
template <typename SrcChar, typename DestChar>
struct SizeCoefficient {
static_assert(sizeof(SrcChar) < sizeof(DestChar),
"Default case: from a smaller encoding to the bigger one");
// ASCII symbols are encoded by one codeunit in all encodings.
static constexpr int value = 1;
};
template <>
struct SizeCoefficient<char16_t, char> {
// One UTF-16 code unit corresponds to at most 3 code units in UTF-8.
static constexpr int value = 3;
};
#if defined(WCHAR_T_IS_UTF32)
template <>
struct SizeCoefficient<wchar_t, char> {
// UTF-8 uses at most 4 code units per character.
static constexpr int value = 4;
};
template <>
struct SizeCoefficient<wchar_t, char16_t> {
// UTF-16 uses at most 2 code units per character.
static constexpr int value = 2;
};
#endif // defined(WCHAR_T_IS_UTF32)
template <typename SrcChar, typename DestChar>
constexpr int size_coefficient_v =
SizeCoefficient<std::decay_t<SrcChar>, std::decay_t<DestChar>>::value;
// UnicodeAppendUnsafe --------------------------------------------------------
// Function overloads that write code_point to the output string. Output string
// has to have enough space for the codepoint.
// Convenience typedef that checks whether the passed in type is integral (i.e.
// bool, char, int or their extended versions) and is of the correct size.
template <typename Char, size_t N>
using EnableIfBitsAre = std::enable_if_t<
std::is_integral<Char>::value && CHAR_BIT * sizeof(Char) == N, bool>;
template <typename Char, EnableIfBitsAre<Char, 8> = true>
void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
CBU8_APPEND_UNSAFE(out, *size, code_point);
}
template <typename Char, EnableIfBitsAre<Char, 16> = true>
void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
CBU16_APPEND_UNSAFE(out, *size, code_point);
}
template <typename Char, EnableIfBitsAre<Char, 32> = true>
void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
out[(*size)++] = code_point;
}
// DoUtfConversion ------------------------------------------------------------
// Main driver of UtfConversion specialized for different Src encodings.
// dest has to have enough room for the converted text.
template <typename DestChar>
bool DoUtfConversion(const char* src, int32_t src_len, DestChar* dest,
int32_t* dest_len) {
bool success = true;
for (int32_t i = 0; i < src_len;) {
int32_t code_point;
CBU8_NEXT(src, i, src_len, code_point);
if (!IsValidCodepoint(code_point)) {
success = false;
code_point = kErrorCodePoint;
}
UnicodeAppendUnsafe(dest, dest_len, code_point);
}
return success;
}
template <typename DestChar>
bool DoUtfConversion(const char16_t* src, int32_t src_len, DestChar* dest,
int32_t* dest_len) {
bool success = true;
auto ConvertSingleChar = [&success](char16_t in) -> int32_t {
if (!CBU16_IS_SINGLE(in) || !IsValidCodepoint(in)) {
success = false;
return kErrorCodePoint;
}
return in;
};
int32_t i = 0;
// Always have another symbol in order to avoid checking boundaries in the
// middle of the surrogate pair.
while (i < src_len - 1) {
int32_t code_point;
if (CBU16_IS_LEAD(src[i]) && CBU16_IS_TRAIL(src[i + 1])) {
code_point = CBU16_GET_SUPPLEMENTARY(src[i], src[i + 1]);
if (!IsValidCodepoint(code_point)) {
code_point = kErrorCodePoint;
success = false;
}
i += 2;
} else {
code_point = ConvertSingleChar(src[i]);
++i;
}
UnicodeAppendUnsafe(dest, dest_len, code_point);
}
if (i < src_len)
UnicodeAppendUnsafe(dest, dest_len, ConvertSingleChar(src[i]));
return success;
}
#if defined(WCHAR_T_IS_UTF32)
template <typename DestChar>
bool DoUtfConversion(const wchar_t* src, int32_t src_len, DestChar* dest,
int32_t* dest_len) {
bool success = true;
for (int32_t i = 0; i < src_len; ++i) {
int32_t code_point = src[i];
if (!IsValidCodepoint(code_point)) {
success = false;
code_point = kErrorCodePoint;
}
UnicodeAppendUnsafe(dest, dest_len, code_point);
}
return success;
}
#endif // defined(WCHAR_T_IS_UTF32)
// UtfConversion --------------------------------------------------------------
// Function template for generating all UTF conversions.
template <typename InputString, typename DestString>
bool UtfConversion(const InputString& src_str, DestString* dest_str) {
if (IsStringAscii(src_str)) {
dest_str->assign(src_str.begin(), src_str.end());
return true;
}
dest_str->resize(src_str.length() *
size_coefficient_v<typename InputString::value_type,
typename DestString::value_type>);
// Empty string is ASCII => it OK to call operator[].
auto* dest = &(*dest_str)[0];
// ICU requires 32 bit numbers.
int32_t src_len32 = static_cast<int32_t>(src_str.length());
int32_t dest_len32 = 0;
bool res = DoUtfConversion(src_str.data(), src_len32, dest, &dest_len32);
dest_str->resize(dest_len32);
dest_str->shrink_to_fit();
return res;
}
#if defined(WCHAR_T_IS_UTF16)
inline const char16_t* as_u16cstr(const wchar_t* str) {
return reinterpret_cast<const char16_t*>(str);
}
inline const char16_t* as_u16cstr(std::wstring_view str) {
return reinterpret_cast<const char16_t*>(str.data());
}
#endif
} // namespace
// UTF16 <-> UTF8 --------------------------------------------------------------
bool Utf8ToUtf16(const char* src, size_t src_len, std::u16string* output) {
return UtfConversion(std::string_view(src, src_len), output);
}
std::u16string Utf8ToUtf16(std::string_view utf8) {
std::u16string ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
Utf8ToUtf16(utf8.data(), utf8.size(), &ret);
return ret;
}
bool Utf16ToUtf8(const char16_t* src, size_t src_len, std::string* output) {
return UtfConversion(std::u16string_view(src, src_len), output);
}
std::string Utf16ToUtf8(std::u16string_view utf16) {
std::string ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
Utf16ToUtf8(utf16.data(), utf16.length(), &ret);
return ret;
}
// UTF-16 <-> Wide -------------------------------------------------------------
#if defined(WCHAR_T_IS_UTF16)
// When wide == UTF-16 the conversions are a NOP.
bool WideToUtf16(const wchar_t* src, size_t src_len, std::u16string* output) {
output->assign(src, src + src_len);
return true;
}
std::u16string WideToUtf16(std::wstring_view wide) {
return std::u16string(wide.begin(), wide.end());
}
bool Utf16ToWide(const char16_t* src, size_t src_len, std::wstring* output) {
output->assign(src, src + src_len);
return true;
}
std::wstring Utf16ToWide(std::u16string_view utf16) {
return std::wstring(utf16.begin(), utf16.end());
}
#elif defined(WCHAR_T_IS_UTF32)
bool WideToUtf16(const wchar_t* src, size_t src_len, std::u16string* output) {
return UtfConversion(std::wstring_view(src, src_len), output);
}
std::u16string WideToUtf16(std::wstring_view wide) {
std::u16string ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
WideToUtf16(wide.data(), wide.length(), &ret);
return ret;
}
bool Utf16ToWide(const char16_t* src, size_t src_len, std::wstring* output) {
return UtfConversion(std::u16string_view(src, src_len), output);
}
std::wstring Utf16ToWide(std::u16string_view utf16) {
std::wstring ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
Utf16ToWide(utf16.data(), utf16.length(), &ret);
return ret;
}
#endif // defined(WCHAR_T_IS_UTF32)
// UTF-8 <-> Wide --------------------------------------------------------------
// UTF8ToWide is the same code, regardless of whether wide is 16 or 32 bits
bool Utf8ToWide(const char* src, size_t src_len, std::wstring* output) {
return UtfConversion(std::string_view(src, src_len), output);
}
std::wstring Utf8ToWide(std::string_view utf8) {
std::wstring ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
Utf8ToWide(utf8.data(), utf8.length(), &ret);
return ret;
}
#if defined(WCHAR_T_IS_UTF16)
// Easy case since we can use the "utf" versions we already wrote above.
bool WideToUtf8(const wchar_t* src, size_t src_len, std::string* output) {
return Utf16ToUtf8(as_u16cstr(src), src_len, output);
}
std::string WideToUtf8(std::wstring_view wide) {
return Utf16ToUtf8(std::u16string_view(as_u16cstr(wide), wide.size()));
}
#elif defined(WCHAR_T_IS_UTF32)
bool WideToUtf8(const wchar_t* src, size_t src_len, std::string* output) {
return UtfConversion(std::wstring_view(src, src_len), output);
}
std::string WideToUtf8(std::wstring_view wide) {
std::string ret;
// Ignore the success flag of this call, it will do the best it can for
// invalid input, which is what we want here.
WideToUtf8(wide.data(), wide.length(), &ret);
return ret;
}
#endif // defined(WCHAR_T_IS_UTF32)
std::u16string AsciiToUtf16(std::string_view ascii) {
NL_DCHECK(IsStringAscii(ascii));
return std::u16string(ascii.begin(), ascii.end());
}
std::string Utf16ToAscii(std::u16string_view utf16) {
NL_DCHECK(IsStringAscii(utf16));
return std::string(utf16.begin(), utf16.end());
}
#if defined(WCHAR_T_IS_UTF16)
std::wstring AsciiToWide(std::string_view ascii) {
NL_DCHECK(IsStringAscii(ascii));
return std::wstring(ascii.begin(), ascii.end());
}
std::string WideToAscii(std::string_view wide) {
NL_DCHECK(IsStringAscii(wide));
return std::string(wide.begin(), wide.end());
}
#endif // defined(WCHAR_T_IS_UTF16)
bool IsStringAscii(std::string_view str) {
return DoIsStringAscii(str.data(), str.length());
}
bool IsStringAscii(std::u16string_view str) {
return DoIsStringAscii(str.data(), str.length());
}
bool IsStringUtf8(std::string_view str) {
return DoIsStringUtf8<IsValidCharacter>(str);
}
bool IsStringAscii(std::wstring_view str) {
return DoIsStringAscii(str.data(), str.length());
}
bool IsValidCodepoint(uint32_t code_point) {
// Excludes code points that are not Unicode scalar values, i.e.
// surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes
// code points larger than 0x10FFFF (the highest codepoint allowed).
// Non-characters and unassigned code points are allowed.
// https://unicode.org/glossary/#unicode_scalar_value
return code_point < 0xD800u ||
(code_point >= 0xE000u && code_point <= 0x10FFFFu);
}
void TruncateUtf8ToByteSize(const std::string& input, size_t byte_size,
std::string* output) {
NL_DCHECK(output);
if (byte_size > input.length()) {
*output = input;
return;
}
// Note: This cast is necessary because CBU8_NEXT uses int32_ts.
int32_t truncation_length = static_cast<int32_t>(byte_size);
int32_t char_index = truncation_length - 1;
const char* data = input.data();
// Using CBU8, we will move backwards from the truncation point
// to the beginning of the string looking for a valid UTF8
// character. Once a full UTF8 character is found, we will
// truncate the string to the end of that character.
while (char_index >= 0) {
int32_t prev = char_index;
int32_t code_point = 0;
CBU8_NEXT(data, char_index, truncation_length, code_point);
if (!IsValidCharacter(code_point) || !IsValidCodepoint(code_point)) {
char_index = prev - 1;
} else {
break;
}
}
if (char_index >= 0)
*output = input.substr(0, char_index);
else
output->clear();
}
std::string ToString(const char* str) {
if (str == nullptr) {
return "";
}
return std::string(str);
}
} // namespace utils
} // namespace nearby
@@ -0,0 +1,117 @@
// Copyright 2021 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_SHARING_INTERNAL_BASE_UTF_STRING_CONVERSIONS_H_
#define THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_UTF_STRING_CONVERSIONS_H_
#include <stddef.h>
#include <cstdint>
#include <string>
#include <string_view>
#include "sharing/internal/base/utf_string_configuration.h"
namespace nearby {
namespace utils {
// These convert between UTF-8, -16, and -32 strings. They are potentially slow,
// so avoid unnecessary conversions. The low-level versions return a boolean
// indicating whether the conversion was 100% valid. In this case, it will still
// do the best it can and put the result in the output buffer. The versions that
// return strings ignore this error and just return the best conversion
// possible.
bool WideToUtf8(const wchar_t* src, size_t src_len, std::string* output);
std::string WideToUtf8(std::wstring_view wide);
bool Utf8ToWide(const char* src, size_t src_len, std::wstring* output);
std::wstring Utf8ToWide(std::string_view utf8);
bool WideToUtf16(const wchar_t* src, size_t src_len, std::u16string* output);
std::u16string WideToUtf16(std::wstring_view wide);
bool Utf16ToWide(const char16_t* src, size_t src_len, std::wstring* output);
std::wstring Utf16ToWide(std::u16string_view utf16);
bool Utf8ToUtf16(const char* src, size_t src_len, std::u16string* output);
std::u16string Utf8ToUtf16(std::string_view utf8);
bool Utf16ToUtf8(const char16_t* src, size_t src_len, std::string* output);
std::string Utf16ToUtf8(std::u16string_view utf16);
// This converts an ASCII string, typically a hard coded constant, to a UTF16
// string.
std::u16string AsciiToUtf16(std::string_view ascii);
// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
// beforehand.
std::string Utf16ToAscii(std::u16string_view utf16);
bool IsStringAscii(std::string_view str);
bool IsStringAscii(std::u16string_view str);
bool IsStringAscii(std::wstring_view str);
bool IsStringUtf8(std::string_view str);
inline bool IsValidCodepoint(uint32_t code_point);
void TruncateUtf8ToByteSize(const std::string& input, size_t byte_size,
std::string* output);
#if defined(WCHAR_T_IS_UTF16)
// This converts an ASCII string, typically a hard coded constant, to a wide
// string.
std::wstring AsciiToWide(std::string_view ascii);
// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
// beforehand.
std::string WideToAscii(std::wstring_view wide);
#endif // defined(WCHAR_T_IS_UTF16)
// The conversion functions in this file should not be used to convert string
// literals. Instead, the corresponding prefixes (e.g. u"" for UTF16 or L"" for
// Wide) should be used. Deleting the overloads here catches these cases at
// compile time.
template <size_t N>
std::u16string WideToUtf16(const wchar_t (&str)[N]) {
static_assert(N == 0, "Error: Use the u\"...\" prefix instead.");
return std::u16string();
}
template <size_t N>
std::u16string Utf8ToUtf16(const char (&str)[N]) {
static_assert(N == 0, "Error: Use the u\"...\" prefix instead.");
return std::u16string();
}
template <size_t N>
std::u16string AsciiToUtf16(const char (&str)[N]) {
static_assert(N == 0, "Error: Use the u\"...\" prefix instead.");
return std::u16string();
}
// Mutable character arrays are usually only populated during runtime. Continue
// to allow this conversion.
template <size_t N>
std::u16string AsciiToUtf16(char (&str)[N]) {
return AsciiToUtf16(std::string_view(str));
}
template <typename T, size_t N>
constexpr size_t size(const T (&array)[N]) noexcept {
return N;
}
std::string ToString(const char* str);
} // namespace utils
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_INTERNAL_BASE_UTF_STRING_CONVERSIONS_H_
@@ -0,0 +1,375 @@
// Copyright 2021 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 "sharing/internal/base/utf_string_conversions.h"
#include <cstring>
#include <cwchar>
#include <sstream>
#include <string>
#include "gtest/gtest.h"
namespace nearby {
namespace utils {
namespace {
const wchar_t* const kConvertRoundtripCases[] = {
L"Google Video",
// "网页 图片 资讯更多 »"
L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
// "Παγκόσμιος Ιστός"
L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
// "Поиск страниц на русском"
L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
// "전체서비스"
L"\xc804\xccb4\xc11c\xbe44\xc2a4",
// Test characters that take more than 16 bits. This will depend on whether
// wchar_t is 16 or 32 bits.
#if defined(WCHAR_T_IS_UTF16)
L"\xd800\xdf00",
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 :
// A,B,C,D,E)
L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
#elif defined(WCHAR_T_IS_UTF32)
L"\x10300",
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 :
// A,B,C,D,E)
L"\x11d40\x11d41\x11d42\x11d43\x11d44",
#endif
};
// Helper used to test TruncateUtf8ToByteSize.
bool Truncated(const std::string& input, const size_t byte_size,
std::string* output) {
size_t prev = input.length();
TruncateUtf8ToByteSize(input, byte_size, output);
return prev != output->length();
}
} // namespace
TEST(UtfStringConversionsTest, ConvertUtf8AndWide) {
// we round-trip all the wide strings through UTF-8 to make sure everything
// agrees on the conversion. This uses the stream operators to test them
// simultaneously.
for (auto* i : kConvertRoundtripCases) {
std::ostringstream utf8;
utf8 << WideToUtf8(i);
std::wostringstream wide;
wide << Utf8ToWide(utf8.str());
EXPECT_EQ(i, wide.str());
}
}
TEST(UtfStringConversionsTest, ConvertUtf8AndWideEmptyString) {
// An empty std::wstring should be converted to an empty std::string,
// and vice versa.
std::wstring wempty;
std::string empty;
EXPECT_EQ(empty, WideToUtf8(wempty));
EXPECT_EQ(wempty, Utf8ToWide(empty));
}
TEST(UtfStringConversionsTest, ConvertUtf8ToWide) {
struct Utf8ToWideCase {
const char* utf8;
const wchar_t* wide;
bool success;
} convert_cases[] = {
// Regular UTF-8 input.
{"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
// Non-character is passed through.
{"\xef\xbf\xbfHello", L"\xffffHello", true},
// Truncated UTF-8 sequence.
{"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
// Truncated off the end.
{"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
// Non-shortest-form UTF-8.
{"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\xfffd\xfffd\xfffd\x597d", false},
// This UTF-8 character is decoded to a UTF-16 surrogate, which is illegal.
{"\xed\xb0\x80", L"\xfffd\xfffd\xfffd", false},
// Non-BMP characters. The second is a non-character regarded as valid.
// The result will either be in UTF-16 or UTF-32.
#if defined(WCHAR_T_IS_UTF16)
{"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
{"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
#elif defined(WCHAR_T_IS_UTF32)
{"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
{"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
#endif
};
for (const auto& i : convert_cases) {
std::wstring converted;
EXPECT_EQ(i.success, Utf8ToWide(i.utf8, strlen(i.utf8), &converted));
std::wstring expected(i.wide);
EXPECT_EQ(expected, converted);
}
// Manually test an embedded NULL.
std::wstring converted;
EXPECT_TRUE(Utf8ToWide("\00Z\t", 3, &converted));
ASSERT_EQ(3U, converted.length());
EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
EXPECT_EQ('Z', converted[1]);
EXPECT_EQ('\t', converted[2]);
// Make sure that conversion replaces, not appends.
EXPECT_TRUE(Utf8ToWide("B", 1, &converted));
ASSERT_EQ(1U, converted.length());
EXPECT_EQ('B', converted[0]);
}
#if defined(WCHAR_T_IS_UTF16)
// This test is only valid when wchar_t == UTF-16.
TEST(UtfStringConversionsTest, ConvertUtf16ToUtf8) {
struct WideToUtf8Case {
const wchar_t* utf16;
const char* utf8;
bool success;
} convert_cases[] = {
// Regular UTF-16 input.
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
// Test a non-BMP character.
{L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
// Non-characters are passed through.
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
{L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
// The first character is a truncated UTF-16 character.
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
// Truncated at the end.
{L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd", false},
};
for (const auto& test : convert_cases) {
std::string converted;
EXPECT_EQ(test.success,
WideToUtf8(test.utf16, wcslen(test.utf16), &converted));
std::string expected(test.utf8);
EXPECT_EQ(expected, converted);
}
}
#elif defined(WCHAR_T_IS_UTF32)
// This test is only valid when wchar_t == UTF-32.
TEST(UtfStringConversionsTest, ConvertUtf32ToUtf8) {
struct WideToUtf8Case {
const wchar_t* utf32;
const char* utf8;
bool success;
} convert_cases[] = {
// Regular 16-bit input.
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
// Test a non-BMP character.
{L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
// Non-characters are passed through.
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
{L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
// Invalid Unicode code points.
{L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
// The first character is a truncated UTF-16 character.
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
{L"\xdc01Hello", "\xef\xbf\xbdHello", false},
};
for (const auto& test : convert_cases) {
std::string converted;
EXPECT_EQ(test.success,
WideToUtf8(test.utf32, wcslen(test.utf32), &converted));
std::string expected(test.utf8);
EXPECT_EQ(expected, converted);
}
}
#endif // defined(WCHAR_T_IS_UTF32)
TEST(UtfStringConversionsTest, TruncateUtf8ToByteSize) {
std::string output;
// Empty strings and invalid byte_size arguments
EXPECT_FALSE(Truncated(std::string(), 0, &output));
EXPECT_EQ(output, "");
EXPECT_TRUE(Truncated("\xe1\x80\xbf", 0, &output));
EXPECT_EQ(output, "");
EXPECT_FALSE(Truncated("\xe1\x80\xbf", static_cast<size_t>(-1), &output));
EXPECT_FALSE(Truncated("\xe1\x80\xbf", 4, &output));
// Testing the truncation of valid UTF8 correctly
EXPECT_TRUE(Truncated("abc", 2, &output));
EXPECT_EQ(output, "ab");
EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 2, &output));
EXPECT_EQ(output.compare("\xc2\x81"), 0);
EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 3, &output));
EXPECT_EQ(output.compare("\xc2\x81"), 0);
EXPECT_FALSE(Truncated("\xc2\x81\xc2\x81", 4, &output));
EXPECT_EQ(output.compare("\xc2\x81\xc2\x81"), 0);
{
const char array[] = "\x00\x00\xc2\x81\xc2\x81";
const std::string array_string(array, size(array));
EXPECT_TRUE(Truncated(array_string, 4, &output));
EXPECT_EQ(output.compare(std::string("\x00\x00\xc2\x81", 4)), 0);
}
{
const char array[] = "\x00\xc2\x81\xc2\x81";
const std::string array_string(array, size(array));
EXPECT_TRUE(Truncated(array_string, 4, &output));
EXPECT_EQ(output.compare(std::string("\x00\xc2\x81", 3)), 0);
}
// Testing invalid UTF8
EXPECT_TRUE(Truncated("\xed\xa0\x80\xed\xbf\xbf", 6, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xed\xa0\x8f", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xed\xbf\xbf", 3, &output));
EXPECT_EQ(output.compare(""), 0);
// Testing invalid UTF8 mixed with valid UTF8
EXPECT_FALSE(Truncated("\xe1\x80\xbf", 3, &output));
EXPECT_EQ(output.compare("\xe1\x80\xbf"), 0);
EXPECT_FALSE(Truncated("\xf1\x80\xa0\xbf", 4, &output));
EXPECT_EQ(output.compare("\xf1\x80\xa0\xbf"), 0);
EXPECT_FALSE(Truncated("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf", 10, &output));
EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf"), 0);
EXPECT_TRUE(
Truncated("a\xc2\x81\xe1\x80\xbf\xf1"
"a"
"\x80\xa0",
10, &output));
EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1"
"a"),
0);
EXPECT_FALSE(
Truncated("\xef\xbb\xbf"
"abc",
6, &output));
EXPECT_EQ(output.compare("\xef\xbb\xbf"
"abc"),
0);
// Overlong sequences
EXPECT_TRUE(Truncated("\xc0\x80", 2, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xc1\x80\xc1\x81", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xe0\x80\x80", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xe0\x82\x80", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xe0\x9f\xbf", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf0\x80\x80\x8D", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf0\x80\x82\x91", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf0\x80\xa0\x80", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf0\x8f\xbb\xbf", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf8\x80\x80\x80\xbf", 5, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xfc\x80\x80\x80\xa0\xa5", 6, &output));
EXPECT_EQ(output.compare(""), 0);
// Beyond U+10FFFF (the upper limit of Unicode codespace)
EXPECT_TRUE(Truncated("\xf4\x90\x80\x80", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf8\xa0\xbf\x80\xbf", 5, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xfc\x9c\xbf\x80\xbf\x80", 6, &output));
EXPECT_EQ(output.compare(""), 0);
// BOMs in UTF-16(BE|LE) and UTF-32(BE|LE)
EXPECT_TRUE(Truncated("\xfe\xff", 2, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xff\xfe", 2, &output));
EXPECT_EQ(output.compare(""), 0);
{
const char array[] = "\x00\x00\xfe\xff";
const std::string array_string(array, size(array));
EXPECT_TRUE(Truncated(array_string, 4, &output));
EXPECT_EQ(output.compare(std::string("\x00\x00", 2)), 0);
}
// Variants on the previous test
{
const char array[] = "\xff\xfe\x00\x00";
const std::string array_string(array, 4);
EXPECT_FALSE(Truncated(array_string, 4, &output));
EXPECT_EQ(output.compare(std::string("\xff\xfe\x00\x00", 4)), 0);
}
{
const char array[] = "\xff\x00\x00\xfe";
const std::string array_string(array, size(array));
EXPECT_TRUE(Truncated(array_string, 4, &output));
EXPECT_EQ(output.compare(std::string("\xff\x00\x00", 3)), 0);
}
// Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and <FDD0,FDEF>
EXPECT_TRUE(Truncated("\xef\xbf\xbe", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf0\x8f\xbf\xbe", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xf3\xbf\xbf\xbf", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xef\xb7\x90", 3, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_TRUE(Truncated("\xef\xb7\xaf", 3, &output));
EXPECT_EQ(output.compare(""), 0);
// Strings in legacy encodings that are valid in UTF-8, but
// are invalid as UTF-8 in real data.
EXPECT_TRUE(Truncated("caf\xe9", 4, &output));
EXPECT_EQ(output.compare("caf"), 0);
EXPECT_TRUE(Truncated("\xb0\xa1\xb0\xa2", 4, &output));
EXPECT_EQ(output.compare(""), 0);
EXPECT_FALSE(Truncated("\xa7\x41\xa6\x6e", 4, &output));
EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
EXPECT_TRUE(Truncated("\xa7\x41\xa6\x6e\xd9\xee\xe4\xee", 7, &output));
EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
// Testing using the same string as input and output.
EXPECT_FALSE(Truncated(output, 4, &output));
EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
EXPECT_TRUE(Truncated(output, 3, &output));
EXPECT_EQ(output.compare("\xa7\x41"), 0);
// "abc" with U+201[CD] in windows-125[0-8]
EXPECT_TRUE(
Truncated("\x93"
"abc\x94",
5, &output));
EXPECT_EQ(output.compare("\x93"
"abc"),
0);
// U+0639 U+064E U+0644 U+064E in ISO-8859-6
EXPECT_TRUE(Truncated("\xd9\xee\xe4\xee", 4, &output));
EXPECT_EQ(output.compare(""), 0);
// U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7
EXPECT_TRUE(Truncated("\xe3\xe5\xe9\xdC", 4, &output));
EXPECT_EQ(output.compare(""), 0);
}
} // namespace utils
} // namespace nearby
+58
View File
@@ -0,0 +1,58 @@
// 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_SHARING_NEARBY_CONNECTION_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTION_H_
#include <cstdint>
#include <functional>
#include <optional>
#include <vector>
namespace nearby {
namespace sharing {
// A socket-like wrapper around Nearby Connections that allows for asynchronous
// reads and writes.
class NearbyConnection {
public:
using ReadCallback =
std::function<void(std::optional<std::vector<uint8_t>> bytes)>;
virtual ~NearbyConnection() = default;
// Reads a stream of bytes from the remote device. Invoke |callback| when
// there is incoming data or when the socket is closed. Previously set
// callback will be replaced by |callback|. Must not be used on an already
// closed connection.
virtual void Read(ReadCallback callback) = 0;
// Writes an outgoing stream of bytes to the remote device asynchronously.
// Must not be used on an already closed connection.
virtual void Write(std::vector<uint8_t> bytes) = 0;
// Closes the socket and disconnects from the remote device. This object will
// be invalidated after |callback| in SetDisconnectionListener is invoked.
virtual void Close() = 0;
// Listens to the socket being closed. Invoke |callback| when the socket is
// closed. This object will be invalidated after |listener| is invoked.
// Previously set listener will be replaced by |listener|.
virtual void SetDisconnectionListener(std::function<void()> listener) = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTION_H_
+111
View File
@@ -0,0 +1,111 @@
// Copyright 2022-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 "sharing/nearby_connection_impl.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex_lock.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
NearbyConnectionImpl::NearbyConnectionImpl(
nearby::DeviceInfo& device_info,
NearbyConnectionsManager* nearby_connections_manager,
absl::string_view endpoint_id)
: device_info_(device_info),
nearby_connections_manager_(nearby_connections_manager),
endpoint_id_(endpoint_id) {
if (!device_info_.PreventSleep()) {
NL_LOG(WARNING) << __func__ << ":Failed to prevent device sleep.";
}
}
NearbyConnectionImpl::~NearbyConnectionImpl() {
MutexLock lock(&mutex_);
if (!device_info_.AllowSleep()) {
NL_LOG(ERROR) << __func__ << ":Failed to allow device sleep.";
}
if (disconnect_listener_) {
disconnect_listener_();
}
if (read_callback_) {
read_callback_(std::nullopt);
}
}
void NearbyConnectionImpl::Read(ReadCallback callback) {
MutexLock lock(&mutex_);
if (reads_.empty()) {
read_callback_ = std::move(callback);
return;
}
std::vector<uint8_t> bytes = std::move(reads_.front());
reads_.pop();
std::move(callback)(std::move(bytes));
}
void NearbyConnectionImpl::Write(std::vector<uint8_t> bytes) {
MutexLock lock(&mutex_);
Payload payload(bytes);
nearby_connections_manager_->Send(
endpoint_id_, std::make_unique<Payload>(payload),
/*listener=*/
std::weak_ptr<NearbyConnectionsManager::PayloadStatusListener>());
}
void NearbyConnectionImpl::Close() {
MutexLock lock(&mutex_);
// As [this] therefore endpoint_id_ will be destroyed in Disconnect, make a
// copy of [endpoint_id] as the parameter is a const ref.
nearby_connections_manager_->Disconnect(endpoint_id_);
}
void NearbyConnectionImpl::SetDisconnectionListener(
std::function<void()> listener) {
MutexLock lock(&mutex_);
disconnect_listener_ = std::move(listener);
}
void NearbyConnectionImpl::WriteMessage(std::vector<uint8_t> bytes) {
MutexLock lock(&mutex_);
if (read_callback_) {
auto callback = std::move(read_callback_);
read_callback_ = nullptr;
callback(std::move(bytes));
return;
}
reads_.push(std::move(bytes));
}
} // namespace sharing
} // namespace nearby
+68
View File
@@ -0,0 +1,68 @@
// Copyright 2022-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_SHARING_NEARBY_CONNECTION_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTION_IMPL_H_
#include <cstdint>
#include <functional>
#include <queue>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex.h"
#include "sharing/nearby_connection.h"
namespace nearby {
namespace sharing {
class NearbyConnectionsManager;
class NearbyConnectionImpl : public NearbyConnection {
public:
NearbyConnectionImpl(nearby::DeviceInfo& device_info,
NearbyConnectionsManager* nearby_connections_manager,
absl::string_view endpoint_id);
~NearbyConnectionImpl() override;
// NearbyConnection:
void Read(ReadCallback callback) override;
void Write(std::vector<uint8_t> bytes) override;
void Close() override;
void SetDisconnectionListener(std::function<void()> listener) override;
// Add bytes to the read queue, notifying ReadCallback.
void WriteMessage(std::vector<uint8_t> bytes);
private:
nearby::DeviceInfo& device_info_;
NearbyConnectionsManager* const nearby_connections_manager_;
std::string endpoint_id_;
RecursiveMutex mutex_;
ReadCallback read_callback_ ABSL_GUARDED_BY(mutex_) = nullptr;
std::function<void()> disconnect_listener_ ABSL_GUARDED_BY(mutex_);
// A read queue. The data that we've read from the remote device ends up here
// until Read() is called to dequeue it.
std::queue<std::vector<uint8_t>> reads_ ABSL_GUARDED_BY(mutex_);
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTION_IMPL_H_
+87
View File
@@ -0,0 +1,87 @@
// 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 "sharing/nearby_connection_impl.h"
#include <memory>
#include <optional>
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "internal/test/fake_device_info.h"
#include "internal/test/fake_task_runner.h"
#include "sharing/fake_nearby_connections_manager.h"
#include "sharing/incoming_frames_reader.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/nearby_sharing_decoder_impl.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
namespace {
TEST(NearbyConnectionImpl, DestructorBeforeReaderDestructor) {
FakeNearbyConnectionsManager connection_manager;
FakeContext context;
FakeDeviceInfo device_info;
NearbySharingDecoderImpl decoder;
bool called = false;
auto connection = std::make_unique<NearbyConnectionImpl>(
device_info, &connection_manager, "test");
auto frames_reader = std::make_shared<IncomingFramesReader>(
&context, &decoder, connection.get());
absl::Notification notification;
frames_reader->ReadFrame(
[&](std::optional<nearby::sharing::service::proto::V1Frame> frame) {
called = true;
notification.Notify();
});
EXPECT_TRUE(FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Seconds(1)));
connection.reset();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(1)));
EXPECT_TRUE(called);
}
TEST(NearbyConnectionImpl, DestructorAfterReaderDestructor) {
FakeNearbyConnectionsManager connection_manager;
FakeContext context;
FakeDeviceInfo device_info;
NearbySharingDecoderImpl decoder;
std::optional<nearby::sharing::service::proto::V1Frame> frame_result;
auto connection = std::make_unique<NearbyConnectionImpl>(
device_info, &connection_manager, "test");
auto frames_reader = std::make_shared<IncomingFramesReader>(
&context, &decoder, connection.get());
absl::Notification notification;
frames_reader->ReadFrame(
[&](std::optional<nearby::sharing::service::proto::V1Frame> frame) {
frame_result = frame;
notification.Notify();
});
EXPECT_TRUE(FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Seconds(1)));
frames_reader.reset();
connection.reset();
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(1)));
EXPECT_FALSE(frame_result.has_value());
}
} // namespace
} // namespace sharing
} // namespace nearby
+86
View File
@@ -0,0 +1,86 @@
// 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 "sharing/nearby_connections_manager.h"
#include <memory>
#include <string>
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
// static
// LINT.IfChange(status_enum)
std::string NearbyConnectionsManager::ConnectionsStatusToString(
ConnectionsStatus status) {
switch (status) {
case ConnectionsStatus::kSuccess:
return "kSuccess";
case ConnectionsStatus::kError:
return "kError";
case ConnectionsStatus::kOutOfOrderApiCall:
return "kOutOfOrderApiCall";
case ConnectionsStatus::kAlreadyHaveActiveStrategy:
return "kAlreadyHaveActiveStrategy";
case ConnectionsStatus::kAlreadyAdvertising:
return "kAlreadyAdvertising";
case ConnectionsStatus::kAlreadyDiscovering:
return "kAlreadyDiscovering";
case ConnectionsStatus::kEndpointIOError:
return "kEndpointIOError";
case ConnectionsStatus::kEndpointUnknown:
return "kEndpointUnknown";
case ConnectionsStatus::kConnectionRejected:
return "kConnectionRejected";
case ConnectionsStatus::kAlreadyConnectedToEndpoint:
return "kAlreadyConnectedToEndpoint";
case ConnectionsStatus::kNotConnectedToEndpoint:
return "kNotConnectedToEndpoint";
case ConnectionsStatus::kBluetoothError:
return "kBluetoothError";
case ConnectionsStatus::kBleError:
return "kBleError";
case ConnectionsStatus::kWifiLanError:
return "kWifiLanError";
case ConnectionsStatus::kPayloadUnknown:
return "kPayloadUnknown";
case ConnectionsStatus::kAlreadyListening:
return "kAlreadyListening";
case ConnectionsStatus::kReset:
return "kReset";
case ConnectionsStatus::kTimeout:
return "kTimeout";
case ConnectionsStatus::kUnknown:
// fall through
default:
return "Unknown";
}
}
// LINT.ThenChange()
NearbyConnectionsManager::PayloadStatusListener::PayloadStatusListener() =
default;
NearbyConnectionsManager::PayloadStatusListener::~PayloadStatusListener() =
default;
std::weak_ptr<NearbyConnectionsManager::PayloadStatusListener>
NearbyConnectionsManager::PayloadStatusListener::GetWeakPtr() {
return this->weak_from_this();
}
} // namespace sharing
} // namespace nearby
+172
View File
@@ -0,0 +1,172 @@
// 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_SHARING_NEARBY_CONNECTIONS_MANAGER_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/proto/enums.pb.h"
namespace nearby {
namespace sharing {
class NearbyConnection;
class PayloadTransferUpdatePtr;
using ConnectionsStatus = nearby::sharing::Status;
class NearbyConnectionsManager {
public:
using ConnectionsCallback = std::function<void(ConnectionsStatus status)>;
using NearbyConnectionCallback =
std::function<void(NearbyConnection*, Status status)>;
// A callback for handling incoming connections while advertising.
class IncomingConnectionListener {
public:
virtual ~IncomingConnectionListener() = default;
// `endpoint_info`is returned from remote devices and should be parsed in
// utility process.
virtual void OnIncomingConnection(absl::string_view endpoint_id,
absl::Span<const uint8_t> endpoint_info,
NearbyConnection* connection) = 0;
};
// A callback for handling discovered devices while discovering.
class DiscoveryListener {
public:
virtual ~DiscoveryListener() = default;
// `endpoint_info` is returned from remote devices and should be parsed in
// utility process.
virtual void OnEndpointDiscovered(
absl::string_view endpoint_id,
absl::Span<const uint8_t> endpoint_info) = 0;
// A callback triggered when the endpoint is lost.
virtual void OnEndpointLost(absl::string_view endpoint_id) = 0;
};
// A callback for tracking the status of a payload (both incoming and
// outgoing).
class PayloadStatusListener
: public std::enable_shared_from_this<PayloadStatusListener> {
public:
PayloadStatusListener();
virtual ~PayloadStatusListener();
std::weak_ptr<PayloadStatusListener> GetWeakPtr();
// Note: `upgraded_medium` is passed in for use in metrics, and it is
// absl::nullopt if the bandwidth has not upgraded yet or if the upgrade
// status is not known.
virtual void OnStatusUpdate(std::unique_ptr<PayloadTransferUpdate> update,
std::optional<Medium> upgraded_medium) = 0;
};
// Converts the status to a logging-friendly string.
static std::string ConnectionsStatusToString(ConnectionsStatus status);
NearbyConnectionsManager() = default;
virtual ~NearbyConnectionsManager() = default;
// Disconnects from all endpoints and shut down Nearby Connections.
// As a side effect of this call, both StopAdvertising and StopDiscovery may
// be invoked if Nearby Connections is advertising or discovering.
virtual void Shutdown() = 0;
// Starts advertising through Nearby Connections. Caller is expected to ensure
// `listener` remains valid until StopAdvertising is called.
virtual void StartAdvertising(std::vector<uint8_t> endpoint_info,
IncomingConnectionListener* listener,
PowerLevel power_level,
proto::DataUsage data_usage,
ConnectionsCallback callback) = 0;
// Stops advertising through Nearby Connections.
virtual void StopAdvertising(ConnectionsCallback callback) = 0;
// Starts discovery through Nearby Connections. Caller is expected to ensure
// `listener` remains valid until StopDiscovery is called.
virtual void StartDiscovery(DiscoveryListener* listener,
proto::DataUsage data_usage,
ConnectionsCallback callback) = 0;
// Stops discovery through Nearby Connections.
virtual void StopDiscovery() = 0;
// Connects to remote `endpoint_id` through Nearby Connections.
virtual void Connect(
std::vector<uint8_t> endpoint_info, absl::string_view endpoint_id,
std::optional<std::vector<uint8_t>> bluetooth_mac_address,
proto::DataUsage data_usage, TransportType transport_type,
NearbyConnectionCallback callback) = 0;
// Disconnects from remote `endpoint_id` through Nearby Connections.
virtual void Disconnect(absl::string_view endpoint_id) = 0;
// Sends `payload` through Nearby Connections.
virtual void Send(absl::string_view endpoint_id,
std::unique_ptr<Payload> payload,
std::weak_ptr<PayloadStatusListener> listener) = 0;
// Register a `listener` with `payload_id`.
virtual void RegisterPayloadStatusListener(
int64_t payload_id, std::weak_ptr<PayloadStatusListener> listener) = 0;
// Register a `file_path` for receiving incoming payload with `payload_id`.
virtual void RegisterPayloadPath(int64_t payload_id,
const std::filesystem::path& file_path,
ConnectionsCallback callback) = 0;
// Gets the payload associated with `payload_id` if available.
virtual Payload* GetIncomingPayload(int64_t payload_id) = 0;
// Cancels a Payload currently in-flight to or from remote endpoints.
virtual void Cancel(int64_t payload_id) = 0;
// Clears all incoming payloads.
virtual void ClearIncomingPayloads() = 0;
// Gets the raw authentication token for the `endpoint_id`.
virtual std::optional<std::vector<uint8_t>> GetRawAuthenticationToken(
absl::string_view endpoint_id) = 0;
// Initiates bandwidth upgrade for `endpoint_id`.
virtual void UpgradeBandwidth(absl::string_view endpoint_id) = 0;
// Sets a custom save path.
virtual void SetCustomSavePath(absl::string_view custom_save_path) = 0;
// Dump internal state for debugging purposes.
virtual std::string Dump() const = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_H_
@@ -0,0 +1,40 @@
// 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 "sharing/nearby_connections_manager_factory.h"
#include <memory>
#include "internal/analytics/event_logger.h"
#include "internal/platform/device_info.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_manager_impl.h"
#include "sharing/nearby_connections_service_impl.h"
namespace nearby {
namespace sharing {
std::unique_ptr<NearbyConnectionsManager>
NearbyConnectionsManagerFactory::CreateConnectionsManager(
LinkType link_type, Context* context,
nearby::DeviceInfo& device_info,
nearby::analytics::EventLogger* event_logger) {
return std::make_unique<NearbyConnectionsManagerImpl>(
context, *context->GetConnectivityManager(), device_info,
std::make_unique<NearbyConnectionsServiceImpl>(event_logger));
}
} // namespace sharing
} // namespace nearby
@@ -0,0 +1,46 @@
// 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_SHARING_NEARBY_CONNECTIONS_MANAGER_FACTORY_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_FACTORY_H_
#include <memory>
#include "internal/analytics/event_logger.h"
#include "internal/platform/device_info.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_sharing_service_factory.h"
namespace nearby {
namespace sharing {
class NearbyConnectionsManagerFactory {
public:
using LinkType = NearbySharingServiceFactory::LinkType;
// Return a singleton instance of NearbyConnectionsManagerFactory.
static std::unique_ptr<NearbyConnectionsManager> CreateConnectionsManager(
NearbySharingServiceFactory::LinkType link_type, Context* context,
nearby::DeviceInfo& device_info,
nearby::analytics::EventLogger* event_logger = nullptr);
private:
NearbyConnectionsManagerFactory() = default;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_FACTORY_H_
+906
View File
@@ -0,0 +1,906 @@
// Copyright 2022-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 "sharing/nearby_connections_manager_impl.h"
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <memory>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex_lock.h"
#include "sharing/advertisement.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/constants.h"
#include "sharing/flags/nearby_sharing_feature_flags.h"
#include "sharing/internal/api/bluetooth_adapter.h"
#include "sharing/internal/base/encode.h"
#include "sharing/internal/public/connectivity_manager.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connection_impl.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/transfer_manager.h"
namespace nearby {
namespace sharing {
namespace {
using ::nearby::sharing::proto::DataUsage;
constexpr char kServiceId[] = "NearbySharing";
constexpr char kFastAdvertisementServiceUuid[] =
"0000fef3-0000-1000-8000-00805f9b34fb";
constexpr Strategy kStrategy = Strategy::kP2pPointToPoint;
const uint8_t kMinimumAdvertisementSize =
/* Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)=
*/
1 + Advertisement::kSaltSize +
Advertisement::kMetadataEncryptionKeyHashByteSize;
bool ShouldUseInternet(ConnectivityManager& connectivity_manager,
DataUsage data_usage, PowerLevel power_level) {
// We won't use the internet if the user requested we don't.
if (data_usage == DataUsage::OFFLINE_DATA_USAGE) return false;
// We won't use the internet in a low power mode.
if (power_level == PowerLevel::kLowPower) return false;
ConnectivityManager::ConnectionType connection_type =
connectivity_manager.GetConnectionType();
// Verify that this network has an internet connection.
if (connection_type == ConnectivityManager::ConnectionType::kNone) {
NL_VLOG(1) << __func__ << ": No internet connection.";
return false;
}
if (data_usage == DataUsage::WIFI_ONLY_DATA_USAGE &&
connection_type != ConnectivityManager::ConnectionType::kWifi) {
return false;
}
// We're online, the user hasn't disabled Wi-Fi, let's use it!
return true;
}
bool ShouldEnableWebRtc(ConnectivityManager& connectivity_manager,
DataUsage data_usage, PowerLevel power_level) {
return NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::
kEnableMediumWebRtc) &&
ShouldUseInternet(connectivity_manager, data_usage, power_level);
}
bool ShouldEnableWifiLan(ConnectivityManager& connectivity_manager) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::
kEnableMediumWifiLan)) {
return false;
}
ConnectivityManager::ConnectionType connection_type =
connectivity_manager.GetConnectionType();
bool is_connection_wifi_or_ethernet =
connection_type == ConnectivityManager::ConnectionType::kWifi ||
connection_type == ConnectivityManager::ConnectionType::kEthernet;
return is_connection_wifi_or_ethernet;
}
bool ShouldEnableBleForTransfers() {
return NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::kEnableBleForTransfer);
}
std::string MediumSelectionToString(const MediumSelection& mediums) {
std::stringstream ss;
ss << "{";
if (mediums.bluetooth) ss << "bluetooth ";
if (mediums.ble) ss << "ble ";
if (mediums.web_rtc) ss << "webrtc ";
if (mediums.wifi_lan) ss << "wifilan ";
ss << "}";
return ss.str();
}
} // namespace
NearbyConnectionsManagerImpl::NearbyConnectionsManagerImpl(
Context* context,
ConnectivityManager& connectivity_manager,
nearby::DeviceInfo& device_info,
std::unique_ptr<NearbyConnectionsService> nearby_connections_service)
: context_(context),
connectivity_manager_(connectivity_manager),
device_info_(device_info),
nearby_connections_service_(std::move(nearby_connections_service)) {}
NearbyConnectionsManagerImpl::~NearbyConnectionsManagerImpl() {
ClearIncomingPayloads();
}
void NearbyConnectionsManagerImpl::Shutdown() { Reset(); }
void NearbyConnectionsManagerImpl::StartAdvertising(
std::vector<uint8_t> endpoint_info, IncomingConnectionListener* listener,
PowerLevel power_level, DataUsage data_usage,
ConnectionsCallback callback) {
NL_DCHECK(listener);
NL_DCHECK(!incoming_connection_listener_);
if (!nearby_connections_service_) {
std::move(callback)(ConnectionsStatus::kError);
return;
}
bool is_high_power = power_level == PowerLevel::kHighPower;
bool use_ble = true;
MediumSelection allowed_mediums = MediumSelection(
/*bluetooth=*/is_high_power, /*ble=*/use_ble,
// Using kHighPower here rather than power_level to signal that power
// level isn't a factor when deciding whether to allow WebRTC
// upgrades from this advertisement.
ShouldEnableWebRtc(connectivity_manager_, data_usage,
PowerLevel::kHighPower),
/*wifi_lan=*/
ShouldEnableWifiLan(connectivity_manager_),
/*wifi_hotspot=*/true);
NL_VLOG(1) << __func__ << ": "
<< "is_high_power=" << (is_high_power ? "yes" : "no")
<< ", data_usage=" << static_cast<int>(data_usage)
<< ", allowed_mediums="
<< MediumSelectionToString(allowed_mediums);
// Nearby Sharing manually controls Wi-Fi/Bluetooth upgrade. Frequent
// Bluetooth connection drops were observed during upgrades for Bluetooth
// transfers. Android has similar logic to handle upgrades, please check
// b/161880863 for more information.
bool auto_upgrade_bandwidth = false;
incoming_connection_listener_ = listener;
NearbyConnectionsService::ConnectionListener connection_listener;
connection_listener.initiated_cb =
[&](absl::string_view endpoint_id,
const ConnectionInfo& connection_info) {
OnConnectionInitiated(endpoint_id, connection_info);
};
connection_listener.accepted_cb = [&](absl::string_view endpoint_id) {
OnConnectionAccepted(endpoint_id);
};
connection_listener.rejected_cb = [&](absl::string_view endpoint_id,
Status status) {
OnConnectionRejected(endpoint_id, status);
};
connection_listener.disconnected_cb = [&](absl::string_view endpoint_id) {
OnDisconnected(endpoint_id);
};
connection_listener.bandwidth_changed_cb = [&](absl::string_view endpoint_id,
Medium medium) {
OnBandwidthChanged(endpoint_id, medium);
};
// Check if BLE hardware supports Extended Advertising
bool extended_advertising_supported =
context_->GetBluetoothAdapter().IsExtendedAdvertisingSupported();
Uuid fast_advertisement_service_uuid;
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::kEnableBleV2)) {
NL_LOG(INFO) << __func__
<< ": Nearby Sharing flag kEnableBleV2 is enabled.";
// Uses fast advertisement when advertisement data size is less than
// kMinimumAdvertisementSize. Nearby Connections will decide whether to use
// GATT server with this information.
if (endpoint_info.size() > kMinimumAdvertisementSize) {
fast_advertisement_service_uuid = Uuid("");
} else {
fast_advertisement_service_uuid = Uuid(kFastAdvertisementServiceUuid);
}
} else {
NL_LOG(INFO) << __func__
<< ": Nearby Sharing flag kEnableBleV2 is disabled.";
// Only use Fast Advertisement if Extended Advertising is not supported
if (extended_advertising_supported) {
// Empty string to instruct Nearby Connection BLE not to use Fast
// Advertisement
fast_advertisement_service_uuid = Uuid("");
} else {
// Handle advertisement on device without BLE advertisement extension.
if (endpoint_info.size() > kMinimumAdvertisementSize) {
// cannot use Fast Advertisement, because the endpoint info size exceeds
// the limitation of Fast Advertisement.
fast_advertisement_service_uuid = Uuid("");
} else {
fast_advertisement_service_uuid = Uuid(kFastAdvertisementServiceUuid);
}
}
}
nearby_connections_service_->StartAdvertising(
kServiceId, endpoint_info,
AdvertisingOptions(
kStrategy, std::move(allowed_mediums), auto_upgrade_bandwidth,
/*enforce_topology_constraints=*/true,
/*enable_bluetooth_listening=*/use_ble,
/*enable_webrtc_listening=*/
ShouldEnableWebRtc(connectivity_manager_, data_usage, power_level),
/*fast_advertisement_service_uuid=*/
fast_advertisement_service_uuid),
std::move(connection_listener), std::move(callback));
}
void NearbyConnectionsManagerImpl::StopAdvertising(
ConnectionsCallback callback) {
incoming_connection_listener_ = nullptr;
if (!nearby_connections_service_) {
std::move(callback)(ConnectionsStatus::kSuccess);
return;
}
nearby_connections_service_->StopAdvertising(kServiceId, std::move(callback));
}
void NearbyConnectionsManagerImpl::StartDiscovery(
DiscoveryListener* listener, DataUsage data_usage,
ConnectionsCallback callback) {
NL_DCHECK(listener);
if (!nearby_connections_service_) {
std::move(callback)(ConnectionsStatus::kError);
return;
}
MediumSelection allowed_mediums = MediumSelection(
/*bluetooth=*/true,
/*ble=*/true,
/*web_rtc=*/
ShouldEnableWebRtc(connectivity_manager_, data_usage,
PowerLevel::kHighPower),
/*wifi_lan=*/
ShouldEnableWifiLan(connectivity_manager_),
/*wifi_hotspot=*/true);
NL_VLOG(1) << __func__ << ": "
<< "data_usage=" << static_cast<int>(data_usage)
<< ", allowed_mediums="
<< MediumSelectionToString(allowed_mediums);
discovery_listener_ = listener;
NearbyConnectionsService::DiscoveryListener service_discovery_listener;
service_discovery_listener.endpoint_found_cb =
[&](absl::string_view endpoint_id, const DiscoveredEndpointInfo& info) {
OnEndpointFound(endpoint_id, info);
};
service_discovery_listener.endpoint_lost_cb =
[&](absl::string_view endpoint_id) { OnEndpointLost(endpoint_id); };
nearby_connections_service_->StartDiscovery(
kServiceId,
DiscoveryOptions(kStrategy, std::move(allowed_mediums),
Uuid(kFastAdvertisementServiceUuid),
/*is_out_of_band_connection=*/false),
std::move(service_discovery_listener), std::move(callback));
}
void NearbyConnectionsManagerImpl::StopDiscovery() {
MutexLock lock(&mutex_);
discovered_endpoints_.clear();
discovery_listener_ = nullptr;
if (!nearby_connections_service_) {
return;
}
nearby_connections_service_->StopDiscovery(
kServiceId, [&](ConnectionsStatus status) {
NL_VLOG(1) << __func__
<< ": Stop discovery attempted over Nearby "
"Connections with result: "
<< ConnectionsStatusToString(status);
});
}
void NearbyConnectionsManagerImpl::Connect(
std::vector<uint8_t> endpoint_info, absl::string_view endpoint_id,
std::optional<std::vector<uint8_t>> bluetooth_mac_address,
DataUsage data_usage, TransportType transport_type,
NearbyConnectionCallback callback) {
MutexLock lock(&mutex_);
if (!nearby_connections_service_) {
callback(nullptr, Status::kError);
return;
}
if (bluetooth_mac_address.has_value() && bluetooth_mac_address->size() != 6) {
bluetooth_mac_address.reset();
}
MediumSelection allowed_mediums = MediumSelection(
/*bluetooth=*/true,
/*ble=*/ShouldEnableBleForTransfers(),
ShouldEnableWebRtc(connectivity_manager_, data_usage,
PowerLevel::kHighPower),
/*wifi_lan=*/
ShouldEnableWifiLan(connectivity_manager_),
/*wifi_hotspot=*/transport_type == TransportType::kHighQuality);
NL_VLOG(1) << __func__ << ": "
<< "data_usage=" << static_cast<int>(data_usage)
<< ", allowed_mediums="
<< MediumSelectionToString(allowed_mediums);
[[maybe_unused]] auto result =
pending_outgoing_connections_.emplace(endpoint_id, std::move(callback));
NL_DCHECK(result.second);
auto timeout_timer = context_->CreateTimer();
timeout_timer->Start(
kInitiateNearbyConnectionTimeout / absl::Milliseconds(1), 0,
[&, endpoint_id]() { OnConnectionTimedOut(endpoint_id); });
connect_timeout_timers_.emplace(endpoint_id, std::move(timeout_timer));
NearbyConnectionsService::ConnectionListener connection_listener;
connection_listener.initiated_cb =
[&](absl::string_view endpoint_id,
const ConnectionInfo& connection_info) {
OnConnectionInitiated(endpoint_id, connection_info);
};
connection_listener.accepted_cb = [&](absl::string_view endpoint_id) {
OnConnectionAccepted(endpoint_id);
};
connection_listener.rejected_cb = [&](absl::string_view endpoint_id,
Status status) {
OnConnectionRejected(endpoint_id, status);
};
connection_listener.disconnected_cb = [&](absl::string_view endpoint_id) {
OnDisconnected(endpoint_id);
};
connection_listener.bandwidth_changed_cb = [&](absl::string_view endpoint_id,
Medium medium) {
OnBandwidthChanged(endpoint_id, medium);
};
nearby_connections_service_->RequestConnection(
kServiceId, endpoint_info, endpoint_id,
ConnectionOptions(std::move(allowed_mediums),
std::move(bluetooth_mac_address),
/*keep_alive_interval=*/std::nullopt,
/*keep_alive_timeout=*/std::nullopt),
std::move(connection_listener),
[&, endpoint_id](ConnectionsStatus status) {
MutexLock lock(&mutex_);
if (status != ConnectionsStatus::kSuccess) {
transfer_managers_.erase(endpoint_id);
}
OnConnectionRequested(endpoint_id, status);
});
// Setup transfer manager.
if (transport_type == TransportType::kHighQuality) {
transfer_managers_[endpoint_id] =
std::make_unique<TransferManager>(context_, endpoint_id);
}
}
void NearbyConnectionsManagerImpl::OnConnectionTimedOut(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
NL_LOG(ERROR) << "Failed to connect to the remote shareTarget: Timed out.";
if (pending_outgoing_connections_.contains(endpoint_id)) {
auto it = connection_info_map_.find(endpoint_id);
if (it != connection_info_map_.end()) {
it->second.connection_layer_status = Status::kTimeout;
}
}
Disconnect(endpoint_id);
}
void NearbyConnectionsManagerImpl::OnConnectionRequested(
absl::string_view endpoint_id, ConnectionsStatus status) {
MutexLock lock(&mutex_);
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it == pending_outgoing_connections_.end()) return;
if (status != ConnectionsStatus::kSuccess) {
NL_LOG(ERROR) << "Failed to connect to the remote shareTarget: "
<< ConnectionsStatusToString(status);
auto info_it = connection_info_map_.find(endpoint_id);
if (info_it != connection_info_map_.end()) {
info_it->second.connection_layer_status = status;
}
Disconnect(endpoint_id);
return;
}
}
void NearbyConnectionsManagerImpl::Disconnect(absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
if (!pending_outgoing_connections_.contains(endpoint_id) &&
!connection_info_map_.contains(endpoint_id)) {
NL_LOG(WARNING) << "No connection for endpoint " << endpoint_id;
return;
}
if (disconnecting_endpoints_.contains(endpoint_id)) {
NL_LOG(INFO) << "Another Disconnecting is running for endpoint_id "
<< endpoint_id;
return;
}
disconnecting_endpoints_.insert(std::string(endpoint_id));
nearby_connections_service_->DisconnectFromEndpoint(
kServiceId, endpoint_id,
[&, endpoint_id = std::string(endpoint_id)](ConnectionsStatus status) {
NL_VLOG(1) << __func__ << ": Disconnecting from endpoint "
<< endpoint_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
context_->GetTaskRunner()->PostTask([&, endpoint_id]() {
OnDisconnected(endpoint_id);
{
MutexLock lock(&mutex_);
disconnecting_endpoints_.erase(endpoint_id);
}
});
NL_LOG(INFO) << "Disconnected from " << endpoint_id;
});
}
void NearbyConnectionsManagerImpl::Send(
absl::string_view endpoint_id, std::unique_ptr<Payload> payload,
std::weak_ptr<PayloadStatusListener> listener) {
MutexLock lock(&mutex_);
if (listener.lock()) {
RegisterPayloadStatusListener(payload->id, listener);
}
if (transfer_managers_.contains(endpoint_id) && payload->content.is_file()) {
NL_LOG(INFO) << __func__ << ": Send payload " << payload->id << " to "
<< endpoint_id
<< " to transfer manager. payload is file: "
<< payload->content.is_file() << ", is bytes "
<< payload->content.is_bytes();
transfer_managers_.at(endpoint_id)
->Send([&, endpoint_id = std::string(endpoint_id),
payload_copy = *payload]() {
NL_LOG(INFO) << __func__ << ": Send payload " << payload_copy.id
<< " to " << endpoint_id;
auto sent_payload = std::make_unique<Payload>(payload_copy);
SendWithoutDelay(endpoint_id, std::move(sent_payload));
});
transfer_managers_.at(endpoint_id)->StartTransfer();
return;
}
SendWithoutDelay(endpoint_id, std::move(payload));
}
void NearbyConnectionsManagerImpl::SendWithoutDelay(
absl::string_view endpoint_id, std::unique_ptr<Payload> payload) {
NL_LOG(INFO) << __func__ << ": Send payload " << payload->id << " to "
<< endpoint_id;
nearby_connections_service_->SendPayload(
kServiceId, {std::string(endpoint_id)}, std::move(payload),
[endpoint_id = std::string(endpoint_id)](ConnectionsStatus status) {
NL_LOG(INFO) << __func__ << ": Sending payload to endpoint "
<< endpoint_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
});
}
void NearbyConnectionsManagerImpl::RegisterPayloadStatusListener(
int64_t payload_id, std::weak_ptr<PayloadStatusListener> listener) {
MutexLock lock(&mutex_);
payload_status_listeners_.insert_or_assign(payload_id, listener);
}
void NearbyConnectionsManagerImpl::RegisterPayloadPath(
int64_t payload_id, const std::filesystem::path& file_path,
ConnectionsCallback callback) {
NL_DCHECK(!file_path.empty());
// Create file is put into Nearby Connections, don't need to create file in
// Nearby Sharing.
callback(Status::kSuccess);
}
Payload* NearbyConnectionsManagerImpl::GetIncomingPayload(int64_t payload_id) {
MutexLock lock(&mutex_);
auto it = incoming_payloads_.find(payload_id);
if (it == incoming_payloads_.end()) return nullptr;
return &it->second;
}
void NearbyConnectionsManagerImpl::Cancel(int64_t payload_id) {
MutexLock lock(&mutex_);
auto it = payload_status_listeners_.find(payload_id);
if (it != payload_status_listeners_.end()) {
std::weak_ptr<PayloadStatusListener> listener = it->second;
payload_status_listeners_.erase(payload_id);
// Note: The listener might be invalidated, for example, if it is shared
// with another payload in the same transfer.
if (auto status_listener = listener.lock()) {
status_listener->OnStatusUpdate(std::make_unique<PayloadTransferUpdate>(
payload_id, PayloadStatus::kCanceled,
/*total_bytes=*/0,
/*bytes_transferred=*/0),
/*upgraded_medium=*/std::nullopt);
}
}
nearby_connections_service_->CancelPayload(
kServiceId, payload_id, [&, payload_id](ConnectionsStatus status) {
NL_VLOG(1) << __func__ << ": Cancelling payload to id " << payload_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
});
NL_LOG(INFO) << "Cancelling payload: " << payload_id;
}
void NearbyConnectionsManagerImpl::ClearIncomingPayloads() {
MutexLock lock(&mutex_);
std::vector<Payload> payloads;
for (auto& it : incoming_payloads_) {
payloads.push_back(std::move(it.second));
payload_status_listeners_.erase(it.first);
}
incoming_payloads_.clear();
}
std::optional<std::vector<uint8_t>>
NearbyConnectionsManagerImpl::GetRawAuthenticationToken(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
auto it = connection_info_map_.find(endpoint_id);
if (it == connection_info_map_.end()) return std::nullopt;
return it->second.raw_authentication_token;
}
void NearbyConnectionsManagerImpl::UpgradeBandwidth(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
// The only bandwidth upgrade mediums at this point are WebRTC and WifiLan.
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::
kEnableMediumWifiLan) &&
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::kEnableMediumWebRtc)) {
return;
}
requested_bwu_endpoint_ids_.emplace(endpoint_id);
nearby_connections_service_->InitiateBandwidthUpgrade(
kServiceId, endpoint_id, [&, endpoint_id](ConnectionsStatus status) {
NL_VLOG(1) << __func__ << ": Bandwidth upgrade attempted to endpoint "
<< endpoint_id << "over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
});
}
void NearbyConnectionsManagerImpl::OnEndpointFound(
absl::string_view endpoint_id, const DiscoveredEndpointInfo& info) {
MutexLock lock(&mutex_);
if (!discovery_listener_) {
NL_LOG(INFO) << "Ignoring discovered endpoint "
<< nearby::utils::HexEncode(info.endpoint_info)
<< " because we're no longer "
"in discovery mode";
return;
}
auto result = discovered_endpoints_.insert(std::string(endpoint_id));
if (!result.second) {
NL_LOG(INFO) << "Ignoring discovered endpoint "
<< nearby::utils::HexEncode(info.endpoint_info)
<< " because we've already "
"reported this endpoint";
return;
}
discovery_listener_->OnEndpointDiscovered(endpoint_id, info.endpoint_info);
NL_LOG(INFO) << "Discovered " << nearby::utils::HexEncode(info.endpoint_info)
<< " over Nearby Connections";
}
void NearbyConnectionsManagerImpl::OnEndpointLost(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
if (!discovered_endpoints_.erase(endpoint_id)) {
NL_LOG(INFO) << "Ignoring lost endpoint " << endpoint_id
<< " because we haven't reported this endpoint";
return;
}
if (!discovery_listener_) {
NL_LOG(INFO) << "Ignoring lost endpoint " << endpoint_id
<< " because we're no longer in discovery mode";
return;
}
discovery_listener_->OnEndpointLost(endpoint_id);
NL_LOG(INFO) << "Endpoint " << endpoint_id << " lost over Nearby Connections";
}
void NearbyConnectionsManagerImpl::OnConnectionInitiated(
absl::string_view endpoint_id, const ConnectionInfo& info) {
MutexLock lock(&mutex_);
[[maybe_unused]] auto result =
connection_info_map_.emplace(endpoint_id, std::move(info));
NL_DCHECK(result.second);
NearbyConnectionsService::PayloadListener payload_listener;
payload_listener.payload_cb = [&](absl::string_view endpoint_id,
Payload payload) {
OnPayloadReceived(endpoint_id, payload);
};
payload_listener.payload_progress_cb =
[&](absl::string_view endpoint_id, const PayloadTransferUpdate& update) {
OnPayloadTransferUpdate(endpoint_id, update);
};
nearby_connections_service_->AcceptConnection(
kServiceId, endpoint_id, std::move(payload_listener),
[&, endpoint_id = std::string(endpoint_id)](ConnectionsStatus status) {
NL_VLOG(1) << __func__ << ": Accept connection attempted to endpoint "
<< endpoint_id << " over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
});
}
void NearbyConnectionsManagerImpl::OnConnectionAccepted(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
auto it = connection_info_map_.find(endpoint_id);
if (it == connection_info_map_.end()) return;
if (it->second.is_incoming_connection) {
if (!incoming_connection_listener_) {
// Not in advertising mode.
Disconnect(endpoint_id);
return;
}
auto result = connections_.emplace(std::string(endpoint_id),
std::make_unique<NearbyConnectionImpl>(
device_info_, this, endpoint_id));
NL_DCHECK(result.second);
incoming_connection_listener_->OnIncomingConnection(
endpoint_id, it->second.endpoint_info, result.first->second.get());
} else {
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it == pending_outgoing_connections_.end()) {
Disconnect(endpoint_id);
return;
}
auto result = connections_.emplace(
endpoint_id, std::make_unique<NearbyConnectionImpl>(device_info_, this,
endpoint_id));
NL_DCHECK(result.second);
std::move(it->second)(result.first->second.get(), Status::kSuccess);
pending_outgoing_connections_.erase(it);
connect_timeout_timers_.erase(endpoint_id);
}
}
void NearbyConnectionsManagerImpl::OnConnectionRejected(
absl::string_view endpoint_id, Status status) {
MutexLock lock(&mutex_);
connection_info_map_.erase(endpoint_id);
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it != pending_outgoing_connections_.end()) {
std::move(it->second)(nullptr, status);
pending_outgoing_connections_.erase(it);
connect_timeout_timers_.erase(endpoint_id);
}
}
void NearbyConnectionsManagerImpl::OnDisconnected(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
// Remove transfer manager.
if (transfer_managers_.contains(endpoint_id)) {
transfer_managers_[endpoint_id]->CancelTransfer();
transfer_managers_.erase(endpoint_id);
}
Status connection_layer_status = Status::kUnknown;
auto info_it = connection_info_map_.find(endpoint_id);
if (info_it != connection_info_map_.end()) {
connection_layer_status = info_it->second.connection_layer_status;
connection_info_map_.erase(info_it);
}
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it != pending_outgoing_connections_.end()) {
std::move(it->second)(nullptr, connection_layer_status);
pending_outgoing_connections_.erase(it);
connect_timeout_timers_.erase(endpoint_id);
}
connections_.erase(endpoint_id);
requested_bwu_endpoint_ids_.erase(endpoint_id);
current_upgraded_mediums_.erase(endpoint_id);
}
void NearbyConnectionsManagerImpl::OnBandwidthChanged(
absl::string_view endpoint_id, Medium medium) {
MutexLock lock(&mutex_);
NL_VLOG(1) << __func__
<< ": Bandwidth changed to medium=" << static_cast<int>(medium)
<< "; endpoint_id=" << endpoint_id;
if (transfer_managers_.contains(endpoint_id)) {
transfer_managers_[endpoint_id]->OnMediumQualityChanged(medium);
}
current_upgraded_mediums_.insert_or_assign(endpoint_id, medium);
// TODO(crbug/1111458): Support TransferManager.
}
void NearbyConnectionsManagerImpl::OnPayloadReceived(
absl::string_view endpoint_id, Payload& payload) {
MutexLock lock(&mutex_);
NL_LOG(INFO) << "Received payload id=" << payload.id;
[[maybe_unused]] auto result =
incoming_payloads_.emplace(payload.id, std::move(payload));
NL_DCHECK(result.second);
}
void NearbyConnectionsManagerImpl::OnPayloadTransferUpdate(
absl::string_view endpoint_id, const PayloadTransferUpdate& update) {
MutexLock lock(&mutex_);
NL_LOG(INFO) << "Received payload transfer update id=" << update.payload_id
<< ",status=" << update.status << ",total=" << update.total_bytes
<< ",bytes_transferred=" << update.bytes_transferred
<< std::endl;
// If this is a payload we've registered for, then forward its status to
// the PayloadStatusListener if it still exists. We don't need to do
// anything more with the payload.
auto listener_it = payload_status_listeners_.find(update.payload_id);
if (listener_it != payload_status_listeners_.end()) {
std::weak_ptr<PayloadStatusListener> listener = listener_it->second;
switch (update.status) {
case PayloadStatus::kInProgress:
break;
case PayloadStatus::kSuccess:
case PayloadStatus::kCanceled:
case PayloadStatus::kFailure:
payload_status_listeners_.erase(update.payload_id);
break;
}
// Note: The listener might be invalidated, for example, if it is shared
// with another payload in the same transfer.
if (auto status_listener = listener.lock()) {
status_listener->OnStatusUpdate(
std::make_unique<PayloadTransferUpdate>(update),
GetUpgradedMedium(endpoint_id));
}
return;
}
// If this is an incoming payload that we have not registered for, then
// we'll treat it as a control frame (e.g. IntroductionFrame) and
// forward it to the associated NearbyConnection.
auto payload_it = incoming_payloads_.find(update.payload_id);
if (payload_it == incoming_payloads_.end()) return;
if (payload_it->second.content.type != PayloadContent::Type::kBytes) {
NL_LOG(WARNING) << "Received unknown payload of file type. Cancelling.";
nearby_connections_service_->CancelPayload(kServiceId, payload_it->first,
[](Status status) {});
return;
}
if (update.status != PayloadStatus::kSuccess) return;
auto connections_it = connections_.find(endpoint_id);
if (connections_it == connections_.end()) return;
NL_LOG(INFO) << "Writing incoming byte message to NearbyConnection.";
connections_it->second->WriteMessage(
payload_it->second.content.bytes_payload.bytes);
}
void NearbyConnectionsManagerImpl::Reset() {
MutexLock lock(&mutex_);
nearby_connections_service_->StopAllEndpoints([](ConnectionsStatus status) {
NL_VLOG(1) << __func__
<< ": Stop all endpoints attempted over Nearby "
"Connections with result: "
<< ConnectionsStatusToString(status);
});
discovered_endpoints_.clear();
payload_status_listeners_.clear();
ClearIncomingPayloads();
connections_.clear();
connection_info_map_.clear();
discovery_listener_ = nullptr;
incoming_connection_listener_ = nullptr;
connect_timeout_timers_.clear();
requested_bwu_endpoint_ids_.clear();
current_upgraded_mediums_.clear();
for (auto& transfer_manager : transfer_managers_) {
transfer_manager.second->CancelTransfer();
}
transfer_managers_.clear();
for (auto& entry : pending_outgoing_connections_)
std::move(entry.second)(/*connection=*/nullptr, Status::kReset);
pending_outgoing_connections_.clear();
}
std::optional<Medium> NearbyConnectionsManagerImpl::GetUpgradedMedium(
absl::string_view endpoint_id) const {
MutexLock lock(&mutex_);
const auto it = current_upgraded_mediums_.find(endpoint_id);
if (it == current_upgraded_mediums_.end()) return std::nullopt;
return it->second;
}
void NearbyConnectionsManagerImpl::SetCustomSavePath(
absl::string_view custom_save_path) {
MutexLock lock(&mutex_);
nearby_connections_service_->SetCustomSavePath(
custom_save_path, [&](Status status) {
NL_VLOG(1) << __func__
<< ": SetCustomSavePath attempted over Nearby "
"Connections with result: "
<< static_cast<int>(status);
});
}
std::string NearbyConnectionsManagerImpl::Dump() const {
return nearby_connections_service_->Dump();
}
} // namespace sharing
} // namespace nearby
+184
View File
@@ -0,0 +1,184 @@
// 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_SHARING_NEARBY_CONNECTIONS_MANAGER_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_IMPL_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/public/connectivity_manager.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connection_impl.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/transfer_manager.h"
namespace nearby {
namespace sharing {
// Concrete NearbyConnectionsManager implementation.
class NearbyConnectionsManagerImpl : public NearbyConnectionsManager {
public:
explicit NearbyConnectionsManagerImpl(
Context* context,
nearby::ConnectivityManager& connectivity_manager,
nearby::DeviceInfo& device_info,
std::unique_ptr<NearbyConnectionsService> nearby_connections_service);
~NearbyConnectionsManagerImpl() override;
NearbyConnectionsManagerImpl(const NearbyConnectionsManagerImpl&) = delete;
NearbyConnectionsManagerImpl& operator=(const NearbyConnectionsManagerImpl&) =
delete;
// NearbyConnectionsManager:
void Shutdown() override;
void StartAdvertising(std::vector<uint8_t> endpoint_info,
IncomingConnectionListener* listener,
PowerLevel power_level, proto::DataUsage data_usage,
ConnectionsCallback callback) override;
void StopAdvertising(ConnectionsCallback callback) override;
void StartDiscovery(DiscoveryListener* listener, proto::DataUsage data_usage,
ConnectionsCallback callback) override;
void StopDiscovery() override;
void Connect(std::vector<uint8_t> endpoint_info,
absl::string_view endpoint_id,
std::optional<std::vector<uint8_t>> bluetooth_mac_address,
proto::DataUsage data_usage, TransportType transport_type,
NearbyConnectionCallback callback) override;
void Disconnect(absl::string_view endpoint_id) override;
void Send(absl::string_view endpoint_id, std::unique_ptr<Payload> payload,
std::weak_ptr<PayloadStatusListener> listener) override;
void RegisterPayloadStatusListener(
int64_t payload_id,
std::weak_ptr<PayloadStatusListener> listener) override;
void RegisterPayloadPath(int64_t payload_id,
const std::filesystem::path& file_path,
ConnectionsCallback callback) override;
Payload* GetIncomingPayload(int64_t payload_id) override;
void Cancel(int64_t payload_id) override;
void ClearIncomingPayloads() override;
std::optional<std::vector<uint8_t>> GetRawAuthenticationToken(
absl::string_view endpoint_id) override;
void UpgradeBandwidth(absl::string_view endpoint_id) override;
void SetCustomSavePath(absl::string_view custom_save_path) override;
std::string Dump() const override;
NearbyConnectionsService* GetNearbyConnectionsService() const {
return nearby_connections_service_.get();
}
private:
// EndpointDiscoveryListener:
void OnEndpointFound(absl::string_view endpoint_id,
const DiscoveredEndpointInfo& info);
void OnEndpointLost(absl::string_view endpoint_id);
// ConnectionLifecycleListener:
void OnConnectionInitiated(absl::string_view endpoint_id,
const ConnectionInfo& info);
void OnConnectionAccepted(absl::string_view endpoint_id);
void OnConnectionRejected(absl::string_view endpoint_id, Status status);
void OnDisconnected(absl::string_view endpoint_id);
void OnBandwidthChanged(absl::string_view endpoint_id, Medium medium);
// PayloadListener:
void OnPayloadReceived(absl::string_view endpoint_id, Payload& payload);
void OnPayloadTransferUpdate(absl::string_view endpoint_id,
const PayloadTransferUpdate& update);
void OnConnectionTimedOut(absl::string_view endpoint_id);
void OnConnectionRequested(absl::string_view endpoint_id,
ConnectionsStatus status);
void Reset();
std::optional<Medium> GetUpgradedMedium(absl::string_view endpoint_id) const;
void SendWithoutDelay(absl::string_view endpoint_id,
std::unique_ptr<Payload> payload);
Context* const context_;
nearby::ConnectivityManager& connectivity_manager_;
nearby::DeviceInfo& device_info_;
// Nearby Connections Manager is called from different threads and may have
// multiple calls to the class from one thread. To avoid deadlock and access
// violation, use a recursive mutex to protect class members.
mutable RecursiveMutex mutex_;
std::unique_ptr<NearbyConnectionsService> nearby_connections_service_ =
nullptr;
IncomingConnectionListener* incoming_connection_listener_ = nullptr;
DiscoveryListener* discovery_listener_ = nullptr;
absl::flat_hash_set<std::string> discovered_endpoints_
ABSL_GUARDED_BY(mutex_);
// A map of endpoint_id to NearbyConnectionCallback.
absl::flat_hash_map<std::string, NearbyConnectionCallback>
pending_outgoing_connections_ ABSL_GUARDED_BY(mutex_);
// A map of endpoint_id to ConnectionInfoPtr.
absl::flat_hash_map<std::string, ConnectionInfo> connection_info_map_
ABSL_GUARDED_BY(mutex_);
// A map of endpoint_id to NearbyConnection.
absl::flat_hash_map<std::string, std::unique_ptr<NearbyConnectionImpl>>
connections_ ABSL_GUARDED_BY(mutex_);
// A map of endpoint_id to timers that timeout a connection request.
absl::flat_hash_map<std::string, std::unique_ptr<Timer>>
connect_timeout_timers_ ABSL_GUARDED_BY(mutex_);
// A map of payload_id to PayloadStatusListener weak pointer.
absl::flat_hash_map<int64_t, std::weak_ptr<PayloadStatusListener>>
payload_status_listeners_ ABSL_GUARDED_BY(mutex_);
// A map of payload_id to PayloadPtr.
absl::flat_hash_map<int64_t, Payload> incoming_payloads_
ABSL_GUARDED_BY(mutex_);
// For metrics. A set of endpoint_ids for which we have requested a
// bandwidth upgrade.
absl::flat_hash_set<std::string> requested_bwu_endpoint_ids_
ABSL_GUARDED_BY(mutex_);
// For metrics. A map of endpoint_id to the current upgraded medium.
absl::flat_hash_map<std::string, Medium> current_upgraded_mediums_
ABSL_GUARDED_BY(mutex_);
// A map of endpoint_id to transfer manager.
absl::flat_hash_map<std::string, std::unique_ptr<TransferManager>>
transfer_managers_ ABSL_GUARDED_BY(mutex_);
// Avoid calling to disconnect on an endpoint multiple times.
absl::flat_hash_set<std::string> disconnecting_endpoints_
ABSL_GUARDED_BY(mutex_);
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_MANAGER_IMPL_H_
File diff suppressed because it is too large Load Diff
+131
View File
@@ -0,0 +1,131 @@
// 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 "sharing/nearby_connections_service.h"
#include <wrapper_internal_exception_macros.h>
#include <algorithm>
#include <cstdint>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "sharing/internal/base/utf_string_conversions.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
Status ConvertToStatus(NcStatus status) {
return static_cast<Status>(status.value);
}
Payload ConvertToPayload(NcPayload payload) {
switch (payload.GetType()) {
case NcPayloadType::kBytes: {
NcByteArray bytes = payload.AsBytes();
std::string data = std::string(bytes);
return Payload(payload.GetId(),
std::vector<uint8_t>(data.begin(), data.end()));
}
case NcPayloadType::kFile: {
std::filesystem::path file_path;
std::string parent_folder;
// Initialize path with UTF8 cause crash on Windows with configured
// locale.
try {
file_path = payload.AsFile()->GetFilePath();
if (!std::filesystem::exists(file_path)) {
file_path = utils::Utf8ToWide(payload.AsFile()->GetFilePath());
parent_folder = payload.GetParentFolder();
}
} catch (std::exception exception) {
file_path = utils::Utf8ToWide(payload.AsFile()->GetFilePath());
parent_folder = payload.GetParentFolder();
} catch (...) {
file_path = utils::Utf8ToWide(payload.AsFile()->GetFilePath());
parent_folder = payload.GetParentFolder();
}
return Payload(payload.GetId(), InputFile(file_path), parent_folder);
NEARBY_LOGS(VERBOSE) << __func__ << ": Payload file_path=" << file_path
<< ", parent_folder = " << parent_folder;
}
default:
return Payload();
}
}
NcPayload ConvertToServicePayload(Payload payload) {
switch (payload.content.type) {
case PayloadContent::Type::kFile: {
// On Windows, a crash may happen when access string() of path if it is
// using wchar. Apply UTF8 to avoid the cross-platform issues.
std::string file_path;
std::string file_name;
std::string parent_folder;
int64_t file_size = payload.content.file_payload.size;
try {
file_path =
utils::WideToUtf8(payload.content.file_payload.file.path.wstring());
file_name = utils::WideToUtf8(
payload.content.file_payload.file.path.filename().wstring());
} catch (std::exception e) {
file_path = payload.content.file_payload.file.path.string();
file_name = payload.content.file_payload.file.path.filename().string();
} catch (...) {
file_path = payload.content.file_payload.file.path.string();
file_name = payload.content.file_payload.file.path.filename().string();
}
parent_folder = payload.content.file_payload.parent_folder;
std::replace(parent_folder.begin(), parent_folder.end(), '\\', '/');
NEARBY_LOGS(VERBOSE) << __func__ << ": NC Payload file_path=" << file_path
<< ", parent_folder = " << parent_folder;
nearby::InputFile input_file(file_path, file_size);
NcPayload nc_payload(payload.id, parent_folder, file_name,
std::move(input_file));
return nc_payload;
}
case PayloadContent::Type::kBytes: {
std::vector<uint8_t> bytes = payload.content.bytes_payload.bytes;
return NcPayload(payload.id,
NcByteArray(std::string(bytes.begin(), bytes.end())));
}
default:
return NcPayload();
}
}
NcResultCallback BuildResultCallback(
std::function<void(Status status)> callback) {
return NcResultCallback{[&, callback = std::move(callback)](NcStatus status) {
callback(ConvertToStatus(status));
}};
}
NcStrategy ConvertToServiceStrategy(Strategy strategy) {
switch (strategy) {
case Strategy::kP2pCluster:
return NcStrategy::kP2pCluster;
case Strategy::kP2pPointToPoint:
return NcStrategy::kP2pPointToPoint;
case Strategy::kP2pStar:
return NcStrategy::kP2pStar;
}
}
} // namespace sharing
} // namespace nearby
+183
View File
@@ -0,0 +1,183 @@
// 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_SHARING_NEARBY_CONNECTIONS_SERVICE_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_SERVICE_H_
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "connections/advertising_options.h"
#include "connections/connection_options.h"
#include "connections/core.h"
#include "connections/discovery_options.h"
#include "connections/implementation/service_controller_router.h"
#include "connections/listeners.h"
#include "connections/medium_selector.h"
#include "connections/out_of_band_connection_metadata.h"
#include "connections/params.h"
#include "connections/payload.h"
#include "connections/payload_type.h"
#include "connections/status.h"
#include "connections/strategy.h"
#include "internal/platform/listeners.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
using Core = ::nearby::connections::Core;
using ServiceControllerRouter = ::nearby::connections::ServiceControllerRouter;
using NcAdvertisingOptions = ::nearby::connections::AdvertisingOptions;
using NcByteArray = ::nearby::ByteArray;
using NcConnectionOptions = ::nearby::connections::ConnectionOptions;
using NcConnectionRequestInfo = ::nearby::connections::ConnectionRequestInfo;
using NcConnectionResponseInfo = ::nearby::connections::ConnectionResponseInfo;
using NcDistanceInfo = ::nearby::connections::DistanceInfo;
using NcDiscoveryListener = ::nearby::connections::DiscoveryListener;
using NcDiscoveryOptions = ::nearby::connections::DiscoveryOptions;
using NcMedium = ::nearby::connections::Medium;
using NcOutOfBandConnectionMetadata =
::nearby::connections::OutOfBandConnectionMetadata;
using NcPayload = ::nearby::connections::Payload;
using NcPayloadType = ::nearby::connections::PayloadType;
using NcPayloadListener = ::nearby::connections::PayloadListener;
using NcPayloadProgressInfo = ::nearby::connections::PayloadProgressInfo;
using NcResultCallback = ::nearby::connections::ResultCallback;
using NcStatus = ::nearby::connections::Status;
using NcStrategy = ::nearby::connections::Strategy;
// Main interface to control the NearbyConnections library. Implemented in a
// sandboxed process. This interface is used by the browser process to connect
// to remote devices and send / receive raw data packets. Parsing of those
// packets is not part of the NearbyConnections library and is done in a
// separate interface.
class NearbyConnectionsService {
public:
using HANDLE = void*;
virtual ~NearbyConnectionsService() = default;
struct ConnectionListener {
std::function<void(const std::string& endpoint_id,
const ConnectionInfo& info)>
initiated_cb =
DefaultFuncCallback<const std::string&, const ConnectionInfo&>();
std::function<void(const std::string& endpoint_id)> accepted_cb =
DefaultFuncCallback<const std::string&>();
std::function<void(const std::string& endpoint_id, Status status)>
rejected_cb = DefaultFuncCallback<const std::string&, Status>();
std::function<void(const std::string& endpoint_id)> disconnected_cb =
DefaultFuncCallback<const std::string&>();
std::function<void(const std::string& endpoint_id, Medium medium)>
bandwidth_changed_cb =
DefaultFuncCallback<const std::string&, Medium>();
};
struct DiscoveryListener {
std::function<void(const std::string& endpoint_id,
const DiscoveredEndpointInfo& info)>
endpoint_found_cb =
DefaultFuncCallback<const std::string&,
const DiscoveredEndpointInfo&>();
std::function<void(const std::string& endpoint_id)> endpoint_lost_cb =
DefaultFuncCallback<const std::string&>();
std::function<void(const std::string& endpoint_id, DistanceInfo info)>
endpoint_distance_changed_cb =
DefaultFuncCallback<const std::string&, DistanceInfo>();
};
struct PayloadListener {
std::function<void(absl::string_view endpoint_id, Payload payload)>
payload_cb = DefaultFuncCallback<absl::string_view, Payload>();
std::function<void(absl::string_view endpoint_id,
const PayloadTransferUpdate& update)>
payload_progress_cb =
DefaultFuncCallback<absl::string_view,
const PayloadTransferUpdate&>();
};
virtual void StartAdvertising(
absl::string_view service_id, const std::vector<uint8_t>& endpoint_info,
AdvertisingOptions advertising_options,
ConnectionListener advertising_listener,
std::function<void(Status status)> callback) = 0;
virtual void StopAdvertising(absl::string_view service_id,
std::function<void(Status status)> callback) = 0;
virtual void StartDiscovery(absl::string_view service_id,
DiscoveryOptions discovery_options,
DiscoveryListener discovery_listener,
std::function<void(Status status)> callback) = 0;
virtual void StopDiscovery(absl::string_view service_id,
std::function<void(Status status)> callback) = 0;
virtual void RequestConnection(
absl::string_view service_id, const std::vector<uint8_t>& endpoint_info,
absl::string_view endpoint_id, ConnectionOptions connection_options,
ConnectionListener connection_listener,
std::function<void(Status status)> callback) = 0;
virtual void DisconnectFromEndpoint(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) = 0;
virtual void SendPayload(absl::string_view service_id,
absl::Span<const std::string> endpoint_ids,
std::unique_ptr<Payload> payload,
std::function<void(Status status)> callback) = 0;
virtual void CancelPayload(absl::string_view service_id, int64_t payload_id,
std::function<void(Status status)> callback) = 0;
virtual void InitiateBandwidthUpgrade(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) = 0;
virtual void AcceptConnection(
absl::string_view service_id, absl::string_view endpoint_id,
PayloadListener payload_listener,
std::function<void(Status status)> callback) = 0;
virtual void StopAllEndpoints(
std::function<void(Status status)> callback) = 0;
virtual void SetCustomSavePath(
absl::string_view path, std::function<void(Status status)> callback) = 0;
virtual std::string Dump() const = 0;
};
Status ConvertToStatus(NcStatus status);
Payload ConvertToPayload(NcPayload payload);
NcPayload ConvertToServicePayload(Payload payload);
NcResultCallback BuildResultCallback(
std::function<void(Status status)> callback);
NcStrategy ConvertToServiceStrategy(Strategy strategy);
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_SERVICE_H_
+340
View File
@@ -0,0 +1,340 @@
// 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 "sharing/nearby_connections_service_impl.h"
#include <stdint.h>
#include <functional>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "connections/listeners.h"
#include "connections/medium_selector.h"
#include "connections/payload.h"
#include "connections/strategy.h"
#include "internal/analytics/event_logger.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
namespace {
Core* GetService(NearbyConnectionsService::HANDLE handle) {
return reinterpret_cast<Core*>(handle);
}
} // namespace
NearbyConnectionsServiceImpl::NearbyConnectionsServiceImpl(
nearby::analytics::EventLogger* event_logger) {
static ServiceControllerRouter* router = new ServiceControllerRouter();
static Core* core = new Core(event_logger, router);
service_handle_ = core;
}
NearbyConnectionsServiceImpl::~NearbyConnectionsServiceImpl() = default;
void NearbyConnectionsServiceImpl::StartAdvertising(
absl::string_view service_id, const std::vector<uint8_t>& endpoint_info,
AdvertisingOptions advertising_options,
ConnectionListener advertising_listener,
std::function<void(Status status)> callback) {
advertising_listener_ = std::move(advertising_listener);
NcAdvertisingOptions options{};
options.strategy = ConvertToServiceStrategy(advertising_options.strategy);
options.allowed.ble = advertising_options.allowed_mediums.ble;
options.allowed.bluetooth = advertising_options.allowed_mediums.bluetooth;
options.allowed.web_rtc = advertising_options.allowed_mediums.web_rtc;
options.allowed.wifi_lan = advertising_options.allowed_mediums.wifi_lan;
options.auto_upgrade_bandwidth = advertising_options.auto_upgrade_bandwidth;
options.enforce_topology_constraints =
advertising_options.enforce_topology_constraints;
options.enable_bluetooth_listening =
advertising_options.enable_bluetooth_listening;
options.enable_webrtc_listening = advertising_options.enable_webrtc_listening;
options.fast_advertisement_service_uuid =
advertising_options.fast_advertisement_service_uuid.uuid;
NcConnectionRequestInfo connection_request_info;
connection_request_info.endpoint_info =
NcByteArray(std::string(endpoint_info.begin(), endpoint_info.end()));
connection_request_info.listener.initiated_cb =
[&](const std::string& endpoint_id,
const NcConnectionResponseInfo& info) {
ConnectionInfo connection_info;
connection_info.authentication_token = info.authentication_token;
std::string remote_end_point = std::string(info.remote_endpoint_info);
connection_info.endpoint_info = std::vector<uint8_t>(
remote_end_point.begin(), remote_end_point.end());
connection_info.is_incoming_connection = info.is_incoming_connection;
std::string raw_authentication_token =
std::string(info.raw_authentication_token);
connection_info.raw_authentication_token = std::vector<uint8_t>(
raw_authentication_token.begin(), raw_authentication_token.end());
advertising_listener_.initiated_cb(endpoint_id, connection_info);
};
connection_request_info.listener.accepted_cb =
[&](const std::string& endpoint_id) {
advertising_listener_.accepted_cb(endpoint_id);
};
connection_request_info.listener.rejected_cb =
[&](const std::string& endpoint_info, NcStatus status) {
advertising_listener_.rejected_cb(endpoint_info,
ConvertToStatus(status));
};
connection_request_info.listener.disconnected_cb =
[&](const std::string& endpoint_info) {
advertising_listener_.disconnected_cb(endpoint_info);
};
connection_request_info.listener.bandwidth_changed_cb =
[&](const std::string& endpoint_id, NcMedium medium) {
advertising_listener_.bandwidth_changed_cb(endpoint_id,
static_cast<Medium>(medium));
};
GetService(service_handle_)
->StartAdvertising(service_id, options,
std::move(connection_request_info),
BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::StopAdvertising(
absl::string_view service_id, std::function<void(Status status)> callback) {
GetService(service_handle_)->StopAdvertising(BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::StartDiscovery(
absl::string_view service_id, DiscoveryOptions discovery_options,
DiscoveryListener discovery_listener,
std::function<void(Status status)> callback) {
discovery_listener_ = std::move(discovery_listener);
NcDiscoveryOptions options{};
options.strategy = ConvertToServiceStrategy(discovery_options.strategy);
options.allowed.ble = discovery_options.allowed_mediums.ble;
options.allowed.bluetooth = discovery_options.allowed_mediums.bluetooth;
options.allowed.web_rtc = discovery_options.allowed_mediums.web_rtc;
options.allowed.wifi_lan = discovery_options.allowed_mediums.wifi_lan;
if (discovery_options.fast_advertisement_service_uuid.has_value()) {
options.fast_advertisement_service_uuid =
(*discovery_options.fast_advertisement_service_uuid).uuid;
}
options.is_out_of_band_connection =
discovery_options.is_out_of_band_connection;
NcDiscoveryListener listener;
listener.endpoint_found_cb = [this](const std::string& endpoint_id,
const NcByteArray& endpoint_info,
const std::string& service_id) {
std::string endpoint_info_data = std::string(endpoint_info);
discovery_listener_.endpoint_found_cb(
endpoint_id,
DiscoveredEndpointInfo(std::vector<uint8_t>(endpoint_info_data.begin(),
endpoint_info_data.end()),
service_id));
};
listener.endpoint_lost_cb = [this](const std::string& endpoint_id) {
discovery_listener_.endpoint_lost_cb(endpoint_id);
};
listener.endpoint_distance_changed_cb = [this](const std::string& endpoint_id,
NcDistanceInfo distance_info) {
discovery_listener_.endpoint_distance_changed_cb(
endpoint_id, static_cast<DistanceInfo>(distance_info));
};
GetService(service_handle_)
->StartDiscovery(service_id, options, std::move(listener),
BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::StopDiscovery(
absl::string_view service_id, std::function<void(Status status)> callback) {
GetService(service_handle_)->StopDiscovery(BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::RequestConnection(
absl::string_view service_id, const std::vector<uint8_t>& endpoint_info,
absl::string_view endpoint_id, ConnectionOptions connection_options,
ConnectionListener connection_listener,
std::function<void(Status status)> callback) {
connection_listener_ = std::move(connection_listener);
NcConnectionOptions options{};
options.allowed.ble = connection_options.allowed_mediums.ble;
options.allowed.bluetooth = connection_options.allowed_mediums.bluetooth;
options.allowed.web_rtc = connection_options.allowed_mediums.web_rtc;
options.allowed.wifi_lan = connection_options.allowed_mediums.wifi_lan;
options.allowed.wifi_hotspot =
connection_options.allowed_mediums.wifi_hotspot;
if (connection_options.keep_alive_interval.has_value()) {
options.keep_alive_interval_millis =
*connection_options.keep_alive_interval / absl::Milliseconds(1);
}
if (connection_options.keep_alive_timeout.has_value()) {
options.keep_alive_timeout_millis =
*connection_options.keep_alive_timeout / absl::Milliseconds(1);
}
if (connection_options.remote_bluetooth_mac_address.has_value()) {
auto mac_address = *connection_options.remote_bluetooth_mac_address;
options.remote_bluetooth_mac_address =
NcByteArray(std::string(mac_address.begin(), mac_address.end()));
}
NcConnectionRequestInfo connection_request_info;
connection_request_info.endpoint_info =
NcByteArray(std::string(endpoint_info.begin(), endpoint_info.end()));
connection_request_info.listener.initiated_cb =
[&](const std::string& endpoint_id,
const NcConnectionResponseInfo& info) {
ConnectionInfo connection_info;
connection_info.authentication_token = info.authentication_token;
std::string remote_end_point = std::string(info.remote_endpoint_info);
connection_info.endpoint_info = std::vector<uint8_t>(
remote_end_point.begin(), remote_end_point.end());
connection_info.is_incoming_connection = info.is_incoming_connection;
std::string raw_authentication_token =
std::string(info.raw_authentication_token);
connection_info.raw_authentication_token = std::vector<uint8_t>(
raw_authentication_token.begin(), raw_authentication_token.end());
connection_listener_.initiated_cb(endpoint_id, connection_info);
};
connection_request_info.listener.accepted_cb =
[&](const std::string& endpoint_id) {
connection_listener_.accepted_cb(endpoint_id);
};
connection_request_info.listener.rejected_cb =
[&](const std::string& endpoint_info, NcStatus status) {
connection_listener_.rejected_cb(endpoint_info,
ConvertToStatus(status));
};
connection_request_info.listener.disconnected_cb =
[&](const std::string& endpoint_info) {
connection_listener_.disconnected_cb(endpoint_info);
};
connection_request_info.listener.bandwidth_changed_cb =
[&](const std::string& endpoint_id, NcMedium medium) {
connection_listener_.bandwidth_changed_cb(endpoint_id,
static_cast<Medium>(medium));
};
GetService(service_handle_)
->RequestConnection(endpoint_id, std::move(connection_request_info),
options, BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::DisconnectFromEndpoint(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) {
GetService(service_handle_)
->DisconnectFromEndpoint(endpoint_id, BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::SendPayload(
absl::string_view service_id, absl::Span<const std::string> endpoint_ids,
std::unique_ptr<Payload> payload,
std::function<void(Status status)> callback) {
GetService(service_handle_)
->SendPayload(endpoint_ids, ConvertToServicePayload(*payload),
BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::CancelPayload(
absl::string_view service_id, int64_t payload_id,
std::function<void(Status status)> callback) {
GetService(service_handle_)
->CancelPayload(payload_id, BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::InitiateBandwidthUpgrade(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) {
GetService(service_handle_)
->InitiateBandwidthUpgrade(endpoint_id, BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::AcceptConnection(
absl::string_view service_id, absl::string_view endpoint_id,
PayloadListener payload_listener,
std::function<void(Status status)> callback) {
payload_listeners_.emplace(endpoint_id, std::move(payload_listener));
NcPayloadListener service_payload_listener{
.payload_cb =
[&](absl::string_view endpoint_id, NcPayload payload) {
auto payload_listener = payload_listeners_.find(endpoint_id);
if (payload_listener == payload_listeners_.end()) {
return;
}
NEARBY_LOGS(VERBOSE) << "payload callback id=" << payload.GetId();
switch (payload.GetType()) {
case NcPayloadType::kBytes:
case NcPayloadType::kFile:
payload_listener->second.payload_cb(
endpoint_id, ConvertToPayload(std::move(payload)));
break;
default:
// TODO(b/219814719); support stream payload.
break;
}
},
.payload_progress_cb =
[&](absl::string_view endpoint_id,
const NcPayloadProgressInfo& info) {
PayloadTransferUpdate transfer_update;
transfer_update.bytes_transferred = info.bytes_transferred;
transfer_update.payload_id = info.payload_id;
transfer_update.status = static_cast<PayloadStatus>(info.status);
transfer_update.total_bytes = info.total_bytes;
NEARBY_LOGS(VERBOSE)
<< "payload transfer update id=" << info.payload_id;
auto payload_listener = payload_listeners_.find(endpoint_id);
if (payload_listener != payload_listeners_.end()) {
payload_listener->second.payload_progress_cb(endpoint_id,
transfer_update);
}
}};
GetService(service_handle_)
->AcceptConnection(endpoint_id, std::move(service_payload_listener),
BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::StopAllEndpoints(
std::function<void(Status status)> callback) {
GetService(service_handle_)->StopAllEndpoints(BuildResultCallback(callback));
}
void NearbyConnectionsServiceImpl::SetCustomSavePath(
absl::string_view path, std::function<void(Status status)> callback) {
GetService(service_handle_)
->SetCustomSavePath(path, BuildResultCallback(callback));
}
std::string NearbyConnectionsServiceImpl::Dump() const {
return GetService(service_handle_)->Dump();
}
} // namespace sharing
} // namespace nearby
+103
View File
@@ -0,0 +1,103 @@
// 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_SHARING_NEARBY_CONNECTIONS_SERVICE_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_SERVICE_IMPL_H_
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "internal/analytics/event_logger.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
class NearbyConnectionsServiceImpl : public NearbyConnectionsService {
public:
explicit NearbyConnectionsServiceImpl(
nearby::analytics::EventLogger* event_logger = nullptr);
NearbyConnectionsServiceImpl() = delete;
~NearbyConnectionsServiceImpl() override;
void StartAdvertising(absl::string_view service_id,
const std::vector<uint8_t>& endpoint_info,
AdvertisingOptions advertising_options,
ConnectionListener advertising_listener,
std::function<void(Status status)> callback) override;
void StopAdvertising(absl::string_view service_id,
std::function<void(Status status)> callback) override;
void StartDiscovery(absl::string_view service_id,
DiscoveryOptions discovery_options,
DiscoveryListener discovery_listener,
std::function<void(Status status)> callback) override;
void StopDiscovery(absl::string_view service_id,
std::function<void(Status status)> callback) override;
void RequestConnection(absl::string_view service_id,
const std::vector<uint8_t>& endpoint_info,
absl::string_view endpoint_id,
ConnectionOptions connection_options,
ConnectionListener connection_listener,
std::function<void(Status status)> callback) override;
void DisconnectFromEndpoint(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) override;
void SendPayload(absl::string_view service_id,
absl::Span<const std::string> endpoint_ids,
std::unique_ptr<Payload> payload,
std::function<void(Status status)> callback) override;
void CancelPayload(absl::string_view service_id, int64_t payload_id,
std::function<void(Status status)> callback) override;
void InitiateBandwidthUpgrade(
absl::string_view service_id, absl::string_view endpoint_id,
std::function<void(Status status)> callback) override;
void AcceptConnection(absl::string_view service_id,
absl::string_view endpoint_id,
PayloadListener payload_listener,
std::function<void(Status status)> callback) override;
void StopAllEndpoints(std::function<void(Status status)> callback) override;
void SetCustomSavePath(absl::string_view path,
std::function<void(Status status)> callback) override;
std::string Dump() const override;
private:
HANDLE service_handle_ = nullptr;
ConnectionListener advertising_listener_;
DiscoveryListener discovery_listener_;
ConnectionListener connection_listener_;
absl::flat_hash_map<std::string, PayloadListener> payload_listeners_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_SERVICE_IMPL_H_
@@ -0,0 +1,121 @@
// 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 "sharing/nearby_connections_stream_buffer_manager.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h"
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace sharing {
NearbyConnectionsStreamBufferManager::PayloadWithBuffer::PayloadWithBuffer(
NcPayload payload)
: buffer_payload(std::move(payload)) {}
NearbyConnectionsStreamBufferManager::NearbyConnectionsStreamBufferManager() =
default;
NearbyConnectionsStreamBufferManager::~NearbyConnectionsStreamBufferManager() =
default;
void NearbyConnectionsStreamBufferManager::StartTrackingPayload(
NcPayload payload) {
int64_t payload_id = payload.GetId();
NL_LOG(INFO) << "Starting to track stream payload with ID " << payload_id;
id_to_payload_with_buffer_map_[payload_id] =
std::make_unique<PayloadWithBuffer>(std::move(payload));
}
bool NearbyConnectionsStreamBufferManager::IsTrackingPayload(
int64_t payload_id) const {
return id_to_payload_with_buffer_map_.contains(payload_id);
}
void NearbyConnectionsStreamBufferManager::StopTrackingFailedPayload(
int64_t payload_id) {
id_to_payload_with_buffer_map_.erase(payload_id);
NL_LOG(INFO) << "Stopped tracking payload with ID " << payload_id << " "
<< "and cleared internal memory.";
}
void NearbyConnectionsStreamBufferManager::HandleBytesTransferred(
int64_t payload_id, int64_t cumulative_bytes_transferred_so_far) {
auto it = id_to_payload_with_buffer_map_.find(payload_id);
if (it == id_to_payload_with_buffer_map_.end()) {
NL_LOG(ERROR) << "Attempted to handle stream bytes for payload with ID "
<< payload_id << ", but this payload was not being tracked.";
return;
}
PayloadWithBuffer* payload_with_buffer = it->second.get();
// We only need to read the new bytes which have not already been inserted
// into the buffer.
size_t bytes_to_read =
cumulative_bytes_transferred_so_far - payload_with_buffer->buffer.size();
NcInputStream* stream = payload_with_buffer->buffer_payload.AsStream();
if (!stream) {
NL_LOG(ERROR) << "Payload with ID " << payload_id << " is not a stream "
<< "payload; transfer has failed.";
StopTrackingFailedPayload(payload_id);
return;
}
NcExceptionOr<NcByteArray> bytes = stream->Read(bytes_to_read);
if (!bytes.ok()) {
NL_LOG(ERROR) << "Payload with ID " << payload_id << " encountered "
<< "exception while reading; transfer has failed.";
StopTrackingFailedPayload(payload_id);
return;
}
// Empty `bytes` means the End Of File. There should be at `bytes_to_read`
// bytes available in the input stream, so we should never face the EOF
// condition.
NL_DCHECK(!bytes.result().Empty());
payload_with_buffer->buffer += static_cast<std::string>(bytes.result());
}
NcByteArray
NearbyConnectionsStreamBufferManager::GetCompletePayloadAndStopTracking(
int64_t payload_id) {
auto it = id_to_payload_with_buffer_map_.find(payload_id);
if (it == id_to_payload_with_buffer_map_.end()) {
NL_LOG(ERROR) << "Attempted to get complete payload with ID " << payload_id
<< ", but this payload was not being tracked.";
return NcByteArray();
}
NcByteArray complete_payload(it->second->buffer);
// Close stream and erase internal state before returning payload.
it->second->buffer_payload.AsStream()->Close();
id_to_payload_with_buffer_map_.erase(it);
return complete_payload;
}
} // namespace sharing
} // namespace nearby
@@ -0,0 +1,93 @@
// 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_SHARING_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
#include <stdint.h>
#include <memory>
#include <string>
#include "absl/container/flat_hash_map.h"
#include "connections/core.h"
#include "connections/payload.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
namespace nearby {
namespace sharing {
using NcByteArray = ::nearby::ByteArray;
using NcException = ::nearby::Exception;
template <class T>
using NcExceptionOr = ::nearby::ExceptionOr<T>;
using NcInputStream = ::nearby::InputStream;
using NcPayload = ::nearby::connections::Payload;
// Manages payloads with type "stream" received over Nearby Connections. Streams
// over a certain size are delivered in chunks and need to be reassembled upon
// completion.
//
// Clients should start tracking a payload via StartTrackingPayload(). When
// more bytes have been transferred, clients should invoke
// HandleBytesTransferred(), passing the cumulative number of bytes that have
// been transferred. When all bytes have finished being transferred, clients
// should invoke GetCompletePayloadAndStopTracking() to get the complete,
// reassembled payload.
//
// If a payload has failed or been canceled, clients should invoke
// StopTrackingFailedPayload() so that this class can clean up its internal
// buffer.
class NearbyConnectionsStreamBufferManager {
public:
NearbyConnectionsStreamBufferManager();
~NearbyConnectionsStreamBufferManager();
// Starts tracking the given payload.
void StartTrackingPayload(NcPayload payload);
// Returns whether a payload with the provided ID is being tracked.
bool IsTrackingPayload(int64_t payload_id) const;
// Stops tracking the payload with the provided ID and cleans up internal
// memory being used to buffer the partially-completed transfer.
void StopTrackingFailedPayload(int64_t payload_id);
// Processes incoming bytes by reading from the input stream.
void HandleBytesTransferred(int64_t payload_id,
int64_t cumulative_bytes_transferred_so_far);
// Returns the completed buffer and deletes internal buffers.
NcByteArray GetCompletePayloadAndStopTracking(int64_t payload_id);
private:
struct PayloadWithBuffer {
explicit PayloadWithBuffer(NcPayload payload);
NcPayload buffer_payload;
// Partially-complete buffer which contains the bytes which have been read
// up to this point.
std::string buffer;
};
absl::flat_hash_map<int64_t, std::unique_ptr<PayloadWithBuffer>>
id_to_payload_with_buffer_map_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
@@ -0,0 +1,176 @@
// 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 "sharing/nearby_connections_stream_buffer_manager.h"
#include <stddef.h>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
namespace nearby {
namespace sharing {
namespace {
class FakeStream : public NcInputStream {
public:
FakeStream() = default;
~FakeStream() override = default;
FakeStream(const FakeStream&) = delete;
FakeStream& operator=(const FakeStream&) = delete;
NcExceptionOr<NcByteArray> Read(std::int64_t size) override {
if (should_throw_exception_) {
return NcException::kIo;
}
return NcExceptionOr<NcByteArray>(NcByteArray(std::string(size, '\0')));
}
NcExceptionOr<size_t> Skip(size_t offset) override {
if (should_throw_exception_) {
return NcException::kIo;
}
return NcExceptionOr<size_t>(0);
}
NcException Close() override {
if (should_throw_exception_) {
return {.value = NcException::kIo};
}
return {.value = NcException::kSuccess};
}
bool should_throw_exception_ = false;
};
} // namespace
struct CreatePayloadStreamResult {
NcPayload payload;
FakeStream* stream;
};
class NearbyConnectionsStreamBufferManagerTest : public testing::Test {
protected:
CreatePayloadStreamResult CreatePayload(int64_t payload_id) {
CreatePayloadStreamResult payload_and_stream;
auto stream = std::make_unique<FakeStream>();
FakeStream* stream_ptr = stream.get();
payload_and_stream.stream = stream_ptr;
NcPayload payload(payload_id, std::move(stream));
payload_and_stream.payload = std::move(payload);
return payload_and_stream;
}
NearbyConnectionsStreamBufferManager buffer_manager_;
};
TEST_F(NearbyConnectionsStreamBufferManagerTest,
SingleStreamTrackingAndCheckingTransferredSize) {
CreatePayloadStreamResult payload_and_stream =
CreatePayload(/*payload_id=*/1);
buffer_manager_.StartTrackingPayload(std::move(payload_and_stream.payload));
EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/1980);
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/2500);
NcByteArray array =
buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/1);
EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
EXPECT_EQ(array.size(), 2500u);
}
TEST_F(NearbyConnectionsStreamBufferManagerTest,
MultipleStreamTrackingAndCheckingTransferredSize) {
CreatePayloadStreamResult payload_and_stream_1 =
CreatePayload(/*payload_id=*/1);
CreatePayloadStreamResult payload_and_stream_2 =
CreatePayload(/*payload_id=*/2);
buffer_manager_.StartTrackingPayload(std::move(payload_and_stream_1.payload));
EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
buffer_manager_.StartTrackingPayload(std::move(payload_and_stream_2.payload));
EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/2));
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/1980);
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/2,
/*cumulative_bytes_transferred_so_far=*/1980);
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/2500);
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/2,
/*cumulative_bytes_transferred_so_far=*/3000);
NcByteArray array1 =
buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/1);
EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
EXPECT_EQ(array1.size(), 2500u);
NcByteArray array2 =
buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/2);
EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/2));
EXPECT_EQ(array2.size(), 3000u);
}
TEST_F(NearbyConnectionsStreamBufferManagerTest,
SingleStreamCheckTrackingFailure) {
CreatePayloadStreamResult payload_and_stream =
CreatePayload(/*payload_id=*/1);
buffer_manager_.StartTrackingPayload(std::move(payload_and_stream.payload));
EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/1980);
buffer_manager_.StopTrackingFailedPayload(/*payload_id=*/1);
EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
}
TEST_F(NearbyConnectionsStreamBufferManagerTest, SingleStreamCheckException) {
CreatePayloadStreamResult payload_and_stream =
CreatePayload(/*payload_id=*/1);
buffer_manager_.StartTrackingPayload(std::move(payload_and_stream.payload));
EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
payload_and_stream.stream->should_throw_exception_ = true;
buffer_manager_.HandleBytesTransferred(
/*payload_id=*/1,
/*cumulative_bytes_transferred_so_far=*/1980);
EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
}
} // namespace sharing
} // namespace nearby
+475
View File
@@ -0,0 +1,475 @@
// 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_SHARING_NEARBY_CONNECTIONS_TYPES_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_TYPES_H_
#include <stdint.h>
#include <wrapper_internal_exception_macros.h>
#include <exception>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/crypto_cros/random.h"
#include "sharing/common/compatible_u8_string.h"
namespace nearby {
namespace sharing {
struct Uuid {
Uuid() = default;
explicit Uuid(std::string uuid) { this->uuid = uuid; }
std::string uuid;
};
// Generic result status of NearbyConnections API calls. These values are
// persisted to logs. Entries should not be renumbered and numeric values should
// never be reused.
// LINT.IfChange(status_enum)
enum class Status {
// The operation was successful.
kSuccess = 0,
// The operation failed, without any more information.
kError = 1,
// The app called an API method out of order (i.e. another method is expected
// to be called first).
kOutOfOrderApiCall = 2,
// The app already has active operations (advertising, discovering, or
// connected to other devices) with another Strategy. Stop these operations on
// the current Strategy before trying to advertise or discover with a new
// Strategy.
kAlreadyHaveActiveStrategy = 3,
// The app is already advertising; call StopAdvertising() before trying to
// advertise again.
kAlreadyAdvertising = 4,
// The app is already discovering; call StopDiscovery() before trying to
// discover again.
kAlreadyDiscovering = 5,
// NC is already listening for incoming connections from remote endpoints.
kAlreadyListening = 6,
// An attempt to read from/write to a connected remote endpoint failed. If
// this occurs repeatedly, consider invoking DisconnectFromEndpoint().
kEndpointIOError = 7,
// An attempt to interact with a remote endpoint failed because it's unknown
// to us -- it's either an endpoint that was never discovered, or an endpoint
// that never connected to us (both of which are indicative of bad input from
// the client app).
kEndpointUnknown = 8,
// The remote endpoint rejected the connection request.
kConnectionRejected = 9,
// The app is already connected to the specified endpoint. Multiple
// connections to a remote endpoint cannot be maintained simultaneously.
kAlreadyConnectedToEndpoint = 10,
// The remote endpoint is not connected; messages cannot be sent to it.
kNotConnectedToEndpoint = 11,
// There was an error trying to use the device's Bluetooth capabilities.
kBluetoothError = 12,
// There was an error trying to use the device's Bluetooth Low Energy
// capabilities.
kBleError = 13,
// There was an error trying to use the device's Wi-Fi capabilities.
kWifiLanError = 14,
// An attempt to interact with an in-flight Payload failed because it's
// unknown to us.
kPayloadUnknown = 15,
// The connection was reset
kReset = 16,
// The connection timed out
kTimeout = 17,
// No status is available
kUnknown = 18,
// Value of the next enum variant.
kNextValue = 19,
};
// LINT.ThenChange(
// ../connections/status.h:status_enum,
// nearby_connections_manager.cc:status_enum,
// nearby_connections_types_test.cc:status_enum
// )
// Information about a connection that is being initiated.
struct ConnectionInfo {
// A short human-readable authentication token that has been given to both
// devices.
std::string authentication_token;
// The raw (significantly longer) version of the authentication token of
// authentication_token -- this is intended for headless authentication,
// typically on devices with no output capabilities, where the authentication
// is purely programmatic and does not have the luxury of human intervention.
std::vector<uint8_t> raw_authentication_token;
// Information that represents the remote device.
std::vector<uint8_t> endpoint_info;
// True if the connection request was initiated from a remote device. False if
// this device was the one to try and initiate the connection.
bool is_incoming_connection;
// Connection status used for analytics
Status connection_layer_status = Status::kUnknown;
};
// Information about an endpoint when it's discovered.
struct DiscoveredEndpointInfo {
DiscoveredEndpointInfo() = default;
DiscoveredEndpointInfo(std::vector<uint8_t> endpoint_info,
std::string service_id) {
this->endpoint_info = std::move(endpoint_info);
this->service_id = std::move(service_id);
}
// Information advertised by the remote endpoint.
std::vector<uint8_t> endpoint_info;
// The ID of the service advertised by the remote endpoint.
std::string service_id;
};
// The Strategy to be used when discovering or advertising to Nearby devices.
// The Strategy defines the connectivity requirements for the device, and the
// topology constraints of the connection.
enum class Strategy {
// Peer-to-peer strategy that supports an M-to-N, or cluster-shaped,
// connection topology. In other words, this enables connecting amorphous
// clusters of devices within radio range (~100m), where each device can both
// initiate outgoing connections to M other devices and accept incoming
// connections from N other devices.
kP2pCluster,
// Peer-to-peer strategy that supports a 1-to-N, or star-shaped, connection
// topology. In other words, this enables connecting devices within radio
// range (~100m) in a star shape, where each device can, at any given time,
// play the role of either a hub (where it can accept incoming connections
// from N other devices), or a spoke (where it can initiate an outgoing
// connection to a single hub), but not both.
kP2pStar,
// Peer-to-peer strategy that supports a 1-to-1 connection topology. In other
// words, this enables connecting to a single device within radio range
// (~100m). This strategy will give the absolute highest bandwidth, but will
// not allow multiple connections at a time.
kP2pPointToPoint,
};
// A selection of on/off toggles to define a set of allowed mediums.
struct MediumSelection {
MediumSelection() = default;
MediumSelection(bool bluetooth, bool ble, bool web_rtc, bool wifi_lan,
bool wifi_hotspot) {
this->bluetooth = bluetooth;
this->ble = ble;
this->web_rtc = web_rtc;
this->wifi_lan = wifi_lan;
this->wifi_hotspot = wifi_hotspot;
}
// Whether Bluetooth should be allowed.
bool bluetooth = true;
// Whether BLE should be allowed.
bool ble = true;
// Whether WebRTC should be allowed.
bool web_rtc = true;
// Whether Wi-Fi LAN should be allowed.
bool wifi_lan = true;
// Whether Wi-Fi Hotspot should be allowed
bool wifi_hotspot = true;
};
// Options for a call to NearbyConnections::StartAdvertising().
struct AdvertisingOptions {
AdvertisingOptions() = default;
AdvertisingOptions(Strategy strategy, MediumSelection allowed_mediums,
bool auto_upgrade_bandwidth,
bool enforce_topology_constraints,
bool enable_bluetooth_listening,
bool enable_webrtc_listening,
Uuid fast_advertisement_service_uuid) {
this->strategy = strategy;
this->allowed_mediums = allowed_mediums;
this->auto_upgrade_bandwidth = auto_upgrade_bandwidth;
this->enforce_topology_constraints = enforce_topology_constraints;
this->enable_bluetooth_listening = enable_bluetooth_listening;
this->enable_webrtc_listening = enable_webrtc_listening;
this->fast_advertisement_service_uuid = fast_advertisement_service_uuid;
}
// The strategy to use for advertising. Must match the strategy used in
// DiscoveryOptions for remote devices to see this advertisement.
Strategy strategy;
// Describes which mediums are allowed to be used for advertising. Note that
// allowing an otherwise unsupported medium is ok. Only the intersection of
// allowed and supported mediums will be used to advertise.
MediumSelection allowed_mediums;
// By default, this option is true. If false, we will not attempt to upgrade
// the bandwidth until a call to InitiateBandwidthUpgrade() is made.
bool auto_upgrade_bandwidth = true;
// By default, this option is true. If false, restrictions on topology will be
// ignored. This allows you treat all strategies as kP2pCluster (N to M),
// although bandwidth will be severely throttled if you don't maintain the
// original topology. When used in conjunction with auto_upgrade_bandwidth,
// you can initially connect as a kP2pCluster and then trim connections until
// you match kP2pStar or kP2pPointToPoint before upgrading the bandwidth.
bool enforce_topology_constraints = true;
// By default, this option is false. If true, this allows listening on
// incoming Bluetooth Classic connections while BLE advertising.
bool enable_bluetooth_listening = false;
// By default, this option is false. If true, this allows listening on
// incoming WebRTC connections while advertising.
bool enable_webrtc_listening = false;
// Optional. If set, BLE advertisements will be in their "fast advertisement"
// form, use this UUID, and non-connectable; if empty, BLE advertisements
// will otherwise be normal and connectable.
Uuid fast_advertisement_service_uuid;
};
// Options for a call to NearbyConnections::StartDiscovery().
struct DiscoveryOptions {
DiscoveryOptions() = default;
DiscoveryOptions(Strategy strategy, MediumSelection allowed_mediums,
std::optional<Uuid> fast_advertisement_service_uuid,
bool is_out_of_band_connection) {
this->strategy = strategy;
this->allowed_mediums = allowed_mediums;
this->fast_advertisement_service_uuid = fast_advertisement_service_uuid,
this->is_out_of_band_connection = is_out_of_band_connection;
}
// The strategy to use for discovering. Must match the strategy used in
// AdvertisingOptions in order to see advertisements.
Strategy strategy;
// Describes which mediums are allowed to be used for scanning/discovery. Note
// that allowing an otherwise unsupported medium is ok. Only the intersection
// of allowed and supported mediums will be used to scan.
MediumSelection allowed_mediums;
// The fast advertisement service id to scan for in BLE.
std::optional<Uuid> fast_advertisement_service_uuid;
// Whether this connection request skips over the normal discovery flow to
// inject discovery information synced outside the Nearby Connections library.
// Intended to be used in conjunction with InjectEndpoint().
bool is_out_of_band_connection = false;
};
// Options for a call to NearbyConnections::RequestConnection().
struct ConnectionOptions {
ConnectionOptions() = default;
ConnectionOptions(
MediumSelection allowed_mediums,
std::optional<std::vector<uint8_t>> remote_bluetooth_mac_address,
std::optional<absl::Duration> keep_alive_interval,
std::optional<absl::Duration> keep_alive_timeout) {
this->allowed_mediums = allowed_mediums;
this->remote_bluetooth_mac_address = remote_bluetooth_mac_address;
this->keep_alive_interval = keep_alive_interval;
this->keep_alive_timeout = keep_alive_timeout;
}
// Describes which mediums are allowed to be used for connection. Note that
// allowing an otherwise unsupported medium is ok. Only the intersection of
// allowed and supported mediums will be used to connect.
MediumSelection allowed_mediums;
// Bluetooth MAC address of remote device in byte format.
std::optional<std::vector<uint8_t>> remote_bluetooth_mac_address;
// How often to send a keep alive message on the channel. An unspecified or
// negative value will result in the Nearby Connections default of 5 seconds
// being used.
std::optional<absl::Duration> keep_alive_interval;
// The connection will time out if no message is received on the channel
// for this length of time. An unspecified or negative value will result in
// the Nearby Connections default of 30 seconds being used.
std::optional<absl::Duration> keep_alive_timeout;
};
// The status of the payload transfer at the time of this update.
enum PayloadStatus {
// The payload transfer has completed successfully.
kSuccess,
// The payload transfer failed.
kFailure,
// The payload transfer is still in progress.
kInProgress,
// The payload transfer has been canceled.
kCanceled,
};
// Describes the status for an active Payload transfer, either incoming or
// outgoing. Delivered to PayloadListener::OnPayloadTransferUpdate.
struct PayloadTransferUpdate {
PayloadTransferUpdate() = default;
PayloadTransferUpdate(int64_t payload_id, PayloadStatus status,
uint64_t total_bytes, uint64_t bytes_transferred) {
this->payload_id = payload_id;
this->status = status;
this->total_bytes = total_bytes;
this->bytes_transferred = bytes_transferred;
}
// The ID for the payload related to this update. Clients should match this
// with Payload::id.
int64_t payload_id;
// The status of this payload transfer. Always starts with kInProgress and
// ends with one of kSuccess, kFailure or kCanceled.
PayloadStatus status;
// The total expected bytes of this transfer.
uint64_t total_bytes;
// The number of bytes transferred so far.
uint64_t bytes_transferred;
};
// Bandwidth quality of a connection.
enum class BandwidthQuality {
// Unknown connection quality.
kUnknown,
// Low quality, e.g. connected via NFC or BLE.
kLow,
// Medium quality, e.g. connected via Bluetooth Classic.
kMedium,
// High quality, e.g. connected via WebRTC or Wi-Fi LAN.
kHigh,
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class Medium {
kUnknown = 0,
kMdns = 1,
kBluetooth = 2,
kWifiHotspot = 3,
kBle = 4,
kWifiLan = 5,
kWifiAware = 6,
kNfc = 7,
kWifiDirect = 8,
kWebRtc = 9,
kBleL2Cap = 10,
};
// Log severity levels. This is passed as a member of
// NearbyConnectionsDependencies to set the minimum log level in the Nearby
// Connections library. Entries should be kept in sync with the values in
// nearby::sharing::api::LogMessage::Severity.
enum class LogSeverity {
kVerbose = -1,
kInfo = 0,
kWarning = 1,
kError = 2,
kFatal = 3,
};
enum class DistanceInfo {
kUnknown = 1,
kVeryClose = 2,
kClose = 3,
kFar = 4,
};
struct InputFile {
InputFile() = default;
explicit InputFile(std::filesystem::path path) { this->path = path; }
std::filesystem::path path;
};
// A simple payload containing raw bytes.
struct BytesPayload {
// The bytes of this payload.
std::vector<uint8_t> bytes;
};
// A file payload representing a file.
struct FilePayload {
// The file to which this payload points to. When sending this payload, the
// NearbyConnections library reads from this file. When receiving a file
// payload it writes to this file.
InputFile file;
int64_t size;
std::string parent_folder;
};
// Union of all supported payload types.
struct PayloadContent {
// A Payload consisting of a single byte array.
BytesPayload bytes_payload;
// A Payload representing a file on the device.
FilePayload file_payload;
enum class Type { kUnknown = 0, kBytes = 1, kStream = 2, kFile = 3 };
Type type;
bool is_bytes() { return type == Type::kBytes; }
bool is_file() { return type == Type::kFile; }
bool is_stream() { return type == Type::kStream; }
};
// A Payload sent between devices. Payloads sent with a particular content type
// will be received as that same type on the other device, e.g. the content for
// a Payload of type BytesPayload must be received by reading from the bytes
// field returned by Payload::content::bytes.
struct Payload {
// A unique identifier for this payload. Generated by the sender of the
// payload and used to keep track of the transfer progress.
int64_t id;
// The content of this payload which is one of multiple types, see
// PayloadContent for all possible types.
PayloadContent content;
Payload() = default;
explicit Payload(std::vector<uint8_t> bytes)
: Payload(GenerateId(), std::move(bytes)) {}
explicit Payload(InputFile file,
absl::string_view parent_folder = absl::string_view()) {
id = std::hash<std::string>()(GetCompatibleU8String(file.path.u8string()));
content.type = PayloadContent::Type::kFile;
if (std::filesystem::exists(file.path)) {
content.file_payload.size = std::filesystem::file_size(file.path);
}
content.file_payload.file = std::move(file);
content.file_payload.parent_folder = std::string(parent_folder);
}
Payload(int64_t id, std::vector<uint8_t> bytes) : id(id) {
content.type = PayloadContent::Type::kBytes;
content.bytes_payload.bytes = std::move(bytes);
}
Payload(int64_t id, InputFile file,
absl::string_view parent_folder = absl::string_view())
: id(id) {
content.type = PayloadContent::Type::kFile;
if (std::filesystem::exists(file.path)) {
content.file_payload.size = std::filesystem::file_size(file.path);
}
content.file_payload.file = std::move(file);
content.file_payload.parent_folder = std::string(parent_folder);
}
Payload(char* bytes, int size)
: Payload(GenerateId(), std::vector<uint8_t>(bytes, bytes + size)) {}
int64_t GenerateId() {
int64_t id;
crypto::RandBytes(&id, sizeof(id));
return id;
}
};
// Transport type to decide whether to upgrade to a high quality medium.
enum class TransportType { kAny = 0, kNonDisruptive = 1, kHighQuality = 2 };
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_CONNECTIONS_TYPES_H_
@@ -0,0 +1,58 @@
// 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 "sharing/nearby_connections_types.h"
#include <filesystem> // NOLINT
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby::sharing {
using ::testing::Eq;
TEST(NearbyConnectionSharingTypesPayloadTest, FromInputFileUTF8) {
InputFile input_file(std::filesystem::u8path("/为甚么/tmp/test.txt"));
Payload payload(input_file);
EXPECT_THAT(payload.id, Eq(7724502655048749887LL));
EXPECT_THAT(payload.content.type, Eq(PayloadContent::Type::kFile));
}
TEST(NearbyConnectionSharingTypesPayloadTest, FromInputFileUTF16) {
InputFile input_file(std::filesystem::path(L"/为甚么/tmp/test.txt"));
Payload payload(input_file);
EXPECT_THAT(payload.id, Eq(7724502655048749887LL));
EXPECT_THAT(payload.content.type, Eq(PayloadContent::Type::kFile));
}
TEST(NearbyConnectionSharingTypesPayloadTest, FromInputFileWithId) {
InputFile input_file(std::filesystem::u8path("/为甚么/tmp/test.txt"));
Payload payload(1234, input_file);
EXPECT_THAT(payload.id, Eq(1234LL));
EXPECT_THAT(payload.content.type, Eq(PayloadContent::Type::kFile));
}
TEST(NearbyConnectionSharingTypesPayloadTest, FromBytes) {
Payload payload({1, 2, 3, 4, 5});
EXPECT_THAT(payload.content.type, Eq(PayloadContent::Type::kBytes));
}
TEST(NearbyConnectionSharingTypesPayloadTest, FromBytesWithId) {
Payload payload(5432, {1, 2, 3, 4, 5});
EXPECT_THAT(payload.id, Eq(5432LL));
EXPECT_THAT(payload.content.type, Eq(PayloadContent::Type::kBytes));
}
} // namespace nearby::sharing
+75
View File
@@ -0,0 +1,75 @@
// 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 "sharing/nearby_connections_types.h"
#include "gtest/gtest.h"
#include "connections/status.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_service.h"
namespace nearby {
namespace sharing {
namespace {
using NsStatus = nearby::sharing::Status;
using NcStatus = nearby::connections::Status;
// LINT.IfChange(status_enum)
TEST(NearbyConnectionSharingTypesTest, TestStatusValueIsSame) {
EXPECT_EQ(NsStatus::kSuccess, ConvertToStatus({NcStatus::kSuccess}));
EXPECT_EQ(NsStatus::kError, ConvertToStatus({NcStatus::kError}));
EXPECT_EQ(NsStatus::kOutOfOrderApiCall,
ConvertToStatus({NcStatus::kOutOfOrderApiCall}));
EXPECT_EQ(NsStatus::kAlreadyHaveActiveStrategy,
ConvertToStatus({NcStatus::kAlreadyHaveActiveStrategy}));
EXPECT_EQ(NsStatus::kAlreadyAdvertising,
ConvertToStatus({NcStatus::kAlreadyAdvertising}));
EXPECT_EQ(NsStatus::kAlreadyDiscovering,
ConvertToStatus({NcStatus::kAlreadyDiscovering}));
EXPECT_EQ(NsStatus::kEndpointIOError,
ConvertToStatus({NcStatus::kEndpointIoError}));
EXPECT_EQ(NsStatus::kEndpointUnknown,
ConvertToStatus({NcStatus::kEndpointUnknown}));
EXPECT_EQ(NsStatus::kConnectionRejected,
ConvertToStatus({NcStatus::kConnectionRejected}));
EXPECT_EQ(NsStatus::kAlreadyConnectedToEndpoint,
ConvertToStatus({NcStatus::kAlreadyConnectedToEndpoint}));
EXPECT_EQ(NsStatus::kAlreadyListening,
ConvertToStatus({NcStatus::kAlreadyListening}));
EXPECT_EQ(NsStatus::kNotConnectedToEndpoint,
ConvertToStatus({NcStatus::kNotConnectedToEndpoint}));
EXPECT_EQ(NsStatus::kBluetoothError,
ConvertToStatus({NcStatus::kBluetoothError}));
EXPECT_EQ(NsStatus::kBleError, ConvertToStatus({NcStatus::kBleError}));
EXPECT_EQ(NsStatus::kWifiLanError,
ConvertToStatus({NcStatus::kWifiLanError}));
EXPECT_EQ(NsStatus::kPayloadUnknown,
ConvertToStatus({NcStatus::kPayloadUnknown}));
EXPECT_EQ(NsStatus::kReset, ConvertToStatus({NcStatus::kReset}));
EXPECT_EQ(NsStatus::kTimeout, ConvertToStatus({NcStatus::kTimeout}));
EXPECT_EQ(NsStatus::kUnknown, ConvertToStatus({NcStatus::kUnknown}));
EXPECT_EQ(NsStatus::kNextValue, ConvertToStatus({NcStatus::kNextValue}));
}
// LINT.ThenChange()
TEST(NearbyConnectionSharingTypesTest, TestNoFailWithUnknownStatus) {
EXPECT_EQ(
NearbyConnectionsManager::ConnectionsStatusToString(NsStatus::kNextValue),
"Unknown");
}
} // namespace
} // namespace sharing
} // namespace nearby
+135
View File
@@ -0,0 +1,135 @@
// 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 "sharing/nearby_file_handler.h"
#include <stdint.h>
#include <wrapper_internal_exception_macros.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <memory>
#include <utility>
#include <vector>
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/platform/task_runner_impl.h"
#include "sharing/common/compatible_u8_string.h"
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace sharing {
namespace {
// Called on the FileTaskRunner to actually open the files passed.
std::vector<NearbyFileHandler::FileInfo> DoOpenFiles(
absl::Span<const std::filesystem::path> file_paths) {
std::vector<NearbyFileHandler::FileInfo> files;
for (const auto& file_path : file_paths) {
if (!std::filesystem::exists(file_path)) {
NL_LOG(ERROR) << __func__ << ": Failed to open file. File="
<< GetCompatibleU8String(file_path.u8string());
return {};
}
int64_t size = std::filesystem::file_size(file_path);
if (size < 0) return {};
files.push_back({size, file_path});
}
return files;
}
std::filesystem::path GenerateUniquePath(const std::filesystem::path& path) {
NL_DCHECK(!path.empty());
// Nearby Share is not responsible for generating unique paths, any more.
// Nearby Connections contains the logic to ensure there is no conflict.
// Just return the original file path, here.
return path;
}
} // namespace
NearbyFileHandler::NearbyFileHandler() {
sequenced_task_runner_ = std::make_unique<TaskRunnerImpl>(1);
}
NearbyFileHandler::~NearbyFileHandler() = default;
void NearbyFileHandler::OpenFiles(std::vector<std::filesystem::path> file_paths,
OpenFilesCallback callback) {
sequenced_task_runner_->PostTask(
[callback = std::move(callback), file_paths = std::move(file_paths)]() {
auto opened_files = DoOpenFiles(file_paths);
callback(opened_files);
});
}
void NearbyFileHandler::GetUniquePath(const std::filesystem::path& file_path,
GetUniquePathCallback callback) {
sequenced_task_runner_->PostTask(
[callback = std::move(callback), file_path]() {
std::filesystem::path unique_path = GenerateUniquePath(file_path);
callback(unique_path);
});
}
bool RemoveFile(const std::filesystem::path file) noexcept {
try {
if (!std::filesystem::remove(file)) {
return false;
}
} catch (std::exception) {
return false;
} catch (...) {
return false;
}
return true;
}
void NearbyFileHandler::DeleteFilesFromDisk(
std::vector<std::filesystem::path> file_paths,
DeleteFilesFromDiskCallback callback) {
sequenced_task_runner_->PostTask([callback = std::move(callback),
file_paths = std::move(file_paths)]() {
// wait 1 second to make the file being released from another process.
absl::SleepFor(absl::Seconds(1));
for (const auto& file_path : file_paths) {
if (!std::filesystem::exists(file_path)) {
continue;
}
if (RemoveFile(file_path)) {
NL_VLOG(1) << __func__ << ": Removed partial file. File="
<< GetCompatibleU8String(file_path.u8string());
} else {
// Try once more after 3 seconds.
absl::SleepFor(absl::Seconds(3));
if (RemoveFile(file_path)) {
NL_VLOG(1) << __func__
<< ": Removed partial file after additional delay. File="
<< GetCompatibleU8String(file_path.u8string());
} else {
NL_LOG(ERROR) << __func__ << "Can't remove file: "
<< GetCompatibleU8String(file_path.u8string());
}
}
}
callback();
});
}
} // namespace sharing
} // namespace nearby
+65
View File
@@ -0,0 +1,65 @@
// 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_SHARING_NEARBY_FILE_HANDLER_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_FILE_HANDLER_H_
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <memory>
#include <vector>
#include "internal/platform/task_runner.h"
namespace nearby {
namespace sharing {
// This class manages async File IO for Nearby Share file payloads. Opening and
// releasing files need to run on a MayBlock task runner.
class NearbyFileHandler {
public:
struct FileInfo {
int64_t size;
std::filesystem::path file_path;
};
using OpenFilesCallback = std::function<void(std::vector<FileInfo>)>;
using GetUniquePathCallback = std::function<void(std::filesystem::path)>;
using DeleteFilesFromDiskCallback = std::function<void()>;
NearbyFileHandler();
~NearbyFileHandler();
// Open the files given in |file_paths| and return the opened files sizes via
// |callback|. If any file fails to open, return an empty list.
void OpenFiles(std::vector<std::filesystem::path> file_paths,
OpenFilesCallback callback);
void DeleteFilesFromDisk(std::vector<std::filesystem::path> file_paths,
DeleteFilesFromDiskCallback callback);
// Finds a unique path name for |file_path| and runs |callback| with the same.
void GetUniquePath(const std::filesystem::path& file_path,
GetUniquePathCallback callback);
private:
std::unique_ptr<TaskRunner> sequenced_task_runner_ = nullptr;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_FILE_HANDLER_H_
+151
View File
@@ -0,0 +1,151 @@
// 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 "sharing/nearby_file_handler.h"
#include <cstdio>
#include <filesystem> // NOLINT(build/c++17)
#include <vector>
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace nearby {
namespace sharing {
namespace {
bool CreateFile(std::filesystem::path file_path) {
std::FILE* file = std::fopen(file_path.string().c_str(), "w+");
if (file == nullptr) {
return false;
}
std::fclose(file);
return true;
}
bool DeleteFile(std::filesystem::path file_path) {
if (std::filesystem::exists(file_path)) {
return std::filesystem::remove(file_path);
}
return true;
}
bool ExistFile(std::filesystem::path file_path) {
if (std::filesystem::exists(file_path)) {
return true;
}
return false;
}
TEST(NearbyFileHandler, GetUniquePath) {
NearbyFileHandler nearby_file_handler;
std::filesystem::path unique_path;
absl::Notification notification;
std::filesystem::path test_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
std::filesystem::path expected_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
ASSERT_TRUE(CreateFile(test_file));
ASSERT_TRUE(DeleteFile(expected_file));
nearby_file_handler.GetUniquePath(
test_file, [&notification, &unique_path](std::filesystem::path path) {
unique_path = path;
notification.Notify();
});
notification.WaitForNotificationWithTimeout(absl::Seconds(1));
EXPECT_EQ(unique_path, expected_file);
}
TEST(NearbyFileHandler, OpenFiles) {
NearbyFileHandler nearby_file_handler;
absl::Notification notification;
std::vector<NearbyFileHandler::FileInfo> result;
std::filesystem::path test_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
ASSERT_TRUE(CreateFile(test_file));
nearby_file_handler.OpenFiles(
{test_file}, [&result, &notification](
std::vector<NearbyFileHandler::FileInfo> file_infos) {
result = file_infos;
notification.Notify();
});
notification.WaitForNotificationWithTimeout(absl::Seconds(1));
EXPECT_EQ(result.size(), 1);
ASSERT_TRUE(DeleteFile(test_file));
}
TEST(NearbyFileHandler, DeleteAFileFromDisk) {
NearbyFileHandler nearby_file_handler;
std::filesystem::path test_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
ASSERT_TRUE(CreateFile(test_file));
std::vector<std::filesystem::path> file_paths;
file_paths.push_back(test_file);
nearby_file_handler.DeleteFilesFromDisk(file_paths, []() {});
ASSERT_TRUE(ExistFile(test_file));
absl::SleepFor(absl::Seconds(2));
ASSERT_FALSE(ExistFile(test_file));
}
TEST(NearbyFileHandler, DeleteMultipleFilesFromDisk) {
NearbyFileHandler nearby_file_handler;
std::filesystem::path test_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
std::filesystem::path test_file2 =
std::filesystem::temp_directory_path() / "nearby_nfh_test_def.jpg";
std::filesystem::path test_file3 =
std::filesystem::temp_directory_path() / "nearby_nfh_test_ghi.jpg";
std::vector<std::filesystem::path> file_paths;
file_paths = {test_file, test_file2, test_file3};
// Check it doesn't throw an exception.
nearby_file_handler.DeleteFilesFromDisk(file_paths, []() {});
ASSERT_FALSE(ExistFile(test_file));
ASSERT_FALSE(ExistFile(test_file2));
ASSERT_FALSE(ExistFile(test_file3));
absl::SleepFor(absl::Seconds(2));
ASSERT_FALSE(ExistFile(test_file));
ASSERT_FALSE(ExistFile(test_file2));
ASSERT_FALSE(ExistFile(test_file3));
}
TEST(NearbyFileHandler, TestCallback) {
bool received_callback = false;
NearbyFileHandler nearby_file_handler;
std::filesystem::path test_file =
std::filesystem::temp_directory_path() / "nearby_nfh_test_abc.jpg";
ASSERT_TRUE(CreateFile(test_file));
std::vector<std::filesystem::path> file_paths;
file_paths.push_back(test_file);
nearby_file_handler.DeleteFilesFromDisk(
file_paths, [&received_callback]() { received_callback = true; });
ASSERT_FALSE(received_callback);
ASSERT_TRUE(ExistFile(test_file));
absl::SleepFor(absl::Seconds(2));
ASSERT_TRUE(received_callback);
ASSERT_FALSE(ExistFile(test_file));
}
} // namespace
} // namespace sharing
} // namespace nearby
@@ -0,0 +1,52 @@
// Copyright 2022-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 "sharing/nearby_share_profile_info_provider_impl.h"
#include <optional>
#include <string>
#include "internal/platform/device_info.h"
#include "internal/platform/implementation/account_manager.h"
namespace nearby {
namespace sharing {
NearbyShareProfileInfoProviderImpl::NearbyShareProfileInfoProviderImpl(
nearby::DeviceInfo& device_info, AccountManager& account_manager)
: device_info_(device_info), account_manager_(account_manager) {}
NearbyShareProfileInfoProviderImpl::~NearbyShareProfileInfoProviderImpl() =
default;
std::optional<std::string> NearbyShareProfileInfoProviderImpl::GetGivenName()
const {
// Use the given name when the user logs in to the backend.
std::optional<AccountManager::Account> account =
account_manager_.GetCurrentAccount();
if (account.has_value() && !account->given_name.empty()) {
return account->given_name;
}
return device_info_.GetGivenName();
}
std::optional<std::string>
NearbyShareProfileInfoProviderImpl::GetProfileUserName() const {
return device_info_.GetProfileUserName();
}
} // namespace sharing
} // namespace nearby
@@ -0,0 +1,53 @@
// Copyright 2022-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_SHARING_NEARBY_SHARE_PROFILE_INFO_PROVIDER_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARE_PROFILE_INFO_PROVIDER_IMPL_H_
#include <optional>
#include <string>
#include "internal/platform/device_info.h"
#include "internal/platform/implementation/account_manager.h"
#include "sharing/common/nearby_share_profile_info_provider.h"
namespace nearby {
namespace sharing {
// An implementation of NearbyShareProfileInfoProvider that accesses the actual
// profile data.
class NearbyShareProfileInfoProviderImpl
: public NearbyShareProfileInfoProvider {
public:
NearbyShareProfileInfoProviderImpl(nearby::DeviceInfo& device_info,
AccountManager& account_manager);
NearbyShareProfileInfoProviderImpl(
const NearbyShareProfileInfoProviderImpl&) = delete;
NearbyShareProfileInfoProviderImpl& operator=(
const NearbyShareProfileInfoProviderImpl&) = delete;
~NearbyShareProfileInfoProviderImpl() override;
// NearbyShareProfileInfoProvider:
std::optional<std::string> GetGivenName() const override;
std::optional<std::string> GetProfileUserName() const override;
private:
nearby::DeviceInfo& device_info_;
AccountManager& account_manager_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARE_PROFILE_INFO_PROVIDER_IMPL_H_
@@ -0,0 +1,125 @@
// Copyright 2022-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 "sharing/nearby_share_profile_info_provider_impl.h"
#include <optional>
#include <string>
#include "gtest/gtest.h"
#include "internal/platform/implementation/account_manager.h"
#include "internal/test/fake_account_manager.h"
#include "internal/test/fake_device_info.h"
namespace nearby {
namespace sharing {
namespace {
constexpr char kTestAccountId[] = "test_account_id";
constexpr char kTestAccountGivenName[] = "given_name";
constexpr char kExpectedTestAccountGivenName[] = "given_name";
constexpr char kProfileGivenName[] = "Barack";
constexpr char kProfileProfileUserName[] = "test@gmail.com";
} // namespace
class NearbyShareProfileInfoProviderImplTest : public ::testing::Test {
protected:
NearbyShareProfileInfoProviderImplTest() = default;
~NearbyShareProfileInfoProviderImplTest() override = default;
void SetUp() override {
fake_device_info_.SetGivenName(std::nullopt);
fake_device_info_.SetProfileUserName(std::nullopt);
}
void SetUserGivenName(const std::string& name) {
fake_device_info_.SetGivenName(name);
}
void SetProfileUserName(const std::string& profile_user_name) {
fake_device_info_.SetProfileUserName(profile_user_name);
}
FakeDeviceInfo& fake_device_info() {
return fake_device_info_;
}
FakeAccountManager& fake_account_manager() { return fake_account_manager_; }
private:
FakeAccountManager fake_account_manager_;
FakeDeviceInfo fake_device_info_;
};
TEST_F(NearbyShareProfileInfoProviderImplTest, GivenName) {
SetProfileUserName(kProfileProfileUserName);
NearbyShareProfileInfoProviderImpl profile_info_provider(
fake_device_info(), fake_account_manager());
// If no user, return std::nullopt.
EXPECT_FALSE(profile_info_provider.GetGivenName());
// If given name is empty, return std::nullopt.
SetUserGivenName(std::string());
EXPECT_FALSE(profile_info_provider.GetGivenName());
SetUserGivenName(kProfileGivenName);
EXPECT_EQ(profile_info_provider.GetGivenName(), kProfileGivenName);
}
TEST_F(NearbyShareProfileInfoProviderImplTest, ProfileUserName) {
{
// If profile username is empty, return std::nullopt.
SetProfileUserName(std::string());
NearbyShareProfileInfoProviderImpl profile_info_provider(
fake_device_info(), fake_account_manager());
EXPECT_FALSE(profile_info_provider.GetProfileUserName());
}
{
SetProfileUserName(kProfileProfileUserName);
NearbyShareProfileInfoProviderImpl profile_info_provider(
fake_device_info(), fake_account_manager());
EXPECT_EQ(profile_info_provider.GetProfileUserName(),
kProfileProfileUserName);
}
}
TEST_F(NearbyShareProfileInfoProviderImplTest, GivenNameUseLoginAccount) {
AccountManager::Account account;
account.id = kTestAccountId;
account.given_name = kTestAccountGivenName;
fake_account_manager().SetAccount(account);
SetUserGivenName(kProfileGivenName);
NearbyShareProfileInfoProviderImpl profile_info_provider(
fake_device_info(), fake_account_manager());
EXPECT_EQ(profile_info_provider.GetGivenName(),
kExpectedTestAccountGivenName);
}
TEST_F(NearbyShareProfileInfoProviderImplTest,
GivenNameNotUseLoginAccountWhenGivenNameEmpty) {
AccountManager::Account account;
account.id = kTestAccountId;
fake_account_manager().SetAccount(account);
SetUserGivenName(kProfileGivenName);
NearbyShareProfileInfoProviderImpl profile_info_provider(
fake_device_info(), fake_account_manager());
EXPECT_EQ(profile_info_provider.GetGivenName(), kProfileGivenName);
}
} // namespace sharing
} // namespace nearby
+42
View File
@@ -0,0 +1,42 @@
// 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_SHARING_NEARBY_SHARING_DECODER_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_DECODER_H_
#include <stdint.h>
#include <memory>
#include "absl/types/span.h"
#include "sharing/advertisement.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
class NearbySharingDecoder {
public:
virtual ~NearbySharingDecoder() = default;
virtual std::unique_ptr<Advertisement> DecodeAdvertisement(
absl::Span<const uint8_t> data) = 0;
virtual std::unique_ptr<nearby::sharing::service::proto::Frame> DecodeFrame(
absl::Span<const uint8_t> data) = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_DECODER_H_
+47
View File
@@ -0,0 +1,47 @@
// 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 "sharing/nearby_sharing_decoder_impl.h"
#include <stdint.h>
#include <memory>
#include "absl/types/span.h"
#include "sharing/advertisement.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
using Frame = ::nearby::sharing::service::proto::Frame;
std::unique_ptr<Advertisement> NearbySharingDecoderImpl::DecodeAdvertisement(
absl::Span<const uint8_t> data) {
return Advertisement::FromEndpointInfo(data);
}
std::unique_ptr<Frame> NearbySharingDecoderImpl::DecodeFrame(
absl::Span<const uint8_t> data) {
auto frame = std::make_unique<Frame>();
if (frame->ParseFromArray(data.data(), data.size())) {
return frame;
} else {
return nullptr;
}
}
} // namespace sharing
} // namespace nearby
+41
View File
@@ -0,0 +1,41 @@
// 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_SHARING_NEARBY_SHARING_DECODER_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_DECODER_IMPL_H_
#include <stdint.h>
#include <memory>
#include "absl/types/span.h"
#include "sharing/advertisement.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/proto/wire_format.pb.h"
namespace nearby {
namespace sharing {
class NearbySharingDecoderImpl : public NearbySharingDecoder {
public:
std::unique_ptr<Advertisement> DecodeAdvertisement(
absl::Span<const uint8_t> data) override;
std::unique_ptr<nearby::sharing::service::proto::Frame> DecodeFrame(
absl::Span<const uint8_t> data) override;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_DECODER_IMPL_H_
+52
View File
@@ -0,0 +1,52 @@
// 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 "sharing/nearby_sharing_event_logger.h"
#include <memory>
#include <utility>
#include "internal/analytics/event_logger.h"
#include "sharing/common/nearby_share_prefs.h"
#include "sharing/internal/api/preference_manager.h"
#include "google/protobuf/message_lite.h"
namespace nearby {
namespace sharing {
using ::nearby::sharing::api::PreferenceManager;
NearbySharingEventLogger::NearbySharingEventLogger(
PreferenceManager& preference_manager,
std::unique_ptr<nearby::analytics::EventLogger> event_logger)
: preference_manager_(preference_manager),
event_logger_(std::move(event_logger)) {}
NearbySharingEventLogger::~NearbySharingEventLogger() = default;
void NearbySharingEventLogger::Log(const proto2::MessageLite& message) {
if (event_logger_ == nullptr) {
return;
}
if (!preference_manager_.GetBoolean(
prefs::kNearbySharingIsAnalyticsEnabledName, false)) {
return;
}
event_logger_->Log(message);
}
} // namespace sharing
} // namespace nearby
+47
View File
@@ -0,0 +1,47 @@
// 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_SHARING_NEARBY_SHARING_EVENT_LOGGER_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_EVENT_LOGGER_H_
#include <memory>
#include "internal/analytics/event_logger.h"
#include "sharing/internal/api/preference_manager.h"
#include "google/protobuf/message_lite.h"
namespace nearby {
namespace sharing {
// Nearby Sharing SDK needs to enable/disable of the event logger according to
// user settings. NearbySharingEventLogger skips analytics logging if analytics
// logging is disabled.
class NearbySharingEventLogger : public nearby::analytics::EventLogger {
public:
NearbySharingEventLogger(
nearby::sharing::api::PreferenceManager& preference_manager,
std::unique_ptr<nearby::analytics::EventLogger> event_logger);
~NearbySharingEventLogger() override;
void Log(const proto2::MessageLite& message) override;
private:
nearby::sharing::api::PreferenceManager& preference_manager_;
std::unique_ptr<nearby::analytics::EventLogger> event_logger_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_EVENT_LOGGER_H_
+111
View File
@@ -0,0 +1,111 @@
// 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 "sharing/nearby_sharing_event_logger.h"
#include <memory>
#include <utility>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/analytics/event_logger.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/common/nearby_share_prefs.h"
#include "sharing/internal/test/fake_preference_manager.h"
#include "sharing/proto/analytics/nearby_sharing_log.pb.h"
#include "google/protobuf/message_lite.h"
namespace nearby {
namespace sharing {
namespace {
using ::location::nearby::proto::sharing::EventCategory;
using ::location::nearby::proto::sharing::EventType;
using ::nearby::analytics::EventLogger;
using ::nearby::sharing::analytics::proto::SharingLog;
class MockEventLogger : public EventLogger {
public:
~MockEventLogger() override = default;
MOCK_METHOD(void, Log, (const proto2::MessageLite& message), (override));
};
class NearbySharingEventLoggerTest : public ::testing::Test {
public:
NearbySharingEventLoggerTest() = default;
void SetUp() override {
auto event_logger = std::make_unique<MockEventLogger>();
raw_event_logger_ = event_logger.get();
sharing_event_logger_ = std::make_unique<NearbySharingEventLogger>(
preference_manager_, std::move(event_logger));
}
void SetEventLogger(bool enabled) {
preference_manager_.SetBoolean(prefs::kNearbySharingIsAnalyticsEnabledName,
enabled);
}
const MockEventLogger* event_logger() { return raw_event_logger_; }
std::unique_ptr<SharingLog> GetTestEvent() {
auto sharing_log =
std::unique_ptr<SharingLog>(SharingLog::default_instance().New());
sharing_log->set_event_category(EventCategory::SETTINGS_EVENT);
sharing_log->set_event_type(EventType::TAP_HELP);
auto tap_help =
analytics::proto::SharingLog::TapHelp::default_instance().New();
sharing_log->set_allocated_tap_help(tap_help);
return sharing_log;
}
NearbySharingEventLogger* sharing_event_logger() {
return sharing_event_logger_.get();
}
private:
nearby::FakePreferenceManager preference_manager_;
MockEventLogger* raw_event_logger_ = nullptr;
std::unique_ptr<NearbySharingEventLogger> sharing_event_logger_;
};
TEST_F(NearbySharingEventLoggerTest, LogEventWhenEnabled) {
SetEventLogger(true);
EXPECT_CALL(*event_logger(), Log)
.WillOnce([&](const ::google::protobuf::MessageLite& message) {
const SharingLog* sharing_log =
dynamic_cast<const SharingLog*>(&message);
ASSERT_NE(sharing_log, nullptr);
EXPECT_EQ(sharing_log->event_category(), EventCategory::SETTINGS_EVENT);
EXPECT_EQ(sharing_log->event_type(), EventType::TAP_HELP);
});
std::unique_ptr<SharingLog> event = GetTestEvent();
sharing_event_logger()->Log(*event);
}
TEST_F(NearbySharingEventLoggerTest, NoLogEventWhenDisabled) {
SetEventLogger(false);
EXPECT_CALL(*event_logger(), Log).Times(0);
std::unique_ptr<SharingLog> event = GetTestEvent();
sharing_event_logger()->Log(*event);
}
} // namespace
} // namespace sharing
} // namespace nearby
+56
View File
@@ -0,0 +1,56 @@
// 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 "sharing/nearby_sharing_service.h"
#include <ostream>
#include <string>
#include "sharing/internal/public/logging.h"
namespace nearby {
namespace sharing {
namespace {
using StatusCodes = NearbySharingService::StatusCodes;
constexpr char kUnknownStatusCodesString[] = "Unknown_StatusCodes";
} // namespace
// static
std::string NearbySharingService::StatusCodeToString(StatusCodes status_code) {
switch (status_code) {
case StatusCodes::kOk:
return "kOk";
case StatusCodes::kError:
return "kError";
case StatusCodes::kOutOfOrderApiCall:
return "kOutOfOrderApiCall";
case StatusCodes::kStatusAlreadyStopped:
return "kStatusAlreadyStopped";
case StatusCodes::kTransferAlreadyInProgress:
return "kTransferAlreadyInProgress";
case StatusCodes::kNoAvailableConnectionMedium:
return "kNoAvailableConnectionMedium";
case StatusCodes::kIrrecoverableHardwareError:
return "kIrrecoverableHardwareError";
}
NL_LOG(ERROR) << "Unexpected value for StatusCodes: "
<< static_cast<int>(status_code);
return kUnknownStatusCodesString;
}
} // namespace sharing
} // namespace nearby
+252
View File
@@ -0,0 +1,252 @@
// 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_SHARING_NEARBY_SHARING_SERVICE_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_H_
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/network/url.h"
#include "sharing/attachment.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/share_target.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_update_callback.h"
namespace nearby {
class AccountManager;
namespace sharing {
class NearbyNotificationDelegate;
class NearbyShareCertificateManager;
class NearbyShareContactManager;
class NearbyShareHttpNotifier;
// This service implements Nearby Sharing on top of the Nearby Connections mojo.
// Currently, only single profile will be allowed to be bound at a time and only
// after the user has enabled Nearby Sharing in prefs.
class NearbySharingService {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. If entries are added, kMaxValue
// should be updated.
enum class StatusCodes {
// The operation was successful.
kOk = 0,
// The operation failed, without any more information.
kError = 1,
// The operation failed since it was called in an invalid order.
kOutOfOrderApiCall = 2,
// Tried to stop something that was already stopped.
kStatusAlreadyStopped = 3,
// Tried to register an opposite foreground surface in the midst of a
// transfer or connection.
// (Tried to register Send Surface when receiving a file or tried to
// register Receive Surface when
// sending a file.)
kTransferAlreadyInProgress = 4,
// There is no available connection medium to use.
kNoAvailableConnectionMedium = 5,
// Bluetooth or WiFi hardware ran into an irrecoverable state. User PC needs
// to be restarted.
kIrrecoverableHardwareError = 6,
kMaxValue = kIrrecoverableHardwareError
};
enum class ReceiveSurfaceState {
// Default, invalid state.
kUnknown,
// Background receive surface advertises only to contacts.
kBackground,
// Foreground receive surface advertises to everyone.
kForeground,
};
enum class SendSurfaceState {
// Default, invalid state.
kUnknown,
// Background send surface only listens to transfer update.
kBackground,
// Foreground send surface both scans and listens to transfer update.
kForeground,
};
class Observer {
public:
virtual ~Observer() = default;
virtual void OnHighVisibilityChangeRequested() {}
virtual void OnHighVisibilityChanged(bool in_high_visibility) = 0;
virtual void OnStartAdvertisingFailure() {}
virtual void OnStartDiscoveryResult(bool success) {}
virtual void OnFastInitiationDevicesDetected() {}
virtual void OnFastInitiationDevicesNotDetected() {}
virtual void OnFastInitiationScanningStopped() {}
virtual void OnBluetoothStatusChanged() {}
virtual void OnWifiStatusChanged() {}
virtual void OnLanStatusChanged() {}
virtual void OnIrrecoverableHardwareErrorReported() {}
// Called during the |KeyedService| shutdown, but before everything has been
// cleaned up. It is safe to remove any observers on this event.
virtual void OnShutdown() = 0;
};
static std::string StatusCodeToString(StatusCodes status_code);
virtual ~NearbySharingService() = default;
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
virtual bool HasObserver(Observer* observer) = 0;
// Shutdown the Nearby Sharing service, and cleanup.
virtual void Shutdown(
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Registers a send surface for handling payload transfer status and device
// discovery.
virtual void RegisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Unregisters the current send surface.
virtual void UnregisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback,
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Registers a receiver surface for handling payload transfer status.
virtual void RegisterReceiveSurface(
TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Unregisters the current receive surface.
virtual void UnregisterReceiveSurface(
TransferUpdateCallback* transfer_callback,
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Unregisters all foreground receive surfaces.
virtual void ClearForegroundReceiveSurfaces(
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Returns true if a foreground receive surface is registered.
virtual bool IsInHighVisibility() const = 0;
// Returns true if there is an ongoing file transfer.
virtual bool IsTransferring() const = 0;
// Returns true if we're currently receiving a file.
virtual bool IsReceivingFile() const = 0;
// Returns true if we're currently sending a file.
virtual bool IsSendingFile() const = 0;
// Returns true if we're currently attempting to connect to a
// remote device.
virtual bool IsConnecting() const = 0;
// Returns true if we are currently scanning for remote devices.
virtual bool IsScanning() const = 0;
// Returns true if the bluetooth adapter is present.
virtual bool IsBluetoothPresent() const = 0;
// Returns true if the bluetooth adapter is powered.
virtual bool IsBluetoothPowered() const = 0;
// Returns true if extended advertising is supported by the BLE adapter
virtual bool IsExtendedAdvertisingSupported() const = 0;
// Returns true if the PC is connected to LAN (wifi/ethernet).
virtual bool IsLanConnected() const = 0;
// Returns true if the Wi-Fi adapter is present.
virtual bool IsWifiPresent() const = 0;
// Returns true if the Wi-Fi adapter is powered.
virtual bool IsWifiPowered() const = 0;
// Returns the QR Code Url.
virtual std::string GetQrCodeUrl() const = 0;
// Sends |attachments| to the remote |share_target|.
virtual void SendAttachments(
const ShareTarget& share_target,
std::vector<std::unique_ptr<Attachment>> attachments,
std::function<void(StatusCodes)> status_codes_callback) = 0;
// Accepts incoming share from the remote |share_target|.
virtual void Accept(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) = 0;
// Rejects incoming share from the remote |share_target|.
virtual void Reject(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) = 0;
// Cancels outgoing shares to the remote |share_target|.
virtual void Cancel(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) = 0;
// Returns true if the local user cancelled the transfer to remote
// |share_target|.
virtual bool DidLocalUserCancelTransfer(const ShareTarget& share_target) = 0;
// Opens attachments from the remote |share_target|.
virtual void Open(
const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback) = 0;
// Opens an url target on a browser instance.
virtual void OpenUrl(const ::nearby::network::Url& url) = 0;
// Copies text to cache/clipboard.
virtual void CopyText(absl::string_view text) = 0;
// Persists and joins the Wi-Fi network.
virtual void JoinWifiNetwork(absl::string_view ssid,
absl::string_view password) = 0;
// Sets a cleanup callback to be called once done with transfer for ARC.
virtual void SetArcTransferCleanupCallback(
std::function<void()> callback) = 0;
virtual std::string Dump() const = 0;
virtual void UpdateFilePathsInProgress(bool update_file_paths) = 0;
virtual NearbyShareSettings* GetSettings() = 0;
virtual NearbyShareHttpNotifier* GetHttpNotifier() = 0;
virtual NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() = 0;
virtual NearbyShareContactManager* GetContactManager() = 0;
virtual NearbyShareCertificateManager* GetCertificateManager() = 0;
virtual AccountManager* GetAccountManager() = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_H_
+200
View File
@@ -0,0 +1,200 @@
// 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 "sharing/nearby_sharing_service_extension.h"
#include <filesystem> // NOLINT(build/c++17)
#include <string>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/notification.h"
#include "internal/network/url.h"
#include "sharing/file_attachment.h"
#include "sharing/internal/base/utf_string_conversions.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
#include "sharing/text_attachment.h"
#include "sharing/wifi_credentials_attachment.h"
namespace nearby {
namespace sharing {
namespace {
using ::nearby::sharing::service::proto::FileMetadata;
using ::nearby::sharing::service::proto::TextMetadata;
using StatusCodes = ::nearby::sharing::NearbySharingService::StatusCodes;
} // namespace
NearbySharingService::StatusCodes NearbySharingServiceExtension::Open(
const ShareTarget& share_target) {
if (share_target.file_attachments.empty() &&
share_target.text_attachments.empty() &&
share_target.wifi_credentials_attachments.empty()) {
return StatusCodes::kOk;
}
if (!share_target.file_attachments.empty() &&
!share_target.text_attachments.empty()) {
NL_LOG(ERROR)
<< __func__
<< ": Text attachments and file attachments can't come together.";
return StatusCodes::kError;
}
if (share_target.text_attachments.size() == 1) {
const TextAttachment& text_attachment = share_target.text_attachments[0];
switch (text_attachment.type()) {
case TextMetadata::TEXT: {
CopyText(text_attachment.text_body());
break;
}
case TextMetadata::URL: {
OpenUrl(*nearby::network::Url::Create(text_attachment.text_body()));
break;
}
default: {
// Copy text for all other text types.
CopyText(text_attachment.text_title());
break;
}
}
return StatusCodes::kOk;
}
if (share_target.text_attachments.size() > 1) {
NL_LOG(ERROR) << __func__
<< ": Multiple text attachments are not supported currently.";
return StatusCodes::kError;
}
if (share_target.wifi_credentials_attachments.size() == 1) {
const WifiCredentialsAttachment& wifi_credentials_attachment =
share_target.wifi_credentials_attachments[0];
JoinWifiNetwork(wifi_credentials_attachment.ssid(),
wifi_credentials_attachment.password());
return StatusCodes::kOk;
}
if (share_target.wifi_credentials_attachments.size() > 1) {
NL_LOG(ERROR) << __func__
<< ": Multiple WiFi credentials attachments are not "
"supported currently.";
return StatusCodes::kError;
}
const FileAttachment& file_attachment = share_target.file_attachments[0];
if ((share_target.file_attachments.size() > 1) ||
((file_attachment.type() != FileMetadata::AUDIO) &&
(file_attachment.type() != FileMetadata::VIDEO) &&
(file_attachment.type() != FileMetadata::IMAGE))) {
// Opens download folder.
NearbySharingService::StatusCodes status_codes = StatusCodes::kOk;
absl::Notification notification;
context_->GetShell().Open(
std::filesystem::path(
utils::Utf8ToWide(settings_->GetCustomSavePath())),
[&status_codes, &notification](absl::Status status) {
if (!status.ok()) {
NL_LOG(ERROR)
<< "Failed to open download folder with error message:"
<< status;
status_codes = StatusCodes::kError;
} else {
status_codes = StatusCodes::kOk;
}
notification.Notify();
});
notification.WaitForNotification();
return status_codes;
}
// Opens the file with default application.
std::filesystem::path file_path;
if (file_attachment.file_path().has_value()) {
file_path = *file_attachment.file_path();
} else {
file_path = std::filesystem::path(
utils::Utf8ToWide(settings_->GetCustomSavePath())) /
// NOLINTNEXTLINE cannot build without the new string creation
utils::Utf8ToWide(std::string(file_attachment.file_name()));
}
NearbySharingService::StatusCodes status_codes = StatusCodes::kOk;
absl::Notification notification;
context_->GetShell().Open(
file_path, [file_name = file_attachment.file_name(), &status_codes,
&notification](absl::Status status) {
if (!status.ok()) {
NL_LOG(ERROR) << "Failed to open file " << file_name;
status_codes = StatusCodes::kError;
} else {
status_codes = StatusCodes::kOk;
}
notification.Notify();
});
notification.WaitForNotification();
return status_codes;
}
// Opens an url target on a browser instance.
void NearbySharingServiceExtension::OpenUrl(const ::nearby::network::Url& url) {
absl::Notification notification;
context_->OpenUrl(url, [url, &notification](absl::Status status) {
if (!status.ok()) {
NL_LOG(ERROR) << "Failed to open URL " << url.GetUrlPath()
<< " with error " << status.message();
}
notification.Notify();
});
notification.WaitForNotification();
}
// Copies text to cache/clipboard.
void NearbySharingServiceExtension::CopyText(absl::string_view text) {
absl::Notification notification;
context_->CopyText(
text, [text = std::string(text), &notification](absl::Status status) {
if (!status.ok()) {
NL_LOG(ERROR) << "Failed to copy text " << text << " with error "
<< status.message();
}
notification.Notify();
});
notification.WaitForNotification();
}
// Persists and joins the Wi-Fi network.
void NearbySharingServiceExtension::JoinWifiNetwork(
absl::string_view ssid, absl::string_view password) {
absl::Notification notification;
context_->GetWifiAdapter().JoinNetwork(
ssid, password,
[ssid = std::string(ssid), &notification](absl::Status status) {
if (!status.ok()) {
NL_LOG(ERROR) << "Failed to join network " << ssid << " with error "
<< status.message();
}
notification.Notify();
});
notification.WaitForNotification();
}
} // namespace sharing
} // namespace nearby
@@ -0,0 +1,62 @@
// 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_SHARING_NEARBY_SHARING_SERVICE_EXTENSION_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_EXTENSION_H_
#include <string>
#include "absl/strings/string_view.h"
#include "internal/network/url.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/share_target.h"
namespace nearby {
namespace sharing {
class NearbySharingServiceExtension {
public:
NearbySharingServiceExtension(Context* context, NearbyShareSettings* settings)
: context_(context), settings_(settings) {}
// Opens attachments from the remote |share_target|.
NearbySharingService::StatusCodes Open(const ShareTarget& share_target);
// Opens an url target on a browser instance.
void OpenUrl(const ::nearby::network::Url& url);
// Copies text to cache/clipboard.
void CopyText(absl::string_view text);
// Persists and joins the Wi-Fi network.
void JoinWifiNetwork(absl::string_view ssid, absl::string_view password);
// Returns the QR Code Url.
std::string GetQrCodeUrl() const { return qr_code_url_; }
private:
Context* context_ = nullptr;
NearbyShareSettings* settings_ = nullptr;
// The qr code url for the current session containing Advertising token,
// Connection token and Sender Public Key.
std::string qr_code_url_ = "http://near.by/launch_by_qrcode";
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_EXTENSION_H_
@@ -0,0 +1,193 @@
// 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 "sharing/nearby_sharing_service_extension.h"
#include <filesystem> // NOLINT(build/c++17)
#include <memory>
#include <optional>
#include "gtest/gtest.h"
#include "internal/test/fake_device_info.h"
#include "sharing/file_attachment.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/internal/test/fake_preference_manager.h"
#include "sharing/internal/test/fake_shell.h"
#include "sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
#include "sharing/text_attachment.h"
#include "sharing/wifi_credentials_attachment.h"
namespace nearby {
namespace sharing {
namespace {
using StatusCodes = NearbySharingService::StatusCodes;
using ::nearby::sharing::service::proto::FileMetadata;
using ::nearby::sharing::service::proto::TextMetadata;
class NearbySharingServiceExtensionTest : public ::testing::Test {
public:
NearbySharingServiceExtensionTest() = default;
void SetUp() override {
service_extension_ = std::make_unique<NearbySharingServiceExtension>(
&context_, &nearby_share_settings_);
}
NearbySharingServiceExtension* service_extension() {
return service_extension_.get();
}
FakeContext* context() { return &context_; }
private:
std::unique_ptr<NearbySharingServiceExtension> service_extension_;
nearby::FakeDeviceInfo device_info_;
nearby::FakePreferenceManager preference_manager_;
FakeContext context_;
FakeNearbyShareLocalDeviceDataManager local_device_data_manager_{"test"};
NearbyShareSettings nearby_share_settings_{&context_, context_.GetClock(),
device_info_, preference_manager_,
&local_device_data_manager_};
};
TEST_F(NearbySharingServiceExtensionTest,
OpenSharedTargetNoFileAndTextAttachment) {
ShareTarget share_target;
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenSharedTargetBothFileAndTextAttachments) {
ShareTarget share_target;
share_target.text_attachments = {
TextAttachment(TextMetadata::TEXT, "body", "title", "mime")};
share_target.file_attachments = {
FileAttachment(std::filesystem::temp_directory_path() / "test.g1")};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kError);
}
TEST_F(NearbySharingServiceExtensionTest, OpenSharedTargetOneTextAttachment) {
ShareTarget share_target;
share_target.text_attachments = {
TextAttachment(TextMetadata::TEXT, "body", "title", "mime")};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenSharedMoreThanOneTextAttachments) {
ShareTarget share_target;
share_target.text_attachments = {
TextAttachment(TextMetadata::TEXT, "body", "title1", "mime"),
TextAttachment(TextMetadata::TEXT, "body", "title2", "mime")};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kError);
}
TEST_F(NearbySharingServiceExtensionTest, OpenShareTargetWithUrlAttacchment) {
ShareTarget share_target;
share_target.text_attachments = {TextAttachment(
TextMetadata::URL, "http://www.google.com", std::nullopt, std::nullopt)};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenShareTargetWithTextAddressAttacchment) {
ShareTarget share_target;
share_target.text_attachments = {TextAttachment(TextMetadata::ADDRESS, "body",
std::nullopt, std::nullopt)};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest, OpenShareTargetWithWifiAttacchment) {
ShareTarget share_target;
share_target.wifi_credentials_attachments = {WifiCredentialsAttachment(
"ssid", service::proto::WifiCredentialsMetadata::WPA_PSK)};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenShareTargetWithMultipleWifiAttacchments) {
ShareTarget share_target;
share_target.wifi_credentials_attachments = {
WifiCredentialsAttachment(
"ssid1", service::proto::WifiCredentialsMetadata::WPA_PSK),
WifiCredentialsAttachment(
"ssid2", service::proto::WifiCredentialsMetadata::WPA_PSK)};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kError);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenShareTargetWithOneFileAttacchment) {
ShareTarget share_target;
share_target.file_attachments = {FileAttachment(
/*id=*/1234, /*size=*/1000, /*file_name=*/"test.png",
/*mime_type=*/"image", /*type=*/FileMetadata::IMAGE)};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest, OpenSharedTargetUseDownloadFolder) {
ShareTarget share_target;
share_target.file_attachments = {
FileAttachment(std::filesystem::temp_directory_path() / "test.g1"),
FileAttachment(std::filesystem::temp_directory_path() / "test.g2")};
StatusCodes status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
auto& shell = dynamic_cast<FakeShell&>(context()->GetShell());
shell.set_return_error(true);
status_codes = service_extension()->Open(share_target);
EXPECT_NE(status_codes, StatusCodes::kOk);
shell.set_return_error(false);
status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest,
OpenSharedTargetUseDefaultApplication) {
ShareTarget share_target;
NearbySharingService::StatusCodes status_codes;
share_target.file_attachments = {
FileAttachment(std::filesystem::temp_directory_path() / "test.jpg")};
status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
share_target.file_attachments = {
FileAttachment(std::filesystem::temp_directory_path() / "test.wav")};
status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
share_target.file_attachments = {
FileAttachment(std::filesystem::temp_directory_path() / "test.wmv")};
status_codes = service_extension()->Open(share_target);
EXPECT_EQ(status_codes, StatusCodes::kOk);
}
TEST_F(NearbySharingServiceExtensionTest, GetQrCodeUrl) {
EXPECT_EQ(service_extension()->GetQrCodeUrl(),
"http://near.by/launch_by_qrcode");
}
} // namespace
} // namespace sharing
} // namespace nearby
+71
View File
@@ -0,0 +1,71 @@
// 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 "sharing/nearby_sharing_service_factory.h"
#include <memory>
#include <utility>
#include "internal/analytics/event_logger.h"
#include "internal/network/http_client_factory_impl.h"
#include "sharing/internal/api/sharing_platform.h"
#include "sharing/internal/public/context_impl.h"
#include "sharing/nearby_connections_manager_factory.h"
#include "sharing/nearby_sharing_decoder_impl.h"
#include "sharing/nearby_sharing_event_logger.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_service_impl.h"
namespace nearby {
namespace sharing {
using ::nearby::sharing::api::SharingPlatform;
NearbySharingServiceFactory* NearbySharingServiceFactory::GetInstance() {
static NearbySharingServiceFactory* instance =
new NearbySharingServiceFactory();
return instance;
}
NearbySharingService* NearbySharingServiceFactory::CreateSharingService(
LinkType link_type,
SharingPlatform& sharing_platform,
std::unique_ptr<::nearby::analytics::EventLogger> event_logger) {
if (nearby_sharing_service_ != nullptr) {
return nullptr;
}
context_ =
std::make_unique<ContextImpl>(sharing_platform);
event_logger_ = std::make_unique<NearbySharingEventLogger>(
sharing_platform.GetPreferenceManager(),
std::move(event_logger));
decoder_ = std::make_unique<NearbySharingDecoderImpl>();
http_client_factory_ =
std::make_unique<nearby::network::HttpClientFactoryImpl>();
nearby_connections_manager_ =
NearbyConnectionsManagerFactory::CreateConnectionsManager(
link_type, context_.get(), sharing_platform.GetDeviceInfo(),
event_logger_.get());
nearby_sharing_service_ = std::make_unique<NearbySharingServiceImpl>(
context_.get(), sharing_platform, decoder_.get(),
http_client_factory_.get(), std::move(nearby_connections_manager_),
event_logger_.get());
return nearby_sharing_service_.get();
}
} // namespace sharing
} // namespace nearby
+57
View File
@@ -0,0 +1,57 @@
// 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_SHARING_NEARBY_SHARING_SERVICE_FACTORY_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_FACTORY_H_
#include <memory>
#include "internal/analytics/event_logger.h"
#include "internal/network/http_client_factory.h"
#include "sharing/internal/api/sharing_platform.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/nearby_sharing_service.h"
namespace nearby {
namespace sharing {
class NearbySharingServiceFactory {
public:
enum class LinkType { kStatic, kDynamic };
// Return a singleton instance of NearbySharingServiceFactory.
static NearbySharingServiceFactory* GetInstance();
NearbySharingService* CreateSharingService(
LinkType link_type,
nearby::sharing::api::SharingPlatform& sharing_platform,
std::unique_ptr<::nearby::analytics::EventLogger> event_logger = nullptr);
private:
NearbySharingServiceFactory() = default;
std::unique_ptr<Context> context_;
std::unique_ptr<::nearby::analytics::EventLogger> event_logger_;
std::unique_ptr<NearbySharingDecoder> decoder_;
std::unique_ptr<nearby::network::HttpClientFactory> http_client_factory_;
std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager_;
std::unique_ptr<NearbySharingService> nearby_sharing_service_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_FACTORY_H_
File diff suppressed because it is too large Load Diff
+684
View File
@@ -0,0 +1,684 @@
// Copyright 2022-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_SHARING_NEARBY_SHARING_SERVICE_IMPL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/analytics/event_logger.h"
#include "internal/base/observer_list.h"
#include "internal/network/http_client_factory.h"
#include "internal/network/url.h"
#include "internal/platform/device_info.h"
#include "internal/platform/implementation/account_manager.h"
#include "internal/platform/task_runner.h"
#include "internal/platform/timer.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/advertisement.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/attachment.h"
#include "sharing/attachment_info.h"
#include "sharing/certificates/nearby_share_certificate_manager.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/certificates/nearby_share_private_certificate.h"
#include "sharing/client/nearby_share_client.h"
#include "sharing/client/nearby_share_client_impl.h"
#include "sharing/client/nearby_share_http_notifier.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/common/nearby_share_profile_info_provider.h"
#include "sharing/fast_initiation/nearby_fast_initiation.h"
#include "sharing/incoming_share_target_info.h"
#include "sharing/internal/api/bluetooth_adapter.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/api/sharing_platform.h"
#include "sharing/internal/api/wifi_adapter.h"
#include "sharing/internal/public/connectivity_manager.h"
#include "sharing/internal/public/context.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/nearby_file_handler.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/nearby_sharing_service_extension.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/outgoing_share_target_info.h"
#include "sharing/paired_key_verification_runner.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/share_target_info.h"
#include "sharing/text_attachment.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
#include "sharing/wifi_credentials_attachment.h"
namespace nearby {
namespace sharing {
class NearbyShareContactManager;
namespace NearbySharingServiceUnitTests {
class NearbySharingServiceImplTest_CreateShareTarget_Test;
};
// All methods should be called from the same sequence that created the service.
class NearbySharingServiceImpl
: public NearbySharingService,
public NearbyShareSettings::Observer,
public NearbyShareCertificateManager::Observer,
public ::nearby::AccountManager::Observer,
public NearbyFastInitiation::Observer,
public sharing::api::BluetoothAdapter::Observer,
public sharing::api::WifiAdapter::Observer,
public NearbyConnectionsManager::IncomingConnectionListener,
public NearbyConnectionsManager::DiscoveryListener {
FRIEND_TEST(NearbySharingServiceUnitTests::NearbySharingServiceImplTest,
CreateShareTarget);
public:
NearbySharingServiceImpl(
Context* context, nearby::sharing::api::SharingPlatform& sharing_platform,
NearbySharingDecoder* decoder,
nearby::network::HttpClientFactory* http_client_factory,
std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager,
nearby::analytics::EventLogger* event_logger = nullptr);
~NearbySharingServiceImpl() override;
// NearbySharingService
void AddObserver(NearbySharingService::Observer* observer) override;
void RemoveObserver(NearbySharingService::Observer* observer) override;
bool HasObserver(NearbySharingService::Observer* observer) override;
void Shutdown(
std::function<void(StatusCodes)> status_codes_callback) override;
void RegisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) override;
void UnregisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback,
std::function<void(StatusCodes)> status_codes_callback) override;
void RegisterReceiveSurface(
TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state,
std::function<void(StatusCodes)> status_codes_callback) override;
void UnregisterReceiveSurface(
TransferUpdateCallback* transfer_callback,
std::function<void(StatusCodes)> status_codes_callback) override;
void ClearForegroundReceiveSurfaces(
std::function<void(StatusCodes)> status_codes_callback) override;
bool IsInHighVisibility() const override;
bool IsTransferring() const override;
bool IsReceivingFile() const override;
bool IsSendingFile() const override;
bool IsScanning() const override;
bool IsConnecting() const override;
bool IsBluetoothPresent() const override;
bool IsBluetoothPowered() const override;
bool IsExtendedAdvertisingSupported() const override;
bool IsLanConnected() const override;
bool IsWifiPresent() const override;
bool IsWifiPowered() const override;
std::string GetQrCodeUrl() const override;
void SendAttachments(
const ShareTarget& share_target,
std::vector<std::unique_ptr<Attachment>> attachments,
std::function<void(StatusCodes)> status_codes_callback) override;
void Accept(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
void Reject(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
void Cancel(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
bool DidLocalUserCancelTransfer(const ShareTarget& share_target) override;
void Open(const ShareTarget& share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback)
override;
void OpenUrl(const ::nearby::network::Url& url) override;
void CopyText(absl::string_view text) override;
void JoinWifiNetwork(absl::string_view ssid,
absl::string_view password) override;
void SetArcTransferCleanupCallback(std::function<void()> callback) override;
NearbyShareSettings* GetSettings() override;
NearbyShareHttpNotifier* GetHttpNotifier() override;
NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() override;
NearbyShareContactManager* GetContactManager() override;
NearbyShareCertificateManager* GetCertificateManager() override;
AccountManager* GetAccountManager() override;
// NearbyConnectionsManager::IncomingConnectionListener:
void OnIncomingConnection(absl::string_view endpoint_id,
absl::Span<const uint8_t> endpoint_info,
NearbyConnection* connection) override;
std::string Dump() const override;
void UpdateFilePathsInProgress(bool update) override;
private:
// Internal implementation of methods to avoid using recursive mutex.
StatusCodes InternalUnregisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback);
StatusCodes InternalUnregisterReceiveSurface(
TransferUpdateCallback* transfer_callback);
// NearbyShareSettings::Observer:
void OnSettingChanged(absl::string_view key, const Data& data) override;
void OnIsFastInitiationHardwareSupportedChanged(bool is_supported) override;
void OnEnabledChanged(bool enabled);
void OnFastInitiationNotificationStateChanged(
proto::FastInitiationNotificationState state);
void OnDeviceNameChanged(absl::string_view device_name);
void OnDataUsageChanged(proto::DataUsage data_usage);
void OnCustomSavePathChanged(absl::string_view custom_save_path);
void OnVisibilityChanged(proto::DeviceVisibility visibility);
void OnAllowedContactsChanged(absl::Span<const std::string> allowed_contacts);
void OnIsOnboardingCompleteChanged(bool is_complete);
void OnIsReceivingChanged(bool is_receiving);
// NearbyShareCertificateManager::Observer:
void OnPublicCertificatesDownloaded() override;
void OnPrivateCertificatesChanged() override;
// AccountManager::Observer:
void OnLoginSucceeded(absl::string_view account_id) override;
void OnLogoutSucceeded(absl::string_view account_id) override;
// NearbyConnectionsManager::DiscoveryListener:
void OnEndpointDiscovered(absl::string_view endpoint_id,
absl::Span<const uint8_t> endpoint_info) override;
void OnEndpointLost(absl::string_view endpoint_id) override;
// Handle the state changes of screen lock.
void OnLockStateChanged(bool locked);
// Handle the state changes of bluetooth adapter.
void AdapterPresentChanged(sharing::api::BluetoothAdapter* adapter,
bool present) override;
void AdapterPoweredChanged(sharing::api::BluetoothAdapter* adapter,
bool powered) override;
// Handle the state changes of Wi-Fi adapter.
void AdapterPresentChanged(sharing::api::WifiAdapter* adapter,
bool present) override;
void AdapterPoweredChanged(sharing::api::WifiAdapter* adapter,
bool powered) override;
// Handle the hardware error reported that requires PC restart.
void HardwareErrorReported(NearbyFastInitiation* fast_init) override;
void SetupBluetoothAdapter();
ObserverList<TransferUpdateCallback>& GetReceiveCallbacksFromState(
ReceiveSurfaceState state);
bool IsVisibleInBackground(proto::DeviceVisibility visibility);
std::optional<std::vector<uint8_t>> CreateEndpointInfo(
const std::optional<std::string>& device_name) const;
void StartFastInitiationAdvertising();
void OnStartFastInitiationAdvertising();
void OnStartFastInitiationAdvertisingError();
void StopFastInitiationAdvertising();
void OnStopFastInitiationAdvertising();
// Processes endpoint discovered/lost events. We queue up the events to ensure
// each discovered or lost event is fully handled before the next is run. For
// example, we don't want to start processing an endpoint-lost event before
// the corresponding endpoint-discovered event is finished. This is especially
// important because of the asynchronous steps required to process an
// endpoint-discovered event.
void AddEndpointDiscoveryEvent(std::function<void()> event);
void HandleEndpointDiscovered(absl::string_view endpoint_id,
absl::Span<const uint8_t> endpoint_info);
void HandleEndpointLost(absl::string_view endpoint_id);
void FinishEndpointDiscoveryEvent();
void OnOutgoingAdvertisementDecoded(
absl::string_view endpoint_id, absl::Span<const uint8_t> endpoint_info,
std::unique_ptr<Advertisement> advertisement);
void OnOutgoingDecryptedCertificate(
absl::string_view endpoint_id, absl::Span<const uint8_t> endpoint_info,
std::unique_ptr<Advertisement> advertisement,
std::optional<NearbyShareDecryptedPublicCertificate> certificate);
void ScheduleCertificateDownloadDuringDiscovery(size_t attempt_count);
void OnCertificateDownloadDuringDiscoveryTimerFired(size_t attempt_count);
bool HasAvailableConnectionMediums();
void InvalidateSurfaceState();
void InvalidateSendSurfaceState();
void InvalidateScanningState();
void InvalidateFastInitiationAdvertising();
void InvalidateReceiveSurfaceState();
void InvalidateAdvertisingState();
void StopAdvertising();
void StartScanning();
StatusCodes StopScanning();
void StopAdvertisingAndInvalidateSurfaceState();
void InvalidateFastInitiationScanning();
void StartFastInitiationScanning();
void OnFastInitiationDevicesDetected();
void OnFastInitiationDevicesNotDetected();
void StopFastInitiationScanning();
void ScheduleRotateBackgroundAdvertisementTimer();
void OnRotateBackgroundAdvertisementTimerFired();
void RemoveOutgoingShareTargetWithEndpointId(absl::string_view endpoint_id);
void OnTransferComplete();
void OnTransferStarted(bool is_incoming);
void ReceivePayloads(
ShareTarget share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback);
StatusCodes SendPayloads(const ShareTarget& share_target);
void OnUniquePathFetched(int64_t attachment_id, int64_t payload_id,
std::function<void(Status)> callback,
std::filesystem::path path);
void OnPayloadPathRegistered(Status status);
void OnPayloadPathsRegistered(
const ShareTarget& share_target, std::unique_ptr<bool> aggregated_success,
std::function<void(StatusCodes status_codes)> status_codes_callback);
void OnOutgoingConnection(const ShareTarget& share_target,
absl::Time connect_start_time,
NearbyConnection* connection);
void SendIntroduction(const ShareTarget& share_target,
std::optional<std::string> four_digit_token);
void CreatePayloads(ShareTarget share_target,
std::function<void(ShareTarget, bool)> callback);
void OnCreatePayloads(std::vector<uint8_t> endpoint_info,
ShareTarget share_target, bool success);
void OnOpenFiles(ShareTarget share_target,
std::function<void(ShareTarget, bool)> callback,
std::vector<NearbyFileHandler::FileInfo> files);
std::vector<Payload> CreateTextPayloads(
const std::vector<TextAttachment>& attachments);
std::vector<Payload> CreateWifiCredentialsPayloads(
const std::vector<WifiCredentialsAttachment>& attachments);
void WriteResponseFrame(
NearbyConnection& connection,
nearby::sharing::service::proto::ConnectionResponseFrame::Status
response_status);
void WriteCancelFrame(NearbyConnection& connection);
void WriteProgressUpdateFrame(NearbyConnection& connection,
std::optional<bool> start_transfer,
std::optional<float> progress);
void Fail(const ShareTarget& share_target, TransferMetadata::Status status);
void OnIncomingAdvertisementDecoded(
absl::string_view endpoint_id, ShareTarget placeholder_share_target,
std::unique_ptr<Advertisement> advertisement);
void OnIncomingTransferUpdate(const ShareTarget& share_target,
const TransferMetadata& metadata);
void OnOutgoingTransferUpdate(const ShareTarget& share_target,
const TransferMetadata& metadata);
void CloseConnection(const ShareTarget& share_target);
void OnIncomingDecryptedCertificate(
absl::string_view endpoint_id,
std::unique_ptr<Advertisement> advertisement,
ShareTarget placeholder_share_target,
std::optional<NearbyShareDecryptedPublicCertificate> certificate);
void RunPairedKeyVerification(
const ShareTarget& share_target, absl::string_view endpoint_id,
std::function<
void(PairedKeyVerificationRunner::PairedKeyVerificationResult,
::location::nearby::proto::sharing::OSType)>
callback);
void OnIncomingConnectionKeyVerificationDone(
ShareTarget share_target, std::optional<std::string> four_digit_token,
PairedKeyVerificationRunner::PairedKeyVerificationResult result,
::location::nearby::proto::sharing::OSType share_target_os_type);
void OnOutgoingConnectionKeyVerificationDone(
const ShareTarget& share_target,
std::optional<std::string> four_digit_token,
PairedKeyVerificationRunner::PairedKeyVerificationResult result,
::location::nearby::proto::sharing::OSType share_target_os_type);
void RefreshUIOnDisconnection(ShareTarget share_target);
void ReceiveIntroduction(ShareTarget share_target,
std::optional<std::string> four_digit_token);
void OnReceivedIntroduction(
ShareTarget share_target, std::optional<std::string> four_digit_token,
std::optional<nearby::sharing::service::proto::V1Frame> frame);
void ReceiveConnectionResponse(ShareTarget share_target);
void OnReceiveConnectionResponse(
ShareTarget share_target,
std::optional<nearby::sharing::service::proto::V1Frame> frame);
void OnStorageCheckCompleted(ShareTarget share_target,
std::optional<std::string> four_digit_token,
bool is_out_of_storage);
void OnFrameRead(
ShareTarget share_target,
std::optional<nearby::sharing::service::proto::V1Frame> frame);
void HandleCertificateInfoFrame(
const nearby::sharing::service::proto::CertificateInfoFrame&
certificate_frame);
void HandleProgressUpdateFrame(
const ShareTarget& share_target,
const nearby::sharing::service::proto::ProgressUpdateFrame&
progress_update_frame);
void OnIncomingConnectionDisconnected(const ShareTarget& share_target);
void OnOutgoingConnectionDisconnected(const ShareTarget& share_target);
void OnIncomingMutualAcceptanceTimeout(const ShareTarget& share_target);
void OnOutgoingMutualAcceptanceTimeout(const ShareTarget& share_target);
void Cleanup();
std::optional<ShareTarget> CreateShareTarget(
absl::string_view endpoint_id,
std::unique_ptr<Advertisement> advertisement,
std::optional<NearbyShareDecryptedPublicCertificate> certificate,
bool is_incoming);
void OnPayloadTransferUpdate(ShareTarget share_target,
TransferMetadata metadata);
bool OnIncomingPayloadsComplete(ShareTarget& share_target);
void RemoveIncomingPayloads(ShareTarget share_target);
void Disconnect(const ShareTarget& share_target, TransferMetadata metadata);
void OnDisconnectingConnectionTimeout(absl::string_view endpoint_id);
void OnDisconnectingConnectionDisconnected(const ShareTarget& share_target,
absl::string_view endpoint_id);
ShareTargetInfo& GetOrCreateShareTargetInfo(const ShareTarget& share_target,
absl::string_view endpoint_id);
ShareTargetInfo* GetShareTargetInfo(const ShareTarget& share_target);
IncomingShareTargetInfo* GetIncomingShareTargetInfo(
const ShareTarget& share_target);
OutgoingShareTargetInfo* GetOutgoingShareTargetInfo(
const ShareTarget& share_target);
NearbyConnection* GetConnection(const ShareTarget& share_target);
std::optional<std::vector<uint8_t>> GetBluetoothMacAddressForShareTarget(
const ShareTarget& share_target);
void ClearOutgoingShareTargetInfoMap();
void SetAttachmentPayloadId(const Attachment& attachment, int64_t payload_id);
std::optional<int64_t> GetAttachmentPayloadId(int64_t attachment_id);
void UnregisterShareTarget(const ShareTarget& share_target);
void OnStartAdvertisingResult(bool used_device_name, Status status);
void OnStopAdvertisingResult(Status status);
void OnStartDiscoveryResult(Status status);
void SetInHighVisibility(bool in_high_visibility);
// Note: |share_target| is intentionally passed by value. A share target
// reference could likely be invalidated by the owner during the multistep
// cancellation process.
void DoCancel(
ShareTarget share_target,
std::function<void(StatusCodes status_codes)> status_codes_callback,
bool is_initiator_of_cancellation);
void AbortAndCloseConnectionIfNecessary(TransferMetadata::Status status,
const ShareTarget& share_target);
// Monitor connectivity changes.
void OnNetworkChanged(nearby::ConnectivityManager::ConnectionType type);
void OnLanConnectedChanged(bool connected);
// Resets all settings of the nearby sharing service.
// Resets user preferences to a valid logged out state when |logout| is true.
// This will clear all preferences, but preserve onboarding state and revert
// visibility to a state that is valid when logged out. For example:
// `contacts` -> `off`.
void ResetAllSettings(bool logout);
// Checks whether SDK should auto-accept remote attachments.
bool ShouldSelfShareAutoAccept(const ShareTarget& share_target) const;
// Checks whether we should accept transfer.
bool ReadyToAccept(const ShareTarget& share_target,
TransferMetadata::Status status) const;
// Runs API/task on the service thread to avoid UI block.
void RunOnNearbySharingServiceThread(absl::string_view task_name,
std::function<void()> task);
// Runs API/task on the service thread with delayed time.
void RunOnNearbySharingServiceThreadDelayed(absl::string_view task_name,
absl::Duration delay,
std::function<void()> task);
// Runs API/task on a random thread.
void RunOnAnyThread(absl::string_view task_name, std::function<void()> task);
// Returns a 1-based position.It is used by group share feature.
int GetConnectedShareTargetPos(const ShareTarget& target);
// Returns the share target count. It is used by group share feature.
int GetConnectedShareTargetCount();
// Returns use case of sender. It is used by group share feature.
::location::nearby::proto::sharing::SharingUseCase GetSenderUseCase();
// Calculates transport type on share target.
TransportType GetTransportType(const ShareTarget& share_target) const;
// Update file path for the file attachment.
void UpdateFilePath(ShareTarget& share_target);
Context* const context_;
nearby::DeviceInfo& device_info_;
nearby::sharing::api::PreferenceManager& preference_manager_;
AccountManager& account_manager_;
NearbySharingDecoder* const decoder_;
std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager_;
// Scanner which is non-null when we are performing a background scan for
// remote devices that are attempting to share.
NearbyShareHttpNotifier nearby_share_http_notifier_;
std::unique_ptr<NearbyShareClientFactory> nearby_share_client_factory_;
std::unique_ptr<NearbyShareProfileInfoProvider> profile_info_provider_;
std::unique_ptr<NearbyShareLocalDeviceDataManager> local_device_data_manager_;
std::unique_ptr<NearbyShareContactManager> contact_manager_;
std::unique_ptr<NearbyShareCertificateManager> certificate_manager_;
std::unique_ptr<NearbyFastInitiation> nearby_fast_initiation_;
// Used to create analytics events.
std::unique_ptr<analytics::AnalyticsRecorder> analytics_recorder_;
// Used to maintain the settings of nearby sharing.
std::unique_ptr<NearbyShareSettings> settings_;
// Accesses the extension methods to Nearby Sharing service.
std::unique_ptr<NearbySharingServiceExtension> service_extension_;
NearbyFileHandler file_handler_;
bool is_screen_locked_ = false;
std::unique_ptr<Timer> rotate_background_advertisement_timer_;
std::unique_ptr<Timer> certificate_download_during_discovery_timer_;
std::unique_ptr<Timer> process_shutdown_pending_timer_;
// A list of service observers.
ObserverList<NearbySharingService::Observer> observers_;
// A list of foreground receivers.
ObserverList<TransferUpdateCallback> foreground_receive_callbacks_;
// A list of background receivers.
ObserverList<TransferUpdateCallback> background_receive_callbacks_;
// A list of foreground receivers for transfer updates on the send surface.
ObserverList<TransferUpdateCallback> foreground_send_transfer_callbacks_;
// A list of foreground receivers for discovered device updates on the send
// surface.
ObserverList<ShareTargetDiscoveredCallback>
foreground_send_discovery_callbacks_;
// A list of background receivers for transfer updates on the send surface.
ObserverList<TransferUpdateCallback> background_send_transfer_callbacks_;
// A list of background receivers for discovered device updates on the send
// surface.
ObserverList<ShareTargetDiscoveredCallback>
background_send_discovery_callbacks_;
// Registers the most recent TransferMetadata and ShareTarget used for
// transitioning notifications between foreground surfaces and background
// surfaces. Empty if no metadata is available.
std::optional<std::pair<ShareTarget, TransferMetadata>>
last_incoming_metadata_;
// The most recent outgoing TransferMetadata and ShareTarget.
std::optional<std::pair<ShareTarget, TransferMetadata>>
last_outgoing_metadata_;
// A map of ShareTarget id to IncomingShareTargetInfo. This lets us know which
// Nearby Connections endpoint and public certificate are related to the
// incoming share target.
absl::flat_hash_map<int64_t, IncomingShareTargetInfo>
incoming_share_target_info_map_;
// A map of endpoint id to ShareTarget, where each ShareTarget entry
// directly corresponds to a OutgoingShareTargetInfo entry in
// outgoing_share_target_info_map_;
absl::flat_hash_map<std::string, ShareTarget> outgoing_share_target_map_;
// A map of ShareTarget id to OutgoingShareTargetInfo. This lets us know which
// endpoint and public certificate are related to the outgoing share target.
absl::flat_hash_map<int64_t, OutgoingShareTargetInfo>
outgoing_share_target_info_map_;
// For metrics. The IDs of ShareTargets that are cancelled while trying to
// establish an outgoing connection.
absl::flat_hash_set<int64_t> all_cancelled_share_target_ids_;
// The IDs of ShareTargets that we cancelled the transfer to.
absl::flat_hash_set<int64_t> locally_cancelled_share_target_ids_;
// A map from endpoint ID to endpoint info from discovered, contact-based
// advertisements that could not decrypt any available public certificates.
// During discovery, if certificates are downloaded, we revisit this map and
// retry certificate decryption.
absl::flat_hash_map<std::string, std::vector<uint8_t>>
discovered_advertisements_to_retry_map_;
// If the discovered advertisements are retried when public certificates
// downloaded, we put it in to the retry set. The retried endpoints will not
// cause new download of public certificates. The purpose is to reduce the
// unnecessary backend API call.
absl::flat_hash_set<std::string> discovered_advertisements_retried_set_;
// A mapping of Attachment ID to additional AttachmentInfo related to the
// Attachment.
absl::flat_hash_map<int64_t, AttachmentInfo> attachment_info_map_;
// This alarm is used to disconnect the sharing connection if both sides do
// not press accept within the timeout.
std::unique_ptr<Timer> mutual_acceptance_timeout_alarm_;
// A map of ShareTarget id to disconnection timeout callback. Used to only
// disconnect after a timeout to keep sending any pending payloads.
absl::flat_hash_map<std::string, std::unique_ptr<Timer>>
disconnection_timeout_alarms_;
// The current advertising power level. PowerLevel::kUnknown while not
// advertising.
PowerLevel advertising_power_level_ = PowerLevel::kUnknown;
// True if we are currently scanning for remote devices.
bool is_scanning_ = false;
// True if we're currently sending or receiving a file.
bool is_transferring_ = false;
// True if we're currently receiving a file.
bool is_receiving_files_ = false;
// True if we're currently sending a file.
bool is_sending_files_ = false;
// True if we're currently attempting to connect to a remote device.
bool is_connecting_ = false;
// The time scanning began.
absl::Time scanning_start_timestamp_;
// True when we are advertising with a device name visible to everyone.
bool in_high_visibility_ = false;
// The time attachments are sent after a share target is selected. This is
// used to time the process from selecting a share target to writing the
// introduction frame (last frame before receiver gets notified).
absl::Time send_attachments_timestamp_;
// Whether an incoming share has been accepted, and we are waiting to log the
// time from acceptance to the start of payload transfer.
bool is_waiting_to_record_accept_to_transfer_start_metric_ = false;
// Time at which an incoming transfer was accepted. This is used to calculate
// the time between an incoming share being accepted and the first payload
// byte being processed.
absl::Time incoming_share_accepted_timestamp_;
std::unique_ptr<Timer> clear_recent_nearby_process_shutdown_count_timer_;
// Used to debounce OnNetworkChanged processing.
std::unique_ptr<Timer> on_network_changed_delay_timer_;
// Used to prevent the "Device nearby is sharing" notification from appearing
// immediately after a completed share.
std::unique_ptr<Timer> fast_initiation_scanner_cooldown_timer_;
// A queue of endpoint-discovered and endpoint-lost events that ensures the
// events are processed sequentially, in the order received from Nearby
// Connections. An event is processed either immediately, if there are no
// other events in the queue, or as soon as the previous event processing
// finishes. When processing finishes, the event is removed from the queue.
std::queue<std::function<void()>> endpoint_discovery_events_;
// Called when cleanup for ARC is needed as part of the transfer.
std::function<void()> arc_transfer_cleanup_callback_;
// Used to run nearby sharing service APIs.
std::unique_ptr<TaskRunner> service_thread_ = nullptr;
// Shouldn't schedule new task after shutting down, and skip task if the
// object is null.
std::shared_ptr<bool> is_shutting_down_ = nullptr;
// Tracks the path registration.
struct PathRegistrationStatus {
ShareTarget share_target;
uint32_t expected_count;
uint32_t current_count;
std::function<void(StatusCodes status_codes)> status_codes_callback;
bool status;
};
PathRegistrationStatus path_registration_status_;
// Used to identify current scanning session.
int64_t scanning_session_id_ = 0;
// Used to identify current advertising session.
int64_t advertising_session_id_ = 0;
// Used to identify current receiving session.
int64_t receiving_session_id_ = 0;
// Used to track the time of screen unlock.
absl::Time screen_unlock_time_;
// Whether to update the file paths in transfer progress.
bool update_file_paths_in_progress_ = false;
// Used to track the time when share sheet activity starts
absl::Time share_foreground_send_surface_start_timestamp_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_IMPL_H_
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
// 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 "sharing/nearby_sharing_service.h"
#include <string>
#include <vector>
#include "gtest/gtest.h"
namespace nearby {
namespace sharing {
namespace {
using ::nearby::sharing::NearbySharingService;
using StatusCodes = NearbySharingService::StatusCodes;
struct StatusCodeToStringData {
StatusCodes status_code;
std::string expected_string_result;
};
std::vector<StatusCodeToStringData> GetTestData() {
static std::vector<StatusCodeToStringData>* kStatusCodeToStringData =
new std::vector<StatusCodeToStringData>({
{StatusCodes::kOk, "kOk"},
{StatusCodes::kError, "kError"},
{StatusCodes::kOutOfOrderApiCall, "kOutOfOrderApiCall"},
{StatusCodes::kStatusAlreadyStopped, "kStatusAlreadyStopped"},
{StatusCodes::kTransferAlreadyInProgress,
"kTransferAlreadyInProgress"},
{StatusCodes::kNoAvailableConnectionMedium,
"kNoAvailableConnectionMedium"},
{StatusCodes::kIrrecoverableHardwareError,
"kIrrecoverableHardwareError"},
// If entries are added, kMaxValue and
// NearbySharingService::StatusCodeToString should be updated.
{StatusCodes::kMaxValue, "kIrrecoverableHardwareError"},
});
return *kStatusCodeToStringData;
}
using StatusCodeToString = testing::TestWithParam<StatusCodeToStringData>;
TEST_P(StatusCodeToString, ToStringResultMatches) {
EXPECT_EQ(GetParam().expected_string_result,
NearbySharingService::StatusCodeToString(GetParam().status_code));
}
INSTANTIATE_TEST_CASE_P(StatusCodeToString, StatusCodeToString,
testing::ValuesIn(GetTestData()));
} // namespace
} // namespace sharing
} // namespace nearby
+620
View File
@@ -0,0 +1,620 @@
// Copyright 2022-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 "sharing/nearby_sharing_settings.h"
#include <cstdint>
#include <filesystem> // NOLINT(build/c++17)
#include <functional>
#include <ios>
#include <memory>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/analytics/event_logger.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/clock.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex_lock.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/common/compatible_u8_string.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/common/nearby_share_prefs.h"
#include "sharing/flags/nearby_sharing_feature_flags.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/public/logging.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/proto/enums.pb.h"
namespace nearby {
namespace sharing {
namespace {
using ::location::nearby::proto::sharing::DesktopNotification;
using ::location::nearby::proto::sharing::DesktopTransferEventType;
using ::location::nearby::proto::sharing::ShowNotificationStatus;
using ::nearby::sharing::api::PreferenceManager;
using ::nearby::sharing::proto::DataUsage;
using ::nearby::sharing::proto::DeviceVisibility;
using ::nearby::sharing::proto::FastInitiationNotificationState;
constexpr absl::string_view kPreferencesObserverName =
"nearby-sharing-settings";
constexpr int kMaxVisibilityExpirationSeconds =
prefs::kDefaultMaxVisibilityExpirationSeconds;
ShowNotificationStatus GetNotificationStatus(
FastInitiationNotificationState state) {
switch (state) {
case FastInitiationNotificationState::ENABLED_FAST_INIT:
return ShowNotificationStatus::SHOW;
case FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT:
case FastInitiationNotificationState::DISABLED_BY_FEATURE_FAST_INIT:
return ShowNotificationStatus::NOT_SHOW;
default:
return ShowNotificationStatus::UNKNOWN_SHOW_NOTIFICATION_STATUS;
}
}
} // namespace
NearbyShareSettings::NearbyShareSettings(
Context* context,
nearby::Clock* clock,
nearby::DeviceInfo& device_info,
PreferenceManager& preference_manager,
NearbyShareLocalDeviceDataManager* local_device_data_manager,
nearby::analytics::EventLogger* event_logger)
: clock_(clock),
device_info_(device_info),
preference_manager_(preference_manager),
local_device_data_manager_(local_device_data_manager),
analytics_recorder_(
std::make_unique<analytics::AnalyticsRecorder>(event_logger)) {
is_desctructing_ = std::make_shared<bool>(false);
visibility_expiration_timer_ = context->CreateTimer();
RestoreFallbackVisibility();
preference_manager_.AddObserver(
kPreferencesObserverName,
[this, desctructing =
std::weak_ptr<bool>(is_desctructing_)](absl::string_view key) {
std::shared_ptr<bool> is_desctructing = desctructing.lock();
if (is_desctructing == nullptr || *is_desctructing) {
NL_LOG(WARNING) << ": Ignore the preferences change callback.";
return;
}
OnPreferenceChanged(key);
});
local_device_data_manager_->AddObserver(this);
}
NearbyShareSettings::~NearbyShareSettings() {
MutexLock lock(&mutex_);
is_desctructing_ = nullptr;
preference_manager_.RemoveObserver(kPreferencesObserverName);
local_device_data_manager_->RemoveObserver(this);
visibility_expiration_timer_->Stop();
}
bool NearbyShareSettings::GetEnabled() const {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(prefs::kNearbySharingEnabledName,
false);
}
FastInitiationNotificationState
NearbyShareSettings::GetFastInitiationNotificationState() const {
MutexLock lock(&mutex_);
return static_cast<FastInitiationNotificationState>(
preference_manager_.GetInteger(
prefs::kNearbySharingFastInitiationNotificationStateName,
static_cast<int>(
FastInitiationNotificationState::ENABLED_FAST_INIT)));
}
void NearbyShareSettings::SetIsFastInitiationHardwareSupported(
bool is_supported) {
MutexLock lock(&mutex_);
// If the new value is the same as the old value, don't notify observers.
if (is_fast_initiation_hardware_supported_ == is_supported) {
return;
}
is_fast_initiation_hardware_supported_ = is_supported;
for (Observer* observer : observers_set_.GetObservers()) {
observer->OnIsFastInitiationHardwareSupportedChanged(is_supported);
}
}
std::string NearbyShareSettings::GetDeviceName() const {
return local_device_data_manager_->GetDeviceName();
}
DataUsage NearbyShareSettings::GetDataUsage() const {
MutexLock lock(&mutex_);
return static_cast<DataUsage>(
preference_manager_.GetInteger(prefs::kNearbySharingDataUsageName, 0));
}
void NearbyShareSettings::StartVisibilityTimer(
absl::Duration expiration) const {
NL_LOG(INFO) << __func__
<< ": start visibility timer. expiration=" << expiration;
visibility_expiration_timer_->Start(
expiration / absl::Milliseconds(1), 0, [this]() {
NL_LOG(INFO) << __func__ << ": visibility timer expired.";
int visibility;
{
MutexLock lock(&mutex_);
visibility_expiration_timer_->Stop();
visibility = preference_manager_.GetInteger(
prefs::kNearbySharingBackgroundFallbackVisibilityName,
static_cast<int>(prefs::kDefaultFallbackVisibility));
}
SetVisibility(DeviceVisibility(visibility));
});
}
void NearbyShareSettings::RestoreFallbackVisibility() {
MutexLock lock(&mutex_);
int64_t expiration_seconds = preference_manager_.GetInteger(
prefs::kNearbySharingBackgroundVisibilityExpirationSeconds, 0);
int64_t fallback_visibility = preference_manager_.GetInteger(
prefs::kNearbySharingBackgroundFallbackVisibilityName,
static_cast<int>(prefs::kDefaultFallbackVisibility));
fallback_visibility_ = static_cast<DeviceVisibility>(fallback_visibility);
int64_t now_seconds = absl::ToUnixSeconds(clock_->Now());
int64_t remaining_seconds = expiration_seconds - now_seconds;
int64_t diff = kMaxVisibilityExpirationSeconds - remaining_seconds;
NL_LOG(INFO) << __func__ << ": diff=" << diff << ", now=" << now_seconds
<< ", expiration=" << expiration_seconds
<< ", max=" << kMaxVisibilityExpirationSeconds;
if (remaining_seconds > 0 &&
remaining_seconds <= kMaxVisibilityExpirationSeconds) { // Not expired
StartVisibilityTimer(absl::Seconds(remaining_seconds));
} else if (expiration_seconds != 0) { // Expired.
NL_LOG(INFO) << __func__
<< ": timer is already expired. Restore fallback visibility.";
SetVisibility(static_cast<DeviceVisibility>(fallback_visibility));
} else {
NL_LOG(INFO) << __func__ << ": No running fallback Visibility.";
}
}
std::vector<std::string> NearbyShareSettings::GetAllowedContacts() const {
MutexLock lock(&mutex_);
std::vector<std::string> allowed_contacts =
preference_manager_.GetStringArray(
prefs::kNearbySharingAllowedContactsName, {});
return allowed_contacts;
}
bool NearbyShareSettings::IsOnboardingComplete() const {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(
prefs::kNearbySharingOnboardingCompleteName, false);
}
std::string NearbyShareSettings::GetCustomSavePath() const {
MutexLock lock(&mutex_);
return preference_manager_.GetString(
prefs::kNearbySharingCustomSavePath,
GetCompatibleU8String(device_info_.GetDownloadPath().u8string()));
}
bool NearbyShareSettings::IsDisabledByPolicy() const { return !GetEnabled(); }
void NearbyShareSettings::AddSettingsObserver(Observer* observer) {
MutexLock lock(&mutex_);
observers_set_.AddObserver(observer);
}
void NearbyShareSettings::RemoveSettingsObserver(Observer* observer) {
MutexLock lock(&mutex_);
observers_set_.RemoveObserver(observer);
}
void NearbyShareSettings::GetEnabled(std::function<void(bool)> callback) {
std::move(callback)(GetEnabled());
}
void NearbyShareSettings::GetFastInitiationNotificationState(
std::function<void(FastInitiationNotificationState)> callback) {
std::move(callback)(GetFastInitiationNotificationState());
}
void NearbyShareSettings::GetIsFastInitiationHardwareSupported(
std::function<void(bool)> callback) {
MutexLock lock(&mutex_);
std::move(callback)(is_fast_initiation_hardware_supported_);
}
void NearbyShareSettings::SetEnabled(bool enabled) {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(prefs::kNearbySharingEnabledName, enabled);
if (enabled &&
GetVisibility() == DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED) {
NL_LOG(ERROR) << "Nearby Share enabled with visibility unset. Setting "
"default visibility to kEveryone.";
SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
}
}
void NearbyShareSettings::SetFastInitiationNotificationState(
FastInitiationNotificationState state) {
MutexLock lock(&mutex_);
analytics_recorder_->NewToggleShowNotification(
GetNotificationStatus(GetFastInitiationNotificationState()),
GetNotificationStatus(state));
preference_manager_.SetInteger(
prefs::kNearbySharingFastInitiationNotificationStateName,
static_cast<int>(state));
}
void NearbyShareSettings::IsOnboardingComplete(
std::function<void(bool)> callback) {
std::move(callback)(IsOnboardingComplete());
}
void NearbyShareSettings::SetIsOnboardingComplete(
bool completed, std::function<void()> callback) {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(prefs::kNearbySharingOnboardingCompleteName,
completed);
std::move(callback)();
}
void NearbyShareSettings::GetDeviceName(
std::function<void(absl::string_view)> callback) {
std::move(callback)(GetDeviceName());
}
void NearbyShareSettings::ValidateDeviceName(
absl::string_view device_name,
std::function<void(DeviceNameValidationResult)> callback) {
std::move(callback)(
local_device_data_manager_->ValidateDeviceName(device_name));
}
void NearbyShareSettings::SetDeviceName(
absl::string_view device_name,
std::function<void(DeviceNameValidationResult)> callback) {
analytics_recorder_->NewSetDeviceName(device_name.size());
std::move(callback)(local_device_data_manager_->SetDeviceName(device_name));
}
void NearbyShareSettings::GetDataUsage(
std::function<void(DataUsage)> callback) {
std::move(callback)(GetDataUsage());
}
void NearbyShareSettings::SetDataUsage(DataUsage data_usage) {
MutexLock lock(&mutex_);
analytics_recorder_->NewSetDataUsage(GetDataUsage(), data_usage);
preference_manager_.SetInteger(prefs::kNearbySharingDataUsageName,
static_cast<int>(data_usage));
}
void NearbyShareSettings::GetVisibility(
std::function<void(DeviceVisibility)> callback) {
std::move(callback)(GetVisibility());
}
DeviceVisibility NearbyShareSettings::GetVisibility() const {
MutexLock lock(&mutex_);
DeviceVisibility visibility =
static_cast<DeviceVisibility>(preference_manager_.GetInteger(
prefs::kNearbySharingBackgroundVisibilityName,
static_cast<int>(prefs::kDefaultVisibility)));
if (visibility == DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS) {
// Set the visibility to self share if it's only visible to selected
// contacts, as part of QuickShare rebrand work.
SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE);
return DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE;
}
return visibility;
}
void NearbyShareSettings::SetVisibility(DeviceVisibility visibility,
absl::Duration expiration) const {
MutexLock lock(&mutex_);
DeviceVisibility last_visibility =
static_cast<DeviceVisibility>(preference_manager_.GetInteger(
prefs::kNearbySharingBackgroundVisibilityName,
static_cast<int>(prefs::kDefaultVisibility)));
analytics_recorder_->NewSetVisibility(last_visibility, visibility,
expiration / absl::Milliseconds(1));
NL_VLOG(1) << __func__
<< ": set visibility. visibility=" << static_cast<int>(visibility)
<< ", expiration=" << expiration;
if (visibility_expiration_timer_->IsRunning()) {
NL_VLOG(1) << __func__
<< ": temporary visibility timer is running. stopped.";
visibility_expiration_timer_->Stop();
}
absl::Time now = clock_->Now();
if (expiration != absl::ZeroDuration()) {
NL_VLOG(1) << __func__ << ": temporary visibility timer starts.";
absl::Time fallback_visibility_timestamp = now + expiration;
preference_manager_.SetInteger(
prefs::kNearbySharingBackgroundVisibilityExpirationSeconds,
absl::ToUnixSeconds(fallback_visibility_timestamp));
StartVisibilityTimer(expiration);
} else {
preference_manager_.SetInteger(
prefs::kNearbySharingBackgroundVisibilityExpirationSeconds, 0);
}
last_visibility_timestamp_ = now;
last_visibility_ = last_visibility;
preference_manager_.SetInteger(
prefs::kNearbySharingBackgroundVisibilityName,
static_cast<int>(visibility));
}
absl::Time NearbyShareSettings::GetLastVisibilityTimestamp() const {
MutexLock lock(&mutex_);
return last_visibility_timestamp_;
}
proto::DeviceVisibility NearbyShareSettings::GetLastVisibility() const {
MutexLock lock(&mutex_);
return static_cast<proto::DeviceVisibility>(last_visibility_);
}
DeviceVisibility NearbyShareSettings::GetFallbackVisibility() const {
MutexLock lock(&mutex_);
NL_VLOG(1) << __func__ << ": get fallback visibility called.";
return fallback_visibility_.has_value() ? *fallback_visibility_
: prefs::kDefaultFallbackVisibility;
}
void NearbyShareSettings::SetFallbackVisibility(
DeviceVisibility visibility) const {
MutexLock lock(&mutex_);
NL_VLOG(1) << __func__ << ": set fallback visibility. visibility="
<< static_cast<int>(visibility);
if (visibility == DeviceVisibility::DEVICE_VISIBILITY_EVERYONE) {
NL_VLOG(1) << __func__ << ": visibility is everyone. Skip.";
return;
}
fallback_visibility_ = visibility;
preference_manager_.SetInteger(
prefs::kNearbySharingBackgroundFallbackVisibilityName,
static_cast<int>(visibility));
}
bool NearbyShareSettings::GetIsTemporarilyVisible() const {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(
prefs::kNearbySharingBackgroundTemporarilyVisibleName, false);
}
void NearbyShareSettings::SetIsTemporarilyVisible(
bool is_temporarily_visible) const {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(
prefs::kNearbySharingBackgroundTemporarilyVisibleName,
is_temporarily_visible);
}
void NearbyShareSettings::GetAllowedContacts(
std::function<void(absl::Span<const std::string>)> callback) {
std::move(callback)(GetAllowedContacts());
}
void NearbyShareSettings::SetAllowedContacts(
absl::Span<const std::string> allowed_contacts) {
MutexLock lock(&mutex_);
preference_manager_.SetStringArray(prefs::kNearbySharingAllowedContactsName,
allowed_contacts);
}
void NearbyShareSettings::GetCustomSavePathAsync(
const std::function<void(absl::string_view)>& callback) const {
callback(GetCustomSavePath());
}
void NearbyShareSettings::SetCustomSavePathAsync(
absl::string_view save_path, const std::function<void()>& callback) {
MutexLock lock(&mutex_);
preference_manager_.SetString(prefs::kNearbySharingCustomSavePath,
save_path);
callback();
}
void NearbyShareSettings::OnPreferenceChanged(absl::string_view key) {
MutexLock lock(&mutex_);
if (key == prefs::kNearbySharingEnabledName) {
NotifyAllObservers(key, Observer::Data(GetEnabled()));
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::
kEnableBackgroundScanning)) {
ProcessFastInitiationNotificationParentPrefChanged(GetEnabled());
}
} else if (key == prefs::kNearbySharingFastInitiationNotificationStateName) {
NotifyAllObservers(key, Observer::Data(static_cast<int64_t>(
GetFastInitiationNotificationState())));
} else if (key == prefs::kNearbySharingBackgroundVisibilityName) {
NotifyAllObservers(key,
Observer::Data(static_cast<int64_t>(GetVisibility())));
} else if (key == prefs::kNearbySharingDataUsageName) {
NotifyAllObservers(key,
Observer::Data(static_cast<int64_t>(GetDataUsage())));
} else if (key == prefs::kNearbySharingAllowedContactsName) {
NotifyAllObservers(key, Observer::Data(GetAllowedContacts()));
} else if (key == prefs::kNearbySharingOnboardingCompleteName) {
NotifyAllObservers(key, Observer::Data(IsOnboardingComplete()));
} else if (key == prefs::kNearbySharingIsReceivingName) {
NotifyAllObservers(key, Observer::Data(GetIsReceiving()));
} else if (key == prefs::kNearbySharingCustomSavePath) {
NotifyAllObservers(key, Observer::Data(GetCustomSavePath()));
} else {
// Not a monitored key.
return;
}
}
void NearbyShareSettings::OnLocalDeviceDataChanged(bool did_device_name_change,
bool did_full_name_change,
bool did_icon_url_change) {
MutexLock lock(&mutex_);
if (!did_device_name_change) return;
std::string device_name = GetDeviceName();
NotifyAllObservers(prefs::kNearbySharingDeviceNameName,
Observer::Data(device_name));
}
void NearbyShareSettings::NotifyAllObservers(absl::string_view key,
Observer::Data value) {
for (Observer* observer : observers_set_.GetObservers()) {
observer->OnSettingChanged(key, value);
}
}
void NearbyShareSettings::ProcessFastInitiationNotificationParentPrefChanged(
bool enabled) {
// If onboarding is not yet complete the Nearby feature should not be able
// to affect the enabled state.
if (!IsOnboardingComplete()) {
return;
}
// If the user explicitly disabled notifications, toggling the Nearby Share
// feature does not re-enable the notification sub-feature.
if (GetFastInitiationNotificationState() ==
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT) {
return;
}
SetFastInitiationNotificationState(
enabled ? FastInitiationNotificationState::ENABLED_FAST_INIT
: FastInitiationNotificationState::DISABLED_BY_FEATURE_FAST_INIT);
}
bool NearbyShareSettings::GetIsReceiving() {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(prefs::kNearbySharingIsReceivingName,
true);
}
void NearbyShareSettings::SetIsReceiving(bool is_receiving) const {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(prefs::kNearbySharingIsReceivingName,
is_receiving);
}
bool NearbyShareSettings::GetIsAnalyticsEnabled() {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(
prefs::kNearbySharingIsAnalyticsEnabledName, true);
}
void NearbyShareSettings::SetIsAnalyticsEnabled(
bool is_analytics_enabled) const {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(prefs::kNearbySharingIsAnalyticsEnabledName,
is_analytics_enabled);
}
std::string NearbyShareSettings::Dump() const {
std::stringstream sstream;
sstream << "Nearby Share Settings" << std::endl;
sstream << " Device name: " << GetDeviceName() << std::endl;
sstream << " Visibility: " << DeviceVisibility_Name(GetVisibility())
<< std::endl;
sstream << " Enabled: " << std::boolalpha << GetEnabled() << std::noboolalpha
<< std::endl;
sstream << " FastInitiationNotification: "
<< FastInitiationNotificationState_Name(
GetFastInitiationNotificationState())
<< std::endl;
sstream << " DataUsage: " << DataUsage_Name(GetDataUsage()) << std::endl;
sstream << " Last Visibility: " << DeviceVisibility_Name(GetLastVisibility())
<< std::endl;
return sstream.str();
}
bool NearbyShareSettings::GetIsAllContactsEnabled() {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(
prefs::kNearbySharingIsAllContactsEnabledName, true);
}
void NearbyShareSettings::SetIsAllContactsEnabled(
bool is_all_contacts_enabled) const {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(
prefs::kNearbySharingIsAllContactsEnabledName, is_all_contacts_enabled);
if (is_all_contacts_enabled) {
if (GetVisibility() ==
DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS) {
SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
}
} else {
if (GetVisibility() == DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS) {
SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS);
}
}
}
bool NearbyShareSettings::GetAutoAppStartEnabled() const {
MutexLock lock(&mutex_);
return preference_manager_.GetBoolean(
prefs::kNearbySharingAutoAppStartEnabledName, true);
}
void NearbyShareSettings::SetAutoAppStartEnabled(bool is_auto_app_start) const {
MutexLock lock(&mutex_);
preference_manager_.SetBoolean(prefs::kNearbySharingAutoAppStartEnabledName,
is_auto_app_start);
}
void NearbyShareSettings::SendDesktopNotification(
DesktopNotification event) const {
analytics_recorder_->NewSendDesktopNotification(event);
}
void NearbyShareSettings::SendDesktopTransferEvent(
DesktopTransferEventType event) const {
analytics_recorder_->NewSendDesktopTransferEvent(event);
}
bool NearbyShareSettings::is_fast_initiation_hardware_supported() {
MutexLock lock(&mutex_);
return is_fast_initiation_hardware_supported_;
}
} // namespace sharing
} // namespace nearby
+298
View File
@@ -0,0 +1,298 @@
// Copyright 2022-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_SHARING_NEARBY_SHARING_SETTINGS_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SETTINGS_H_
#include <cstddef>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/analytics/event_logger.h"
#include "internal/base/observer_list.h"
#include "internal/platform/clock.h"
#include "internal/platform/device_info.h"
#include "internal/platform/mutex.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/public/context.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/proto/settings_observer_data.pb.h"
namespace nearby {
namespace sharing {
// Provides a type safe wrapper/abstraction over prefs for both C++ and
// Javascript (over mojo) to interact with Nearby user settings. This class
// always reads directly from prefs and relies on preference's memory cache.
// It is designed to be contained within the Nearby Sharing Service with an
// instance per user profile. This class also helps to keep some prefs
// logic out of |NearbyShareServiceImpl|.
//
// This class is also used to expose device properties that affect the settings
// UI, but cannot be added at load time because they need to be re-computed. See
// GetIsFastInitiationHardwareSupported() as an example.
//
// The mojo interface is intended to be exposed in settings, os_settings, and
// the nearby WebUI.
//
// NOTE: The pref-change registrar only notifies observers of pref value
// changes; observers are not notified if the pref value is set but does not
// change. This class inherits this behavior.
//
// NOTE: Because the observer interface is over mojo, setting a value directly
// will not synchronously trigger the observer event. Generally this is not a
// problem because these settings should only be changed by user interaction,
// but this is necessary to know when writing unit-tests.
class NearbyShareSettings
: nearby::sharing::NearbyShareLocalDeviceDataManager::Observer {
public:
class Observer {
public:
// LINT.IfChange(TaggedUnion)
// The C++ counterpart of message `Data` in
// third_party/nearby/sharing/proto/settings_observer_data.proto
struct Data {
proto::Tag tag;
union Value {
bool as_bool;
int64_t as_int64;
std::string as_string;
std::vector<std::string> as_string_array;
explicit Value(std::nullptr_t = nullptr) {}
explicit Value(bool data) : as_bool(data) {}
explicit Value(int64_t data) : as_int64(data) {}
explicit Value(std::string data) : as_string(data) {}
explicit Value(std::vector<std::string> data) : as_string_array(data) {}
~Value() {}
} value;
~Data() {
// Call the destructors of members who are not basic types.
if (tag == proto::Tag::TAG_STRING) {
this->value.as_string.~basic_string();
}
if (tag == proto::Tag::TAG_STRING_ARRAY) {
this->value.as_string_array.~vector();
}
}
explicit Data(std::nullptr_t = nullptr)
: tag(proto::Tag::TAG_NULL), value() {}
explicit Data(const bool data) : tag(proto::Tag::TAG_BOOL), value(data) {}
explicit Data(const int64_t data)
: tag(proto::Tag::TAG_INT64), value(data) {}
explicit Data(const std::string& data)
: tag(proto::Tag::TAG_STRING), value(data) {}
explicit Data(const std::vector<std::string>& data)
: tag(proto::Tag::TAG_STRING_ARRAY), value(data) {}
explicit operator std::unique_ptr<proto::Data>() const {
auto result = std::make_unique<proto::Data>();
result->set_tag(this->tag);
switch (this->tag) {
case proto::Tag::TAG_NULL:
break;
case proto::Tag::TAG_BOOL:
result->set_as_bool(this->value.as_bool);
break;
case proto::Tag::TAG_INT64:
result->set_as_int64(this->value.as_int64);
break;
case proto::Tag::TAG_STRING:
result->set_as_string(this->value.as_string);
break;
case proto::Tag::TAG_STRING_ARRAY:
for (const auto& value : this->value.as_string_array) {
result->add_as_string_array(value);
}
break;
default:
LOG(QFATAL) << "Invalid tag: " << this->tag;
break;
}
return result;
}
};
// LINT.ThenChange(
// //depot/google3/third_party/nearby/sharing/proto/settings_observer_data.proto:TaggedUnion
// )
virtual ~Observer() = default;
virtual void OnSettingChanged(absl::string_view key, const Data& data) {}
// Called when the fast initiation hardware offloading support state
// changes.
virtual void OnIsFastInitiationHardwareSupportedChanged(
bool is_supported) = 0;
};
NearbyShareSettings(
Context* context,
nearby::Clock* clock,
nearby::DeviceInfo& device_info,
nearby::sharing::api::PreferenceManager& preference_manager,
NearbyShareLocalDeviceDataManager* local_device_data_manager,
nearby::analytics::EventLogger* event_logger = nullptr);
~NearbyShareSettings() override;
// Internal synchronous getters for C++ clients
bool GetEnabled() const;
proto::FastInitiationNotificationState GetFastInitiationNotificationState()
const;
bool is_fast_initiation_hardware_supported();
void SetIsFastInitiationHardwareSupported(bool is_supported);
std::string GetDeviceName() const;
proto::DataUsage GetDataUsage() const;
proto::DeviceVisibility GetVisibility() const;
// Gets the timestamp of last visibility change. Need the timestamp to decide
// whether need to send optional signature data during key pairing.
absl::Time GetLastVisibilityTimestamp() const;
proto::DeviceVisibility GetLastVisibility() const;
proto::DeviceVisibility GetFallbackVisibility() const;
bool GetIsTemporarilyVisible() const;
void SetIsTemporarilyVisible(bool is_temporarily_visible) const;
std::vector<std::string> GetAllowedContacts() const;
bool IsOnboardingComplete() const;
std::string GetCustomSavePath() const;
// Returns true if the feature is disabled by policy.
bool IsDisabledByPolicy() const;
// Asynchronous APIs exposed by NearbyShareSettings
void AddSettingsObserver(Observer* observer);
void RemoveSettingsObserver(Observer* observer);
void GetEnabled(std::function<void(bool)> callback);
void GetFastInitiationNotificationState(
std::function<void(proto::FastInitiationNotificationState)> callback);
void GetIsFastInitiationHardwareSupported(std::function<void(bool)> callback);
void SetEnabled(bool enabled);
void SetFastInitiationNotificationState(
proto::FastInitiationNotificationState state);
void IsOnboardingComplete(std::function<void(bool)> callback);
void SetIsOnboardingComplete(bool completed, std::function<void()> callback);
void GetDeviceName(std::function<void(absl::string_view)> callback);
void ValidateDeviceName(
absl::string_view device_name,
std::function<void(DeviceNameValidationResult)> callback);
void SetDeviceName(absl::string_view device_name,
std::function<void(DeviceNameValidationResult)> callback);
void GetDataUsage(std::function<void(proto::DataUsage)> callback);
void SetDataUsage(proto::DataUsage data_usage);
void GetVisibility(std::function<void(proto::DeviceVisibility)> callback);
void SetVisibility(proto::DeviceVisibility visibility,
absl::Duration expiration = absl::ZeroDuration()) const;
void SetFallbackVisibility(proto::DeviceVisibility visibility) const;
bool GetIsReceiving();
void SetIsReceiving(bool is_receiving) const;
bool GetIsAnalyticsEnabled();
void SetIsAnalyticsEnabled(bool is_analytics_enabled) const;
bool GetIsAllContactsEnabled();
void SetIsAllContactsEnabled(bool is_all_contacts_enabled) const;
void GetAllowedContacts(
std::function<void(absl::Span<const std::string>)> callback);
void SetAllowedContacts(absl::Span<const std::string> allowed_contacts);
void GetCustomSavePathAsync(
const std::function<void(absl::string_view)>& callback) const;
void SetCustomSavePathAsync(absl::string_view save_path,
const std::function<void()>& callback);
bool GetAutoAppStartEnabled() const;
void SetAutoAppStartEnabled(bool is_auto_app_start) const;
// NearbyShareLocalDeviceDataManager::Observer:
void OnLocalDeviceDataChanged(bool did_device_name_change,
bool did_full_name_change,
bool did_icon_url_change) override;
void SendDesktopNotification(
::location::nearby::proto::sharing::DesktopNotification event) const;
void SendDesktopTransferEvent(
::location::nearby::proto::sharing::DesktopTransferEventType event) const;
std::string Dump() const;
private:
void OnEnabledPrefChanged();
void OnFastInitiationNotificationStatePrefChanged();
void OnDataUsagePrefChanged();
void OnVisibilityPrefChanged();
void OnIsReceivingPrefChanged();
void OnAllowedContactsPrefChanged();
void OnIsOnboardingCompletePrefChanged();
void OnCustomSavePathChanged();
void OnPreferenceChanged(absl::string_view key);
void NotifyAllObservers(absl::string_view key, Observer::Data value)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// If the Nearby Share parent feature is toggled on then Fast Initiation
// notifications should be re-enabled unless the user explicitly disabled the
// notification sub-feature.
void ProcessFastInitiationNotificationParentPrefChanged(bool enabled)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void StartVisibilityTimer(absl::Duration expiration) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Restore/Save fallback visibility
void RestoreFallbackVisibility();
// Make sure thread safe to access Nearby settings
mutable RecursiveMutex mutex_;
nearby::Clock* const clock_;
nearby::DeviceInfo& device_info_;
nearby::sharing::api::PreferenceManager& preference_manager_;
NearbyShareLocalDeviceDataManager* const local_device_data_manager_;
// Used to create analytics events.
std::unique_ptr<analytics::AnalyticsRecorder> analytics_recorder_;
std::shared_ptr<bool> is_desctructing_ = nullptr;
bool is_fast_initiation_hardware_supported_ ABSL_GUARDED_BY(mutex_) = false;
ObserverList<Observer> observers_set_ ABSL_GUARDED_BY(mutex_);
std::unique_ptr<Timer> visibility_expiration_timer_ ABSL_GUARDED_BY(mutex_);
mutable std::optional<proto::DeviceVisibility> fallback_visibility_
ABSL_GUARDED_BY(mutex_);
// Used to track the timestamp of visibility change.
mutable absl::Time last_visibility_timestamp_ ABSL_GUARDED_BY(mutex_) =
absl::InfinitePast();
mutable proto::DeviceVisibility last_visibility_ ABSL_GUARDED_BY(mutex_) =
proto::DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SETTINGS_H_
+576
View File
@@ -0,0 +1,576 @@
// Copyright 2022-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 "sharing/nearby_sharing_settings.h"
#include <algorithm>
#include <filesystem> // NOLINT(build/c++17)
#include <memory>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "internal/test/fake_device_info.h"
#include "internal/test/fake_task_runner.h"
#include "sharing/common/compatible_u8_string.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/common/nearby_share_prefs.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/internal/test/fake_preference_manager.h"
#include "sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
#include "sharing/proto/enums.pb.h"
namespace nearby {
namespace sharing {
namespace {
using ::location::nearby::proto::sharing::DesktopNotification;
using ::location::nearby::proto::sharing::DesktopTransferEventType;
using ::nearby::sharing::proto::DataUsage;
using ::nearby::sharing::proto::DeviceVisibility;
using ::nearby::sharing::proto::FastInitiationNotificationState;
constexpr char kDefaultDeviceName[] = "Josh's Chromebook";
class FakeNearbyShareSettingsObserver : public NearbyShareSettings::Observer {
public:
void OnSettingChanged(absl::string_view key, const Data& data) override {
absl::MutexLock lock(&mutex_);
if (key == prefs::kNearbySharingEnabledName) {
enabled_ = data.value.as_bool;
} else if (key ==
prefs::kNearbySharingFastInitiationNotificationStateName) {
fast_initiation_notification_state_ =
static_cast<FastInitiationNotificationState>(data.value.as_int64);
} else if (key == prefs::kNearbySharingDataUsageName) {
data_usage_ = static_cast<DataUsage>(data.value.as_int64);
} else if (key == prefs::kNearbySharingCustomSavePath) {
custom_save_path_ = data.value.as_string;
} else if (key == prefs::kNearbySharingBackgroundVisibilityName) {
visibility_ = static_cast<DeviceVisibility>(data.value.as_int64);
} else if (key == prefs::kNearbySharingOnboardingCompleteName) {
is_onboarding_complete_ = data.value.as_bool;
} else if (key == prefs::kNearbySharingIsReceivingName) {
is_receiving_ = data.value.as_bool;
} else if (key == prefs::kNearbySharingAllowedContactsName) {
allowed_contacts_.clear();
for (auto& allowed_contact : data.value.as_string_array) {
allowed_contacts_.push_back(allowed_contact);
}
} else if (key == prefs::kNearbySharingDeviceNameName) {
device_name_ = data.value.as_string;
}
}
void OnIsFastInitiationHardwareSupportedChanged(bool is_supported) override {
absl::MutexLock lock(&mutex_);
is_fast_initiation_notification_hardware_supported_ = is_supported;
}
bool enabled() const {
absl::MutexLock lock(&mutex_);
return enabled_;
}
void set_enabled(bool enabled) {
absl::MutexLock lock(&mutex_);
enabled_ = enabled;
}
FastInitiationNotificationState fast_initiation_notification_state() const {
absl::MutexLock lock(&mutex_);
return fast_initiation_notification_state_;
}
bool is_fast_initiation_notification_hardware_supported() const {
absl::MutexLock lock(&mutex_);
return is_fast_initiation_notification_hardware_supported_;
}
bool is_onboarding_complete() const {
absl::MutexLock lock(&mutex_);
return is_onboarding_complete_;
}
const std::string& device_name() const {
absl::MutexLock lock(&mutex_);
return device_name_;
}
const std::string& custom_save_path() const {
absl::MutexLock lock(&mutex_);
return custom_save_path_;
}
DataUsage data_usage() const {
absl::MutexLock lock(&mutex_);
return data_usage_;
}
DeviceVisibility visibility() const {
absl::MutexLock lock(&mutex_);
return visibility_;
}
const std::vector<std::string>& allowed_contacts() {
absl::MutexLock lock(&mutex_);
return allowed_contacts_;
}
private:
mutable absl::Mutex mutex_;
bool enabled_ ABSL_GUARDED_BY(mutex_) = false;
FastInitiationNotificationState fast_initiation_notification_state_
ABSL_GUARDED_BY(mutex_) =
FastInitiationNotificationState::ENABLED_FAST_INIT;
bool is_fast_initiation_notification_hardware_supported_
ABSL_GUARDED_BY(mutex_) = false;
bool is_onboarding_complete_ ABSL_GUARDED_BY(mutex_) = false;
bool is_receiving_ ABSL_GUARDED_BY(mutex_) = false;
std::string device_name_ ABSL_GUARDED_BY(mutex_) = "uncalled";
std::string custom_save_path_ ABSL_GUARDED_BY(mutex_);
DataUsage data_usage_ ABSL_GUARDED_BY(mutex_) = DataUsage::UNKNOWN_DATA_USAGE;
DeviceVisibility visibility_ ABSL_GUARDED_BY(mutex_) =
DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED;
std::vector<std::string> allowed_contacts_ ABSL_GUARDED_BY(mutex_);
};
class NearbyShareSettingsTest : public ::testing::Test {
public:
NearbyShareSettingsTest()
: local_device_data_manager_(kDefaultDeviceName) {
prefs::RegisterNearbySharingPrefs(preference_manager_);
nearby_share_settings_ = std::make_unique<NearbyShareSettings>(
&context_, context_.GetClock(), fake_device_info_, preference_manager_,
&local_device_data_manager_);
nearby_share_settings_->AddSettingsObserver(&observer_);
}
~NearbyShareSettingsTest() override = default;
void TearDown() override { Flush(); }
NearbyShareSettings* settings() { return nearby_share_settings_.get(); }
void SetIsOnboardingComplete(bool is_complete) {
preference_manager_.SetBoolean(
prefs::kNearbySharingOnboardingCompleteName, is_complete);
}
void SetVisibilityExpirationPreference(int expiration) {
preference_manager_.SetInteger(
prefs::kNearbySharingBackgroundVisibilityExpirationSeconds, expiration);
}
void SetCustomSavePath(absl::string_view path) {
preference_manager_.SetString(
prefs::kNearbySharingCustomSavePath, path);
}
// Waits for running tasks to complete.
void Flush() {
absl::SleepFor(absl::Seconds(1));
FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Milliseconds(200));
}
void FastForward(absl::Duration duration) {
context_.fake_clock()->FastForward(duration);
}
bool Contains(std::vector<std::string> v, std::string val) {
if (std::find(v.begin(), v.end(), val) != v.end()) {
return true;
}
return false;
}
protected:
nearby::FakeDeviceInfo fake_device_info_;
nearby::FakePreferenceManager preference_manager_;
FakeContext context_;
FakeNearbyShareLocalDeviceDataManager local_device_data_manager_;
FakeNearbyShareSettingsObserver observer_;
std::unique_ptr<NearbyShareSettings> nearby_share_settings_;
};
TEST_F(NearbyShareSettingsTest, GetAndSetEnabled) {
EXPECT_EQ(observer_.enabled(), false);
settings()->SetIsOnboardingComplete(true, []() {});
settings()->SetEnabled(true);
EXPECT_EQ(settings()->GetEnabled(), true);
Flush();
EXPECT_EQ(observer_.enabled(), true);
bool enabled = false;
settings()->GetEnabled([&enabled](bool result) { enabled = result; });
EXPECT_EQ(enabled, true);
settings()->SetEnabled(false);
EXPECT_EQ(settings()->GetEnabled(), false);
Flush();
EXPECT_EQ(observer_.enabled(), false);
settings()->GetEnabled([&enabled](bool result) { enabled = result; });
EXPECT_EQ(enabled, false);
// Verify that setting the value to false again value doesn't trigger an
// observer event.
observer_.set_enabled(true);
settings()->SetEnabled(false);
EXPECT_EQ(settings()->GetEnabled(), false);
Flush();
// the observers' value should not have been updated.
EXPECT_EQ(observer_.enabled(), true);
}
TEST_F(NearbyShareSettingsTest, GetAndSetFastInitiationNotificationState) {
// Fast init notifications are enabled by default.
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::ENABLED_FAST_INIT);
settings()->SetFastInitiationNotificationState(
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
EXPECT_EQ(FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT,
settings()->GetFastInitiationNotificationState());
Flush();
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
FastInitiationNotificationState state =
FastInitiationNotificationState::ENABLED_FAST_INIT;
settings()->GetFastInitiationNotificationState(
[&state](FastInitiationNotificationState result) { state = result; });
EXPECT_EQ(state, FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
}
TEST_F(NearbyShareSettingsTest,
ParentFeatureChangesFastInitiationNotificationState) {
// Fast init notifications are enabled by default.
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::ENABLED_FAST_INIT);
settings()->SetIsOnboardingComplete(true, []() {});
settings()->SetEnabled(true);
Flush();
// Simulate toggling the parent feature off.
settings()->SetEnabled(false);
Flush();
EXPECT_FALSE(settings()->GetEnabled());
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::DISABLED_BY_FEATURE_FAST_INIT);
// Simulate toggling the parent feature on.
settings()->SetEnabled(true);
Flush();
EXPECT_TRUE(settings()->GetEnabled());
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::ENABLED_FAST_INIT);
}
TEST_F(NearbyShareSettingsTest,
ParentFeatureChangesFastInitiationNotificationDisabledByUser) {
// Fast init notifications are enabled by default.
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::ENABLED_FAST_INIT);
// Set explicitly disabled by user.
settings()->SetFastInitiationNotificationState(
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
Flush();
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
// Simulate toggling parent feature on.
settings()->SetIsOnboardingComplete(true, []() {});
settings()->SetEnabled(true);
Flush();
// The disabled by user flag should persist if the parent feature is enabled.
EXPECT_EQ(observer_.fast_initiation_notification_state(),
FastInitiationNotificationState::DISABLED_BY_USER_FAST_INIT);
}
TEST_F(NearbyShareSettingsTest, GetAndSetCustomSavePath) {
absl::Notification notification;
settings()->SetCustomSavePathAsync(
GetCompatibleU8String(std::filesystem::temp_directory_path().u8string()),
[&]() { notification.Notify(); });
Flush();
EXPECT_TRUE(notification.HasBeenNotified());
settings()->GetCustomSavePathAsync([&](absl::string_view path) {
observer_.OnSettingChanged(
prefs::kNearbySharingCustomSavePath,
NearbyShareSettings::Observer::Data(std::string(path)));
});
Flush();
EXPECT_EQ(
observer_.custom_save_path(),
GetCompatibleU8String(std::filesystem::temp_directory_path().u8string()));
}
TEST_F(NearbyShareSettingsTest, GetAndSetIsOnboardingComplete) {
EXPECT_FALSE(observer_.is_onboarding_complete());
SetIsOnboardingComplete(true);
EXPECT_TRUE(settings()->IsOnboardingComplete());
Flush();
EXPECT_TRUE(observer_.is_onboarding_complete());
bool is_complete = false;
settings()->IsOnboardingComplete(
[&is_complete](bool result) { is_complete = result; });
EXPECT_TRUE(is_complete);
}
TEST_F(NearbyShareSettingsTest, GetAndSetIsFastInitiationHardwareSupported) {
EXPECT_FALSE(observer_.is_fast_initiation_notification_hardware_supported());
settings()->SetIsFastInitiationHardwareSupported(true);
Flush();
EXPECT_TRUE(observer_.is_fast_initiation_notification_hardware_supported());
bool is_supported = false;
settings()->GetIsFastInitiationHardwareSupported(
[&is_supported](bool result) { is_supported = result; });
EXPECT_TRUE(is_supported);
}
TEST_F(NearbyShareSettingsTest, ValidateDeviceName) {
auto result = DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
DeviceNameValidationResult::kErrorEmpty);
settings()->ValidateDeviceName(
"", [&result](DeviceNameValidationResult res) { result = res; });
EXPECT_EQ(result, DeviceNameValidationResult::kErrorEmpty);
local_device_data_manager_.set_next_validation_result(
DeviceNameValidationResult::kValid);
settings()->ValidateDeviceName(
"this string is 32 bytes in UTF-8",
[&result](DeviceNameValidationResult res) { result = res; });
EXPECT_EQ(result, DeviceNameValidationResult::kValid);
}
TEST_F(NearbyShareSettingsTest, GetAndSetDeviceName) {
std::string name = "not_the_default";
settings()->GetDeviceName(
[&name](absl::string_view result) { name = std::string(result); });
EXPECT_EQ(kDefaultDeviceName, name);
// When we get a validation error, setting the name should not succeed.
EXPECT_EQ(observer_.device_name(), "uncalled");
auto result = DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
DeviceNameValidationResult::kErrorEmpty);
settings()->SetDeviceName(
"", [&result](DeviceNameValidationResult res) { result = res; });
EXPECT_EQ(result, DeviceNameValidationResult::kErrorEmpty);
EXPECT_EQ(settings()->GetDeviceName(), kDefaultDeviceName);
// When the name is valid, the setting should succeed.
EXPECT_EQ(observer_.device_name(), "uncalled");
result = DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
DeviceNameValidationResult::kValid);
settings()->SetDeviceName(
"d", [&result](DeviceNameValidationResult res) { result = res; });
EXPECT_EQ(result, DeviceNameValidationResult::kValid);
EXPECT_EQ(settings()->GetDeviceName(), "d");
Flush();
EXPECT_EQ(observer_.device_name(), "d");
settings()->GetDeviceName(
[&name](absl::string_view result) { name = std::string(result); });
EXPECT_EQ(name, "d");
}
TEST_F(NearbyShareSettingsTest, GetAndSetDataUsage) {
EXPECT_EQ(observer_.data_usage(), DataUsage::UNKNOWN_DATA_USAGE);
settings()->SetDataUsage(DataUsage::OFFLINE_DATA_USAGE);
EXPECT_EQ(settings()->GetDataUsage(), DataUsage::OFFLINE_DATA_USAGE);
Flush();
EXPECT_EQ(observer_.data_usage(), DataUsage::OFFLINE_DATA_USAGE);
DataUsage data_usage = DataUsage::UNKNOWN_DATA_USAGE;
settings()->GetDataUsage(
[&data_usage](DataUsage usage) { data_usage = usage; });
EXPECT_EQ(data_usage, DataUsage::OFFLINE_DATA_USAGE);
}
TEST_F(NearbyShareSettingsTest, GetAndSetVisibility) {
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED);
settings()->SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
EXPECT_EQ(settings()->GetVisibility(),
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
Flush();
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
DeviceVisibility visibility = DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED;
settings()->GetVisibility(
[&visibility](DeviceVisibility result) { visibility = result; });
EXPECT_EQ(visibility, DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
}
TEST_F(NearbyShareSettingsTest, GetAndSetVisibilityWithSelectedContacts) {
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED);
settings()->SetVisibility(
DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS);
EXPECT_EQ(settings()->GetVisibility(),
DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE);
Flush();
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE);
}
TEST_F(NearbyShareSettingsTest, GetFallbackVisibility) {
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED);
DeviceVisibility visibility = settings()->GetFallbackVisibility();
EXPECT_EQ(visibility, DeviceVisibility::DEVICE_VISIBILITY_HIDDEN);
settings()->SetFallbackVisibility(
DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
settings()->SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
EXPECT_EQ(settings()->GetVisibility(),
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
EXPECT_EQ(settings()->GetFallbackVisibility(),
DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
settings()->SetFallbackVisibility(
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
EXPECT_EQ(settings()->GetFallbackVisibility(),
DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
Flush();
EXPECT_EQ(observer_.visibility(),
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE);
settings()->SetVisibility(settings()->GetVisibility(), absl::Seconds(1));
Flush();
FastForward(absl::Seconds(1));
Flush();
visibility = DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED;
settings()->GetVisibility(
[&visibility](DeviceVisibility result) { visibility = result; });
EXPECT_EQ(visibility, DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
Flush();
}
TEST_F(NearbyShareSettingsTest, GetAndSetAllowedContacts) {
const std::string id1("1");
std::vector<std::string> allowed_contacts;
settings()->GetAllowedContacts(
[&allowed_contacts](absl::Span<const std::string> result) {
allowed_contacts.clear();
for (auto& contact : result) {
allowed_contacts.push_back(contact);
}
});
EXPECT_EQ(allowed_contacts.size(), 0u);
settings()->SetAllowedContacts({id1});
Flush();
EXPECT_EQ(observer_.allowed_contacts().size(), 1u);
EXPECT_TRUE(Contains(observer_.allowed_contacts(), id1));
settings()->GetAllowedContacts(
[&allowed_contacts](absl::Span<const std::string> result) {
allowed_contacts.clear();
for (auto& contact : result) {
allowed_contacts.push_back(contact);
}
});
EXPECT_EQ(allowed_contacts.size(), 1u);
EXPECT_TRUE(Contains(observer_.allowed_contacts(), id1));
settings()->SetAllowedContacts({});
Flush();
EXPECT_EQ(observer_.allowed_contacts().size(), 0u);
settings()->GetAllowedContacts(
[&allowed_contacts](absl::Span<const std::string> result) {
allowed_contacts.clear();
for (auto& contact : result) {
allowed_contacts.push_back(contact);
}
});
EXPECT_EQ(allowed_contacts.size(), 0u);
}
TEST_F(NearbyShareSettingsTest, GetAndSetAutoAppStartEnabled) {
bool is_auto_app_start_enabled = settings()->GetAutoAppStartEnabled();
EXPECT_TRUE(is_auto_app_start_enabled);
settings()->SetAutoAppStartEnabled(false);
Flush();
is_auto_app_start_enabled = settings()->GetAutoAppStartEnabled();
EXPECT_FALSE(is_auto_app_start_enabled);
}
TEST_F(NearbyShareSettingsTest, SendDesktopNotification) {
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_UNKNOWN);
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_CONNECTING);
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_PROGRESS);
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_ACCEPT);
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_RECEIVED);
settings()->SendDesktopNotification(
DesktopNotification::DESKTOP_NOTIFICATION_ERROR);
}
TEST_F(NearbyShareSettingsTest, ReceiveDesktopTransferEvent) {
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ACCEPT);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_PROGRESS);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_RECEIVED);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ERROR);
}
TEST_F(NearbyShareSettingsTest, SendDesktopTransferEvent) {
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_SEND_TYPE_START);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::
DESKTOP_TRANSFER_EVENT_SEND_TYPE_SELECT_A_DEVICE);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_SEND_TYPE_PROGRESS);
settings()->SendDesktopTransferEvent(
DesktopTransferEventType::DESKTOP_TRANSFER_EVENT_SEND_TYPE_SENT);
}
} // namespace
} // namespace sharing
} // namespace nearby
+288
View File
@@ -0,0 +1,288 @@
// 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 "sharing/nearby_sharing_util.h"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <filesystem> // NOLINT(build/c++17)
#include <optional>
#include <string>
#include <vector>
#include "absl/hash/hash.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/device_info.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/advertisement.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/flags/nearby_sharing_feature_flags.h"
#include "sharing/internal/base/encode.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/transfer_metadata.h"
namespace nearby {
namespace sharing {
namespace {
using ::location::nearby::proto::sharing::AttachmentTransmissionStatus;
using ::location::nearby::proto::sharing::ConnectionLayerStatus;
// Used to hash a token into a 4 digit string.
constexpr int kHashModulo = 9973;
constexpr int kHashBaseMultiplier = 31;
} // namespace
bool IsBackgroundScanningFeatureEnabled() {
return NearbyFlags::GetInstance().GetBoolFlag(
sharing::config_package_nearby::nearby_sharing_feature::
kEnableBackgroundScanning);
}
std::string ReceiveSurfaceStateToString(
NearbySharingService::ReceiveSurfaceState state) {
switch (state) {
case NearbySharingService::ReceiveSurfaceState::kForeground:
return "FOREGROUND";
case NearbySharingService::ReceiveSurfaceState::kBackground:
return "BACKGROUND";
case NearbySharingService::ReceiveSurfaceState::kUnknown:
return "UNKNOWN";
}
}
std::string SendSurfaceStateToString(
NearbySharingService::SendSurfaceState state) {
switch (state) {
case NearbySharingService::SendSurfaceState::kForeground:
return "FOREGROUND";
case NearbySharingService::SendSurfaceState::kBackground:
return "BACKGROUND";
case NearbySharingService::SendSurfaceState::kUnknown:
return "UNKNOWN";
}
}
std::string PowerLevelToString(PowerLevel level) {
switch (level) {
case PowerLevel::kLowPower:
return "LOW_POWER";
case PowerLevel::kMediumPower:
return "MEDIUM_POWER";
case PowerLevel::kHighPower:
return "HIGH_POWER";
case PowerLevel::kUnknown:
return "UNKNOWN";
}
}
std::optional<std::vector<uint8_t>> GetBluetoothMacAddressFromCertificate(
const NearbyShareDecryptedPublicCertificate& certificate) {
if (!certificate.unencrypted_metadata().has_bluetooth_mac_address()) {
NL_LOG(WARNING) << __func__ << ": Public certificate "
<< nearby::utils::HexEncode(certificate.id())
<< " did not contain a Bluetooth mac address.";
return std::nullopt;
}
std::string mac_address =
certificate.unencrypted_metadata().bluetooth_mac_address();
if (mac_address.size() != 6) {
NL_LOG(ERROR) << __func__ << ": Invalid bluetooth mac address: '"
<< mac_address << "'";
return std::nullopt;
}
return std::vector<uint8_t>(mac_address.begin(), mac_address.end());
}
std::optional<std::string> GetDeviceName(
const Advertisement* advertisement,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate) {
NL_DCHECK(advertisement);
// Device name is always included when visible to everyone.
if (advertisement->device_name().has_value()) {
return advertisement->device_name();
}
// For contacts only advertisements, we can't do anything without the
// certificate.
if (!certificate.has_value() ||
!certificate->unencrypted_metadata().has_device_name()) {
return std::nullopt;
}
return certificate->unencrypted_metadata().device_name();
}
// Return the most stable device identifier with the following priority:
// 1. Hash of Bluetooth MAC address.
// 2. Certificate ID.
// 3. Endpoint ID.
std::string GetDeviceId(
absl::string_view endpoint_id,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate) {
if (!certificate.has_value()) {
return std::string(endpoint_id);
}
std::optional<std::vector<uint8_t>> mac_address =
GetBluetoothMacAddressFromCertificate(*certificate);
if (mac_address.has_value()) {
return absl::StrCat(absl::Hash<std::vector<uint8_t>>{}(*mac_address));
}
if (!certificate->id().empty()) {
return std::string(certificate->id().begin(), certificate->id().end());
}
return std::string(endpoint_id);
}
std::optional<std::string> TokenToFourDigitString(
const std::optional<std::vector<uint8_t>>& bytes) {
if (!bytes.has_value()) {
return std::nullopt;
}
int hash = 0;
int multiplier = 1;
for (uint8_t byte : *bytes) {
// Java bytes are signed two's complement so cast to use the correct sign.
hash = (hash + static_cast<int8_t>(byte) * multiplier) % kHashModulo;
multiplier = (multiplier * kHashBaseMultiplier) % kHashModulo;
}
return absl::StrFormat("%04d", std::abs(hash));
}
bool IsOutOfStorage(DeviceInfo& device_info, std::filesystem::path file_path,
int64_t storage_required) {
std::optional<size_t> available_storage =
device_info.GetAvailableDiskSpaceInBytes(file_path);
if (!available_storage.has_value()) {
return false;
}
return *available_storage <= storage_required;
}
AttachmentTransmissionStatus ConvertToTransmissionStatus(
TransferMetadata::Status status) {
switch (status) {
case TransferMetadata::Status::kComplete:
return AttachmentTransmissionStatus::
COMPLETE_ATTACHMENT_TRANSMISSION_STATUS;
case TransferMetadata::Status::kCancelled:
return AttachmentTransmissionStatus::
CANCELED_ATTACHMENT_TRANSMISSION_STATUS;
case TransferMetadata::Status::kFailed:
return AttachmentTransmissionStatus::
FAILED_ATTACHMENT_TRANSMISSION_STATUS;
case TransferMetadata::Status::kAwaitingRemoteAcceptanceFailed:
return AttachmentTransmissionStatus::
AWAITING_REMOTE_ACCEPTANCE_FAILED_ATTACHMENT;
case TransferMetadata::Status::kFailedToInitiateOutgoingConnection:
return AttachmentTransmissionStatus::FAILED_NULL_CONNECTION_INIT_OUTGOING;
case TransferMetadata::Status::kFailedToReadOutgoingConnectionResponse:
return AttachmentTransmissionStatus::FAILED_UNKNOWN_REMOTE_RESPONSE;
case TransferMetadata::Status::kIncompletePayloads:
return AttachmentTransmissionStatus::FAILED_NO_PAYLOAD;
case TransferMetadata::Status::kInvalidIntroductionFrame:
return AttachmentTransmissionStatus::FAILED_WRITE_INTRODUCTION;
case TransferMetadata::Status::kMediaUnavailable:
return AttachmentTransmissionStatus::MEDIA_UNAVAILABLE_ATTACHMENT;
case TransferMetadata::Status::kMissingEndpointId:
return AttachmentTransmissionStatus::FAILED_NO_SHARE_TARGET_ENDPOINT;
case TransferMetadata::Status::kMissingPayloads:
return AttachmentTransmissionStatus::FAILED_NO_PAYLOAD;
case TransferMetadata::Status::kMissingTransferUpdateCallback:
return AttachmentTransmissionStatus::FAILED_NO_TRANSFER_UPDATE_CALLBACK;
case TransferMetadata::Status::kPairedKeyVerificationFailed:
return AttachmentTransmissionStatus::FAILED_PAIRED_KEYHANDSHAKE;
case TransferMetadata::Status::kRejected:
return AttachmentTransmissionStatus::REJECTED_ATTACHMENT;
case TransferMetadata::Status::kTimedOut:
return AttachmentTransmissionStatus::TIMED_OUT_ATTACHMENT;
case TransferMetadata::Status::kUnexpectedDisconnection:
return AttachmentTransmissionStatus::FAILED_NULL_CONNECTION_DISCONNECTED;
case TransferMetadata::Status::kUnsupportedAttachmentType:
return AttachmentTransmissionStatus::
UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT;
default:
return AttachmentTransmissionStatus::
UNKNOWN_ATTACHMENT_TRANSMISSION_STATUS;
}
}
ConnectionLayerStatus ConvertToConnectionLayerStatus(Status status) {
switch (status) {
case Status::kUnknown:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_UNKNOWN;
case Status::kSuccess:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_SUCCESS;
case Status::kError:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_ERROR;
case Status::kOutOfOrderApiCall:
return ConnectionLayerStatus::
CONNECTION_LAYER_STATUS_OUT_OF_ORDER_API_CALL;
case Status::kAlreadyHaveActiveStrategy:
return ConnectionLayerStatus::
CONNECTION_LAYER_STATUS_ALREADY_HAVE_ACTIVE_STRATEGY;
case Status::kAlreadyAdvertising:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_ALREADY_ADVERTISING;
case Status::kAlreadyDiscovering:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_ALREADY_DISCOVERING;
case Status::kAlreadyListening:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_ALREADY_LISTENING;
case Status::kEndpointIOError:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_END_POINT_IO_ERROR;
case Status::kEndpointUnknown:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_END_POINT_UNKNOWN;
case Status::kConnectionRejected:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_CONNECTION_REJECTED;
case Status::kAlreadyConnectedToEndpoint:
return ConnectionLayerStatus::
CONNECTION_LAYER_STATUS_ALREADY_CONNECTED_TO_END_POINT;
case Status::kNotConnectedToEndpoint:
return ConnectionLayerStatus::
CONNECTION_LAYER_STATUS_NOT_CONNECTED_TO_END_POINT;
case Status::kBluetoothError:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_BLUETOOTH_ERROR;
case Status::kBleError:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_BLE_ERROR;
case Status::kWifiLanError:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_WIFI_LAN_ERROR;
case Status::kPayloadUnknown:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_PAYLOAD_UNKNOWN;
case Status::kReset:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_RESET;
case Status::kTimeout:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_TIMEOUT;
default:
return ConnectionLayerStatus::CONNECTION_LAYER_STATUS_UNKNOWN;
}
}
} // namespace sharing
} // namespace nearby
+86
View File
@@ -0,0 +1,86 @@
// 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_SHARING_NEARBY_SHARING_UTIL_H_
#define THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_UTIL_H_
#include <cstdint>
#include <filesystem> // NOLINT(build/c++17)
#include <optional>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/platform/device_info.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/advertisement.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/common/nearby_share_enums.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/nearby_sharing_service.h"
#include "sharing/transfer_metadata.h"
namespace nearby {
namespace sharing {
// Checks whether the background scanning feature is enabled or not.
bool IsBackgroundScanningFeatureEnabled();
// Checks whether having enough disk space for required storage.
//
// device_info - Nearby Share DeviceInfo
// file_path - The path is to store sharing contents.
// storage_required - required storage space.
bool IsOutOfStorage(nearby::DeviceInfo& device_info,
std::filesystem::path file_path, int64_t storage_required);
// Decodes certificate to find MAC address encoded in it.
std::optional<std::vector<uint8_t>> GetBluetoothMacAddressFromCertificate(
const NearbyShareDecryptedPublicCertificate& certificate);
// Returns device name based on arguments advertisement and certificate.
std::optional<std::string> GetDeviceName(
const Advertisement* advertisement,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate);
// Converts authentication token to four bytes digit string.
std::optional<std::string> TokenToFourDigitString(
const std::optional<std::vector<uint8_t>>& bytes);
std::string ReceiveSurfaceStateToString(
NearbySharingService::ReceiveSurfaceState state);
std::string SendSurfaceStateToString(
NearbySharingService::SendSurfaceState state);
std::string PowerLevelToString(PowerLevel level);
// Return the most stable device identifier with the following priority:
// 1. Hash of Bluetooth MAC address.
// 2. Certificate ID.
// 3. Endpoint ID.
std::string GetDeviceId(
absl::string_view endpoint_id,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate);
::location::nearby::proto::sharing::AttachmentTransmissionStatus
ConvertToTransmissionStatus(TransferMetadata::Status status);
::location::nearby::proto::sharing::ConnectionLayerStatus
ConvertToConnectionLayerStatus(Status status);
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_UTIL_H_
+68
View File
@@ -0,0 +1,68 @@
// 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 "sharing/outgoing_share_target_info.h"
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
OutgoingShareTargetInfo::OutgoingShareTargetInfo() = default;
OutgoingShareTargetInfo::OutgoingShareTargetInfo(OutgoingShareTargetInfo&&) =
default;
OutgoingShareTargetInfo& OutgoingShareTargetInfo::operator=(
OutgoingShareTargetInfo&&) = default;
OutgoingShareTargetInfo::~OutgoingShareTargetInfo() = default;
std::vector<Payload> OutgoingShareTargetInfo::ExtractTextPayloads() {
return std::move(text_payloads_);
}
std::vector<Payload> OutgoingShareTargetInfo::ExtractFilePayloads() {
return std::move(file_payloads_);
}
std::optional<Payload> OutgoingShareTargetInfo::ExtractNextPayload() {
if (!text_payloads_.empty()) {
Payload payload = text_payloads_.back();
text_payloads_.pop_back();
return payload;
}
if (!file_payloads_.empty()) {
Payload payload = file_payloads_.back();
file_payloads_.pop_back();
return payload;
}
if (!wifi_credentials_payloads_.empty()) {
Payload payload = wifi_credentials_payloads_.back();
wifi_credentials_payloads_.pop_back();
return payload;
}
return std::nullopt;
}
} // namespace sharing
} // namespace nearby
+87
View File
@@ -0,0 +1,87 @@
// 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_SHARING_OUTGOING_SHARE_TARGET_INFO_H_
#define THIRD_PARTY_NEARBY_SHARING_OUTGOING_SHARE_TARGET_INFO_H_
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "sharing/nearby_connections_types.h"
#include "sharing/share_target_info.h"
namespace nearby {
namespace sharing {
// A description of the outgoing connection to a remote device.
class OutgoingShareTargetInfo : public ShareTargetInfo {
public:
OutgoingShareTargetInfo();
OutgoingShareTargetInfo(OutgoingShareTargetInfo&&);
OutgoingShareTargetInfo& operator=(OutgoingShareTargetInfo&&);
~OutgoingShareTargetInfo() override;
const std::optional<std::string>& obfuscated_gaia_id() const {
return obfuscated_gaia_id_;
}
void set_obfuscated_gaia_id(std::string obfuscated_gaia_id) {
obfuscated_gaia_id_ = std::move(obfuscated_gaia_id);
}
const std::vector<Payload>& text_payloads() const { return text_payloads_; }
void set_text_payloads(std::vector<Payload> payloads) {
text_payloads_ = std::move(payloads);
}
const std::vector<Payload>& wifi_credentials_payloads() const {
return wifi_credentials_payloads_;
}
void set_wifi_credentials_payloads(std::vector<Payload> payloads) {
wifi_credentials_payloads_ = std::move(payloads);
}
const std::vector<Payload>& file_payloads() const { return file_payloads_; }
void set_file_payloads(std::vector<Payload> payloads) {
file_payloads_ = std::move(payloads);
}
Status connection_layer_status() const { return connection_layer_status_; }
void set_connection_layer_status(Status status) {
connection_layer_status_ = status;
}
std::vector<Payload> ExtractTextPayloads();
std::vector<Payload> ExtractFilePayloads();
std::vector<Payload> ExtractWifiCredentialsPayloads();
std::optional<Payload> ExtractNextPayload();
private:
std::optional<std::string> obfuscated_gaia_id_;
std::vector<Payload> text_payloads_;
std::vector<Payload> file_payloads_;
std::vector<Payload> wifi_credentials_payloads_;
Status connection_layer_status_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_OUTGOING_SHARE_TARGET_INFO_H_
+503
View File
@@ -0,0 +1,503 @@
// Copyright 2022-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 "sharing/paired_key_verification_runner.h"
#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/clock.h"
#include "internal/platform/device_info.h"
#include "internal/platform/implementation/device_info.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/certificates/common.h"
#include "sharing/certificates/constants.h"
#include "sharing/certificates/nearby_share_certificate_manager.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/incoming_frames_reader.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/proto/rpc_resources.pb.h"
#include "sharing/proto/timestamp.pb.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
namespace nearby {
namespace sharing {
using ::location::nearby::proto::sharing::OSType;
using ::nearby::sharing::proto::DeviceVisibility;
using ::nearby::sharing::service::proto::CertificateInfoFrame;
using ::nearby::sharing::service::proto::Frame;
using ::nearby::sharing::service::proto::PairedKeyEncryptionFrame;
using ::nearby::sharing::service::proto::PairedKeyResultFrame;
using ::nearby::sharing::service::proto::V1Frame;
namespace {
// The size of the random byte array used for the encryption frame's signed data
// if a valid signature cannot be generated. This size is consistent with the
// GmsCore implementation.
const size_t kNearbyShareNumBytesRandomSignature = 72;
constexpr absl::Duration kRelaxAfterSetVisibilityTimeout = absl::Minutes(15);
PairedKeyVerificationRunner::PairedKeyVerificationResult Convert(
nearby::sharing::service::proto::PairedKeyResultFrame::Status status) {
switch (status) {
case PairedKeyResultFrame::UNKNOWN:
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnknown;
case PairedKeyResultFrame::SUCCESS:
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess;
case PairedKeyResultFrame::FAIL:
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail;
case PairedKeyResultFrame::UNABLE:
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable;
}
}
std::vector<uint8_t> PadPrefix(char prefix, std::vector<uint8_t> bytes) {
bytes.insert(bytes.begin(), prefix);
return bytes;
}
OSType ToProtoOsType(::nearby::api::DeviceInfo::OsType os_type) {
switch (os_type) {
case ::nearby::api::DeviceInfo::OsType::kAndroid:
return OSType::ANDROID;
case ::nearby::api::DeviceInfo::OsType::kChromeOs:
return OSType::CHROME_OS;
case ::nearby::api::DeviceInfo::OsType::kWindows:
return OSType::WINDOWS;
case ::nearby::api::DeviceInfo::OsType::kIos:
return OSType::IOS;
case ::nearby::api::DeviceInfo::OsType::kMacOS:
return OSType::MACOS;
case ::nearby::api::DeviceInfo::OsType::kUnknown:
break;
}
return OSType::UNKNOWN_OS_TYPE;
}
} // namespace
std::ostream& operator<<(
std::ostream& out,
const PairedKeyVerificationRunner::PairedKeyVerificationResult& obj) {
out << static_cast<std::underlying_type<
PairedKeyVerificationRunner::PairedKeyVerificationResult>::type>(obj);
return out;
}
PairedKeyVerificationRunner::PairedKeyVerificationRunner(
Clock* clock,
DeviceInfo& device_info,
NearbyShareSettings* nearby_share_settings,
bool self_share_feature_enabled, const ShareTarget& share_target,
absl::string_view endpoint_id, const std::vector<uint8_t>& token,
NearbyConnection* connection,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate,
NearbyShareCertificateManager* certificate_manager,
bool restrict_to_contacts, IncomingFramesReader* frames_reader,
absl::Duration read_frame_timeout)
: clock_(clock),
device_info_(device_info),
nearby_share_settings_(nearby_share_settings),
self_share_feature_enabled_(self_share_feature_enabled),
share_target_(share_target),
endpoint_id_(std::string(endpoint_id)),
raw_token_(token),
connection_(connection),
certificate_(certificate),
certificate_manager_(certificate_manager),
restrict_to_contacts_(restrict_to_contacts),
frames_reader_(frames_reader),
read_frame_timeout_(read_frame_timeout) {
NL_DCHECK(clock_);
NL_DCHECK(nearby_share_settings);
NL_DCHECK(connection);
NL_DCHECK(certificate_manager);
NL_DCHECK(frames_reader);
if (share_target.is_incoming) {
local_prefix_ = kNearbyShareReceiverVerificationPrefix;
remote_prefix_ = kNearbyShareSenderVerificationPrefix;
relax_restrict_to_contacts_ =
RelaxRestrictToContactsIfNeeded() ||
nearby_share_settings_->GetVisibility() ==
DeviceVisibility::DEVICE_VISIBILITY_EVERYONE;
} else {
remote_prefix_ = kNearbyShareReceiverVerificationPrefix;
local_prefix_ = kNearbyShareSenderVerificationPrefix;
}
}
PairedKeyVerificationRunner::~PairedKeyVerificationRunner() = default;
void PairedKeyVerificationRunner::Run(
std::function<void(PairedKeyVerificationResult, OSType)> callback) {
NL_DCHECK(!callback_);
callback_ = std::move(callback);
SendPairedKeyEncryptionFrame();
frames_reader_->ReadFrame(
V1Frame::PAIRED_KEY_ENCRYPTION,
[&, runner = GetWeakPtr()](std::optional<V1Frame> frame) {
auto verification_runner = runner.lock();
if (verification_runner == nullptr) {
NL_LOG(WARNING) << "PairedKeyVerificationRunner is released before.";
return;
}
OnReadPairedKeyEncryptionFrame(std::move(frame));
},
read_frame_timeout_);
}
void PairedKeyVerificationRunner::OnReadPairedKeyEncryptionFrame(
std::optional<V1Frame> frame) {
if (!frame.has_value()) {
NL_LOG(WARNING) << __func__
<< ": Failed to read remote paired key encryption";
std::move(callback_)(PairedKeyVerificationResult::kFail,
OSType::UNKNOWN_OS_TYPE);
return;
}
std::vector<PairedKeyVerificationResult> verification_results;
PairedKeyVerificationResult remote_public_certificate_result =
VerifyRemotePublicCertificate(*frame);
if (remote_public_certificate_result ==
PairedKeyVerificationResult::kSuccess) {
SendCertificateInfo();
} else if (restrict_to_contacts_ && !relax_restrict_to_contacts_) {
NL_VLOG(1) << __func__
<< ": we are only allowing connections with contacts. "
"Rejecting connection from unknown ShareTarget - "
<< share_target_.id;
std::move(callback_)(PairedKeyVerificationResult::kFail,
OSType::UNKNOWN_OS_TYPE);
return;
} else if (relax_restrict_to_contacts_) {
remote_public_certificate_result =
VerifyRemotePublicCertificateRelaxed(*frame);
}
verification_results.push_back(remote_public_certificate_result);
NL_VLOG(1) << __func__
<< ": Remote public certificate verification result "
<< remote_public_certificate_result;
PairedKeyVerificationResult local_result =
VerifyPairedKeyEncryptionFrame(*frame);
verification_results.push_back(local_result);
NL_VLOG(1) << __func__ << ": Paired key encryption verification result "
<< local_result;
SendPairedKeyResultFrame(local_result);
frames_reader_->ReadFrame(
V1Frame::PAIRED_KEY_RESULT,
[&, runner = GetWeakPtr(),
verification_results =
std::move(verification_results)](std::optional<V1Frame> frame) {
auto verification_runner = runner.lock();
if (verification_runner == nullptr) {
NL_LOG(WARNING) << "PairedKeyVerificationRunner is released before.";
return;
}
OnReadPairedKeyResultFrame(verification_results, std::move(frame));
},
read_frame_timeout_);
}
void PairedKeyVerificationRunner::OnReadPairedKeyResultFrame(
std::vector<PairedKeyVerificationResult> verification_results,
std::optional<V1Frame> frame) {
if (!frame.has_value()) {
NL_LOG(WARNING) << __func__ << ": Failed to read remote paired key result";
std::move(callback_)(PairedKeyVerificationResult::kFail,
OSType::UNKNOWN_OS_TYPE);
return;
}
PairedKeyVerificationResult key_result =
Convert(frame->paired_key_result().status());
verification_results.push_back(key_result);
NL_VLOG(1) << __func__ << ": Paired key result frame result "
<< key_result;
PairedKeyVerificationResult combined_result =
MergeResults(verification_results);
NL_VLOG(1) << __func__ << ": Combined verification result "
<< combined_result;
OSType os_type = OSType::UNKNOWN_OS_TYPE;
if (frame->paired_key_result().has_os_type()) {
os_type = frame->paired_key_result().os_type();
}
std::move(callback_)(combined_result, os_type);
}
void PairedKeyVerificationRunner::SendPairedKeyResultFrame(
PairedKeyVerificationResult result) {
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::PAIRED_KEY_RESULT);
PairedKeyResultFrame* result_frame = v1_frame->mutable_paired_key_result();
switch (result) {
case PairedKeyVerificationResult::kUnable:
result_frame->set_status(PairedKeyResultFrame::UNABLE);
break;
case PairedKeyVerificationResult::kSuccess:
result_frame->set_status(PairedKeyResultFrame::SUCCESS);
break;
case PairedKeyVerificationResult::kFail:
result_frame->set_status(PairedKeyResultFrame::FAIL);
break;
case PairedKeyVerificationResult::kUnknown:
result_frame->set_status(PairedKeyResultFrame::UNKNOWN);
break;
}
// Set OS type to allow remote device knowns the paring device OS type.
result_frame->set_os_type(ToProtoOsType(device_info_.GetOsType()));
std::vector<uint8_t> data(frame.ByteSize());
frame.SerializeToArray(data.data(), frame.ByteSize());
connection_->Write(std::move(data));
}
void PairedKeyVerificationRunner::SendCertificateInfo() {
if (self_share_feature_enabled_) return;
std::vector<nearby::sharing::proto::PublicCertificate> certificates;
if (certificates.empty()) return;
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::CERTIFICATE_INFO);
CertificateInfoFrame* cert_frame = v1_frame->mutable_certificate_info();
for (const auto& certificate : certificates) {
nearby::sharing::service::proto::PublicCertificate* cert =
cert_frame->add_public_certificate();
cert->set_secret_id(certificate.secret_id());
cert->set_authenticity_key(certificate.secret_key());
cert->set_public_key(certificate.public_key());
cert->set_start_time(certificate.start_time().seconds() * 1000);
cert->set_end_time(certificate.end_time().seconds() * 1000);
cert->set_encrypted_metadata_bytes(certificate.encrypted_metadata_bytes());
cert->set_metadata_encryption_key_tag(
certificate.metadata_encryption_key_tag());
}
std::vector<uint8_t> data(frame.ByteSize());
frame.SerializeToArray(data.data(), frame.ByteSize());
connection_->Write(std::move(data));
}
void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() {
std::optional<std::vector<uint8_t>> signature =
certificate_manager_->SignWithPrivateCertificate(
nearby_share_settings_->GetVisibility(),
PadPrefix(local_prefix_, raw_token_));
if (!signature.has_value() || signature->empty()) {
signature = GenerateRandomBytes(kNearbyShareNumBytesRandomSignature);
}
std::vector<uint8_t> certificate_id_hash;
if (certificate_.has_value()) {
certificate_id_hash = certificate_->HashAuthenticationToken(raw_token_);
}
if (certificate_id_hash.empty()) {
certificate_id_hash =
GenerateRandomBytes(kNearbyShareNumBytesAuthenticationTokenHash);
}
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::PAIRED_KEY_ENCRYPTION);
PairedKeyEncryptionFrame* encryption_frame =
v1_frame->mutable_paired_key_encryption();
encryption_frame->set_signed_data(signature->data(), signature->size());
if (RelaxRestrictToContactsIfNeeded()) {
NL_LOG(INFO)
<< "Attempts to sign authentication token with a previous private key.";
std::optional<std::vector<uint8_t>> optional_signature =
certificate_manager_->SignWithPrivateCertificate(
nearby_share_settings_->GetLastVisibility(),
PadPrefix(local_prefix_, raw_token_));
if (optional_signature.has_value()) {
encryption_frame->set_optional_signed_data(optional_signature->data(),
optional_signature->size());
}
}
encryption_frame->set_secret_id_hash(certificate_id_hash.data(),
certificate_id_hash.size());
std::vector<uint8_t> data(frame.ByteSize());
frame.SerializeToArray(data.data(), frame.ByteSize());
connection_->Write(std::move(data));
}
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::VerifyRemotePublicCertificate(
const V1Frame& frame) {
return VerifyRemotePublicCertificateWithPrivateCertificate(
nearby_share_settings_->GetVisibility(), frame);
}
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::VerifyRemotePublicCertificateRelaxed(
const nearby::sharing::service::proto::V1Frame& frame) {
return VerifyRemotePublicCertificateWithPrivateCertificate(
nearby_share_settings_->GetLastVisibility(), frame);
}
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::
VerifyRemotePublicCertificateWithPrivateCertificate(
DeviceVisibility visibility,
const nearby::sharing::service::proto::V1Frame& frame) {
std::optional<std::vector<uint8_t>> hash =
certificate_manager_->HashAuthenticationTokenWithPrivateCertificate(
visibility, raw_token_);
const std::string& frame_hash =
frame.paired_key_encryption().secret_id_hash();
std::vector<uint8_t> frame_hash_data{frame_hash.begin(), frame_hash.end()};
if (hash.has_value() && *hash == frame_hash_data) {
NL_VLOG(1) << __func__
<< ": Successfully verified remote public certificate.";
return PairedKeyVerificationResult::kSuccess;
}
NL_VLOG(1) << __func__
<< ": Unable to verify remote public certificate.";
return PairedKeyVerificationResult::kUnable;
}
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::VerifyPairedKeyEncryptionFrame(
const V1Frame& frame) {
if (!certificate_) {
NL_VLOG(1) << __func__
<< ": Unable to verify remote paired key encryption frame. "
"Certificate not found.";
return PairedKeyVerificationResult::kUnable;
}
auto signed_data = frame.paired_key_encryption().signed_data();
std::vector<uint8_t> data(signed_data.begin(), signed_data.end());
if (!certificate_->VerifySignature(PadPrefix(remote_prefix_, raw_token_),
data)) {
if (!frame.paired_key_encryption().has_optional_signed_data()) {
NL_LOG(WARNING)
<< __func__
<< ": Unable to verify remote paired key encryption frame. "
"no optional signed data.";
return PairedKeyVerificationResult::kFail;
}
if (!RelaxRestrictToContactsIfNeeded()) {
NL_LOG(WARNING)
<< __func__
<< ": Unable to verify remote paired key encryption frame. "
"no need to try relax check.";
return PairedKeyVerificationResult::kFail;
}
// Verify optional signed data.
auto optional_signed_data =
frame.paired_key_encryption().optional_signed_data();
std::vector<uint8_t> optional_data(optional_signed_data.begin(),
optional_signed_data.end());
if (certificate_->VerifySignature(PadPrefix(remote_prefix_, raw_token_),
optional_data)) {
NL_LOG(INFO) << "Successfully verified remote paired key encryption "
"frame with the optional signed data.";
} else {
NL_LOG(WARNING)
<< __func__
<< ": Unable to verify remote paired key encryption frame.";
return PairedKeyVerificationResult::kFail;
}
}
if (!share_target_.is_known) {
NL_LOG(INFO) << __func__
<< ": Unable to verify remote paired key encryption frame. "
"Remote side is not a known share target.";
return PairedKeyVerificationResult::kUnable;
}
NL_VLOG(1)
<< __func__
<< ": Successfully verified remote paired key encryption frame.";
return PairedKeyVerificationResult::kSuccess;
}
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::MergeResults(
const std::vector<PairedKeyVerificationResult>& results) {
bool all_success = true;
for (const auto& result : results) {
if (result == PairedKeyVerificationResult::kFail) return result;
if (result != PairedKeyVerificationResult::kSuccess)
all_success = false;
}
return all_success ? PairedKeyVerificationResult::kSuccess
: PairedKeyVerificationResult::kUnable;
}
bool PairedKeyVerificationRunner::RelaxRestrictToContactsIfNeeded() const {
return share_target_.is_known &&
(clock_->Now() - nearby_share_settings_->GetLastVisibilityTimestamp() <
kRelaxAfterSetVisibilityTimeout);
}
} // namespace sharing
} // namespace nearby
+128
View File
@@ -0,0 +1,128 @@
// Copyright 2022-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_SHARING_PAIRED_KEY_VERIFICATION_RUNNER_H_
#define THIRD_PARTY_NEARBY_SHARING_PAIRED_KEY_VERIFICATION_RUNNER_H_
#include <stdint.h>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/clock.h"
#include "internal/platform/device_info.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/certificates/nearby_share_certificate_manager.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/incoming_frames_reader.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
namespace nearby {
namespace sharing {
class PairedKeyVerificationRunner
: public std::enable_shared_from_this<PairedKeyVerificationRunner> {
public:
enum class PairedKeyVerificationResult {
// Default value for verification result.
kUnknown,
// Succeeded with verification.
kSuccess,
// Failed to verify.
kFail,
// Unable to verify. Occurs when missing proper certificates.
kUnable,
};
PairedKeyVerificationRunner(
nearby::Clock* clock,
nearby::DeviceInfo& device_info,
NearbyShareSettings* nearby_share_settings,
bool self_share_feature_enabled, const ShareTarget& share_target,
absl::string_view endpoint_id, const std::vector<uint8_t>& token,
NearbyConnection* connection,
const std::optional<NearbyShareDecryptedPublicCertificate>& certificate,
NearbyShareCertificateManager* certificate_manager,
bool restrict_to_contacts, IncomingFramesReader* frames_reader,
absl::Duration read_frame_timeout);
~PairedKeyVerificationRunner();
void Run(std::function<
void(PairedKeyVerificationResult verification_result,
::location::nearby::proto::sharing::OSType remote_os_type)>
callback);
std::weak_ptr<PairedKeyVerificationRunner> GetWeakPtr() {
return this->weak_from_this();
}
private:
void SendPairedKeyEncryptionFrame();
void OnReadPairedKeyEncryptionFrame(
std::optional<nearby::sharing::service::proto::V1Frame> frame);
void OnReadPairedKeyResultFrame(
std::vector<PairedKeyVerificationResult> verification_results,
std::optional<nearby::sharing::service::proto::V1Frame> frame);
void SendPairedKeyResultFrame(PairedKeyVerificationResult result);
PairedKeyVerificationResult VerifyRemotePublicCertificate(
const nearby::sharing::service::proto::V1Frame& frame);
PairedKeyVerificationResult VerifyRemotePublicCertificateRelaxed(
const nearby::sharing::service::proto::V1Frame& frame);
PairedKeyVerificationResult
VerifyRemotePublicCertificateWithPrivateCertificate(
proto::DeviceVisibility visibility,
const nearby::sharing::service::proto::V1Frame& frame);
PairedKeyVerificationResult VerifyPairedKeyEncryptionFrame(
const nearby::sharing::service::proto::V1Frame& frame);
PairedKeyVerificationResult MergeResults(
const std::vector<PairedKeyVerificationResult>& results);
void SendCertificateInfo();
bool RelaxRestrictToContactsIfNeeded() const;
nearby::Clock* const clock_;
nearby::DeviceInfo& device_info_;
NearbyShareSettings* nearby_share_settings_;
bool self_share_feature_enabled_;
ShareTarget share_target_;
std::string endpoint_id_;
std::vector<uint8_t> raw_token_;
NearbyConnection* connection_;
std::optional<NearbyShareDecryptedPublicCertificate> certificate_;
NearbyShareCertificateManager* certificate_manager_;
bool restrict_to_contacts_ = false;
IncomingFramesReader* frames_reader_;
const absl::Duration read_frame_timeout_;
std::function<void(PairedKeyVerificationResult,
::location::nearby::proto::sharing::OSType)>
callback_;
bool relax_restrict_to_contacts_ = false;
char local_prefix_;
char remote_prefix_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_PAIRED_KEY_VERIFICATION_RUNNER_H_
@@ -0,0 +1,495 @@
// Copyright 2022-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 "sharing/paired_key_verification_runner.h"
#include <stdint.h>
#include <functional>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/flags/nearby_flags.h"
#include "internal/test/fake_clock.h"
#include "internal/test/fake_device_info.h"
#include "proto/sharing_enums.pb.h"
#include "sharing/certificates/fake_nearby_share_certificate_manager.h"
#include "sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "sharing/certificates/test_util.h"
#include "sharing/fake_nearby_connection.h"
#include "sharing/flags/nearby_sharing_feature_flags.h"
#include "sharing/incoming_frames_reader.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/internal/test/fake_preference_manager.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_connection.h"
#include "sharing/nearby_sharing_decoder.h"
#include "sharing/nearby_sharing_decoder_impl.h"
#include "sharing/nearby_sharing_settings.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/proto/rpc_resources.pb.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
namespace nearby {
namespace sharing {
namespace {
using V1Frame = ::nearby::sharing::service::proto::V1Frame;
using PairedKeyResultFrame =
::nearby::sharing::service::proto::PairedKeyResultFrame;
using ::nearby::sharing::proto::DeviceVisibility;
using PairedKeyVerificationResult =
PairedKeyVerificationRunner::PairedKeyVerificationResult;
using ::location::nearby::proto::sharing::OSType;
constexpr char kEndpointId[] = "test_endpoint_id";
const std::vector<uint8_t>& GetAuthToken() {
static std::vector<uint8_t>* auth_token = new std::vector<uint8_t>({0, 1, 2});
return *auth_token;
}
const std::vector<uint8_t>& GetPrivateCertificateHashAuthToken() {
static std::vector<uint8_t>* private_certificate_hash_auth_token =
new std::vector<uint8_t>({0x8b, 0xcb, 0xa2, 0xf8, 0xe4, 0x06});
return *private_certificate_hash_auth_token;
}
const std::vector<uint8_t>& GetIncomingConnectionSignedData() {
static std::vector<uint8_t>* incoming_connection_signed_data =
new std::vector<uint8_t>(
{0x30, 0x45, 0x02, 0x20, 0x4f, 0x83, 0x72, 0xbd, 0x02, 0x70, 0xd9,
0xda, 0x62, 0x83, 0x5d, 0xb2, 0xdc, 0x6e, 0x3f, 0xa6, 0xa8, 0xa1,
0x4f, 0x5f, 0xd3, 0xe3, 0xd9, 0x1a, 0x5d, 0x2d, 0x61, 0xd2, 0x6c,
0xdd, 0x8d, 0xa5, 0x02, 0x21, 0x00, 0xd4, 0xe1, 0x1d, 0x14, 0xcb,
0x58, 0xf7, 0x02, 0xd5, 0xab, 0x48, 0xe2, 0x2f, 0xcb, 0xc0, 0x53,
0x41, 0x06, 0x50, 0x65, 0x95, 0x19, 0xa9, 0x22, 0x92, 0x00, 0x42,
0x01, 0x26, 0x25, 0xcb, 0x8c});
return *incoming_connection_signed_data;
}
const std::vector<uint8_t>& GetInvalidIncomingConnectionSignedData() {
static std::vector<uint8_t>* incoming_connection_signed_data =
new std::vector<uint8_t>(
{0x30, 0x45, 0x02, 0x20, 0x4f, 0x83, 0x72, 0xbd, 0x02, 0x70, 0xd9,
0xda, 0x61, 0x83, 0x5d, 0xb2, 0xdc, 0x6e, 0x3f, 0xa6, 0xa8, 0xa1,
0x4f, 0x5f, 0xd3, 0xe3, 0xd9, 0x1a, 0x5d, 0x2d, 0x61, 0xd2, 0x6c,
0xdd, 0x8d, 0xa5, 0x02, 0x21, 0x05, 0xd4, 0xe1, 0x1d, 0x14, 0xcb,
0x58, 0xf7, 0x02, 0xd5, 0xab, 0x48, 0xe2, 0x2f, 0xcb, 0xc0, 0x53,
0x41, 0x06, 0x50, 0x65, 0x95, 0x19, 0xa9, 0x22, 0x92, 0x00, 0x42,
0x01, 0x26, 0x25, 0xcb, 0x82});
return *incoming_connection_signed_data;
}
std::list<PairedKeyResultFrame> GeneratePairedKeyResultFrame() {
std::list<PairedKeyResultFrame> result;
PairedKeyResultFrame frame;
frame.set_status(PairedKeyResultFrame::UNKNOWN);
result.push_back(frame);
frame.set_status(PairedKeyResultFrame::SUCCESS);
frame.set_os_type(OSType::ANDROID);
result.push_back(frame);
frame.set_status(PairedKeyResultFrame::FAIL);
frame.set_os_type(OSType::UNKNOWN_OS_TYPE);
result.push_back(frame);
frame.set_status(PairedKeyResultFrame::UNABLE);
frame.set_os_type(OSType::WINDOWS);
result.push_back(frame);
return result;
}
const absl::Duration kTimeout = absl::Seconds(1);
class MockNearbyShareLocalDeviceDataManager
: public NearbyShareLocalDeviceDataManager {
public:
MOCK_METHOD(std::string, GetId, (), (override));
MOCK_METHOD(std::string, GetDeviceName, (), (override, const));
MOCK_METHOD(std::optional<std::string>, GetFullName, (), (override, const));
MOCK_METHOD(std::optional<std::string>, GetIconUrl, (), (override, const));
MOCK_METHOD(DeviceNameValidationResult, ValidateDeviceName,
(absl::string_view), (override));
MOCK_METHOD(DeviceNameValidationResult, SetDeviceName, (absl::string_view),
(override));
MOCK_METHOD(void, DownloadDeviceData, (), (override));
MOCK_METHOD(void, UploadContacts,
(std::vector<nearby::sharing::proto::Contact>,
UploadCompleteCallback),
(override));
MOCK_METHOD(void, UploadCertificates,
(std::vector<nearby::sharing::proto::PublicCertificate>,
UploadCompleteCallback),
(override));
MOCK_METHOD(void, OnStart, (), (override));
MOCK_METHOD(void, OnStop, (), (override));
};
class MockIncomingFramesReader : public IncomingFramesReader {
public:
MockIncomingFramesReader(Context* context, NearbySharingDecoder* decoder,
NearbyConnection* connection)
: IncomingFramesReader(context, decoder, connection) {}
MOCK_METHOD(void, ReadFrame,
(std::function<void(std::optional<V1Frame>)> callback),
(override));
MOCK_METHOD(void, ReadFrame,
(service::proto::V1Frame_FrameType frame_type,
std::function<void(std::optional<V1Frame>)> callback,
absl::Duration timeout),
(override));
};
PairedKeyVerificationRunner::PairedKeyVerificationResult Merge(
PairedKeyVerificationRunner::PairedKeyVerificationResult local_result,
PairedKeyResultFrame::Status remote_result) {
if (remote_result == PairedKeyResultFrame::FAIL ||
local_result ==
PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail) {
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail;
}
if (remote_result == PairedKeyResultFrame::SUCCESS &&
local_result ==
PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess) {
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess;
}
return PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable;
}
class PairedKeyVerificationRunnerTest : public testing::Test {
public:
enum class ReturnFrameType {
// Return absl::nullopt for the frame.
kNull,
// Return an empty frame.
kEmpty,
// Return a valid frame.
kValid,
// Return a valid optional frame.
kOptionalValid,
// Return an invalid frame with both signed signature.
kInValid,
};
PairedKeyVerificationRunnerTest()
: frames_reader_(&context_, &decoder_, &connection_) {}
void SetUp() override {
nearby_share_settings_ = std::make_unique<NearbyShareSettings>(
&context_, context_.GetClock(), fake_device_info_, preference_manager_,
&local_device_data_manager_);
nearby_share_settings_->SetVisibility(
DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
FastForward(absl::Minutes(15));
share_target_.is_incoming = true;
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_sharing_feature::kEnableSelfShare, true);
}
void RunVerification(
bool use_valid_public_certificate, bool restricted_to_contacts,
PairedKeyVerificationRunner::PairedKeyVerificationResult expected_result,
OSType expected_os_type = OSType::UNKNOWN_OS_TYPE) {
std::optional<NearbyShareDecryptedPublicCertificate> public_certificate =
use_valid_public_certificate
? std::make_optional<NearbyShareDecryptedPublicCertificate>(
GetNearbyShareTestDecryptedPublicCertificate())
: std::nullopt;
bool self_share_feature_enabled = NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_sharing_feature::kEnableSelfShare);
auto runner = std::make_shared<PairedKeyVerificationRunner>(
context_.GetClock(), fake_device_info_, nearby_share_settings_.get(),
self_share_feature_enabled, share_target_, kEndpointId, GetAuthToken(),
&connection_, std::move(public_certificate), &certificate_manager_,
restricted_to_contacts, &frames_reader_, kTimeout);
runner->Run(
[&, expected_result, expected_os_type](
PairedKeyVerificationRunner::PairedKeyVerificationResult result,
OSType remote_os_type) {
EXPECT_EQ(expected_result, result);
EXPECT_EQ(expected_os_type, remote_os_type);
});
}
void SetUpPairedKeyEncryptionFrame(ReturnFrameType frame_type) {
EXPECT_CALL(frames_reader_,
ReadFrame(testing::Eq(V1Frame::PAIRED_KEY_ENCRYPTION),
testing::_, testing::Eq(kTimeout)))
.WillOnce(testing::WithArg<1>(testing::Invoke(
[this,
frame_type](std::function<void(std::optional<V1Frame>)> callback) {
if (frame_type == ReturnFrameType::kNull) {
std::move(callback)(std::nullopt);
return;
}
auto frame = V1Frame();
if (frame_type == ReturnFrameType::kValid) {
nearby::sharing::service::proto::PairedKeyEncryptionFrame*
encryption_frame = frame.mutable_paired_key_encryption();
encryption_frame->set_signed_data(
GetIncomingConnectionSignedData().data(),
GetIncomingConnectionSignedData().size());
encryption_frame->set_secret_id_hash(
GetPrivateCertificateHashAuthToken().data(),
GetPrivateCertificateHashAuthToken().size());
} else if (frame_type == ReturnFrameType::kOptionalValid) {
nearby::sharing::service::proto::PairedKeyEncryptionFrame*
encryption_frame = frame.mutable_paired_key_encryption();
encryption_frame->set_signed_data(
GetInvalidIncomingConnectionSignedData().data(),
GetInvalidIncomingConnectionSignedData().size());
encryption_frame->set_optional_signed_data(
GetIncomingConnectionSignedData().data(),
GetIncomingConnectionSignedData().size());
encryption_frame->set_secret_id_hash(
GetPrivateCertificateHashAuthToken().data(),
GetPrivateCertificateHashAuthToken().size());
// make sure the optional codes are executed
nearby_share_settings_->SetVisibility(
DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS);
} else if (frame_type == ReturnFrameType::kInValid) {
nearby::sharing::service::proto::PairedKeyEncryptionFrame*
encryption_frame = frame.mutable_paired_key_encryption();
encryption_frame->set_signed_data(
GetInvalidIncomingConnectionSignedData().data(),
GetInvalidIncomingConnectionSignedData().size());
encryption_frame->set_optional_signed_data(
GetInvalidIncomingConnectionSignedData().data(),
GetInvalidIncomingConnectionSignedData().size());
encryption_frame->set_secret_id_hash(
GetPrivateCertificateHashAuthToken().data(),
GetPrivateCertificateHashAuthToken().size());
} else {
nearby::sharing::service::proto::PairedKeyEncryptionFrame*
encryption_frame = frame.mutable_paired_key_encryption();
encryption_frame->clear_signed_data();
encryption_frame->clear_secret_id_hash();
}
std::move(callback)(std::move(frame));
})));
}
void SetUpPairedKeyResultFrame(
ReturnFrameType frame_type,
PairedKeyResultFrame::Status status = PairedKeyResultFrame::UNKNOWN,
OSType os_type = OSType::UNKNOWN_OS_TYPE) {
EXPECT_CALL(frames_reader_,
ReadFrame(testing::Eq(V1Frame::PAIRED_KEY_RESULT), testing::_,
testing::Eq(kTimeout)))
.WillOnce(testing::WithArg<1>(testing::Invoke(
[=](std::function<void(std::optional<V1Frame>)> callback) {
if (frame_type == ReturnFrameType::kNull) {
std::move(callback)(std::nullopt);
return;
}
auto frame = V1Frame();
PairedKeyResultFrame* result_frame =
frame.mutable_paired_key_result();
result_frame->set_status(status);
result_frame->set_os_type(os_type);
std::move(callback)(std::move(frame));
})));
}
nearby::sharing::service::proto::Frame GetWrittenFrame() {
std::vector<uint8_t> data = connection_.GetWrittenData();
nearby::sharing::service::proto::Frame frame;
frame.ParseFromArray(data.data(), data.size());
return frame;
}
void ExpectPairedKeyEncryptionFrameSent() {
nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
ASSERT_TRUE(frame.has_v1());
ASSERT_TRUE(frame.v1().has_paired_key_encryption());
}
void ExpectCertificateInfoSent() {}
void ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::Status status) {
nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
ASSERT_TRUE(frame.has_v1());
ASSERT_TRUE(frame.v1().has_paired_key_result());
EXPECT_EQ(status, frame.v1().paired_key_result().status());
}
void FastForward(absl::Duration duration) {
context_.fake_clock()->FastForward(duration);
}
protected:
ShareTarget share_target_;
private:
nearby::FakePreferenceManager preference_manager_;
FakeDeviceInfo fake_device_info_;
FakeContext context_;
FakeNearbyConnection connection_;
NearbySharingDecoderImpl decoder_;
testing::NiceMock<MockIncomingFramesReader> frames_reader_;
FakeNearbyShareCertificateManager certificate_manager_;
::testing::NiceMock<MockNearbyShareLocalDeviceDataManager>
local_device_data_manager_;
std::unique_ptr<NearbyShareSettings> nearby_share_settings_;
};
TEST_F(PairedKeyVerificationRunnerTest,
NullCertificate_InvalidPairedKeyEncryptionFrame_RestrictToContacts) {
// Empty key encryption frame fails the certificate verification.
SetUpPairedKeyEncryptionFrame(ReturnFrameType::kEmpty);
RunVerification(
/*use_valid_public_certificate=*/false,
/*restricted_to_contacts=*/true,
/*expected_result=*/
PairedKeyVerificationResult::kFail);
ExpectPairedKeyEncryptionFrameSent();
}
TEST_F(PairedKeyVerificationRunnerTest,
ValidPairedKeyEncryptionFrame_ResultFrameTimedOut) {
SetUpPairedKeyEncryptionFrame(ReturnFrameType::kValid);
// Null result frame fails the certificate verification process.
SetUpPairedKeyResultFrame(ReturnFrameType::kNull);
RunVerification(
/*use_valid_public_certificate=*/true,
/*restricted_to_contacts=*/false,
/*expected_result=*/
PairedKeyVerificationResult::kFail);
ExpectPairedKeyEncryptionFrameSent();
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::UNABLE);
}
struct TestParameters {
bool is_target_known;
bool is_valid_certificate;
PairedKeyVerificationRunnerTest::ReturnFrameType encryption_frame_type;
PairedKeyVerificationRunner::PairedKeyVerificationResult result;
} kParameters[] = {
{true, true, PairedKeyVerificationRunnerTest::ReturnFrameType::kValid,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess},
{true, true, PairedKeyVerificationRunnerTest::ReturnFrameType::kEmpty,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail},
{true, false, PairedKeyVerificationRunnerTest::ReturnFrameType::kValid,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable},
{true, false, PairedKeyVerificationRunnerTest::ReturnFrameType::kEmpty,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable},
{false, true, PairedKeyVerificationRunnerTest::ReturnFrameType::kValid,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable},
{true, true,
PairedKeyVerificationRunnerTest::ReturnFrameType::kOptionalValid,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess},
{true, true, PairedKeyVerificationRunnerTest::ReturnFrameType::kInValid,
PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail},
};
using KeyVerificationTestParam =
std::tuple<TestParameters, service::proto::PairedKeyResultFrame>;
class ParameterisedPairedKeyVerificationRunnerTest
: public PairedKeyVerificationRunnerTest,
public testing::WithParamInterface<KeyVerificationTestParam> {};
TEST_P(ParameterisedPairedKeyVerificationRunnerTest,
ValidEncryptionFrame_ValidResultFrame) {
const TestParameters& params = std::get<0>(GetParam());
PairedKeyResultFrame result_frame = std::get<1>(GetParam());
PairedKeyVerificationRunner::PairedKeyVerificationResult expected_result =
Merge(params.result, result_frame.status());
share_target_.is_known = params.is_target_known;
SetUpPairedKeyEncryptionFrame(params.encryption_frame_type);
SetUpPairedKeyResultFrame(
PairedKeyVerificationRunnerTest::ReturnFrameType::kValid,
result_frame.status(),
result_frame.has_os_type() ? result_frame.os_type()
: OSType::UNKNOWN_OS_TYPE);
RunVerification(
/*use_valid_public_certificate=*/params.is_valid_certificate,
/*restricted_to_contacts=*/false, expected_result,
result_frame.has_os_type() ? result_frame.os_type()
: OSType::UNKNOWN_OS_TYPE);
ExpectPairedKeyEncryptionFrameSent();
if (params.encryption_frame_type ==
PairedKeyVerificationRunnerTest::ReturnFrameType::kValid)
ExpectCertificateInfoSent();
// Check for result frame sent.
if (!params.is_valid_certificate) {
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::UNABLE);
return;
}
if (params.encryption_frame_type ==
PairedKeyVerificationRunnerTest::ReturnFrameType::kEmpty) {
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::FAIL);
return;
}
if (params.encryption_frame_type ==
PairedKeyVerificationRunnerTest::ReturnFrameType::kInValid) {
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::FAIL);
return;
}
if (params.is_target_known) {
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::SUCCESS);
} else {
ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::UNABLE);
}
}
INSTANTIATE_TEST_SUITE_P(
/*no prefix*/, ParameterisedPairedKeyVerificationRunnerTest,
testing::Combine(testing::ValuesIn(kParameters),
testing::ValuesIn(GeneratePairedKeyResultFrame())));
} // namespace
} // namespace sharing
} // namespace nearby
+56
View File
@@ -0,0 +1,56 @@
// 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_SHARING_PAYLOAD_LISTENER_H_
#define THIRD_PARTY_NEARBY_SHARING_PAYLOAD_LISTENER_H_
#include "absl/strings/string_view.h"
#include "sharing/nearby_connections_types.h"
namespace nearby {
namespace sharing {
// Listener for payload status. Methods in this interface are called from
// utility process, and are used by the browser process to listen for payload
// status associated with remote endpoints.
class PayloadListener {
public:
virtual ~PayloadListener() = default;
// Called when a Payload is received from a remote endpoint. Depending on the
// type of the Payload, all the data may or may not have been received at
// the time of this call. OnPayloadTransferUpdate() should be used to get
// updates on the status of the data received.
//
// endpoint_id - The identifier for the remote endpoint that sent the
// payload.
// payload - The Payload object received.
virtual void OnPayloadReceived(absl::string_view endpoint_id,
Payload& payload) = 0;
// Called with progress information about an active Payload transfer, either
// incoming or outgoing.
//
// endpoint_id - The identifier for the remote endpoint that is sending or
// receiving this payload.
// update - The PayloadTransferUpdate structure describing the status of
// the transfer.
virtual void OnPayloadTransferUpdate(absl::string_view endpoint_id,
PayloadTransferUpdate& update) = 0;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_PAYLOAD_LISTENER_H_
+289
View File
@@ -0,0 +1,289 @@
// Copyright 2022-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 "sharing/payload_tracker.h"
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "sharing/attachment_info.h"
#include "sharing/constants.h"
#include "sharing/file_attachment.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/public/logging.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/share_target.h"
#include "sharing/text_attachment.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_metadata_builder.h"
#include "sharing/wifi_credentials_attachment.h"
namespace nearby {
namespace sharing {
PayloadTracker::PayloadTracker(
Context* context, const ShareTarget& share_target,
const absl::flat_hash_map<int64_t, AttachmentInfo>& attachment_info_map,
std::function<void(ShareTarget, TransferMetadata)> update_callback)
: context_(context),
share_target_(share_target),
update_callback_(std::move(update_callback)) {
total_transfer_size_ = 0;
confirmed_transfer_size_ = 0;
for (const auto& file : share_target.file_attachments) {
auto it = attachment_info_map.find(file.id());
if (it == attachment_info_map.end() || !it->second.payload_id) {
NL_LOG(WARNING)
<< __func__
<< ": Failed to retrieve payload for file attachment id - "
<< file.id();
continue;
}
payload_state_.emplace(*it->second.payload_id,
State(file.id(), file.size()));
++num_file_attachments_;
total_transfer_size_ += file.size();
}
for (const auto& text : share_target.text_attachments) {
auto it = attachment_info_map.find(text.id());
if (it == attachment_info_map.end() || !it->second.payload_id) {
NL_LOG(WARNING)
<< __func__
<< ": Failed to retrieve payload for text attachment id - "
<< text.id();
continue;
}
payload_state_.emplace(*it->second.payload_id,
State(text.id(), text.size()));
++num_text_attachments_;
total_transfer_size_ += text.size();
}
for (const auto& wifi_credentials :
share_target.wifi_credentials_attachments) {
auto it = attachment_info_map.find(wifi_credentials.id());
if (it == attachment_info_map.end() || !it->second.payload_id) {
NL_LOG(WARNING) << __func__
<< ": Failed to retrieve payload for WiFi credentials "
"attachment id - "
<< wifi_credentials.id();
continue;
}
payload_state_.emplace(
*it->second.payload_id,
State(wifi_credentials.id(), wifi_credentials.size()));
++num_wifi_credentials_attachments_;
total_transfer_size_ += wifi_credentials.size();
}
}
PayloadTracker::~PayloadTracker() = default;
void PayloadTracker::OnStatusUpdate(
std::unique_ptr<PayloadTransferUpdate> update,
std::optional<Medium> upgraded_medium) {
auto it = payload_state_.find(update->payload_id);
if (it == payload_state_.end()) return;
// For metrics.
if (!first_update_timestamp_.has_value()) {
first_update_timestamp_ = absl::Now();
num_first_update_bytes_ = update->bytes_transferred;
}
if (upgraded_medium.has_value()) {
last_upgraded_medium_ = upgraded_medium;
}
if (it->second.status != update->status) {
it->second.status = update->status;
NL_VLOG(1) << __func__ << ": Payload id " << update->payload_id
<< " had status change: " << update->status;
}
if (it->second.status == PayloadStatus::kSuccess) {
NL_LOG(INFO) << __func__ << ": Completed transfer of payload " << it->first
<< " with attachment id " << it->second.attachment_id;
transferred_attachments_count_++;
confirmed_transfer_size_ += update->bytes_transferred;
}
// The number of bytes transferred should never go down. That said, some
// status updates like cancellation might send a value of 0. In that case, we
// retain the last known value for use in metrics.
if (update->bytes_transferred > it->second.amount_transferred) {
it->second.amount_transferred = update->bytes_transferred;
}
// Handle in progress attachment.
if (!in_progress_payload_id_.has_value() ||
*in_progress_payload_id_ != update->payload_id) {
in_progress_payload_id_ = update->payload_id;
}
OnTransferUpdate(it->second);
}
void PayloadTracker::OnTransferUpdate(const State& state) {
if (IsComplete()) {
NL_VLOG(1) << __func__ << ": All payloads are complete.";
update_callback_(
share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kComplete)
.set_progress(100)
.set_total_attachments_count(payload_state_.size())
.set_transferred_attachments_count(transferred_attachments_count_)
.build());
return;
}
if (IsCancelled(state)) {
NL_VLOG(1) << __func__ << ": Payloads cancelled.";
update_callback_(
share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kCancelled)
.set_total_attachments_count(payload_state_.size())
.set_transferred_attachments_count(transferred_attachments_count_)
.build());
return;
}
if (HasFailed(state)) {
NL_VLOG(1) << __func__ << ": Payloads failed.";
update_callback_(
share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kFailed)
.set_total_attachments_count(payload_state_.size())
.set_transferred_attachments_count(transferred_attachments_count_)
.build());
return;
}
double percent = CalculateProgressPercent(state);
int current_progress = static_cast<int>(percent);
absl::Time current_time = context_->GetClock()->Now();
uint64_t current_transferred_size = GetTotalTransferred(state);
if (current_progress == last_update_progress_ &&
(current_time - last_update_timestamp_) < kMinProgressUpdateFrequency &&
state.status != PayloadStatus::kSuccess) {
return;
}
// Update transfer speed approximately every `kTransferSpeedUpdateInterval`
// second.
if (current_speed_ == 0 ||
current_time - last_transfer_speed_update_timestamp_ >
absl::Seconds(kTransferSpeedUpdateInterval)) {
current_speed_ = (current_transferred_size - last_transferred_size_) /
absl::ToDoubleSeconds(
current_time - last_transfer_speed_update_timestamp_);
// Use current speed for the ETA calculation for the first
// `kEstimatedTimeRemainingUpdateInterval` seconds to avoid getting stuck at
// showing 24+ hours left.
if ((first_window_ == true) &&
(current_time - last_eta_update_timestamp_ <
absl::Seconds(kEstimatedTimeRemainingUpdateInterval))) {
estimated_time_remaining_ =
(total_transfer_size_ - current_transferred_size) /
(current_speed_ + std::numeric_limits<uint64_t>::min());
first_window_ = false;
}
rolling_window_speed_bucket_ += current_speed_;
last_transferred_size_ = current_transferred_size;
last_transfer_speed_update_timestamp_ = current_time;
}
// Update estimated time remaining approximately every
// `kEstimatedTimeRemainingUpdateInterval` seconds.
if (current_time - last_eta_update_timestamp_ >
absl::Seconds(kEstimatedTimeRemainingUpdateInterval)) {
double average_speed =
rolling_window_speed_bucket_ / kEstimatedTimeRemainingUpdateInterval;
estimated_time_remaining_ =
(total_transfer_size_ - current_transferred_size) /
(average_speed + std::numeric_limits<uint64_t>::min());
last_eta_update_timestamp_ = current_time;
rolling_window_speed_bucket_ = 0.0;
}
last_update_progress_ = current_progress;
last_update_timestamp_ = current_time;
update_callback_(
share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(percent)
.set_transferred_bytes(current_transferred_size)
.set_transfer_speed(static_cast<uint64_t>(current_speed_))
.set_estimated_time_remaining(
static_cast<uint64_t>(estimated_time_remaining_))
.set_total_attachments_count(payload_state_.size())
.set_transferred_attachments_count(transferred_attachments_count_)
.set_in_progress_attachment_id(state.attachment_id)
.set_in_progress_attachment_total_bytes(state.total_size)
.set_in_progress_attachment_transferred_bytes(
state.amount_transferred)
.build());
}
bool PayloadTracker::IsComplete() const {
return transferred_attachments_count_ == payload_state_.size();
}
bool PayloadTracker::IsCancelled(const State& state) const {
return state.status == PayloadStatus::kCanceled;
}
bool PayloadTracker::HasFailed(const State& state) const {
return state.status == PayloadStatus::kFailure;
}
uint64_t PayloadTracker::GetTotalTransferred(const State& state) const {
if (state.status == PayloadStatus::kSuccess) {
return confirmed_transfer_size_;
}
return confirmed_transfer_size_ + state.amount_transferred;
}
double PayloadTracker::CalculateProgressPercent(const State& state) const {
if (!total_transfer_size_) {
NL_LOG(WARNING) << __func__ << ": Total attachment size is 0";
return 100.0;
}
return (100.0 * GetTotalTransferred(state)) / total_transfer_size_;
}
} // namespace sharing
} // namespace nearby
+112
View File
@@ -0,0 +1,112 @@
// Copyright 2022-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_SHARING_PAYLOAD_TRACKER_H_
#define THIRD_PARTY_NEARBY_SHARING_PAYLOAD_TRACKER_H_
#include <stddef.h>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include "absl/container/flat_hash_map.h"
#include "absl/time/time.h"
#include "sharing/attachment_info.h"
#include "sharing/internal/public/context.h"
#include "sharing/nearby_connections_manager.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/share_target.h"
#include "sharing/transfer_metadata.h"
namespace nearby {
namespace sharing {
// Listens for incoming or outgoing transfer updates from Nearby Connections and
// forwards the transfer progress to the |update_callback|.
class PayloadTracker : public NearbyConnectionsManager::PayloadStatusListener {
public:
PayloadTracker(
Context* context, const ShareTarget& share_target,
const absl::flat_hash_map<int64_t, AttachmentInfo>& attachment_info_map,
std::function<void(ShareTarget, TransferMetadata)> update_callback);
~PayloadTracker() override;
// NearbyConnectionsManager::PayloadStatusListener:
void OnStatusUpdate(std::unique_ptr<PayloadTransferUpdate> update,
std::optional<Medium> upgraded_medium) override;
private:
struct State {
explicit State(int64_t attachment_id, int64_t total_size)
: attachment_id(attachment_id), total_size(total_size) {}
~State() = default;
int64_t attachment_id = 0;
uint64_t amount_transferred = 0;
const uint64_t total_size;
PayloadStatus status = PayloadStatus::kInProgress;
};
void OnTransferUpdate(const State& state);
bool IsComplete() const;
bool IsCancelled(const State& state) const;
bool HasFailed(const State& state) const;
uint64_t GetTotalTransferred(const State& state) const;
double CalculateProgressPercent(const State& state) const;
Context* context_;
ShareTarget share_target_;
std::function<void(ShareTarget, TransferMetadata)> update_callback_;
// Map of payload id to state of payload.
std::map<int64_t, State> payload_state_;
// Tracks in progress payload.
std::optional<int64_t> in_progress_payload_id_ = std::nullopt;
uint64_t total_transfer_size_;
uint64_t confirmed_transfer_size_;
int last_update_progress_ = 0;
absl::Time last_update_timestamp_; // progress percentage
absl::Time last_transfer_speed_update_timestamp_;
absl::Time last_eta_update_timestamp_;
uint64_t last_transferred_size_ = 0;
double current_speed_ = 0.0;
double rolling_window_speed_bucket_ = 0.0;
double estimated_time_remaining_ = 0.0;
bool first_window_ = true;
// Tracks transferred attachments count.
int transferred_attachments_count_ = 0;
// For metrics.
size_t num_text_attachments_ = 0;
size_t num_file_attachments_ = 0;
size_t num_wifi_credentials_attachments_ = 0;
uint64_t num_first_update_bytes_ = 0;
std::optional<absl::Time> first_update_timestamp_;
std::optional<Medium> last_upgraded_medium_;
};
} // namespace sharing
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_SHARING_PAYLOAD_TRACKER_H_
+114
View File
@@ -0,0 +1,114 @@
// 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 "sharing/payload_tracker.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/test/fake_clock.h"
#include "sharing/attachment_info.h"
#include "sharing/file_attachment.h"
#include "sharing/internal/public/context.h"
#include "sharing/internal/test/fake_context.h"
#include "sharing/nearby_connections_types.h"
#include "sharing/proto/wire_format.pb.h"
#include "sharing/share_target.h"
#include "sharing/transfer_metadata.h"
namespace nearby {
namespace sharing {
namespace {
constexpr int64_t kFileId = 1;
constexpr int64_t kFileSize = 100 * 1024; // 100KB
constexpr absl::string_view kFileName = "test.jpg";
constexpr absl::string_view kMimeType = "image/jpg";
class PayloadTrackerTest : public ::testing::Test {
public:
void SetUp() override {
share_target_.file_attachments.clear();
share_target_.file_attachments.push_back(FileAttachment(
kFileId, kFileSize, std::string(kFileName), std::string(kMimeType),
service::proto::FileMetadata::IMAGE));
attachment_info_map_.clear();
AttachmentInfo attachment_info;
attachment_info.payload_id = kFileId;
attachment_info_map_.emplace(share_target_.file_attachments.at(0).id(),
std::move(attachment_info));
payload_tracker_ = std::make_unique<PayloadTracker>(
context(), share_target_, attachment_info_map_,
[&](ShareTarget share_target, TransferMetadata transfer_metadata) {
current_percentage_ = transfer_metadata.progress();
});
}
float percentage() const { return current_percentage_; }
void FastForward(absl::Duration duration) {
FakeClock* clock = dynamic_cast<FakeClock*>(context()->GetClock());
clock->FastForward(duration);
}
void PayloadUpdate(int bytes_transferred) {
auto transfer_update = std::make_unique<PayloadTransferUpdate>(
/*payload_id=*/kFileId, PayloadStatus::kInProgress,
/*total_bytes=*/kFileSize, /*bytes_transferred=*/bytes_transferred);
payload_tracker_->OnStatusUpdate(std::move(transfer_update), std::nullopt);
}
private:
Context* context() {
static FakeContext* context = new FakeContext();
return context;
}
std::unique_ptr<PayloadTracker> payload_tracker_ = nullptr;
float current_percentage_ = 0.0;
ShareTarget share_target_;
absl::flat_hash_map<int64_t, AttachmentInfo> attachment_info_map_;
};
TEST_F(PayloadTrackerTest, StatusUpdateWithoutTimeUpdate) {
EXPECT_EQ(percentage(), 0.0);
PayloadUpdate(1024);
EXPECT_EQ(percentage(), 1.0);
PayloadUpdate(2048);
EXPECT_EQ(percentage(), 2.0);
}
TEST_F(PayloadTrackerTest, StatusUpdateWithTimeUpdate) {
EXPECT_EQ(percentage(), 0.0);
PayloadUpdate(1024);
EXPECT_EQ(percentage(), 1.0);
FastForward(absl::Milliseconds(100));
PayloadUpdate(2048);
EXPECT_EQ(percentage(), 2.0);
FastForward(absl::Milliseconds(100));
PayloadUpdate(3072);
EXPECT_EQ(percentage(), 3.0);
}
} // namespace
} // namespace sharing
} // namespace nearby

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