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_t node_id
,
39 const CrasAudioHandler::NodeIdList
& id_list
) {
40 return std::find(id_list
.begin(), id_list
.end(), node_id
) != id_list
.end();
43 bool IsNodeInTheList(uint64_t node_id
, const AudioNodeList
& node_list
) {
44 for (size_t i
= 0; i
< node_list
.size(); ++i
) {
45 if (node_id
== node_list
[i
].id
)
53 CrasAudioHandler::AudioObserver::AudioObserver() {
56 CrasAudioHandler::AudioObserver::~AudioObserver() {
59 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged(
60 uint64_t /* node_id */,
64 void CrasAudioHandler::AudioObserver::OnInputNodeGainChanged(
65 uint64_t /* node_id */,
69 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged(bool /* mute_on */) {
72 void CrasAudioHandler::AudioObserver::OnInputMuteChanged(bool /* mute_on */) {
75 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
78 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
81 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
85 void CrasAudioHandler::Initialize(
86 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
) {
87 CHECK(!g_cras_audio_handler
);
88 g_cras_audio_handler
= new CrasAudioHandler(audio_pref_handler
);
92 void CrasAudioHandler::InitializeForTesting() {
93 CHECK(!g_cras_audio_handler
);
94 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
98 void CrasAudioHandler::Shutdown() {
99 CHECK(g_cras_audio_handler
);
100 delete g_cras_audio_handler
;
101 g_cras_audio_handler
= NULL
;
105 bool CrasAudioHandler::IsInitialized() {
106 return g_cras_audio_handler
!= NULL
;
110 CrasAudioHandler
* CrasAudioHandler::Get() {
111 CHECK(g_cras_audio_handler
)
112 << "CrasAudioHandler::Get() called before Initialize().";
113 return g_cras_audio_handler
;
116 void CrasAudioHandler::AddAudioObserver(AudioObserver
* observer
) {
117 observers_
.AddObserver(observer
);
120 void CrasAudioHandler::RemoveAudioObserver(AudioObserver
* observer
) {
121 observers_
.RemoveObserver(observer
);
124 bool CrasAudioHandler::HasKeyboardMic() {
125 return GetKeyboardMic() != NULL
;
128 bool CrasAudioHandler::IsOutputMuted() {
129 return output_mute_on_
;
132 bool CrasAudioHandler::IsOutputMutedForDevice(uint64_t device_id
) {
133 const AudioDevice
* device
= GetDeviceFromId(device_id
);
136 DCHECK(!device
->is_input
);
137 return audio_pref_handler_
->GetMuteValue(*device
);
140 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
141 return output_volume_
<= kMuteThresholdPercent
;
144 bool CrasAudioHandler::IsInputMuted() {
145 return input_mute_on_
;
148 bool CrasAudioHandler::IsInputMutedForDevice(uint64_t device_id
) {
149 const AudioDevice
* device
= GetDeviceFromId(device_id
);
152 DCHECK(device
->is_input
);
153 // We don't record input mute state for each device in the prefs,
154 // for any non-active input device, we assume mute is off.
155 if (device
->id
== active_input_node_id_
)
156 return input_mute_on_
;
160 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
161 return kMuteThresholdPercent
;
164 int CrasAudioHandler::GetOutputVolumePercent() {
165 return output_volume_
;
168 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64_t device_id
) {
169 if (device_id
== active_output_node_id_
) {
170 return output_volume_
;
172 const AudioDevice
* device
= GetDeviceFromId(device_id
);
173 return static_cast<int>(audio_pref_handler_
->GetOutputVolumeValue(device
));
177 int CrasAudioHandler::GetInputGainPercent() {
181 int CrasAudioHandler::GetInputGainPercentForDevice(uint64_t device_id
) {
182 if (device_id
== active_input_node_id_
) {
185 const AudioDevice
* device
= GetDeviceFromId(device_id
);
186 return static_cast<int>(audio_pref_handler_
->GetInputGainValue(device
));
190 uint64_t CrasAudioHandler::GetPrimaryActiveOutputNode() const {
191 return active_output_node_id_
;
194 uint64_t CrasAudioHandler::GetPrimaryActiveInputNode() const {
195 return active_input_node_id_
;
198 void CrasAudioHandler::GetAudioDevices(AudioDeviceList
* device_list
) const {
199 device_list
->clear();
200 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
201 it
!= audio_devices_
.end(); ++it
)
202 device_list
->push_back(it
->second
);
205 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice
* device
) const {
206 const AudioDevice
* active_device
= GetDeviceFromId(active_output_node_id_
);
207 if (!active_device
|| !device
)
209 *device
= *active_device
;
213 void CrasAudioHandler::SetKeyboardMicActive(bool active
) {
214 const AudioDevice
* keyboard_mic
= GetKeyboardMic();
217 // Keyboard mic is invisible to chromeos users. It is always added or removed
218 // as additional active node.
219 DCHECK(active_input_node_id_
&& active_input_node_id_
!= keyboard_mic
->id
);
221 AddActiveNode(keyboard_mic
->id
, true);
223 RemoveActiveNodeInternal(keyboard_mic
->id
, true);
226 void CrasAudioHandler::AddActiveNode(uint64_t node_id
, bool notify
) {
227 const AudioDevice
* device
= GetDeviceFromId(node_id
);
229 VLOG(1) << "AddActiveInputNode: Cannot find device id="
230 << "0x" << std::hex
<< node_id
;
234 // If there is no primary active device, set |node_id| to primary active node.
235 if ((device
->is_input
&& !active_input_node_id_
) ||
236 (!device
->is_input
&& !active_output_node_id_
)) {
237 SwitchToDevice(*device
, notify
);
241 AddAdditionalActiveNode(node_id
, notify
);
244 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList
& new_active_ids
) {
245 // Flags for whether there are input or output nodes passed in from
246 // |new_active_ids|. If there are no input nodes passed in, we will not
247 // make any change for input nodes; same for the output nodes.
248 bool request_input_change
= false;
249 bool request_output_change
= false;
251 // Flags for whether we will actually change active status of input
253 bool make_input_change
= false;
254 bool make_output_change
= false;
256 NodeIdList nodes_to_activate
;
257 for (size_t i
= 0; i
< new_active_ids
.size(); ++i
) {
258 const AudioDevice
* device
= GetDeviceFromId(new_active_ids
[i
]);
260 if (device
->is_input
)
261 request_input_change
= true;
263 request_output_change
= true;
265 // If the new active device is already active, keep it as active.
269 nodes_to_activate
.push_back(new_active_ids
[i
]);
270 if (device
->is_input
)
271 make_input_change
= true;
273 make_output_change
= true;
277 // Remove all existing active devices that are not in the |new_active_ids|
279 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
280 it
!= audio_devices_
.end(); ++it
) {
281 AudioDevice device
= it
->second
;
282 // Remove the existing active input or output nodes that are not in the new
283 // active node list if there are new input or output nodes specified.
285 if ((device
.is_input
&& request_input_change
&&
286 !IsInNodeList(device
.id
, new_active_ids
))) {
287 make_input_change
= true;
288 RemoveActiveNodeInternal(device
.id
, false); // no notification.
289 } else if (!device
.is_input
&& request_output_change
&&
290 !IsInNodeList(device
.id
, new_active_ids
)) {
291 make_output_change
= true;
292 RemoveActiveNodeInternal(device
.id
, false); // no notification.
297 // Adds the new active devices.
298 for (size_t i
= 0; i
< nodes_to_activate
.size(); ++i
)
299 AddActiveNode(nodes_to_activate
[i
], false); // no notification.
301 // Notify the active nodes change now.
302 if (make_input_change
)
303 NotifyActiveNodeChanged(true);
304 if (make_output_change
)
305 NotifyActiveNodeChanged(false);
308 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap
) {
309 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
310 it
!= audio_devices_
.end();
312 const AudioDevice
& device
= it
->second
;
313 if (!device
.is_input
&& device
.type
== AUDIO_TYPE_INTERNAL_SPEAKER
) {
314 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
321 bool CrasAudioHandler::has_alternative_input() const {
322 return has_alternative_input_
;
325 bool CrasAudioHandler::has_alternative_output() const {
326 return has_alternative_output_
;
329 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent
) {
330 // Set all active devices to the same volume.
331 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
332 it
!= audio_devices_
.end();
334 const AudioDevice
& device
= it
->second
;
335 if (!device
.is_input
&& device
.active
)
336 SetOutputNodeVolumePercent(device
.id
, volume_percent
);
340 // TODO: Rename the 'Percent' to something more meaningful.
341 void CrasAudioHandler::SetInputGainPercent(int gain_percent
) {
342 // TODO(jennyz): Should we set all input devices' gain to the same level?
343 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
344 it
!= audio_devices_
.end();
346 const AudioDevice
& device
= it
->second
;
347 if (device
.is_input
&& device
.active
)
348 SetInputNodeGainPercent(active_input_node_id_
, gain_percent
);
352 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent
) {
353 SetOutputVolumePercent(output_volume_
+ adjust_by_percent
);
356 void CrasAudioHandler::SetOutputMute(bool mute_on
) {
357 if (!SetOutputMuteInternal(mute_on
))
360 // Save the mute state for all active output audio devices.
361 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
362 it
!= audio_devices_
.end();
364 const AudioDevice
& device
= it
->second
;
365 if (!device
.is_input
&& device
.active
) {
366 audio_pref_handler_
->SetMuteValue(device
, output_mute_on_
);
370 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
371 OnOutputMuteChanged(output_mute_on_
));
374 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
375 if (output_volume_
<= kMuteThresholdPercent
) {
376 // Avoid the situation when sound has been unmuted, but the volume
377 // is set to a very low value, so user still can't hear any sound.
378 SetOutputVolumePercent(kDefaultUnmuteVolumePercent
);
382 void CrasAudioHandler::SetInputMute(bool mute_on
) {
383 SetInputMuteInternal(mute_on
);
384 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
385 OnInputMuteChanged(input_mute_on_
));
388 void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id
, bool notify
) {
389 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
390 SetActiveOutputNode(node_id
);
392 NotifyActiveNodeChanged(false);
395 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id
, bool notify
) {
396 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
397 SetActiveInputNode(node_id
);
399 NotifyActiveNodeChanged(true);
402 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id
,
404 const AudioDevice
* device
= GetDeviceFromId(device_id
);
408 if (device
->is_input
)
409 SetInputNodeGainPercent(device_id
, value
);
411 SetOutputNodeVolumePercent(device_id
, value
);
414 void CrasAudioHandler::SetMuteForDevice(uint64_t device_id
, bool mute_on
) {
415 if (device_id
== active_output_node_id_
) {
416 SetOutputMute(mute_on
);
418 } else if (device_id
== active_input_node_id_
) {
419 VLOG(1) << "SetMuteForDevice sets active input device id="
420 << "0x" << std::hex
<< device_id
<< " mute=" << mute_on
;
421 SetInputMute(mute_on
);
425 const AudioDevice
* device
= GetDeviceFromId(device_id
);
426 // Input device's mute state is not recorded in the pref. crbug.com/365050.
427 if (device
&& !device
->is_input
)
428 audio_pref_handler_
->SetMuteValue(*device
, mute_on
);
431 void CrasAudioHandler::LogErrors() {
435 CrasAudioHandler::CrasAudioHandler(
436 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
)
437 : audio_pref_handler_(audio_pref_handler
),
438 output_mute_on_(false),
439 input_mute_on_(false),
442 active_output_node_id_(0),
443 active_input_node_id_(0),
444 has_alternative_input_(false),
445 has_alternative_output_(false),
446 output_mute_locked_(false),
448 weak_ptr_factory_(this) {
449 if (!audio_pref_handler
.get())
451 // If the DBusThreadManager or the CrasAudioClient aren't available, there
452 // isn't much we can do. This should only happen when running tests.
453 if (!chromeos::DBusThreadManager::IsInitialized() ||
454 !chromeos::DBusThreadManager::Get() ||
455 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
457 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
458 audio_pref_handler_
->AddAudioPrefObserver(this);
459 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
460 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
463 InitializeAudioState();
466 CrasAudioHandler::~CrasAudioHandler() {
467 if (!chromeos::DBusThreadManager::IsInitialized() ||
468 !chromeos::DBusThreadManager::Get() ||
469 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
471 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
472 RemoveObserver(this);
473 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
474 RemoveObserver(this);
475 if (audio_pref_handler_
.get())
476 audio_pref_handler_
->RemoveAudioPrefObserver(this);
477 audio_pref_handler_
= NULL
;
480 void CrasAudioHandler::AudioClientRestarted() {
481 // Make sure the logging is enabled in case cras server
482 // restarts after crashing.
484 InitializeAudioState();
487 void CrasAudioHandler::NodesChanged() {
488 // Refresh audio nodes data.
492 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id
) {
493 if (active_output_node_id_
== node_id
)
496 // Active audio output device should always be changed by chrome.
497 // During system boot, cras may change active input to unknown device 0x1,
498 // we don't need to log it, since it is not an valid device.
499 if (GetDeviceFromId(node_id
)) {
500 LOG_IF(WARNING
, log_errors_
)
501 << "Active output node changed unexpectedly by system node_id="
502 << "0x" << std::hex
<< node_id
;
506 void CrasAudioHandler::ActiveInputNodeChanged(uint64_t node_id
) {
507 if (active_input_node_id_
== node_id
)
510 // Active audio input device should always be changed by chrome.
511 // During system boot, cras may change active input to unknown device 0x2,
512 // we don't need to log it, since it is not an valid device.
513 if (GetDeviceFromId(node_id
)) {
514 LOG_IF(WARNING
, log_errors_
)
515 << "Active input node changed unexpectedly by system node_id="
516 << "0x" << std::hex
<< node_id
;
520 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
524 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
525 // Enable logging after cras server is started, which will be after
526 // EmitLoginPromptVisible.
530 const AudioDevice
* CrasAudioHandler::GetDeviceFromId(uint64_t device_id
) const {
531 AudioDeviceMap::const_iterator it
= audio_devices_
.find(device_id
);
532 if (it
== audio_devices_
.end())
535 return &(it
->second
);
538 const AudioDevice
* CrasAudioHandler::GetKeyboardMic() const {
539 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
540 it
!= audio_devices_
.end(); it
++) {
541 if (it
->second
.is_input
&& it
->second
.type
== AUDIO_TYPE_KEYBOARD_MIC
)
542 return &(it
->second
);
547 void CrasAudioHandler::SetupAudioInputState() {
548 // Set the initial audio state to the ones read from audio prefs.
549 const AudioDevice
* device
= GetDeviceFromId(active_input_node_id_
);
551 LOG_IF(ERROR
, log_errors_
)
552 << "Can't set up audio state for unknown input device id ="
553 << "0x" << std::hex
<< active_input_node_id_
;
556 input_gain_
= audio_pref_handler_
->GetInputGainValue(device
);
557 VLOG(1) << "SetupAudioInputState for active device id="
558 << "0x" << std::hex
<< device
->id
<< " mute=" << input_mute_on_
;
559 SetInputMuteInternal(input_mute_on_
);
560 // TODO(rkc,jennyz): Set input gain once we decide on how to store
561 // the gain values since the range and step are both device specific.
564 void CrasAudioHandler::SetupAudioOutputState() {
565 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
567 LOG_IF(ERROR
, log_errors_
)
568 << "Can't set up audio state for unknown output device id ="
569 << "0x" << std::hex
<< active_output_node_id_
;
572 DCHECK(!device
->is_input
);
573 output_mute_on_
= audio_pref_handler_
->GetMuteValue(*device
);
574 output_volume_
= audio_pref_handler_
->GetOutputVolumeValue(device
);
576 SetOutputMuteInternal(output_mute_on_
);
577 SetOutputNodeVolume(active_output_node_id_
, output_volume_
);
580 // This sets up the state of an additional active node.
581 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64_t node_id
) {
582 const AudioDevice
* device
= GetDeviceFromId(node_id
);
584 VLOG(1) << "Can't set up audio state for unknown device id ="
585 << "0x" << std::hex
<< node_id
;
589 DCHECK(node_id
!= active_output_node_id_
&& node_id
!= active_input_node_id_
);
591 // Note: The mute state is a system wide state, we don't set mute per device,
592 // but just keep the mute state consistent for the active node in prefs.
593 // The output volume should be set to the same value for all active output
594 // devices. For input devices, we don't restore their gain value so far.
595 // TODO(jennyz): crbug.com/417418, track the status for the decison if
596 // we should persist input gain value in prefs.
597 if (!device
->is_input
) {
598 audio_pref_handler_
->SetMuteValue(*device
, IsOutputMuted());
599 SetOutputNodeVolumePercent(node_id
, GetOutputVolumePercent());
603 void CrasAudioHandler::InitializeAudioState() {
608 void CrasAudioHandler::ApplyAudioPolicy() {
609 output_mute_locked_
= false;
610 if (!audio_pref_handler_
->GetAudioOutputAllowedValue()) {
611 // Mute the device, but do not update the preference.
612 SetOutputMuteInternal(true);
613 output_mute_locked_
= true;
615 // Restore the mute state.
616 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
618 SetOutputMuteInternal(audio_pref_handler_
->GetMuteValue(*device
));
621 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
625 void CrasAudioHandler::SetOutputNodeVolume(uint64_t node_id
, int volume
) {
626 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
627 SetOutputNodeVolume(node_id
, volume
);
630 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64_t node_id
,
631 int volume_percent
) {
632 const AudioDevice
* device
= this->GetDeviceFromId(node_id
);
633 if (!device
|| device
->is_input
)
636 volume_percent
= min(max(volume_percent
, 0), 100);
637 if (volume_percent
<= kMuteThresholdPercent
)
639 if (node_id
== active_output_node_id_
)
640 output_volume_
= volume_percent
;
642 audio_pref_handler_
->SetVolumeGainValue(*device
, volume_percent
);
644 if (device
->active
) {
645 SetOutputNodeVolume(node_id
, volume_percent
);
646 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
647 OnOutputNodeVolumeChanged(node_id
, volume_percent
));
651 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on
) {
652 if (output_mute_locked_
)
655 output_mute_on_
= mute_on
;
656 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
657 SetOutputUserMute(mute_on
);
661 void CrasAudioHandler::SetInputNodeGain(uint64_t node_id
, int gain
) {
662 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
663 SetInputNodeGain(node_id
, gain
);
666 void CrasAudioHandler::SetInputNodeGainPercent(uint64_t node_id
,
668 const AudioDevice
* device
= GetDeviceFromId(node_id
);
669 if (!device
|| !device
->is_input
)
672 // NOTE: We do not sanitize input gain values since the range is completely
673 // dependent on the device.
674 if (active_input_node_id_
== node_id
)
675 input_gain_
= gain_percent
;
677 audio_pref_handler_
->SetVolumeGainValue(*device
, gain_percent
);
679 if (device
->active
) {
680 SetInputNodeGain(node_id
, gain_percent
);
681 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
682 OnInputNodeGainChanged(node_id
, gain_percent
));
686 void CrasAudioHandler::SetInputMuteInternal(bool mute_on
) {
687 input_mute_on_
= mute_on
;
688 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
689 SetInputMute(mute_on
);
692 void CrasAudioHandler::GetNodes() {
693 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
694 base::Bind(&CrasAudioHandler::HandleGetNodes
,
695 weak_ptr_factory_
.GetWeakPtr()),
696 base::Bind(&CrasAudioHandler::HandleGetNodesError
,
697 weak_ptr_factory_
.GetWeakPtr()));
700 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice
& new_active_device
,
701 uint64_t* current_active_node_id
) {
702 // If the device we want to switch to is already the current active device,
704 if (new_active_device
.active
&&
705 new_active_device
.id
== *current_active_node_id
) {
709 // Reset all other input or output devices' active status. The active audio
710 // device from the previous user session can be remembered by cras, but not
711 // in chrome. see crbug.com/273271.
712 for (AudioDeviceMap::iterator it
= audio_devices_
.begin();
713 it
!= audio_devices_
.end(); ++it
) {
714 if (it
->second
.is_input
== new_active_device
.is_input
&&
715 it
->second
.id
!= new_active_device
.id
)
716 it
->second
.active
= false;
719 // Set the current active input/output device to the new_active_device.
720 *current_active_node_id
= new_active_device
.id
;
721 audio_devices_
[*current_active_node_id
].active
= true;
725 bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size
,
726 size_t new_devices_size
,
727 uint64_t 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_
))
736 SetupAudioInputState();
737 SetActiveInputNode(active_input_node_id_
, notify
);
739 if (!ChangeActiveDevice(device
, &active_output_node_id_
))
741 SetupAudioOutputState();
742 SetActiveOutputNode(active_output_node_id_
, notify
);
746 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList
& new_nodes
,
748 AudioNodeList
* new_discovered
) {
749 size_t num_old_devices
= 0;
750 size_t num_new_devices
= 0;
751 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
752 it
!= audio_devices_
.end(); ++it
) {
753 if (is_input
== it
->second
.is_input
)
757 bool new_or_changed_device
= false;
758 new_discovered
->clear();
759 for (AudioNodeList::const_iterator it
= new_nodes
.begin();
760 it
!= new_nodes
.end(); ++it
) {
761 if (is_input
== it
->is_input
) {
763 // Look to see if the new device not in the old device list.
764 AudioDevice
device(*it
);
765 DeviceStatus status
= CheckDeviceStatus(device
);
766 if (status
== NEW_DEVICE
)
767 new_discovered
->push_back(*it
);
768 if (status
== NEW_DEVICE
|| status
== CHANGED_DEVICE
) {
769 new_or_changed_device
= true;
773 return new_or_changed_device
|| (num_old_devices
!= num_new_devices
);
776 CrasAudioHandler::DeviceStatus
CrasAudioHandler::CheckDeviceStatus(
777 const AudioDevice
& device
) {
778 const AudioDevice
* device_found
= GetDeviceFromId(device
.id
);
782 if (!IsSameAudioDevice(device
, *device_found
)) {
783 LOG(WARNING
) << "Different Audio devices with same id:"
784 << " new device: " << device
.ToString()
785 << " old device: " << device_found
->ToString();
786 return CHANGED_DEVICE
;
787 } else if (device
.active
!= device_found
->active
) {
788 return CHANGED_DEVICE
;
794 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input
) {
796 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveInputNodeChanged());
798 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveOutputNodeChanged());
801 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
802 const AudioNodeList
& nodes
) {
803 size_t old_output_device_size
= 0;
804 size_t old_input_device_size
= 0;
805 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
806 it
!= audio_devices_
.end(); ++it
) {
807 if (it
->second
.is_input
)
808 ++old_input_device_size
;
810 ++old_output_device_size
;
813 AudioNodeList hotplug_output_nodes
;
814 AudioNodeList hotplug_input_nodes
;
815 bool output_devices_changed
=
816 HasDeviceChange(nodes
, false, &hotplug_output_nodes
);
817 bool input_devices_changed
=
818 HasDeviceChange(nodes
, true, &hotplug_input_nodes
);
819 audio_devices_
.clear();
820 has_alternative_input_
= false;
821 has_alternative_output_
= false;
823 while (!input_devices_pq_
.empty())
824 input_devices_pq_
.pop();
825 while (!output_devices_pq_
.empty())
826 output_devices_pq_
.pop();
828 size_t new_output_device_size
= 0;
829 size_t new_input_device_size
= 0;
830 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
831 AudioDevice
device(nodes
[i
]);
832 audio_devices_
[device
.id
] = device
;
834 if (!has_alternative_input_
&&
836 device
.type
!= AUDIO_TYPE_INTERNAL_MIC
&&
837 device
.type
!= AUDIO_TYPE_KEYBOARD_MIC
) {
838 has_alternative_input_
= true;
839 } else if (!has_alternative_output_
&&
841 device
.type
!= AUDIO_TYPE_INTERNAL_SPEAKER
) {
842 has_alternative_output_
= true;
845 if (device
.is_input
) {
846 input_devices_pq_
.push(device
);
847 ++new_input_device_size
;
849 output_devices_pq_
.push(device
);
850 ++new_output_device_size
;
854 // If the previous active device is removed from the new node list,
855 // or changed to inactive by cras, reset active_output_node_id_.
856 // See crbug.com/478968.
857 const AudioDevice
* active_output
= GetDeviceFromId(active_output_node_id_
);
858 if (!active_output
|| !active_output
->active
)
859 active_output_node_id_
= 0;
860 const AudioDevice
* active_input
= GetDeviceFromId(active_input_node_id_
);
861 if (!active_input
|| !active_input
->active
)
862 active_input_node_id_
= 0;
864 // If audio nodes change is caused by unplugging some non-active audio
865 // devices, the previously set active audio device will stay active.
866 // Otherwise, switch to a new active audio device according to their priority.
867 if (input_devices_changed
&&
868 !NonActiveDeviceUnplugged(old_input_device_size
,
869 new_input_device_size
,
870 active_input_node_id_
)) {
871 // Some devices like chromeboxes don't have the internal audio input. In
872 // that case the active input node id should be reset.
873 if (input_devices_pq_
.empty()) {
874 active_input_node_id_
= 0;
875 NotifyActiveNodeChanged(true);
877 // If user has hot plugged a new node, we should change to the active
878 // device to the new node if it has the highest priority; otherwise,
879 // we should keep the existing active node chosen by user.
880 // For all other cases, we will choose the node with highest priority.
881 if (!active_input_node_id_
|| hotplug_input_nodes
.empty() ||
882 IsNodeInTheList(input_devices_pq_
.top().id
, hotplug_input_nodes
)) {
883 SwitchToDevice(input_devices_pq_
.top(), true);
887 if (output_devices_changed
&&
888 !NonActiveDeviceUnplugged(old_output_device_size
,
889 new_output_device_size
,
890 active_output_node_id_
)) {
891 // This is really unlikely to happen because all ChromeOS devices have the
892 // internal audio output.
893 if (output_devices_pq_
.empty()) {
894 active_output_node_id_
= 0;
895 NotifyActiveNodeChanged(false);
897 // ditto input node case.
898 if (!active_output_node_id_
|| hotplug_output_nodes
.empty() ||
899 IsNodeInTheList(output_devices_pq_
.top().id
, hotplug_output_nodes
)) {
900 SwitchToDevice(output_devices_pq_
.top(), true);
906 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList
& node_list
,
909 LOG_IF(ERROR
, log_errors_
) << "Failed to retrieve audio nodes data";
913 UpdateDevicesAndSwitchActive(node_list
);
914 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnAudioNodesChanged());
917 void CrasAudioHandler::HandleGetNodesError(const std::string
& error_name
,
918 const std::string
& error_msg
) {
919 LOG_IF(ERROR
, log_errors_
) << "Failed to call GetNodes: "
920 << error_name
<< ": " << error_msg
;
923 void CrasAudioHandler::AddAdditionalActiveNode(uint64_t node_id
, bool notify
) {
924 const AudioDevice
* device
= GetDeviceFromId(node_id
);
926 VLOG(1) << "AddActiveInputNode: Cannot find device id="
927 << "0x" << std::hex
<< node_id
;
931 audio_devices_
[node_id
].active
= true;
932 SetupAdditionalActiveAudioNodeState(node_id
);
934 if (device
->is_input
) {
935 DCHECK(node_id
!= active_input_node_id_
);
936 chromeos::DBusThreadManager::Get()
937 ->GetCrasAudioClient()
938 ->AddActiveInputNode(node_id
);
940 NotifyActiveNodeChanged(true);
942 DCHECK(node_id
!= active_output_node_id_
);
943 chromeos::DBusThreadManager::Get()
944 ->GetCrasAudioClient()
945 ->AddActiveOutputNode(node_id
);
947 NotifyActiveNodeChanged(false);
951 void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id
, bool notify
) {
952 const AudioDevice
* device
= GetDeviceFromId(node_id
);
954 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
955 << "0x" << std::hex
<< node_id
;
959 audio_devices_
[node_id
].active
= false;
960 if (device
->is_input
) {
961 if (node_id
== active_input_node_id_
)
962 active_input_node_id_
= 0;
963 chromeos::DBusThreadManager::Get()
964 ->GetCrasAudioClient()
965 ->RemoveActiveInputNode(node_id
);
967 NotifyActiveNodeChanged(true);
969 if (node_id
== active_output_node_id_
)
970 active_output_node_id_
= 0;
971 chromeos::DBusThreadManager::Get()
972 ->GetCrasAudioClient()
973 ->RemoveActiveOutputNode(node_id
);
975 NotifyActiveNodeChanged(false);
979 } // namespace chromeos