Commit 90b9a06c authored by En-Shuo Hsu's avatar En-Shuo Hsu Committed by Commit Bot

Add BluetoothBatteryChanged event and interface

This event and corresponding interface will be used to support battery
indicator for headphone supporting Hands-Free Profile (HFP).

Design doc: go/cros-bt-bi-hfp

BUG=chromium:785758,b:140783217
TEST=build chrome and run chromeos_unittests

Change-Id: Ieda4b83f4aa86f21038e7e6a9e9e5aa2b58cba0e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2041970
Auto-Submit: En-Shuo Hsu <enshuo@chromium.org>
Commit-Queue: En-Shuo Hsu <enshuo@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740851}
parent 050fce37
...@@ -90,6 +90,10 @@ void CrasAudioHandler::AudioObserver::OnHotwordTriggered( ...@@ -90,6 +90,10 @@ void CrasAudioHandler::AudioObserver::OnHotwordTriggered(
uint64_t /* tv_sec */, uint64_t /* tv_sec */,
uint64_t /* tv_nsec */) {} uint64_t /* tv_nsec */) {}
void CrasAudioHandler::AudioObserver::OnBluetoothBatteryChanged(
const std::string& /* address */,
uint32_t /* level */) {}
void CrasAudioHandler::AudioObserver::OnOutputStarted() {} void CrasAudioHandler::AudioObserver::OnOutputStarted() {}
void CrasAudioHandler::AudioObserver::OnOutputStopped() {} void CrasAudioHandler::AudioObserver::OnOutputStopped() {}
...@@ -764,6 +768,12 @@ void CrasAudioHandler::NumberOfActiveStreamsChanged() { ...@@ -764,6 +768,12 @@ void CrasAudioHandler::NumberOfActiveStreamsChanged() {
GetNumberOfOutputStreams(); GetNumberOfOutputStreams();
} }
void CrasAudioHandler::BluetoothBatteryChanged(const std::string& address,
uint32_t level) {
for (auto& observer : observers_)
observer.OnBluetoothBatteryChanged(address, level);
}
void CrasAudioHandler::OnAudioPolicyPrefChanged() { void CrasAudioHandler::OnAudioPolicyPrefChanged() {
ApplyAudioPolicy(); ApplyAudioPolicy();
} }
......
...@@ -86,6 +86,15 @@ class COMPONENT_EXPORT(CHROMEOS_AUDIO) CrasAudioHandler ...@@ -86,6 +86,15 @@ class COMPONENT_EXPORT(CHROMEOS_AUDIO) CrasAudioHandler
// Called when hotword is detected. // Called when hotword is detected.
virtual void OnHotwordTriggered(uint64_t tv_sec, uint64_t tv_nsec); virtual void OnHotwordTriggered(uint64_t tv_sec, uint64_t tv_nsec);
// Called when the battery level change is reported over the Hands-Free
// Profile for a Bluetooth headset.
// The address is a Bluetooth address as 6 bytes written in hexadecimal and
// separated by colons. Example: 00:11:22:33:44:FF
// The level ranges from 0 to 100. Erroneous value reported by the headset
// will be ignored and won't trigger this callback.
virtual void OnBluetoothBatteryChanged(const std::string& address,
uint32_t level);
// Called when an initial output stream is opened. // Called when an initial output stream is opened.
virtual void OnOutputStarted(); virtual void OnOutputStarted();
...@@ -316,6 +325,8 @@ class COMPONENT_EXPORT(CHROMEOS_AUDIO) CrasAudioHandler ...@@ -316,6 +325,8 @@ class COMPONENT_EXPORT(CHROMEOS_AUDIO) CrasAudioHandler
void ActiveInputNodeChanged(uint64_t node_id) override; void ActiveInputNodeChanged(uint64_t node_id) override;
void OutputNodeVolumeChanged(uint64_t node_id, int volume) override; void OutputNodeVolumeChanged(uint64_t node_id, int volume) override;
void HotwordTriggered(uint64_t tv_sec, uint64_t tv_nsec) override; void HotwordTriggered(uint64_t tv_sec, uint64_t tv_nsec) override;
void BluetoothBatteryChanged(const std::string& address,
uint32_t level) override;
void NumberOfActiveStreamsChanged() override; void NumberOfActiveStreamsChanged() override;
// AudioPrefObserver overrides. // AudioPrefObserver overrides.
......
...@@ -105,6 +105,15 @@ class CrasAudioClientImpl : public CrasAudioClient { ...@@ -105,6 +105,15 @@ class CrasAudioClientImpl : public CrasAudioClient {
weak_ptr_factory_.GetWeakPtr()), weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&CrasAudioClientImpl::SignalConnected, base::BindOnce(&CrasAudioClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
// Monitor the D-Bus signal for changes in Bluetooth headset battery level.
cras_proxy_->ConnectToSignal(
cras::kCrasControlInterface, cras::kBluetoothBatteryChanged,
base::BindRepeating(
&CrasAudioClientImpl::BluetoothBatteryChangedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&CrasAudioClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
} }
~CrasAudioClientImpl() override = default; ~CrasAudioClientImpl() override = default;
...@@ -424,6 +433,24 @@ class CrasAudioClientImpl : public CrasAudioClient { ...@@ -424,6 +433,24 @@ class CrasAudioClientImpl : public CrasAudioClient {
observer.NumberOfActiveStreamsChanged(); observer.NumberOfActiveStreamsChanged();
} }
void BluetoothBatteryChangedReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
std::string address;
uint32_t level;
if (!reader.PopString(&address)) {
LOG(ERROR) << "Error reading signal from cras:" << signal->ToString();
return;
}
if (!reader.PopUint32(&level)) {
LOG(ERROR) << "Error reading signal from cras:" << signal->ToString();
return;
}
for (auto& observer : observers_)
observer.BluetoothBatteryChanged(address, level);
}
void OnGetVolumeState(DBusMethodCallback<VolumeState> callback, void OnGetVolumeState(DBusMethodCallback<VolumeState> callback,
dbus::Response* response) { dbus::Response* response) {
if (!response) { if (!response) {
...@@ -667,6 +694,10 @@ void CrasAudioClient::Observer::HotwordTriggered(uint64_t tv_sec, ...@@ -667,6 +694,10 @@ void CrasAudioClient::Observer::HotwordTriggered(uint64_t tv_sec,
void CrasAudioClient::Observer::NumberOfActiveStreamsChanged() {} void CrasAudioClient::Observer::NumberOfActiveStreamsChanged() {}
void CrasAudioClient::Observer::BluetoothBatteryChanged(
const std::string& address,
uint32_t level) {}
CrasAudioClient::CrasAudioClient() { CrasAudioClient::CrasAudioClient() {
DCHECK(!g_instance); DCHECK(!g_instance);
g_instance = this; g_instance = this;
......
...@@ -58,6 +58,10 @@ class COMPONENT_EXPORT(DBUS_AUDIO) CrasAudioClient { ...@@ -58,6 +58,10 @@ class COMPONENT_EXPORT(DBUS_AUDIO) CrasAudioClient {
// Called when the number of active output streams has changed. // Called when the number of active output streams has changed.
virtual void NumberOfActiveStreamsChanged(); virtual void NumberOfActiveStreamsChanged();
// Called when the battery level for a Bluetooth headset changed.
virtual void BluetoothBatteryChanged(const std::string& address,
uint32_t level);
protected: protected:
virtual ~Observer(); virtual ~Observer();
}; };
......
...@@ -94,6 +94,8 @@ class MockObserver : public CrasAudioClient::Observer { ...@@ -94,6 +94,8 @@ class MockObserver : public CrasAudioClient::Observer {
MOCK_METHOD2(OutputNodeVolumeChanged, void(uint64_t node_id, int volume)); MOCK_METHOD2(OutputNodeVolumeChanged, void(uint64_t node_id, int volume));
MOCK_METHOD2(HotwordTriggered, void(uint64_t tv_sec, uint64_t tv_nsec)); MOCK_METHOD2(HotwordTriggered, void(uint64_t tv_sec, uint64_t tv_nsec));
MOCK_METHOD0(NumberOfActiveStreamsChanged, void()); MOCK_METHOD0(NumberOfActiveStreamsChanged, void());
MOCK_METHOD2(BluetoothBatteryChanged,
void(const std::string& address, uint32_t level));
}; };
// Expect the reader to be empty. // Expect the reader to be empty.
...@@ -349,6 +351,15 @@ class CrasAudioClientTest : public testing::Test { ...@@ -349,6 +351,15 @@ class CrasAudioClientTest : public testing::Test {
.WillRepeatedly( .WillRepeatedly(
Invoke(this, &CrasAudioClientTest::OnNumberOfActiveStreamsChanged)); Invoke(this, &CrasAudioClientTest::OnNumberOfActiveStreamsChanged));
// Set an expectation so mock_cras_proxy's monitoring
// BluetoothBatteryChanged ConnectToSignal will use
// OnBluetoothBatteryChanged() to run the callback.
EXPECT_CALL(*mock_cras_proxy_.get(),
DoConnectToSignal(interface_name_,
cras::kBluetoothBatteryChanged, _, _))
.WillRepeatedly(
Invoke(this, &CrasAudioClientTest::OnBluetoothBatteryChanged));
// Set an expectation so mock_bus's GetObjectProxy() for the given // Set an expectation so mock_bus's GetObjectProxy() for the given
// service name and the object path will return mock_cras_proxy_. // service name and the object path will return mock_cras_proxy_.
EXPECT_CALL(*mock_bus_.get(), EXPECT_CALL(*mock_bus_.get(),
...@@ -432,6 +443,12 @@ class CrasAudioClientTest : public testing::Test { ...@@ -432,6 +443,12 @@ class CrasAudioClientTest : public testing::Test {
number_of_active_streams_changed_handler_.Run(signal); number_of_active_streams_changed_handler_.Run(signal);
} }
// Send Bluetooth battery changed signal to the tested client.
void SendBluetoothBatteryChangedSignal(dbus::Signal* signal) {
ASSERT_FALSE(bluetooth_battery_changed_handler_.is_null());
bluetooth_battery_changed_handler_.Run(signal);
}
CrasAudioClient* client() { return CrasAudioClient::Get(); } CrasAudioClient* client() { return CrasAudioClient::Get(); }
// The interface name. // The interface name.
...@@ -459,6 +476,8 @@ class CrasAudioClientTest : public testing::Test { ...@@ -459,6 +476,8 @@ class CrasAudioClientTest : public testing::Test {
dbus::ObjectProxy::SignalCallback hotword_triggered_handler_; dbus::ObjectProxy::SignalCallback hotword_triggered_handler_;
// The NumberOfActiveStreamsChanged signal handler given by the tested client. // The NumberOfActiveStreamsChanged signal handler given by the tested client.
dbus::ObjectProxy::SignalCallback number_of_active_streams_changed_handler_; dbus::ObjectProxy::SignalCallback number_of_active_streams_changed_handler_;
// The BluetoothBatteryChanged signal handler given by the tested client.
dbus::ObjectProxy::SignalCallback bluetooth_battery_changed_handler_;
// The name of the method which is expected to be called. // The name of the method which is expected to be called.
std::string expected_method_name_; std::string expected_method_name_;
// The response which the mock cras proxy returns. // The response which the mock cras proxy returns.
...@@ -579,6 +598,20 @@ class CrasAudioClientTest : public testing::Test { ...@@ -579,6 +598,20 @@ class CrasAudioClientTest : public testing::Test {
interface_name, signal_name, success)); interface_name, signal_name, success));
} }
// Checks the requested interface name and signal name.
// Used to implement the mock cras proxy.
void OnBluetoothBatteryChanged(
const std::string& interface_name,
const std::string& signal_name,
const dbus::ObjectProxy::SignalCallback& signal_callback,
dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
bluetooth_battery_changed_handler_ = signal_callback;
const bool success = true;
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(*on_connected_callback),
interface_name, signal_name, success));
}
// Checks the content of the method call and returns the response. // Checks the content of the method call and returns the response.
// Used to implement the mock cras proxy. // Used to implement the mock cras proxy.
void OnCallMethod(dbus::MethodCall* method_call, void OnCallMethod(dbus::MethodCall* method_call,
...@@ -709,6 +742,31 @@ TEST_F(CrasAudioClientTest, NumberOfActiveStreamsChanged) { ...@@ -709,6 +742,31 @@ TEST_F(CrasAudioClientTest, NumberOfActiveStreamsChanged) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
TEST_F(CrasAudioClientTest, BluetoothBatteryChanged) {
const std::string address = "11:22:33:44:55:66";
const uint32_t level = 82;
dbus::Signal signal(cras::kCrasControlInterface,
cras::kBluetoothBatteryChanged);
dbus::MessageWriter writer(&signal);
writer.AppendString(address);
writer.AppendUint32(level);
MockObserver observer;
EXPECT_CALL(observer, BluetoothBatteryChanged(address, level)).Times(1);
client()->AddObserver(&observer);
SendBluetoothBatteryChangedSignal(&signal);
client()->RemoveObserver(&observer);
EXPECT_CALL(observer, BluetoothBatteryChanged(_, _)).Times(0);
// Run the signal callback again and make sure the observer isn't called.
SendBluetoothBatteryChangedSignal(&signal);
base::RunLoop().RunUntilIdle();
}
TEST_F(CrasAudioClientTest, NodesChanged) { TEST_F(CrasAudioClientTest, NodesChanged) {
// Create a signal. // Create a signal.
dbus::Signal signal(cras::kCrasControlInterface, cras::kNodesChanged); dbus::Signal signal(cras::kCrasControlInterface, cras::kNodesChanged);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment