1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/dbus/cras_audio_client.h"
8 #include "base/format_macros.h"
9 #include "base/strings/stringprintf.h"
11 #include "dbus/message.h"
12 #include "dbus/object_path.h"
13 #include "dbus/object_proxy.h"
14 #include "third_party/cros_system_api/dbus/service_constants.h"
18 // Error name if cras dbus call fails with empty ErrorResponse.
19 const char kNoResponseError
[] =
20 "org.chromium.cras.Error.NoResponse";
22 // The CrasAudioClient implementation used in production.
23 class CrasAudioClientImpl
: public CrasAudioClient
{
25 CrasAudioClientImpl() : cras_proxy_(NULL
), weak_ptr_factory_(this) {}
27 ~CrasAudioClientImpl() override
{}
29 // CrasAudioClient overrides:
30 void AddObserver(Observer
* observer
) override
{
31 observers_
.AddObserver(observer
);
34 void RemoveObserver(Observer
* observer
) override
{
35 observers_
.RemoveObserver(observer
);
38 bool HasObserver(const Observer
* observer
) const override
{
39 return observers_
.HasObserver(observer
);
42 void GetVolumeState(const GetVolumeStateCallback
& callback
) override
{
43 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
44 cras::kGetVolumeState
);
45 cras_proxy_
->CallMethod(
47 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
48 base::Bind(&CrasAudioClientImpl::OnGetVolumeState
,
49 weak_ptr_factory_
.GetWeakPtr(), callback
));
52 void GetNodes(const GetNodesCallback
& callback
,
53 const ErrorCallback
& error_callback
) override
{
54 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
56 cras_proxy_
->CallMethodWithErrorCallback(
58 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
59 base::Bind(&CrasAudioClientImpl::OnGetNodes
,
60 weak_ptr_factory_
.GetWeakPtr(), callback
),
61 base::Bind(&CrasAudioClientImpl::OnError
,
62 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
65 void SetOutputNodeVolume(uint64 node_id
, int32 volume
) override
{
66 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
67 cras::kSetOutputNodeVolume
);
68 dbus::MessageWriter
writer(&method_call
);
69 writer
.AppendUint64(node_id
);
70 writer
.AppendInt32(volume
);
71 cras_proxy_
->CallMethod(
73 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
74 dbus::ObjectProxy::EmptyResponseCallback());
77 void SetOutputUserMute(bool mute_on
) override
{
78 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
79 cras::kSetOutputUserMute
);
80 dbus::MessageWriter
writer(&method_call
);
81 writer
.AppendBool(mute_on
);
82 cras_proxy_
->CallMethod(
84 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
85 dbus::ObjectProxy::EmptyResponseCallback());
88 void SetInputNodeGain(uint64 node_id
, int32 input_gain
) override
{
89 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
90 cras::kSetInputNodeGain
);
91 dbus::MessageWriter
writer(&method_call
);
92 writer
.AppendUint64(node_id
);
93 writer
.AppendInt32(input_gain
);
94 cras_proxy_
->CallMethod(
96 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
97 dbus::ObjectProxy::EmptyResponseCallback());
100 void SetInputMute(bool mute_on
) override
{
101 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
102 cras::kSetInputMute
);
103 dbus::MessageWriter
writer(&method_call
);
104 writer
.AppendBool(mute_on
);
105 cras_proxy_
->CallMethod(
107 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
108 dbus::ObjectProxy::EmptyResponseCallback());
111 void SetActiveOutputNode(uint64 node_id
) override
{
112 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
113 cras::kSetActiveOutputNode
);
114 dbus::MessageWriter
writer(&method_call
);
115 writer
.AppendUint64(node_id
);
116 cras_proxy_
->CallMethod(
118 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
119 dbus::ObjectProxy::EmptyResponseCallback());
122 void SetActiveInputNode(uint64 node_id
) override
{
123 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
124 cras::kSetActiveInputNode
);
125 dbus::MessageWriter
writer(&method_call
);
126 writer
.AppendUint64(node_id
);
127 cras_proxy_
->CallMethod(
129 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
130 dbus::ObjectProxy::EmptyResponseCallback());
133 void AddActiveInputNode(uint64 node_id
) override
{
134 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
135 cras::kAddActiveInputNode
);
136 dbus::MessageWriter
writer(&method_call
);
137 writer
.AppendUint64(node_id
);
138 cras_proxy_
->CallMethod(
140 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
141 dbus::ObjectProxy::EmptyResponseCallback());
144 void RemoveActiveInputNode(uint64 node_id
) override
{
145 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
146 cras::kRemoveActiveInputNode
);
147 dbus::MessageWriter
writer(&method_call
);
148 writer
.AppendUint64(node_id
);
149 cras_proxy_
->CallMethod(
151 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
152 dbus::ObjectProxy::EmptyResponseCallback());
155 void AddActiveOutputNode(uint64 node_id
) override
{
156 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
157 cras::kAddActiveOutputNode
);
158 dbus::MessageWriter
writer(&method_call
);
159 writer
.AppendUint64(node_id
);
160 cras_proxy_
->CallMethod(&method_call
,
161 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
162 dbus::ObjectProxy::EmptyResponseCallback());
165 void RemoveActiveOutputNode(uint64 node_id
) override
{
166 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
167 cras::kRemoveActiveOutputNode
);
168 dbus::MessageWriter
writer(&method_call
);
169 writer
.AppendUint64(node_id
);
170 cras_proxy_
->CallMethod(&method_call
,
171 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
172 dbus::ObjectProxy::EmptyResponseCallback());
175 void SwapLeftRight(uint64 node_id
, bool swap
) override
{
176 dbus::MethodCall
method_call(cras::kCrasControlInterface
,
177 cras::kSwapLeftRight
);
178 dbus::MessageWriter
writer(&method_call
);
179 writer
.AppendUint64(node_id
);
180 writer
.AppendBool(swap
);
181 cras_proxy_
->CallMethod(&method_call
,
182 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
183 dbus::ObjectProxy::EmptyResponseCallback());
187 void Init(dbus::Bus
* bus
) override
{
188 cras_proxy_
= bus
->GetObjectProxy(cras::kCrasServiceName
,
189 dbus::ObjectPath(cras::kCrasServicePath
));
191 // Monitor NameOwnerChanged signal.
192 cras_proxy_
->SetNameOwnerChangedCallback(
193 base::Bind(&CrasAudioClientImpl::NameOwnerChangedReceived
,
194 weak_ptr_factory_
.GetWeakPtr()));
196 // Monitor the D-Bus signal for output mute change.
197 cras_proxy_
->ConnectToSignal(
198 cras::kCrasControlInterface
,
199 cras::kOutputMuteChanged
,
200 base::Bind(&CrasAudioClientImpl::OutputMuteChangedReceived
,
201 weak_ptr_factory_
.GetWeakPtr()),
202 base::Bind(&CrasAudioClientImpl::SignalConnected
,
203 weak_ptr_factory_
.GetWeakPtr()));
205 // Monitor the D-Bus signal for input mute change.
206 cras_proxy_
->ConnectToSignal(
207 cras::kCrasControlInterface
,
208 cras::kInputMuteChanged
,
209 base::Bind(&CrasAudioClientImpl::InputMuteChangedReceived
,
210 weak_ptr_factory_
.GetWeakPtr()),
211 base::Bind(&CrasAudioClientImpl::SignalConnected
,
212 weak_ptr_factory_
.GetWeakPtr()));
214 // Monitor the D-Bus signal for nodes change.
215 cras_proxy_
->ConnectToSignal(
216 cras::kCrasControlInterface
,
218 base::Bind(&CrasAudioClientImpl::NodesChangedReceived
,
219 weak_ptr_factory_
.GetWeakPtr()),
220 base::Bind(&CrasAudioClientImpl::SignalConnected
,
221 weak_ptr_factory_
.GetWeakPtr()));
223 // Monitor the D-Bus signal for active output node change.
224 cras_proxy_
->ConnectToSignal(
225 cras::kCrasControlInterface
,
226 cras::kActiveOutputNodeChanged
,
227 base::Bind(&CrasAudioClientImpl::ActiveOutputNodeChangedReceived
,
228 weak_ptr_factory_
.GetWeakPtr()),
229 base::Bind(&CrasAudioClientImpl::SignalConnected
,
230 weak_ptr_factory_
.GetWeakPtr()));
232 // Monitor the D-Bus signal for active input node change.
233 cras_proxy_
->ConnectToSignal(
234 cras::kCrasControlInterface
,
235 cras::kActiveInputNodeChanged
,
236 base::Bind(&CrasAudioClientImpl::ActiveInputNodeChangedReceived
,
237 weak_ptr_factory_
.GetWeakPtr()),
238 base::Bind(&CrasAudioClientImpl::SignalConnected
,
239 weak_ptr_factory_
.GetWeakPtr()));
243 // Called when the cras signal is initially connected.
244 void SignalConnected(const std::string
& interface_name
,
245 const std::string
& signal_name
,
247 LOG_IF(ERROR
, !success
)
248 << "Failed to connect to cras signal:" << signal_name
;
251 void NameOwnerChangedReceived(const std::string
& old_owner
,
252 const std::string
& new_owner
) {
253 FOR_EACH_OBSERVER(Observer
, observers_
, AudioClientRestarted());
256 // Called when a OutputMuteChanged signal is received.
257 void OutputMuteChangedReceived(dbus::Signal
* signal
) {
258 // Chrome should always call SetOutputUserMute api to set the output
259 // mute state and monitor user_mute state from OutputMuteChanged signal.
260 dbus::MessageReader
reader(signal
);
261 bool system_mute
, user_mute
;
262 if (!reader
.PopBool(&system_mute
) || !reader
.PopBool(&user_mute
)) {
263 LOG(ERROR
) << "Error reading signal from cras:"
264 << signal
->ToString();
266 FOR_EACH_OBSERVER(Observer
, observers_
, OutputMuteChanged(user_mute
));
269 // Called when a InputMuteChanged signal is received.
270 void InputMuteChangedReceived(dbus::Signal
* signal
) {
271 dbus::MessageReader
reader(signal
);
273 if (!reader
.PopBool(&mute
)) {
274 LOG(ERROR
) << "Error reading signal from cras:"
275 << signal
->ToString();
277 FOR_EACH_OBSERVER(Observer
, observers_
, InputMuteChanged(mute
));
280 void NodesChangedReceived(dbus::Signal
* signal
) {
281 FOR_EACH_OBSERVER(Observer
, observers_
, NodesChanged());
284 void ActiveOutputNodeChangedReceived(dbus::Signal
* signal
) {
285 dbus::MessageReader
reader(signal
);
287 if (!reader
.PopUint64(&node_id
)) {
288 LOG(ERROR
) << "Error reading signal from cras:"
289 << signal
->ToString();
291 FOR_EACH_OBSERVER(Observer
, observers_
, ActiveOutputNodeChanged(node_id
));
294 void ActiveInputNodeChangedReceived(dbus::Signal
* signal
) {
295 dbus::MessageReader
reader(signal
);
297 if (!reader
.PopUint64(&node_id
)) {
298 LOG(ERROR
) << "Error reading signal from cras:"
299 << signal
->ToString();
301 FOR_EACH_OBSERVER(Observer
, observers_
, ActiveInputNodeChanged(node_id
));
304 void OnGetVolumeState(const GetVolumeStateCallback
& callback
,
305 dbus::Response
* response
) {
307 VolumeState volume_state
;
309 dbus::MessageReader
reader(response
);
310 if (!reader
.PopInt32(&volume_state
.output_volume
) ||
311 !reader
.PopBool(&volume_state
.output_system_mute
) ||
312 !reader
.PopInt32(&volume_state
.input_gain
) ||
313 !reader
.PopBool(&volume_state
.input_mute
) ||
314 !reader
.PopBool(&volume_state
.output_user_mute
)) {
316 LOG(ERROR
) << "Error reading response from cras: "
317 << response
->ToString();
321 LOG(ERROR
) << "Error calling " << cras::kGetVolumeState
;
324 callback
.Run(volume_state
, success
);
327 void OnGetNodes(const GetNodesCallback
& callback
,
328 dbus::Response
* response
) {
330 AudioNodeList node_list
;
332 dbus::MessageReader
response_reader(response
);
333 dbus::MessageReader
array_reader(response
);
334 while (response_reader
.HasMoreData()) {
335 if (!response_reader
.PopArray(&array_reader
)) {
337 LOG(ERROR
) << "Error reading response from cras: "
338 << response
->ToString();
343 if (!GetAudioNode(response
, &array_reader
, &node
)) {
345 LOG(WARNING
) << "Error reading audio node data from cras: "
346 << response
->ToString();
349 // Filter out the "UNKNOWN" type of audio devices.
350 if (node
.type
!= "UNKNOWN")
351 node_list
.push_back(node
);
355 if (node_list
.empty())
358 callback
.Run(node_list
, success
);
361 void OnError(const ErrorCallback
& error_callback
,
362 dbus::ErrorResponse
* response
) {
363 // Error response has optional error message argument.
364 std::string error_name
;
365 std::string error_message
;
367 dbus::MessageReader
reader(response
);
368 error_name
= response
->GetErrorName();
369 reader
.PopString(&error_message
);
371 error_name
= kNoResponseError
;
374 error_callback
.Run(error_name
, error_message
);
377 bool GetAudioNode(dbus::Response
* response
,
378 dbus::MessageReader
* array_reader
,
380 while (array_reader
->HasMoreData()) {
381 dbus::MessageReader
dict_entry_reader(response
);
382 dbus::MessageReader
value_reader(response
);
384 if (!array_reader
->PopDictEntry(&dict_entry_reader
) ||
385 !dict_entry_reader
.PopString(&key
) ||
386 !dict_entry_reader
.PopVariant(&value_reader
)) {
390 if (key
== cras::kIsInputProperty
) {
391 if (!value_reader
.PopBool(&node
->is_input
))
393 } else if (key
== cras::kIdProperty
) {
394 if (!value_reader
.PopUint64(&node
->id
))
396 } else if (key
== cras::kDeviceNameProperty
) {
397 if (!value_reader
.PopString(&node
->device_name
))
399 } else if (key
== cras::kTypeProperty
) {
400 if (!value_reader
.PopString(&node
->type
))
402 } else if (key
== cras::kNameProperty
) {
403 if (!value_reader
.PopString(&node
->name
))
405 } else if (key
== cras::kActiveProperty
) {
406 if (!value_reader
.PopBool(&node
->active
))
408 } else if (key
== cras::kPluggedTimeProperty
) {
409 if (!value_reader
.PopUint64(&node
->plugged_time
))
417 dbus::ObjectProxy
* cras_proxy_
;
418 base::ObserverList
<Observer
> observers_
;
420 // Note: This should remain the last member so it'll be destroyed and
421 // invalidate its weak pointers before any other members are destroyed.
422 base::WeakPtrFactory
<CrasAudioClientImpl
> weak_ptr_factory_
;
424 DISALLOW_COPY_AND_ASSIGN(CrasAudioClientImpl
);
427 CrasAudioClient::Observer::~Observer() {
430 void CrasAudioClient::Observer::AudioClientRestarted() {
433 void CrasAudioClient::Observer::OutputMuteChanged(bool mute_on
) {
436 void CrasAudioClient::Observer::InputMuteChanged(bool mute_on
) {
439 void CrasAudioClient::Observer::NodesChanged() {
442 void CrasAudioClient::Observer::ActiveOutputNodeChanged(uint64 node_id
){
445 void CrasAudioClient::Observer::ActiveInputNodeChanged(uint64 node_id
) {
448 CrasAudioClient::CrasAudioClient() {
451 CrasAudioClient::~CrasAudioClient() {
455 CrasAudioClient
* CrasAudioClient::Create() {
456 return new CrasAudioClientImpl();
459 } // namespace chromeos