blob: 60443a2d47af3be4c65f429883c06c265b05d676 [file] [log] [blame]
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001// 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.
4
5#include "chromeos/dbus/cras_audio_client.h"
6
7#include "base/bind.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01008#include "base/format_macros.h"
9#include "base/strings/stringprintf.h"
Ben Murdoch558790d2013-07-30 15:19:42 +010010#include "chromeos/dbus/cras_audio_client_stub_impl.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010011#include "dbus/bus.h"
12#include "dbus/message.h"
13#include "dbus/object_path.h"
14#include "dbus/object_proxy.h"
15#include "third_party/cros_system_api/dbus/service_constants.h"
16
17namespace chromeos {
18
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +010019// Error name if cras dbus call fails with empty ErrorResponse.
20const char kNoResponseError[] =
21 "org.chromium.cras.Error.NoResponse";
22
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010023// The CrasAudioClient implementation used in production.
24class CrasAudioClientImpl : public CrasAudioClient {
25 public:
Torne (Richard Coles)424c4d72013-08-30 15:14:49 +010026 CrasAudioClientImpl() : cras_proxy_(NULL), weak_ptr_factory_(this) {}
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010027
28 virtual ~CrasAudioClientImpl() {
29 }
30
31 // CrasAudioClient overrides:
32 virtual void AddObserver(Observer* observer) OVERRIDE {
33 observers_.AddObserver(observer);
34 }
35
36 virtual void RemoveObserver(Observer* observer) OVERRIDE {
37 observers_.RemoveObserver(observer);
38 }
39
40 virtual bool HasObserver(Observer* observer) OVERRIDE {
41 return observers_.HasObserver(observer);
42 }
43
44 virtual void GetVolumeState(const GetVolumeStateCallback& callback) OVERRIDE {
45 dbus::MethodCall method_call(cras::kCrasControlInterface,
46 cras::kGetVolumeState);
47 cras_proxy_->CallMethod(
48 &method_call,
49 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
50 base::Bind(&CrasAudioClientImpl::OnGetVolumeState,
51 weak_ptr_factory_.GetWeakPtr(), callback));
52 }
53
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +010054 virtual void GetNodes(const GetNodesCallback& callback,
55 const ErrorCallback& error_callback) OVERRIDE {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010056 dbus::MethodCall method_call(cras::kCrasControlInterface,
57 cras::kGetNodes);
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +010058 cras_proxy_->CallMethodWithErrorCallback(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010059 &method_call,
60 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
61 base::Bind(&CrasAudioClientImpl::OnGetNodes,
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +010062 weak_ptr_factory_.GetWeakPtr(), callback),
63 base::Bind(&CrasAudioClientImpl::OnError,
64 weak_ptr_factory_.GetWeakPtr(), error_callback));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010065 }
66
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010067 virtual void SetOutputNodeVolume(uint64 node_id, int32 volume) OVERRIDE {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010068 dbus::MethodCall method_call(cras::kCrasControlInterface,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010069 cras::kSetOutputNodeVolume);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010070 dbus::MessageWriter writer(&method_call);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010071 writer.AppendUint64(node_id);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010072 writer.AppendInt32(volume);
73 cras_proxy_->CallMethod(
74 &method_call,
75 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
76 dbus::ObjectProxy::EmptyResponseCallback());
77 }
78
Ben Murdocheb525c52013-07-10 11:40:50 +010079 virtual void SetOutputUserMute(bool mute_on) OVERRIDE {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010080 dbus::MethodCall method_call(cras::kCrasControlInterface,
Ben Murdocheb525c52013-07-10 11:40:50 +010081 cras::kSetOutputUserMute);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010082 dbus::MessageWriter writer(&method_call);
83 writer.AppendBool(mute_on);
84 cras_proxy_->CallMethod(
85 &method_call,
86 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
87 dbus::ObjectProxy::EmptyResponseCallback());
88 }
89
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010090 virtual void SetInputNodeGain(uint64 node_id, int32 input_gain) OVERRIDE {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010091 dbus::MethodCall method_call(cras::kCrasControlInterface,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010092 cras::kSetInputNodeGain);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010093 dbus::MessageWriter writer(&method_call);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010094 writer.AppendUint64(node_id);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010095 writer.AppendInt32(input_gain);
96 cras_proxy_->CallMethod(
97 &method_call,
98 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
99 dbus::ObjectProxy::EmptyResponseCallback());
100 }
101
102 virtual void SetInputMute(bool mute_on) OVERRIDE {
103 dbus::MethodCall method_call(cras::kCrasControlInterface,
104 cras::kSetInputMute);
105 dbus::MessageWriter writer(&method_call);
106 writer.AppendBool(mute_on);
107 cras_proxy_->CallMethod(
108 &method_call,
109 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
110 dbus::ObjectProxy::EmptyResponseCallback());
111 }
112
113 virtual void SetActiveOutputNode(uint64 node_id) OVERRIDE {
114 dbus::MethodCall method_call(cras::kCrasControlInterface,
115 cras::kSetActiveOutputNode);
116 dbus::MessageWriter writer(&method_call);
117 writer.AppendUint64(node_id);
118 cras_proxy_->CallMethod(
119 &method_call,
120 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
121 dbus::ObjectProxy::EmptyResponseCallback());
122 }
123
124 virtual void SetActiveInputNode(uint64 node_id) OVERRIDE {
125 dbus::MethodCall method_call(cras::kCrasControlInterface,
126 cras::kSetActiveInputNode);
127 dbus::MessageWriter writer(&method_call);
128 writer.AppendUint64(node_id);
129 cras_proxy_->CallMethod(
130 &method_call,
131 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
132 dbus::ObjectProxy::EmptyResponseCallback());
133 }
134
Torne (Richard Coles)424c4d72013-08-30 15:14:49 +0100135 protected:
136 virtual void Init(dbus::Bus* bus) OVERRIDE {
137 cras_proxy_ = bus->GetObjectProxy(cras::kCrasServiceName,
138 dbus::ObjectPath(cras::kCrasServicePath));
139
140 // Monitor NameOwnerChanged signal.
141 cras_proxy_->SetNameOwnerChangedCallback(
142 base::Bind(&CrasAudioClientImpl::NameOwnerChangedReceived,
143 weak_ptr_factory_.GetWeakPtr()));
144
145 // Monitor the D-Bus signal for output mute change.
146 cras_proxy_->ConnectToSignal(
147 cras::kCrasControlInterface,
148 cras::kOutputMuteChanged,
149 base::Bind(&CrasAudioClientImpl::OutputMuteChangedReceived,
150 weak_ptr_factory_.GetWeakPtr()),
151 base::Bind(&CrasAudioClientImpl::SignalConnected,
152 weak_ptr_factory_.GetWeakPtr()));
153
154 // Monitor the D-Bus signal for input mute change.
155 cras_proxy_->ConnectToSignal(
156 cras::kCrasControlInterface,
157 cras::kInputMuteChanged,
158 base::Bind(&CrasAudioClientImpl::InputMuteChangedReceived,
159 weak_ptr_factory_.GetWeakPtr()),
160 base::Bind(&CrasAudioClientImpl::SignalConnected,
161 weak_ptr_factory_.GetWeakPtr()));
162
163 // Monitor the D-Bus signal for nodes change.
164 cras_proxy_->ConnectToSignal(
165 cras::kCrasControlInterface,
166 cras::kNodesChanged,
167 base::Bind(&CrasAudioClientImpl::NodesChangedReceived,
168 weak_ptr_factory_.GetWeakPtr()),
169 base::Bind(&CrasAudioClientImpl::SignalConnected,
170 weak_ptr_factory_.GetWeakPtr()));
171
172 // Monitor the D-Bus signal for active output node change.
173 cras_proxy_->ConnectToSignal(
174 cras::kCrasControlInterface,
175 cras::kActiveOutputNodeChanged,
176 base::Bind(&CrasAudioClientImpl::ActiveOutputNodeChangedReceived,
177 weak_ptr_factory_.GetWeakPtr()),
178 base::Bind(&CrasAudioClientImpl::SignalConnected,
179 weak_ptr_factory_.GetWeakPtr()));
180
181 // Monitor the D-Bus signal for active input node change.
182 cras_proxy_->ConnectToSignal(
183 cras::kCrasControlInterface,
184 cras::kActiveInputNodeChanged,
185 base::Bind(&CrasAudioClientImpl::ActiveInputNodeChangedReceived,
186 weak_ptr_factory_.GetWeakPtr()),
187 base::Bind(&CrasAudioClientImpl::SignalConnected,
188 weak_ptr_factory_.GetWeakPtr()));
189 }
190
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100191 private:
192 // Called when the cras signal is initially connected.
193 void SignalConnected(const std::string& interface_name,
194 const std::string& signal_name,
195 bool success) {
196 LOG_IF(ERROR, !success)
197 << "Failed to connect to cras signal:" << signal_name;
198 }
199
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100200 void NameOwnerChangedReceived(const std::string& old_owner,
201 const std::string& new_owner) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100202 FOR_EACH_OBSERVER(Observer, observers_, AudioClientRestarted());
203 }
204
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100205 // Called when a OutputMuteChanged signal is received.
206 void OutputMuteChangedReceived(dbus::Signal* signal) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100207 // Chrome should always call SetOutputUserMute api to set the output
208 // mute state and monitor user_mute state from OutputMuteChanged signal.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100209 dbus::MessageReader reader(signal);
Ben Murdocheb525c52013-07-10 11:40:50 +0100210 bool system_mute, user_mute;
211 if (!reader.PopBool(&system_mute) || !reader.PopBool(&user_mute)) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100212 LOG(ERROR) << "Error reading signal from cras:"
213 << signal->ToString();
214 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100215 FOR_EACH_OBSERVER(Observer, observers_, OutputMuteChanged(user_mute));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100216 }
217
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100218 // Called when a InputMuteChanged signal is received.
219 void InputMuteChangedReceived(dbus::Signal* signal) {
220 dbus::MessageReader reader(signal);
221 bool mute;
222 if (!reader.PopBool(&mute)) {
223 LOG(ERROR) << "Error reading signal from cras:"
224 << signal->ToString();
225 }
226 FOR_EACH_OBSERVER(Observer, observers_, InputMuteChanged(mute));
227 }
228
229 void NodesChangedReceived(dbus::Signal* signal) {
230 FOR_EACH_OBSERVER(Observer, observers_, NodesChanged());
231 }
232
233 void ActiveOutputNodeChangedReceived(dbus::Signal* signal) {
234 dbus::MessageReader reader(signal);
235 uint64 node_id;
236 if (!reader.PopUint64(&node_id)) {
237 LOG(ERROR) << "Error reading signal from cras:"
238 << signal->ToString();
239 }
240 FOR_EACH_OBSERVER(Observer, observers_, ActiveOutputNodeChanged(node_id));
241 }
242
243 void ActiveInputNodeChangedReceived(dbus::Signal* signal) {
244 dbus::MessageReader reader(signal);
245 uint64 node_id;
246 if (!reader.PopUint64(&node_id)) {
247 LOG(ERROR) << "Error reading signal from cras:"
248 << signal->ToString();
249 }
250 FOR_EACH_OBSERVER(Observer, observers_, ActiveInputNodeChanged(node_id));
251 }
252
253 void OnGetVolumeState(const GetVolumeStateCallback& callback,
254 dbus::Response* response) {
255 bool success = true;
256 VolumeState volume_state;
257 if (response) {
258 dbus::MessageReader reader(response);
259 if (!reader.PopInt32(&volume_state.output_volume) ||
Ben Murdocheb525c52013-07-10 11:40:50 +0100260 !reader.PopBool(&volume_state.output_system_mute) ||
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100261 !reader.PopInt32(&volume_state.input_gain) ||
Ben Murdocheb525c52013-07-10 11:40:50 +0100262 !reader.PopBool(&volume_state.input_mute) ||
263 !reader.PopBool(&volume_state.output_user_mute)) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100264 success = false;
265 LOG(ERROR) << "Error reading response from cras: "
266 << response->ToString();
267 }
268 } else {
269 success = false;
270 LOG(ERROR) << "Error calling " << cras::kGetVolumeState;
271 }
272
273 callback.Run(volume_state, success);
274 }
275
276 void OnGetNodes(const GetNodesCallback& callback,
277 dbus::Response* response) {
278 bool success = true;
279 AudioNodeList node_list;
280 if (response) {
281 dbus::MessageReader response_reader(response);
282 dbus::MessageReader array_reader(response);
283 while (response_reader.HasMoreData()) {
284 if (!response_reader.PopArray(&array_reader)) {
285 success = false;
286 LOG(ERROR) << "Error reading response from cras: "
287 << response->ToString();
288 break;
289 }
290
291 AudioNode node;
292 if (!GetAudioNode(response, &array_reader, &node)) {
293 success = false;
294 LOG(WARNING) << "Error reading audio node data from cras: "
295 << response->ToString();
296 break;
297 }
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100298 // Filter out the "UNKNOWN" type of audio devices.
299 if (node.type != "UNKNOWN")
300 node_list.push_back(node);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100301 }
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100302 }
303
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +0100304 if (node_list.empty())
305 return;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100306
307 callback.Run(node_list, success);
308 }
309
Torne (Richard Coles)d0247b12013-09-19 22:36:51 +0100310 void OnError(const ErrorCallback& error_callback,
311 dbus::ErrorResponse* response) {
312 // Error response has optional error message argument.
313 std::string error_name;
314 std::string error_message;
315 if (response) {
316 dbus::MessageReader reader(response);
317 error_name = response->GetErrorName();
318 reader.PopString(&error_message);
319 } else {
320 error_name = kNoResponseError;
321 error_message = "";
322 }
323 error_callback.Run(error_name, error_message);
324 }
325
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100326 bool GetAudioNode(dbus::Response* response,
327 dbus::MessageReader* array_reader,
328 AudioNode *node) {
329 while (array_reader->HasMoreData()) {
330 dbus::MessageReader dict_entry_reader(response);
331 dbus::MessageReader value_reader(response);
332 std::string key;
333 if (!array_reader->PopDictEntry(&dict_entry_reader) ||
334 !dict_entry_reader.PopString(&key) ||
335 !dict_entry_reader.PopVariant(&value_reader)) {
336 return false;
337 }
338
339 if (key == cras::kIsInputProperty) {
340 if (!value_reader.PopBool(&node->is_input))
341 return false;
342 } else if (key == cras::kIdProperty) {
343 if (!value_reader.PopUint64(&node->id))
344 return false;
345 } else if (key == cras::kDeviceNameProperty) {
346 if (!value_reader.PopString(&node->device_name))
347 return false;
348 } else if (key == cras::kTypeProperty) {
349 if (!value_reader.PopString(&node->type))
350 return false;
351 } else if (key == cras::kNameProperty) {
352 if (!value_reader.PopString(&node->name))
353 return false;
354 } else if (key == cras::kActiveProperty) {
355 if (!value_reader.PopBool(&node->active))
356 return false;
357 } else if (key == cras::kPluggedTimeProperty) {
358 if (!value_reader.PopUint64(&node->plugged_time))
359 return false;
360 }
361 }
362
363 return true;
364 }
365
366 dbus::ObjectProxy* cras_proxy_;
367 ObserverList<Observer> observers_;
368
369 // Note: This should remain the last member so it'll be destroyed and
370 // invalidate its weak pointers before any other members are destroyed.
371 base::WeakPtrFactory<CrasAudioClientImpl> weak_ptr_factory_;
372
373 DISALLOW_COPY_AND_ASSIGN(CrasAudioClientImpl);
374};
375
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100376CrasAudioClient::Observer::~Observer() {
377}
378
379void CrasAudioClient::Observer::AudioClientRestarted() {
380}
381
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100382void CrasAudioClient::Observer::OutputMuteChanged(bool mute_on) {
383}
384
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100385void CrasAudioClient::Observer::InputMuteChanged(bool mute_on) {
386}
387
388void CrasAudioClient::Observer::NodesChanged() {
389}
390
391void CrasAudioClient::Observer::ActiveOutputNodeChanged(uint64 node_id){
392}
393
394void CrasAudioClient::Observer::ActiveInputNodeChanged(uint64 node_id) {
395}
396
397CrasAudioClient::CrasAudioClient() {
398}
399
400CrasAudioClient::~CrasAudioClient() {
401}
402
403// static
Torne (Richard Coles)424c4d72013-08-30 15:14:49 +0100404CrasAudioClient* CrasAudioClient::Create(DBusClientImplementationType type) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100405 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
Torne (Richard Coles)424c4d72013-08-30 15:14:49 +0100406 return new CrasAudioClientImpl();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100407 }
408 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
409 return new CrasAudioClientStubImpl();
410}
411
412} // namespace chromeos