diff --git a/.gitignore b/.gitignore index d3a7b147..c0299bed 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ bazel-* # Devcontainers /.devcontainer/ +/connections/walkietalkie/ diff --git a/MODULE.bazel b/MODULE.bazel index b6559060..87aa443b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -130,14 +130,14 @@ package(default_visibility = ["//visibility:public"]) cc_import( name = "sdbus_cpp", - shared_library = "lib64/libsdbus-c++.so", + shared_library = "lib/x86_64-linux-gnu/libsdbus-c++.so", hdrs = glob(["include/sdbus-c++/**/*.h*"]), includes = ["include"], ) cc_import( name = "libsystemd", - shared_library = "lib64/libsystemd.so", + shared_library = "lib/x86_64-linux-gnu/libsystemd.so", ) """, ) diff --git a/internal/platform/implementation/linux/BUILD b/internal/platform/implementation/linux/BUILD index bcee965e..3b20885a 100644 --- a/internal/platform/implementation/linux/BUILD +++ b/internal/platform/implementation/linux/BUILD @@ -6,11 +6,11 @@ cc_library( hdrs = [ "atomics.h", "bluetooth_adapter.h", - "generated/bluez_adapter_client_glue.h", "device_info.h", + "generated/bluez_adapter_client_glue.h", "multi_thread_executor.h", - "scheduled_executor.h", "platform.h", + "scheduled_executor.h", ], visibility = ["//visibility:private"], deps = [ @@ -19,7 +19,7 @@ cc_library( "//internal/platform:logging", "//internal/platform/implementation:types", "@com_google_absl//absl/strings", - "@sdbus_cpp//:sdbus_cpp", + "@sdbus_cpp", "@sdbus_cpp//:libsystemd", ], ) @@ -30,49 +30,50 @@ cc_library( "bluetooth_adapter.cc", "platform.cc", "scheduled_executor.cc", + "system_clock.cc", ], hdrs = [ - "device_info.h", "bluetooth_adapter.h", + "device_info.h", ], visibility = ["//visibility:public"], deps = [ - ":types", - "//connections/implementation/flags:connections_flags", - "//internal/base:file_path", - "//internal/base:files", - "//internal/flags:nearby_flags", - "//internal/platform:base", - "//internal/platform:cancellation_flag", - "//internal/platform:logging", - "//internal/platform:mac_address", - "//internal/platform:types", - "//internal/platform:uuid", - "//internal/platform/flags:platform_flags", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:platform", - "//internal/platform/implementation:types", - "//internal/platform/implementation:wifi_utils", - "//internal/platform/implementation/shared:posix_condition_variable", - "//internal/platform/implementation/shared:posix_mutex", - "//internal/platform/implementation/shared:count_down_latch", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/base:nullability", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@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", - "@nlohmann_json//:json", - - "@sdbus_cpp//:sdbus_cpp", - "@sdbus_cpp//:libsystemd", - ], + ":crypto_impl", + ":types", + "//connections/implementation/flags:connections_flags", + "//internal/base:file_path", + "//internal/base:files", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "//internal/platform:logging", + "//internal/platform:mac_address", + "//internal/platform:types", + "//internal/platform:uuid", + "//internal/platform/flags:platform_flags", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", + "//internal/platform/implementation:wifi_utils", + "//internal/platform/implementation/shared:count_down_latch", + "//internal/platform/implementation/shared:posix_condition_variable", + "//internal/platform/implementation/shared:posix_mutex", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@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", + "@nlohmann_json//:json", + "@sdbus_cpp", + "@sdbus_cpp//:libsystemd", + ], ) # The existing target that other BUILD files select on; keep for compatibility. @@ -80,20 +81,20 @@ cc_library( name = "linux_platform_impl", srcs = [], hdrs = [], + visibility = ["//visibility:public"], deps = [ ":linux", ":types", ], - visibility = ["//visibility:public"], ) cc_library( name = "multi_thread_executor_hdrs", hdrs = ["multi_thread_executor.h"], - visibility = ["//visibility:public" ], + visibility = ["//visibility:public"], deps = [ - "//internal/platform/implementation:types" - ] + "//internal/platform/implementation:types", + ], ) cc_test( @@ -104,17 +105,27 @@ cc_test( deps = [ ":linux", ":types", - "//internal/platform/implementation:platform", "//internal/platform:base", + "//internal/platform/implementation:platform", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", - "@sdbus_cpp//:sdbus_cpp", + "@sdbus_cpp", "@sdbus_cpp//:libsystemd", ], ) +cc_library( + name = "crypto_impl", + srcs = ["crypto.cc"], + linkopts = ["-lcrypto"], # usually enough; -lssl not needed for hashing + deps = [ + "//internal/platform/implementation:types", + "@com_google_absl//absl/strings", + ], +) + cc_test( name = "multi_thread_executor_test", srcs = [ @@ -128,6 +139,5 @@ cc_test( "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", - ], ) diff --git a/internal/platform/implementation/linux/bluetooth_adapter.h b/internal/platform/implementation/linux/bluetooth_adapter.h index c6e74e10..28f5048d 100644 --- a/internal/platform/implementation/linux/bluetooth_adapter.h +++ b/internal/platform/implementation/linux/bluetooth_adapter.h @@ -24,6 +24,8 @@ #include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/linux/generated/bluez_adapter_client_glue.h" +#include "internal/platform/mac_address.h" +#include "internal/platform/implementation/bluetooth_classic.h" constexpr uint8_t kAndroidDiscoverableBluetoothNameMaxLength = 37; // bytes namespace nearby { @@ -35,7 +37,7 @@ class BluetoothAdapter : public api::BluetoothAdapter, public sdbus::ProxyInterf BluetoothAdapter(sdbus::IConnection& system_bus, const sdbus::ObjectPath& object_path): ProxyInterfaces( system_bus, - "org.bluez.Adap", object_path ) + "org.bluez", object_path ) { registerProxy(); }; @@ -55,7 +57,7 @@ class BluetoothAdapter : public api::BluetoothAdapter, public sdbus::ProxyInterf bool SetStatus(Status status) override; // Returns true if the BluetoothAdapter's current status is // Status::Value::kEnabled. - [[nodiscard]] bool IsEnabled() const override; + bool IsEnabled() const override; // Scan modes of a BluetoothAdapter, as described at // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode(). @@ -69,25 +71,25 @@ class BluetoothAdapter : public api::BluetoothAdapter, public sdbus::ProxyInterf // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode() // // Returns ScanMode::kUnknown on error. - [[nodiscard]] ScanMode GetScanMode() const override; + ScanMode GetScanMode() const override; // Synchronously sets the scan mode of the adapter, and returns true if the // operation was a success. bool SetScanMode(ScanMode scan_mode) override; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error - [[nodiscard]] std::string GetName() const override; + std::string GetName() const override; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) bool SetName(absl::string_view name) override; bool SetName(absl::string_view name, bool persist) override; // Returns BT MAC address assigned to this adapter. ABSL_DEPRECATED("Use GetAddress() instead.") - [[nodiscard]] std::string GetMacAddress() const override; + std::string GetMacAddress() const override; // Implementation for migration only. Once subclasses implement this, the // above GetMacAddress() can be removed. - [[nodiscard]] MacAddress GetAddress() const override { + MacAddress GetAddress() const override { std::string mac_address = GetMacAddress(); if (mac_address.empty()) { return {}; @@ -98,6 +100,25 @@ class BluetoothAdapter : public api::BluetoothAdapter, public sdbus::ProxyInterf } }; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. + class BluetoothDevice : public api::BluetoothDevice { + public: + ~BluetoothDevice() override = default; + + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() + std::string GetName() const override; + std::string GetMacAddress() const override; + MacAddress GetAddress() const override; + BluetoothAdapter& GetAdapter() { return adapter_; } + + private: + // Only BluetoothAdapter may instantiate BluetoothDevice. + friend class BluetoothAdapter; + + explicit BluetoothDevice(BluetoothAdapter* adapter); + + BluetoothAdapter& adapter_; + }; } // namespace linux } // namespace nearby diff --git a/internal/platform/implementation/linux/bluetooth_adapter_test.cc b/internal/platform/implementation/linux/bluetooth_adapter_test.cc index 72799d3e..7550aa77 100644 --- a/internal/platform/implementation/linux/bluetooth_adapter_test.cc +++ b/internal/platform/implementation/linux/bluetooth_adapter_test.cc @@ -35,7 +35,8 @@ constexpr absl::string_view kName = "Test Radio Name"; // validations to mirror the Windows test suite. TEST(BluetoothAdapter, DISABLED_SetStatusReturnsTrue) { - auto connection = sdbus::createConnection(); + auto connection = sdbus::createSystemBusConnection(); + connection -> enterEventLoopAsync(); sdbus::ObjectPath object_path{"/org/bluez/hci0"}; BluetoothAdapter adapter(*connection, object_path); @@ -45,7 +46,8 @@ TEST(BluetoothAdapter, DISABLED_SetStatusReturnsTrue) { } TEST(BluetoothAdapter, DISABLED_SetAndGetName) { - auto connection = sdbus::createConnection(); + auto connection = sdbus::createSystemBusConnection(); + connection -> enterEventLoopAsync(); sdbus::ObjectPath object_path{"/org/bluez/hci0"}; BluetoothAdapter adapter(*connection, object_path); @@ -59,7 +61,8 @@ TEST(BluetoothAdapter, DISABLED_SetAndGetName) { } TEST(BluetoothAdapter, DISABLED_GetMacAddressNotEmpty) { - auto connection = sdbus::createConnection(); + auto connection = sdbus::createSystemBusConnection(); + connection -> enterEventLoopAsync(); sdbus::ObjectPath object_path{"/org/bluez/hci0"}; BluetoothAdapter adapter(*connection, object_path); @@ -67,7 +70,8 @@ TEST(BluetoothAdapter, DISABLED_GetMacAddressNotEmpty) { } TEST(BluetoothAdapter, DISABLED_SetScanModeWhenEnabled) { - auto connection = sdbus::createConnection(); + auto connection = sdbus::createSystemBusConnection(); + connection -> enterEventLoopAsync(); sdbus::ObjectPath object_path{"/org/bluez/hci0"}; BluetoothAdapter adapter(*connection, object_path); diff --git a/internal/platform/implementation/linux/bluetooth_classic.cc b/internal/platform/implementation/linux/bluetooth_classic.cc new file mode 100644 index 00000000..e97266ed --- /dev/null +++ b/internal/platform/implementation/linux/bluetooth_classic.cc @@ -0,0 +1,277 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/implementation/linux/bluetooth_classic.h" + +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "internal/platform/implementation/g3/bluetooth_adapter.h" +#include "internal/platform/logging.h" +#include "internal/platform/mac_address.h" +#include "internal/platform/medium_environment.h" +#include "internal/platform/types.h" + +namespace nearby { +namespace linux { + +BluetoothDevice* BluetoothSocket::GetRemoteDevice() { + BluetoothSocket* remote_socket = + static_cast(GetRemoteSocket()); + if (remote_socket == nullptr || remote_socket->adapter_ == nullptr) { + return nullptr; + } + return &remote_socket->adapter_->GetDevice(); +} + +std::unique_ptr BluetoothServerSocket::Accept() { + absl::MutexLock lock(mutex_); + while (!closed_ && pending_sockets_.empty()) { + cond_.Wait(&mutex_); + } + // whether or not we were running in the wait loop, return early if closed. + if (closed_) return {}; + auto* remote_socket = + pending_sockets_.extract(pending_sockets_.begin()).value(); + CHECK(remote_socket); + auto local_socket = std::make_unique(adapter_); + local_socket->Connect(*remote_socket); + remote_socket->Connect(*local_socket); + cond_.SignalAll(); + return local_socket; +} + +bool BluetoothServerSocket::Connect(BluetoothSocket& socket) { + absl::MutexLock lock(mutex_); + if (closed_) return false; + if (socket.IsConnected()) { + LOG(ERROR) << "Failed to connect to BT server socket: already connected"; + return true; // already connected. + } + // add client socket to the pending list + pending_sockets_.emplace(&socket); + cond_.SignalAll(); + while (!socket.IsConnected()) { + cond_.Wait(&mutex_); + if (closed_) return false; + } + return true; +} + +void BluetoothServerSocket::SetCloseNotifier( + absl::AnyInvocable notifier) { + absl::MutexLock lock(mutex_); + close_notifier_ = std::move(notifier); +} + +BluetoothServerSocket::~BluetoothServerSocket() { + absl::MutexLock lock(mutex_); + DoClose(); +} + +Exception BluetoothServerSocket::Close() { + absl::MutexLock lock(mutex_); + return DoClose(); +} + +Exception BluetoothServerSocket::DoClose() { + bool should_notify = !closed_; + closed_ = true; + if (should_notify) { + cond_.SignalAll(); + if (close_notifier_) { + auto notifier = std::move(close_notifier_); + mutex_.unlock(); + // Notifier may contain calls to public API, and may cause deadlock, if + // mutex_ is held during the call. + notifier(); + mutex_.lock(); + } + } + return {Exception::kSuccess}; +} + +BluetoothPairing::BluetoothPairing(api::BluetoothDevice& remote_device) + : remote_device_(remote_device) {} + +BluetoothPairing::~BluetoothPairing() { + MediumEnvironment::Instance().ClearBluetoothDevicesForPairing(); +} + +bool BluetoothPairing::InitiatePairing( + api::BluetoothPairingCallback pairing_cb) { + return MediumEnvironment::Instance().InitiatePairing(&remote_device_, + std::move(pairing_cb)); +} + +bool BluetoothPairing::FinishPairing( + std::optional pin_code) { + return MediumEnvironment::Instance().FinishPairing(&remote_device_); +} + +bool BluetoothPairing::CancelPairing() { + return MediumEnvironment::Instance().CancelPairing(&remote_device_); +} + +bool BluetoothPairing::Unpair() { + return MediumEnvironment::Instance().SetPairingState(&remote_device_, false); +} + +bool BluetoothPairing::IsPaired() { + return MediumEnvironment::Instance().IsPaired(&remote_device_); +} + +BluetoothClassicMedium::BluetoothClassicMedium(api::BluetoothAdapter& adapter) + : adapter_(static_cast(&adapter)) { + adapter_->SetBluetoothClassicMedium(this); + auto& env = MediumEnvironment::Instance(); + env.RegisterBluetoothMedium(*this, GetAdapter()); +} + +BluetoothClassicMedium::~BluetoothClassicMedium() { + adapter_->SetBluetoothClassicMedium(nullptr); + auto& env = MediumEnvironment::Instance(); + env.UnregisterBluetoothMedium(*this); +} + +bool BluetoothClassicMedium::StartDiscovery(DiscoveryCallback callback) { + auto& env = MediumEnvironment::Instance(); + env.UpdateBluetoothMedium(*this, std::move(callback)); + return true; +} + +bool BluetoothClassicMedium::StopDiscovery() { + auto& env = MediumEnvironment::Instance(); + env.UpdateBluetoothMedium(*this, {}); + return true; +} + +std::unique_ptr BluetoothClassicMedium::ConnectToService( + api::BluetoothDevice& remote_device, const std::string& service_uuid, + CancellationFlag* cancellation_flag) { + LOG(INFO) << "G3 ConnectToService [self]: medium=" << this + << ", adapter=" << &GetAdapter() + << ", device=" << &GetAdapter().GetDevice(); + + // Find the device in the MediumEnvironment, so that injected devices are + // supported in tests. + api::BluetoothDevice* device = + MediumEnvironment::Instance().FindBluetoothDevice( + remote_device.GetAddress()); + if (device == nullptr) { + LOG(ERROR) << "G3 ConnectToService [peer]: device=" << &remote_device + << " not found"; + return {}; + } + + auto& adapter = down_cast(device)->GetAdapter(); + auto* medium = + down_cast(adapter.GetBluetoothClassicMedium()); + + if (!medium) return {}; // Adapter is not bound to medium. Bail out. + + BluetoothServerSocket* server_socket = nullptr; + LOG(INFO) << "G3 ConnectToService [peer]: medium=" << medium + << ", adapter=" << &adapter << ", device=" << &remote_device + << ", uuid=" << service_uuid; + // Then, find our server socket context in this medium. + { + absl::MutexLock medium_lock(medium->mutex_); + auto item = medium->sockets_.find(service_uuid); + server_socket = item != medium->sockets_.end() ? item->second : nullptr; + if (server_socket == nullptr) { + LOG(ERROR) << "Failed to find BT Server socket: uuid=" << service_uuid; + return {}; + } + } + + if (cancellation_flag->Cancelled()) { + LOG(ERROR) << "G3 Bluetooth Connect: Has been cancelled: " + "service_uuid=" + << service_uuid; + return {}; + } + + CancellationFlagListener listener(cancellation_flag, [&server_socket]() { + LOG(INFO) << "G3 Bluetooth Cancel Connect."; + if (server_socket != nullptr) server_socket->Close(); + }); + + auto socket = std::make_unique(&GetAdapter()); + // Finally, Request to connect to this socket. + if (!server_socket->Connect(*socket)) { + LOG(ERROR) << "Failed to connect to existing BT Server socket: uuid=" + << service_uuid; + return {}; + } + + if (cancellation_flag->Cancelled()) { + LOG(ERROR) << "G3 Bluetooth Connect: Has been cancelled after connected: " + "service_uuid=" + << service_uuid; + socket->Close(); + return {}; + } + + LOG(INFO) << "G3 ConnectToService: connected: socket=" << socket.get(); + return socket; +} + +std::unique_ptr +BluetoothClassicMedium::ListenForService(const std::string& service_name, + const std::string& service_uuid) { + auto socket = std::make_unique(GetAdapter()); + socket->SetCloseNotifier([this, uuid = service_uuid]() { + absl::MutexLock lock(mutex_); + sockets_.erase(uuid); + }); + LOG(INFO) << "Adding service: medium=" << this << ", uuid=" << service_uuid; + absl::MutexLock lock(mutex_); + sockets_.emplace(service_uuid, socket.get()); + return socket; +} + +std::unique_ptr BluetoothClassicMedium::CreatePairing( + api::BluetoothDevice& remote_device) { + return std::make_unique(remote_device); +} + +api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( + MacAddress mac_address) { + return MediumEnvironment::Instance().FindBluetoothDevice(mac_address); +} + +void BluetoothClassicMedium::AddObserver( + api::BluetoothClassicMedium::Observer* observer) { + MediumEnvironment::Instance().AddObserver(observer); +} + +void BluetoothClassicMedium::RemoveObserver( + api::BluetoothClassicMedium::Observer* observer) { + MediumEnvironment::Instance().RemoveObserver(observer); +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/bluetooth_classic.h b/internal/platform/implementation/linux/bluetooth_classic.h new file mode 100644 index 00000000..fef9dc3e --- /dev/null +++ b/internal/platform/implementation/linux/bluetooth_classic.h @@ -0,0 +1,222 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_H_ +#define PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "internal/platform/implementation/g3/bluetooth_adapter.h" +#include "internal/platform/implementation/g3/socket_base.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/mac_address.h" +#include "internal/platform/output_stream.h" + +namespace nearby { +namespace linux { + +// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. +class BluetoothSocket : public api::BluetoothSocket, public SocketBase { + public: + BluetoothSocket() = default; + explicit BluetoothSocket(BluetoothAdapter* adapter) : adapter_(adapter) {} + + // Returns the InputStream of this connected BluetoothSocket. + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } + + // Returns the OutputStream of this connected BluetoothSocket. + // This stream is for local side to write. + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } + + // Closes both input and output streams, marks Socket as closed. + // After this call object should be treated as not connected. + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() override { return SocketBase::Close(); } + + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() + // Returns valid BluetoothDevice pointer if there is a connection, and + // nullptr otherwise. + BluetoothDevice* GetRemoteDevice() override; + + private: + BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. +}; + +// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. +class BluetoothServerSocket : public api::BluetoothServerSocket { + public: + explicit BluetoothServerSocket(BluetoothAdapter& adapter) + : adapter_(&adapter) {} + ~BluetoothServerSocket() override; + + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // On success, returns connected socket, ready to exchange data. + // Returns nullptr on error. + // Once error is reported, it is permanent, and ServerSocket has to be closed. + // + // Called by the server side of a connection. + // Returns BluetoothSocket to the server side. + // If not null, returned socket is connected to its remote (client-side) peer. + std::unique_ptr Accept() override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Blocks until either: + // - connection is available, or + // - server socket is closed, or + // - error happens. + // + // Called by the client side of a connection. + // socket is an initialized BluetoothSocket, associated with a client + // BluetoothAdapter. + // Returns true, if socket is successfully connected. + bool Connect(BluetoothSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_); + + // Called by the server side of a connection before passing ownership of + // BluetoothServerSocker to user, to track validity of a pointer to this + // server socket, + void SetCloseNotifier(absl::AnyInvocable notifier) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + // Calls close_notifier if it was previously set, and marks socket as closed. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + private: + Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + absl::Mutex mutex_; + absl::CondVar cond_; + BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. + absl::flat_hash_set pending_sockets_ + ABSL_GUARDED_BY(mutex_); + absl::AnyInvocable close_notifier_ ABSL_GUARDED_BY(mutex_); + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +// A concrete implementation for BluetoothPairing. +class BluetoothPairing : public api::BluetoothPairing { + public: + explicit BluetoothPairing(api::BluetoothDevice& remote_device); + BluetoothPairing(const BluetoothPairing&) = default; + BluetoothPairing& operator=(const BluetoothPairing&) = default; + ~BluetoothPairing() override; + + bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) override; + bool FinishPairing(std::optional pin_code) override; + bool CancelPairing() override; + bool Unpair() override; + bool IsPaired() override; + + private: + api::BluetoothDevice& remote_device_; +}; + +// Container of operations that can be performed over the Bluetooth Classic +// medium. +class BluetoothClassicMedium : public api::BluetoothClassicMedium { + public: + explicit BluetoothClassicMedium(api::BluetoothAdapter& adapter); + ~BluetoothClassicMedium() override; + + // NOTE(DiscoveryCallback): + // BluetoothDevice is a proxy object created as a result of BT discovery. + // Its lifetime spans between calls to device_discovered_cb and + // device_lost_cb. + // It is safe to use BluetoothDevice in device_discovered_cb() callback + // and at any time afterwards, until device_lost_cb() is called. + // It is not safe to use BluetoothDevice after returning from + // device_lost_cb() callback. + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery() + // + // Returns true once the process of discovery has been initiated. + bool StartDiscovery(DiscoveryCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery() + // + // Returns true once discovery is well and truly stopped; after this returns, + // there must be no more invocations of the DiscoveryCallback passed in to + // StartDiscovery(). + bool StopDiscovery() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Connects to existing remote BT service. + // + // A combination of + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord + // followed by + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect(). + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // On success, returns a new BluetoothSocket. + // On error, returns nullptr. + std::unique_ptr ConnectToService( + api::BluetoothDevice& remote_device, const std::string& service_uuid, + CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_); + + BluetoothAdapter& GetAdapter() { return *adapter_; } + + // Creates BT service, and begins listening for remote attempts to connect. + // + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // Returns nullptr on error. + std::unique_ptr ListenForService( + const std::string& service_name, const std::string& service_uuid) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + api::BluetoothDevice& remote_device) override; + + api::BluetoothDevice* GetRemoteDevice(MacAddress mac_address) override; + + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + + private: + absl::Mutex mutex_; + BluetoothAdapter* adapter_; // Our device adapter; read-only. + absl::flat_hash_map sockets_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_G3_BLUETOOTH_CLASSIC_H_ diff --git a/internal/platform/implementation/linux/crypto.cc b/internal/platform/implementation/linux/crypto.cc new file mode 100644 index 00000000..334aeceb --- /dev/null +++ b/internal/platform/implementation/linux/crypto.cc @@ -0,0 +1,57 @@ +// ...existing code... +// filepath: /workspace/internal/platform/implementation/linux/crypto.cc +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/implementation/crypto.h" + +#include +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" +#include + +// Function implementations for platform/api/crypto.h. + +namespace nearby { + +// Initialize global crypto state. +void Crypto::Init() {} + +static ByteArray Hash(absl::string_view input, const EVP_MD* algo) { + unsigned int md_out_size = EVP_MAX_MD_SIZE; + uint8_t digest_buffer[EVP_MAX_MD_SIZE]; + if (input.empty()) return {}; + + if (!EVP_Digest(input.data(), input.size(), digest_buffer, &md_out_size, algo, + nullptr)) + return {}; + + return ByteArray{reinterpret_cast(digest_buffer), md_out_size}; +} + +// Return MD5 hash of input. +ByteArray Crypto::Md5(absl::string_view input) { + return Hash(input, EVP_md5()); +} + +// Return SHA256 hash of input. +ByteArray Crypto::Sha256(absl::string_view input) { + return Hash(input, EVP_sha256()); +} + +} // namespace nearby + +// ...existing code... diff --git a/internal/platform/implementation/linux/generated/bluez_client_glue.h b/internal/platform/implementation/linux/generated/bluez_client_glue.h new file mode 100644 index 00000000..44e253b3 --- /dev/null +++ b/internal/platform/implementation/linux/generated/bluez_client_glue.h @@ -0,0 +1,46 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_bluez_client_glue_h__proxy__H__ +#define __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_bluez_client_glue_h__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace DBus { + +class ObjectManager_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; + +protected: + ObjectManager_proxy(sdbus::IProxy& proxy) + : proxy_(&proxy) + { + proxy_->uponSignal("InterfacesAdded").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& object_path, const std::map>& interfaces_and_properties){ this->onInterfacesAdded(object_path, interfaces_and_properties); }); + proxy_->uponSignal("InterfacesRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& object_path, const std::vector& interfaces){ this->onInterfacesRemoved(object_path, interfaces); }); + } + + ObjectManager_proxy(const ObjectManager_proxy&) = delete; + ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete; + ObjectManager_proxy(ObjectManager_proxy&&) = default; + ObjectManager_proxy& operator=(ObjectManager_proxy&&) = default; + + ~ObjectManager_proxy() = default; + + virtual void onInterfacesAdded(const sdbus::ObjectPath& object_path, const std::map>& interfaces_and_properties) = 0; + virtual void onInterfacesRemoved(const sdbus::ObjectPath& object_path, const std::vector& interfaces) = 0; + +private: + sdbus::IProxy* proxy_; +}; + +}}} // namespaces + +#endif diff --git a/internal/platform/implementation/linux/generated/bluez_device_client_glue.h b/internal/platform/implementation/linux/generated/bluez_device_client_glue.h new file mode 100644 index 00000000..76c69bb1 --- /dev/null +++ b/internal/platform/implementation/linux/generated/bluez_device_client_glue.h @@ -0,0 +1,225 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_bluez_device_client_glue_h__proxy__H__ +#define __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_bluez_device_client_glue_h__proxy__H__ + +#include +#include +#include + +namespace org { +namespace bluez { + +class Device1_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.bluez.Device1"; + +protected: + Device1_proxy(sdbus::IProxy& proxy) + : proxy_(&proxy) + { + proxy_->uponSignal("Disconnected").onInterface(INTERFACE_NAME).call([this](const std::string& name, const std::string& message){ this->onDisconnected(name, message); }); + } + + Device1_proxy(const Device1_proxy&) = delete; + Device1_proxy& operator=(const Device1_proxy&) = delete; + Device1_proxy(Device1_proxy&&) = default; + Device1_proxy& operator=(Device1_proxy&&) = default; + + ~Device1_proxy() = default; + + virtual void onDisconnected(const std::string& name, const std::string& message) = 0; + +public: + void Disconnect() + { + proxy_->callMethod("Disconnect").onInterface(INTERFACE_NAME); + } + + void Connect() + { + proxy_->callMethod("Connect").onInterface(INTERFACE_NAME); + } + + void ConnectProfile(const std::string& UUID) + { + proxy_->callMethod("ConnectProfile").onInterface(INTERFACE_NAME).withArguments(UUID); + } + + void DisconnectProfile(const std::string& UUID) + { + proxy_->callMethod("DisconnectProfile").onInterface(INTERFACE_NAME).withArguments(UUID); + } + + void Pair() + { + proxy_->callMethod("Pair").onInterface(INTERFACE_NAME); + } + + void CancelPairing() + { + proxy_->callMethod("CancelPairing").onInterface(INTERFACE_NAME); + } + +public: + std::string Address() + { + return proxy_->getProperty("Address").onInterface(INTERFACE_NAME); + } + + std::string AddressType() + { + return proxy_->getProperty("AddressType").onInterface(INTERFACE_NAME); + } + + std::string Name() + { + return proxy_->getProperty("Name").onInterface(INTERFACE_NAME); + } + + std::string Alias() + { + return proxy_->getProperty("Alias").onInterface(INTERFACE_NAME); + } + + void Alias(const std::string& value) + { + proxy_->setProperty("Alias").onInterface(INTERFACE_NAME).toValue(value); + } + + uint32_t Class() + { + return proxy_->getProperty("Class").onInterface(INTERFACE_NAME); + } + + uint16_t Appearance() + { + return proxy_->getProperty("Appearance").onInterface(INTERFACE_NAME); + } + + std::string Icon() + { + return proxy_->getProperty("Icon").onInterface(INTERFACE_NAME); + } + + bool Paired() + { + return proxy_->getProperty("Paired").onInterface(INTERFACE_NAME); + } + + bool Bonded() + { + return proxy_->getProperty("Bonded").onInterface(INTERFACE_NAME); + } + + bool Trusted() + { + return proxy_->getProperty("Trusted").onInterface(INTERFACE_NAME); + } + + void Trusted(const bool& value) + { + proxy_->setProperty("Trusted").onInterface(INTERFACE_NAME).toValue(value); + } + + bool Blocked() + { + return proxy_->getProperty("Blocked").onInterface(INTERFACE_NAME); + } + + void Blocked(const bool& value) + { + proxy_->setProperty("Blocked").onInterface(INTERFACE_NAME).toValue(value); + } + + bool LegacyPairing() + { + return proxy_->getProperty("LegacyPairing").onInterface(INTERFACE_NAME); + } + + bool CablePairing() + { + return proxy_->getProperty("CablePairing").onInterface(INTERFACE_NAME); + } + + int16_t RSSI() + { + return proxy_->getProperty("RSSI").onInterface(INTERFACE_NAME); + } + + bool Connected() + { + return proxy_->getProperty("Connected").onInterface(INTERFACE_NAME); + } + + std::vector UUIDs() + { + return proxy_->getProperty("UUIDs").onInterface(INTERFACE_NAME); + } + + std::string Modalias() + { + return proxy_->getProperty("Modalias").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Adapter() + { + return proxy_->getProperty("Adapter").onInterface(INTERFACE_NAME); + } + + std::map ManufacturerData() + { + return proxy_->getProperty("ManufacturerData").onInterface(INTERFACE_NAME); + } + + std::map ServiceData() + { + return proxy_->getProperty("ServiceData").onInterface(INTERFACE_NAME); + } + + int16_t TxPower() + { + return proxy_->getProperty("TxPower").onInterface(INTERFACE_NAME); + } + + bool ServicesResolved() + { + return proxy_->getProperty("ServicesResolved").onInterface(INTERFACE_NAME); + } + + std::vector AdvertisingFlags() + { + return proxy_->getProperty("AdvertisingFlags").onInterface(INTERFACE_NAME); + } + + std::map AdvertisingData() + { + return proxy_->getProperty("AdvertisingData").onInterface(INTERFACE_NAME); + } + + bool WakeAllowed() + { + return proxy_->getProperty("WakeAllowed").onInterface(INTERFACE_NAME); + } + + void WakeAllowed(const bool& value) + { + proxy_->setProperty("WakeAllowed").onInterface(INTERFACE_NAME).toValue(value); + } + + std::map> Sets() + { + return proxy_->getProperty("Sets").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy* proxy_; +}; + +}} // namespaces + +#endif diff --git a/internal/platform/implementation/linux/generated/org.bluez.Device1.xml b/internal/platform/implementation/linux/generated/org.bluez.Device1.xml new file mode 100644 index 00000000..a56ec7dc --- /dev/null +++ b/internal/platform/implementation/linux/generated/org.bluez.Device1.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/platform/implementation/linux/generated/org.bluez.xml b/internal/platform/implementation/linux/generated/org.bluez.xml new file mode 100644 index 00000000..4071a17c --- /dev/null +++ b/internal/platform/implementation/linux/generated/org.bluez.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/internal/platform/implementation/linux/platform.cc b/internal/platform/implementation/linux/platform.cc index ccc1fb3a..522641fd 100644 --- a/internal/platform/implementation/linux/platform.cc +++ b/internal/platform/implementation/linux/platform.cc @@ -113,6 +113,7 @@ std::unique_ptr ImplementationPlatform::CreateAwdlMedium() { return std::unique_ptr ImplementationPlatform::CreateBluetoothAdapter() { static auto connection = sdbus::createConnection(); + connection -> enterEventLoopAsync(); return std::make_unique(*connection, "/org/bluez/hci0"); } std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium(BluetoothAdapter&) { return nullptr; } diff --git a/internal/platform/implementation/linux/system_clock.cc b/internal/platform/implementation/linux/system_clock.cc new file mode 100644 index 00000000..c6a07f98 --- /dev/null +++ b/internal/platform/implementation/linux/system_clock.cc @@ -0,0 +1,49 @@ +// ...existing code... +// filepath: /workspace/internal/platform/implementation/linux/system_clock.cc +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/implementation/system_clock.h" + +#include + +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "internal/platform/exception.h" + +namespace nearby { + +// Initialize global system state. +void SystemClock::Init() {} + +// Returns current elapsed (monotonic) time. +absl::Time SystemClock::ElapsedRealtime() { + const auto now = std::chrono::steady_clock::now().time_since_epoch(); + const auto nanos = + std::chrono::duration_cast(now).count(); + + // Represent monotonic time as an absl::Time value. + // (Anchor is arbitrary; only differences matter for elapsed time.) + return absl::FromUnixNanos(static_cast(nanos)); +} + +// Pauses current thread for the specified duration. +Exception SystemClock::Sleep(absl::Duration duration) { + absl::SleepFor(duration); + return {Exception::kSuccess}; +} + +} // namespace nearby + +