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 17 namespace chromeos { 18 19 // Error name if cras dbus call fails with empty ErrorResponse. 20 const char kNoResponseError[] = 21 "org.chromium.cras.Error.NoResponse"; 22 23 // The CrasAudioClient implementation used in production. 24 class CrasAudioClientImpl : public CrasAudioClient { 25 public: 26 CrasAudioClientImpl() : cras_proxy_(NULL), weak_ptr_factory_(this) {} 27 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 54 virtual void GetNodes(const GetNodesCallback& callback, 55 const ErrorCallback& error_callback) OVERRIDE { 56 dbus::MethodCall method_call(cras::kCrasControlInterface, 57 cras::kGetNodes); 58 cras_proxy_->CallMethodWithErrorCallback( 59 &method_call, 60 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 61 base::Bind(&CrasAudioClientImpl::OnGetNodes, 62 weak_ptr_factory_.GetWeakPtr(), callback), 63 base::Bind(&CrasAudioClientImpl::OnError, 64 weak_ptr_factory_.GetWeakPtr(), error_callback)); 65 } 66 67 virtual void SetOutputNodeVolume(uint64 node_id, int32 volume) OVERRIDE { 68 dbus::MethodCall method_call(cras::kCrasControlInterface, 69 cras::kSetOutputNodeVolume); 70 dbus::MessageWriter writer(&method_call); 71 writer.AppendUint64(node_id); 72 writer.AppendInt32(volume); 73 cras_proxy_->CallMethod( 74 &method_call, 75 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 76 dbus::ObjectProxy::EmptyResponseCallback()); 77 } 78 79 virtual void SetOutputUserMute(bool mute_on) OVERRIDE { 80 dbus::MethodCall method_call(cras::kCrasControlInterface, 81 cras::kSetOutputUserMute); 82 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 90 virtual void SetInputNodeGain(uint64 node_id, int32 input_gain) OVERRIDE { 91 dbus::MethodCall method_call(cras::kCrasControlInterface, 92 cras::kSetInputNodeGain); 93 dbus::MessageWriter writer(&method_call); 94 writer.AppendUint64(node_id); 95 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 135 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 191 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 200 void NameOwnerChangedReceived(const std::string& old_owner, 201 const std::string& new_owner) { 202 FOR_EACH_OBSERVER(Observer, observers_, AudioClientRestarted()); 203 } 204 205 // Called when a OutputMuteChanged signal is received. 206 void OutputMuteChangedReceived(dbus::Signal* signal) { 207 // Chrome should always call SetOutputUserMute api to set the output 208 // mute state and monitor user_mute state from OutputMuteChanged signal. 209 dbus::MessageReader reader(signal); 210 bool system_mute, user_mute; 211 if (!reader.PopBool(&system_mute) || !reader.PopBool(&user_mute)) { 212 LOG(ERROR) << "Error reading signal from cras:" 213 << signal->ToString(); 214 } 215 FOR_EACH_OBSERVER(Observer, observers_, OutputMuteChanged(user_mute)); 216 } 217 218 // 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) || 260 !reader.PopBool(&volume_state.output_system_mute) || 261 !reader.PopInt32(&volume_state.input_gain) || 262 !reader.PopBool(&volume_state.input_mute) || 263 !reader.PopBool(&volume_state.output_user_mute)) { 264 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 } 298 // Filter out the "UNKNOWN" type of audio devices. 299 if (node.type != "UNKNOWN") 300 node_list.push_back(node); 301 } 302 } 303 304 if (node_list.empty()) 305 return; 306 307 callback.Run(node_list, success); 308 } 309 310 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 326 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 376 CrasAudioClient::Observer::~Observer() { 377 } 378 379 void CrasAudioClient::Observer::AudioClientRestarted() { 380 } 381 382 void CrasAudioClient::Observer::OutputMuteChanged(bool mute_on) { 383 } 384 385 void CrasAudioClient::Observer::InputMuteChanged(bool mute_on) { 386 } 387 388 void CrasAudioClient::Observer::NodesChanged() { 389 } 390 391 void CrasAudioClient::Observer::ActiveOutputNodeChanged(uint64 node_id){ 392 } 393 394 void CrasAudioClient::Observer::ActiveInputNodeChanged(uint64 node_id) { 395 } 396 397 CrasAudioClient::CrasAudioClient() { 398 } 399 400 CrasAudioClient::~CrasAudioClient() { 401 } 402 403 // static 404 CrasAudioClient* CrasAudioClient::Create() { 405 return new CrasAudioClientImpl(); 406 } 407 408 } // namespace chromeos 409