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