Revert of [telemetry] Fix decorator hack for benchmark_smoke_unittest. (patchset...
[chromium-blink-merge.git] / chromeos / audio / cras_audio_handler.cc
blobcb3e47ea1cf9a12fa3339c05a54435af26ed1ee3
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 bool IsInNodeList(uint64 node_id, const CrasAudioHandler::NodeIdList& id_list) {
39 return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end();
42 } // namespace
44 CrasAudioHandler::AudioObserver::AudioObserver() {
47 CrasAudioHandler::AudioObserver::~AudioObserver() {
50 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
53 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
56 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
59 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
62 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
65 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
68 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
71 // static
72 void CrasAudioHandler::Initialize(
73 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
74 CHECK(!g_cras_audio_handler);
75 g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
78 // static
79 void CrasAudioHandler::InitializeForTesting() {
80 CHECK(!g_cras_audio_handler);
81 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
84 // static
85 void CrasAudioHandler::Shutdown() {
86 CHECK(g_cras_audio_handler);
87 delete g_cras_audio_handler;
88 g_cras_audio_handler = NULL;
91 // static
92 bool CrasAudioHandler::IsInitialized() {
93 return g_cras_audio_handler != NULL;
96 // static
97 CrasAudioHandler* CrasAudioHandler::Get() {
98 CHECK(g_cras_audio_handler)
99 << "CrasAudioHandler::Get() called before Initialize().";
100 return g_cras_audio_handler;
103 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
104 observers_.AddObserver(observer);
107 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
108 observers_.RemoveObserver(observer);
111 bool CrasAudioHandler::HasKeyboardMic() {
112 return GetKeyboardMic() != NULL;
115 bool CrasAudioHandler::IsOutputMuted() {
116 return output_mute_on_;
119 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
120 const AudioDevice* device = GetDeviceFromId(device_id);
121 if (!device)
122 return false;
123 DCHECK(!device->is_input);
124 return audio_pref_handler_->GetMuteValue(*device);
127 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
128 return output_volume_ <= kMuteThresholdPercent;
131 bool CrasAudioHandler::IsInputMuted() {
132 return input_mute_on_;
135 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
136 const AudioDevice* device = GetDeviceFromId(device_id);
137 if (!device)
138 return false;
139 DCHECK(device->is_input);
140 // We don't record input mute state for each device in the prefs,
141 // for any non-active input device, we assume mute is off.
142 if (device->id == active_input_node_id_)
143 return input_mute_on_;
144 return false;
147 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
148 return kMuteThresholdPercent;
151 int CrasAudioHandler::GetOutputVolumePercent() {
152 return output_volume_;
155 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
156 if (device_id == active_output_node_id_) {
157 return output_volume_;
158 } else {
159 const AudioDevice* device = GetDeviceFromId(device_id);
160 return static_cast<int>(audio_pref_handler_->GetOutputVolumeValue(device));
164 int CrasAudioHandler::GetInputGainPercent() {
165 return input_gain_;
168 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
169 if (device_id == active_input_node_id_) {
170 return input_gain_;
171 } else {
172 const AudioDevice* device = GetDeviceFromId(device_id);
173 return static_cast<int>(audio_pref_handler_->GetInputGainValue(device));
177 uint64 CrasAudioHandler::GetPrimaryActiveOutputNode() const {
178 return active_output_node_id_;
181 uint64 CrasAudioHandler::GetPrimaryActiveInputNode() const {
182 return active_input_node_id_;
185 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
186 device_list->clear();
187 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
188 it != audio_devices_.end(); ++it)
189 device_list->push_back(it->second);
192 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice* device) const {
193 const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
194 if (!active_device || !device)
195 return false;
196 *device = *active_device;
197 return true;
200 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
201 const AudioDevice* keyboard_mic = GetKeyboardMic();
202 if (!keyboard_mic)
203 return;
204 // Keyboard mic is invisible to chromeos users. It is always added or removed
205 // as additional active node.
206 DCHECK(active_input_node_id_ && active_input_node_id_ != keyboard_mic->id);
207 if (active)
208 AddActiveNode(keyboard_mic->id, true);
209 else
210 RemoveActiveNodeInternal(keyboard_mic->id, true);
213 void CrasAudioHandler::AddActiveNode(uint64 node_id, bool notify) {
214 const AudioDevice* device = GetDeviceFromId(node_id);
215 if (!device) {
216 VLOG(1) << "AddActiveInputNode: Cannot find device id="
217 << "0x" << std::hex << node_id;
218 return;
221 // If there is no primary active device, set |node_id| to primary active node.
222 if ((device->is_input && !active_input_node_id_) ||
223 (!device->is_input && !active_output_node_id_)) {
224 SwitchToDevice(*device, notify);
225 return;
228 AddAdditionalActiveNode(node_id, notify);
231 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList& new_active_ids) {
232 // Flags for whether there are input or output nodes passed in from
233 // |new_active_ids|. If there are no input nodes passed in, we will not
234 // make any change for input nodes; same for the output nodes.
235 bool request_input_change = false;
236 bool request_output_change = false;
238 // Flags for whether we will actually change active status of input
239 // or output nodes.
240 bool make_input_change = false;
241 bool make_output_change = false;
243 NodeIdList nodes_to_activate;
244 for (size_t i = 0; i < new_active_ids.size(); ++i) {
245 const AudioDevice* device = GetDeviceFromId(new_active_ids[i]);
246 if (device) {
247 if (device->is_input)
248 request_input_change = true;
249 else
250 request_output_change = true;
252 // If the new active device is already active, keep it as active.
253 if (device->active)
254 continue;
256 nodes_to_activate.push_back(new_active_ids[i]);
257 if (device->is_input)
258 make_input_change = true;
259 else
260 make_output_change = true;
264 // Remove all existing active devices that are not in the |new_active_ids|
265 // list.
266 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
267 it != audio_devices_.end(); ++it) {
268 AudioDevice device = it->second;
269 // Remove the existing active input or output nodes that are not in the new
270 // active node list if there are new input or output nodes specified.
271 if (device.active) {
272 if ((device.is_input && request_input_change &&
273 !IsInNodeList(device.id, new_active_ids))) {
274 make_input_change = true;
275 RemoveActiveNodeInternal(device.id, false); // no notification.
276 } else if (!device.is_input && request_output_change &&
277 !IsInNodeList(device.id, new_active_ids)) {
278 make_output_change = true;
279 RemoveActiveNodeInternal(device.id, false); // no notification.
284 // Adds the new active devices.
285 for (size_t i = 0; i < nodes_to_activate.size(); ++i)
286 AddActiveNode(nodes_to_activate[i], false); // no notification.
288 // Notify the active nodes change now.
289 if (make_input_change)
290 NotifyActiveNodeChanged(true);
291 if (make_output_change)
292 NotifyActiveNodeChanged(false);
295 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap) {
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.type == AUDIO_TYPE_INTERNAL_SPEAKER) {
301 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
302 device.id, swap);
303 break;
308 bool CrasAudioHandler::has_alternative_input() const {
309 return has_alternative_input_;
312 bool CrasAudioHandler::has_alternative_output() const {
313 return has_alternative_output_;
316 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
317 // Set all active devices to the same volume.
318 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
319 it != audio_devices_.end();
320 it++) {
321 const AudioDevice& device = it->second;
322 if (!device.is_input && device.active)
323 SetOutputNodeVolumePercent(device.id, volume_percent);
327 // TODO: Rename the 'Percent' to something more meaningful.
328 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
329 // TODO(jennyz): Should we set all input devices' gain to the same level?
330 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
331 it != audio_devices_.end();
332 it++) {
333 const AudioDevice& device = it->second;
334 if (device.is_input && device.active)
335 SetInputNodeGainPercent(active_input_node_id_, gain_percent);
339 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
340 SetOutputVolumePercent(output_volume_ + adjust_by_percent);
343 void CrasAudioHandler::SetOutputMute(bool mute_on) {
344 if (!SetOutputMuteInternal(mute_on))
345 return;
347 // Save the mute state for all active output audio devices.
348 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
349 it != audio_devices_.end();
350 it++) {
351 const AudioDevice& device = it->second;
352 if (!device.is_input && device.active) {
353 audio_pref_handler_->SetMuteValue(device, output_mute_on_);
357 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
360 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
361 if (output_volume_ <= kMuteThresholdPercent) {
362 // Avoid the situation when sound has been unmuted, but the volume
363 // is set to a very low value, so user still can't hear any sound.
364 SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
368 void CrasAudioHandler::SetInputMute(bool mute_on) {
369 if (!SetInputMuteInternal(mute_on))
370 return;
372 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
375 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id, bool notify) {
376 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
377 SetActiveOutputNode(node_id);
378 if (notify)
379 NotifyActiveNodeChanged(false);
382 void CrasAudioHandler::SetActiveInputNode(uint64 node_id, bool notify) {
383 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
384 SetActiveInputNode(node_id);
385 if (notify)
386 NotifyActiveNodeChanged(true);
389 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
390 int value) {
391 const AudioDevice* device = GetDeviceFromId(device_id);
392 if (!device)
393 return;
395 if (device->is_input)
396 SetInputNodeGainPercent(device_id, value);
397 else
398 SetOutputNodeVolumePercent(device_id, value);
401 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
402 if (device_id == active_output_node_id_) {
403 SetOutputMute(mute_on);
404 return;
405 } else if (device_id == active_input_node_id_) {
406 VLOG(1) << "SetMuteForDevice sets active input device id="
407 << "0x" << std::hex << device_id << " mute=" << mute_on;
408 SetInputMute(mute_on);
409 return;
412 const AudioDevice* device = GetDeviceFromId(device_id);
413 // Input device's mute state is not recorded in the pref. crbug.com/365050.
414 if (device && !device->is_input)
415 audio_pref_handler_->SetMuteValue(*device, mute_on);
418 void CrasAudioHandler::LogErrors() {
419 log_errors_ = true;
422 CrasAudioHandler::CrasAudioHandler(
423 scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
424 : audio_pref_handler_(audio_pref_handler),
425 output_mute_on_(false),
426 input_mute_on_(false),
427 output_volume_(0),
428 input_gain_(0),
429 active_output_node_id_(0),
430 active_input_node_id_(0),
431 has_alternative_input_(false),
432 has_alternative_output_(false),
433 output_mute_locked_(false),
434 input_mute_locked_(false),
435 log_errors_(false),
436 weak_ptr_factory_(this) {
437 if (!audio_pref_handler.get())
438 return;
439 // If the DBusThreadManager or the CrasAudioClient aren't available, there
440 // isn't much we can do. This should only happen when running tests.
441 if (!chromeos::DBusThreadManager::IsInitialized() ||
442 !chromeos::DBusThreadManager::Get() ||
443 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
444 return;
445 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
446 audio_pref_handler_->AddAudioPrefObserver(this);
447 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
448 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
449 AddObserver(this);
451 InitializeAudioState();
454 CrasAudioHandler::~CrasAudioHandler() {
455 if (!chromeos::DBusThreadManager::IsInitialized() ||
456 !chromeos::DBusThreadManager::Get() ||
457 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
458 return;
459 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
460 RemoveObserver(this);
461 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
462 RemoveObserver(this);
463 if (audio_pref_handler_.get())
464 audio_pref_handler_->RemoveAudioPrefObserver(this);
465 audio_pref_handler_ = NULL;
468 void CrasAudioHandler::AudioClientRestarted() {
469 // Make sure the logging is enabled in case cras server
470 // restarts after crashing.
471 LogErrors();
472 InitializeAudioState();
475 void CrasAudioHandler::NodesChanged() {
476 // Refresh audio nodes data.
477 GetNodes();
480 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
481 if (active_output_node_id_ == node_id)
482 return;
484 // Active audio output device should always be changed by chrome.
485 // During system boot, cras may change active input to unknown device 0x1,
486 // we don't need to log it, since it is not an valid device.
487 if (GetDeviceFromId(node_id)) {
488 LOG_IF(WARNING, log_errors_)
489 << "Active output node changed unexpectedly by system node_id="
490 << "0x" << std::hex << node_id;
494 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
495 if (active_input_node_id_ == node_id)
496 return;
498 // Active audio input device should always be changed by chrome.
499 // During system boot, cras may change active input to unknown device 0x2,
500 // we don't need to log it, since it is not an valid device.
501 if (GetDeviceFromId(node_id)) {
502 LOG_IF(WARNING, log_errors_)
503 << "Active input node changed unexpectedly by system node_id="
504 << "0x" << std::hex << node_id;
508 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
509 ApplyAudioPolicy();
512 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
513 // Enable logging after cras server is started, which will be after
514 // EmitLoginPromptVisible.
515 LogErrors();
518 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
519 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
520 if (it == audio_devices_.end())
521 return NULL;
523 return &(it->second);
526 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
527 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
528 it != audio_devices_.end(); it++) {
529 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
530 return &(it->second);
532 return NULL;
535 void CrasAudioHandler::SetupAudioInputState() {
536 // Set the initial audio state to the ones read from audio prefs.
537 const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
538 if (!device) {
539 LOG_IF(ERROR, log_errors_)
540 << "Can't set up audio state for unknown input device id ="
541 << "0x" << std::hex << active_input_node_id_;
542 return;
544 input_gain_ = audio_pref_handler_->GetInputGainValue(device);
545 VLOG(1) << "SetupAudioInputState for active device id="
546 << "0x" << std::hex << device->id << " mute=" << input_mute_on_;
547 SetInputMuteInternal(input_mute_on_);
548 // TODO(rkc,jennyz): Set input gain once we decide on how to store
549 // the gain values since the range and step are both device specific.
552 void CrasAudioHandler::SetupAudioOutputState() {
553 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
554 if (!device) {
555 LOG_IF(ERROR, log_errors_)
556 << "Can't set up audio state for unknown output device id ="
557 << "0x" << std::hex << active_output_node_id_;
558 return;
560 DCHECK(!device->is_input);
561 output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
562 output_volume_ = audio_pref_handler_->GetOutputVolumeValue(device);
564 SetOutputMuteInternal(output_mute_on_);
565 SetOutputNodeVolume(active_output_node_id_, output_volume_);
568 // This sets up the state of an additional active node.
569 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64 node_id) {
570 const AudioDevice* device = GetDeviceFromId(node_id);
571 if (!device) {
572 VLOG(1) << "Can't set up audio state for unknown device id ="
573 << "0x" << std::hex << node_id;
574 return;
577 DCHECK(node_id != active_output_node_id_ && node_id != active_input_node_id_);
579 // Note: The mute state is a system wide state, we don't set mute per device,
580 // but just keep the mute state consistent for the active node in prefs.
581 // The output volume should be set to the same value for all active output
582 // devices. For input devices, we don't restore their gain value so far.
583 // TODO(jennyz): crbug.com/417418, track the status for the decison if
584 // we should persist input gain value in prefs.
585 if (!device->is_input) {
586 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
587 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
591 void CrasAudioHandler::InitializeAudioState() {
592 ApplyAudioPolicy();
593 GetNodes();
596 void CrasAudioHandler::ApplyAudioPolicy() {
597 output_mute_locked_ = false;
598 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
599 // Mute the device, but do not update the preference.
600 SetOutputMuteInternal(true);
601 output_mute_locked_ = true;
602 } else {
603 // Restore the mute state.
604 const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
605 if (device)
606 SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
609 input_mute_locked_ = false;
610 if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
611 VLOG(1) << "Audio input allowed by policy, sets input id="
612 << "0x" << std::hex << active_input_node_id_ << " mute=false";
613 SetInputMuteInternal(false);
614 } else {
615 VLOG(0) << "Audio input NOT allowed by policy, sets input id="
616 << "0x" << std::hex << active_input_node_id_ << " mute=true";
617 SetInputMuteInternal(true);
618 input_mute_locked_ = true;
622 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
623 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
624 SetOutputNodeVolume(node_id, volume);
627 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64 node_id,
628 int volume_percent) {
629 const AudioDevice* device = this->GetDeviceFromId(node_id);
630 if (!device || device->is_input)
631 return;
633 volume_percent = min(max(volume_percent, 0), 100);
634 if (volume_percent <= kMuteThresholdPercent)
635 volume_percent = 0;
636 if (node_id == active_output_node_id_)
637 output_volume_ = volume_percent;
639 audio_pref_handler_->SetVolumeGainValue(*device, volume_percent);
641 if (device->active) {
642 SetOutputNodeVolume(node_id, volume_percent);
643 FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
647 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
648 if (output_mute_locked_)
649 return false;
651 output_mute_on_ = mute_on;
652 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
653 SetOutputUserMute(mute_on);
654 return true;
657 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
658 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
659 SetInputNodeGain(node_id, gain);
662 void CrasAudioHandler::SetInputNodeGainPercent(uint64 node_id,
663 int gain_percent) {
664 const AudioDevice* device = GetDeviceFromId(node_id);
665 if (!device || !device->is_input)
666 return;
668 // NOTE: We do not sanitize input gain values since the range is completely
669 // dependent on the device.
670 if (active_input_node_id_ == node_id)
671 input_gain_ = gain_percent;
673 audio_pref_handler_->SetVolumeGainValue(*device, gain_percent);
675 if (device->active) {
676 SetInputNodeGain(node_id, gain_percent);
677 FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
681 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
682 if (input_mute_locked_)
683 return false;
685 input_mute_on_ = mute_on;
686 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
687 SetInputMute(mute_on);
688 return true;
691 void CrasAudioHandler::GetNodes() {
692 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
693 base::Bind(&CrasAudioHandler::HandleGetNodes,
694 weak_ptr_factory_.GetWeakPtr()),
695 base::Bind(&CrasAudioHandler::HandleGetNodesError,
696 weak_ptr_factory_.GetWeakPtr()));
699 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
700 uint64* current_active_node_id) {
701 // If the device we want to switch to is already the current active device,
702 // do nothing.
703 if (new_active_device.active &&
704 new_active_device.id == *current_active_node_id) {
705 return false;
708 // Reset all other input or output devices' active status. The active audio
709 // device from the previous user session can be remembered by cras, but not
710 // in chrome. see crbug.com/273271.
711 for (AudioDeviceMap::iterator it = audio_devices_.begin();
712 it != audio_devices_.end(); ++it) {
713 if (it->second.is_input == new_active_device.is_input &&
714 it->second.id != new_active_device.id)
715 it->second.active = false;
718 // Set the current active input/output device to the new_active_device.
719 *current_active_node_id = new_active_device.id;
720 audio_devices_[*current_active_node_id].active = true;
721 return true;
724 bool CrasAudioHandler::NonActiveDeviceUnplugged(
725 size_t old_devices_size,
726 size_t new_devices_size,
727 uint64 current_active_node) {
728 return (new_devices_size < old_devices_size &&
729 GetDeviceFromId(current_active_node));
732 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, bool notify) {
733 if (device.is_input) {
734 if (!ChangeActiveDevice(device, &active_input_node_id_))
735 return;
736 SetupAudioInputState();
737 SetActiveInputNode(active_input_node_id_, notify);
738 } else {
739 if (!ChangeActiveDevice(device, &active_output_node_id_))
740 return;
741 SetupAudioOutputState();
742 SetActiveOutputNode(active_output_node_id_, notify);
746 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
747 bool is_input) {
748 size_t num_old_devices = 0;
749 size_t num_new_devices = 0;
750 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
751 it != audio_devices_.end(); ++it) {
752 if (is_input == it->second.is_input)
753 ++num_old_devices;
756 for (AudioNodeList::const_iterator it = new_nodes.begin();
757 it != new_nodes.end(); ++it) {
758 if (is_input == it->is_input) {
759 ++num_new_devices;
760 // Look to see if the new device not in the old device list.
761 AudioDevice device(*it);
762 if (FoundNewOrChangedDevice(device))
763 return true;
766 return num_old_devices != num_new_devices;
769 bool CrasAudioHandler::FoundNewOrChangedDevice(const AudioDevice& device) {
770 const AudioDevice* device_found = GetDeviceFromId(device.id);
771 if (!device_found)
772 return true;
774 if (!IsSameAudioDevice(device, *device_found)) {
775 LOG(WARNING) << "Different Audio devices with same id:"
776 << " new device: " << device.ToString()
777 << " old device: " << device_found->ToString();
778 return true;
779 } else if (device.active != device_found->active) {
780 return true;
783 return false;
786 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) {
787 if (is_input)
788 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
789 else
790 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
793 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
794 const AudioNodeList& nodes) {
795 size_t old_audio_devices_size = audio_devices_.size();
796 bool output_devices_changed = HasDeviceChange(nodes, false);
797 bool input_devices_changed = HasDeviceChange(nodes, true);
798 audio_devices_.clear();
799 has_alternative_input_ = false;
800 has_alternative_output_ = false;
802 while (!input_devices_pq_.empty())
803 input_devices_pq_.pop();
804 while (!output_devices_pq_.empty())
805 output_devices_pq_.pop();
807 for (size_t i = 0; i < nodes.size(); ++i) {
808 AudioDevice device(nodes[i]);
809 audio_devices_[device.id] = device;
811 if (!has_alternative_input_ &&
812 device.is_input &&
813 device.type != AUDIO_TYPE_INTERNAL_MIC &&
814 device.type != AUDIO_TYPE_KEYBOARD_MIC) {
815 has_alternative_input_ = true;
816 } else if (!has_alternative_output_ &&
817 !device.is_input &&
818 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
819 has_alternative_output_ = true;
822 if (device.is_input)
823 input_devices_pq_.push(device);
824 else
825 output_devices_pq_.push(device);
828 // If audio nodes change is caused by unplugging some non-active audio
829 // devices, the previously set active audio device will stay active.
830 // Otherwise, switch to a new active audio device according to their priority.
831 if (input_devices_changed &&
832 !NonActiveDeviceUnplugged(old_audio_devices_size,
833 audio_devices_.size(),
834 active_input_node_id_) &&
835 !input_devices_pq_.empty())
836 SwitchToDevice(input_devices_pq_.top(), true);
837 if (output_devices_changed &&
838 !NonActiveDeviceUnplugged(old_audio_devices_size,
839 audio_devices_.size(),
840 active_output_node_id_) &&
841 !output_devices_pq_.empty()) {
842 SwitchToDevice(output_devices_pq_.top(), true);
846 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
847 bool success) {
848 if (!success) {
849 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
850 return;
853 UpdateDevicesAndSwitchActive(node_list);
854 FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
857 void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
858 const std::string& error_msg) {
859 LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
860 << error_name << ": " << error_msg;
863 void CrasAudioHandler::AddAdditionalActiveNode(uint64 node_id, bool notify) {
864 const AudioDevice* device = GetDeviceFromId(node_id);
865 if (!device) {
866 VLOG(1) << "AddActiveInputNode: Cannot find device id="
867 << "0x" << std::hex << node_id;
868 return;
871 audio_devices_[node_id].active = true;
872 SetupAdditionalActiveAudioNodeState(node_id);
874 if (device->is_input) {
875 DCHECK(node_id != active_input_node_id_);
876 chromeos::DBusThreadManager::Get()
877 ->GetCrasAudioClient()
878 ->AddActiveInputNode(node_id);
879 if (notify)
880 NotifyActiveNodeChanged(true);
881 } else {
882 DCHECK(node_id != active_output_node_id_);
883 chromeos::DBusThreadManager::Get()
884 ->GetCrasAudioClient()
885 ->AddActiveOutputNode(node_id);
886 if (notify)
887 NotifyActiveNodeChanged(false);
891 void CrasAudioHandler::RemoveActiveNodeInternal(uint64 node_id, bool notify) {
892 const AudioDevice* device = GetDeviceFromId(node_id);
893 if (!device) {
894 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
895 << "0x" << std::hex << node_id;
896 return;
899 audio_devices_[node_id].active = false;
900 if (device->is_input) {
901 if (node_id == active_input_node_id_)
902 active_input_node_id_ = 0;
903 chromeos::DBusThreadManager::Get()
904 ->GetCrasAudioClient()
905 ->RemoveActiveInputNode(node_id);
906 if (notify)
907 NotifyActiveNodeChanged(true);
908 } else {
909 if (node_id == active_output_node_id_)
910 active_output_node_id_ = 0;
911 chromeos::DBusThreadManager::Get()
912 ->GetCrasAudioClient()
913 ->RemoveActiveOutputNode(node_id);
914 if (notify)
915 NotifyActiveNodeChanged(false);
919 } // namespace chromeos