// 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_PUBLIC_BLUETOOTH_CLASSIC_H_ #define PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_ #include #include #include "platform/api/bluetooth_classic.h" #include "platform/api/platform.h" #include "platform/base/byte_array.h" #include "platform/base/cancellation_flag.h" #include "platform/base/exception.h" #include "platform/base/input_stream.h" #include "platform/base/listeners.h" #include "platform/base/output_stream.h" #include "platform/public/bluetooth_adapter.h" #include "platform/public/mutex.h" #include "absl/container/flat_hash_map.h" namespace location { namespace nearby { // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. class BluetoothSocket final { public: BluetoothSocket() = default; BluetoothSocket(const BluetoothSocket&) = default; BluetoothSocket& operator=(const BluetoothSocket&) = default; explicit BluetoothSocket(std::unique_ptr socket) : impl_(socket.release()) {} ~BluetoothSocket() = default; // Returns the InputStream of this connected BluetoothSocket. InputStream& GetInputStream() { return impl_->GetInputStream(); } // Returns the OutputStream of this connected BluetoothSocket. OutputStream& GetOutputStream() { return impl_->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() { return impl_->Close(); } // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() BluetoothDevice GetRemoteDevice() { return BluetoothDevice(impl_->GetRemoteDevice()); } // Returns true if a socket is usable. If this method returns false, // it is not safe to call any other method. // NOTE(socket validity): // Socket created by a default public constructor is not valid, because // it is missing platform implementation. // The only way to obtain a valid socket is through connection, such as // an object returned by either BluetoothClassicMedium::ConnectTotService or // BluetoothServerSocket::Accept(). // These methods may also return an invalid socket if connection failed for // any reason. bool IsValid() const { return impl_ != nullptr; } // Returns reference to platform implementation. // This is used to communicate with platform code, and for debugging purposes. // Returned reference will remain valid for while BluetoothSocket object is // itself valid. Typically BluetoothSocket lifetime matches duration of the // connection, and is controlled by end user, since they hold the instance. api::BluetoothSocket& GetImpl() { return *impl_; } private: std::shared_ptr impl_; }; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. class BluetoothServerSocket final { public: BluetoothServerSocket() = default; BluetoothServerSocket(const BluetoothServerSocket&) = default; BluetoothServerSocket& operator=(const BluetoothServerSocket&) = default; ~BluetoothServerSocket() = default; explicit BluetoothServerSocket( std::unique_ptr socket) : impl_(std::move(socket)) {} // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept() // // 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. BluetoothSocket Accept() { return BluetoothSocket(impl_->Accept()); } // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close() // // Returns Exception::kIo on error, Exception::kSuccess otherwise. Exception Close() { return impl_->Close(); } bool IsValid() const { return impl_ != nullptr; } api::BluetoothServerSocket& GetImpl() { return *impl_; } private: std::shared_ptr impl_; }; // Container of operations that can be performed over the Bluetooth Classic // medium. class BluetoothClassicMedium final { public: using Platform = api::ImplementationPlatform; struct 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. std::function device_discovered_cb = DefaultCallback(); std::function device_name_changed_cb = DefaultCallback(); std::function device_lost_cb = DefaultCallback(); }; struct DeviceDiscoveryInfo { BluetoothDevice device; }; explicit BluetoothClassicMedium(BluetoothAdapter& adapter) : impl_(Platform::CreateBluetoothClassicMedium(adapter.GetImpl())), adapter_(adapter) {} ~BluetoothClassicMedium(); // 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); // 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(); // 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. // // Returns a new BluetoothSocket. On Success, BluetoothSocket::IsValid() // returns true. BluetoothSocket ConnectToService(BluetoothDevice& remote_device, const std::string& service_uuid, CancellationFlag* cancellation_flag); // 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 a new BluetoothServerSocket. // On Success, BluetoothServerSocket::IsValid() returns true. BluetoothServerSocket ListenForService(const std::string& service_name, const std::string& service_uuid) { return BluetoothServerSocket( impl_->ListenForService(service_name, service_uuid)); } bool IsValid() const { return impl_ != nullptr; } api::BluetoothClassicMedium& GetImpl() { return *impl_; } BluetoothAdapter& GetAdapter() { return adapter_; } std::string GetMacAddress() const { return adapter_.GetMacAddress(); } BluetoothDevice GetRemoteDevice(const std::string& mac_address) { return BluetoothDevice(impl_->GetRemoteDevice(mac_address)); } private: Mutex mutex_; std::unique_ptr impl_; BluetoothAdapter& adapter_; absl::flat_hash_map> devices_ ABSL_GUARDED_BY(mutex_); DiscoveryCallback discovery_callback_ ABSL_GUARDED_BY(mutex_); bool discovery_enabled_ ABSL_GUARDED_BY(mutex_) = false; }; } // namespace nearby } // namespace location #endif // PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_