| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_MANAGER_H_ |
| #define DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_MANAGER_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/time/time.h" |
| #include "dbus/object_manager.h" |
| #include "device/bluetooth/bluetooth_export.h" |
| #include "device/bluetooth/floss/floss_version.h" |
| |
| namespace base { |
| class Thread; |
| } // namespace base |
| |
| namespace dbus { |
| class Bus; |
| class Response; |
| class ErrorResponse; |
| } // namespace dbus |
| |
| namespace floss { |
| |
| class FlossAdapterClient; |
| class FlossAdvertiserClient; |
| class FlossBatteryManagerClient; |
| class FlossDBusManager; |
| class FlossDBusManagerSetter; |
| class FlossGattManagerClient; |
| class FlossLEScanClient; |
| class FlossLoggingClient; |
| class FlossManagerClient; |
| class FlossBluetoothTelephonyClient; |
| class FlossSocketManager; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| class FlossAdminClient; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // The bundle of all DBus clients used by FlossDBusManager. The bundle is used |
| // to control the correct ordering of creation and deletion of clients before |
| // shutting down the system bus. |
| class DEVICE_BLUETOOTH_EXPORT FlossClientBundle { |
| public: |
| FlossClientBundle(const FlossClientBundle&) = delete; |
| FlossClientBundle& operator=(const FlossClientBundle&) = delete; |
| |
| explicit FlossClientBundle(bool use_stubs); |
| ~FlossClientBundle(); |
| |
| FlossManagerClient* manager_client() { return manager_client_.get(); } |
| |
| FlossAdapterClient* adapter_client() { return adapter_client_.get(); } |
| |
| FlossGattManagerClient* gatt_manager_client() { |
| return gatt_manager_client_.get(); |
| } |
| |
| FlossSocketManager* socket_manager() { return socket_manager_.get(); } |
| |
| FlossLEScanClient* lescan_client() { return lescan_client_.get(); } |
| |
| FlossLoggingClient* logging_client() { return logging_client_.get(); } |
| |
| FlossAdvertiserClient* advertiser_client() { |
| return advertiser_client_.get(); |
| } |
| #if BUILDFLAG(IS_CHROMEOS) |
| FlossAdminClient* admin_client() { return admin_client_.get(); } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| FlossBatteryManagerClient* battery_manager_client() { |
| return battery_manager_client_.get(); |
| } |
| |
| FlossBluetoothTelephonyClient* bluetooth_telephony_client() { |
| return bluetooth_telephony_client_.get(); |
| } |
| |
| private: |
| friend FlossDBusManagerSetter; |
| friend FlossDBusManager; |
| |
| void ResetAdapterClients(); |
| |
| bool use_stubs_; |
| std::unique_ptr<FlossManagerClient> manager_client_; |
| std::unique_ptr<FlossAdapterClient> adapter_client_; |
| std::unique_ptr<FlossGattManagerClient> gatt_manager_client_; |
| std::unique_ptr<FlossSocketManager> socket_manager_; |
| std::unique_ptr<FlossLEScanClient> lescan_client_; |
| std::unique_ptr<FlossLoggingClient> logging_client_; |
| std::unique_ptr<FlossAdvertiserClient> advertiser_client_; |
| std::unique_ptr<FlossBatteryManagerClient> battery_manager_client_; |
| std::unique_ptr<FlossBluetoothTelephonyClient> bluetooth_telephony_client_; |
| #if BUILDFLAG(IS_CHROMEOS) |
| std::unique_ptr<FlossAdminClient> admin_client_; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| }; |
| |
| // FlossDBusManager manages the lifetimes of D-Bus connections and clients. It |
| // ensures the proper ordering of shutdowns for the D-Bus thread, connections |
| // and clients. |
| // |
| // While similar to the BluezDBusManager, it requires totally different client |
| // implementations since the Floss dbus apis are drastically different. Thus, it |
| // doesn't make sense to share a common implementation between the two. |
| class DEVICE_BLUETOOTH_EXPORT FlossDBusManager |
| : public dbus::ObjectManager::Interface { |
| public: |
| class ClientInitializer { |
| public: |
| ClientInitializer(base::OnceClosure on_ready, int client_count); |
| ~ClientInitializer(); |
| |
| static std::unique_ptr<ClientInitializer> CreateWithTimeout( |
| base::OnceClosure on_ready, |
| int client_count, |
| base::TimeDelta timeout) { |
| std::unique_ptr<ClientInitializer> self = |
| std::make_unique<ClientInitializer>(std::move(on_ready), |
| client_count); |
| self->ScheduleTimeout(timeout); |
| |
| return self; |
| } |
| |
| // Grab closure to indicate client is ready and decrement expected closure |
| // count. |
| base::OnceClosure CreateReadyClosure() { |
| DCHECK(expected_closure_count_ > 0); |
| |
| --expected_closure_count_; |
| return base::BindOnce(&ClientInitializer::OnReady, |
| weak_ptr_factory_.GetWeakPtr()); |
| } |
| |
| void OnReady() { |
| DCHECK(pending_client_ready_ > 0); |
| |
| pending_client_ready_--; |
| if (pending_client_ready_ == 0 && on_ready_) { |
| DCHECK(expected_closure_count_ == 0); |
| std::move(on_ready_).Run(); |
| } |
| } |
| |
| void OnTimeout() { |
| if (pending_client_ready_ > 0) { |
| LOG(WARNING) << "ClientInitializer timed out with pending clients=" |
| << pending_client_ready_; |
| } |
| |
| if (on_ready_) { |
| std::move(on_ready_).Run(); |
| } |
| } |
| |
| bool Finished() { return on_ready_.is_null(); } |
| |
| private: |
| void ScheduleTimeout(base::TimeDelta timeout) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&ClientInitializer::OnTimeout, |
| weak_ptr_factory_.GetWeakPtr()), |
| timeout); |
| } |
| |
| int expected_closure_count_; |
| int pending_client_ready_; |
| base::OnceClosure on_ready_; |
| |
| base::WeakPtrFactory<ClientInitializer> weak_ptr_factory_{this}; |
| }; |
| |
| // Initializes the global instance with a real client. Must be called before |
| // any calls to Get(). We explicitly initialize and shutdown the global object |
| // rather than making it a Singleton to ensure clean startup and shutdown. |
| static void Initialize(dbus::Bus* system_bus); |
| |
| // Initializes the global instance with a fake client |
| // TODO(b/193712484) - Implement this. Only added here to fix debug build. |
| static void InitializeFake(); |
| |
| // Returns a FlossDBusManagerSetter instance that allows tests to |
| // replace individual D-Bus clients with their own implementations. |
| // Also initializes the main FlossDBusManager for testing if necessary. |
| static std::unique_ptr<FlossDBusManagerSetter> GetSetterForTesting(); |
| |
| // Returns true if FlossDBusManager has been initialized. Call this to |
| // avoid initializing + shutting down FlossDBusManager more than once. |
| static bool IsInitialized(); |
| |
| // Destroys the global instance. This will clean up all the clients managed by |
| // the global instance. |
| static void Shutdown(); |
| |
| // Gets the global instance. Initialize() must be called first. |
| static FlossDBusManager* Get(); |
| |
| // Adapters are indexed from 0-N (corresponding to hci0..hciN). Use negative |
| // value to represent invalid adapter. |
| static const int kInvalidAdapter; |
| |
| // Timeout to wait for clients to become ready. |
| static const int kClientReadyTimeoutMs; |
| // Timeout to wait for bluetooth service to become ready. |
| static const int kWaitServiceTimeoutMs; |
| |
| FlossDBusManager(const FlossDBusManager&) = delete; |
| FlossDBusManager& operator=(const FlossDBusManager&) = delete; |
| |
| // Calls |callback| once we know whether Object Manager is supported or not. |
| // If the object manager support is already known when this is called, |
| // |callback| is run right away and this function returns true. Otherwise, it |
| // will return false. |
| bool CallWhenObjectManagerSupportIsKnown(base::OnceClosure callback); |
| |
| // Returns true if Object Manager is support is known. |
| bool IsObjectManagerSupportKnown() const { |
| return object_manager_support_known_; |
| } |
| |
| // Returns true if Object Manager is supported. |
| bool IsObjectManagerSupported() const { |
| return object_manager_supported_ && mgmt_client_present_; |
| } |
| |
| // Shuts down the existing adapter clients and initializes a new set for the |
| // given adapter. When the new adapter clients are ready, calls the |on_ready| |
| // callback. |
| void SwitchAdapter(int adapter, base::OnceClosure on_ready); |
| |
| // Checks whether an adapter is currently enabled and being used. |
| bool HasActiveAdapter() const; |
| |
| // Get the active adapter. |
| int GetActiveAdapter() const; |
| |
| // Checks whether the necessary clients are ready. This will happen |
| // asynchronously and may take multiple seconds during tests. |
| bool AreClientsReady() const; |
| |
| // Returns system bus pointer (owned by FlossDBusThreadManager). |
| dbus::Bus* GetSystemBus() const { return bus_; } |
| |
| // Gets Floss API version. |
| base::Version GetFlossApiVersion() const { return version_; } |
| |
| // All returned objects are owned by FlossDBusManager. Do not use these |
| // pointers after FlossDBusManager has been shut down. |
| FlossAdapterClient* GetAdapterClient(); |
| FlossAdvertiserClient* GetAdvertiserClient(); |
| FlossBatteryManagerClient* GetBatteryManagerClient(); |
| FlossGattManagerClient* GetGattManagerClient(); |
| FlossLEScanClient* GetLEScanClient(); |
| FlossLoggingClient* GetLoggingClient(); |
| FlossManagerClient* GetManagerClient(); |
| FlossBluetoothTelephonyClient* GetBluetoothTelephonyClient(); |
| FlossSocketManager* GetSocketManager(); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| FlossAdminClient* GetAdminClient(); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| protected: |
| friend class FlossDBusManagerTest; |
| // dbus::ObjectManager::Interface overrides |
| dbus::PropertySet* CreateProperties( |
| dbus::ObjectProxy* object_proxy, |
| const dbus::ObjectPath& object_path, |
| const std::string& interface_name) override; |
| |
| void ObjectAdded(const dbus::ObjectPath& object_path, |
| const std::string& interface_name) override; |
| void ObjectRemoved(const dbus::ObjectPath& object_path, |
| const std::string& interface_name) override; |
| |
| // Keep track of the object manager so we can keep track of when the manager |
| // disappears. Managed by the bus object (do not delete). |
| raw_ptr<dbus::ObjectManager> object_manager_ = nullptr; |
| |
| private: |
| friend class FlossDBusManagerSetter; |
| |
| // Creates a global instance of FlossDBusManager. Cannot be called more than |
| // once. |
| static void CreateGlobalInstance(dbus::Bus* bus, bool use_stubs); |
| |
| FlossDBusManager(dbus::Bus* bus, bool use_stubs); |
| ~FlossDBusManager() override; |
| |
| void OnObjectManagerSupported(dbus::Response* response); |
| void OnObjectManagerNotSupported(dbus::ErrorResponse* response); |
| void OnObjectManagerResponse(dbus::Response* response, |
| dbus::ErrorResponse* error_response); |
| void OnManagerClientInitComplete(); |
| void OnManagerServiceAvailable(bool is_available); |
| void OnWaitServiceAvailableTimeout(); |
| |
| // Initializes the manager client |
| void InitializeManagerClient(); |
| |
| // Initializes all currently stored DBusClients with the system bus and |
| // performs additional setup for a specific adapter. Once all clients are |
| // ready, calls the |on_ready| callback. |
| void InitializeAdapterClients(int adapter, base::OnceClosure on_ready); |
| |
| void InitAdapterClientsIfReady(); |
| void InitAdapterLoggingClientsIfReady(); |
| #if BUILDFLAG(IS_CHROMEOS) |
| void InitAdminClientsIfReady(); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| void InitBatteryClientsIfReady(); |
| void InitTelephonyClientsIfReady(); |
| void InitGattClientsIfReady(); |
| void InitSocketManagerClientsIfReady(); |
| |
| void SetAllClientsPresentForTesting(); |
| |
| // System bus instance (owned by FlossDBusThreadManager). |
| raw_ptr<dbus::Bus> bus_; |
| |
| // Bundle together all Floss clients to be initialized and shutdown in |
| // a specified order. |
| std::unique_ptr<FlossClientBundle> client_bundle_ = nullptr; |
| |
| // FlossDBusManager depends on object manager support to know when the manager |
| // object has been added (via the InterfaceAdded signal) so it can detect if |
| // an adapter is present. This callback gets called after GetManagedObjects |
| // returns a response (an array or an error). |
| base::OnceClosure object_manager_support_known_callback_; |
| bool object_manager_support_known_ = false; |
| bool object_manager_supported_ = false; |
| |
| // Whether the manager client has been initialized successfully. |
| bool mgmt_client_present_ = false; |
| |
| bool adapter_interface_present_ = false; |
| bool adapter_logging_interface_present_ = false; |
| #if BUILDFLAG(IS_CHROMEOS) |
| bool admin_interface_present_ = false; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| bool battery_interface_present_ = false; |
| bool telephony_interface_present_ = false; |
| bool gatt_interface_present_ = false; |
| bool socket_manager_interface_present_ = false; |
| base::Time instance_created_time_; |
| |
| // Currently active Bluetooth adapter |
| int active_adapter_ = kInvalidAdapter; |
| |
| // Floss API version exported by Floss daemon or |
| // specified by a test stub for unit tests. |
| base::Version version_; |
| |
| // Callback for when adapter clients are ready after init. |
| std::unique_ptr<ClientInitializer> client_on_ready_; |
| |
| base::WeakPtrFactory<FlossDBusManager> weak_ptr_factory_{this}; |
| }; |
| |
| class DEVICE_BLUETOOTH_EXPORT FlossDBusManagerSetter { |
| public: |
| void SetFlossAdapterClient(std::unique_ptr<FlossAdapterClient> client); |
| void SetFlossAdvertiserClient(std::unique_ptr<FlossAdvertiserClient> client); |
| void SetFlossBatteryManagerClient( |
| std::unique_ptr<FlossBatteryManagerClient> client); |
| void SetFlossGattManagerClient( |
| std::unique_ptr<FlossGattManagerClient> client); |
| void SetFlossLEScanClient(std::unique_ptr<FlossLEScanClient> client); |
| void SetFlossLoggingClient(std::unique_ptr<FlossLoggingClient> client); |
| void SetFlossManagerClient(std::unique_ptr<FlossManagerClient> client); |
| void SetFlossBluetoothTelephonyClient( |
| std::unique_ptr<FlossBluetoothTelephonyClient> client); |
| void SetFlossSocketManager(std::unique_ptr<FlossSocketManager> manager); |
| #if BUILDFLAG(IS_CHROMEOS) |
| void SetFlossAdminClient(std::unique_ptr<FlossAdminClient> client); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| }; |
| |
| // FlossDBusThreadManager manages the D-Bus thread, the thread dedicated to |
| // handling asynchronous D-Bus operations. |
| class DEVICE_BLUETOOTH_EXPORT FlossDBusThreadManager { |
| public: |
| FlossDBusThreadManager(const FlossDBusThreadManager&) = delete; |
| FlossDBusThreadManager& operator=(const FlossDBusThreadManager&) = delete; |
| |
| // Sets the global instance. Must be called before any calls to Get(). |
| // We explicitly initialize and shut down the global object, rather than |
| // making it a Singleton, to ensure clean startup and shutdown. |
| static void Initialize(); |
| |
| // Destroys the global instance. |
| static void Shutdown(); |
| |
| // Gets the global instance. Initialize() must be called first. |
| static FlossDBusThreadManager* Get(); |
| |
| // Returns system bus pointer (owned by FlossDBusThreadManager). |
| dbus::Bus* GetSystemBus(); |
| |
| private: |
| FlossDBusThreadManager(); |
| ~FlossDBusThreadManager(); |
| |
| std::unique_ptr<base::Thread> dbus_thread_; |
| scoped_refptr<dbus::Bus> system_bus_; |
| }; |
| |
| } // namespace floss |
| |
| #endif // DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_MANAGER_H_ |