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/audio/cras_audio_handler.h" 6 7#include <algorithm> 8#include <cmath> 9 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/logging.h" 13#include "chromeos/audio/audio_devices_pref_handler.h" 14#include "chromeos/audio/audio_devices_pref_handler_stub.h" 15#include "chromeos/dbus/dbus_thread_manager.h" 16 17using std::max; 18using std::min; 19 20namespace chromeos { 21 22namespace { 23 24// Default value for unmuting, as a percent in the range [0, 100]. 25// Used when sound is unmuted, but volume was less than kMuteThresholdPercent. 26const int kDefaultUnmuteVolumePercent = 4; 27 28// Volume value which should be considered as muted in range [0, 100]. 29const int kMuteThresholdPercent = 1; 30 31static CrasAudioHandler* g_cras_audio_handler = NULL; 32 33} // namespace 34 35CrasAudioHandler::AudioObserver::AudioObserver() { 36} 37 38CrasAudioHandler::AudioObserver::~AudioObserver() { 39} 40 41void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() { 42} 43 44void CrasAudioHandler::AudioObserver::OnInputGainChanged() { 45} 46 47void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() { 48} 49 50void CrasAudioHandler::AudioObserver::OnInputMuteChanged() { 51} 52 53void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() { 54} 55 56void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() { 57} 58 59void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() { 60} 61 62// static 63void CrasAudioHandler::Initialize( 64 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) { 65 CHECK(!g_cras_audio_handler); 66 g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler); 67} 68 69// static 70void CrasAudioHandler::InitializeForTesting() { 71 CHECK(!g_cras_audio_handler); 72 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub()); 73} 74 75// static 76void CrasAudioHandler::Shutdown() { 77 CHECK(g_cras_audio_handler); 78 delete g_cras_audio_handler; 79 g_cras_audio_handler = NULL; 80} 81 82// static 83bool CrasAudioHandler::IsInitialized() { 84 return g_cras_audio_handler != NULL; 85} 86 87// static 88CrasAudioHandler* CrasAudioHandler::Get() { 89 CHECK(g_cras_audio_handler) 90 << "CrasAudioHandler::Get() called before Initialize()."; 91 return g_cras_audio_handler; 92} 93 94void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) { 95 observers_.AddObserver(observer); 96} 97 98void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) { 99 observers_.RemoveObserver(observer); 100} 101 102bool CrasAudioHandler::IsOutputMuted() { 103 return output_mute_on_; 104} 105 106bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) { 107 const AudioDevice* device = GetDeviceFromId(device_id); 108 if (!device) 109 return false; 110 return audio_pref_handler_->GetMuteValue(*device); 111} 112 113bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLvel() { 114 return output_volume_ <= kMuteThresholdPercent; 115} 116 117bool CrasAudioHandler::IsInputMuted() { 118 return input_mute_on_; 119} 120 121bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) { 122 const AudioDevice* device = GetDeviceFromId(device_id); 123 if (!device) 124 return false; 125 return audio_pref_handler_->GetMuteValue(*device); 126} 127 128int CrasAudioHandler::GetOutputVolumePercent() { 129 return output_volume_; 130} 131 132int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) { 133 if (device_id == active_output_node_id_) { 134 return output_volume_; 135 } else { 136 const AudioDevice* device = GetDeviceFromId(device_id); 137 if (!device) 138 return kDefaultVolumeGainPercent; 139 return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device)); 140 } 141} 142 143int CrasAudioHandler::GetInputGainPercent() { 144 return input_gain_; 145} 146 147int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) { 148 if (device_id == active_input_node_id_) { 149 return input_gain_; 150 } else { 151 const AudioDevice* device = GetDeviceFromId(device_id); 152 if (!device) 153 return kDefaultVolumeGainPercent; 154 return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device)); 155 } 156} 157 158uint64 CrasAudioHandler::GetActiveOutputNode() const { 159 return active_output_node_id_; 160} 161 162uint64 CrasAudioHandler::GetActiveInputNode() const { 163 return active_input_node_id_; 164} 165 166void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const { 167 device_list->clear(); 168 for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); 169 it != audio_devices_.end(); ++it) 170 device_list->push_back(it->second); 171} 172 173bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const { 174 const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_); 175 if (!active_device || !device) 176 return false; 177 *device = *active_device; 178 return true; 179} 180 181bool CrasAudioHandler::has_alternative_input() const { 182 return has_alternative_input_; 183} 184 185bool CrasAudioHandler::has_alternative_output() const { 186 return has_alternative_output_; 187} 188 189void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) { 190 volume_percent = min(max(volume_percent, 0), 100); 191 if (volume_percent <= kMuteThresholdPercent) 192 volume_percent = 0; 193 output_volume_ = volume_percent; 194 195 if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_)) 196 audio_pref_handler_->SetVolumeGainValue(*device, output_volume_); 197 198 SetOutputNodeVolume(active_output_node_id_, output_volume_); 199 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged()); 200} 201 202// TODO: Rename the 'Percent' to something more meaningful. 203void CrasAudioHandler::SetInputGainPercent(int gain_percent) { 204 // NOTE: We do not sanitize input gain values since the range is completely 205 // dependent on the device. 206 input_gain_ = gain_percent; 207 208 if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_)) 209 audio_pref_handler_->SetVolumeGainValue(*device, input_gain_); 210 211 SetInputNodeGain(active_input_node_id_, input_gain_); 212 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged()); 213} 214 215void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) { 216 SetOutputVolumePercent(output_volume_ + adjust_by_percent); 217} 218 219void CrasAudioHandler::SetOutputMute(bool mute_on) { 220 if (!SetOutputMuteInternal(mute_on)) 221 return; 222 223 if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_)) 224 audio_pref_handler_->SetMuteValue(*device, output_mute_on_); 225 226 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged()); 227} 228 229void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() { 230 if (output_volume_ <= kMuteThresholdPercent) { 231 // Avoid the situation when sound has been unmuted, but the volume 232 // is set to a very low value, so user still can't hear any sound. 233 SetOutputVolumePercent(kDefaultUnmuteVolumePercent); 234 } 235} 236 237void CrasAudioHandler::SetInputMute(bool mute_on) { 238 if (!SetInputMuteInternal(mute_on)) 239 return; 240 241 AudioDevice device; 242 if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_)) 243 audio_pref_handler_->SetMuteValue(*device, input_mute_on_); 244 245 246 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged()); 247} 248 249void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) { 250 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 251 SetActiveOutputNode(node_id); 252 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged()); 253} 254 255void CrasAudioHandler::SetActiveInputNode(uint64 node_id) { 256 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 257 SetActiveInputNode(node_id); 258 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged()); 259} 260 261void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id, 262 int value) { 263 if (device_id == active_output_node_id_) { 264 SetOutputVolumePercent(value); 265 return; 266 } else if (device_id == active_input_node_id_) { 267 SetInputGainPercent(value); 268 return; 269 } 270 271 if (const AudioDevice* device = GetDeviceFromId(device_id)) { 272 if (!device->is_input) { 273 value = min(max(value, 0), 100); 274 if (value <= kMuteThresholdPercent) 275 value = 0; 276 } 277 audio_pref_handler_->SetVolumeGainValue(*device, value); 278 } 279} 280 281void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) { 282 if (device_id == active_output_node_id_) { 283 SetOutputMute(mute_on); 284 return; 285 } else if (device_id == active_input_node_id_) { 286 SetInputMute(mute_on); 287 return; 288 } 289 290 AudioDevice device; 291 if (const AudioDevice* device = GetDeviceFromId(device_id)) 292 audio_pref_handler_->SetMuteValue(*device, mute_on); 293} 294 295CrasAudioHandler::CrasAudioHandler( 296 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) 297 : audio_pref_handler_(audio_pref_handler), 298 weak_ptr_factory_(this), 299 output_mute_on_(false), 300 input_mute_on_(false), 301 output_volume_(0), 302 input_gain_(0), 303 active_output_node_id_(0), 304 active_input_node_id_(0), 305 has_alternative_input_(false), 306 has_alternative_output_(false), 307 output_mute_locked_(false), 308 input_mute_locked_(false) { 309 if (!audio_pref_handler.get()) 310 return; 311 // If the DBusThreadManager or the CrasAudioClient aren't available, there 312 // isn't much we can do. This should only happen when running tests. 313 if (!chromeos::DBusThreadManager::IsInitialized() || 314 !chromeos::DBusThreadManager::Get() || 315 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient()) 316 return; 317 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this); 318 audio_pref_handler_->AddAudioPrefObserver(this); 319 InitializeAudioState(); 320} 321 322CrasAudioHandler::~CrasAudioHandler() { 323 if (!chromeos::DBusThreadManager::IsInitialized() || 324 !chromeos::DBusThreadManager::Get() || 325 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient()) 326 return; 327 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 328 RemoveObserver(this); 329 if (audio_pref_handler_.get()) 330 audio_pref_handler_->RemoveAudioPrefObserver(this); 331 audio_pref_handler_ = NULL; 332} 333 334void CrasAudioHandler::AudioClientRestarted() { 335 InitializeAudioState(); 336} 337 338void CrasAudioHandler::NodesChanged() { 339 // Refresh audio nodes data. 340 GetNodes(); 341} 342 343void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) { 344 if (active_output_node_id_ == node_id) 345 return; 346 347 // Active audio output device should always be changed by chrome. 348 LOG(WARNING) << "Active output node changed unexpectedly by system node_id=" 349 << "0x" << std::hex << node_id; 350} 351 352void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) { 353 if (active_input_node_id_ == node_id) 354 return; 355 356 // Active audio input device should always be changed by chrome. 357 LOG(WARNING) << "Active input node changed unexpectedly by system node_id=" 358 << "0x" << std::hex << node_id; 359} 360 361void CrasAudioHandler::OnAudioPolicyPrefChanged() { 362 ApplyAudioPolicy(); 363} 364 365const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const { 366 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id); 367 if (it == audio_devices_.end()) 368 return NULL; 369 370 return &(it->second); 371} 372 373void CrasAudioHandler::SetupAudioInputState() { 374 // Set the initial audio state to the ones read from audio prefs. 375 const AudioDevice* device = GetDeviceFromId(active_input_node_id_); 376 if (!device) { 377 LOG(ERROR) << "Can't set up audio state for unknow input device id =" 378 << "0x" << std::hex << active_input_node_id_; 379 return; 380 } 381 input_mute_on_ = audio_pref_handler_->GetMuteValue(*device); 382 input_gain_ = audio_pref_handler_->GetVolumeGainValue(*device); 383 SetInputMuteInternal(input_mute_on_); 384 // TODO(rkc,jennyz): Set input gain once we decide on how to store 385 // the gain values since the range and step are both device specific. 386} 387 388void CrasAudioHandler::SetupAudioOutputState() { 389 const AudioDevice* device = GetDeviceFromId(active_output_node_id_); 390 if (!device) { 391 LOG(ERROR) << "Can't set up audio state for unknow output device id =" 392 << "0x" << std::hex << active_output_node_id_; 393 return; 394 } 395 output_mute_on_ = audio_pref_handler_->GetMuteValue(*device); 396 output_volume_ = audio_pref_handler_->GetVolumeGainValue(*device); 397 398 SetOutputMuteInternal(output_mute_on_); 399 SetOutputNodeVolume(active_output_node_id_, output_volume_); 400} 401 402void CrasAudioHandler::InitializeAudioState() { 403 ApplyAudioPolicy(); 404 GetNodes(); 405} 406 407void CrasAudioHandler::ApplyAudioPolicy() { 408 output_mute_locked_ = false; 409 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) { 410 // Mute the device, but do not update the preference. 411 SetOutputMuteInternal(true); 412 output_mute_locked_ = true; 413 } else { 414 // Restore the mute state. 415 const AudioDevice* device = GetDeviceFromId(active_output_node_id_); 416 if (device) 417 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device)); 418 } 419 420 input_mute_locked_ = false; 421 if (audio_pref_handler_->GetAudioCaptureAllowedValue()) { 422 SetInputMute(false); 423 } else { 424 SetInputMute(true); 425 input_mute_locked_ = true; 426 } 427} 428 429void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) { 430 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 431 SetOutputNodeVolume(node_id, volume); 432} 433 434bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) { 435 if (output_mute_locked_) 436 return false; 437 438 output_mute_on_ = mute_on; 439 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 440 SetOutputUserMute(mute_on); 441 return true; 442} 443 444void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) { 445 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 446 SetInputNodeGain(node_id, gain); 447} 448 449bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) { 450 if (input_mute_locked_) 451 return false; 452 453 input_mute_on_ = mute_on; 454 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 455 SetInputMute(mute_on); 456 return true; 457} 458 459void CrasAudioHandler::GetNodes() { 460 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes( 461 base::Bind(&CrasAudioHandler::HandleGetNodes, 462 weak_ptr_factory_.GetWeakPtr())); 463} 464 465bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device, 466 uint64* current_active_node_id) { 467 // If the device we want to switch to is already the current active device, 468 // do nothing. 469 if (new_active_device.active && 470 new_active_device.id == *current_active_node_id) { 471 return false; 472 } 473 474 // Reset all other input or output devices' active status. The active audio 475 // device from the previous user session can be remembered by cras, but not 476 // in chrome. see crbug.com/273271. 477 for (AudioDeviceMap::iterator it = audio_devices_.begin(); 478 it != audio_devices_.end(); ++it) { 479 if (it->second.is_input == new_active_device.is_input && 480 it->second.id != new_active_device.id) 481 it->second.active = false; 482 } 483 484 // Set the current active input/output device to the new_active_device. 485 *current_active_node_id = new_active_device.id; 486 audio_devices_[*current_active_node_id].active = true; 487 return true; 488} 489 490bool CrasAudioHandler::NonActiveDeviceUnplugged( 491 size_t old_devices_size, 492 size_t new_devices_size, 493 uint64 current_active_node) { 494 // There could be cases that more than one NodesChanged signals are 495 // triggered by cras for unplugging or plugging one audio devices, both coming 496 // with the same node data. After handling the first NodesChanged signal, the 497 // audio_devices_ can be overwritten by staled node data from handling 2nd 498 // NodesChanged signal. Therefore, we need to check if the device with 499 // current_active_node is consistently active or not. 500 // crbug.com/274641. 501 return (new_devices_size <= old_devices_size && 502 GetDeviceFromId(current_active_node) && 503 audio_devices_[current_active_node].active); 504} 505 506void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) { 507 if (device.is_input) { 508 if (!ChangeActiveDevice(device, &active_input_node_id_)) 509 return; 510 SetupAudioInputState(); 511 SetActiveInputNode(active_input_node_id_); 512 } else { 513 if (!ChangeActiveDevice(device, &active_output_node_id_)) 514 return; 515 SetupAudioOutputState(); 516 SetActiveOutputNode(active_output_node_id_); 517 } 518} 519 520bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes, 521 bool is_input) { 522 size_t num_old_devices = 0; 523 size_t num_new_devices = 0; 524 for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); 525 it != audio_devices_.end(); ++it) { 526 if (is_input == it->second.is_input) 527 ++num_old_devices; 528 } 529 530 for (AudioNodeList::const_iterator it = new_nodes.begin(); 531 it != new_nodes.end(); ++it) { 532 if (is_input == it->is_input) 533 ++num_new_devices; 534 } 535 return num_old_devices != num_new_devices; 536} 537 538void CrasAudioHandler::UpdateDevicesAndSwitchActive( 539 const AudioNodeList& nodes) { 540 size_t old_audio_devices_size = audio_devices_.size(); 541 bool output_devices_changed = HasDeviceChange(nodes, false); 542 bool input_devices_changed = HasDeviceChange(nodes, true); 543 audio_devices_.clear(); 544 has_alternative_input_ = false; 545 has_alternative_output_ = false; 546 547 while (!input_devices_pq_.empty()) 548 input_devices_pq_.pop(); 549 while (!output_devices_pq_.empty()) 550 output_devices_pq_.pop(); 551 552 for (size_t i = 0; i < nodes.size(); ++i) { 553 AudioDevice device(nodes[i]); 554 audio_devices_[device.id] = device; 555 556 if (!has_alternative_input_ && 557 device.is_input && 558 device.type != AUDIO_TYPE_INTERNAL_MIC) { 559 has_alternative_input_ = true; 560 } else if (!has_alternative_output_ && 561 !device.is_input && 562 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) { 563 has_alternative_output_ = true; 564 } 565 566 if (device.is_input) 567 input_devices_pq_.push(device); 568 else 569 output_devices_pq_.push(device); 570 } 571 572 // If audio nodes change is caused by unplugging some non-active audio 573 // devices, the previously set active audio device will stay active. 574 // Otherwise, switch to a new active audio device according to their priority. 575 if (input_devices_changed && 576 !NonActiveDeviceUnplugged(old_audio_devices_size, 577 audio_devices_.size(), 578 active_input_node_id_) && 579 !input_devices_pq_.empty()) 580 SwitchToDevice(input_devices_pq_.top()); 581 if (output_devices_changed && 582 !NonActiveDeviceUnplugged(old_audio_devices_size, 583 audio_devices_.size(), 584 active_output_node_id_) && 585 !output_devices_pq_.empty()) { 586 SwitchToDevice(output_devices_pq_.top()); 587 } 588} 589 590void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list, 591 bool success) { 592 if (!success) { 593 LOG(ERROR) << "Failed to retrieve audio nodes data"; 594 return; 595 } 596 597 UpdateDevicesAndSwitchActive(node_list); 598 FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged()); 599} 600 601} // namespace chromeos 602