blob: c9d4a2373dab093dd821d6626cfc5bd532425880 [file] [log] [blame]
// 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_