[SyncFS] Use variadic template in callback_tracker_internal.h
[chromium-blink-merge.git] / chromeos / audio / cras_audio_handler.cc
blobc6453118b4c393381d38ddf2489b37e0845530f4
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.
5 #include "chromeos/audio/cras_audio_handler.h"
7 #include <algorithm>
8 #include <cmath>
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"
17 using std::max;
18 using std::min;
20 namespace chromeos {
22 namespace {
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.
26 const int kDefaultUnmuteVolumePercent = 4;
28 // Volume value which should be considered as muted in range [0, 100].
29 const int kMuteThresholdPercent = 1;
31 static CrasAudioHandler* g_cras_audio_handler = NULL;
33 bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) {
34 return a.id == b.id && a.is_input == b.is_input && a.type == b.type
35 && a.device_name == b.device_name;
38 } // namespace
40 CrasAudioHandler::AudioObserver::AudioObserver() {
43 CrasAudioHandler::AudioObserver::~AudioObserver() {
46 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
49 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
52 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
55 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
58 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
61 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
64 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
67 // static
68 void CrasAudioHandler::Initialize(
69 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
70 CHECK(!g_cras_audio_handler);
71 g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
74 // static
75 void CrasAudioHandler::InitializeForTesting() {
76 CHECK(!g_cras_audio_handler);
77 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
80 // static
81 void CrasAudioHandler::Shutdown() {
82 CHECK(g_cras_audio_handler);
83 delete g_cras_audio_handler;
84 g_cras_audio_handler = NULL;
87 // static
88 bool CrasAudioHandler::IsInitialized() {
89 return g_cras_audio_handler != NULL;
92 // static
93 CrasAudioHandler* CrasAudioHandler::Get() {
94 CHECK(g_cras_audio_handler)
95 << "CrasAudioHandler::Get() called before Initialize().";
96 return g_cras_audio_handler;
99 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
100 observers_.AddObserver(observer);
103 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
104 observers_.RemoveObserver(observer);
107 bool CrasAudioHandler::HasKeyboardMic() {
108 return GetKeyboardMic() != NULL;
111 bool CrasAudioHandler::IsOutputMuted() {
112 return output_mute_on_;
115 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
116 const AudioDevice* device = GetDeviceFromId(device_id);
117 if (!device)
118 return false;
119 DCHECK(!device->is_input);
120 return audio_pref_handler_->GetMuteValue(*device);
123 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
124 return output_volume_ <= kMuteThresholdPercent;
127 bool CrasAudioHandler::IsInputMuted() {
128 return input_mute_on_;
131 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
132 const AudioDevice* device = GetDeviceFromId(device_id);
133 if (!device)
134 return false;
135 DCHECK(device->is_input);
136 // We don't record input mute state for each device in the prefs,
137 // for any non-active input device, we assume mute is off.
138 if (device->id == active_input_node_id_)
139 return input_mute_on_;
140 return false;
143 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
144 return kMuteThresholdPercent;
147 int CrasAudioHandler::GetOutputVolumePercent() {
148 return output_volume_;
151 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
152 if (device_id == active_output_node_id_) {
153 return output_volume_;
154 } else {
155 const AudioDevice* device = GetDeviceFromId(device_id);
156 return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
160 int CrasAudioHandler::GetInputGainPercent() {
161 return input_gain_;
164 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
165 if (device_id == active_input_node_id_) {
166 return input_gain_;
167 } else {
168 const AudioDevice* device = GetDeviceFromId(device_id);
169 return static_cast<int>(audio_pref_handler_->GetInputGainValue(device));
173 uint64 CrasAudioHandler::GetPrimaryActiveOutputNode() const {
174 return active_output_node_id_;
177 uint64 CrasAudioHandler::GetPrimaryActiveInputNode() const {
178 return active_input_node_id_;
181 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
182 device_list->clear();
183 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
184 it != audio_devices_.end(); ++it)
185 device_list->push_back(it->second);
188 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice* device) const {
189 const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
190 if (!active_device || !device)
191 return false;
192 *device = *active_device;
193 return true;
196 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
197 const AudioDevice* keyboard_mic = GetKeyboardMic();
198 if (!keyboard_mic)
199 return;
200 // Keyboard mic is invisible to chromeos users. It is always added or removed
201 // as additional active node.
202 DCHECK(active_input_node_id_ && active_input_node_id_ != keyboard_mic->id);
203 if (active)
204 AddActiveNode(keyboard_mic->id);
205 else
206 RemoveActiveNode(keyboard_mic->id);
209 void CrasAudioHandler::AddActiveNode(uint64 node_id) {
210 const AudioDevice* device = GetDeviceFromId(node_id);
211 if (!device) {
212 VLOG(1) << "AddActiveInputNode: Cannot find device id="
213 << "0x" << std::hex << node_id;
214 return;
217 // If there is no primary active device, set |node_id| to primary active node.
218 if ((device->is_input && !active_input_node_id_) ||
219 (!device->is_input && !active_output_node_id_)) {
220 SwitchToDevice(*device);
221 return;
223 AddAdditionalActiveNode(node_id);
226 void CrasAudioHandler::RemoveActiveNode(uint64 node_id) {
227 const AudioDevice* device = GetDeviceFromId(node_id);
228 if (!device) {
229 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
230 << "0x" << std::hex << node_id;
231 return;
234 // We do NOT allow to remove the primary active node.
235 if (device->is_input && device->id == active_input_node_id_) {
236 VLOG(1) << "Cannot remove the primary active input node: "
237 << "0x" << std::hex << node_id;
238 return;
240 if (!device->is_input && device->id == active_output_node_id_) {
241 VLOG(1) << "Cannot remove the primary active output node: "
242 << "0x" << std::hex << node_id;
243 return;
245 RemoveActiveNodeInternal(node_id);
248 void CrasAudioHandler::RemoveAllActiveNodes() {
249 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
250 it != audio_devices_.end();
251 ++it) {
252 RemoveActiveNodeInternal(it->second.id);
256 bool CrasAudioHandler::has_alternative_input() const {
257 return has_alternative_input_;
260 bool CrasAudioHandler::has_alternative_output() const {
261 return has_alternative_output_;
264 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
265 // Set all active devices to the same volume.
266 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
267 it != audio_devices_.end();
268 it++) {
269 const AudioDevice& device = it->second;
270 if (!device.is_input && device.active)
271 SetOutputNodeVolumePercent(device.id, volume_percent);
275 // TODO: Rename the 'Percent' to something more meaningful.
276 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
277 // TODO(jennyz): Should we set all input devices' gain to the same level?
278 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
279 it != audio_devices_.end();
280 it++) {
281 const AudioDevice& device = it->second;
282 if (device.is_input && device.active)
283 SetInputNodeGainPercent(active_input_node_id_, gain_percent);
287 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
288 SetOutputVolumePercent(output_volume_ + adjust_by_percent);
291 void CrasAudioHandler::SetOutputMute(bool mute_on) {
292 if (!SetOutputMuteInternal(mute_on))
293 return;
295 // Save the mute state for all active output audio devices.
296 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
297 it != audio_devices_.end();
298 it++) {
299 const AudioDevice& device = it->second;
300 if (!device.is_input && device.active) {
301 audio_pref_handler_->SetMuteValue(device, output_mute_on_);
305 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
308 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
309 if (output_volume_ <= kMuteThresholdPercent) {
310 // Avoid the situation when sound has been unmuted, but the volume
311 // is set to a very low value, so user still can't hear any sound.
312 SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
316 void CrasAudioHandler::SetInputMute(bool mute_on) {
317 if (!SetInputMuteInternal(mute_on))
318 return;
320 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
323 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) {
324 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
325 SetActiveOutputNode(node_id);
326 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
329 void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
330 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
331 SetActiveInputNode(node_id);
332 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
335 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
336 int value) {
337 const AudioDevice* device = GetDeviceFromId(device_id);
338 if (!device)
339 return;
341 if (device->is_input)
342 SetInputNodeGainPercent(device_id, value);
343 else
344 SetOutputNodeVolumePercent(device_id, value);
347 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
348 if (device_id == active_output_node_id_) {
349 SetOutputMute(mute_on);
350 return;
351 } else if (device_id == active_input_node_id_) {
352 VLOG(1) << "SetMuteForDevice sets active input device id="
353 << "0x" << std::hex << device_id << " mute=" << mute_on;
354 SetInputMute(mute_on);
355 return;
358 const AudioDevice* device = GetDeviceFromId(device_id);
359 // Input device's mute state is not recorded in the pref. crbug.com/365050.
360 if (device && !device->is_input)
361 audio_pref_handler_->SetMuteValue(*device, mute_on);
364 void CrasAudioHandler::LogErrors() {
365 log_errors_ = true;
368 CrasAudioHandler::CrasAudioHandler(
369 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
370 : audio_pref_handler_(audio_pref_handler),
371 output_mute_on_(false),
372 input_mute_on_(false),
373 output_volume_(0),
374 input_gain_(0),
375 active_output_node_id_(0),
376 active_input_node_id_(0),
377 has_alternative_input_(false),
378 has_alternative_output_(false),
379 output_mute_locked_(false),
380 input_mute_locked_(false),
381 log_errors_(false),
382 weak_ptr_factory_(this) {
383 if (!audio_pref_handler.get())
384 return;
385 // If the DBusThreadManager or the CrasAudioClient aren't available, there
386 // isn't much we can do. This should only happen when running tests.
387 if (!chromeos::DBusThreadManager::IsInitialized() ||
388 !chromeos::DBusThreadManager::Get() ||
389 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
390 return;
391 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
392 audio_pref_handler_->AddAudioPrefObserver(this);
393 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
394 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
395 AddObserver(this);
397 InitializeAudioState();
400 CrasAudioHandler::~CrasAudioHandler() {
401 if (!chromeos::DBusThreadManager::IsInitialized() ||
402 !chromeos::DBusThreadManager::Get() ||
403 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
404 return;
405 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
406 RemoveObserver(this);
407 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
408 RemoveObserver(this);
409 if (audio_pref_handler_.get())
410 audio_pref_handler_->RemoveAudioPrefObserver(this);
411 audio_pref_handler_ = NULL;
414 void CrasAudioHandler::AudioClientRestarted() {
415 // Make sure the logging is enabled in case cras server
416 // restarts after crashing.
417 LogErrors();
418 InitializeAudioState();
421 void CrasAudioHandler::NodesChanged() {
422 // Refresh audio nodes data.
423 GetNodes();
426 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
427 if (active_output_node_id_ == node_id)
428 return;
430 // Active audio output device should always be changed by chrome.
431 // During system boot, cras may change active input to unknown device 0x1,
432 // we don't need to log it, since it is not an valid device.
433 if (GetDeviceFromId(node_id)) {
434 LOG_IF(WARNING, log_errors_)
435 << "Active output node changed unexpectedly by system node_id="
436 << "0x" << std::hex << node_id;
440 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
441 if (active_input_node_id_ == node_id)
442 return;
444 // Active audio input device should always be changed by chrome.
445 // During system boot, cras may change active input to unknown device 0x2,
446 // we don't need to log it, since it is not an valid device.
447 if (GetDeviceFromId(node_id)) {
448 LOG_IF(WARNING, log_errors_)
449 << "Active input node changed unexpectedly by system node_id="
450 << "0x" << std::hex << node_id;
454 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
455 ApplyAudioPolicy();
458 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
459 // Enable logging after cras server is started, which will be after
460 // EmitLoginPromptVisible.
461 LogErrors();
464 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
465 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
466 if (it == audio_devices_.end())
467 return NULL;
469 return &(it->second);
472 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
473 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
474 it != audio_devices_.end(); it++) {
475 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
476 return &(it->second);
478 return NULL;
481 void CrasAudioHandler::SetupAudioInputState() {
482 // Set the initial audio state to the ones read from audio prefs.
483 const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
484 if (!device) {
485 LOG_IF(ERROR, log_errors_)
486 << "Can't set up audio state for unknown input device id ="
487 << "0x" << std::hex << active_input_node_id_;
488 return;
490 input_gain_ = audio_pref_handler_->GetInputGainValue(device);
491 VLOG(1) << "SetupAudioInputState for active device id="
492 << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
493 SetInputMuteInternal(input_mute_on_);
494 // TODO(rkc,jennyz): Set input gain once we decide on how to store
495 // the gain values since the range and step are both device specific.
498 void CrasAudioHandler::SetupAudioOutputState() {
499 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
500 if (!device) {
501 LOG_IF(ERROR, log_errors_)
502 << "Can't set up audio state for unknown output device id ="
503 << "0x" << std::hex << active_output_node_id_;
504 return;
506 DCHECK(!device->is_input);
507 output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
508 output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
510 SetOutputMuteInternal(output_mute_on_);
511 SetOutputNodeVolume(active_output_node_id_, output_volume_);
514 // This sets up the state of an additional active node.
515 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64 node_id) {
516 const AudioDevice* device = GetDeviceFromId(node_id);
517 if (!device) {
518 VLOG(1) << "Can't set up audio state for unknown device id ="
519 << "0x" << std::hex << node_id;
520 return;
523 DCHECK(node_id != active_output_node_id_ && node_id != active_input_node_id_);
525 // Note: The mute state is a system wide state, we don't set mute per device,
526 // but just keep the mute state consistent for the active node in prefs.
527 // The output volume should be set to the same value for all active output
528 // devices. For input devices, we don't restore their gain value so far.
529 // TODO(jennyz): crbug.com/417418, track the status for the decison if
530 // we should persist input gain value in prefs.
531 if (!device->is_input) {
532 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
533 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
537 void CrasAudioHandler::InitializeAudioState() {
538 ApplyAudioPolicy();
539 GetNodes();
542 void CrasAudioHandler::ApplyAudioPolicy() {
543 output_mute_locked_ = false;
544 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
545 // Mute the device, but do not update the preference.
546 SetOutputMuteInternal(true);
547 output_mute_locked_ = true;
548 } else {
549 // Restore the mute state.
550 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
551 if (device)
552 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
555 input_mute_locked_ = false;
556 if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
557 VLOG(1) << "Audio input allowed by policy, sets input id="
558 << "0x" << std::hex << active_input_node_id_ << " mute=false";
559 SetInputMuteInternal(false);
560 } else {
561 VLOG(0) << "Audio input NOT allowed by policy, sets input id="
562 << "0x" << std::hex << active_input_node_id_ << " mute=true";
563 SetInputMuteInternal(true);
564 input_mute_locked_ = true;
568 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
569 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
570 SetOutputNodeVolume(node_id, volume);
573 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64 node_id,
574 int volume_percent) {
575 const AudioDevice* device = this->GetDeviceFromId(node_id);
576 if (!device || device->is_input)
577 return;
579 volume_percent = min(max(volume_percent, 0), 100);
580 if (volume_percent <= kMuteThresholdPercent)
581 volume_percent = 0;
582 if (node_id == active_output_node_id_)
583 output_volume_ = volume_percent;
585 audio_pref_handler_->SetVolumeGainValue(*device, volume_percent);
587 if (device->active) {
588 SetOutputNodeVolume(node_id, volume_percent);
589 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
593 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
594 if (output_mute_locked_)
595 return false;
597 output_mute_on_ = mute_on;
598 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
599 SetOutputUserMute(mute_on);
600 return true;
603 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
604 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
605 SetInputNodeGain(node_id, gain);
608 void CrasAudioHandler::SetInputNodeGainPercent(uint64 node_id,
609 int gain_percent) {
610 const AudioDevice* device = GetDeviceFromId(node_id);
611 if (!device || !device->is_input)
612 return;
614 // NOTE: We do not sanitize input gain values since the range is completely
615 // dependent on the device.
616 if (active_input_node_id_ == node_id)
617 input_gain_ = gain_percent;
619 audio_pref_handler_->SetVolumeGainValue(*device, gain_percent);
621 if (device->active) {
622 SetInputNodeGain(node_id, gain_percent);
623 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
627 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
628 if (input_mute_locked_)
629 return false;
631 input_mute_on_ = mute_on;
632 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
633 SetInputMute(mute_on);
634 return true;
637 void CrasAudioHandler::GetNodes() {
638 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
639 base::Bind(&CrasAudioHandler::HandleGetNodes,
640 weak_ptr_factory_.GetWeakPtr()),
641 base::Bind(&CrasAudioHandler::HandleGetNodesError,
642 weak_ptr_factory_.GetWeakPtr()));
645 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
646 uint64* current_active_node_id) {
647 // If the device we want to switch to is already the current active device,
648 // do nothing.
649 if (new_active_device.active &&
650 new_active_device.id == *current_active_node_id) {
651 return false;
654 // Reset all other input or output devices' active status. The active audio
655 // device from the previous user session can be remembered by cras, but not
656 // in chrome. see crbug.com/273271.
657 for (AudioDeviceMap::iterator it = audio_devices_.begin();
658 it != audio_devices_.end(); ++it) {
659 if (it->second.is_input == new_active_device.is_input &&
660 it->second.id != new_active_device.id)
661 it->second.active = false;
664 // Set the current active input/output device to the new_active_device.
665 *current_active_node_id = new_active_device.id;
666 audio_devices_[*current_active_node_id].active = true;
667 return true;
670 bool CrasAudioHandler::NonActiveDeviceUnplugged(
671 size_t old_devices_size,
672 size_t new_devices_size,
673 uint64 current_active_node) {
674 return (new_devices_size < old_devices_size &&
675 GetDeviceFromId(current_active_node));
678 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) {
679 if (device.is_input) {
680 if (!ChangeActiveDevice(device, &active_input_node_id_))
681 return;
682 SetupAudioInputState();
683 SetActiveInputNode(active_input_node_id_);
684 } else {
685 if (!ChangeActiveDevice(device, &active_output_node_id_))
686 return;
687 SetupAudioOutputState();
688 SetActiveOutputNode(active_output_node_id_);
692 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
693 bool is_input) {
694 size_t num_old_devices = 0;
695 size_t num_new_devices = 0;
696 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
697 it != audio_devices_.end(); ++it) {
698 if (is_input == it->second.is_input)
699 ++num_old_devices;
702 for (AudioNodeList::const_iterator it = new_nodes.begin();
703 it != new_nodes.end(); ++it) {
704 if (is_input == it->is_input) {
705 ++num_new_devices;
706 // Look to see if the new device not in the old device list.
707 AudioDevice device(*it);
708 if (FoundNewOrChangedDevice(device))
709 return true;
712 return num_old_devices != num_new_devices;
715 bool CrasAudioHandler::FoundNewOrChangedDevice(const AudioDevice& device) {
716 const AudioDevice* device_found = GetDeviceFromId(device.id);
717 if (!device_found)
718 return true;
720 if (!IsSameAudioDevice(device, *device_found)) {
721 LOG(WARNING) << "Different Audio devices with same id:"
722 << " new device: " << device.ToString()
723 << " old device: " << device_found->ToString();
724 return true;
725 } else if (device.active != device_found->active) {
726 return true;
729 return false;
732 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
733 const AudioNodeList& nodes) {
734 size_t old_audio_devices_size = audio_devices_.size();
735 bool output_devices_changed = HasDeviceChange(nodes, false);
736 bool input_devices_changed = HasDeviceChange(nodes, true);
737 audio_devices_.clear();
738 has_alternative_input_ = false;
739 has_alternative_output_ = false;
741 while (!input_devices_pq_.empty())
742 input_devices_pq_.pop();
743 while (!output_devices_pq_.empty())
744 output_devices_pq_.pop();
746 for (size_t i = 0; i < nodes.size(); ++i) {
747 AudioDevice device(nodes[i]);
748 audio_devices_[device.id] = device;
750 if (!has_alternative_input_ &&
751 device.is_input &&
752 device.type != AUDIO_TYPE_INTERNAL_MIC &&
753 device.type != AUDIO_TYPE_KEYBOARD_MIC) {
754 has_alternative_input_ = true;
755 } else if (!has_alternative_output_ &&
756 !device.is_input &&
757 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
758 has_alternative_output_ = true;
761 if (device.is_input)
762 input_devices_pq_.push(device);
763 else
764 output_devices_pq_.push(device);
767 // If audio nodes change is caused by unplugging some non-active audio
768 // devices, the previously set active audio device will stay active.
769 // Otherwise, switch to a new active audio device according to their priority.
770 if (input_devices_changed &&
771 !NonActiveDeviceUnplugged(old_audio_devices_size,
772 audio_devices_.size(),
773 active_input_node_id_) &&
774 !input_devices_pq_.empty())
775 SwitchToDevice(input_devices_pq_.top());
776 if (output_devices_changed &&
777 !NonActiveDeviceUnplugged(old_audio_devices_size,
778 audio_devices_.size(),
779 active_output_node_id_) &&
780 !output_devices_pq_.empty()) {
781 SwitchToDevice(output_devices_pq_.top());
785 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
786 bool success) {
787 if (!success) {
788 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
789 return;
792 UpdateDevicesAndSwitchActive(node_list);
793 FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
796 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
797 const std::string& error_msg) {
798 LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
799 << error_name << ": " << error_msg;
802 void CrasAudioHandler::AddAdditionalActiveNode(uint64 node_id) {
803 const AudioDevice* device = GetDeviceFromId(node_id);
804 if (!device) {
805 VLOG(1) << "AddActiveInputNode: Cannot find device id="
806 << "0x" << std::hex << node_id;
807 return;
810 audio_devices_[node_id].active = true;
811 SetupAdditionalActiveAudioNodeState(node_id);
813 if (device->is_input) {
814 DCHECK(node_id != active_input_node_id_);
815 chromeos::DBusThreadManager::Get()
816 ->GetCrasAudioClient()
817 ->AddActiveInputNode(node_id);
818 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
819 } else {
820 DCHECK(node_id != active_output_node_id_);
821 chromeos::DBusThreadManager::Get()
822 ->GetCrasAudioClient()
823 ->AddActiveOutputNode(node_id);
824 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
828 void CrasAudioHandler::RemoveActiveNodeInternal(uint64 node_id) {
829 const AudioDevice* device = GetDeviceFromId(node_id);
830 if (!device) {
831 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
832 << "0x" << std::hex << node_id;
833 return;
836 audio_devices_[node_id].active = false;
837 if (device->is_input) {
838 if (node_id == active_input_node_id_)
839 active_input_node_id_ = 0;
840 chromeos::DBusThreadManager::Get()
841 ->GetCrasAudioClient()
842 ->RemoveActiveInputNode(node_id);
843 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
844 } else {
845 if (node_id == active_output_node_id_)
846 active_output_node_id_ = 0;
847 chromeos::DBusThreadManager::Get()
848 ->GetCrasAudioClient()
849 ->RemoveActiveOutputNode(node_id);
851 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
854 } // namespace chromeos