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 // The duration of HDMI output re-discover grace period in milliseconds.
32 const int kHDMIRediscoverGracePeriodDurationInMs
= 2000;
34 static CrasAudioHandler
* g_cras_audio_handler
= NULL
;
36 bool IsSameAudioDevice(const AudioDevice
& a
, const AudioDevice
& b
) {
37 return a
.id
== b
.id
&& a
.is_input
== b
.is_input
&& a
.type
== b
.type
38 && a
.device_name
== b
.device_name
;
41 bool IsInNodeList(uint64_t node_id
,
42 const CrasAudioHandler::NodeIdList
& id_list
) {
43 return std::find(id_list
.begin(), id_list
.end(), node_id
) != id_list
.end();
46 bool IsNodeInTheList(uint64_t node_id
, const AudioNodeList
& node_list
) {
47 for (size_t i
= 0; i
< node_list
.size(); ++i
) {
48 if (node_id
== node_list
[i
].id
)
56 CrasAudioHandler::AudioObserver::AudioObserver() {
59 CrasAudioHandler::AudioObserver::~AudioObserver() {
62 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged(
63 uint64_t /* node_id */,
67 void CrasAudioHandler::AudioObserver::OnInputNodeGainChanged(
68 uint64_t /* node_id */,
72 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged(
74 bool /* system_adjust */) {}
76 void CrasAudioHandler::AudioObserver::OnInputMuteChanged(bool /* mute_on */) {
79 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
82 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
85 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
89 void CrasAudioHandler::Initialize(
90 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
) {
91 CHECK(!g_cras_audio_handler
);
92 g_cras_audio_handler
= new CrasAudioHandler(audio_pref_handler
);
96 void CrasAudioHandler::InitializeForTesting() {
97 CHECK(!g_cras_audio_handler
);
98 CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
102 void CrasAudioHandler::Shutdown() {
103 CHECK(g_cras_audio_handler
);
104 delete g_cras_audio_handler
;
105 g_cras_audio_handler
= NULL
;
109 bool CrasAudioHandler::IsInitialized() {
110 return g_cras_audio_handler
!= NULL
;
114 CrasAudioHandler
* CrasAudioHandler::Get() {
115 CHECK(g_cras_audio_handler
)
116 << "CrasAudioHandler::Get() called before Initialize().";
117 return g_cras_audio_handler
;
120 void CrasAudioHandler::AddAudioObserver(AudioObserver
* observer
) {
121 observers_
.AddObserver(observer
);
124 void CrasAudioHandler::RemoveAudioObserver(AudioObserver
* observer
) {
125 observers_
.RemoveObserver(observer
);
128 bool CrasAudioHandler::HasKeyboardMic() {
129 return GetKeyboardMic() != NULL
;
132 bool CrasAudioHandler::IsOutputMuted() {
133 return output_mute_on_
;
136 bool CrasAudioHandler::IsOutputMutedForDevice(uint64_t device_id
) {
137 const AudioDevice
* device
= GetDeviceFromId(device_id
);
140 DCHECK(!device
->is_input
);
141 return audio_pref_handler_
->GetMuteValue(*device
);
144 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLevel() {
145 return output_volume_
<= kMuteThresholdPercent
;
148 bool CrasAudioHandler::IsInputMuted() {
149 return input_mute_on_
;
152 bool CrasAudioHandler::IsInputMutedForDevice(uint64_t device_id
) {
153 const AudioDevice
* device
= GetDeviceFromId(device_id
);
156 DCHECK(device
->is_input
);
157 // We don't record input mute state for each device in the prefs,
158 // for any non-active input device, we assume mute is off.
159 if (device
->id
== active_input_node_id_
)
160 return input_mute_on_
;
164 int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
165 return kMuteThresholdPercent
;
168 int CrasAudioHandler::GetOutputVolumePercent() {
169 return output_volume_
;
172 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64_t device_id
) {
173 if (device_id
== active_output_node_id_
) {
174 return output_volume_
;
176 const AudioDevice
* device
= GetDeviceFromId(device_id
);
177 return static_cast<int>(audio_pref_handler_
->GetOutputVolumeValue(device
));
181 int CrasAudioHandler::GetInputGainPercent() {
185 int CrasAudioHandler::GetInputGainPercentForDevice(uint64_t device_id
) {
186 if (device_id
== active_input_node_id_
) {
189 const AudioDevice
* device
= GetDeviceFromId(device_id
);
190 return static_cast<int>(audio_pref_handler_
->GetInputGainValue(device
));
194 uint64_t CrasAudioHandler::GetPrimaryActiveOutputNode() const {
195 return active_output_node_id_
;
198 uint64_t CrasAudioHandler::GetPrimaryActiveInputNode() const {
199 return active_input_node_id_
;
202 void CrasAudioHandler::GetAudioDevices(AudioDeviceList
* device_list
) const {
203 device_list
->clear();
204 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
205 it
!= audio_devices_
.end(); ++it
)
206 device_list
->push_back(it
->second
);
209 bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice
* device
) const {
210 const AudioDevice
* active_device
= GetDeviceFromId(active_output_node_id_
);
211 if (!active_device
|| !device
)
213 *device
= *active_device
;
217 void CrasAudioHandler::SetKeyboardMicActive(bool active
) {
218 const AudioDevice
* keyboard_mic
= GetKeyboardMic();
221 // Keyboard mic is invisible to chromeos users. It is always added or removed
222 // as additional active node.
223 DCHECK(active_input_node_id_
&& active_input_node_id_
!= keyboard_mic
->id
);
225 AddActiveNode(keyboard_mic
->id
, true);
227 RemoveActiveNodeInternal(keyboard_mic
->id
, true);
230 void CrasAudioHandler::AddActiveNode(uint64_t node_id
, bool notify
) {
231 const AudioDevice
* device
= GetDeviceFromId(node_id
);
233 VLOG(1) << "AddActiveInputNode: Cannot find device id="
234 << "0x" << std::hex
<< node_id
;
238 // If there is no primary active device, set |node_id| to primary active node.
239 if ((device
->is_input
&& !active_input_node_id_
) ||
240 (!device
->is_input
&& !active_output_node_id_
)) {
241 SwitchToDevice(*device
, notify
);
245 AddAdditionalActiveNode(node_id
, notify
);
248 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList
& new_active_ids
) {
249 // Flags for whether there are input or output nodes passed in from
250 // |new_active_ids|. If there are no input nodes passed in, we will not
251 // make any change for input nodes; same for the output nodes.
252 bool request_input_change
= false;
253 bool request_output_change
= false;
255 // Flags for whether we will actually change active status of input
257 bool make_input_change
= false;
258 bool make_output_change
= false;
260 NodeIdList nodes_to_activate
;
261 for (size_t i
= 0; i
< new_active_ids
.size(); ++i
) {
262 const AudioDevice
* device
= GetDeviceFromId(new_active_ids
[i
]);
264 if (device
->is_input
)
265 request_input_change
= true;
267 request_output_change
= true;
269 // If the new active device is already active, keep it as active.
273 nodes_to_activate
.push_back(new_active_ids
[i
]);
274 if (device
->is_input
)
275 make_input_change
= true;
277 make_output_change
= true;
281 // Remove all existing active devices that are not in the |new_active_ids|
283 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
284 it
!= audio_devices_
.end(); ++it
) {
285 AudioDevice device
= it
->second
;
286 // Remove the existing active input or output nodes that are not in the new
287 // active node list if there are new input or output nodes specified.
289 if ((device
.is_input
&& request_input_change
&&
290 !IsInNodeList(device
.id
, new_active_ids
))) {
291 make_input_change
= true;
292 RemoveActiveNodeInternal(device
.id
, false); // no notification.
293 } else if (!device
.is_input
&& request_output_change
&&
294 !IsInNodeList(device
.id
, new_active_ids
)) {
295 make_output_change
= true;
296 RemoveActiveNodeInternal(device
.id
, false); // no notification.
301 // Adds the new active devices.
302 for (size_t i
= 0; i
< nodes_to_activate
.size(); ++i
)
303 AddActiveNode(nodes_to_activate
[i
], false); // no notification.
305 // Notify the active nodes change now.
306 if (make_input_change
)
307 NotifyActiveNodeChanged(true);
308 if (make_output_change
)
309 NotifyActiveNodeChanged(false);
312 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap
) {
313 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
314 it
!= audio_devices_
.end();
316 const AudioDevice
& device
= it
->second
;
317 if (!device
.is_input
&& device
.type
== AUDIO_TYPE_INTERNAL_SPEAKER
) {
318 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->SwapLeftRight(
325 bool CrasAudioHandler::has_alternative_input() const {
326 return has_alternative_input_
;
329 bool CrasAudioHandler::has_alternative_output() const {
330 return has_alternative_output_
;
333 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent
) {
334 // Set all active devices to the same volume.
335 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
336 it
!= audio_devices_
.end();
338 const AudioDevice
& device
= it
->second
;
339 if (!device
.is_input
&& device
.active
)
340 SetOutputNodeVolumePercent(device
.id
, volume_percent
);
344 // TODO: Rename the 'Percent' to something more meaningful.
345 void CrasAudioHandler::SetInputGainPercent(int gain_percent
) {
346 // TODO(jennyz): Should we set all input devices' gain to the same level?
347 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
348 it
!= audio_devices_
.end();
350 const AudioDevice
& device
= it
->second
;
351 if (device
.is_input
&& device
.active
)
352 SetInputNodeGainPercent(active_input_node_id_
, gain_percent
);
356 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent
) {
357 SetOutputVolumePercent(output_volume_
+ adjust_by_percent
);
360 void CrasAudioHandler::SetOutputMute(bool mute_on
) {
361 if (!SetOutputMuteInternal(mute_on
))
364 // Save the mute state for all active output audio devices.
365 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
366 it
!= audio_devices_
.end();
368 const AudioDevice
& device
= it
->second
;
369 if (!device
.is_input
&& device
.active
) {
370 audio_pref_handler_
->SetMuteValue(device
, output_mute_on_
);
375 AudioObserver
, observers_
,
376 OnOutputMuteChanged(output_mute_on_
, false /* system_adjust */));
379 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
380 if (output_volume_
<= kMuteThresholdPercent
) {
381 // Avoid the situation when sound has been unmuted, but the volume
382 // is set to a very low value, so user still can't hear any sound.
383 SetOutputVolumePercent(kDefaultUnmuteVolumePercent
);
387 void CrasAudioHandler::SetInputMute(bool mute_on
) {
388 SetInputMuteInternal(mute_on
);
389 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
390 OnInputMuteChanged(input_mute_on_
));
393 void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id
, bool notify
) {
394 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
395 SetActiveOutputNode(node_id
);
397 NotifyActiveNodeChanged(false);
400 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id
, bool notify
) {
401 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
402 SetActiveInputNode(node_id
);
404 NotifyActiveNodeChanged(true);
407 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id
,
409 const AudioDevice
* device
= GetDeviceFromId(device_id
);
413 if (device
->is_input
)
414 SetInputNodeGainPercent(device_id
, value
);
416 SetOutputNodeVolumePercent(device_id
, value
);
419 void CrasAudioHandler::SetMuteForDevice(uint64_t device_id
, bool mute_on
) {
420 if (device_id
== active_output_node_id_
) {
421 SetOutputMute(mute_on
);
423 } else if (device_id
== active_input_node_id_
) {
424 VLOG(1) << "SetMuteForDevice sets active input device id="
425 << "0x" << std::hex
<< device_id
<< " mute=" << mute_on
;
426 SetInputMute(mute_on
);
430 const AudioDevice
* device
= GetDeviceFromId(device_id
);
431 // Input device's mute state is not recorded in the pref. crbug.com/365050.
432 if (device
&& !device
->is_input
)
433 audio_pref_handler_
->SetMuteValue(*device
, mute_on
);
436 void CrasAudioHandler::LogErrors() {
440 // If the HDMI device is the active output device, when the device enters/exits
441 // docking mode, or HDMI display changes resolution, or chromeos device
442 // suspends/resumes, cras will lose the HDMI output node for a short period of
443 // time, then rediscover it. This hotplug behavior will cause the audio output
444 // be leaked to the alternatvie active audio output during HDMI re-discovering
445 // period. See crbug.com/503667.
446 void CrasAudioHandler::SetActiveHDMIOutoutRediscoveringIfNecessary(
447 bool force_rediscovering
) {
448 if (!GetDeviceFromId(active_output_node_id_
))
451 // Marks the start of the HDMI re-discovering grace period, during which we
452 // will mute the audio output to prevent it to be be leaked to the
453 // alternative output device.
454 if ((hdmi_rediscovering_
&& force_rediscovering
) ||
455 (!hdmi_rediscovering_
&& IsHDMIPrimaryOutputDevice())) {
456 StartHDMIRediscoverGracePeriod();
460 CrasAudioHandler::CrasAudioHandler(
461 scoped_refptr
<AudioDevicesPrefHandler
> audio_pref_handler
)
462 : audio_pref_handler_(audio_pref_handler
),
463 output_mute_on_(false),
464 input_mute_on_(false),
467 active_output_node_id_(0),
468 active_input_node_id_(0),
469 has_alternative_input_(false),
470 has_alternative_output_(false),
471 output_mute_locked_(false),
473 hdmi_rediscover_grace_period_duration_in_ms_(
474 kHDMIRediscoverGracePeriodDurationInMs
),
475 hdmi_rediscovering_(false),
476 weak_ptr_factory_(this) {
477 if (!audio_pref_handler
.get())
479 // If the DBusThreadManager or the CrasAudioClient aren't available, there
480 // isn't much we can do. This should only happen when running tests.
481 if (!chromeos::DBusThreadManager::IsInitialized() ||
482 !chromeos::DBusThreadManager::Get() ||
483 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
485 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
486 audio_pref_handler_
->AddAudioPrefObserver(this);
487 if (chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) {
488 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
491 InitializeAudioState();
494 CrasAudioHandler::~CrasAudioHandler() {
495 hdmi_rediscover_timer_
.Stop();
496 if (!chromeos::DBusThreadManager::IsInitialized() ||
497 !chromeos::DBusThreadManager::Get() ||
498 !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
500 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
501 RemoveObserver(this);
502 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
503 RemoveObserver(this);
504 if (audio_pref_handler_
.get())
505 audio_pref_handler_
->RemoveAudioPrefObserver(this);
506 audio_pref_handler_
= NULL
;
509 void CrasAudioHandler::AudioClientRestarted() {
510 // Make sure the logging is enabled in case cras server
511 // restarts after crashing.
513 InitializeAudioState();
516 void CrasAudioHandler::NodesChanged() {
517 // Refresh audio nodes data.
521 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id
) {
522 if (active_output_node_id_
== node_id
)
525 // Active audio output device should always be changed by chrome.
526 // During system boot, cras may change active input to unknown device 0x1,
527 // we don't need to log it, since it is not an valid device.
528 if (GetDeviceFromId(node_id
)) {
529 LOG_IF(WARNING
, log_errors_
)
530 << "Active output node changed unexpectedly by system node_id="
531 << "0x" << std::hex
<< node_id
;
535 void CrasAudioHandler::ActiveInputNodeChanged(uint64_t node_id
) {
536 if (active_input_node_id_
== node_id
)
539 // Active audio input device should always be changed by chrome.
540 // During system boot, cras may change active input to unknown device 0x2,
541 // we don't need to log it, since it is not an valid device.
542 if (GetDeviceFromId(node_id
)) {
543 LOG_IF(WARNING
, log_errors_
)
544 << "Active input node changed unexpectedly by system node_id="
545 << "0x" << std::hex
<< node_id
;
549 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
553 void CrasAudioHandler::EmitLoginPromptVisibleCalled() {
554 // Enable logging after cras server is started, which will be after
555 // EmitLoginPromptVisible.
559 const AudioDevice
* CrasAudioHandler::GetDeviceFromId(uint64_t device_id
) const {
560 AudioDeviceMap::const_iterator it
= audio_devices_
.find(device_id
);
561 if (it
== audio_devices_
.end())
564 return &(it
->second
);
567 const AudioDevice
* CrasAudioHandler::GetKeyboardMic() const {
568 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
569 it
!= audio_devices_
.end(); it
++) {
570 if (it
->second
.is_input
&& it
->second
.type
== AUDIO_TYPE_KEYBOARD_MIC
)
571 return &(it
->second
);
576 void CrasAudioHandler::SetupAudioInputState() {
577 // Set the initial audio state to the ones read from audio prefs.
578 const AudioDevice
* device
= GetDeviceFromId(active_input_node_id_
);
580 LOG_IF(ERROR
, log_errors_
)
581 << "Can't set up audio state for unknown input device id ="
582 << "0x" << std::hex
<< active_input_node_id_
;
585 input_gain_
= audio_pref_handler_
->GetInputGainValue(device
);
586 VLOG(1) << "SetupAudioInputState for active device id="
587 << "0x" << std::hex
<< device
->id
<< " mute=" << input_mute_on_
;
588 SetInputMuteInternal(input_mute_on_
);
589 // TODO(rkc,jennyz): Set input gain once we decide on how to store
590 // the gain values since the range and step are both device specific.
593 void CrasAudioHandler::SetupAudioOutputState() {
594 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
596 LOG_IF(ERROR
, log_errors_
)
597 << "Can't set up audio state for unknown output device id ="
598 << "0x" << std::hex
<< active_output_node_id_
;
601 DCHECK(!device
->is_input
);
602 // Mute the output during HDMI re-discovering grace period.
603 if (hdmi_rediscovering_
&& !IsHDMIPrimaryOutputDevice()) {
604 VLOG(1) << "Mute the output during HDMI re-discovering grace period";
605 output_mute_on_
= true;
607 output_mute_on_
= audio_pref_handler_
->GetMuteValue(*device
);
609 output_volume_
= audio_pref_handler_
->GetOutputVolumeValue(device
);
611 SetOutputMuteInternal(output_mute_on_
);
612 SetOutputNodeVolume(active_output_node_id_
, output_volume_
);
615 // This sets up the state of an additional active node.
616 void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64_t node_id
) {
617 const AudioDevice
* device
= GetDeviceFromId(node_id
);
619 VLOG(1) << "Can't set up audio state for unknown device id ="
620 << "0x" << std::hex
<< node_id
;
624 DCHECK(node_id
!= active_output_node_id_
&& node_id
!= active_input_node_id_
);
626 // Note: The mute state is a system wide state, we don't set mute per device,
627 // but just keep the mute state consistent for the active node in prefs.
628 // The output volume should be set to the same value for all active output
629 // devices. For input devices, we don't restore their gain value so far.
630 // TODO(jennyz): crbug.com/417418, track the status for the decison if
631 // we should persist input gain value in prefs.
632 if (!device
->is_input
) {
633 audio_pref_handler_
->SetMuteValue(*device
, IsOutputMuted());
634 SetOutputNodeVolumePercent(node_id
, GetOutputVolumePercent());
638 void CrasAudioHandler::InitializeAudioState() {
643 void CrasAudioHandler::ApplyAudioPolicy() {
644 output_mute_locked_
= false;
645 if (!audio_pref_handler_
->GetAudioOutputAllowedValue()) {
646 // Mute the device, but do not update the preference.
647 SetOutputMuteInternal(true);
648 output_mute_locked_
= true;
650 // Restore the mute state.
651 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
653 SetOutputMuteInternal(audio_pref_handler_
->GetMuteValue(*device
));
656 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
660 void CrasAudioHandler::SetOutputNodeVolume(uint64_t node_id
, int volume
) {
661 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
662 SetOutputNodeVolume(node_id
, volume
);
665 void CrasAudioHandler::SetOutputNodeVolumePercent(uint64_t node_id
,
666 int volume_percent
) {
667 const AudioDevice
* device
= this->GetDeviceFromId(node_id
);
668 if (!device
|| device
->is_input
)
671 volume_percent
= min(max(volume_percent
, 0), 100);
672 if (volume_percent
<= kMuteThresholdPercent
)
674 if (node_id
== active_output_node_id_
)
675 output_volume_
= volume_percent
;
677 audio_pref_handler_
->SetVolumeGainValue(*device
, volume_percent
);
679 if (device
->active
) {
680 SetOutputNodeVolume(node_id
, volume_percent
);
681 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
682 OnOutputNodeVolumeChanged(node_id
, volume_percent
));
686 bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on
) {
687 if (output_mute_locked_
)
690 output_mute_on_
= mute_on
;
691 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
692 SetOutputUserMute(mute_on
);
696 void CrasAudioHandler::SetInputNodeGain(uint64_t node_id
, int gain
) {
697 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
698 SetInputNodeGain(node_id
, gain
);
701 void CrasAudioHandler::SetInputNodeGainPercent(uint64_t node_id
,
703 const AudioDevice
* device
= GetDeviceFromId(node_id
);
704 if (!device
|| !device
->is_input
)
707 // NOTE: We do not sanitize input gain values since the range is completely
708 // dependent on the device.
709 if (active_input_node_id_
== node_id
)
710 input_gain_
= gain_percent
;
712 audio_pref_handler_
->SetVolumeGainValue(*device
, gain_percent
);
714 if (device
->active
) {
715 SetInputNodeGain(node_id
, gain_percent
);
716 FOR_EACH_OBSERVER(AudioObserver
, observers_
,
717 OnInputNodeGainChanged(node_id
, gain_percent
));
721 void CrasAudioHandler::SetInputMuteInternal(bool mute_on
) {
722 input_mute_on_
= mute_on
;
723 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
724 SetInputMute(mute_on
);
727 void CrasAudioHandler::GetNodes() {
728 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
729 base::Bind(&CrasAudioHandler::HandleGetNodes
,
730 weak_ptr_factory_
.GetWeakPtr()),
731 base::Bind(&CrasAudioHandler::HandleGetNodesError
,
732 weak_ptr_factory_
.GetWeakPtr()));
735 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice
& new_active_device
,
736 uint64_t* current_active_node_id
) {
737 // If the device we want to switch to is already the current active device,
739 if (new_active_device
.active
&&
740 new_active_device
.id
== *current_active_node_id
) {
744 // Reset all other input or output devices' active status. The active audio
745 // device from the previous user session can be remembered by cras, but not
746 // in chrome. see crbug.com/273271.
747 for (AudioDeviceMap::iterator it
= audio_devices_
.begin();
748 it
!= audio_devices_
.end(); ++it
) {
749 if (it
->second
.is_input
== new_active_device
.is_input
&&
750 it
->second
.id
!= new_active_device
.id
)
751 it
->second
.active
= false;
754 // Set the current active input/output device to the new_active_device.
755 *current_active_node_id
= new_active_device
.id
;
756 audio_devices_
[*current_active_node_id
].active
= true;
760 bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size
,
761 size_t new_devices_size
,
762 uint64_t current_active_node
) {
763 return (new_devices_size
< old_devices_size
&&
764 GetDeviceFromId(current_active_node
));
767 void CrasAudioHandler::SwitchToDevice(const AudioDevice
& device
, bool notify
) {
768 if (device
.is_input
) {
769 if (!ChangeActiveDevice(device
, &active_input_node_id_
))
771 SetupAudioInputState();
772 SetActiveInputNode(active_input_node_id_
, notify
);
774 if (!ChangeActiveDevice(device
, &active_output_node_id_
))
776 SetupAudioOutputState();
777 SetActiveOutputNode(active_output_node_id_
, notify
);
781 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList
& new_nodes
,
783 AudioNodeList
* new_discovered
) {
784 size_t num_old_devices
= 0;
785 size_t num_new_devices
= 0;
786 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
787 it
!= audio_devices_
.end(); ++it
) {
788 if (is_input
== it
->second
.is_input
)
792 bool new_or_changed_device
= false;
793 new_discovered
->clear();
794 for (AudioNodeList::const_iterator it
= new_nodes
.begin();
795 it
!= new_nodes
.end(); ++it
) {
796 if (is_input
== it
->is_input
) {
798 // Look to see if the new device not in the old device list.
799 AudioDevice
device(*it
);
800 DeviceStatus status
= CheckDeviceStatus(device
);
801 if (status
== NEW_DEVICE
)
802 new_discovered
->push_back(*it
);
803 if (status
== NEW_DEVICE
|| status
== CHANGED_DEVICE
) {
804 new_or_changed_device
= true;
808 return new_or_changed_device
|| (num_old_devices
!= num_new_devices
);
811 CrasAudioHandler::DeviceStatus
CrasAudioHandler::CheckDeviceStatus(
812 const AudioDevice
& device
) {
813 const AudioDevice
* device_found
= GetDeviceFromId(device
.id
);
817 if (!IsSameAudioDevice(device
, *device_found
)) {
818 LOG(WARNING
) << "Different Audio devices with same id:"
819 << " new device: " << device
.ToString()
820 << " old device: " << device_found
->ToString();
821 return CHANGED_DEVICE
;
822 } else if (device
.active
!= device_found
->active
) {
823 return CHANGED_DEVICE
;
829 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input
) {
831 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveInputNodeChanged());
833 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnActiveOutputNodeChanged());
836 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
837 const AudioNodeList
& nodes
) {
838 size_t old_output_device_size
= 0;
839 size_t old_input_device_size
= 0;
840 for (AudioDeviceMap::const_iterator it
= audio_devices_
.begin();
841 it
!= audio_devices_
.end(); ++it
) {
842 if (it
->second
.is_input
)
843 ++old_input_device_size
;
845 ++old_output_device_size
;
848 AudioNodeList hotplug_output_nodes
;
849 AudioNodeList hotplug_input_nodes
;
850 bool output_devices_changed
=
851 HasDeviceChange(nodes
, false, &hotplug_output_nodes
);
852 bool input_devices_changed
=
853 HasDeviceChange(nodes
, true, &hotplug_input_nodes
);
854 audio_devices_
.clear();
855 has_alternative_input_
= false;
856 has_alternative_output_
= false;
858 while (!input_devices_pq_
.empty())
859 input_devices_pq_
.pop();
860 while (!output_devices_pq_
.empty())
861 output_devices_pq_
.pop();
863 size_t new_output_device_size
= 0;
864 size_t new_input_device_size
= 0;
865 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
866 AudioDevice
device(nodes
[i
]);
867 audio_devices_
[device
.id
] = device
;
869 if (!has_alternative_input_
&&
871 device
.type
!= AUDIO_TYPE_INTERNAL_MIC
&&
872 device
.type
!= AUDIO_TYPE_KEYBOARD_MIC
) {
873 has_alternative_input_
= true;
874 } else if (!has_alternative_output_
&&
876 device
.type
!= AUDIO_TYPE_INTERNAL_SPEAKER
) {
877 has_alternative_output_
= true;
880 if (device
.is_input
) {
881 input_devices_pq_
.push(device
);
882 ++new_input_device_size
;
884 output_devices_pq_
.push(device
);
885 ++new_output_device_size
;
889 // If the previous active device is removed from the new node list,
890 // or changed to inactive by cras, reset active_output_node_id_.
891 // See crbug.com/478968.
892 const AudioDevice
* active_output
= GetDeviceFromId(active_output_node_id_
);
893 if (!active_output
|| !active_output
->active
)
894 active_output_node_id_
= 0;
895 const AudioDevice
* active_input
= GetDeviceFromId(active_input_node_id_
);
896 if (!active_input
|| !active_input
->active
)
897 active_input_node_id_
= 0;
899 // If audio nodes change is caused by unplugging some non-active audio
900 // devices, the previously set active audio device will stay active.
901 // Otherwise, switch to a new active audio device according to their priority.
902 if (input_devices_changed
&&
903 !NonActiveDeviceUnplugged(old_input_device_size
,
904 new_input_device_size
,
905 active_input_node_id_
)) {
906 // Some devices like chromeboxes don't have the internal audio input. In
907 // that case the active input node id should be reset.
908 if (input_devices_pq_
.empty()) {
909 active_input_node_id_
= 0;
910 NotifyActiveNodeChanged(true);
912 // If user has hot plugged a new node, we should change to the active
913 // device to the new node if it has the highest priority; otherwise,
914 // we should keep the existing active node chosen by user.
915 // For all other cases, we will choose the node with highest priority.
916 if (!active_input_node_id_
|| hotplug_input_nodes
.empty() ||
917 IsNodeInTheList(input_devices_pq_
.top().id
, hotplug_input_nodes
)) {
918 SwitchToDevice(input_devices_pq_
.top(), true);
922 if (output_devices_changed
&&
923 !NonActiveDeviceUnplugged(old_output_device_size
,
924 new_output_device_size
,
925 active_output_node_id_
)) {
926 // This is really unlikely to happen because all ChromeOS devices have the
927 // internal audio output.
928 if (output_devices_pq_
.empty()) {
929 active_output_node_id_
= 0;
930 NotifyActiveNodeChanged(false);
932 // ditto input node case.
933 if (!active_output_node_id_
|| hotplug_output_nodes
.empty() ||
934 IsNodeInTheList(output_devices_pq_
.top().id
, hotplug_output_nodes
)) {
935 SwitchToDevice(output_devices_pq_
.top(), true);
941 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList
& node_list
,
944 LOG_IF(ERROR
, log_errors_
) << "Failed to retrieve audio nodes data";
948 UpdateDevicesAndSwitchActive(node_list
);
949 FOR_EACH_OBSERVER(AudioObserver
, observers_
, OnAudioNodesChanged());
952 void CrasAudioHandler::HandleGetNodesError(const std::string
& error_name
,
953 const std::string
& error_msg
) {
954 LOG_IF(ERROR
, log_errors_
) << "Failed to call GetNodes: "
955 << error_name
<< ": " << error_msg
;
958 void CrasAudioHandler::AddAdditionalActiveNode(uint64_t node_id
, bool notify
) {
959 const AudioDevice
* device
= GetDeviceFromId(node_id
);
961 VLOG(1) << "AddActiveInputNode: Cannot find device id="
962 << "0x" << std::hex
<< node_id
;
966 audio_devices_
[node_id
].active
= true;
967 SetupAdditionalActiveAudioNodeState(node_id
);
969 if (device
->is_input
) {
970 DCHECK(node_id
!= active_input_node_id_
);
971 chromeos::DBusThreadManager::Get()
972 ->GetCrasAudioClient()
973 ->AddActiveInputNode(node_id
);
975 NotifyActiveNodeChanged(true);
977 DCHECK(node_id
!= active_output_node_id_
);
978 chromeos::DBusThreadManager::Get()
979 ->GetCrasAudioClient()
980 ->AddActiveOutputNode(node_id
);
982 NotifyActiveNodeChanged(false);
986 void CrasAudioHandler::RemoveActiveNodeInternal(uint64_t node_id
, bool notify
) {
987 const AudioDevice
* device
= GetDeviceFromId(node_id
);
989 VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
990 << "0x" << std::hex
<< node_id
;
994 audio_devices_
[node_id
].active
= false;
995 if (device
->is_input
) {
996 if (node_id
== active_input_node_id_
)
997 active_input_node_id_
= 0;
998 chromeos::DBusThreadManager::Get()
999 ->GetCrasAudioClient()
1000 ->RemoveActiveInputNode(node_id
);
1002 NotifyActiveNodeChanged(true);
1004 if (node_id
== active_output_node_id_
)
1005 active_output_node_id_
= 0;
1006 chromeos::DBusThreadManager::Get()
1007 ->GetCrasAudioClient()
1008 ->RemoveActiveOutputNode(node_id
);
1010 NotifyActiveNodeChanged(false);
1014 void CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod() {
1015 VLOG(1) << "HDMI output re-discover grace period ends.";
1016 hdmi_rediscovering_
= false;
1017 if (!IsOutputMutedForDevice(active_output_node_id_
)) {
1018 // Unmute the audio output after the HDMI transition period.
1019 VLOG(1) << "Unmute output after HDMI rediscovering grace period.";
1020 SetOutputMuteInternal(false);
1022 // Notify UI about the mute state change.
1024 AudioObserver
, observers_
,
1025 OnOutputMuteChanged(output_mute_on_
, true /* system adjustment */));
1029 bool CrasAudioHandler::IsHDMIPrimaryOutputDevice() const {
1030 const AudioDevice
* device
= GetDeviceFromId(active_output_node_id_
);
1031 return (device
&& device
->type
== chromeos::AUDIO_TYPE_HDMI
);
1034 void CrasAudioHandler::StartHDMIRediscoverGracePeriod() {
1035 VLOG(1) << "Start HDMI rediscovering grace period.";
1036 hdmi_rediscovering_
= true;
1037 hdmi_rediscover_timer_
.Stop();
1038 hdmi_rediscover_timer_
.Start(
1039 FROM_HERE
, base::TimeDelta::FromMilliseconds(
1040 hdmi_rediscover_grace_period_duration_in_ms_
),
1041 this, &CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod
);
1044 void CrasAudioHandler::SetHDMIRediscoverGracePeriodForTesting(
1045 int duration_in_ms
) {
1046 hdmi_rediscover_grace_period_duration_in_ms_
= duration_in_ms
;
1049 } // namespace chromeos