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"
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"
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 bool IsNodeInTheList(uint64 node_id
, const AudioNodeList
& node_list
) {
43 for (size_t i
= 0; i
< node_list
.size(); ++i
) {
44 if (node_id
== node_list
[i
].id
)
52 CrasAudioHandler::AudioObserver::AudioObserver() {
55 CrasAudioHandler::AudioObserver::~AudioObserver() {
58 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
61 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
64 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
67 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
70 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
73 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
76 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
80 void CrasAudioHandler::Initialize(
81 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
) {
82 CHECK(!g_cras_audio_handler
);
83 g_cras_audio_handler
= new CrasAudioHandler(audio_pref_handler
);
87 void CrasAudioHandler::InitializeForTesting() {
88 CHECK(!g_cras_audio_handler
);
89 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
93 void CrasAudioHandler::Shutdown() {
94 CHECK(g_cras_audio_handler
);
95 delete g_cras_audio_handler
;
96 g_cras_audio_handler
= NULL
;
100 bool CrasAudioHandler::IsInitialized() {
101 return g_cras_audio_handler
!= NULL
;
105 CrasAudioHandler
* CrasAudioHandler::Get() {
106 CHECK(g_cras_audio_handler
)
107 << "CrasAudioHandler::Get() called before Initialize().";
108 return g_cras_audio_handler
;
111 void CrasAudioHandler::AddAudioObserver(AudioObserver
* observer
) {
112 observers_
.AddObserver(observer
);
115 void CrasAudioHandler::RemoveAudioObserver(AudioObserver
* observer
) {
116 observers_
.RemoveObserver(observer
);
119 bool CrasAudioHandler::HasKeyboardMic() {
120 return GetKeyboardMic() != NULL
;
123 bool CrasAudioHandler::IsOutputMuted() {
124 return output_mute_on_
;
127 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id
) {
128 const AudioDevice
* device
= GetDeviceFromId(device_id
);
131 DCHECK(!device
->is_input
);
132 return audio_pref_handler_
->GetMuteValue(*device
);
135 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
136 return output_volume_
<= kMuteThresholdPercent
;
139 bool CrasAudioHandler::IsInputMuted() {
140 return input_mute_on_
;
143 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id
) {
144 const AudioDevice
* device
= GetDeviceFromId(device_id
);
147 DCHECK(device
->is_input
);
148 // We don't record input mute state for each device in the prefs,
149 // for any non-active input device, we assume mute is off.
150 if (device
->id
== active_input_node_id_
)
151 return input_mute_on_
;
155 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
156 return kMuteThresholdPercent
;
159 int CrasAudioHandler::GetOutputVolumePercent() {
160 return output_volume_
;
163 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id
) {
164 if (device_id
== active_output_node_id_
) {
165 return output_volume_
;
167 const AudioDevice
* device
= GetDeviceFromId(device_id
);
168 return static_cast<int>(audio_pref_handler_
->GetOutputVolumeValue(device
));
172 int CrasAudioHandler::GetInputGainPercent() {
176 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id
) {
177 if (device_id
== active_input_node_id_
) {
180 const AudioDevice
* device
= GetDeviceFromId(device_id
);
181 return static_cast<int>(audio_pref_handler_
->GetInputGainValue(device
));
185 uint64
CrasAudioHandler::GetPrimaryActiveOutputNode() const {
186 return active_output_node_id_
;
189 uint64
CrasAudioHandler::GetPrimaryActiveInputNode() const {
190 return active_input_node_id_
;
193 void CrasAudioHandler::GetAudioDevices(AudioDeviceList
* device_list
) const {
194 device_list
->clear();
195 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
196 it
!= audio_devices_
.end(); ++it
)
197 device_list
->push_back(it
->second
);
200 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice
* device
) const {
201 const AudioDevice
* active_device
= GetDeviceFromId(active_output_node_id_
);
202 if (!active_device
|| !device
)
204 *device
= *active_device
;
208 void CrasAudioHandler::SetKeyboardMicActive(bool active
) {
209 const AudioDevice
* keyboard_mic
= GetKeyboardMic();
212 // Keyboard mic is invisible to chromeos users. It is always added or removed
213 // as additional active node.
214 DCHECK(active_input_node_id_
&& active_input_node_id_
!= keyboard_mic
->id
);
216 AddActiveNode(keyboard_mic
->id
, true);
218 RemoveActiveNodeInternal(keyboard_mic
->id
, true);
221 void CrasAudioHandler::AddActiveNode(uint64 node_id
, bool notify
) {
222 const AudioDevice
* device
= GetDeviceFromId(node_id
);
224 VLOG(1) << "AddActiveInputNode: Cannot find device id="
225 << "0x" << std::hex
<< node_id
;
229 // If there is no primary active device, set |node_id| to primary active node.
230 if ((device
->is_input
&& !active_input_node_id_
) ||
231 (!device
->is_input
&& !active_output_node_id_
)) {
232 SwitchToDevice(*device
, notify
);
236 AddAdditionalActiveNode(node_id
, notify
);
239 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList
& new_active_ids
) {
240 // Flags for whether there are input or output nodes passed in from
241 // |new_active_ids|. If there are no input nodes passed in, we will not
242 // make any change for input nodes; same for the output nodes.
243 bool request_input_change
= false;
244 bool request_output_change
= false;
246 // Flags for whether we will actually change active status of input
248 bool make_input_change
= false;
249 bool make_output_change
= false;
251 NodeIdList nodes_to_activate
;
252 for (size_t i
= 0; i
< new_active_ids
.size(); ++i
) {
253 const AudioDevice
* device
= GetDeviceFromId(new_active_ids
[i
]);
255 if (device
->is_input
)
256 request_input_change
= true;
258 request_output_change
= true;
260 // If the new active device is already active, keep it as active.
264 nodes_to_activate
.push_back(new_active_ids
[i
]);
265 if (device
->is_input
)
266 make_input_change
= true;
268 make_output_change
= true;
272 // Remove all existing active devices that are not in the |new_active_ids|
274 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
275 it
!= audio_devices_
.end(); ++it
) {
276 AudioDevice device
= it
->second
;
277 // Remove the existing active input or output nodes that are not in the new
278 // active node list if there are new input or output nodes specified.
280 if ((device
.is_input
&& request_input_change
&&
281 !IsInNodeList(device
.id
, new_active_ids
))) {
282 make_input_change
= true;
283 RemoveActiveNodeInternal(device
.id
, false); // no notification.
284 } else if (!device
.is_input
&& request_output_change
&&
285 !IsInNodeList(device
.id
, new_active_ids
)) {
286 make_output_change
= true;
287 RemoveActiveNodeInternal(device
.id
, false); // no notification.
292 // Adds the new active devices.
293 for (size_t i
= 0; i
< nodes_to_activate
.size(); ++i
)
294 AddActiveNode(nodes_to_activate
[i
], false); // no notification.
296 // Notify the active nodes change now.
297 if (make_input_change
)
298 NotifyActiveNodeChanged(true);
299 if (make_output_change
)
300 NotifyActiveNodeChanged(false);
303 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap
) {
304 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
305 it
!= audio_devices_
.end();
307 const AudioDevice
& device
= it
->second
;
308 if (!device
.is_input
&& device
.type
== AUDIO_TYPE_INTERNAL_SPEAKER
) {
309 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
316 bool CrasAudioHandler::has_alternative_input() const {
317 return has_alternative_input_
;
320 bool CrasAudioHandler::has_alternative_output() const {
321 return has_alternative_output_
;
324 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent
) {
325 // Set all active devices to the same volume.
326 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
327 it
!= audio_devices_
.end();
329 const AudioDevice
& device
= it
->second
;
330 if (!device
.is_input
&& device
.active
)
331 SetOutputNodeVolumePercent(device
.id
, volume_percent
);
335 // TODO: Rename the 'Percent' to something more meaningful.
336 void CrasAudioHandler::SetInputGainPercent(int gain_percent
) {
337 // TODO(jennyz): Should we set all input devices' gain to the same level?
338 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
339 it
!= audio_devices_
.end();
341 const AudioDevice
& device
= it
->second
;
342 if (device
.is_input
&& device
.active
)
343 SetInputNodeGainPercent(active_input_node_id_
, gain_percent
);
347 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent
) {
348 SetOutputVolumePercent(output_volume_
+ adjust_by_percent
);
351 void CrasAudioHandler::SetOutputMute(bool mute_on
) {
352 if (!SetOutputMuteInternal(mute_on
))
355 // Save the mute state for all active output audio devices.
356 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
357 it
!= audio_devices_
.end();
359 const AudioDevice
& device
= it
->second
;
360 if (!device
.is_input
&& device
.active
) {
361 audio_pref_handler_
->SetMuteValue(device
, output_mute_on_
);
365 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnOutputMuteChanged());
368 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
369 if (output_volume_
<= kMuteThresholdPercent
) {
370 // Avoid the situation when sound has been unmuted, but the volume
371 // is set to a very low value, so user still can't hear any sound.
372 SetOutputVolumePercent(kDefaultUnmuteVolumePercent
);
376 void CrasAudioHandler::SetInputMute(bool mute_on
) {
377 SetInputMuteInternal(mute_on
);
378 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnInputMuteChanged());
381 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id
, bool notify
) {
382 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
383 SetActiveOutputNode(node_id
);
385 NotifyActiveNodeChanged(false);
388 void CrasAudioHandler::SetActiveInputNode(uint64 node_id
, bool notify
) {
389 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
390 SetActiveInputNode(node_id
);
392 NotifyActiveNodeChanged(true);
395 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id
,
397 const AudioDevice
* device
= GetDeviceFromId(device_id
);
401 if (device
->is_input
)
402 SetInputNodeGainPercent(device_id
, value
);
404 SetOutputNodeVolumePercent(device_id
, value
);
407 void CrasAudioHandler::SetMuteForDevice(uint64 device_id
, bool mute_on
) {
408 if (device_id
== active_output_node_id_
) {
409 SetOutputMute(mute_on
);
411 } else if (device_id
== active_input_node_id_
) {
412 VLOG(1) << "SetMuteForDevice sets active input device id="
413 << "0x" << std::hex
<< device_id
<< " mute=" << mute_on
;
414 SetInputMute(mute_on
);
418 const AudioDevice
* device
= GetDeviceFromId(device_id
);
419 // Input device's mute state is not recorded in the pref. crbug.com/365050.
420 if (device
&& !device
->is_input
)
421 audio_pref_handler_
->SetMuteValue(*device
, mute_on
);
424 void CrasAudioHandler::LogErrors() {
428 CrasAudioHandler::CrasAudioHandler(
429 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
)
430 : audio_pref_handler_(audio_pref_handler
),
431 output_mute_on_(false),
432 input_mute_on_(false),
435 active_output_node_id_(0),
436 active_input_node_id_(0),
437 has_alternative_input_(false),
438 has_alternative_output_(false),
439 output_mute_locked_(false),
441 weak_ptr_factory_(this) {
442 if (!audio_pref_handler
.get())
444 // If the DBusThreadManager or the CrasAudioClient aren't available, there
445 // isn't much we can do. This should only happen when running tests.
446 if (!chromeos::DBusThreadManager::IsInitialized() ||
447 !chromeos::DBusThreadManager::Get() ||
448 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
450 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
451 audio_pref_handler_
->AddAudioPrefObserver(this);
452 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
453 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
456 InitializeAudioState();
459 CrasAudioHandler::~CrasAudioHandler() {
460 if (!chromeos::DBusThreadManager::IsInitialized() ||
461 !chromeos::DBusThreadManager::Get() ||
462 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
464 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
465 RemoveObserver(this);
466 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
467 RemoveObserver(this);
468 if (audio_pref_handler_
.get())
469 audio_pref_handler_
->RemoveAudioPrefObserver(this);
470 audio_pref_handler_
= NULL
;
473 void CrasAudioHandler::AudioClientRestarted() {
474 // Make sure the logging is enabled in case cras server
475 // restarts after crashing.
477 InitializeAudioState();
480 void CrasAudioHandler::NodesChanged() {
481 // Refresh audio nodes data.
485 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id
) {
486 if (active_output_node_id_
== node_id
)
489 // Active audio output device should always be changed by chrome.
490 // During system boot, cras may change active input to unknown device 0x1,
491 // we don't need to log it, since it is not an valid device.
492 if (GetDeviceFromId(node_id
)) {
493 LOG_IF(WARNING
, log_errors_
)
494 << "Active output node changed unexpectedly by system node_id="
495 << "0x" << std::hex
<< node_id
;
499 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id
) {
500 if (active_input_node_id_
== node_id
)
503 // Active audio input device should always be changed by chrome.
504 // During system boot, cras may change active input to unknown device 0x2,
505 // we don't need to log it, since it is not an valid device.
506 if (GetDeviceFromId(node_id
)) {
507 LOG_IF(WARNING
, log_errors_
)
508 << "Active input node changed unexpectedly by system node_id="
509 << "0x" << std::hex
<< node_id
;
513 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
517 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
518 // Enable logging after cras server is started, which will be after
519 // EmitLoginPromptVisible.
523 const AudioDevice
* CrasAudioHandler::GetDeviceFromId(uint64 device_id
) const {
524 AudioDeviceMap::const_iterator it
= audio_devices_
.find(device_id
);
525 if (it
== audio_devices_
.end())
528 return &(it
->second
);
531 const AudioDevice
* CrasAudioHandler::GetKeyboardMic() const {
532 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
533 it
!= audio_devices_
.end(); it
++) {
534 if (it
->second
.is_input
&& it
->second
.type
== AUDIO_TYPE_KEYBOARD_MIC
)
535 return &(it
->second
);
540 void CrasAudioHandler::SetupAudioInputState() {
541 // Set the initial audio state to the ones read from audio prefs.
542 const AudioDevice
* device
= GetDeviceFromId(active_input_node_id_
);
544 LOG_IF(ERROR
, log_errors_
)
545 << "Can't set up audio state for unknown input device id ="
546 << "0x" << std::hex
<< active_input_node_id_
;
549 input_gain_
= audio_pref_handler_
->GetInputGainValue(device
);
550 VLOG(1) << "SetupAudioInputState for active device id="
551 << "0x" << std::hex
<< device
->id
<< " mute=" << input_mute_on_
;
552 SetInputMuteInternal(input_mute_on_
);
553 // TODO(rkc,jennyz): Set input gain once we decide on how to store
554 // the gain values since the range and step are both device specific.
557 void CrasAudioHandler::SetupAudioOutputState() {
558 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
560 LOG_IF(ERROR
, log_errors_
)
561 << "Can't set up audio state for unknown output device id ="
562 << "0x" << std::hex
<< active_output_node_id_
;
565 DCHECK(!device
->is_input
);
566 output_mute_on_
= audio_pref_handler_
->GetMuteValue(*device
);
567 output_volume_
= audio_pref_handler_
->GetOutputVolumeValue(device
);
569 SetOutputMuteInternal(output_mute_on_
);
570 SetOutputNodeVolume(active_output_node_id_
, output_volume_
);
573 // This sets up the state of an additional active node.
574 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64 node_id
) {
575 const AudioDevice
* device
= GetDeviceFromId(node_id
);
577 VLOG(1) << "Can't set up audio state for unknown device id ="
578 << "0x" << std::hex
<< node_id
;
582 DCHECK(node_id
!= active_output_node_id_
&& node_id
!= active_input_node_id_
);
584 // Note: The mute state is a system wide state, we don't set mute per device,
585 // but just keep the mute state consistent for the active node in prefs.
586 // The output volume should be set to the same value for all active output
587 // devices. For input devices, we don't restore their gain value so far.
588 // TODO(jennyz): crbug.com/417418, track the status for the decison if
589 // we should persist input gain value in prefs.
590 if (!device
->is_input
) {
591 audio_pref_handler_
->SetMuteValue(*device
, IsOutputMuted());
592 SetOutputNodeVolumePercent(node_id
, GetOutputVolumePercent());
596 void CrasAudioHandler::InitializeAudioState() {
601 void CrasAudioHandler::ApplyAudioPolicy() {
602 output_mute_locked_
= false;
603 if (!audio_pref_handler_
->GetAudioOutputAllowedValue()) {
604 // Mute the device, but do not update the preference.
605 SetOutputMuteInternal(true);
606 output_mute_locked_
= true;
608 // Restore the mute state.
609 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
611 SetOutputMuteInternal(audio_pref_handler_
->GetMuteValue(*device
));
614 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
618 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id
, int volume
) {
619 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
620 SetOutputNodeVolume(node_id
, volume
);
623 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64 node_id
,
624 int volume_percent
) {
625 const AudioDevice
* device
= this->GetDeviceFromId(node_id
);
626 if (!device
|| device
->is_input
)
629 volume_percent
= min(max(volume_percent
, 0), 100);
630 if (volume_percent
<= kMuteThresholdPercent
)
632 if (node_id
== active_output_node_id_
)
633 output_volume_
= volume_percent
;
635 audio_pref_handler_
->SetVolumeGainValue(*device
, volume_percent
);
637 if (device
->active
) {
638 SetOutputNodeVolume(node_id
, volume_percent
);
639 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnOutputVolumeChanged());
643 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on
) {
644 if (output_mute_locked_
)
647 output_mute_on_
= mute_on
;
648 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
649 SetOutputUserMute(mute_on
);
653 void CrasAudioHandler::SetInputNodeGain(uint64 node_id
, int gain
) {
654 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
655 SetInputNodeGain(node_id
, gain
);
658 void CrasAudioHandler::SetInputNodeGainPercent(uint64 node_id
,
660 const AudioDevice
* device
= GetDeviceFromId(node_id
);
661 if (!device
|| !device
->is_input
)
664 // NOTE: We do not sanitize input gain values since the range is completely
665 // dependent on the device.
666 if (active_input_node_id_
== node_id
)
667 input_gain_
= gain_percent
;
669 audio_pref_handler_
->SetVolumeGainValue(*device
, gain_percent
);
671 if (device
->active
) {
672 SetInputNodeGain(node_id
, gain_percent
);
673 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnInputGainChanged());
677 void CrasAudioHandler::SetInputMuteInternal(bool mute_on
) {
678 input_mute_on_
= mute_on
;
679 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
680 SetInputMute(mute_on
);
683 void CrasAudioHandler::GetNodes() {
684 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
685 base::Bind(&CrasAudioHandler::HandleGetNodes
,
686 weak_ptr_factory_
.GetWeakPtr()),
687 base::Bind(&CrasAudioHandler::HandleGetNodesError
,
688 weak_ptr_factory_
.GetWeakPtr()));
691 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice
& new_active_device
,
692 uint64
* current_active_node_id
) {
693 // If the device we want to switch to is already the current active device,
695 if (new_active_device
.active
&&
696 new_active_device
.id
== *current_active_node_id
) {
700 // Reset all other input or output devices' active status. The active audio
701 // device from the previous user session can be remembered by cras, but not
702 // in chrome. see crbug.com/273271.
703 for (AudioDeviceMap::iterator it
= audio_devices_
.begin();
704 it
!= audio_devices_
.end(); ++it
) {
705 if (it
->second
.is_input
== new_active_device
.is_input
&&
706 it
->second
.id
!= new_active_device
.id
)
707 it
->second
.active
= false;
710 // Set the current active input/output device to the new_active_device.
711 *current_active_node_id
= new_active_device
.id
;
712 audio_devices_
[*current_active_node_id
].active
= true;
716 bool CrasAudioHandler::NonActiveDeviceUnplugged(
717 size_t old_devices_size
,
718 size_t new_devices_size
,
719 uint64 current_active_node
) {
720 return (new_devices_size
< old_devices_size
&&
721 GetDeviceFromId(current_active_node
));
724 void CrasAudioHandler::SwitchToDevice(const AudioDevice
& device
, bool notify
) {
725 if (device
.is_input
) {
726 if (!ChangeActiveDevice(device
, &active_input_node_id_
))
728 SetupAudioInputState();
729 SetActiveInputNode(active_input_node_id_
, notify
);
731 if (!ChangeActiveDevice(device
, &active_output_node_id_
))
733 SetupAudioOutputState();
734 SetActiveOutputNode(active_output_node_id_
, notify
);
738 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList
& new_nodes
,
740 AudioNodeList
* new_discovered
) {
741 size_t num_old_devices
= 0;
742 size_t num_new_devices
= 0;
743 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
744 it
!= audio_devices_
.end(); ++it
) {
745 if (is_input
== it
->second
.is_input
)
749 bool new_or_changed_device
= false;
750 new_discovered
->clear();
751 for (AudioNodeList::const_iterator it
= new_nodes
.begin();
752 it
!= new_nodes
.end(); ++it
) {
753 if (is_input
== it
->is_input
) {
755 // Look to see if the new device not in the old device list.
756 AudioDevice
device(*it
);
757 DeviceStatus status
= CheckDeviceStatus(device
);
758 if (status
== NEW_DEVICE
)
759 new_discovered
->push_back(*it
);
760 if (status
== NEW_DEVICE
|| status
== CHANGED_DEVICE
) {
761 new_or_changed_device
= true;
765 return new_or_changed_device
|| (num_old_devices
!= num_new_devices
);
768 CrasAudioHandler::DeviceStatus
CrasAudioHandler::CheckDeviceStatus(
769 const AudioDevice
& device
) {
770 const AudioDevice
* device_found
= GetDeviceFromId(device
.id
);
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 CHANGED_DEVICE
;
779 } else if (device
.active
!= device_found
->active
) {
780 return CHANGED_DEVICE
;
786 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input
) {
788 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveInputNodeChanged());
790 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveOutputNodeChanged());
793 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
794 const AudioNodeList
& nodes
) {
795 size_t old_output_device_size
= 0;
796 size_t old_input_device_size
= 0;
797 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
798 it
!= audio_devices_
.end(); ++it
) {
799 if (it
->second
.is_input
)
800 ++old_input_device_size
;
802 ++old_output_device_size
;
805 AudioNodeList hotplug_output_nodes
;
806 AudioNodeList hotplug_input_nodes
;
807 bool output_devices_changed
=
808 HasDeviceChange(nodes
, false, &hotplug_output_nodes
);
809 bool input_devices_changed
=
810 HasDeviceChange(nodes
, true, &hotplug_input_nodes
);
811 audio_devices_
.clear();
812 has_alternative_input_
= false;
813 has_alternative_output_
= false;
815 while (!input_devices_pq_
.empty())
816 input_devices_pq_
.pop();
817 while (!output_devices_pq_
.empty())
818 output_devices_pq_
.pop();
820 size_t new_output_device_size
= 0;
821 size_t new_input_device_size
= 0;
822 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
823 AudioDevice
device(nodes
[i
]);
824 audio_devices_
[device
.id
] = device
;
826 if (!has_alternative_input_
&&
828 device
.type
!= AUDIO_TYPE_INTERNAL_MIC
&&
829 device
.type
!= AUDIO_TYPE_KEYBOARD_MIC
) {
830 has_alternative_input_
= true;
831 } else if (!has_alternative_output_
&&
833 device
.type
!= AUDIO_TYPE_INTERNAL_SPEAKER
) {
834 has_alternative_output_
= true;
837 if (device
.is_input
) {
838 input_devices_pq_
.push(device
);
839 ++new_input_device_size
;
841 output_devices_pq_
.push(device
);
842 ++new_output_device_size
;
846 // If the previous active device is removed from the new node list,
847 // reset active_output_node_id_.
848 if (!GetDeviceFromId(active_output_node_id_
))
849 active_output_node_id_
= 0;
850 if (!GetDeviceFromId(active_input_node_id_
))
851 active_input_node_id_
= 0;
853 // If audio nodes change is caused by unplugging some non-active audio
854 // devices, the previously set active audio device will stay active.
855 // Otherwise, switch to a new active audio device according to their priority.
856 if (input_devices_changed
&&
857 !NonActiveDeviceUnplugged(old_input_device_size
,
858 new_input_device_size
,
859 active_input_node_id_
)) {
860 // Some devices like chromeboxes don't have the internal audio input. In
861 // that case the active input node id should be reset.
862 if (input_devices_pq_
.empty()) {
863 active_input_node_id_
= 0;
864 NotifyActiveNodeChanged(true);
866 // If user has hot plugged a new node, we should change to the active
867 // device to the new node if it has the highest priority; otherwise,
868 // we should keep the existing active node chosen by user.
869 // For all other cases, we will choose the node with highest priority.
870 if (!active_input_node_id_
|| hotplug_input_nodes
.empty() ||
871 IsNodeInTheList(input_devices_pq_
.top().id
, hotplug_input_nodes
)) {
872 SwitchToDevice(input_devices_pq_
.top(), true);
876 if (output_devices_changed
&&
877 !NonActiveDeviceUnplugged(old_output_device_size
,
878 new_output_device_size
,
879 active_output_node_id_
)) {
880 // This is really unlikely to happen because all ChromeOS devices have the
881 // internal audio output.
882 if (output_devices_pq_
.empty()) {
883 active_output_node_id_
= 0;
884 NotifyActiveNodeChanged(false);
886 // ditto input node case.
887 if (!active_output_node_id_
|| hotplug_output_nodes
.empty() ||
888 IsNodeInTheList(output_devices_pq_
.top().id
, hotplug_output_nodes
)) {
889 SwitchToDevice(output_devices_pq_
.top(), true);
895 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList
& node_list
,
898 LOG_IF(ERROR
, log_errors_
) << "Failed to retrieve audio nodes data";
902 UpdateDevicesAndSwitchActive(node_list
);
903 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnAudioNodesChanged());
906 void CrasAudioHandler::HandleGetNodesError(const std::string
& error_name
,
907 const std::string
& error_msg
) {
908 LOG_IF(ERROR
, log_errors_
) << "Failed to call GetNodes: "
909 << error_name
<< ": " << error_msg
;
912 void CrasAudioHandler::AddAdditionalActiveNode(uint64 node_id
, bool notify
) {
913 const AudioDevice
* device
= GetDeviceFromId(node_id
);
915 VLOG(1) << "AddActiveInputNode: Cannot find device id="
916 << "0x" << std::hex
<< node_id
;
920 audio_devices_
[node_id
].active
= true;
921 SetupAdditionalActiveAudioNodeState(node_id
);
923 if (device
->is_input
) {
924 DCHECK(node_id
!= active_input_node_id_
);
925 chromeos::DBusThreadManager::Get()
926 ->GetCrasAudioClient()
927 ->AddActiveInputNode(node_id
);
929 NotifyActiveNodeChanged(true);
931 DCHECK(node_id
!= active_output_node_id_
);
932 chromeos::DBusThreadManager::Get()
933 ->GetCrasAudioClient()
934 ->AddActiveOutputNode(node_id
);
936 NotifyActiveNodeChanged(false);
940 void CrasAudioHandler::RemoveActiveNodeInternal(uint64 node_id
, bool notify
) {
941 const AudioDevice
* device
= GetDeviceFromId(node_id
);
943 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
944 << "0x" << std::hex
<< node_id
;
948 audio_devices_
[node_id
].active
= false;
949 if (device
->is_input
) {
950 if (node_id
== active_input_node_id_
)
951 active_input_node_id_
= 0;
952 chromeos::DBusThreadManager::Get()
953 ->GetCrasAudioClient()
954 ->RemoveActiveInputNode(node_id
);
956 NotifyActiveNodeChanged(true);
958 if (node_id
== active_output_node_id_
)
959 active_output_node_id_
= 0;
960 chromeos::DBusThreadManager::Get()
961 ->GetCrasAudioClient()
962 ->RemoveActiveOutputNode(node_id
);
964 NotifyActiveNodeChanged(false);
968 } // namespace chromeos