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.
4
5#include "chromeos/dbus/cras_audio_client.h"
6
7#include "base/bind.h"
8#include "base/format_macros.h"
9#include "base/strings/stringprintf.h"
10#include "chromeos/dbus/cras_audio_client_stub_impl.h"
11#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
19// The CrasAudioClient implementation used in production.
20class CrasAudioClientImpl : public CrasAudioClient {
21 public:
22  explicit CrasAudioClientImpl(dbus::Bus* bus)
23      : cras_proxy_(NULL),
24        weak_ptr_factory_(this) {
25    cras_proxy_ = bus->GetObjectProxy(
26        cras::kCrasServiceName,
27        dbus::ObjectPath(cras::kCrasServicePath));
28
29    // Monitor NameOwnerChanged signal.
30    cras_proxy_->SetNameOwnerChangedCallback(
31        base::Bind(&CrasAudioClientImpl::NameOwnerChangedReceived,
32                   weak_ptr_factory_.GetWeakPtr()));
33
34    // Monitor the D-Bus signal for output mute change.
35    cras_proxy_->ConnectToSignal(
36        cras::kCrasControlInterface,
37        cras::kOutputMuteChanged,
38        base::Bind(&CrasAudioClientImpl::OutputMuteChangedReceived,
39                   weak_ptr_factory_.GetWeakPtr()),
40        base::Bind(&CrasAudioClientImpl::SignalConnected,
41                   weak_ptr_factory_.GetWeakPtr()));
42
43    // Monitor the D-Bus signal for input mute change.
44    cras_proxy_->ConnectToSignal(
45        cras::kCrasControlInterface,
46        cras::kInputMuteChanged,
47        base::Bind(&CrasAudioClientImpl::InputMuteChangedReceived,
48                   weak_ptr_factory_.GetWeakPtr()),
49        base::Bind(&CrasAudioClientImpl::SignalConnected,
50                   weak_ptr_factory_.GetWeakPtr()));
51
52    // Monitor the D-Bus signal for nodes change.
53    cras_proxy_->ConnectToSignal(
54        cras::kCrasControlInterface,
55        cras::kNodesChanged,
56        base::Bind(&CrasAudioClientImpl::NodesChangedReceived,
57                   weak_ptr_factory_.GetWeakPtr()),
58        base::Bind(&CrasAudioClientImpl::SignalConnected,
59                   weak_ptr_factory_.GetWeakPtr()));
60
61    // Monitor the D-Bus signal for active output node change.
62    cras_proxy_->ConnectToSignal(
63        cras::kCrasControlInterface,
64        cras::kActiveOutputNodeChanged,
65        base::Bind(&CrasAudioClientImpl::ActiveOutputNodeChangedReceived,
66                   weak_ptr_factory_.GetWeakPtr()),
67        base::Bind(&CrasAudioClientImpl::SignalConnected,
68                   weak_ptr_factory_.GetWeakPtr()));
69
70    // Monitor the D-Bus signal for active input node change.
71    cras_proxy_->ConnectToSignal(
72        cras::kCrasControlInterface,
73        cras::kActiveInputNodeChanged,
74        base::Bind(&CrasAudioClientImpl::ActiveInputNodeChangedReceived,
75                   weak_ptr_factory_.GetWeakPtr()),
76        base::Bind(&CrasAudioClientImpl::SignalConnected,
77                   weak_ptr_factory_.GetWeakPtr()));
78  }
79
80  virtual ~CrasAudioClientImpl() {
81  }
82
83  // CrasAudioClient overrides:
84  virtual void AddObserver(Observer* observer) OVERRIDE {
85    observers_.AddObserver(observer);
86  }
87
88  virtual void RemoveObserver(Observer* observer) OVERRIDE {
89    observers_.RemoveObserver(observer);
90  }
91
92  virtual bool HasObserver(Observer* observer) OVERRIDE {
93    return observers_.HasObserver(observer);
94  }
95
96  virtual void GetVolumeState(const GetVolumeStateCallback& callback) OVERRIDE {
97    dbus::MethodCall method_call(cras::kCrasControlInterface,
98                                 cras::kGetVolumeState);
99    cras_proxy_->CallMethod(
100        &method_call,
101        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
102        base::Bind(&CrasAudioClientImpl::OnGetVolumeState,
103                   weak_ptr_factory_.GetWeakPtr(), callback));
104  }
105
106  virtual void GetNodes(const GetNodesCallback& callback) OVERRIDE {
107    dbus::MethodCall method_call(cras::kCrasControlInterface,
108                                 cras::kGetNodes);
109    cras_proxy_->CallMethod(
110        &method_call,
111        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
112        base::Bind(&CrasAudioClientImpl::OnGetNodes,
113                   weak_ptr_factory_.GetWeakPtr(), callback));
114  }
115
116  virtual void SetOutputNodeVolume(uint64 node_id, int32 volume) OVERRIDE {
117    dbus::MethodCall method_call(cras::kCrasControlInterface,
118                                 cras::kSetOutputNodeVolume);
119    dbus::MessageWriter writer(&method_call);
120    writer.AppendUint64(node_id);
121    writer.AppendInt32(volume);
122    cras_proxy_->CallMethod(
123        &method_call,
124        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
125        dbus::ObjectProxy::EmptyResponseCallback());
126  }
127
128  virtual void SetOutputUserMute(bool mute_on) OVERRIDE {
129    dbus::MethodCall method_call(cras::kCrasControlInterface,
130                                 cras::kSetOutputUserMute);
131    dbus::MessageWriter writer(&method_call);
132    writer.AppendBool(mute_on);
133    cras_proxy_->CallMethod(
134        &method_call,
135        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
136        dbus::ObjectProxy::EmptyResponseCallback());
137  }
138
139  virtual void SetInputNodeGain(uint64 node_id, int32 input_gain) OVERRIDE {
140    dbus::MethodCall method_call(cras::kCrasControlInterface,
141                                 cras::kSetInputNodeGain);
142    dbus::MessageWriter writer(&method_call);
143    writer.AppendUint64(node_id);
144    writer.AppendInt32(input_gain);
145    cras_proxy_->CallMethod(
146        &method_call,
147        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
148        dbus::ObjectProxy::EmptyResponseCallback());
149  }
150
151  virtual void SetInputMute(bool mute_on) OVERRIDE {
152    dbus::MethodCall method_call(cras::kCrasControlInterface,
153                                 cras::kSetInputMute);
154    dbus::MessageWriter writer(&method_call);
155    writer.AppendBool(mute_on);
156    cras_proxy_->CallMethod(
157        &method_call,
158        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
159        dbus::ObjectProxy::EmptyResponseCallback());
160  }
161
162  virtual void SetActiveOutputNode(uint64 node_id) OVERRIDE {
163    dbus::MethodCall method_call(cras::kCrasControlInterface,
164                                 cras::kSetActiveOutputNode);
165    dbus::MessageWriter writer(&method_call);
166    writer.AppendUint64(node_id);
167    cras_proxy_->CallMethod(
168        &method_call,
169        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
170        dbus::ObjectProxy::EmptyResponseCallback());
171  }
172
173  virtual void SetActiveInputNode(uint64 node_id) OVERRIDE {
174    dbus::MethodCall method_call(cras::kCrasControlInterface,
175                                 cras::kSetActiveInputNode);
176    dbus::MessageWriter writer(&method_call);
177    writer.AppendUint64(node_id);
178    cras_proxy_->CallMethod(
179        &method_call,
180        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
181        dbus::ObjectProxy::EmptyResponseCallback());
182  }
183
184 private:
185  // Called when the cras signal is initially connected.
186  void SignalConnected(const std::string& interface_name,
187                       const std::string& signal_name,
188                       bool success) {
189    LOG_IF(ERROR, !success)
190        << "Failed to connect to cras signal:" << signal_name;
191  }
192
193  void NameOwnerChangedReceived(dbus::Signal* signal) {
194    FOR_EACH_OBSERVER(Observer, observers_, AudioClientRestarted());
195  }
196
197  // Called when a OutputMuteChanged signal is received.
198  void OutputMuteChangedReceived(dbus::Signal* signal) {
199    // Chrome should always call SetOutputUserMute api to set the output
200    // mute state and monitor user_mute state from OutputMuteChanged signal.
201    dbus::MessageReader reader(signal);
202    bool system_mute, user_mute;
203    if (!reader.PopBool(&system_mute) || !reader.PopBool(&user_mute)) {
204      LOG(ERROR) << "Error reading signal from cras:"
205                 << signal->ToString();
206    }
207    FOR_EACH_OBSERVER(Observer, observers_, OutputMuteChanged(user_mute));
208  }
209
210  // Called when a InputMuteChanged signal is received.
211  void InputMuteChangedReceived(dbus::Signal* signal) {
212    dbus::MessageReader reader(signal);
213    bool mute;
214    if (!reader.PopBool(&mute)) {
215      LOG(ERROR) << "Error reading signal from cras:"
216                 << signal->ToString();
217    }
218    FOR_EACH_OBSERVER(Observer, observers_, InputMuteChanged(mute));
219  }
220
221  void NodesChangedReceived(dbus::Signal* signal) {
222    FOR_EACH_OBSERVER(Observer, observers_, NodesChanged());
223  }
224
225  void ActiveOutputNodeChangedReceived(dbus::Signal* signal) {
226    dbus::MessageReader reader(signal);
227    uint64 node_id;
228    if (!reader.PopUint64(&node_id)) {
229      LOG(ERROR) << "Error reading signal from cras:"
230                 << signal->ToString();
231    }
232    FOR_EACH_OBSERVER(Observer, observers_, ActiveOutputNodeChanged(node_id));
233  }
234
235  void ActiveInputNodeChangedReceived(dbus::Signal* signal) {
236    dbus::MessageReader reader(signal);
237    uint64 node_id;
238    if (!reader.PopUint64(&node_id)) {
239      LOG(ERROR) << "Error reading signal from cras:"
240                 << signal->ToString();
241    }
242    FOR_EACH_OBSERVER(Observer, observers_, ActiveInputNodeChanged(node_id));
243  }
244
245  void OnGetVolumeState(const GetVolumeStateCallback& callback,
246                        dbus::Response* response) {
247    bool success = true;
248    VolumeState volume_state;
249    if (response) {
250      dbus::MessageReader reader(response);
251      if (!reader.PopInt32(&volume_state.output_volume) ||
252          !reader.PopBool(&volume_state.output_system_mute) ||
253          !reader.PopInt32(&volume_state.input_gain) ||
254          !reader.PopBool(&volume_state.input_mute) ||
255          !reader.PopBool(&volume_state.output_user_mute)) {
256        success = false;
257        LOG(ERROR) << "Error reading response from cras: "
258                   << response->ToString();
259      }
260    } else {
261      success = false;
262      LOG(ERROR) << "Error calling " << cras::kGetVolumeState;
263    }
264
265    callback.Run(volume_state, success);
266  }
267
268  void OnGetNodes(const GetNodesCallback& callback,
269                  dbus::Response* response) {
270    bool success = true;
271    AudioNodeList node_list;
272    if (response) {
273      dbus::MessageReader response_reader(response);
274      dbus::MessageReader array_reader(response);
275      while (response_reader.HasMoreData()) {
276        if (!response_reader.PopArray(&array_reader)) {
277          success = false;
278          LOG(ERROR) << "Error reading response from cras: "
279                     << response->ToString();
280          break;
281        }
282
283        AudioNode node;
284        if (!GetAudioNode(response, &array_reader, &node)) {
285          success = false;
286          LOG(WARNING) << "Error reading audio node data from cras: "
287                       << response->ToString();
288          break;
289        }
290        // Filter out the "UNKNOWN" type of audio devices.
291        if (node.type != "UNKNOWN")
292          node_list.push_back(node);
293      }
294    }
295
296    if (node_list.size() == 0) {
297      success = false;
298      LOG(ERROR) << "Error calling " << cras::kGetNodes;
299    }
300
301    callback.Run(node_list, success);
302  }
303
304  bool GetAudioNode(dbus::Response* response,
305                    dbus::MessageReader* array_reader,
306                    AudioNode *node) {
307    while (array_reader->HasMoreData()) {
308      dbus::MessageReader dict_entry_reader(response);
309      dbus::MessageReader value_reader(response);
310      std::string key;
311      if (!array_reader->PopDictEntry(&dict_entry_reader) ||
312          !dict_entry_reader.PopString(&key) ||
313          !dict_entry_reader.PopVariant(&value_reader)) {
314         return false;
315      }
316
317      if (key == cras::kIsInputProperty) {
318        if (!value_reader.PopBool(&node->is_input))
319          return false;
320      } else if (key == cras::kIdProperty) {
321        if (!value_reader.PopUint64(&node->id))
322          return false;
323      } else if (key == cras::kDeviceNameProperty) {
324        if (!value_reader.PopString(&node->device_name))
325          return false;
326      } else if (key == cras::kTypeProperty) {
327        if (!value_reader.PopString(&node->type))
328          return false;
329      } else if (key == cras::kNameProperty) {
330        if (!value_reader.PopString(&node->name))
331          return false;
332      } else if (key == cras::kActiveProperty) {
333        if (!value_reader.PopBool(&node->active))
334          return false;
335      } else if (key == cras::kPluggedTimeProperty) {
336        if (!value_reader.PopUint64(&node->plugged_time))
337          return false;
338      }
339    }
340
341    return true;
342  }
343
344  dbus::ObjectProxy* cras_proxy_;
345  ObserverList<Observer> observers_;
346
347  // Note: This should remain the last member so it'll be destroyed and
348  // invalidate its weak pointers before any other members are destroyed.
349  base::WeakPtrFactory<CrasAudioClientImpl> weak_ptr_factory_;
350
351  DISALLOW_COPY_AND_ASSIGN(CrasAudioClientImpl);
352};
353
354CrasAudioClient::Observer::~Observer() {
355}
356
357void CrasAudioClient::Observer::AudioClientRestarted() {
358}
359
360void CrasAudioClient::Observer::OutputMuteChanged(bool mute_on) {
361}
362
363void CrasAudioClient::Observer::InputMuteChanged(bool mute_on) {
364}
365
366void CrasAudioClient::Observer::NodesChanged() {
367}
368
369void CrasAudioClient::Observer::ActiveOutputNodeChanged(uint64 node_id){
370}
371
372void CrasAudioClient::Observer::ActiveInputNodeChanged(uint64 node_id) {
373}
374
375CrasAudioClient::CrasAudioClient() {
376}
377
378CrasAudioClient::~CrasAudioClient() {
379}
380
381// static
382CrasAudioClient* CrasAudioClient::Create(
383    DBusClientImplementationType type,
384    dbus::Bus* bus) {
385  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
386    return new CrasAudioClientImpl(bus);
387  }
388  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
389  return new CrasAudioClientStubImpl();
390}
391
392}  // namespace chromeos
393